[WIP] Refactoring Logics

This commit is contained in:
HeesungB 2022-12-15 15:02:50 +09:00
parent 13de62028d
commit 7de3767d03
10 changed files with 179 additions and 118 deletions

View File

@ -0,0 +1,4 @@
export const TWITTER_LOGIN_ERROR = "Twitter login access denied";
export const TWITTER_PROFILE_ERROR = "Twitter auth code is not valid";
export const KEPLR_NOT_FOUND_ERROR = "Can't fount window.keplr";

View File

@ -2,14 +2,8 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
// Types // Types
import { import { ChainItemType, TwitterProfileType } from "../../types";
ChainItemType, import { checkTwitterAuthQueryParameter, request } from "../../utils/url";
IcnsVerificationResponse,
RegisteredAddresses,
TwitterAuthInfoResponse,
TwitterProfileType,
} from "../../types";
import { request } from "../../utils/url";
// Styles // Styles
import styled from "styled-components"; import styled from "styled-components";
@ -23,7 +17,6 @@ import { PrimaryButton } from "../../components/primary-button";
import { TwitterProfile } from "../../components/twitter-profile"; import { TwitterProfile } from "../../components/twitter-profile";
import { ChainList } from "../../components/chain-list"; import { ChainList } from "../../components/chain-list";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { ContractFee } from "../../constants/wallet";
import { import {
getKeplrFromWindow, getKeplrFromWindow,
KeplrWallet, KeplrWallet,
@ -45,9 +38,17 @@ import {
import { import {
fetchTwitterInfo, fetchTwitterInfo,
makeClaimMessage,
makeSetRecordMessage,
queryAddressesFromTwitterName, queryAddressesFromTwitterName,
queryRegisteredTwitterId, queryRegisteredTwitterId,
verifyTwitterAccount,
} from "../../repository"; } from "../../repository";
import { ErrorHandler } from "../../utils/error";
import {
KEPLR_NOT_FOUND_ERROR,
TWITTER_LOGIN_ERROR,
} from "../../constants/error-message";
export default function VerificationPage() { export default function VerificationPage() {
const router = useRouter(); const router = useRouter();
@ -65,37 +66,21 @@ export default function VerificationPage() {
const [allChecked, setAllChecked] = useState(false); const [allChecked, setAllChecked] = useState(false);
const [searchValue, setSearchValue] = useState(""); const [searchValue, setSearchValue] = useState("");
const fetchUrlQueryParameter = (): { state: string; code: string } => {
// Twitter state, auth code check
const [, state, code] =
window.location.search.match(
/^(?=.*state=([^&]+)|)(?=.*code=([^&]+)|).+$/,
) || [];
return {
state,
code,
};
};
useEffect(() => { useEffect(() => {
const init = async () => { const init = async () => {
if (window.location.search) { if (window.location.search) {
// Twitter Login Error Check
if (window.location.search.match("error")) {
await router.push("/");
return;
}
const { state, code } = fetchUrlQueryParameter();
try { try {
const { state, code } = checkTwitterAuthQueryParameter(
window.location.search,
);
// Initialize Wallet // Initialize Wallet
await initWallet(); await initWallet();
// Fetch Twitter Profile // Fetch Twitter Profile
const twitterInfo = await fetchTwitterInfo(state, code); const twitterInfo = await fetchTwitterInfo(state, code);
// check registered
const registeredQueryResponse = await queryRegisteredTwitterId( const registeredQueryResponse = await queryRegisteredTwitterId(
twitterInfo.id, twitterInfo.id,
); );
@ -116,8 +101,12 @@ export default function VerificationPage() {
), ),
); );
} }
} catch (e) { } catch (error) {
console.error(e); if (error instanceof Error && error.message === TWITTER_LOGIN_ERROR) {
await router.push("/");
}
console.error(error);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
@ -127,15 +116,6 @@ export default function VerificationPage() {
init(); init();
}, []); }, []);
const initWallet = async () => {
const keplr = await getKeplrFromWindow();
if (keplr) {
const keplrWallet = new KeplrWallet(keplr);
setWallet(keplrWallet);
}
};
useEffect(() => { useEffect(() => {
// After Wallet Initialize // After Wallet Initialize
if (wallet) { if (wallet) {
@ -144,6 +124,7 @@ export default function VerificationPage() {
}, [wallet]); }, [wallet]);
useEffect(() => { useEffect(() => {
// To check registered chain
const filteredChainList = chainList.filter((chain) => { const filteredChainList = chainList.filter((chain) => {
return registeredAddressList.includes(chain.address); return registeredAddressList.includes(chain.address);
}); });
@ -151,6 +132,17 @@ export default function VerificationPage() {
setCheckedItems(new Set(filteredChainList)); setCheckedItems(new Set(filteredChainList));
}, [registeredAddressList]); }, [registeredAddressList]);
const initWallet = async () => {
const keplr = await getKeplrFromWindow();
if (keplr) {
const keplrWallet = new KeplrWallet(keplr);
setWallet(keplrWallet);
} else {
ErrorHandler(KEPLR_NOT_FOUND_ERROR);
}
};
const fetchChainList = async () => { const fetchChainList = async () => {
if (wallet) { if (wallet) {
const chainIds = (await wallet.getChainInfosWithoutEndpoints()).map( const chainIds = (await wallet.getChainInfosWithoutEndpoints()).map(
@ -210,25 +202,6 @@ export default function VerificationPage() {
} }
}; };
const verifyTwitterAccount = async (accessToken: string) => {
if (wallet) {
const key = await wallet.getKey(MainChainId);
return (
await request<IcnsVerificationResponse>("/api/icns-verification", {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
claimer: key.bech32Address,
authToken: accessToken,
}),
})
).verificationList;
}
};
const checkAdr36 = async () => { const checkAdr36 = async () => {
if (twitterAuthInfo && wallet) { if (twitterAuthInfo && wallet) {
const key = await wallet.getKey(MainChainId); const key = await wallet.getKey(MainChainId);
@ -237,7 +210,7 @@ export default function VerificationPage() {
return (chain as ChainItemType).chainId; return (chain as ChainItemType).chainId;
}); });
return await wallet.signICNSAdr36( return wallet.signICNSAdr36(
MainChainId, MainChainId,
RESOLVER_ADDRESS, RESOLVER_ADDRESS,
key.bech32Address, key.bech32Address,
@ -248,59 +221,33 @@ export default function VerificationPage() {
}; };
const onClickRegistration = async () => { const onClickRegistration = async () => {
const { state, code } = fetchUrlQueryParameter(); const { state, code } = checkTwitterAuthQueryParameter(
window.location.search,
);
const twitterInfo = await fetchTwitterInfo(state, code); const twitterInfo = await fetchTwitterInfo(state, code);
const adr36Infos = await checkAdr36(); const adr36Infos = await checkAdr36();
const icnsVerificationList = await verifyTwitterAccount( if (wallet && adr36Infos) {
twitterInfo.accessToken,
);
if (wallet && icnsVerificationList && adr36Infos) {
const key = await wallet.getKey(MainChainId); const key = await wallet.getKey(MainChainId);
const registerMsg = makeCosmwasmExecMsg( const icnsVerificationList = await verifyTwitterAccount(
key.bech32Address, key.bech32Address,
REGISTRAR_ADDRESS, twitterInfo.accessToken,
{ );
claim: {
name: twitterInfo.username, const registerMsg = makeClaimMessage(
verifying_msg: key.bech32Address,
icnsVerificationList[0].status === "fulfilled" twitterInfo.username,
? icnsVerificationList[0].value.data.verifying_msg icnsVerificationList,
: "",
verifications: icnsVerificationList.map((verification) => {
if (verification.status === "fulfilled") {
return {
public_key: verification.value.data.public_key,
signature: verification.value.data.signature,
};
}
}),
},
},
[ContractFee],
); );
const addressMsgs = adr36Infos.map((adr36Info) => { const addressMsgs = adr36Infos.map((adr36Info) => {
return makeCosmwasmExecMsg( return makeSetRecordMessage(
key.bech32Address, key.bech32Address,
RESOLVER_ADDRESS, twitterInfo.username,
{ adr36Info,
set_record: {
name: twitterInfo.username,
bech32_prefix: adr36Info.bech32Prefix,
adr36_info: {
signer_bech32_address: adr36Info.bech32Address,
address_hash: adr36Info.addressHash,
pub_key: Buffer.from(adr36Info.pubKey).toString("base64"),
signature: Buffer.from(adr36Info.signature).toString("base64"),
signature_salt: adr36Info.signatureSalt.toString(),
},
},
},
[],
); );
}); });
@ -316,8 +263,6 @@ export default function VerificationPage() {
protoMsgs.push(addressMsg.proto); protoMsgs.push(addressMsg.proto);
} }
console.log(aminoMsgs);
const chainInfo = { const chainInfo = {
chainId: MainChainId, chainId: MainChainId,
rest: REST_URL, rest: REST_URL,
@ -348,7 +293,7 @@ export default function VerificationPage() {
}, },
); );
router.push({ await router.push({
pathname: "complete", pathname: "complete",
query: { txHash: Buffer.from(txHash).toString("hex") }, query: { txHash: Buffer.from(txHash).toString("hex") },
}); });

View File

@ -7,9 +7,12 @@ import {
import { Buffer } from "buffer/"; import { Buffer } from "buffer/";
import { import {
AddressesQueryResponse, AddressesQueryResponse,
CosmwasmExecuteMessageResult,
NameByTwitterIdQueryResponse, NameByTwitterIdQueryResponse,
QueryError, QueryError,
} from "../types"; } from "../types";
import { makeCosmwasmExecMsg } from "../wallets";
import { ContractFee } from "../constants/wallet";
const getCosmwasmQueryUrl = (contractAddress: string, queryMsg: string) => const getCosmwasmQueryUrl = (contractAddress: string, queryMsg: string) =>
`${REST_URL}/cosmwasm/wasm/v1/contract/${contractAddress}/smart/${queryMsg}`; `${REST_URL}/cosmwasm/wasm/v1/contract/${contractAddress}/smart/${queryMsg}`;
@ -34,10 +37,65 @@ export const queryAddressesFromTwitterName = async (
const msg = { const msg = {
addresses: { name: twitterUsername }, addresses: { name: twitterUsername },
}; };
return request<any>(
return request<AddressesQueryResponse>(
getCosmwasmQueryUrl( getCosmwasmQueryUrl(
RESOLVER_ADDRESS, RESOLVER_ADDRESS,
Buffer.from(JSON.stringify(msg)).toString("base64"), Buffer.from(JSON.stringify(msg)).toString("base64"),
), ),
); );
}; };
export const makeClaimMessage = (
senderAddress: string,
twitterUserName: string,
verificationList: any[],
): CosmwasmExecuteMessageResult => {
return makeCosmwasmExecMsg(
senderAddress,
REGISTRAR_ADDRESS,
{
claim: {
name: twitterUserName,
verifying_msg:
verificationList[0].status === "fulfilled"
? verificationList[0].value.data.verifying_msg
: "",
verifications: verificationList.map((verification) => {
if (verification.status === "fulfilled") {
return {
public_key: verification.value.data.public_key,
signature: verification.value.data.signature,
};
}
}),
},
},
[ContractFee],
);
};
export const makeSetRecordMessage = (
senderAddress: string,
twitterUserName: string,
adr36Info: any,
): CosmwasmExecuteMessageResult => {
return makeCosmwasmExecMsg(
senderAddress,
RESOLVER_ADDRESS,
{
set_record: {
name: twitterUserName,
bech32_prefix: adr36Info.bech32Prefix,
adr36_info: {
signer_bech32_address: adr36Info.bech32Address,
address_hash: adr36Info.addressHash,
pub_key: Buffer.from(adr36Info.pubKey).toString("base64"),
signature: Buffer.from(adr36Info.signature).toString("base64"),
signature_salt: adr36Info.signatureSalt.toString(),
},
},
},
[],
);
};

View File

@ -1,4 +1,8 @@
import { TwitterAuthInfoResponse, TwitterAuthUrlResponse } from "../types"; import {
IcnsVerificationResponse,
TwitterAuthInfoResponse,
TwitterAuthUrlResponse,
} from "../types";
import { request } from "../utils/url"; import { request } from "../utils/url";
export const loginWithTwitter = async () => { export const loginWithTwitter = async () => {
@ -13,9 +17,25 @@ export const fetchTwitterInfo = async (
state: string, state: string,
code: string, code: string,
): Promise<TwitterAuthInfoResponse> => { ): Promise<TwitterAuthInfoResponse> => {
const newTwitterAuthInfo = await request<TwitterAuthInfoResponse>( return await request<TwitterAuthInfoResponse>(
`/api/twitter-auth-info?state=${state}&code=${code}`, `/api/twitter-auth-info?state=${state}&code=${code}`,
); );
};
return newTwitterAuthInfo;
export const verifyTwitterAccount = async (
claimer: string,
accessToken: string,
) => {
return (
await request<IcnsVerificationResponse>("/api/icns-verification", {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
claimer: claimer,
authToken: accessToken,
}),
})
).verificationList;
}; };

9
types/icns.ts Normal file
View File

@ -0,0 +1,9 @@
import { Any } from "@keplr-wallet/proto-types/google/protobuf/any";
export interface CosmwasmExecuteMessageResult {
amino: {
readonly type: string;
readonly value: any;
};
proto: Any;
}

View File

@ -3,3 +3,4 @@ export * from "./api-response";
export * from "./chain-item-type"; export * from "./chain-item-type";
export * from "./msg"; export * from "./msg";
export * from "./twitter"; export * from "./twitter";
export * from "./icns";

View File

@ -3,3 +3,8 @@ import { TwitterAuthInfoResponse } from "./api-response";
export interface TwitterProfileType extends TwitterAuthInfoResponse { export interface TwitterProfileType extends TwitterAuthInfoResponse {
isRegistered: boolean; isRegistered: boolean;
} }
export interface TwitterLoginSuccess {
code: string;
state: string;
}

3
utils/error.ts Normal file
View File

@ -0,0 +1,3 @@
export const ErrorHandler = (message: string) => {
console.error(message);
};

View File

@ -1,3 +1,6 @@
import { TwitterLoginSuccess } from "../types";
import { TWITTER_LOGIN_ERROR } from "../constants/error-message";
export function request<TResponse>( export function request<TResponse>(
url: string, url: string,
config: RequestInit = {}, config: RequestInit = {},
@ -23,3 +26,21 @@ export function buildQueryString(query: Record<string, any>): string {
) )
.join("&"); .join("&");
} }
export const checkTwitterAuthQueryParameter = (
query: string,
): TwitterLoginSuccess => {
// Twitter Login Error Check
if (query.match("error")) {
throw new Error(TWITTER_LOGIN_ERROR);
}
// Twitter state, auth code check
const [, state, code] =
query.match(/^(?=.*state=([^&]+)|)(?=.*code=([^&]+)|).+$/) || [];
return {
state,
code,
};
};

View File

@ -15,6 +15,7 @@ import { Any } from "@keplr-wallet/proto-types/google/protobuf/any";
import { PubKey } from "@keplr-wallet/proto-types/cosmos/crypto/secp256k1/keys"; import { PubKey } from "@keplr-wallet/proto-types/cosmos/crypto/secp256k1/keys";
import { SignMode } from "@keplr-wallet/proto-types/cosmos/tx/signing/v1beta1/signing"; import { SignMode } from "@keplr-wallet/proto-types/cosmos/tx/signing/v1beta1/signing";
import { MsgExecuteContract } from "@keplr-wallet/proto-types/cosmwasm/wasm/v1/tx"; import { MsgExecuteContract } from "@keplr-wallet/proto-types/cosmwasm/wasm/v1/tx";
import { CosmwasmExecuteMessageResult } from "../types";
export async function sendMsgs( export async function sendMsgs(
wallet: Wallet, wallet: Wallet,
@ -189,13 +190,7 @@ export function makeCosmwasmExecMsg(
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
obj: object, obj: object,
funds: { readonly amount: string; readonly denom: string }[], funds: { readonly amount: string; readonly denom: string }[],
): { ): CosmwasmExecuteMessageResult {
amino: {
readonly type: string;
readonly value: any;
};
proto: Any;
} {
const amino = { const amino = {
type: "wasm/MsgExecuteContract", type: "wasm/MsgExecuteContract",
value: { value: {