icns-frontend/pages/verification/index.tsx

406 lines
10 KiB
TypeScript
Raw Normal View History

2022-12-07 09:17:59 +00:00
// React
2022-12-01 08:33:51 +00:00
import { useEffect, useState } from "react";
2022-12-07 09:17:59 +00:00
// Types
2022-12-15 06:02:50 +00:00
import { ChainItemType, TwitterProfileType } from "../../types";
import { checkTwitterAuthQueryParameter, request } from "../../utils/url";
2022-12-01 08:33:51 +00:00
2022-12-07 09:17:59 +00:00
// Styles
2022-12-07 13:55:22 +00:00
import styled from "styled-components";
2022-12-06 14:53:31 +00:00
import color from "../../styles/color";
2022-12-01 08:33:51 +00:00
2022-12-07 09:17:59 +00:00
// Components
import { Logo } from "../../components/logo";
2022-12-09 11:59:52 +00:00
import { SkeletonChainList } from "../../components/skeleton";
2022-12-07 09:17:59 +00:00
import { PrimaryButton } from "../../components/primary-button";
2022-12-12 07:10:11 +00:00
import { TwitterProfile } from "../../components/twitter-profile";
2022-12-09 11:59:52 +00:00
import { ChainList } from "../../components/chain-list";
import { useRouter } from "next/router";
2022-12-13 14:46:32 +00:00
import {
getKeplrFromWindow,
KeplrWallet,
makeCosmwasmExecMsg,
2022-12-14 07:52:05 +00:00
sendMsgs,
2022-12-13 14:46:32 +00:00
simulateMsgs,
} from "../../wallets";
import { ChainIdHelper } from "@keplr-wallet/cosmos";
2022-12-07 09:17:59 +00:00
import AllChainsIcon from "../../public/images/svg/all-chains-icon.svg";
import { AllChainsItem } from "../../components/chain-list/all-chains-item";
2022-12-13 13:10:31 +00:00
import { SearchInput } from "../../components/search-input";
2022-12-13 14:46:32 +00:00
import {
2022-12-14 07:52:05 +00:00
MainChainId,
2022-12-13 14:46:32 +00:00
REGISTRAR_ADDRESS,
RESOLVER_ADDRESS,
REST_URL,
} from "../../constants/icns";
2022-12-14 11:36:08 +00:00
import {
fetchTwitterInfo,
2022-12-15 06:02:50 +00:00
makeClaimMessage,
makeSetRecordMessage,
2022-12-14 11:36:08 +00:00
queryAddressesFromTwitterName,
queryRegisteredTwitterId,
2022-12-15 06:02:50 +00:00
verifyTwitterAccount,
2022-12-14 11:36:08 +00:00
} from "../../repository";
2022-12-15 06:02:50 +00:00
import { ErrorHandler } from "../../utils/error";
import {
KEPLR_NOT_FOUND_ERROR,
TWITTER_LOGIN_ERROR,
} from "../../constants/error-message";
2022-12-14 07:52:05 +00:00
2022-12-01 08:33:51 +00:00
export default function VerificationPage() {
const router = useRouter();
2022-12-14 11:36:08 +00:00
const [twitterAuthInfo, setTwitterAuthInfo] = useState<TwitterProfileType>();
2022-12-01 08:33:51 +00:00
2022-12-07 09:17:59 +00:00
const [isLoading, setIsLoading] = useState(true);
const [wallet, setWallet] = useState<KeplrWallet>();
const [allChains, setAllChains] = useState<ChainItemType>();
const [chainList, setChainList] = useState<ChainItemType[]>([]);
2022-12-14 11:36:08 +00:00
const [registeredAddressList, setRegisteredAddressList] = useState<string[]>(
[],
);
const [checkedItems, setCheckedItems] = useState(new Set());
const [allChecked, setAllChecked] = useState(false);
2022-12-13 13:10:31 +00:00
const [searchValue, setSearchValue] = useState("");
useEffect(() => {
2022-12-14 07:52:05 +00:00
const init = async () => {
if (window.location.search) {
2022-12-14 11:36:08 +00:00
try {
2022-12-15 06:02:50 +00:00
const { state, code } = checkTwitterAuthQueryParameter(
window.location.search,
);
2022-12-14 11:36:08 +00:00
// Initialize Wallet
await initWallet();
// Fetch Twitter Profile
const twitterInfo = await fetchTwitterInfo(state, code);
2022-12-15 06:02:50 +00:00
// check registered
2022-12-14 11:36:08 +00:00
const registeredQueryResponse = await queryRegisteredTwitterId(
twitterInfo.id,
);
setTwitterAuthInfo({
...twitterInfo,
isRegistered: "data" in registeredQueryResponse,
});
if ("data" in registeredQueryResponse) {
const addressesQueryResponse = await queryAddressesFromTwitterName(
registeredQueryResponse.data.name,
);
setRegisteredAddressList(
addressesQueryResponse.data.addresses.map(
(address) => address.address,
),
);
}
2022-12-15 06:02:50 +00:00
} catch (error) {
if (error instanceof Error && error.message === TWITTER_LOGIN_ERROR) {
await router.push("/");
}
console.error(error);
2022-12-14 11:36:08 +00:00
} finally {
setIsLoading(false);
}
}
};
2022-12-01 08:33:51 +00:00
2022-12-14 07:52:05 +00:00
init();
2022-12-01 08:33:51 +00:00
}, []);
useEffect(() => {
2022-12-14 07:52:05 +00:00
// After Wallet Initialize
if (wallet) {
fetchChainList();
}
}, [wallet]);
2022-12-14 11:36:08 +00:00
useEffect(() => {
2022-12-15 06:02:50 +00:00
// To check registered chain
2022-12-14 11:36:08 +00:00
const filteredChainList = chainList.filter((chain) => {
return registeredAddressList.includes(chain.address);
});
setCheckedItems(new Set(filteredChainList));
}, [registeredAddressList]);
2022-12-15 06:02:50 +00:00
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(
(c) => c.chainId,
);
const chainKeys = await Promise.all(
chainIds.map((chainId) => wallet.getKey(chainId)),
);
const chainInfos = (await wallet.getChainInfosWithoutEndpoints()).map(
(chainInfo) => {
return {
chainId: chainInfo.chainId,
2022-12-14 07:52:05 +00:00
chainName: chainInfo.chainName,
prefix: chainInfo.bech32Config.bech32PrefixAccAddr,
chainImageUrl: `https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/${
ChainIdHelper.parse(chainInfo.chainId).identifier
}/chain.png`,
};
},
);
2022-12-14 07:52:05 +00:00
const chainArray = [];
for (let i = 0; i < chainKeys.length; i++) {
chainArray.push({
address: chainKeys[i].bech32Address,
...chainInfos[i],
});
}
// remove duplicated item
2022-12-14 07:52:05 +00:00
const filteredChainList = chainArray.filter((nextChain, index, self) => {
return (
index ===
self.findIndex((prevChain) => {
const isDuplicated = prevChain.prefix === nextChain.prefix;
if (isDuplicated && prevChain.chainName !== nextChain.chainName) {
console.log(
`${nextChain.chainName} has been deleted due to a duplicate name with ${prevChain.chainName}`,
);
}
return isDuplicated;
})
);
});
2022-12-14 07:52:05 +00:00
setAllChains({
chainId: "all chains",
prefix: `all chains(${filteredChainList.length})`,
address: chainInfos.map((chainInfo) => chainInfo.chainName).join(", "),
chainImageUrl: AllChainsIcon,
});
setChainList(filteredChainList);
}
};
2022-12-13 14:46:32 +00:00
const checkAdr36 = async () => {
if (twitterAuthInfo && wallet) {
const key = await wallet.getKey(MainChainId);
2022-12-14 15:39:51 +00:00
2022-12-14 11:36:08 +00:00
const chainIds = Array.from(checkedItems).map((chain) => {
return (chain as ChainItemType).chainId;
});
2022-12-14 15:39:51 +00:00
2022-12-15 06:02:50 +00:00
return wallet.signICNSAdr36(
2022-12-13 14:46:32 +00:00
MainChainId,
RESOLVER_ADDRESS,
key.bech32Address,
twitterAuthInfo.username,
chainIds,
);
}
};
const onClickRegistration = async () => {
2022-12-15 06:02:50 +00:00
const { state, code } = checkTwitterAuthQueryParameter(
window.location.search,
);
2022-12-14 07:52:05 +00:00
const twitterInfo = await fetchTwitterInfo(state, code);
2022-12-13 14:46:32 +00:00
const adr36Infos = await checkAdr36();
2022-12-15 06:02:50 +00:00
if (wallet && adr36Infos) {
2022-12-13 14:46:32 +00:00
const key = await wallet.getKey(MainChainId);
2022-12-15 06:02:50 +00:00
const icnsVerificationList = await verifyTwitterAccount(
2022-12-13 14:46:32 +00:00
key.bech32Address,
2022-12-15 06:02:50 +00:00
twitterInfo.accessToken,
);
const registerMsg = makeClaimMessage(
key.bech32Address,
twitterInfo.username,
icnsVerificationList,
2022-12-13 14:46:32 +00:00
);
const addressMsgs = adr36Infos.map((adr36Info) => {
2022-12-15 06:02:50 +00:00
return makeSetRecordMessage(
2022-12-13 14:46:32 +00:00
key.bech32Address,
2022-12-15 06:02:50 +00:00
twitterInfo.username,
adr36Info,
2022-12-13 14:46:32 +00:00
);
});
2022-12-14 11:36:08 +00:00
const aminoMsgs = twitterAuthInfo?.isRegistered
? []
: [registerMsg.amino];
const protoMsgs = twitterAuthInfo?.isRegistered
? []
: [registerMsg.proto];
2022-12-13 14:46:32 +00:00
for (const addressMsg of addressMsgs) {
aminoMsgs.push(addressMsg.amino);
protoMsgs.push(addressMsg.proto);
}
const chainInfo = {
chainId: MainChainId,
rest: REST_URL,
};
const simulated = await simulateMsgs(
chainInfo,
key.bech32Address,
{
proto: protoMsgs,
},
{
amount: [],
},
);
2022-12-14 07:52:05 +00:00
const txHash = await sendMsgs(
wallet,
chainInfo,
key.bech32Address,
{
amino: aminoMsgs,
proto: protoMsgs,
},
{
amount: [],
gas: Math.floor(simulated.gasUsed * 1.5).toString(),
},
);
2022-12-15 06:02:50 +00:00
await router.push({
2022-12-14 07:52:05 +00:00
pathname: "complete",
query: { txHash: Buffer.from(txHash).toString("hex") },
});
2022-12-13 14:46:32 +00:00
}
};
2022-12-01 08:33:51 +00:00
return (
2022-12-06 14:53:31 +00:00
<Container>
<Logo />
2022-12-01 08:33:51 +00:00
2022-12-06 14:53:31 +00:00
<MainContainer>
2022-12-07 09:17:59 +00:00
{isLoading ? (
2022-12-09 11:59:52 +00:00
<SkeletonChainList />
2022-12-07 09:17:59 +00:00
) : (
<ContentContainer>
2022-12-09 11:59:52 +00:00
<TwitterProfile twitterProfileInformation={twitterAuthInfo} />
2022-12-07 09:17:59 +00:00
<ChainListTitleContainer>
<ChainListTitle>Chain List</ChainListTitle>
2022-12-13 13:10:31 +00:00
<SearchInput
searchValue={searchValue}
setSearchValue={setSearchValue}
2022-12-14 15:39:51 +00:00
/>
2022-12-07 09:17:59 +00:00
</ChainListTitleContainer>
2022-12-14 15:39:51 +00:00
2022-12-13 13:10:31 +00:00
{allChains && !searchValue ? (
<AllChainsItem
allChecked={allChecked}
setAllChecked={setAllChecked}
chainItem={allChains}
/>
) : null}
<ChainList
allChecked={allChecked}
setAllChecked={setAllChecked}
2022-12-13 13:10:31 +00:00
chainList={chainList.filter(
(chain) =>
chain.chainId.includes(searchValue) ||
chain.address.includes(searchValue) ||
chain.prefix.includes(searchValue),
)}
checkedItems={checkedItems}
setCheckedItems={setCheckedItems}
/>
2022-12-07 09:17:59 +00:00
<ButtonContainer>
<PrimaryButton
disabled={checkedItems.size < 1}
onClick={onClickRegistration}
>
Register
</PrimaryButton>
2022-12-07 09:17:59 +00:00
</ButtonContainer>
</ContentContainer>
)}
2022-12-06 14:53:31 +00:00
</MainContainer>
</Container>
2022-12-01 08:33:51 +00:00
);
}
2022-12-07 13:55:22 +00:00
const Container = styled.div`
width: 100vw;
height: 100vh;
`;
const MainContainer = styled.div`
display: flex;
justify-content: center;
color: white;
`;
2022-12-09 11:59:52 +00:00
export const ContentContainer = styled.div`
2022-12-07 13:55:22 +00:00
display: flex;
flex-direction: column;
align-items: center;
width: 40rem;
margin-top: 5rem;
`;
2022-12-09 11:59:52 +00:00
export const ButtonContainer = styled.div`
2022-12-07 13:55:22 +00:00
width: 12rem;
height: 4rem;
2022-12-09 11:59:52 +00:00
margin-top: 2rem;
2022-12-07 13:55:22 +00:00
`;
2022-12-09 11:59:52 +00:00
export const ChainListTitleContainer = styled.div`
2022-12-07 13:55:22 +00:00
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
margin-top: 2rem;
margin-bottom: 1rem;
`;
const ChainListTitle = styled.div`
font-weight: 700;
font-size: 1.5rem;
line-height: 1.9rem;
color: ${color.white};
`;