From 7de3767d034f7663f963ba46793dccaed9fd5980 Mon Sep 17 00:00:00 2001 From: HeesungB Date: Thu, 15 Dec 2022 15:02:50 +0900 Subject: [PATCH] [WIP] Refactoring Logics --- constants/error-message.ts | 4 + pages/verification/index.tsx | 157 ++++++++++++----------------------- repository/icns.ts | 60 ++++++++++++- repository/twitter.ts | 28 ++++++- types/icns.ts | 9 ++ types/index.ts | 1 + types/twitter.ts | 5 ++ utils/error.ts | 3 + utils/url.ts | 21 +++++ wallets/cosmos.ts | 9 +- 10 files changed, 179 insertions(+), 118 deletions(-) create mode 100644 constants/error-message.ts create mode 100644 types/icns.ts create mode 100644 utils/error.ts diff --git a/constants/error-message.ts b/constants/error-message.ts new file mode 100644 index 0000000..5d01dda --- /dev/null +++ b/constants/error-message.ts @@ -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"; diff --git a/pages/verification/index.tsx b/pages/verification/index.tsx index be71e02..1ac11c3 100644 --- a/pages/verification/index.tsx +++ b/pages/verification/index.tsx @@ -2,14 +2,8 @@ import { useEffect, useState } from "react"; // Types -import { - ChainItemType, - IcnsVerificationResponse, - RegisteredAddresses, - TwitterAuthInfoResponse, - TwitterProfileType, -} from "../../types"; -import { request } from "../../utils/url"; +import { ChainItemType, TwitterProfileType } from "../../types"; +import { checkTwitterAuthQueryParameter, request } from "../../utils/url"; // Styles import styled from "styled-components"; @@ -23,7 +17,6 @@ import { PrimaryButton } from "../../components/primary-button"; import { TwitterProfile } from "../../components/twitter-profile"; import { ChainList } from "../../components/chain-list"; import { useRouter } from "next/router"; -import { ContractFee } from "../../constants/wallet"; import { getKeplrFromWindow, KeplrWallet, @@ -45,9 +38,17 @@ import { import { fetchTwitterInfo, + makeClaimMessage, + makeSetRecordMessage, queryAddressesFromTwitterName, queryRegisteredTwitterId, + verifyTwitterAccount, } from "../../repository"; +import { ErrorHandler } from "../../utils/error"; +import { + KEPLR_NOT_FOUND_ERROR, + TWITTER_LOGIN_ERROR, +} from "../../constants/error-message"; export default function VerificationPage() { const router = useRouter(); @@ -65,37 +66,21 @@ export default function VerificationPage() { const [allChecked, setAllChecked] = useState(false); 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(() => { const init = async () => { if (window.location.search) { - // Twitter Login Error Check - if (window.location.search.match("error")) { - await router.push("/"); - return; - } - - const { state, code } = fetchUrlQueryParameter(); - try { + const { state, code } = checkTwitterAuthQueryParameter( + window.location.search, + ); + // Initialize Wallet await initWallet(); // Fetch Twitter Profile const twitterInfo = await fetchTwitterInfo(state, code); + // check registered const registeredQueryResponse = await queryRegisteredTwitterId( twitterInfo.id, ); @@ -116,8 +101,12 @@ export default function VerificationPage() { ), ); } - } catch (e) { - console.error(e); + } catch (error) { + if (error instanceof Error && error.message === TWITTER_LOGIN_ERROR) { + await router.push("/"); + } + + console.error(error); } finally { setIsLoading(false); } @@ -127,15 +116,6 @@ export default function VerificationPage() { init(); }, []); - const initWallet = async () => { - const keplr = await getKeplrFromWindow(); - - if (keplr) { - const keplrWallet = new KeplrWallet(keplr); - setWallet(keplrWallet); - } - }; - useEffect(() => { // After Wallet Initialize if (wallet) { @@ -144,6 +124,7 @@ export default function VerificationPage() { }, [wallet]); useEffect(() => { + // To check registered chain const filteredChainList = chainList.filter((chain) => { return registeredAddressList.includes(chain.address); }); @@ -151,6 +132,17 @@ export default function VerificationPage() { setCheckedItems(new Set(filteredChainList)); }, [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 () => { if (wallet) { 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("/api/icns-verification", { - method: "post", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - claimer: key.bech32Address, - authToken: accessToken, - }), - }) - ).verificationList; - } - }; - const checkAdr36 = async () => { if (twitterAuthInfo && wallet) { const key = await wallet.getKey(MainChainId); @@ -237,7 +210,7 @@ export default function VerificationPage() { return (chain as ChainItemType).chainId; }); - return await wallet.signICNSAdr36( + return wallet.signICNSAdr36( MainChainId, RESOLVER_ADDRESS, key.bech32Address, @@ -248,59 +221,33 @@ export default function VerificationPage() { }; const onClickRegistration = async () => { - const { state, code } = fetchUrlQueryParameter(); + const { state, code } = checkTwitterAuthQueryParameter( + window.location.search, + ); + const twitterInfo = await fetchTwitterInfo(state, code); const adr36Infos = await checkAdr36(); - const icnsVerificationList = await verifyTwitterAccount( - twitterInfo.accessToken, - ); - - if (wallet && icnsVerificationList && adr36Infos) { + if (wallet && adr36Infos) { const key = await wallet.getKey(MainChainId); - const registerMsg = makeCosmwasmExecMsg( + const icnsVerificationList = await verifyTwitterAccount( key.bech32Address, - REGISTRAR_ADDRESS, - { - claim: { - name: twitterInfo.username, - verifying_msg: - icnsVerificationList[0].status === "fulfilled" - ? icnsVerificationList[0].value.data.verifying_msg - : "", - verifications: icnsVerificationList.map((verification) => { - if (verification.status === "fulfilled") { - return { - public_key: verification.value.data.public_key, - signature: verification.value.data.signature, - }; - } - }), - }, - }, - [ContractFee], + twitterInfo.accessToken, + ); + + const registerMsg = makeClaimMessage( + key.bech32Address, + twitterInfo.username, + icnsVerificationList, ); const addressMsgs = adr36Infos.map((adr36Info) => { - return makeCosmwasmExecMsg( + return makeSetRecordMessage( key.bech32Address, - RESOLVER_ADDRESS, - { - 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(), - }, - }, - }, - [], + twitterInfo.username, + adr36Info, ); }); @@ -316,8 +263,6 @@ export default function VerificationPage() { protoMsgs.push(addressMsg.proto); } - console.log(aminoMsgs); - const chainInfo = { chainId: MainChainId, rest: REST_URL, @@ -348,7 +293,7 @@ export default function VerificationPage() { }, ); - router.push({ + await router.push({ pathname: "complete", query: { txHash: Buffer.from(txHash).toString("hex") }, }); diff --git a/repository/icns.ts b/repository/icns.ts index cd51b39..658451f 100644 --- a/repository/icns.ts +++ b/repository/icns.ts @@ -7,9 +7,12 @@ import { import { Buffer } from "buffer/"; import { AddressesQueryResponse, + CosmwasmExecuteMessageResult, NameByTwitterIdQueryResponse, QueryError, } from "../types"; +import { makeCosmwasmExecMsg } from "../wallets"; +import { ContractFee } from "../constants/wallet"; const getCosmwasmQueryUrl = (contractAddress: string, queryMsg: string) => `${REST_URL}/cosmwasm/wasm/v1/contract/${contractAddress}/smart/${queryMsg}`; @@ -34,10 +37,65 @@ export const queryAddressesFromTwitterName = async ( const msg = { addresses: { name: twitterUsername }, }; - return request( + + return request( getCosmwasmQueryUrl( RESOLVER_ADDRESS, 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(), + }, + }, + }, + [], + ); +}; diff --git a/repository/twitter.ts b/repository/twitter.ts index 6c141a4..163726a 100644 --- a/repository/twitter.ts +++ b/repository/twitter.ts @@ -1,4 +1,8 @@ -import { TwitterAuthInfoResponse, TwitterAuthUrlResponse } from "../types"; +import { + IcnsVerificationResponse, + TwitterAuthInfoResponse, + TwitterAuthUrlResponse, +} from "../types"; import { request } from "../utils/url"; export const loginWithTwitter = async () => { @@ -13,9 +17,25 @@ export const fetchTwitterInfo = async ( state: string, code: string, ): Promise => { - const newTwitterAuthInfo = await request( + return await request( `/api/twitter-auth-info?state=${state}&code=${code}`, ); - - return newTwitterAuthInfo; +}; + +export const verifyTwitterAccount = async ( + claimer: string, + accessToken: string, +) => { + return ( + await request("/api/icns-verification", { + method: "post", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + claimer: claimer, + authToken: accessToken, + }), + }) + ).verificationList; }; diff --git a/types/icns.ts b/types/icns.ts new file mode 100644 index 0000000..4e28b87 --- /dev/null +++ b/types/icns.ts @@ -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; +} diff --git a/types/index.ts b/types/index.ts index 7de37b7..a14cb5f 100644 --- a/types/index.ts +++ b/types/index.ts @@ -3,3 +3,4 @@ export * from "./api-response"; export * from "./chain-item-type"; export * from "./msg"; export * from "./twitter"; +export * from "./icns"; diff --git a/types/twitter.ts b/types/twitter.ts index 3241069..20a529c 100644 --- a/types/twitter.ts +++ b/types/twitter.ts @@ -3,3 +3,8 @@ import { TwitterAuthInfoResponse } from "./api-response"; export interface TwitterProfileType extends TwitterAuthInfoResponse { isRegistered: boolean; } + +export interface TwitterLoginSuccess { + code: string; + state: string; +} diff --git a/utils/error.ts b/utils/error.ts new file mode 100644 index 0000000..c0092c6 --- /dev/null +++ b/utils/error.ts @@ -0,0 +1,3 @@ +export const ErrorHandler = (message: string) => { + console.error(message); +}; diff --git a/utils/url.ts b/utils/url.ts index b76ac95..f89d962 100644 --- a/utils/url.ts +++ b/utils/url.ts @@ -1,3 +1,6 @@ +import { TwitterLoginSuccess } from "../types"; +import { TWITTER_LOGIN_ERROR } from "../constants/error-message"; + export function request( url: string, config: RequestInit = {}, @@ -23,3 +26,21 @@ export function buildQueryString(query: Record): string { ) .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, + }; +}; diff --git a/wallets/cosmos.ts b/wallets/cosmos.ts index 9ad5ee0..e677e28 100644 --- a/wallets/cosmos.ts +++ b/wallets/cosmos.ts @@ -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 { SignMode } from "@keplr-wallet/proto-types/cosmos/tx/signing/v1beta1/signing"; import { MsgExecuteContract } from "@keplr-wallet/proto-types/cosmwasm/wasm/v1/tx"; +import { CosmwasmExecuteMessageResult } from "../types"; export async function sendMsgs( wallet: Wallet, @@ -189,13 +190,7 @@ export function makeCosmwasmExecMsg( // eslint-disable-next-line @typescript-eslint/ban-types obj: object, funds: { readonly amount: string; readonly denom: string }[], -): { - amino: { - readonly type: string; - readonly value: any; - }; - proto: Any; -} { +): CosmwasmExecuteMessageResult { const amino = { type: "wasm/MsgExecuteContract", value: {