Compare commits

..
Author SHA1 Message Date
Thunnini c69788989d Change contract 2022-12-13 13:46:47 +09:00
Thunnini ef68b49027 Change contract address 2022-12-12 18:57:59 +09:00
Thunnini c900e86e3a Fix unknown problem 2022-12-12 18:57:49 +09:00
Thunnini de2a90457e WIP: test execute 2022-12-12 17:50:52 +09:00
Thunnini 7ae09a0c11 Change some typings 2022-12-12 17:50:40 +09:00
Thunnini 1ea85324aa Use Buffer package instead of Buffer on nodejs 2022-12-12 16:12:04 +09:00
89 changed files with 678 additions and 2180 deletions
Generated
-43
View File
@@ -38,7 +38,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/react", "npm:18.0.25"],\
["@types/react-dom", "npm:18.0.9"],\
["@types/react-modal", "npm:3.13.1"],\
["@types/semver", "npm:7.3.13"],\
["@types/styled-components", "npm:5.1.26"],\
["@typescript-eslint/eslint-plugin", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.45.0"],\
["@typescript-eslint/parser", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.45.0"],\
@@ -62,8 +61,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["react-dom", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:18.2.0"],\
["react-is", "npm:18.2.0"],\
["react-modal", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:3.16.1"],\
["react-typed", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:1.2.0"],\
["semver", "npm:7.3.8"],\
["styled-components", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.3.6"],\
["typescript", "patch:typescript@npm%3A4.9.3#~builtin<compat/typescript>::version=4.9.3&hash=d73830"]\
],\
@@ -3987,7 +3984,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/react", "npm:18.0.25"],\
["@types/react-dom", "npm:18.0.9"],\
["@types/react-modal", "npm:3.13.1"],\
["@types/semver", "npm:7.3.13"],\
["@types/styled-components", "npm:5.1.26"],\
["@typescript-eslint/eslint-plugin", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.45.0"],\
["@typescript-eslint/parser", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.45.0"],\
@@ -4011,8 +4007,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["react-dom", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:18.2.0"],\
["react-is", "npm:18.2.0"],\
["react-modal", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:3.16.1"],\
["react-typed", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:1.2.0"],\
["semver", "npm:7.3.8"],\
["styled-components", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.3.6"],\
["typescript", "patch:typescript@npm%3A4.9.3#~builtin<compat/typescript>::version=4.9.3&hash=d73830"]\
],\
@@ -5735,34 +5729,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["react-typed", [\
["npm:1.2.0", {\
"packageLocation": "./.yarn/cache/react-typed-npm-1.2.0-8feca2dd12-c750d68710.zip/node_modules/react-typed/",\
"packageDependencies": [\
["react-typed", "npm:1.2.0"]\
],\
"linkType": "SOFT"\
}],\
["virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:1.2.0", {\
"packageLocation": "./.yarn/__virtual__/react-typed-virtual-c12f16610e/0/cache/react-typed-npm-1.2.0-8feca2dd12-c750d68710.zip/node_modules/react-typed/",\
"packageDependencies": [\
["react-typed", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:1.2.0"],\
["@types/react", "npm:18.0.25"],\
["@types/react-dom", "npm:18.0.9"],\
["prop-types", "npm:15.8.1"],\
["react", "npm:18.2.0"],\
["react-dom", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:18.2.0"],\
["typed.js", "npm:2.0.12"]\
],\
"packagePeers": [\
"@types/react-dom",\
"@types/react",\
"react-dom",\
"react"\
],\
"linkType": "HARD"\
}]\
]],\
["read-pkg", [\
["npm:5.2.0", {\
"packageLocation": "./.yarn/cache/read-pkg-npm-5.2.0-50426bd8dc-eb696e6052.zip/node_modules/read-pkg/",\
@@ -6647,15 +6613,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["typed.js", [\
["npm:2.0.12", {\
"packageLocation": "./.yarn/cache/typed.js-npm-2.0.12-7618b42dfc-ea753bf8e0.zip/node_modules/typed.js/",\
"packageDependencies": [\
["typed.js", "npm:2.0.12"]\
],\
"linkType": "HARD"\
}]\
]],\
["typeforce", [\
["npm:1.18.0", {\
"packageLocation": "./.yarn/cache/typeforce-npm-1.18.0-372e34e2a2-e3b21e27e7.zip/node_modules/typeforce/",\
Binary file not shown.
Binary file not shown.
-61
View File
@@ -1,61 +0,0 @@
import { FunctionComponent } from "react";
import styled from "styled-components";
import color from "../../styles/color";
import ArrowLeftIcon from "../../public/images/svg/arrow-left.svg";
import Image from "next/image";
export const BackButton: FunctionComponent = () => {
return (
<Container
onClick={() => {
location.href = "/";
}}
>
<ContentContainer>
<IconContainer>
<Image
src={ArrowLeftIcon}
fill={true}
sizes="1rem"
alt="arrow left icon"
/>
</IconContainer>
<div>BACK</div>
</ContentContainer>
</Container>
);
};
const Container = styled.div`
display: flex;
justify-content: flex-end;
width: 100%;
padding: 0.7rem 0.5rem;
font-family: "Inter", serif;
font-style: normal;
font-weight: 500;
font-size: 0.8rem;
line-height: 0.8rem;
color: ${color.grey["400"]};
cursor: pointer;
`;
const ContentContainer = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: 0.25rem;
`;
const IconContainer = styled.div`
position: relative;
width: 1rem;
height: 1rem;
`;
-78
View File
@@ -1,78 +0,0 @@
import { ChainItemType } from "../../types";
import {
Dispatch,
FunctionComponent,
SetStateAction,
useEffect,
useState,
} from "react";
import { ChainImage } from "./chain-image";
import { Flex1 } from "../../styles/flex-1";
import {
ChainInfoContainer,
ChainItemContainer,
ChainName,
WalletAddress,
} from "./chain-item";
import color from "../../styles/color";
import styled from "styled-components";
import { Checkbox } from "../checkbox";
import AllChainsIcon from "../../public/images/svg/all-chains-icon.svg";
interface Props {
chainList: ChainItemType[];
checkedItems: Set<unknown>;
setCheckedItems: Dispatch<SetStateAction<Set<unknown>>>;
}
export const AllChainsItem: FunctionComponent<Props> = (props) => {
const { chainList, checkedItems, setCheckedItems } = props;
const [checked, setChecked] = useState(false);
const checkHandler = () => {
if (checked) {
setCheckedItems(new Set());
} else if (chainList.length !== checkedItems.size) {
setCheckedItems(new Set(chainList));
}
};
useEffect(() => {
if (chainList.length === checkedItems.size && checkedItems.size !== 0) {
setChecked(true);
} else {
setChecked(false);
}
}, [checkedItems]);
return (
<AllChainsContainer>
<ChainItemContainer
key="all chains"
isLoading={false}
checked={checked}
onClick={checkHandler}
>
<ChainImage src={AllChainsIcon} fill={true} alt={`all chain images`} />
<ChainInfoContainer>
<ChainName>{`.all chains(${chainList.length})`}</ChainName>
<WalletAddress>
{chainList.map((chain) => chain.chainName).join(", ")}
</WalletAddress>
</ChainInfoContainer>
<Flex1 />
<Checkbox checked={checked} />
</ChainItemContainer>
</AllChainsContainer>
);
};
const AllChainsContainer = styled.div`
width: 100%;
background-color: ${color.grey["900"]};
`;
-31
View File
@@ -1,31 +0,0 @@
import { useState } from "react";
import Image, { ImageProps } from "next/image";
import KeplrIcon from "../../public/images/svg/keplr-icon.svg";
import styled from "styled-components";
export const ChainImage = ({ src, ...props }: ImageProps) => {
const [srcState, setSrcState] = useState(src);
return (
<ImageWrapper>
<Image
{...props}
src={srcState}
alt="chain image"
sizes="3rem"
onError={() => setSrcState(KeplrIcon)}
/>
</ImageWrapper>
);
};
const ImageWrapper = styled.div`
position: relative;
width: 3rem;
height: 3rem;
img {
border-radius: 50%;
}
`;
+29 -88
View File
@@ -1,119 +1,60 @@
import { ChainItemType } from "../../types";
import { FunctionComponent, useEffect, useState } from "react";
import { AccountInfo } from "../../types";
import { FunctionComponent } from "react";
import {
ChainImageContainer,
ChainInfoContainer,
ChainItemContainer,
} from "./chain-list";
import Image from "next/image";
import color from "../../styles/color";
import { Flex1 } from "../../styles/flex-1";
import styled from "styled-components";
import { ChainImage } from "./chain-image";
import { Checkbox } from "../checkbox";
interface Props {
chainItem: ChainItemType;
checkedItemHandler: (chainItem: ChainItemType, isChecked: boolean) => void;
checkedItems: Set<unknown>;
disabled?: boolean;
chainInfo: AccountInfo;
}
export const ChainItem: FunctionComponent<Props> = (props) => {
const { chainItem, checkedItemHandler, checkedItems, disabled } = props;
const [checked, setChecked] = useState(!!disabled);
const checkHandler = () => {
if (!disabled) {
setChecked(!checked);
checkedItemHandler(chainItem, !checked);
}
};
useEffect(() => {
if (!disabled) {
setChecked(checkedItems.has(chainItem));
}
}, [checkedItems]);
const { chainInfo } = props;
return (
<ChainItemContainer
key={chainItem.prefix}
isLoading={false}
disabled={disabled}
checked={checked}
onClick={checkHandler}
>
<ChainImage
src={chainItem.chainImageUrl}
fill={true}
alt={`${chainItem.prefix} chain image`}
/>
<ChainItemContainer key={chainInfo.prefix} isLoading={false}>
<ChainImageContainer width="3rem" height="3rem">
<Image
src={chainInfo.chainImageUrl}
fill={true}
alt={`${chainInfo.prefix} chain image`}
/>
</ChainImageContainer>
<ChainInfoContainer>
<ChainName>{`.${chainItem.prefix}`}</ChainName>
<WalletAddress>{chainItem.address}</WalletAddress>
<ChainName>{`.${chainInfo.prefix}`}</ChainName>
<WalletAddress>{chainInfo.address}</WalletAddress>
</ChainInfoContainer>
<Flex1 />
<Checkbox checked={checked} />
<ChainCheckBox />
</ChainItemContainer>
);
};
export const ChainItemContainer = styled.div<{
isLoading: boolean;
checked?: boolean;
disabled?: boolean;
isSkeleton?: boolean;
}>`
display: flex;
flex-direction: row;
align-items: center;
gap: 1rem;
padding: 1.5rem;
cursor: pointer;
opacity: ${(props) => (props.disabled ? "0.5" : "1")};
background-color: ${(props) =>
props.disabled
? color.black
: props.checked
? color.grey["800"]
: props.isSkeleton
? color.grey["800"]
: color.grey["900"]};
&:hover {
background: ${(props) => (props.isLoading ? null : color.grey["700"])};
}
`;
export const ChainInfoContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.5rem;
`;
export const ChainName = styled.div`
const ChainName = styled.div`
font-weight: 600;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.white};
color: ${color.grey["100"]};
`;
export const WalletAddress = styled.div`
const WalletAddress = styled.div`
font-weight: 500;
font-size: 0.8rem;
line-height: 1rem;
max-height: 2rem;
max-width: 27rem;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
color: ${color.grey["400"]};
`;
const ChainCheckBox = styled.input.attrs({ type: "checkbox" })`
width: 1.5rem;
height: 1.5rem;
`;
+37 -40
View File
@@ -1,49 +1,19 @@
import { Dispatch, FunctionComponent, SetStateAction, useEffect } from "react";
import { ChainItemType } from "../../types";
import { FunctionComponent } from "react";
import { AccountInfo, WidthHeightProps } from "../../types";
import color from "../../styles/color";
import styled from "styled-components";
import { ChainItem } from "./chain-item";
interface Props {
chainList: ChainItemType[];
disabledChainList: ChainItemType[];
checkedItems: Set<unknown>;
setCheckedItems: Dispatch<SetStateAction<Set<unknown>>>;
chainList: AccountInfo[];
}
export const ChainList: FunctionComponent<Props> = (props) => {
const { chainList, disabledChainList, checkedItems, setCheckedItems } = props;
const checkedItemHandler = (chainItem: ChainItemType, isChecked: boolean) => {
const tempSet = new Set(checkedItems);
if (isChecked) {
tempSet.add(chainItem);
} else if (!isChecked && checkedItems.has(chainItem)) {
tempSet.delete(chainItem);
}
setCheckedItems(tempSet);
};
const { chainList } = props;
return (
<ChainContainer color={color.grey["900"]}>
{chainList.map((chainItem) => (
<ChainItem
key={chainItem.address}
chainItem={chainItem}
checkedItemHandler={checkedItemHandler}
checkedItems={checkedItems}
/>
))}
{disabledChainList.map((chainItem) => (
<ChainItem
key={chainItem.address}
chainItem={chainItem}
checkedItemHandler={checkedItemHandler}
checkedItems={checkedItems}
disabled={true}
/>
<ChainContainer color={color.grey["800"]}>
{chainList.map((chainInfo) => (
<ChainItem key={chainInfo.prefix} chainInfo={chainInfo} />
))}
</ChainContainer>
);
@@ -53,10 +23,37 @@ export const ChainContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
//max-height: 33rem;
max-height: 33rem;
overflow: scroll;
flex: 1;
background-color: ${(props) => props.color};
`;
export const ChainItemContainer = styled.div<{ isLoading: boolean }>`
display: flex;
flex-direction: row;
align-items: center;
gap: 1rem;
padding: 1.5rem;
cursor: pointer;
&:hover {
background: ${(props) => (props.isLoading ? null : color.grey["700"])};
}
`;
export const ChainImageContainer = styled.div<WidthHeightProps>`
width: ${(props) => props.width};
height: ${(props) => props.height};
position: relative;
`;
export const ChainInfoContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.5rem;
`;
-70
View File
@@ -1,70 +0,0 @@
import { FunctionComponent } from "react";
import color from "../../styles/color";
import styled from "styled-components";
export const Checkbox: FunctionComponent<{ checked: boolean }> = ({
checked,
}) => {
return (
<ChainCheckBoxContainer>
<ChainCheckBoxHidden checked={checked} readOnly={true} />
<ChainCheckBoxStyled checked={checked}>
<Center>
<Icon width="18" height="14" viewBox="0 0 15 12">
<path
strokeLinecap="square"
strokeWidth="2"
d="M2.25 4.426l4.083 5.011 6.417-7.874"
/>
</Icon>
</Center>
</ChainCheckBoxStyled>
</ChainCheckBoxContainer>
);
};
const Center = styled.div`
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
`;
const Icon = styled.svg`
fill: none;
stroke: ${color.orange["50"]};
`;
const ChainCheckBoxContainer = styled.div`
position: relative;
display: inline-block;
vertical-align: middle;
`;
const ChainCheckBoxStyled = styled.div<{ checked: boolean }>`
display: inline-block;
width: 1.5rem;
height: 1.5rem;
border: ${(props) => (props.checked ? "none" : "2px solid #868686")};
background: ${(props) =>
props.checked ? color.orange["200"] : color.grey["400"]};
${Icon} {
visibility: ${(props) => (props.checked ? "visible" : "hidden")};
}
`;
const ChainCheckBoxHidden = styled.input.attrs({ type: "checkbox" })`
border: 0;
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
`;
+74 -2
View File
@@ -1,2 +1,74 @@
export * from "./modal";
export * from "./wallet-item";
import { FunctionComponent } from "react";
import ReactModal from "react-modal";
import styled from "styled-components";
import color from "../../styles/color";
import { WalletList } from "../../constants/wallet";
import { WalletItem } from "./wallet-item";
interface Props {
isModalOpen: boolean;
onCloseModal: () => void;
}
export const ConnectWalletModal: FunctionComponent<Props> = (props) => {
const { isModalOpen, onCloseModal } = props;
return (
<ReactModal
isOpen={isModalOpen}
onRequestClose={onCloseModal}
style={{
overlay: { background: "#181818b3" },
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
padding: 0,
marginRight: "-50%",
transform: "translate(-50%, -50%)",
background: color.grey["800"],
border: 0,
},
}}
>
<ModalContainer>
<ModalTitle>Connect Wallet</ModalTitle>
<ModalDescription>
Plz check which account is selected after you connect it
</ModalDescription>
{WalletList.map((walletItem) => {
return <WalletItem wallet={walletItem} key={walletItem.name} />;
})}
</ModalContainer>
</ReactModal>
);
};
const ModalContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.9rem;
padding: 2.2rem;
`;
const ModalTitle = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 600;
font-size: 1.5rem;
line-height: 1.8rem;
color: ${color.white};
`;
const ModalDescription = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 500;
font-size: 0.8rem;
line-height: 1.1rem;
color: ${color.grey["400"]};
`;
+31 -10
View File
@@ -1,9 +1,7 @@
import { FunctionComponent } from "react";
import ReactModal from "react-modal";
import { FunctionComponent } from "react";
import styled from "styled-components";
import color from "../../styles/color";
import { WalletList } from "../../constants/wallet";
import { WalletItem } from "./wallet-item";
interface Props {
isModalOpen: boolean;
@@ -17,9 +15,8 @@ export const ConnectWalletModal: FunctionComponent<Props> = (props) => {
<ReactModal
isOpen={isModalOpen}
onRequestClose={onCloseModal}
ariaHideApp={false}
style={{
overlay: { background: "#121212cc" },
overlay: { background: "#181818b3" },
content: {
top: "50%",
left: "50%",
@@ -39,9 +36,35 @@ export const ConnectWalletModal: FunctionComponent<Props> = (props) => {
Plz check which account is selected after you connect it
</ModalDescription>
{WalletList.map((walletItem) => {
return <WalletItem wallet={walletItem} key={walletItem.name} />;
})}
{/*<ChainConnectItem>*/}
{/* <ChainConnectIcon>*/}
{/* <Image src={KeplrIcon} fill={true} alt="Keplr Icon" />*/}
{/* </ChainConnectIcon>*/}
{/* <ChainConnectContentContainer>*/}
{/* <ChainConnectContentTitle>Keplr</ChainConnectContentTitle>*/}
{/* </ChainConnectContentContainer>*/}
{/* <Flex1 />*/}
{/* <Image src={ArrowRightIcon} alt="arrow right icon" />*/}
{/*</ChainConnectItem>*/}
{/*<ChainConnectItem>*/}
{/* <ChainConnectIcon>*/}
{/* <Image src={CosmostationIcon} fill={true} alt="Cosmostation Icon" />*/}
{/* </ChainConnectIcon>*/}
{/* <ChainConnectContentContainer>*/}
{/* <ChainConnectContentTitle>Cosmostation</ChainConnectContentTitle>*/}
{/* <ChainConnectContentDescription>*/}
{/* Coming soon*/}
{/* </ChainConnectContentDescription>*/}
{/* </ChainConnectContentContainer>*/}
{/* <Flex1 />*/}
{/* <Image src={ArrowRightIcon} alt="arrow right icon" />*/}
{/*</ChainConnectItem>*/}
</ModalContainer>
</ReactModal>
);
@@ -52,8 +75,6 @@ const ModalContainer = styled.div`
flex-direction: column;
gap: 0.9rem;
padding: 2.2rem;
min-width: 28rem;
`;
const ModalTitle = styled.div`
@@ -1,22 +1,11 @@
import { FunctionComponent, useEffect, useState } from "react";
import { FunctionComponent } from "react";
import ArrowRightIcon from "../../public/images/svg/arrow-right.svg";
import color from "../../styles/color";
import { Flex1 } from "../../styles/flex-1";
import styled from "styled-components";
import Image from "next/image";
import {
MINIMUM_VERSION,
SELECTED_WALLET_KEY,
WALLET_INSTALL_URL,
WalletType,
} from "../../constants/wallet";
import { getKeplrFromWindow, KeplrWallet } from "../../wallets";
import { loginWithTwitter } from "../../queries";
import {
KEPLR_NOT_FOUND_ERROR,
KEPLR_VERSION_ERROR,
} from "../../constants/error-message";
import semver from "semver/preload";
import { WalletType } from "../../constants/wallet";
import { TwitterAuthUrlResponse } from "../../types";
interface Props {
wallet: WalletType;
@@ -24,64 +13,23 @@ interface Props {
export const WalletItem: FunctionComponent<Props> = (props: Props) => {
const { wallet } = props;
const [isInstalled, setIsInstalled] = useState<boolean>();
useEffect(() => {
setIsInstalled(!!window.keplr);
}, []);
const onClickWalletItem = async () => {
try {
if (wallet.name === "Keplr") {
await connectKeplr();
localStorage.setItem(SELECTED_WALLET_KEY, wallet.name);
}
const { authUrl }: TwitterAuthUrlResponse = await (
await fetch("/api/twitter-auth-url")
).json();
await loginWithTwitter();
} catch (e) {
console.log(e);
}
};
const connectKeplr = async () => {
const keplr = await getKeplrFromWindow();
if (keplr === undefined) {
window.location.href = WALLET_INSTALL_URL;
throw new Error(KEPLR_NOT_FOUND_ERROR);
}
if (semver.lt(keplr.version, MINIMUM_VERSION)) {
throw new Error(KEPLR_VERSION_ERROR);
}
if (keplr) {
const wallet = new KeplrWallet(keplr);
const chainIds = (await wallet.getChainInfosWithoutEndpoints()).map(
(c) => c.chainId,
);
await wallet.init(chainIds);
}
window.location.href = authUrl;
};
return (
<WalletContainer isReady={wallet.isReady} onClick={onClickWalletItem}>
<WalletIcon>
<Image
src={wallet.image}
fill={true}
sizes="3.75rem"
alt="wallet Icon"
/>
<Image src={wallet.image} fill={true} alt="wallet Icon" />
</WalletIcon>
<WalletContentContainer>
<WalletName>{wallet.name}</WalletName>
{wallet.isReady ? (
isInstalled ? null : (
<WalletDescription>Go to install Keplr Extension</WalletDescription>
)
) : (
{wallet.isReady ? null : (
<WalletDescription>Comming soon</WalletDescription>
)}
</WalletContentContainer>
-40
View File
@@ -1,40 +0,0 @@
import Link from "next/link";
import React, { Component, ErrorInfo, ReactNode } from "react";
interface Props {
children?: ReactNode;
}
interface State {
hasError: boolean;
}
class ErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false,
};
public static getDerivedStateFromError(): State {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("Uncaught error:", error, errorInfo);
}
public render() {
if (this.state.hasError) {
return (
<h1>
Oops.. there is something wrong.
<br /> Please try again. <Link href="/">Go to home</Link>
</h1>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
+2 -21
View File
@@ -6,34 +6,15 @@ import Link from "next/link";
import LogoIcon from "../../public/images/svg/logo.svg";
// Styles
import styled from "styled-components";
import { LogoContainer } from "./styled";
import { FunctionComponent } from "react";
export const Logo: FunctionComponent = () => {
return (
<Link href="/">
<LogoContainer>
<Image
src={LogoIcon}
fill={true}
sizes="10rem"
alt="Home Logo"
priority
/>
<Image src={LogoIcon} fill={true} alt="Home Logo" />
</LogoContainer>
</Link>
);
};
export const LogoContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
position: absolute;
width: 10rem;
height: 5rem;
margin-top: 5rem;
margin-left: 5rem;
`;
+16
View File
@@ -0,0 +1,16 @@
// Styles
import styled from "styled-components";
export const LogoContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
position: absolute;
width: 160px;
height: 80px;
margin-top: 80px;
margin-left: 80px;
`;
+4 -8
View File
@@ -7,18 +7,16 @@ export const PrimaryButton = styled.button`
border: none;
background-color: ${color.orange["100"]};
padding: 11px 30px;
font-family: "Inter", serif;
font-style: normal;
font-weight: 600;
font-size: 1.25rem;
line-height: 1.25rem;
letter-spacing: 0.07em;
text-transform: uppercase;
font-size: 1.5rem;
line-height: 20px;
color: ${color.orange["50"]};
background-color: ${color.orange["100"]};
cursor: pointer;
@@ -28,8 +26,6 @@ export const PrimaryButton = styled.button`
}
&:disabled {
opacity: 0.5;
background-color: ${color.orange["300"]};
background-color: ${color.orange["200"]};
}
`;
-81
View File
@@ -1,81 +0,0 @@
import { Dispatch, FunctionComponent, SetStateAction } from "react";
import Image from "next/image";
import styled from "styled-components";
import color from "../../styles/color";
import SearchIcon from "../../public/images/svg/search-icon.svg";
interface Props {
searchValue: string;
setSearchValue: Dispatch<SetStateAction<string>>;
}
export const SearchInput: FunctionComponent<Props> = (props) => {
const { searchValue, setSearchValue } = props;
return (
<SearchContainer>
<SearchIconContainer>
<Image src={SearchIcon} fill={true} sizes="1.3rem" alt="search icon" />
</SearchIconContainer>
<SearchText
type="text"
placeholder="search"
value={searchValue}
onChange={(event) => {
setSearchValue(event.target.value);
}}
/>
</SearchContainer>
);
};
const SearchContainer = styled.div`
display: flex;
align-items: center;
gap: 0.625rem;
height: 2.3rem;
padding: 0.6rem 1.4rem;
border-radius: 3rem;
background-color: ${color.grey["700"]};
`;
const SearchIconContainer = styled.div`
position: relative;
width: 1.3rem;
height: 1.3rem;
`;
const SearchText = styled.input`
border: none;
color: ${color.white};
background-color: ${color.grey["700"]};
font-family: "Inter", serif;
font-style: normal;
font-weight: 500;
font-size: 1rem;
line-height: 1.2rem;
width: 5.5rem;
transition: width 0.2s ease-out;
::placeholder,
::-webkit-input-placeholder {
color: ${color.grey["400"]};
}
&:focus {
outline: none;
width: 10rem;
}
`;
+3 -3
View File
@@ -9,13 +9,13 @@ export const SkeletonAnimation = styled.div`
${color.grey["400"]},
transparent
),
linear-gradient(${color.grey["600"]}, ${color.grey["600"]}),
linear-gradient(${color.grey["500"]}, ${color.grey["500"]}),
radial-gradient(
38px circle at 19px 19px,
${color.grey["600"]} 50%,
${color.grey["500"]} 50%,
transparent 51%
),
linear-gradient(${color.grey["600"]}, ${color.grey["600"]});
linear-gradient(${color.grey["500"]}, ${color.grey["500"]});
background-repeat: no-repeat;
background-size: 315px 250px, 315px 180px, 100px 100px, 225px 30px;
background-position: -315px 0, 0 0, 0px 190px, 50px 195px;
+82 -61
View File
@@ -21,99 +21,120 @@ import {
} from "../twitter-profile";
import {
ChainContainer,
ChainImageContainer,
ChainInfoContainer,
ChainItemContainer,
} from "../chain-list";
export const SkeletonChainList: FunctionComponent = () => (
<ContentContainer>
<SkeletonContainer>
<ProfileContainer color={color.grey["800"]}>
<SkeletonCircle width="5.5rem" height="5.5rem" />
<ProfileContainer color={color.grey["700"]}>
<SkeletonCircle width="5.5rem" height="5.5rem" />
<ProfileContentContainer>
<ProfileNameContainer>
<SkeletonText width="5rem" height="1.5rem" />
</ProfileNameContainer>
<ProfileUserNameContainer>
<SkeletonText width="5rem" height="1rem" />
</ProfileUserNameContainer>
<ProfileContentContainer>
<ProfileNameContainer>
<SkeletonText width="5rem" height="1.5rem" />
</ProfileNameContainer>
<ProfileUserNameContainer>
<SkeletonText width="5rem" height="1rem" />
</ProfileUserNameContainer>
<ProfileFollowContainer>
<SkeletonText width="8rem" height="1rem" />
<SkeletonText width="8rem" height="1rem" />
</ProfileFollowContainer>
<ProfileFollowContainer>
<SkeletonText width="8rem" height="1rem" />
<SkeletonText width="8rem" height="1rem" />
</ProfileFollowContainer>
<SkeletonText width="20rem" height="1rem" />
</ProfileContentContainer>
</ProfileContainer>
</SkeletonContainer>
<SkeletonText width="20rem" height="1rem" />
</ProfileContentContainer>
</ProfileContainer>
<ChainListTitleContainer>
<SkeletonTitle />
</ChainListTitleContainer>
<ChainContainer color={color.grey["800"]}>
{Array(10)
.fill("")
.map((_, index) => (
<div key={index}>
<ChainItemContainer isLoading={true} isSkeleton={true}>
<SkeletonImageContainer>
<SkeletonCircle width="3rem" height="3rem" />
</SkeletonImageContainer>
<ChainInfoContainer>
<SkeletonText width="4rem" height="1rem" />
<SkeletonText width="12rem" height="1rem" />
</ChainInfoContainer>
</ChainItemContainer>
<ChainContainer color={color.grey["700"]}>
<ChainItemContainer isLoading={true}>
<ChainImageContainer width="3rem" height="3rem">
<SkeletonCircle width="3rem" height="3rem" />
</ChainImageContainer>
<ChainInfoContainer>
<SkeletonText width="4rem" height="1rem" />
<SkeletonText width="12rem" height="1rem" />
</ChainInfoContainer>
</ChainItemContainer>
<SkeletonDivider />
</div>
))}
<SkeletonDivider />
<ChainItemContainer isLoading={true}>
<ChainImageContainer width="3rem" height="3rem">
<SkeletonCircle width="3rem" height="3rem" />
</ChainImageContainer>
<ChainInfoContainer>
<SkeletonText width="4rem" height="1rem" />
<SkeletonText width="12rem" height="1rem" />
</ChainInfoContainer>
</ChainItemContainer>
<SkeletonDivider />
<ChainItemContainer isLoading={true}>
<ChainImageContainer width="3rem" height="3rem">
<SkeletonCircle width="3rem" height="3rem" />
</ChainImageContainer>
<ChainInfoContainer>
<SkeletonText width="4rem" height="1rem" />
<SkeletonText width="12rem" height="1rem" />
</ChainInfoContainer>
</ChainItemContainer>
<SkeletonDivider />
<ChainItemContainer isLoading={true}>
<ChainImageContainer width="3rem" height="3rem">
<SkeletonCircle width="3rem" height="3rem" />
</ChainImageContainer>
<ChainInfoContainer>
<SkeletonText width="4rem" height="1rem" />
<SkeletonText width="12rem" height="1rem" />
</ChainInfoContainer>
</ChainItemContainer>
<SkeletonDivider />
<ChainItemContainer isLoading={true}>
<ChainImageContainer width="3rem" height="3rem">
<SkeletonCircle width="3rem" height="3rem" />
</ChainImageContainer>
<ChainInfoContainer>
<SkeletonText width="4rem" height="1rem" />
<SkeletonText width="12rem" height="1rem" />
</ChainInfoContainer>
</ChainItemContainer>
<SkeletonDivider />
</ChainContainer>
<SkeletonButtonContainer>
<ButtonContainer>
<SkeletonButton />
</ButtonContainer>
</SkeletonButtonContainer>
<ButtonContainer>
<SkeletonButton />
</ButtonContainer>
</ContentContainer>
);
const SkeletonContainer = styled.div`
width: 100%;
padding-top: 2.4rem;
`;
const SkeletonTitle = styled.div`
width: 8rem;
height: 1.5rem;
background-color: ${color.grey["800"]};
`;
const SkeletonImageContainer = styled.div`
width: 3rem;
height: 3rem;
position: relative;
`;
const SkeletonButtonContainer = styled.div`
margin-top: 1.5rem;
background-color: ${color.grey["700"]};
`;
const SkeletonButton = styled.div`
width: 12rem;
height: 4rem;
padding-top: 1.5rem;
background-color: ${color.grey["800"]};
background-color: ${color.grey["700"]};
`;
const SkeletonDivider = styled(SkeletonAnimation)`
width: 100%;
height: 1px;
background-color: ${color.grey["600"]};
background-color: ${color.grey["500"]};
`;
+1 -1
View File
@@ -12,6 +12,6 @@ export const SkeletonCircle = styled(SkeletonAnimation)<WidthHeightProps>`
width: ${(props) => props.width};
height: ${(props) => props.height};
background-color: ${color.grey["600"]};
background-color: ${color.grey["500"]};
border-radius: 50%;
`;
+1 -7
View File
@@ -12,12 +12,11 @@ export const TwitterProfile: FunctionComponent<Props> = (props) => {
const { twitterProfileInformation } = props;
return (
<ProfileContainer color={color.grey["900"]}>
<ProfileContainer color={color.grey["800"]}>
<ProfileImageContainer>
<Image
src={twitterProfileInformation?.profile_image_url ?? ""}
fill={true}
sizes="5rem"
alt="twitter profile image"
/>
</ProfileImageContainer>
@@ -130,10 +129,5 @@ export const ProfileDescriptionContainer = styled.div`
font-size: 0.8rem;
line-height: 1rem;
max-width: 27.5rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: ${color.grey["100"]};
`;
+160
View File
@@ -0,0 +1,160 @@
import { AccountInfo } from "./types";
export const AccountInfos: AccountInfo[] = [
{
prefix: "cosmos",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/cosmoshub/chain.png",
},
{
prefix: "osmo",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/osmosis/chain.png",
},
{
prefix: "agoric",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/agoric/chain.png",
},
{
prefix: "akash",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/akashnet/chain.png",
},
{
prefix: "axelar",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/axelar-dojo/chain.png",
},
{
prefix: "bostrom",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/bostrom/chain.png",
},
{
prefix: "persistence",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/core/chain.png",
},
{
prefix: "cro",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/crypto-org-chain-mainnet/chain.png",
},
{
prefix: "emoney",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/emoney/chain.png",
},
{
prefix: "evmos",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/evmos_9001/chain.png",
},
{
prefix: "gravity",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/gravity-bridge/chain.png",
},
{
prefix: "ixo",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/impacthub/chain.png",
},
{
prefix: "star",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/iov-mainnet-ibc/chain.png",
},
{
prefix: "iaa",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/irishub/chain.png",
},
{
prefix: "juno",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/juno/chain.png",
},
{
prefix: "kava",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/kava_2222/chain.png",
},
{
prefix: "regen",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/regen/chain.png",
},
{
prefix: "secret",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/secret/chain.png",
},
{
prefix: "sent",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/sentinelhub/chain.png",
},
{
prefix: "certik",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/shentu-2.2/chain.png",
},
{
prefix: "sif",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/sifchain/chain.png",
},
{
prefix: "somm",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/sommelier/chain.png",
},
{
prefix: "stars",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/stargaze/chain.png",
},
{
prefix: "stride",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/stride/chain.png",
},
{
prefix: "tgrade",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/tgrade-mainnet/chain.png",
},
{
prefix: "umee",
address: "cosmos14ky6udatsvdx859050mrnr7rvml0huue2wszvs",
chainImageUrl:
"https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/umee/chain.png",
},
];
-5
View File
@@ -1,5 +0,0 @@
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";
export const KEPLR_VERSION_ERROR = "You should update keplr";
-18
View File
@@ -1,18 +0,0 @@
export const MainChainId = "osmo-test-4";
export const REFERRAL_KEY = "icns-referral";
export const RPC_URL =
process.env.OSMOSIS_RPC_URL ?? "https://rpc.testnet.osmosis.zone";
export const REST_URL =
process.env.OSMOSIS_REST_URL ?? "https://lcd.testnet.osmosis.zone";
export const NAME_NFT_ADDRESS =
process.env.ICNS_NAME_NFT_CONTRACT_ADDRESS ??
"osmo1xahnjn872smah6xle8n3z5a5teqq390qr959l805mkuw0kcy8g5smtdagg";
export const REGISTRAR_ADDRESS =
process.env.ICNS_REGISTRAR_CONTRACT_ADDRESS ??
"osmo1npn97g7hsgqlp70rw8nhd7c7vyvkukv9x0n25sn4fk5mgcjlz4gq9zlgf3";
export const RESOLVER_ADDRESS =
process.env.ICNS_RESOLVER_CONTRACT_ADDRESS ??
"osmo1002awr7frr9wk44lc3vfzt0d2w6vz5z03ql6fszjsjy8vdcvk0sskruz3c";
-2
View File
@@ -7,5 +7,3 @@ export const twitterOAuthScopes = [
];
export const twitterApiBaseUrl = "https://api.twitter.com/2";
export const SHARE_URL = "https://twitter.com/share";
+1 -14
View File
@@ -3,16 +3,8 @@ import { StaticImageData } from "next/image";
import KeplrIcon from "../public/images/svg/keplr-icon.svg";
import CosmostationIcon from "../public/images/svg/cosmostation-icon.svg";
export const WALLET_INSTALL_URL =
"https://chrome.google.com/webstore/detail/keplr/dmkamcknogkgcdfhhbddcghachkejeap";
export const SELECTED_WALLET_KEY = "SELECTED_WALLET_KEY";
export const MINIMUM_VERSION = "0.11.22";
export const MINIMUM_OSMO_FEE = process.env.MINIMUM_OSMO_FEE ?? "0.5 OSMO";
export type WalletName = "Keplr" | "Cosmostation";
export interface WalletType {
name: WalletName;
name: string;
image: StaticImageData;
isReady: boolean;
}
@@ -29,8 +21,3 @@ export const WalletList: WalletType[] = [
isReady: false,
},
];
export const ContractFee = {
denom: "uosmo",
amount: "500000",
};
-90
View File
@@ -1,90 +0,0 @@
import { CosmwasmExecuteMessageResult } from "../types";
import { makeCosmwasmExecMsg } from "../wallets";
import { REGISTRAR_ADDRESS, RESOLVER_ADDRESS } from "../constants/icns";
import { ContractFee } from "../constants/wallet";
export const makeClaimMessage = (
senderAddress: string,
twitterUserName: string,
verificationList: any[],
referral?: string,
): CosmwasmExecuteMessageResult => {
verificationList = verificationList.filter((verification) => {
if (verification.status !== "fulfilled") {
console.log("verification", verification);
}
return verification.status === "fulfilled";
});
if (verificationList.length === 0) {
throw new Error("Verifications rejected all");
}
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,
};
}
}),
referral,
},
},
[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(),
},
},
},
[],
);
};
export const makeRemoveRecordMessage = (
twitterUserName: string,
senderAddress: string,
removeAddress: string,
): CosmwasmExecuteMessageResult => {
return makeCosmwasmExecMsg(
senderAddress,
RESOLVER_ADDRESS,
{
remove_record: {
name: twitterUserName,
bech32_address: removeAddress,
},
},
[],
);
};
-1
View File
@@ -1 +0,0 @@
export * from "./icns";
-4
View File
@@ -15,10 +15,6 @@ const nextConfig = {
protocol: "https",
hostname: "raw.githubusercontent.com",
},
{
protocol: "https",
hostname: "abs.twimg.com",
},
],
},
};
-3
View File
@@ -23,8 +23,6 @@
"react-dom": "18.2.0",
"react-is": "^18.2.0",
"react-modal": "^3.16.1",
"react-typed": "^1.2.0",
"semver": "^7.3.8",
"styled-components": "^5.3.6"
},
"devDependencies": {
@@ -34,7 +32,6 @@
"@types/react": "18.0.25",
"@types/react-dom": "18.0.9",
"@types/react-modal": "^3",
"@types/semver": "^7",
"@types/styled-components": "^5",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
+5 -130
View File
@@ -1,138 +1,13 @@
import type { AppProps } from "next/app";
import Head from "next/head";
import { useRouter } from "next/router";
import React, { useMemo } from "react";
import { DefaultTheme, ThemeProvider } from "styled-components";
import ErrorBoundary from "../components/error-boundary";
import React from "react";
import { GlobalStyle } from "../styles/global";
const homePageTheme: DefaultTheme = {
bgColor: "rgba(18, 18, 18, 1)",
bgGridColor: "rgba(51, 51, 51, 1)",
};
const defaultPageTheme: DefaultTheme = {
bgColor: "rgba(18, 18, 18, 0.8)",
bgGridColor: "rgba(51, 51, 51, 0.3)",
};
export default function App({ Component, pageProps }: AppProps) {
const router = useRouter();
const pageTheme = useMemo(() => {
return router.pathname === "/" ? homePageTheme : defaultPageTheme;
}, [router.pathname]);
const origin = typeof window !== "undefined" ? window.location.origin : "";
return (
<ThemeProvider theme={pageTheme}>
<Head>
<title>Interchain Name Service</title>
<meta content="Interchain Name Service" property="og:title" />
<meta content="Interchain Name Service" property="twitter:title" />
<meta
content="Your identity for the Interchain. Claim yours today."
property="og:description"
/>
<meta
content="Your identity for the Interchain. Claim yours today."
property="twitter:description"
/>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<meta
content={`${origin}/images/og-image.webp`}
property="twitter:image"
/>
<meta content={`${origin}/images/og-image.webp`} property="og:image" />
<meta content="summary_large_image" name="twitter:card" />
<meta property="og:type" content="website" />
{/* generated favicons */}
<link
rel="apple-touch-icon"
sizes="57x57"
href="/images/favicon/apple-icon-57x57.png"
/>
<link
rel="apple-touch-icon"
sizes="60x60"
href="/images/favicon/apple-icon-60x60.png"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="/images/favicon/apple-icon-72x72.png"
/>
<link
rel="apple-touch-icon"
sizes="76x76"
href="/images/favicon/apple-icon-76x76.png"
/>
<link
rel="apple-touch-icon"
sizes="114x114"
href="/images/favicon/apple-icon-114x114.png"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="/images/favicon/apple-icon-120x120.png"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="/images/favicon/apple-icon-144x144.png"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="/images/favicon/apple-icon-152x152.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/images/favicon/apple-icon-180x180.png"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="/images/favicon/android-icon-192x192.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/images/favicon/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="/images/favicon/favicon-96x96.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/images/favicon/favicon-16x16.png"
/>
<link rel="manifest" href="/images/favicon/manifest.json" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta
name="msapplication-TileImage"
content="/images/favicon/ms-icon-144x144.png"
/>
<meta name="theme-color" content="#ffffff" />
</Head>
<React.Fragment>
<GlobalStyle />
<ErrorBoundary>
<Component {...pageProps} />
</ErrorBoundary>
</React.Fragment>
</ThemeProvider>
<React.Fragment>
<GlobalStyle />
<Component {...pageProps} />
</React.Fragment>
);
}
+11 -26
View File
@@ -5,39 +5,24 @@ import Document, {
Main,
NextScript,
} from "next/document";
import { ServerStyleSheet } from "styled-components";
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
// Run the React rendering logic synchronously
ctx.renderPage = () =>
originalRenderPage({
// Useful for wrapping the whole react tree
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
// Useful for wrapping in a per-page basis
enhanceComponent: (Component) => Component,
});
// Run the React rendering logic synchronously
ctx.renderPage = () =>
originalRenderPage({
// Useful for wrapping the whole react tree
enhanceApp: (App) => App,
// Useful for wrapping in a per-page basis
enhanceComponent: (Component) => Component,
});
// Run the parent `getInitialProps`, it now includes the custom `renderPage`
const initialProps = await Document.getInitialProps(ctx);
// Run the parent `getInitialProps`, it now includes the custom `renderPage`
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<div>
{initialProps.styles}
{sheet.getStyleElement()}
</div>
),
};
} finally {
sheet.seal();
}
return initialProps;
}
render() {
+1 -1
View File
@@ -1,7 +1,7 @@
import { withIronSessionApiRoute } from "iron-session/next";
import type { NextApiRequest, NextApiResponse } from "next";
import { ironOptions } from "../../iron.config";
import { IcnsVerificationResponse } from "../../types";
import { IcnsVerificationResponse } from "../../types/api-response";
import { request } from "../../utils/url";
export default withIronSessionApiRoute(async function handler(
-271
View File
@@ -1,271 +0,0 @@
import Image from "next/image";
import styled from "styled-components";
import Typed from "react-typed";
import { Logo } from "../../components/logo";
import color from "../../styles/color";
import AlertCircleOutlineIcon from "../../public/images/svg/alert-circle-outline.svg";
import TwitterIcon from "../../public/images/svg/twitter-icon.svg";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { TendermintTxTracer } from "@keplr-wallet/cosmos";
import { queryAddressesFromTwitterName } from "../../queries";
import { RegisteredAddresses } from "../../types";
import { SHARE_URL } from "../../constants/twitter";
export default function CompletePage() {
const router = useRouter();
const [registeredAddressed, setRegisteredAddressed] =
useState<RegisteredAddresses[]>();
const [availableAddress, setAvailableAddress] = useState("");
const [isSuccess, setIsSuccess] = useState<boolean>(false);
useEffect(() => {
const { txHash, twitterUsername } = router.query;
if (txHash && twitterUsername) {
initialize(txHash as string, twitterUsername as string);
}
}, [router.query]);
const initialize = async (txHash: string, twitterUserName: string) => {
const txTracer = new TendermintTxTracer(
"https://rpc.testnet.osmosis.zone",
"/websocket",
);
try {
const result: { code?: number } = await txTracer.traceTx(
Buffer.from(txHash, "hex"),
);
if (!result.code || result.code === 0) {
const addresses = await queryAddressesFromTwitterName(twitterUserName);
setRegisteredAddressed(addresses.data.addresses);
setIsSuccess(true);
}
} catch (e) {
console.log("error", e);
}
};
const onClickShareButton = () => {
const { twitterUsername } = router.query;
const shareMessage = `👨‍🚀 To Interchain... And Beyond!%0a%0aHey frens, I just minted my name for the interchain on @icns_xyz: ${twitterUsername}%0a%0aClaim yours now ▶`;
const width = 500;
const height = 700;
window.open(
`${SHARE_URL}?url=https://app.icns.xyz?referral=${twitterUsername}/&text=${shareMessage}`,
"Share Twitter",
`top=${(window.screen.height - height) / 2}, left=${
(window.screen.width - width) / 2
}, width=${width}, height=${height}, status=no, menubar=no, toolbar=no, resizable=no`,
);
};
return (
<Container>
<Logo />
<MainContainer>
<MainTitle>
{isSuccess && (
<Typed
strings={["Your Name is Active Now!"]}
stopped={!isSuccess}
typeSpeed={100}
/>
)}
</MainTitle>
<ContentContainer>
<RecipentContainer>
<RecipentTitle>Recipent</RecipentTitle>
<AddressContainer>
{`${router.query.twitterUsername}.`}
{registeredAddressed && (
<Typed
strings={registeredAddressed.map(
(address) => address.bech32_prefix,
)}
typeSpeed={150}
backSpeed={150}
backDelay={1000}
loop
smartBackspace
onStringTyped={(arrayPos: number) => {
setAvailableAddress(registeredAddressed[arrayPos].address);
}}
/>
)}
</AddressContainer>
<AvailableAddressText>{availableAddress}</AvailableAddressText>
</RecipentContainer>
</ContentContainer>
<DescriptionContainer>
<AlertIcon>
<Image src={AlertCircleOutlineIcon} fill={true} alt="alert icon" />
</AlertIcon>
<DescriptionText>
If you want to make that name address with same twitter account,
just go to home and start again.
</DescriptionText>
</DescriptionContainer>
{/*<ShareButtonContainer onClick={onClickShareButton}>*/}
{/* <ShareButtonText>SHARE MY NAME</ShareButtonText>*/}
{/* <Image src={TwitterIcon} alt="twitter icon" />*/}
{/*</ShareButtonContainer>*/}
</MainContainer>
</Container>
);
}
const Container = styled.div`
width: 100vw;
height: 100vh;
`;
const MainContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
padding-top: 10.1rem;
color: white;
`;
const MainTitle = styled.div`
display: flex;
align-items: center;
justify-content: center;
font-family: "Inter", serif;
font-style: normal;
font-weight: 700;
font-size: 2rem;
line-height: 2rem;
height: 5rem;
`;
const ContentContainer = styled.div`
width: 30rem;
padding: 2rem 2rem;
background-color: ${color.grey["900"]};
`;
const RecipentContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 2rem;
`;
const RecipentTitle = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 500;
font-size: 1rem;
line-height: 1rem;
color: ${color.grey["400"]};
`;
const AddressContainer = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 600;
font-size: 0.9rem;
line-height: 0.9rem;
color: ${color.white};
padding: 1rem;
background-color: ${color.grey["600"]};
`;
const AvailableAddressText = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 400;
font-size: 0.75rem;
line-height: 0.75rem;
min-height: 0.75rem;
color: ${color.blue};
`;
const DescriptionContainer = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: 1rem;
width: 30rem;
margin-top: 1.5rem;
padding: 1.25rem 2rem;
background-color: ${color.grey["900"]};
`;
const AlertIcon = styled.div`
position: relative;
width: 1.5rem;
height: 1.5rem;
`;
const DescriptionText = styled.div`
width: 100%;
font-family: "Inter", serif;
font-style: normal;
font-weight: 400;
font-size: 0.8rem;
line-height: 140%;
color: ${color.grey["400"]};
`;
const ShareButtonContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 0.625rem;
width: 20rem;
height: 5rem;
margin-top: 2.5rem;
cursor: pointer;
user-select: none;
background-color: ${color.grey["700"]};
`;
const ShareButtonText = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 700;
font-size: 1.25rem;
line-height: 1.25rem;
color: ${color.grey["100"]};
`;
+10 -92
View File
@@ -7,18 +7,14 @@ import color from "../styles/color";
// Components
import { PrimaryButton } from "../components/primary-button";
import { ConnectWalletModal } from "../components/connect-wallet-modal";
// Image Assets
import MainTitle from "../public/images/svg/main-title.svg";
import MainLogo from "../public/images/svg/main-logo.svg";
import CheckIcon from "../public/images/svg/check-icon.svg";
import StarIcon from "../public/images/svg/bg-asset-3.svg";
import { Logo } from "../components/logo";
import { useEffect, useState } from "react";
import { MINIMUM_OSMO_FEE, SELECTED_WALLET_KEY } from "../constants/wallet";
import { replaceToInstallPage } from "../utils/url";
import { REFERRAL_KEY } from "../constants/icns";
import { useState } from "react";
import { ConnectWalletModal } from "../components/connect-wallet-modal";
export default function Home() {
const [isModalOpen, setModalOpen] = useState(false);
@@ -27,21 +23,6 @@ export default function Home() {
setModalOpen(true);
};
useEffect(() => {
localStorage.removeItem(REFERRAL_KEY);
if (window.location.search) {
const [, referral] =
window.location.search.match(/^(?=.*referral=([^&]+)|).+$/) || [];
if (referral) {
localStorage.setItem(REFERRAL_KEY, referral);
}
}
localStorage.removeItem(SELECTED_WALLET_KEY);
}, []);
return (
<Container>
<Logo />
@@ -49,28 +30,8 @@ export default function Home() {
<MainContainer>
<MainTitleContainer>
<MainTitleImageBackground>
<Image
src={StarIcon}
width={50}
height={50}
alt="Star Icon"
className="starIcon first"
/>
<Image
src={StarIcon}
width={50}
height={50}
alt="Star Icon"
className="starIcon last"
/>
<MainTitleImageContainer>
<Image
src={MainTitle}
fill={true}
sizes="60rem"
alt="Main Title"
priority
/>
<Image src={MainTitle} fill={true} alt="Main Title" />
</MainTitleImageContainer>
</MainTitleImageBackground>
@@ -82,34 +43,23 @@ export default function Home() {
<SubContainer>
<CheckContainer>
<CheckIconContainer>
<Image
src={CheckIcon}
fill={true}
sizes="1.6rem"
alt="Check Icon"
/>
<Image src={CheckIcon} fill={true} alt="Check Icon" />
</CheckIconContainer>
You are a <CheckBoldText>&nbsp;keplr&nbsp;</CheckBoldText> user.
if not, you can install&nbsp;
<InstallLInk onClick={replaceToInstallPage}>HERE</InstallLInk>
if not, you can install here
</CheckContainer>
<CheckContainer>
<CheckIconContainer>
<Image
src={CheckIcon}
fill={true}
sizes="1.6rem"
alt="Check Icon"
/>
<Image src={CheckIcon} fill={true} alt="Check Icon" />
</CheckIconContainer>
<CheckBoldText>{MINIMUM_OSMO_FEE}&nbsp;</CheckBoldText> is
required for this transaction
<CheckBoldText>Osmo&nbsp;</CheckBoldText> is required for this
transaction
</CheckContainer>
</SubContainer>
</MainTitleContainer>
<MainLogoContainer>
<Image src={MainLogo} fill={true} sizes="25rem" alt="Main Logo" />
<Image src={MainLogo} layout="fixed" fill={true} alt="Main Logo" />
</MainLogoContainer>
</MainContainer>
@@ -127,15 +77,6 @@ const Container = styled.div`
width: 100vw;
height: 100vh;
background-image: url("/images/svg/bg-asset-1.svg"),
url("/images/svg/bg-asset-2.svg"), url("/images/svg/bg-asset-2.svg"),
url("/images/svg/bg-asset-3.svg"), url("/images/svg/bg-asset-3.svg");
background-size: 5rem 5rem, 5rem 5rem, 5rem 5rem, 3.125rem 3.125rem,
3.125rem 3.125rem;
background-position: 80px 640px, 960px 80px, 1200px 720px, 136px 776px,
1016px 696px;
background-repeat: no-repeat;
`;
const MainContainer = styled.div`
@@ -144,20 +85,6 @@ const MainContainer = styled.div`
margin-top: 15rem;
margin-left: 10rem;
img.starIcon {
position: absolute;
&.first {
left: -24px;
top: -24px;
}
&.last {
right: -23px;
top: -24px;
}
}
`;
const MainTitleContainer = styled.div`
@@ -234,16 +161,7 @@ const CheckContainer = styled.div`
text-transform: uppercase;
padding-left: 0.75rem;
color: ${color.grey["400"]};
`;
const InstallLInk = styled.a`
color: ${color.grey["400"]};
text-decoration: underline;
cursor: pointer;
color: ${color.grey["300"]};
`;
const CheckBoldText = styled.span`
+173 -461
View File
@@ -1,14 +1,12 @@
// React
import { useEffect, useState } from "react";
// NextJs
import Image from "next/image";
// Types
import {
ChainItemType,
QueryError,
RegisteredAddresses,
TwitterProfileType,
} from "../../types";
import { checkTwitterAuthQueryParameter } from "../../utils/url";
import { IcnsVerificationResponse, TwitterAuthInfoResponse } from "../../types";
import { request } from "../../utils/url";
// Styles
import styled from "styled-components";
@@ -19,340 +17,186 @@ import { Logo } from "../../components/logo";
import { SkeletonChainList } from "../../components/skeleton";
import { PrimaryButton } from "../../components/primary-button";
import { AccountInfos } from "../../config";
import { TwitterProfile } from "../../components/twitter-profile";
import { ChainList } from "../../components/chain-list";
import { useRouter } from "next/router";
import {
getKeplrFromWindow,
KeplrWallet,
makeCosmwasmExecMsg,
sendMsgs,
simulateMsgs,
} from "../../wallets";
import { ChainIdHelper } from "@keplr-wallet/cosmos";
import { AllChainsItem } from "../../components/chain-list/all-chains-item";
import { SearchInput } from "../../components/search-input";
import {
MainChainId,
REFERRAL_KEY,
RESOLVER_ADDRESS,
REST_URL,
} from "../../constants/icns";
import {
fetchTwitterInfo,
queryAddressesFromTwitterName,
queryOwnerOfTwitterName,
queryRegisteredTwitterId,
verifyTwitterAccount,
} from "../../queries";
import { ErrorHandler } from "../../utils/error";
import {
KEPLR_NOT_FOUND_ERROR,
TWITTER_LOGIN_ERROR,
} from "../../constants/error-message";
import {
makeClaimMessage,
makeRemoveRecordMessage,
makeSetRecordMessage,
} from "../../messages";
import Axios from "axios";
import { BackButton } from "../../components/back-button";
import { TendermintTxTracer } from "@keplr-wallet/cosmos";
import { Buffer } from "buffer/";
export default function VerificationPage() {
const router = useRouter();
const [twitterAuthInfo, setTwitterAuthInfo] = useState<TwitterProfileType>();
const [twitterAuthInfo, setTwitterAuthInfo] =
useState<TwitterAuthInfoResponse | null>();
const [isLoading, setIsLoading] = useState(true);
const [wallet, setWallet] = useState<KeplrWallet>();
const [chainList, setChainList] = useState<ChainItemType[]>([]);
const [disabledChainList, setDisabledChainList] = useState<ChainItemType[]>(
[],
);
const [registeredChainList, setRegisteredChainList] = useState<
RegisteredAddresses[]
>([]);
const [checkedItems, setCheckedItems] = useState(new Set());
const [searchValue, setSearchValue] = useState("");
const [isOwner, setIsOwner] = useState(false);
const [isAgree, setIsAgree] = useState(false);
useEffect(() => {
init();
}, []);
const handleVerification = async () => {
if (window.location.search) {
const [, state, code] =
window.location.search.match(
/^(?=.*state=([^&]+)|)(?=.*code=([^&]+)|).+$/,
) || [];
useEffect(() => {
if (wallet) {
window.addEventListener("keplr_keystorechange", async () => {
await init();
});
}
}, [wallet]);
useEffect(() => {
const disabledChainList = chainList.filter((chain) => {
for (const registeredChain of registeredChainList) {
if (
chain.prefix === registeredChain.bech32_prefix &&
chain.address === registeredChain.address
) {
return true;
}
}
return false;
});
const filteredChainList = chainList.filter(
(chain) => !disabledChainList.includes(chain),
);
setChainList(filteredChainList);
setDisabledChainList(disabledChainList);
setCheckedItems(new Set(filteredChainList));
}, [registeredChainList]);
useEffect(() => {
setCheckedItems(new Set(chainList));
}, [chainList]);
const init = async () => {
if (window.location.search) {
try {
const { state, code } = checkTwitterAuthQueryParameter(
window.location.search,
const newTwitterAuthInfo = await request<TwitterAuthInfoResponse>(
`/api/twitter-auth-info?state=${state}&code=${code}`,
);
// Initialize Wallet
const keplrWallet = await initWallet();
setTwitterAuthInfo(newTwitterAuthInfo);
// Fetch Twitter Profile
const twitterInfo = await fetchTwitterInfo(state, code);
await (async () => {
const keplr = await getKeplrFromWindow();
if (keplr) {
const wallet = new KeplrWallet(keplr);
// contract check registered
const registeredQueryResponse = await queryRegisteredTwitterId(
twitterInfo.id,
);
const mainChainId = "osmo-test-4";
const chainIds = (await wallet.getChainInfosWithoutEndpoints()).map(
(c) => c.chainId,
);
setTwitterAuthInfo({
...twitterInfo,
isRegistered: "data" in registeredQueryResponse,
});
await wallet.init(chainIds);
if ("data" in registeredQueryResponse) {
const ownerOfQueryResponse = await queryOwnerOfTwitterName(
registeredQueryResponse.data.name,
);
const key = await wallet.getKey(mainChainId);
const addressesQueryResponse = await queryAddressesFromTwitterName(
registeredQueryResponse.data.name,
);
const icnsVerificationList = (
await request<IcnsVerificationResponse>(
"/api/icns-verification",
{
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
claimer: key.bech32Address,
authToken: newTwitterAuthInfo.accessToken,
}),
},
)
).verificationList;
if (keplrWallet) {
const key = await keplrWallet.getKey(MainChainId);
setIsOwner(ownerOfQueryResponse.data.owner === key.bech32Address);
console.log(icnsVerificationList);
const registerMsg = makeCosmwasmExecMsg(
key.bech32Address,
"osmo1u3sm9029430ca7xqz5afx4n0d42mgs2w97syex23g3vz2m673hxsv905sn",
{
claim: {
name: newTwitterAuthInfo.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,
};
}
}),
},
},
[
{
denom: "uosmo",
amount: "500000",
},
],
);
const test = await wallet.signICNSAdr36(
mainChainId,
"osmo1hnjg39mu9r9tygy6zpypd25rhmh9726jm369uh7z0r6gjkmnk5zs3tdtug",
key.bech32Address,
newTwitterAuthInfo.username,
chainIds,
);
const addressMsgs = test.map((adr36Info) => {
console.log(adr36Info);
return makeCosmwasmExecMsg(
key.bech32Address,
"osmo1hnjg39mu9r9tygy6zpypd25rhmh9726jm369uh7z0r6gjkmnk5zs3tdtug",
{
set_record: {
name: newTwitterAuthInfo.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(),
},
},
},
[],
);
});
const aminoMsgs = [registerMsg.amino];
const protoMsgs = [registerMsg.proto];
for (const addressMsg of addressMsgs) {
aminoMsgs.push(addressMsg.amino);
protoMsgs.push(addressMsg.proto);
}
const chainInfo = {
chainId: mainChainId,
rest: "https://lcd.testnet.osmosis.zone",
};
const simulated = await simulateMsgs(
chainInfo,
key.bech32Address,
{
proto: protoMsgs,
},
{
amount: [],
},
);
const txHash = await sendMsgs(
wallet,
chainInfo,
key.bech32Address,
{
amino: aminoMsgs,
proto: protoMsgs,
},
{
amount: [],
gas: Math.floor(simulated.gasUsed * 1.5).toString(),
},
);
const txTracer = new TendermintTxTracer(
"https://rpc.testnet.osmosis.zone",
"/websocket",
);
const result = await txTracer.traceTx(txHash);
console.log(result);
}
})();
setRegisteredChainList(addressesQueryResponse.data.addresses);
}
} catch (error) {
if (error instanceof Error && error.message === TWITTER_LOGIN_ERROR) {
await router.push("/");
}
console.error(error);
} finally {
setIsLoading(false);
}
}
};
};
const initWallet = async () => {
const keplr = await getKeplrFromWindow();
if (keplr) {
const keplrWallet = new KeplrWallet(keplr);
await fetchChainList(keplrWallet);
setWallet(keplrWallet);
return keplrWallet;
} else {
ErrorHandler(KEPLR_NOT_FOUND_ERROR);
}
};
const fetchChainList = async (wallet: KeplrWallet) => {
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,
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`,
};
},
);
const chainArray = [];
for (let i = 0; i < chainKeys.length; i++) {
chainArray.push({
address: chainKeys[i].bech32Address,
...chainInfos[i],
});
}
// remove duplicated item
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;
})
);
});
setChainList(filteredChainList);
};
const checkAdr36 = async () => {
if (twitterAuthInfo && wallet) {
const key = await wallet.getKey(MainChainId);
const chainIds = Array.from(checkedItems).map((chain) => {
return (chain as ChainItemType).chainId;
});
return wallet.signICNSAdr36(
MainChainId,
RESOLVER_ADDRESS,
key.bech32Address,
twitterAuthInfo.username,
chainIds,
);
}
};
const onClickRegistration = async () => {
try {
const { state, code } = checkTwitterAuthQueryParameter(
window.location.search,
);
const twitterInfo = await fetchTwitterInfo(state, code);
const adr36Infos = await checkAdr36();
if (wallet && adr36Infos) {
const key = await wallet.getKey(MainChainId);
const icnsVerificationList = await verifyTwitterAccount(
key.bech32Address,
twitterInfo.accessToken,
);
const registerMsg = makeClaimMessage(
key.bech32Address,
twitterInfo.username,
icnsVerificationList,
localStorage.getItem(REFERRAL_KEY) ?? undefined,
);
const addressMsgs = adr36Infos.map((adr36Info) => {
return makeSetRecordMessage(
key.bech32Address,
twitterInfo.username,
adr36Info,
);
});
const aminoMsgs = twitterAuthInfo?.isRegistered
? []
: [registerMsg.amino];
const protoMsgs = twitterAuthInfo?.isRegistered
? []
: [registerMsg.proto];
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: [],
},
);
const txHash = await sendMsgs(
wallet,
chainInfo,
key.bech32Address,
{
amino: aminoMsgs,
proto: protoMsgs,
},
{
amount: [],
gas: Math.floor(simulated.gasUsed * 1.5).toString(),
},
);
await router.push({
pathname: "complete",
query: {
txHash: Buffer.from(txHash).toString("hex"),
twitterUsername: twitterInfo.username,
},
});
}
} catch (error) {
if (Axios.isAxiosError(error)) {
console.error((error?.response?.data as QueryError).message);
}
}
};
const isRegisterButtonDisable = (() => {
const hasCheckedItem = checkedItems.size > 0;
if (isOwner) {
return !hasCheckedItem;
} else {
return !(isAgree && hasCheckedItem);
}
})();
handleVerification();
}, []);
return (
<Container>
@@ -363,124 +207,18 @@ export default function VerificationPage() {
<SkeletonChainList />
) : (
<ContentContainer>
<BackButton />
<div>
<button
onClick={async () => {
if (twitterAuthInfo && wallet) {
const key = await wallet.getKey(MainChainId);
const removeMessages = registeredChainList.map((chain) => {
return makeRemoveRecordMessage(
twitterAuthInfo.username,
key.bech32Address,
chain.address,
);
});
const aminoMsgs = [];
const protoMsgs = [];
for (const msg of removeMessages) {
aminoMsgs.push(msg.amino);
protoMsgs.push(msg.proto);
}
console.log(aminoMsgs);
const chainInfo = {
chainId: MainChainId,
rest: REST_URL,
};
const simulated = await simulateMsgs(
chainInfo,
key.bech32Address,
{
proto: protoMsgs,
},
{
amount: [],
},
);
const txHash = await sendMsgs(
wallet,
chainInfo,
key.bech32Address,
{
amino: aminoMsgs,
proto: protoMsgs,
},
{
amount: [],
gas: Math.floor(simulated.gasUsed * 1.5).toString(),
},
);
console.log(txHash);
}
}}
>
All Remove
</button>
</div>
<TwitterProfile twitterProfileInformation={twitterAuthInfo} />
<ChainListTitleContainer>
<ChainListTitle>Chain List</ChainListTitle>
<SearchInput
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
<SearchContainer>Search</SearchContainer>
</ChainListTitleContainer>
{!searchValue ? (
<AllChainsItem
chainList={chainList}
checkedItems={checkedItems}
setCheckedItems={setCheckedItems}
/>
) : null}
<ChainList chainList={AccountInfos} />
<ChainList
chainList={chainList.filter(
(chain) =>
chain.chainId.includes(searchValue) ||
chain.address.includes(searchValue) ||
chain.prefix.includes(searchValue),
)}
disabledChainList={disabledChainList.filter(
(chain) =>
chain.chainId.includes(searchValue) ||
chain.address.includes(searchValue) ||
chain.prefix.includes(searchValue),
)}
checkedItems={checkedItems}
setCheckedItems={setCheckedItems}
/>
{!isOwner && (
<AgreeContainer
onClick={() => {
setIsAgree(!isAgree);
}}
>
<AgreeCheckBox type="checkbox" checked={isAgree} readOnly />I
check that Osmo is required for this transaction
</AgreeContainer>
)}
{chainList.length > 0 && (
<ButtonContainer disabled={isRegisterButtonDisable}>
<PrimaryButton
disabled={isRegisterButtonDisable}
onClick={onClickRegistration}
>
Register
</PrimaryButton>
</ButtonContainer>
)}
<ButtonContainer>
<PrimaryButton>Register</PrimaryButton>
</ButtonContainer>
</ContentContainer>
)}
</MainContainer>
@@ -497,18 +235,7 @@ const MainContainer = styled.div`
display: flex;
justify-content: center;
height: 100vh;
padding: 2.7rem 0;
color: white;
background-image: url("/images/svg/bg-asset-3.svg"),
url("/images/svg/bg-asset-3.svg"), url("/images/svg/bg-asset-3.svg"),
url("/images/svg/bg-asset-3.svg");
background-size: 3.125rem 3.125rem;
background-position: 296px 536px, 1256px 216px, 376px 776px, 1176px 856px;
background-repeat: no-repeat;
`;
export const ContentContainer = styled.div`
@@ -517,14 +244,15 @@ export const ContentContainer = styled.div`
align-items: center;
width: 40rem;
margin-top: 5rem;
`;
export const ButtonContainer = styled.div<{ disabled?: boolean }>`
width: 11rem;
height: 3.5rem;
export const ButtonContainer = styled.div`
width: 12rem;
height: 4rem;
background-color: ${(props) =>
props.disabled ? color.orange["300"] : color.orange["100"]};
margin-top: 2rem;
`;
export const ChainListTitleContainer = styled.div`
@@ -547,30 +275,14 @@ const ChainListTitle = styled.div`
color: ${color.white};
`;
const AgreeContainer = styled.div`
const SearchContainer = styled.div`
display: flex;
align-items: center;
gap: 0.5rem;
font-family: "Inter", serif;
font-style: normal;
font-weight: 500;
font-size: 0.8rem;
line-height: 0.8rem;
border-radius: 3rem;
text-transform: uppercase;
user-select: none;
min-width: 10rem;
height: 2rem;
color: ${color.grey["400"]};
padding: 2rem 0;
cursor: pointer;
`;
const AgreeCheckBox = styled.input.attrs({ type: "checkbox" })`
width: 1.2rem;
height: 1.2rem;
accent-color: ${color.orange["200"]};
background-color: ${color.grey["700"]};
`;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

-2
View File
@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

-41
View File
@@ -1,41 +0,0 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

@@ -1,5 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 12C21 7.03125 16.9688 3 12 3C7.03125 3 3 7.03125 3 12C3 16.9688 7.03125 21 12 21C16.9688 21 21 16.9688 21 12Z" stroke="#64646D" stroke-width="1.5" stroke-miterlimit="10"/>
<path d="M11.0007 8.03664L11.803 13.2223C11.838 13.4482 12.1634 13.4482 12.1983 13.2223L12.999 8.04535C13.0051 7.90953 12.9835 7.77388 12.9355 7.64669C12.8875 7.5195 12.814 7.40344 12.7196 7.3056C12.6252 7.20777 12.5118 7.13021 12.3864 7.07767C12.261 7.02513 12.1262 6.99872 11.9902 7.00005C11.8567 7.00135 11.7247 7.02941 11.6021 7.08256C11.4796 7.13571 11.3689 7.21287 11.2767 7.3095C11.1844 7.40612 11.1125 7.52025 11.0651 7.64514C11.0177 7.77003 10.9958 7.90315 11.0007 8.03664Z" fill="#64646D" stroke="#64646D" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 17C11.8022 17 11.6089 16.9414 11.4444 16.8315C11.28 16.7216 11.1518 16.5654 11.0761 16.3827C11.0004 16.2 10.9806 15.9989 11.0192 15.8049C11.0578 15.6109 11.153 15.4327 11.2929 15.2929C11.4327 15.153 11.6109 15.0578 11.8049 15.0192C11.9989 14.9806 12.2 15.0004 12.3827 15.0761C12.5654 15.1518 12.7216 15.28 12.8315 15.4444C12.9414 15.6089 13 15.8022 13 16C13 16.2652 12.8947 16.5196 12.7071 16.7071C12.5196 16.8946 12.2652 17 12 17Z" fill="#64646D"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 646 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.57812 14.0625L3.51562 9L8.57812 3.9375M4.21875 9H14.4844" stroke="#5B5B5B" stroke-width="1.5" stroke-linecap="square"/>
</svg>

Before

Width:  |  Height:  |  Size: 235 B

-4
View File
@@ -1,4 +0,0 @@
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0C-9.27438e-07 10.6087 4.21427 20.7828 11.7157 28.2843C19.2172 35.7857 29.3913 40 40 40C50.6087 40 60.7828 35.7857 68.2843 28.2843C75.7857 20.7828 80 10.6087 80 1.30337e-05L0 0Z" fill="#242424"/>
<path d="M0 40C-9.27438e-07 50.6087 4.21427 60.7828 11.7157 68.2843C19.2172 75.7857 29.3913 80 40 80C50.6087 80 60.7828 75.7857 68.2843 68.2843C75.7857 60.7828 80 50.6087 80 40L0 40Z" fill="#242424"/>
</svg>

Before

Width:  |  Height:  |  Size: 512 B

-4
View File
@@ -1,4 +0,0 @@
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 80C10.6087 80 20.7828 75.7857 28.2843 68.2843C35.7857 60.7828 40 50.6087 40 40C40 29.3913 35.7857 19.2172 28.2843 11.7157C20.7828 4.21428 10.6087 -4.6372e-07 9.53674e-06 0L0 80Z" fill="#242424"/>
<path d="M40 80C50.6087 80 60.7828 75.7857 68.2843 68.2843C75.7857 60.7828 80 50.6087 80 40C80 29.3913 75.7857 19.2172 68.2843 11.7157C60.7828 4.21428 50.6087 -4.6372e-07 40 0L40 80Z" fill="#242424"/>
</svg>

Before

Width:  |  Height:  |  Size: 512 B

-3
View File
@@ -1,3 +0,0 @@
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25 0L25.3504 2.74704C26.8096 14.1848 35.8152 23.1904 47.253 24.6496L50 25L47.253 25.3504C35.8152 26.8096 26.8096 35.8152 25.3504 47.253L25 50L24.6496 47.253C23.1904 35.8152 14.1848 26.8096 2.74704 25.3504L0 25L2.74704 24.6496C14.1848 23.1904 23.1904 14.1848 24.6496 2.74704L25 0Z" fill="#2B2B2B"/>
</svg>

Before

Width:  |  Height:  |  Size: 411 B

-3
View File
@@ -1,3 +0,0 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.08358 3.5C5.9967 3.5 3.5 5.93947 3.5 8.95559C3.5 11.9717 5.9967 14.4112 9.08358 14.4112C10.1857 14.4112 11.2056 14.0953 12.0704 13.5587L16.1041 17.5L17.5 16.1361L13.5176 12.255C14.2335 11.3374 14.6672 10.2004 14.6672 8.95559C14.6672 5.93947 12.1705 3.5 9.08358 3.5ZM9.08358 4.78367C11.4481 4.78367 13.3534 6.64524 13.3534 8.95559C13.3534 11.2659 11.4481 13.1275 9.08358 13.1275C6.71902 13.1275 4.81378 11.2659 4.81378 8.95559C4.81378 6.64524 6.71902 4.78367 9.08358 4.78367Z" fill="#5B5B5B"/>
</svg>

Before

Width:  |  Height:  |  Size: 608 B

-3
View File
@@ -1,3 +0,0 @@
<svg width="28" height="29" viewBox="0 0 28 29" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.3502 7.84668C23.5894 8.18363 22.7712 8.40845 21.9081 8.51483C22.7856 7.99388 23.4606 7.163 23.7775 6.18148C22.9564 6.66275 22.0467 7.01638 21.079 7.20268C20.3062 6.3833 19.2039 5.875 17.9826 5.875C15.6366 5.875 13.7351 7.75985 13.7351 10.084C13.7351 10.4129 13.7736 10.7332 13.8461 11.0443C10.3167 10.8672 7.18641 9.1893 5.09226 6.6432C4.72426 7.26535 4.51726 7.99388 4.51726 8.76265C4.51726 10.2237 5.26534 11.51 6.40614 12.2679C5.70981 12.2466 5.05431 12.0528 4.48046 11.7423C4.48046 11.755 4.48046 11.7739 4.48046 11.7918C4.48046 13.8336 5.94499 15.5339 7.88619 15.9208C7.53141 16.018 7.15651 16.0721 6.77011 16.0721C6.49584 16.0721 6.22846 16.041 5.96971 15.9944C6.51021 17.6631 8.07824 18.8827 9.93606 18.9189C8.48246 20.0459 6.65166 20.7209 4.66101 20.7209C4.31716 20.7209 3.98079 20.7014 3.64844 20.6611C5.52926 21.852 7.76256 22.55 10.1597 22.55C17.9711 22.55 22.2445 16.1353 22.2445 10.5705C22.2445 10.3882 22.2388 10.2065 22.2301 10.0271C23.0639 9.43828 23.7826 8.69538 24.3502 7.84668Z" fill="#5081FF"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

-66
View File
@@ -1,66 +0,0 @@
import { request } from "../utils/url";
import {
NAME_NFT_ADDRESS,
REGISTRAR_ADDRESS,
RESOLVER_ADDRESS,
REST_URL,
} from "../constants/icns";
import { Buffer } from "buffer/";
import {
AddressesQueryResponse,
NameByTwitterIdQueryResponse,
OwnerOfQueryResponse,
QueryError,
} from "../types";
const getCosmwasmQueryUrl = (contractAddress: string, queryMsg: string) =>
`${REST_URL}/cosmwasm/wasm/v1/contract/${contractAddress}/smart/${queryMsg}`;
export const queryRegisteredTwitterId = async (
twitterId: string,
): Promise<NameByTwitterIdQueryResponse | QueryError> => {
const msg = {
name_by_twitter_id: { twitter_id: twitterId },
};
return request<NameByTwitterIdQueryResponse>(
getCosmwasmQueryUrl(
REGISTRAR_ADDRESS,
Buffer.from(JSON.stringify(msg)).toString("base64"),
),
{},
true,
);
};
export const queryAddressesFromTwitterName = async (
twitterUsername: string,
): Promise<AddressesQueryResponse> => {
const msg = {
addresses: { name: twitterUsername },
};
return request<AddressesQueryResponse>(
getCosmwasmQueryUrl(
RESOLVER_ADDRESS,
Buffer.from(JSON.stringify(msg)).toString("base64"),
),
);
};
export const queryOwnerOfTwitterName = async (
twitterUsername: string,
): Promise<OwnerOfQueryResponse> => {
const msg = {
owner_of: { token_id: twitterUsername },
};
return request<OwnerOfQueryResponse>(
getCosmwasmQueryUrl(
NAME_NFT_ADDRESS,
Buffer.from(JSON.stringify(msg)).toString("base64"),
),
{},
true,
);
};
-2
View File
@@ -1,2 +0,0 @@
export * from "./twitter";
export * from "./icns";
-41
View File
@@ -1,41 +0,0 @@
import {
IcnsVerificationResponse,
TwitterAuthInfoResponse,
TwitterAuthUrlResponse,
} from "../types";
import { request } from "../utils/url";
export const loginWithTwitter = async () => {
const { authUrl } = await request<TwitterAuthUrlResponse>(
"/api/twitter-auth-url",
);
window.location.href = authUrl;
};
export const fetchTwitterInfo = async (
state: string,
code: string,
): Promise<TwitterAuthInfoResponse> => {
return await request<TwitterAuthInfoResponse>(
`/api/twitter-auth-info?state=${state}&code=${code}`,
);
};
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;
};
+2 -5
View File
@@ -6,8 +6,6 @@ const orange = {
400: "#442924",
};
const blue = "#536EF8";
const white = "#FCFCFC";
const grey = {
100: "#A3A3A3",
@@ -16,8 +14,8 @@ const grey = {
400: "#5B5B5B",
500: "#424242",
600: "#333333",
700: "#222222",
800: "#1D1D1D",
700: "#2B2B2B",
800: "#242424",
900: "#181818",
};
const black = "#121212";
@@ -27,7 +25,6 @@ const color = {
white,
black,
orange,
blue,
};
export default color;
+9 -8
View File
@@ -8,19 +8,20 @@ export const GlobalStyle = createGlobalStyle`
font-family: 'Inter', sans-serif;
font-size: 16px;
background-color: ${({ theme }) => theme.bgColor ?? color.black};
background-color: ${color.black};
background-size: 5rem 5rem;
background-position: top left;
background-repeat: repeat;
background-image: linear-gradient(${({ theme }) =>
theme.bgGridColor ?? color.grey[600]} 0.1rem,
transparent 0.1rem),
linear-gradient(90deg, ${({ theme }) =>
theme.bgGridColor ?? color.grey[600]} 0.1rem, transparent 0.1rem);
background-image: linear-gradient(
rgba(51, 51, 51, 1) 0.1rem,
transparent 0.1rem
),
linear-gradient(90deg, rgba(51, 51, 51, 1) 0.1rem, transparent 0.1rem);
}
* {
box-sizing: border-box;
}
-8
View File
@@ -1,8 +0,0 @@
import "styled-components";
declare module "styled-components" {
export interface DefaultTheme {
bgColor: string;
bgGridColor: string;
}
}
+5
View File
@@ -0,0 +1,5 @@
export interface AccountInfo {
prefix: string;
chainImageUrl: string;
address: string;
}
-28
View File
@@ -37,31 +37,3 @@ export interface IcnsVerificationResponse {
}
)[];
}
export interface NameByTwitterIdQueryResponse {
data: {
name: string;
};
}
export interface AddressesQueryResponse {
data: {
addresses: RegisteredAddresses[];
};
}
export interface RegisteredAddresses {
address: string;
bech32_prefix: string;
}
export interface QueryError {
code: number;
message: string;
}
export interface OwnerOfQueryResponse {
data: {
owner: string;
};
}
-7
View File
@@ -1,7 +0,0 @@
export interface ChainItemType {
chainId: string;
chainName: string;
prefix: string;
chainImageUrl: string;
address: string;
}
-9
View File
@@ -1,9 +0,0 @@
import { Any } from "@keplr-wallet/proto-types/google/protobuf/any";
export interface CosmwasmExecuteMessageResult {
amino: {
readonly type: string;
readonly value: any;
};
proto: Any;
}
+1 -3
View File
@@ -1,6 +1,4 @@
export * from "./width-height-props";
export * from "./api-response";
export * from "./chain-item-type";
export * from "./account-info";
export * from "./msg";
export * from "./twitter";
export * from "./icns";
-10
View File
@@ -1,10 +0,0 @@
import { TwitterAuthInfoResponse } from "./api-response";
export interface TwitterProfileType extends TwitterAuthInfoResponse {
isRegistered: boolean;
}
export interface TwitterLoginSuccess {
code: string;
state: string;
}
-3
View File
@@ -1,3 +0,0 @@
export const ErrorHandler = (message: string) => {
console.error(message);
};
+1 -35
View File
@@ -1,21 +1,9 @@
import { TwitterLoginSuccess } from "../types";
import { TWITTER_LOGIN_ERROR } from "../constants/error-message";
import { WALLET_INSTALL_URL } from "../constants/wallet";
export function request<TResponse>(
url: string,
config: RequestInit = {},
isIgnore?: boolean,
): Promise<TResponse> {
return fetch(url, config)
.then((response) => {
if (!response.ok && !isIgnore) {
throw new Error(
`This is an HTTP error: The status is ${response.status} ${response.statusText}`,
);
}
return response.json();
})
.then((response) => response.json())
.then((data) => data as TResponse);
}
@@ -28,25 +16,3 @@ export function buildQueryString(query: Record<string, any>): 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,
};
};
export const replaceToInstallPage = () => {
window.location.href = WALLET_INSTALL_URL;
};
+7 -2
View File
@@ -15,7 +15,6 @@ 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,
@@ -190,7 +189,13 @@ export function makeCosmwasmExecMsg(
// eslint-disable-next-line @typescript-eslint/ban-types
obj: object,
funds: { readonly amount: string; readonly denom: string }[],
): CosmwasmExecuteMessageResult {
): {
amino: {
readonly type: string;
readonly value: any;
};
proto: Any;
} {
const amino = {
type: "wasm/MsgExecuteContract",
value: {
Vendored
+1 -3
View File
@@ -2,7 +2,5 @@ import { Window as KeplrWindow } from "@keplr-wallet/types";
declare global {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Window extends KeplrWindow {
cosmostation: any;
}
interface Window extends KeplrWindow {}
}
+2 -25
View File
@@ -1109,7 +1109,7 @@ __metadata:
languageName: node
linkType: hard
"@types/semver@npm:^7, @types/semver@npm:^7.3.12":
"@types/semver@npm:^7.3.12":
version: 7.3.13
resolution: "@types/semver@npm:7.3.13"
checksum: 00c0724d54757c2f4bc60b5032fe91cda6410e48689633d5f35ece8a0a66445e3e57fa1d6e07eb780f792e82ac542948ec4d0b76eb3484297b79bd18b8cf1cb0
@@ -3175,7 +3175,6 @@ __metadata:
"@types/react": 18.0.25
"@types/react-dom": 18.0.9
"@types/react-modal": ^3
"@types/semver": ^7
"@types/styled-components": ^5
"@typescript-eslint/eslint-plugin": ^5.45.0
"@typescript-eslint/parser": ^5.45.0
@@ -3199,8 +3198,6 @@ __metadata:
react-dom: 18.2.0
react-is: ^18.2.0
react-modal: ^3.16.1
react-typed: ^1.2.0
semver: ^7.3.8
styled-components: ^5.3.6
typescript: 4.9.3
languageName: unknown
@@ -4577,7 +4574,7 @@ __metadata:
languageName: node
linkType: hard
"prop-types@npm:^15.6.0, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
"prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
version: 15.8.1
resolution: "prop-types@npm:15.8.1"
dependencies:
@@ -4699,19 +4696,6 @@ __metadata:
languageName: node
linkType: hard
"react-typed@npm:^1.2.0":
version: 1.2.0
resolution: "react-typed@npm:1.2.0"
dependencies:
prop-types: ^15.6.0
typed.js: ^2.0.6
peerDependencies:
react: ^16.3.0
react-dom: ^16.3.0
checksum: c750d68710f918904b9bf07ea253e51d3447f9533c8277bd0afcdd20725b9e42e52ddcccd63ce93b4d0eb40db14573ccc82fce27b568b72a250d2167fc375b6f
languageName: node
linkType: hard
"react@npm:18.2.0":
version: 18.2.0
resolution: "react@npm:18.2.0"
@@ -5497,13 +5481,6 @@ __metadata:
languageName: node
linkType: hard
"typed.js@npm:^2.0.6":
version: 2.0.12
resolution: "typed.js@npm:2.0.12"
checksum: ea753bf8e01fd6e37390f8d02de58cc084e7b11620e937e75a6cad58829dd55a9273954191529cddad58ab102479f097db4587c6790ba4ff2dabc5d4fb6192d8
languageName: node
linkType: hard
"typeforce@npm:^1.11.5":
version: 1.18.0
resolution: "typeforce@npm:1.18.0"