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
delivan b47c09fb6f Merge remote-tracking branch 'origin/Thunnini/wallet-utils'
# Conflicts:
#	yarn.lock
2022-12-12 16:02:13 +09:00
HeesungB 9db9a0478b Merge branch 'main' of github.com:interchain-name/icns-frontend 2022-12-12 15:51:41 +09:00
HeesungB 564b401331 Add select wallet modal component 2022-12-12 15:51:25 +09:00
delivan f16159d189 Add session destruction when re-signin 2022-12-12 15:48:34 +09:00
HeesungB b33309493a Merge branch 'main' of github.com:interchain-name/icns-frontend
# Conflicts:
#	pages/verification/index.tsx
2022-12-12 15:42:13 +09:00
delivan 8771fd1880 Update README.md 2022-12-12 15:33:39 +09:00
delivan cd81a19f00 Update to get verification list 2022-12-12 15:31:52 +09:00
HeesungB fc58cad6a8 Divided into several components 2022-12-09 20:59:52 +09:00
34 changed files with 1102 additions and 395 deletions
Generated
+72
View File
@@ -37,6 +37,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/node", "npm:18.11.9"],\
["@types/react", "npm:18.0.25"],\
["@types/react-dom", "npm:18.0.9"],\
["@types/react-modal", "npm:3.13.1"],\
["@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"],\
@@ -59,6 +60,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["react", "npm:18.2.0"],\
["react-dom", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:18.2.0"],\
["react-is", "npm:18.2.0"],\
["react-modal", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:3.16.1"],\
["styled-components", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.3.6"],\
["typescript", "patch:typescript@npm%3A4.9.3#~builtin<compat/typescript>::version=4.9.3&hash=d73830"]\
],\
@@ -1336,6 +1338,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["@types/react-modal", [\
["npm:3.13.1", {\
"packageLocation": "./.yarn/cache/@types-react-modal-npm-3.13.1-6bb3eb42bb-b0ad403e00.zip/node_modules/@types/react-modal/",\
"packageDependencies": [\
["@types/react-modal", "npm:3.13.1"],\
["@types/react", "npm:18.0.25"]\
],\
"linkType": "HARD"\
}]\
]],\
["@types/scheduler", [\
["npm:0.16.2", {\
"packageLocation": "./.yarn/cache/@types-scheduler-npm-0.16.2-ba3a7d8c68-b6b4dcfeae.zip/node_modules/@types/scheduler/",\
@@ -3348,6 +3360,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["exenv", [\
["npm:1.2.2", {\
"packageLocation": "./.yarn/cache/exenv-npm-1.2.2-eeb9069149-a894f3b60a.zip/node_modules/exenv/",\
"packageDependencies": [\
["exenv", "npm:1.2.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["fast-deep-equal", [\
["npm:3.1.1", {\
"packageLocation": "./.yarn/cache/fast-deep-equal-npm-3.1.1-cbd83be021-98bcc0eece.zip/node_modules/fast-deep-equal/",\
@@ -3962,6 +3983,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/node", "npm:18.11.9"],\
["@types/react", "npm:18.0.25"],\
["@types/react-dom", "npm:18.0.9"],\
["@types/react-modal", "npm:3.13.1"],\
["@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"],\
@@ -3984,6 +4006,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["react", "npm:18.2.0"],\
["react-dom", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:18.2.0"],\
["react-is", "npm:18.2.0"],\
["react-modal", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:3.16.1"],\
["styled-components", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:5.3.6"],\
["typescript", "patch:typescript@npm%3A4.9.3#~builtin<compat/typescript>::version=4.9.3&hash=d73830"]\
],\
@@ -5667,6 +5690,45 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["react-lifecycles-compat", [\
["npm:3.0.4", {\
"packageLocation": "./.yarn/cache/react-lifecycles-compat-npm-3.0.4-d5e285a39e-a904b0fc0a.zip/node_modules/react-lifecycles-compat/",\
"packageDependencies": [\
["react-lifecycles-compat", "npm:3.0.4"]\
],\
"linkType": "HARD"\
}]\
]],\
["react-modal", [\
["npm:3.16.1", {\
"packageLocation": "./.yarn/cache/react-modal-npm-3.16.1-3ba7cf9e93-978936e932.zip/node_modules/react-modal/",\
"packageDependencies": [\
["react-modal", "npm:3.16.1"]\
],\
"linkType": "SOFT"\
}],\
["virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:3.16.1", {\
"packageLocation": "./.yarn/__virtual__/react-modal-virtual-f4afc3f69f/0/cache/react-modal-npm-3.16.1-3ba7cf9e93-978936e932.zip/node_modules/react-modal/",\
"packageDependencies": [\
["react-modal", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:3.16.1"],\
["@types/react", "npm:18.0.25"],\
["@types/react-dom", "npm:18.0.9"],\
["exenv", "npm:1.2.2"],\
["prop-types", "npm:15.8.1"],\
["react", "npm:18.2.0"],\
["react-dom", "virtual:4b77e00d446246df1ed27001550885fbf1b51be18c660c1b5c357d3d763078ecef2a676194291a120f149b87573081e5af0621dc83cf1f83383639f39ac133c7#npm:18.2.0"],\
["react-lifecycles-compat", "npm:3.0.4"],\
["warning", "npm:4.0.3"]\
],\
"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/",\
@@ -6650,6 +6712,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["warning", [\
["npm:4.0.3", {\
"packageLocation": "./.yarn/cache/warning-npm-4.0.3-291e921d6d-4f2cb6a957.zip/node_modules/warning/",\
"packageDependencies": [\
["warning", "npm:4.0.3"],\
["loose-envify", "npm:1.4.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["webcrypto-core", [\
["npm:1.7.5", {\
"packageLocation": "./.yarn/cache/webcrypto-core-npm-1.7.5-4734fab0f3-f6e529ca5c.zip/node_modules/webcrypto-core/",\
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -1
View File
@@ -12,7 +12,8 @@ TWITTER_AUTH_CALLBACK_URI=...
IRON_PASSWORD=...
ICNS_VERIFIER_URI=...
# must be separated by commas
ICNS_VERIFIER_ORIGIN_LIST=http:localhost:8080,http://localhost:8081
```
Run the development server:
+60
View File
@@ -0,0 +1,60 @@
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";
interface Props {
chainInfo: AccountInfo;
}
export const ChainItem: FunctionComponent<Props> = (props) => {
const { chainInfo } = props;
return (
<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>{`.${chainInfo.prefix}`}</ChainName>
<WalletAddress>{chainInfo.address}</WalletAddress>
</ChainInfoContainer>
<Flex1 />
<ChainCheckBox />
</ChainItemContainer>
);
};
const ChainName = styled.div`
font-weight: 600;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.grey["100"]};
`;
const WalletAddress = styled.div`
font-weight: 500;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.grey["400"]};
`;
const ChainCheckBox = styled.input.attrs({ type: "checkbox" })`
width: 1.5rem;
height: 1.5rem;
`;
+59
View File
@@ -0,0 +1,59 @@
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: AccountInfo[];
}
export const ChainList: FunctionComponent<Props> = (props) => {
const { chainList } = props;
return (
<ChainContainer color={color.grey["800"]}>
{chainList.map((chainInfo) => (
<ChainItem key={chainInfo.prefix} chainInfo={chainInfo} />
))}
</ChainContainer>
);
};
export const ChainContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
max-height: 33rem;
overflow: scroll;
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;
`;
+2
View File
@@ -0,0 +1,2 @@
export * from "./chain-list";
export * from "./chain-item";
+74
View File
@@ -0,0 +1,74 @@
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"]};
`;
+98
View File
@@ -0,0 +1,98 @@
import ReactModal from "react-modal";
import { FunctionComponent } from "react";
import styled from "styled-components";
import color from "../../styles/color";
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>
{/*<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>
);
};
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"]};
`;
@@ -0,0 +1,92 @@
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 { WalletType } from "../../constants/wallet";
import { TwitterAuthUrlResponse } from "../../types";
interface Props {
wallet: WalletType;
}
export const WalletItem: FunctionComponent<Props> = (props: Props) => {
const { wallet } = props;
const onClickWalletItem = async () => {
const { authUrl }: TwitterAuthUrlResponse = await (
await fetch("/api/twitter-auth-url")
).json();
window.location.href = authUrl;
};
return (
<WalletContainer isReady={wallet.isReady} onClick={onClickWalletItem}>
<WalletIcon>
<Image src={wallet.image} fill={true} alt="wallet Icon" />
</WalletIcon>
<WalletContentContainer>
<WalletName>{wallet.name}</WalletName>
{wallet.isReady ? null : (
<WalletDescription>Comming soon</WalletDescription>
)}
</WalletContentContainer>
<Flex1 />
<Image src={ArrowRightIcon} alt="arrow right icon" />
</WalletContainer>
);
};
const WalletContainer = styled.div<{ isReady: boolean }>`
display: flex;
flex-direction: row;
align-items: center;
height: 5.8rem;
padding: 1rem;
background-color: ${color.grey["600"]};
opacity: ${(props) => (props.isReady ? 1 : 0.3)};
cursor: ${(props) => (props.isReady ? "pointer" : "default")};
`;
const WalletIcon = styled.div`
position: relative;
width: 3.75rem;
height: 3.75rem;
`;
const WalletContentContainer = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
padding-left: 1rem;
`;
const WalletName = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 600;
font-size: 1.3rem;
line-height: 1.5rem;
text-align: center;
color: ${color.white};
`;
const WalletDescription = styled.div`
font-family: "Inter", serif;
font-style: normal;
font-weight: 600;
font-size: 0.8rem;
line-height: 1.1rem;
color: ${color.grey["300"]};
`;
+11 -2
View File
@@ -7,7 +7,7 @@ export const PrimaryButton = styled.button`
border: none;
background-color: ${color.primary};
background-color: ${color.orange["100"]};
padding: 11px 30px;
font-family: "Inter", serif;
@@ -16,7 +16,16 @@ export const PrimaryButton = styled.button`
font-size: 1.5rem;
line-height: 20px;
color: ${color.orange};
color: ${color.orange["50"]};
cursor: pointer;
&:hover {
transition-duration: 0.5s;
background-color: ${color.orange["200"]};
}
&:disabled {
background-color: ${color.orange["200"]};
}
`;
+2
View File
@@ -1,3 +1,5 @@
export * from "./skeleton-animation";
export * from "./skeleton-circle";
export * from "./skeleton-text";
export * from "./skeleton-chain-list";
+140
View File
@@ -0,0 +1,140 @@
import styled from "styled-components";
import color from "../../styles/color";
import { FunctionComponent } from "react";
import { SkeletonAnimation } from "./skeleton-animation";
import { SkeletonCircle } from "./skeleton-circle";
import { SkeletonText } from "./skeleton-text";
import {
ChainListTitleContainer,
ContentContainer,
ButtonContainer,
} from "../../pages/verification";
import {
ProfileContainer,
ProfileContentContainer,
ProfileFollowContainer,
ProfileNameContainer,
ProfileUserNameContainer,
} from "../twitter-profile";
import {
ChainContainer,
ChainImageContainer,
ChainInfoContainer,
ChainItemContainer,
} from "../chain-list";
export const SkeletonChainList: FunctionComponent = () => (
<ContentContainer>
<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>
<ProfileFollowContainer>
<SkeletonText width="8rem" height="1rem" />
<SkeletonText width="8rem" height="1rem" />
</ProfileFollowContainer>
<SkeletonText width="20rem" height="1rem" />
</ProfileContentContainer>
</ProfileContainer>
<ChainListTitleContainer>
<SkeletonTitle />
</ChainListTitleContainer>
<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 />
<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>
<ButtonContainer>
<SkeletonButton />
</ButtonContainer>
</ContentContainer>
);
const SkeletonTitle = styled.div`
width: 8rem;
height: 1.5rem;
background-color: ${color.grey["700"]};
`;
const SkeletonButton = styled.div`
width: 12rem;
height: 4rem;
background-color: ${color.grey["700"]};
`;
const SkeletonDivider = styled(SkeletonAnimation)`
width: 100%;
height: 1px;
background-color: ${color.grey["500"]};
`;
+133
View File
@@ -0,0 +1,133 @@
import color from "../../styles/color";
import styled from "styled-components";
import { TwitterAuthInfoResponse } from "../../types";
import { FunctionComponent } from "react";
import Image from "next/image";
interface Props {
twitterProfileInformation?: TwitterAuthInfoResponse | null;
}
export const TwitterProfile: FunctionComponent<Props> = (props) => {
const { twitterProfileInformation } = props;
return (
<ProfileContainer color={color.grey["800"]}>
<ProfileImageContainer>
<Image
src={twitterProfileInformation?.profile_image_url ?? ""}
fill={true}
alt="twitter profile image"
/>
</ProfileImageContainer>
<ProfileContentContainer>
<ProfileNameContainer>
{twitterProfileInformation?.name}
</ProfileNameContainer>
<ProfileUserNameContainer>
@{twitterProfileInformation?.username}
</ProfileUserNameContainer>
<ProfileFollowContainer>
<ProfileFollowerContainer>
<ProfileFollowBold>
{twitterProfileInformation?.public_metrics?.following_count}
</ProfileFollowBold>{" "}
Following
</ProfileFollowerContainer>
<ProfileFollowerContainer>
<ProfileFollowBold>
{twitterProfileInformation?.public_metrics?.followers_count}
</ProfileFollowBold>{" "}
Followers
</ProfileFollowerContainer>
</ProfileFollowContainer>
<ProfileDescriptionContainer>
{twitterProfileInformation?.description}
</ProfileDescriptionContainer>
</ProfileContentContainer>
</ProfileContainer>
);
};
export const ProfileContainer = styled.div`
display: flex;
flex-direction: row;
width: 100%;
padding: 1.5rem 2rem;
background-color: ${(props) => props.color};
`;
export const ProfileContentContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.85rem;
margin-left: 1.5rem;
`;
export const ProfileImageContainer = styled.div`
width: 5rem;
height: 5rem;
margin-top: -3rem;
border-radius: 50%;
overflow: hidden;
position: relative;
`;
export const ProfileNameContainer = styled.div`
font-weight: 600;
font-size: 1.2rem;
line-height: 1.5rem;
color: ${color.white};
`;
export const ProfileUserNameContainer = styled.div`
font-weight: 500;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.grey["100"]};
`;
export const ProfileFollowContainer = styled.div`
display: flex;
flex-direction: row;
gap: 1.5rem;
`;
export const ProfileFollowerContainer = styled.div`
font-weight: 500;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.grey["400"]};
`;
export const ProfileFollowBold = styled.span`
font-weight: 600;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.white};
`;
export const ProfileDescriptionContainer = styled.div`
font-weight: 500;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.grey["100"]};
`;
+23
View File
@@ -0,0 +1,23 @@
import { StaticImageData } from "next/image";
import KeplrIcon from "../public/images/svg/keplr-icon.svg";
import CosmostationIcon from "../public/images/svg/cosmostation-icon.svg";
export interface WalletType {
name: string;
image: StaticImageData;
isReady: boolean;
}
export const WalletList: WalletType[] = [
{
name: "Keplr",
image: KeplrIcon,
isReady: true,
},
{
name: "Cosmostation",
image: CosmostationIcon,
isReady: false,
},
];
+2
View File
@@ -22,6 +22,7 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-is": "^18.2.0",
"react-modal": "^3.16.1",
"styled-components": "^5.3.6"
},
"devDependencies": {
@@ -30,6 +31,7 @@
"@types/node": "18.11.9",
"@types/react": "18.0.25",
"@types/react-dom": "18.0.9",
"@types/react-modal": "^3",
"@types/styled-components": "^5",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
+19 -11
View File
@@ -9,21 +9,29 @@ export default withIronSessionApiRoute(async function handler(
res: NextApiResponse,
) {
try {
if (!process.env.ICNS_VERIFIER_URI) {
if (!process.env.ICNS_VERIFIER_ORIGIN_LIST) {
console.log(".env is not set");
return res.status(500).json({ error: "Internal server error" });
}
const icnsVerificationInfo = await request<IcnsVerificationResponse>(
process.env.ICNS_VERIFIER_URI,
{
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(req.body),
},
const verifierOriginList = process.env.ICNS_VERIFIER_ORIGIN_LIST.split(",");
const verificationList = await Promise.allSettled(
verifierOriginList.map((verfierOrigin) =>
request<IcnsVerificationResponse>(
`${verfierOrigin}/api/verify_twitter`,
{
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(req.body),
},
),
),
);
res.status(200).json(icnsVerificationInfo);
res.status(200).json({
verificationList,
});
} catch (error) {
console.error(error);
res.status(500).json({ error: "Internal server error" });
+2
View File
@@ -23,6 +23,8 @@ export default withIronSessionApiRoute(async function handler(
}
try {
req.session.destroy();
const codeVerifier = base64URLEncode(crypto.randomBytes(32));
req.session.code_verifier = codeVerifier;
await req.session.save();
+12 -11
View File
@@ -1,12 +1,8 @@
// NextJs
import Image from "next/image";
import Link from "next/link";
// Types
import { TwitterAuthUrlResponse } from "../types/api-response";
// Styles
import styled, { useTheme } from "styled-components";
import styled from "styled-components";
import color from "../styles/color";
// Components
@@ -17,14 +13,14 @@ 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 { Logo } from "../components/logo";
import { useState } from "react";
import { ConnectWalletModal } from "../components/connect-wallet-modal";
export default function Home() {
const handleSignInWithTwitter = async () => {
const { authUrl }: TwitterAuthUrlResponse = await (
await fetch("/api/twitter-auth-url")
).json();
const [isModalOpen, setModalOpen] = useState(false);
window.location.href = authUrl;
const onClickConnectWalletButton = async () => {
setModalOpen(true);
};
return (
@@ -40,7 +36,7 @@ export default function Home() {
</MainTitleImageBackground>
<ConnectButtonContainer>
<PrimaryButton onClick={handleSignInWithTwitter}>
<PrimaryButton onClick={onClickConnectWalletButton}>
Connect Wallet
</PrimaryButton>
</ConnectButtonContainer>
@@ -66,6 +62,11 @@ export default function Home() {
<Image src={MainLogo} layout="fixed" fill={true} alt="Main Logo" />
</MainLogoContainer>
</MainContainer>
<ConnectWalletModal
isModalOpen={isModalOpen}
onCloseModal={() => setModalOpen(false)}
/>
</Container>
);
}
+159 -336
View File
@@ -5,12 +5,7 @@ import { useEffect, useState } from "react";
import Image from "next/image";
// Types
import {
IcnsVerificationResponse,
TwitterAuthInfoResponse,
VerifierMsg,
WidthHeightProps,
} from "../../types";
import { IcnsVerificationResponse, TwitterAuthInfoResponse } from "../../types";
import { request } from "../../utils/url";
// Styles
@@ -19,14 +14,21 @@ import color from "../../styles/color";
// Components
import { Logo } from "../../components/logo";
import {
SkeletonAnimation,
SkeletonCircle,
SkeletonText,
} from "../../components/skeleton";
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 {
getKeplrFromWindow,
KeplrWallet,
makeCosmwasmExecMsg,
sendMsgs,
simulateMsgs,
} from "../../wallets";
import { TendermintTxTracer } from "@keplr-wallet/cosmos";
import { Buffer } from "buffer/";
export default function VerificationPage() {
const [twitterAuthInfo, setTwitterAuthInfo] =
@@ -48,24 +50,146 @@ export default function VerificationPage() {
setTwitterAuthInfo(newTwitterAuthInfo);
const verifierMsg: VerifierMsg = {
unique_twitter_id: newTwitterAuthInfo.id,
name: newTwitterAuthInfo.username,
claimer: "osmo1y5mm5nj5m8ttddt5ccspek6xgyyavehrkak7gq",
contract_address: "osmo1y5mm5nj5m8ttddt5ccspek6xgyyavehrkak7gq",
chain_id: "osmosis-1",
};
await (async () => {
const keplr = await getKeplrFromWindow();
if (keplr) {
const wallet = new KeplrWallet(keplr);
await request<IcnsVerificationResponse>("/api/icns-verification", {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
msg: JSON.stringify(verifierMsg),
authToken: newTwitterAuthInfo.accessToken,
}),
});
const mainChainId = "osmo-test-4";
const chainIds = (await wallet.getChainInfosWithoutEndpoints()).map(
(c) => c.chainId,
);
await wallet.init(chainIds);
const key = await wallet.getKey(mainChainId);
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;
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);
}
})();
setIsLoading(false);
}
@@ -80,164 +204,17 @@ export default function VerificationPage() {
<MainContainer>
{isLoading ? (
<ContentContainer>
<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>
<ProfileFollowContainer>
<SkeletonText width="8rem" height="1rem" />
<SkeletonText width="8rem" height="1rem" />
</ProfileFollowContainer>
<SkeletonText width="20rem" height="1rem" />
</ProfileContentContainer>
</ProfileContainer>
<ChainListTitleContainer>
<SkeletonText width="8rem" height="1.5rem" />
</ChainListTitleContainer>
<ChainContainer color={color.grey["700"]}>
<ChainItemContainer>
<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>
<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>
<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>
<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>
<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>
<ButtonContainer>
<SkeletonButton />
</ButtonContainer>
</ContentContainer>
<SkeletonChainList />
) : (
<ContentContainer>
<ProfileContainer color={color.grey["700"]}>
<ProfileImageContainer>
<Image
src={twitterAuthInfo?.profile_image_url ?? ""}
alt="profile image"
fill={true}
/>
</ProfileImageContainer>
<ProfileContentContainer>
<ProfileNameContainer>
{twitterAuthInfo?.name}
</ProfileNameContainer>
<ProfileUserNameContainer>
{twitterAuthInfo?.username}
</ProfileUserNameContainer>
<ProfileFollowContainer>
<ProfileFollowerContainer>
<ProfileFollowBold>
{twitterAuthInfo?.public_metrics?.following_count}
</ProfileFollowBold>{" "}
Following
</ProfileFollowerContainer>
<ProfileFollowerContainer>
<ProfileFollowBold>
{twitterAuthInfo?.public_metrics?.followers_count}
</ProfileFollowBold>{" "}
Followers
</ProfileFollowerContainer>
</ProfileFollowContainer>
<ProfileDescriptionContainer>
{twitterAuthInfo?.description}
</ProfileDescriptionContainer>
</ProfileContentContainer>
</ProfileContainer>
<TwitterProfile twitterProfileInformation={twitterAuthInfo} />
<ChainListTitleContainer>
<ChainListTitle>Chain List</ChainListTitle>
<SearchContainer>Search</SearchContainer>
</ChainListTitleContainer>
<ChainContainer color={color.grey["700"]}>
{AccountInfos.map((accountInfo) => (
<ChainItemContainer key={accountInfo.prefix}>
<ChainImageContainer width="3rem" height="3rem">
<Image
src={accountInfo.chainImageUrl}
alt={`${accountInfo.prefix} image`}
fill={true}
/>
</ChainImageContainer>
<ChainInfoContainer>
<ChainName>{`.${accountInfo.prefix}`}</ChainName>
<WalletAddress>{accountInfo.address}</WalletAddress>
</ChainInfoContainer>
<Flex1 />
<ChainCheckBox />
</ChainItemContainer>
))}
</ChainContainer>
<ChainList chainList={AccountInfos} />
<ButtonContainer>
<PrimaryButton>Register</PrimaryButton>
@@ -261,7 +238,7 @@ const MainContainer = styled.div`
color: white;
`;
const ContentContainer = styled.div`
export const ContentContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
@@ -271,143 +248,14 @@ const ContentContainer = styled.div`
margin-top: 5rem;
`;
const ProfileContainer = styled.div`
display: flex;
flex-direction: row;
width: 100%;
padding: 1.5rem 2rem;
background-color: ${(props) => props.color};
`;
const ProfileContentContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.85rem;
margin-left: 1.5rem;
`;
const ProfileFollowContainer = styled.div`
display: flex;
flex-direction: row;
gap: 1.5rem;
`;
const ChainContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
max-height: 33rem;
overflow: scroll;
background-color: ${(props) => props.color};
`;
const ChainItemContainer = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: 1rem;
padding: 1.5rem;
&:hover {
background: ${color.grey["900"]};
}
`;
const ChainImageContainer = styled.div<WidthHeightProps>`
width: ${(props) => props.width};
height: ${(props) => props.height};
position: relative;
`;
const ChainInfoContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.5rem;
`;
const ButtonContainer = styled.div`
width: 10rem;
export const ButtonContainer = styled.div`
width: 12rem;
height: 4rem;
margin-top: 2rem;
`;
const SkeletonButton = styled.div`
width: 12rem;
height: 4rem;
background-color: ${color.grey["700"]};
`;
const SkeletonDivider = styled(SkeletonAnimation)`
width: 100%;
height: 1px;
background-color: ${color.grey["600"]};
`;
const ProfileImageContainer = styled.div`
width: 5rem;
height: 5rem;
margin-top: -3rem;
border-radius: 50%;
overflow: hidden;
position: relative;
`;
const ProfileNameContainer = styled.div`
font-weight: 600;
font-size: 1.2rem;
line-height: 1.5rem;
color: ${color.white};
`;
const ProfileUserNameContainer = styled.div`
font-weight: 500;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.grey["100"]};
`;
const ProfileFollowerContainer = styled.div`
font-weight: 500;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.grey["400"]};
`;
const ProfileFollowBold = styled.span`
font-weight: 600;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.white};
`;
const ProfileDescriptionContainer = styled.div`
font-weight: 500;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.grey["300"]};
`;
const ChainListTitleContainer = styled.div`
export const ChainListTitleContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
@@ -438,28 +286,3 @@ const SearchContainer = styled.div`
background-color: ${color.grey["700"]};
`;
const ChainName = styled.div`
font-weight: 600;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.grey["100"]};
`;
const WalletAddress = styled.div`
font-weight: 500;
font-size: 0.8rem;
line-height: 1rem;
color: ${color.grey["400"]};
`;
const Flex1 = styled.div`
flex: 1;
`;
const ChainCheckBox = styled.input.attrs({ type: "checkbox" })`
width: 1.5rem;
height: 1.5rem;
`;
+3
View File
@@ -0,0 +1,3 @@
<svg width="26" height="27" viewBox="0 0 26 27" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.9906 6.4124L21.0781 13.4999L13.9906 20.5874M20.0938 13.4999L5.72188 13.4999" stroke="#6F6F6F" stroke-width="2" stroke-linecap="square"/>
</svg>

After

Width:  |  Height:  |  Size: 253 B

+12
View File
@@ -0,0 +1,12 @@
<svg width="81" height="80" viewBox="0 0 81 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.25" width="80" height="80" rx="20" fill="#1B1B21"/>
<g clip-path="url(#clip0_249_4007)">
<path d="M30.5465 56.0417L19.3466 37.1577C18.779 36.2007 18.779 35.0167 19.3466 34.0597L30.5465 15.1855C31.1141 14.2285 32.1682 13.6365 33.3034 13.6365H55.7032C56.8384 13.6365 57.8925 14.2285 58.4601 15.1855L69.6598 34.0696L64.1562 37.1676L53.8583 19.8325H35.1278L25.7624 35.6186L36.04 52.9538L30.5363 56.0516L30.5465 56.0417Z" fill="#9C6CFF"/>
<path d="M46.5965 66.3028H24.2067C23.0716 66.3028 22.0175 65.7109 21.4499 64.7539L10.25 45.8698L15.7537 42.7718L26.0312 60.1068H44.7515L54.1171 44.3208L43.8396 26.9857L49.3432 23.8877L60.5429 42.7718C61.1105 43.7288 61.1105 44.9127 60.5429 45.8698L49.3432 64.7539C48.7756 65.7109 47.7215 66.3028 46.5863 66.3028H46.5965Z" fill="#05D2DD"/>
</g>
<defs>
<clipPath id="clip0_249_4007">
<rect width="59.0909" height="52.7273" fill="white" transform="translate(10.25 13.6365)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 148 KiB

+8 -6
View File
@@ -1,7 +1,12 @@
const primary = "#FB5232";
const orange = "#FFCDC3";
const orange = {
50: "#FFCDC3",
100: "#FB5232",
200: "#A13E2A",
300: "#62332A",
400: "#442924",
};
const white = "#FCFCFC";
const grey = {
100: "#A3A3A3",
200: "#868686",
@@ -13,12 +18,9 @@ const grey = {
800: "#242424",
900: "#181818",
};
const white = "#FFFFFF";
const black = "#121212";
const color = {
primary,
grey,
white,
black,
+5
View File
@@ -0,0 +1,5 @@
import styled from "styled-components";
export const Flex1 = styled.div`
flex: 1;
`;
+21 -2
View File
@@ -15,6 +15,25 @@ export interface TwitterAuthInfoResponse {
}
export interface IcnsVerificationResponse {
signature: number[];
algorithm: string;
verificationList: (
| {
status: "fulfilled";
value: {
errors: Error[];
data: {
// JSON string
verifying_msg: string;
// Base64 encoded
public_key: string;
// Base64 encoded
signature: string;
algorithm: string;
};
};
}
| {
status: "rejected";
reason: Error;
}
)[];
}
+1 -1
View File
@@ -1,4 +1,4 @@
export interface VerifierMsg {
export interface VerifyingMsg {
unique_twitter_id: string;
name: string;
claimer: string;
+4 -2
View File
@@ -1,5 +1,7 @@
export function base64URLEncode(str: Buffer) {
return str
import { Buffer } from "buffer/";
export function base64URLEncode(str: Uint8Array) {
return Buffer.from(str)
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
+13 -11
View File
@@ -41,7 +41,7 @@ export class KeplrWallet implements Wallet {
constructor(public readonly keplr: Keplr) {}
broadcastTxSync(chainId: string, tx: Uint8Array): Promise<Uint8Array> {
return this.keplr.sendTx(chainId, tx, BroadcastMode.Sync);
return this.keplr.sendTx(chainId, tx, "sync" as BroadcastMode);
}
getChainInfosWithoutEndpoints(): Promise<
@@ -54,7 +54,7 @@ export class KeplrWallet implements Wallet {
getKey(chainId: string): Promise<{
readonly name: string;
readonly pubKey: Uint8Array;
readonly address: Uint8Array;
readonly bech32Address: string;
}> {
return this.keplr.getKey(chainId);
}
@@ -77,15 +77,17 @@ export class KeplrWallet implements Wallet {
owner: string,
username: string,
addressChainIds: string[],
): Promise<{
chainId: string;
bech32Prefix: string;
bech32Address: string;
addressHash: "cosmos" | "ethereum";
pubKey: Uint8Array;
signatureSalt: number;
signature: Uint8Array;
}> {
): Promise<
{
chainId: string;
bech32Prefix: string;
bech32Address: string;
addressHash: "cosmos" | "ethereum";
pubKey: Uint8Array;
signatureSalt: number;
signature: Uint8Array;
}[]
> {
// TODO: Update @keplr-wallet/types
return (this.keplr as any).signICNSAdr36(
chainId,
+12 -10
View File
@@ -10,7 +10,7 @@ export interface Wallet {
getKey(chainId: string): Promise<{
readonly name: string;
readonly pubKey: Uint8Array;
readonly address: Uint8Array;
readonly bech32Address: string;
}>;
signAmino(
chainId: string,
@@ -25,13 +25,15 @@ export interface Wallet {
owner: string,
username: string,
addressChainIds: string[],
): Promise<{
chainId: string;
bech32Prefix: string;
bech32Address: string;
addressHash: "cosmos" | "ethereum";
pubKey: Uint8Array;
signatureSalt: number;
signature: Uint8Array;
}>;
): Promise<
{
chainId: string;
bech32Prefix: string;
bech32Address: string;
addressHash: "cosmos" | "ethereum";
pubKey: Uint8Array;
signatureSalt: number;
signature: Uint8Array;
}[]
>;
}
+51 -2
View File
@@ -1082,6 +1082,15 @@ __metadata:
languageName: node
linkType: hard
"@types/react-modal@npm:^3":
version: 3.13.1
resolution: "@types/react-modal@npm:3.13.1"
dependencies:
"@types/react": "*"
checksum: b0ad403e0051a2882e9086c6006cc342901b1c90caf760893b31717a5cbc04f86cc1ea7b5ce12a2c8350c9c09e9b344de3783404e70b7c7ea6a037d0ffd70e99
languageName: node
linkType: hard
"@types/react@npm:*, @types/react@npm:18.0.25":
version: 18.0.25
resolution: "@types/react@npm:18.0.25"
@@ -2619,6 +2628,13 @@ __metadata:
languageName: node
linkType: hard
"exenv@npm:^1.2.0":
version: 1.2.2
resolution: "exenv@npm:1.2.2"
checksum: a894f3b60ab8419e0b6eec99c690a009c8276b4c90655ccaf7d28faba2de3a6b93b3d92210f9dc5efd36058d44f04098f6bbccef99859151104bfd49939904dc
languageName: node
linkType: hard
"fast-deep-equal@npm:3.1.1":
version: 3.1.1
resolution: "fast-deep-equal@npm:3.1.1"
@@ -3158,6 +3174,7 @@ __metadata:
"@types/node": 18.11.9
"@types/react": 18.0.25
"@types/react-dom": 18.0.9
"@types/react-modal": ^3
"@types/styled-components": ^5
"@typescript-eslint/eslint-plugin": ^5.45.0
"@typescript-eslint/parser": ^5.45.0
@@ -3180,6 +3197,7 @@ __metadata:
react: 18.2.0
react-dom: 18.2.0
react-is: ^18.2.0
react-modal: ^3.16.1
styled-components: ^5.3.6
typescript: 4.9.3
languageName: unknown
@@ -3798,7 +3816,7 @@ __metadata:
languageName: node
linkType: hard
"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
version: 1.4.0
resolution: "loose-envify@npm:1.4.0"
dependencies:
@@ -4556,7 +4574,7 @@ __metadata:
languageName: node
linkType: hard
"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:
@@ -4656,6 +4674,28 @@ __metadata:
languageName: node
linkType: hard
"react-lifecycles-compat@npm:^3.0.0":
version: 3.0.4
resolution: "react-lifecycles-compat@npm:3.0.4"
checksum: a904b0fc0a8eeb15a148c9feb7bc17cec7ef96e71188280061fc340043fd6d8ee3ff233381f0e8f95c1cf926210b2c4a31f38182c8f35ac55057e453d6df204f
languageName: node
linkType: hard
"react-modal@npm:^3.16.1":
version: 3.16.1
resolution: "react-modal@npm:3.16.1"
dependencies:
exenv: ^1.2.0
prop-types: ^15.7.2
react-lifecycles-compat: ^3.0.0
warning: ^4.0.3
peerDependencies:
react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18
react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18
checksum: 978936e9320fad839c039b9ee4de55d40888156cb40e093615d6fbc2ff07139d5db06f14782cb7767f780bd5fb57956778669426c535ebc0068a7a03882c7e75
languageName: node
linkType: hard
"react@npm:18.2.0":
version: 18.2.0
resolution: "react@npm:18.2.0"
@@ -5538,6 +5578,15 @@ __metadata:
languageName: node
linkType: hard
"warning@npm:^4.0.3":
version: 4.0.3
resolution: "warning@npm:4.0.3"
dependencies:
loose-envify: ^1.0.0
checksum: 4f2cb6a9575e4faf71ddad9ad1ae7a00d0a75d24521c193fa464f30e6b04027bd97aa5d9546b0e13d3a150ab402eda216d59c1d0f2d6ca60124d96cd40dfa35c
languageName: node
linkType: hard
"webcrypto-core@npm:^1.7.4":
version: 1.7.5
resolution: "webcrypto-core@npm:1.7.5"