Feat/make dapp nextjs (#41)

This commit is contained in:
Celine Sarafa 2022-08-08 12:32:24 +03:00 committed by GitHub
parent c87a77ef10
commit 31ec2ecb9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 3573 additions and 2747 deletions

View File

@ -1,3 +1,2 @@
REACT_APP_PROJECT_ID=39bc...
REACT_APP_RELAY_URL=wss://relay.walletconnect.com
PROJECT_ID=39bc...
RELAY_URL=wss://relay.walletconnect.com

View File

@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

View File

@ -8,18 +8,29 @@
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
.eslintcache
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@ -1,7 +0,0 @@
{
"tabWidth": 2,
"useTabs": false,
"trailingComma": "all",
"printWidth": 100,
"arrowParens": "avoid"
}

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 WalletConnect, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -29,13 +29,13 @@ cp .env.local.example .env.local
Your `.env.local` now contains the following environment variables:
- `REACT_APP_PROJECT_ID` (placeholder) - You can generate your own ProjectId at https://cloud.walletconnect.com
- `REACT_APP_RELAY_URL` (already set)
- `PROJECT_ID` (placeholder) - You can generate your own ProjectId at https://cloud.walletconnect.com
- `RELAY_URL` (already set)
## Develop
```bash
yarn start
yarn dev
```
## Test
@ -49,3 +49,4 @@ yarn test
```bash
yarn build
```

View File

@ -1,7 +0,0 @@
declare module "*.svg";
declare module "*.png";
declare module "*.jpg";
declare module "*.jpeg";
declare module "*.gif";
declare module "*.bmp";
declare module "*.tiff";

View File

@ -0,0 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
distDir: 'build'
};
module.exports = nextConfig;

View File

@ -1,31 +1,13 @@
{
"name": "react-dapp-v2",
"name": "react-dapp-v2-next",
"version": "2.0.0-6de2113.0",
"private": true,
"keywords": [
"walletconnect",
"ethereum",
"web3",
"crypto"
],
"author": "WalletConnect, Inc. <walletconnect.com>",
"license": "MIT",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"prettier": "prettier --check '**/*.{js,ts,jsx,tsx}'"
},
"repository": {
"type": "git",
"url": "git+https://github.com/walletconnect/walletconnect-monorepo.git"
},
"bugs": {
"url": "https://github.com/walletconnect/walletconnect-monorepo/issues"
},
"resolutions": {
"react-error-overlay": "6.0.9"
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"prettier:write": "prettier --write '**/*.{js,ts,jsx,tsx}'"
},
"dependencies": {
"@ethereumjs/tx": "^3.5.0",
@ -43,6 +25,7 @@
"eth-sig-util": "^2.5.3",
"ethereumjs-util": "^7.0.6",
"ethers": "^5.3.0",
"next": "12.2.4",
"prop-types": "^15.7.2",
"qr-image": "^3.2.0",
"react": "^17.0.2",
@ -50,41 +33,22 @@
"react-scripts": "^4.0.3",
"solana-wallet": "^1.0.1",
"styled-components": "^5.2.0",
"typescript": "^4.3.2",
"web-vitals": "^0.2.4"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"@types/bn.js": "^5.1.0",
"@types/eth-sig-util": "^2.1.1",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.14",
"@types/pino": "^7.0.5",
"@types/prop-types": "^15.7.4",
"@types/qr-image": "^3.2.5",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"@types/styled-components": "^5.1.21",
"prettier": "^2.5.1"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
"@types/react": "18.0.15",
"@types/react-dom": "18.0.6",
"@types/styled-components": "^5.1.25",
"better-sqlite3": "^7.6.2",
"eslint": "8.21.0",
"eslint-config-next": "12.2.4",
"prettier": "^2.7.1",
"typescript": "^4.7.4"
}
}

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 234 B

After

Width:  |  Height:  |  Size: 234 B

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,20 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
<meta name="description" content="React App for WalletConnect" />
<style>
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,500,600,700,800");
</style>
</head>
<body>
<noscript> You need to enable JavaScript to run this app. </noscript>
<div id="root"></div>
</body>
</html>

View File

@ -1,15 +0,0 @@
{
"short_name": "WalletConnect",
"name": "WalletConnect React App",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -2,7 +2,11 @@ import { JsonRpcRequest } from "@walletconnect/jsonrpc-utils";
import { BLOCKCHAIN_LOGO_BASE_URL } from "../constants";
import { NamespaceMetadata, ChainMetadata, ChainRequestRender } from "../helpers";
import {
NamespaceMetadata,
ChainMetadata,
ChainRequestRender,
} from "../helpers";
export const CosmosMetadata: NamespaceMetadata = {
"cosmoshub-4": {
@ -20,7 +24,9 @@ export function getChainMetadata(chainId: string): ChainMetadata {
return metadata;
}
export function getChainRequestRender(request: JsonRpcRequest): ChainRequestRender[] {
export function getChainRequestRender(
request: JsonRpcRequest
): ChainRequestRender[] {
let params = [{ label: "Method", value: request.method }];
switch (request.method) {

View File

@ -83,7 +83,9 @@ export function getChainMetadata(chainId: string): ChainMetadata {
return metadata;
}
export function getChainRequestRender(request: JsonRpcRequest): ChainRequestRender[] {
export function getChainRequestRender(
request: JsonRpcRequest
): ChainRequestRender[] {
let params = [{ label: "Method", value: request.method }];
switch (request.method) {
@ -111,7 +113,9 @@ export function getChainRequestRender(request: JsonRpcRequest): ChainRequestRend
},
{
label: "Value",
value: request.params[0].value ? convertHexToNumber(request.params[0].value) : "",
value: request.params[0].value
? convertHexToNumber(request.params[0].value)
: "",
},
{ label: "Data", value: request.params[0].data },
];

View File

@ -25,7 +25,7 @@ export function getChainMetadata(chainId: string): ChainMetadata {
export function getChainRequestRender(
request: JsonRpcRequest,
chainId: string,
chainId: string
): ChainRequestRender[] {
const namespace = chainId.split(":")[0];
switch (namespace) {

View File

@ -1,12 +1,18 @@
import { JsonRpcRequest } from "@walletconnect/jsonrpc-utils";
import { BLOCKCHAIN_LOGO_BASE_URL } from "../constants";
import { NamespaceMetadata, ChainMetadata, ChainRequestRender } from "../helpers";
import {
NamespaceMetadata,
ChainMetadata,
ChainRequestRender,
} from "../helpers";
export const PolkadotMetadata: NamespaceMetadata = {
// eslint-disable-next-line no-useless-computed-key
["91b171bb158e2d3848fa23a9f1c25182"]: {
logo: BLOCKCHAIN_LOGO_BASE_URL + "polkadot:91b171bb158e2d3848fa23a9f1c25182.png",
logo:
BLOCKCHAIN_LOGO_BASE_URL +
"polkadot:91b171bb158e2d3848fa23a9f1c25182.png",
rgb: "230, 1, 122",
},
};
@ -20,7 +26,9 @@ export function getChainMetadata(chainId: string): ChainMetadata {
return metadata;
}
export function getChainRequestRender(request: JsonRpcRequest): ChainRequestRender[] {
export function getChainRequestRender(
request: JsonRpcRequest
): ChainRequestRender[] {
let params = [{ label: "Method", value: request.method }];
switch (request.method) {

View File

@ -6,7 +6,10 @@ export const SolanaChainData: ChainsMap = {
"4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ": {
id: "solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ",
name: "Solana Mainnet",
rpc: ["https://api.mainnet-beta.solana.com", "https://solana-api.projectserum.com"],
rpc: [
"https://api.mainnet-beta.solana.com",
"https://solana-api.projectserum.com",
],
slip44: 501,
testnet: false,
},
@ -22,12 +25,12 @@ export const SolanaChainData: ChainsMap = {
export const SolanaMetadata: NamespaceMetadata = {
// Solana Mainnet
"4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ": {
logo: "/solana_logo.png",
logo: "/assets/solana_logo.png",
rgb: "0, 0, 0",
},
// Solana Devnet
"8E9rvCKLFQia2Y35HXjjpWzj8weVo44K": {
logo: "/solana_logo.png",
logo: "/assets/solana_logo.png",
rgb: "0, 0, 0",
},
};

View File

@ -5,8 +5,6 @@ import Icon from "./Icon";
import { AssetData, fromWad } from "../helpers";
import eth from "../assets/eth.svg";
import erc20 from "../assets/erc20.svg";
import { getChainMetadata } from "../chains";
const xdai = getChainMetadata("eip155:100").logo;
@ -38,17 +36,17 @@ const SAssetBalance = styled.div`
function getAssetIcon(asset: AssetData): JSX.Element {
if (!!asset.contractAddress) {
const src = `https://raw.githubusercontent.com/TrustWallet/tokens/master/tokens/${asset.contractAddress.toLowerCase()}.png`;
return <Icon src={src} fallback={erc20} />;
return <Icon src={src} fallback={"/assets/erc20.svg"} />;
}
switch (asset.symbol.toLowerCase()) {
case "eth":
return <Icon src={eth} />;
return <Icon src={"/assets/eth.svg"} />;
case "xdai":
return <Icon src={xdai} />;
case "matic":
return <Icon src={matic} />;
default:
return <Icon src={erc20} />;
return <Icon src={"/assets/eth20.svg"} />;
}
}
@ -65,7 +63,9 @@ const Asset = (props: AssetProps) => {
<SAssetName>{asset.name}</SAssetName>
</SAssetLeft>
<SAssetRight>
<SAssetBalance>{`${fromWad(asset.balance || "0")} ${asset.symbol}`}</SAssetBalance>
<SAssetBalance>{`${fromWad(asset.balance || "0")} ${
asset.symbol
}`}</SAssetBalance>
</SAssetRight>
</SAsset>
);

View File

@ -1,6 +1,5 @@
import * as React from "react";
import styled from "styled-components";
import logo from "../assets/walletconnect.png";
const SBannerWrapper = styled.div`
display: flex;
@ -11,7 +10,7 @@ const SBannerWrapper = styled.div`
const SBanner = styled.div`
width: 275px;
height: 45px;
background: url(${logo}) no-repeat;
background: url(/assets/walletconnect.png) no-repeat;
background-size: cover;
background-position: center;
`;

View File

@ -99,7 +99,7 @@ interface BlockchainDisplayData {
function getBlockchainDisplayData(
chainId: string,
chainData: ChainNamespaces,
chainData: ChainNamespaces
): BlockchainDisplayData | undefined {
const [namespace, reference] = chainId.split(":");
let meta: ChainMetadata;
@ -114,9 +114,18 @@ function getBlockchainDisplayData(
}
const Blockchain: FC<PropsWithChildren<BlockchainProps>> = (
props: PropsWithChildren<BlockchainProps>,
props: PropsWithChildren<BlockchainProps>
) => {
const { chainData, fetching, chainId, address, onClick, active, balances, actions } = props;
const {
chainData,
fetching,
chainId,
address,
onClick,
active,
balances,
actions,
} = props;
if (!Object.keys(chainData).length) return null;
const chain = getBlockchainDisplayData(chainId, chainData);
@ -124,9 +133,12 @@ const Blockchain: FC<PropsWithChildren<BlockchainProps>> = (
if (typeof chain === "undefined") return null;
const name = chain.meta.name || chain.data.name;
const account = typeof address !== "undefined" ? `${chainId}:${address}` : undefined;
const account =
typeof address !== "undefined" ? `${chainId}:${address}` : undefined;
const assets =
typeof account !== "undefined" && typeof balances !== "undefined" ? balances[account] : [];
typeof account !== "undefined" && typeof balances !== "undefined"
? balances[account]
: [];
return (
<React.Fragment>
<SAccount
@ -152,8 +164,10 @@ const Blockchain: FC<PropsWithChildren<BlockchainProps>> = (
<SFullWidthContainer>
<h6>Balances</h6>
<Column center>
{assets.map(asset =>
asset.symbol ? <Asset key={asset.symbol} asset={asset} /> : null,
{assets.map((asset) =>
asset.symbol ? (
<Asset key={asset.symbol} asset={asset} />
) : null
)}
</Column>
</SFullWidthContainer>
@ -161,7 +175,7 @@ const Blockchain: FC<PropsWithChildren<BlockchainProps>> = (
{address && !!actions && actions.length ? (
<SFullWidthContainer>
<h6>Methods</h6>
{actions.map(action => (
{actions.map((action) => (
<SAction
key={action.method}
left

View File

@ -47,9 +47,12 @@ const SButton = styled.button<ButtonStyleProps>`
border: none;
border-style: none;
box-sizing: border-box;
background-color: ${({ outline, color }) => (outline ? "transparent" : `rgb(${colors[color]})`)};
border: ${({ outline, color }) => (outline ? `1px solid rgb(${colors[color]})` : "none")};
color: ${({ outline, color }) => (outline ? `rgb(${colors[color]})` : `rgb(${colors.white})`)};
background-color: ${({ outline, color }) =>
outline ? "transparent" : `rgb(${colors[color]})`};
border: ${({ outline, color }) =>
outline ? `1px solid rgb(${colors[color]})` : "none"};
color: ${({ outline, color }) =>
outline ? `rgb(${colors[color]})` : `rgb(${colors.white})`};
box-shadow: ${({ outline }) => (outline ? "none" : `${shadows.soft}`)};
border-radius: 8px;
font-size: ${fonts.size.medium};
@ -68,7 +71,11 @@ const SButton = styled.button<ButtonStyleProps>`
&:hover {
transform: ${({ disabled }) => (!disabled ? "translateY(-1px)" : "none")};
box-shadow: ${({ disabled, outline }) =>
!disabled ? (outline ? "none" : `${shadows.hover}`) : `${shadows.soft}`};
!disabled
? outline
? "none"
: `${shadows.hover}`
: `${shadows.soft}`};
}
&:hover ${SHoverLayer} {

View File

@ -27,7 +27,12 @@ const SColumn = styled.div<ColumnStyleProps>`
const Column = (props: ColumnProps) => {
const { children, spanHeight, maxWidth, center } = props;
return (
<SColumn {...props} spanHeight={spanHeight} maxWidth={maxWidth} center={center}>
<SColumn
{...props}
spanHeight={spanHeight}
maxWidth={maxWidth}
center={center}
>
{children}
</SColumn>
);

View File

@ -49,7 +49,15 @@ const Loader = (props: LoaderProps) => {
fill={rgb}
fillRule="nonzero"
/>
<rect id="Rectangle" fill={rgb} x="44" y="44.34375" width="98" height="98" rx="35" />
<rect
id="Rectangle"
fill={rgb}
x="44"
y="44.34375"
width="98"
height="98"
rx="35"
/>
</g>
</SLoader>
);

View File

@ -38,7 +38,8 @@ const SToggle = styled.div<IToggleStyleProps>`
}
& div:after {
transition: ${transitions.base};
box-shadow: inset 0 1px 0 rgb(${colors.grey}), 0px 2px 2px 1px rgba(${colors.black}, 0.2);
box-shadow: inset 0 1px 0 rgb(${colors.grey}),
0px 2px 2px 1px rgba(${colors.black}, 0.2);
border-radius: 1rem;
left: ${({ active }) => (active ? `20px` : `0`)};
content: "";

View File

@ -22,9 +22,9 @@ export const DEFAULT_TEST_CHAINS = [
export const DEFAULT_CHAINS = [...DEFAULT_MAIN_CHAINS, ...DEFAULT_TEST_CHAINS];
export const DEFAULT_PROJECT_ID = process.env.REACT_APP_PROJECT_ID;
export const DEFAULT_PROJECT_ID = process.env.PROJECT_ID;
export const DEFAULT_RELAY_URL = process.env.REACT_APP_RELAY_URL;
export const DEFAULT_RELAY_URL = process.env.RELAY_URL;
export const DEFAULT_LOGGER = "debug";

View File

@ -1,5 +1,11 @@
import { apiGetChainNamespace, ChainsMap } from "caip-api";
import { createContext, ReactNode, useContext, useEffect, useState } from "react";
import {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from "react";
import { SolanaChainData } from "../chains/solana";
import { ChainNamespaces, getAllChainNamespaces } from "../helpers";
@ -19,14 +25,18 @@ export const ChainDataContext = createContext<IContext>({} as IContext);
/**
* Provider
*/
export function ChainDataContextProvider({ children }: { children: ReactNode | ReactNode[] }) {
export function ChainDataContextProvider({
children,
}: {
children: ReactNode | ReactNode[];
}) {
const [chainData, setChainData] = useState<ChainNamespaces>({});
const loadChainData = async () => {
const namespaces = getAllChainNamespaces();
const chainData: ChainNamespaces = {};
await Promise.all(
namespaces.map(async namespace => {
namespaces.map(async (namespace) => {
let chains: ChainsMap | undefined;
try {
if (namespace === "solana") {
@ -40,7 +50,7 @@ export function ChainDataContextProvider({ children }: { children: ReactNode | R
if (typeof chains !== "undefined") {
chainData[namespace] = chains;
}
}),
})
);
setChainData(chainData);
@ -64,7 +74,9 @@ export function ChainDataContextProvider({ children }: { children: ReactNode | R
export function useChainData() {
const context = useContext(ChainDataContext);
if (context === undefined) {
throw new Error("useChainData must be used within a ChainDataContextProvider");
throw new Error(
"useChainData must be used within a ChainDataContextProvider"
);
}
return context;
}

View File

@ -49,7 +49,11 @@ export const ClientContext = createContext<IContext>({} as IContext);
/**
* Provider
*/
export function ClientContextProvider({ children }: { children: ReactNode | ReactNode[] }) {
export function ClientContextProvider({
children,
}: {
children: ReactNode | ReactNode[];
}) {
const [client, setClient] = useState<Client>();
const [pairings, setPairings] = useState<PairingTypes.Struct[]>([]);
const [session, setSession] = useState<SessionTypes.Struct>();
@ -59,7 +63,8 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
const [balances, setBalances] = useState<AccountBalances>({});
const [accounts, setAccounts] = useState<string[]>([]);
const [solanaPublicKeys, setSolanaPublicKeys] = useState<Record<string, PublicKey>>();
const [solanaPublicKeys, setSolanaPublicKeys] =
useState<Record<string, PublicKey>>();
const [chains, setChains] = useState<string[]>([]);
const reset = () => {
@ -73,12 +78,12 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
setIsFetchingBalances(true);
try {
const arr = await Promise.all(
_accounts.map(async account => {
_accounts.map(async (account) => {
const [namespace, reference, address] = account.split(":");
const chainId = `${namespace}:${reference}`;
const assets = await apiGetAccountBalance(address, chainId);
return { account, assets: [assets] };
}),
})
);
const balances: AccountBalances = {};
@ -93,9 +98,10 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
}
};
const onSessionConnected = useCallback(async (_session: SessionTypes.Struct) => {
const onSessionConnected = useCallback(
async (_session: SessionTypes.Struct) => {
const allNamespaceAccounts = Object.values(_session.namespaces)
.map(namespace => namespace.accounts)
.map((namespace) => namespace.accounts)
.flat();
const allNamespaceChains = Object.keys(_session.namespaces);
@ -104,17 +110,22 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
setAccounts(allNamespaceAccounts);
setSolanaPublicKeys(getPublicKeysFromAccounts(allNamespaceAccounts));
await getAccountBalances(allNamespaceAccounts);
}, []);
},
[]
);
const connect = useCallback(
async pairing => {
async (pairing: any) => {
if (typeof client === "undefined") {
throw new Error("WalletConnect is not initialized");
}
console.log("connect, pairing topic is:", pairing?.topic);
try {
const requiredNamespaces = getRequiredNamespaces(chains);
console.log("requiredNamespaces config for connect:", requiredNamespaces);
console.log(
"requiredNamespaces config for connect:",
requiredNamespaces
);
const { uri, approval } = await client.connect({
pairingTopic: pairing?.topic,
@ -141,7 +152,7 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
QRCodeModal.close();
}
},
[chains, client, onSessionConnected],
[chains, client, onSessionConnected]
);
const disconnect = useCallback(async () => {
@ -165,11 +176,11 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
throw new Error("WalletConnect is not initialized");
}
_client.on("session_ping", args => {
_client.on("session_ping", (args) => {
console.log("EVENT", "session_ping", args);
});
_client.on("session_event", args => {
_client.on("session_event", (args) => {
console.log("EVENT", "session_event", args);
});
@ -186,7 +197,7 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
reset();
});
},
[onSessionConnected],
[onSessionConnected]
);
const _checkPersistedState = useCallback(
@ -196,19 +207,24 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
}
// populates existing pairings to state
setPairings(_client.pairing.getAll({ active: true }));
console.log("RESTORED PAIRINGS: ", _client.pairing.getAll({ active: true }));
console.log(
"RESTORED PAIRINGS: ",
_client.pairing.getAll({ active: true })
);
if (typeof session !== "undefined") return;
// populates (the last) existing session to state
if (_client.session.length) {
const lastKeyIndex = _client.session.keys.length - 1;
const _session = _client.session.get(_client.session.keys[lastKeyIndex]);
const _session = _client.session.get(
_client.session.keys[lastKeyIndex]
);
console.log("RESTORED SESSION:", _session);
await onSessionConnected(_session);
return _session;
}
},
[session, onSessionConnected],
[session, onSessionConnected]
);
const createClient = useCallback(async () => {
@ -267,7 +283,7 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
connect,
disconnect,
setChains,
],
]
);
return (
@ -284,7 +300,9 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
export function useWalletConnectClient() {
const context = useContext(ClientContext);
if (context === undefined) {
throw new Error("useWalletConnectClient must be used within a ClientContextProvider");
throw new Error(
"useWalletConnectClient must be used within a ClientContextProvider"
);
}
return context;
}

View File

@ -19,7 +19,11 @@ import {
Transaction as SolanaTransaction,
} from "@solana/web3.js";
import { eip712, formatTestTransaction, getLocalStorageTestnetFlag } from "../helpers";
import {
eip712,
formatTestTransaction,
getLocalStorageTestnetFlag,
} from "../helpers";
import { useWalletConnectClient } from "./ClientContext";
import {
DEFAULT_COSMOS_METHODS,
@ -71,17 +75,27 @@ export const JsonRpcContext = createContext<IContext>({} as IContext);
/**
* Provider
*/
export function JsonRpcContextProvider({ children }: { children: ReactNode | ReactNode[] }) {
export function JsonRpcContextProvider({
children,
}: {
children: ReactNode | ReactNode[];
}) {
const [pending, setPending] = useState(false);
const [result, setResult] = useState<IFormattedRpcResponse | null>();
const [isTestnet, setIsTestnet] = useState(getLocalStorageTestnetFlag());
const { client, session, accounts, balances, solanaPublicKeys } = useWalletConnectClient();
const { client, session, accounts, balances, solanaPublicKeys } =
useWalletConnectClient();
const { chainData } = useChainData();
const _createJsonRpcRequestHandler =
(rpcRequest: (chainId: string, address: string) => Promise<IFormattedRpcResponse>) =>
(
rpcRequest: (
chainId: string,
address: string
) => Promise<IFormattedRpcResponse>
) =>
async (chainId: string, address: string) => {
if (typeof client === "undefined") {
throw new Error("WalletConnect is not initialized");
@ -106,8 +120,13 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
}
};
const _verifyEip155MessageSignature = (message: string, signature: string, address: string) =>
utils.verifyMessage(message, signature).toLowerCase() === address.toLowerCase();
const _verifyEip155MessageSignature = (
message: string,
signature: string,
address: string
) =>
utils.verifyMessage(message, signature).toLowerCase() ===
address.toLowerCase();
const ping = async () => {
if (typeof client === "undefined") {
@ -146,10 +165,14 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
// -------- ETHEREUM/EIP155 RPC METHODS --------
const ethereumRpc = {
testSendTransaction: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
testSendTransaction: _createJsonRpcRequestHandler(
async (chainId: string, address: string) => {
const caipAccountAddress = `${chainId}:${address}`;
const account = accounts.find(account => account === caipAccountAddress);
if (account === undefined) throw new Error(`Account for ${caipAccountAddress} not found`);
const account = accounts.find(
(account) => account === caipAccountAddress
);
if (account === undefined)
throw new Error(`Account for ${caipAccountAddress} not found`);
const tx = await formatTestTransaction(account);
@ -179,11 +202,16 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
valid: true,
result,
};
}),
testSignTransaction: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
}
),
testSignTransaction: _createJsonRpcRequestHandler(
async (chainId: string, address: string) => {
const caipAccountAddress = `${chainId}:${address}`;
const account = accounts.find(account => account === caipAccountAddress);
if (account === undefined) throw new Error(`Account for ${caipAccountAddress} not found`);
const account = accounts.find(
(account) => account === caipAccountAddress
);
if (account === undefined)
throw new Error(`Account for ${caipAccountAddress} not found`);
const tx = await formatTestTransaction(account);
@ -196,7 +224,9 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
},
});
const valid = EthTransaction.fromSerializedTx(signedTx as any).verifySignature();
const valid = EthTransaction.fromSerializedTx(
signedTx as any
).verifySignature();
return {
method: DEFAULT_EIP155_METHODS.ETH_SIGN_TRANSACTION,
@ -204,7 +234,8 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
valid,
result: signedTx,
};
}),
}
),
testSignPersonalMessage: _createJsonRpcRequestHandler(
async (chainId: string, address: string) => {
// test message
@ -235,7 +266,11 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
throw new Error(`Missing chain data for chainId: ${chainId}`);
}
const valid = _verifyEip155MessageSignature(message, signature, address);
const valid = _verifyEip155MessageSignature(
message,
signature,
address
);
// format displayed result
return {
@ -244,9 +279,10 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
valid,
result: signature,
};
},
}
),
testEthSign: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
testEthSign: _createJsonRpcRequestHandler(
async (chainId: string, address: string) => {
// test message
const message = `My email is john@doe.com - ${Date.now()}`;
// encode message (hex)
@ -273,7 +309,11 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
throw new Error(`Missing chain data for chainId: ${chainId}`);
}
const valid = _verifyEip155MessageSignature(message, signature, address);
const valid = _verifyEip155MessageSignature(
message,
signature,
address
);
// format displayed result
return {
@ -282,8 +322,10 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
valid,
result: signature,
};
}),
testSignTypedData: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
}
),
testSignTypedData: _createJsonRpcRequestHandler(
async (chainId: string, address: string) => {
const message = JSON.stringify(eip712.example);
// eth_signTypedData params
@ -302,12 +344,19 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
// Separate `EIP712Domain` type from remaining types to verify, otherwise `ethers.utils.verifyTypedData`
// will throw due to "unused" `EIP712Domain` type.
// See: https://github.com/ethers-io/ethers.js/issues/687#issuecomment-714069471
const { EIP712Domain, ...nonDomainTypes }: Record<string, TypedDataField[]> =
eip712.example.types;
const {
EIP712Domain,
...nonDomainTypes
}: Record<string, TypedDataField[]> = eip712.example.types;
const valid =
utils
.verifyTypedData(eip712.example.domain, nonDomainTypes, eip712.example.message, signature)
.verifyTypedData(
eip712.example.domain,
nonDomainTypes,
eip712.example.message,
signature
)
.toLowerCase() === address.toLowerCase();
return {
@ -316,13 +365,15 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
valid,
result: signature,
};
}),
}
),
};
// -------- COSMOS RPC METHODS --------
const cosmosRpc = {
testSignDirect: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
testSignDirect: _createJsonRpcRequestHandler(
async (chainId: string, address: string) => {
// test direct sign doc inputs
const inputs = {
fee: [{ amount: "2000", denom: "ucosm" }],
@ -347,7 +398,7 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
inputs.accountNumber,
inputs.sequence,
inputs.bodyBytes,
reference,
reference
);
// cosmos_signDirect params
@ -372,7 +423,11 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
throw new Error(`Missing chain data for chainId: ${chainId}`);
}
const valid = await verifyDirectSignature(address, result.signature, signDoc);
const valid = await verifyDirectSignature(
address,
result.signature,
signDoc
);
// format displayed result
return {
@ -381,8 +436,10 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
valid,
result: result.signature,
};
}),
testSignAmino: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
}
),
testSignAmino: _createJsonRpcRequestHandler(
async (chainId: string, address: string) => {
// split chainId
const [namespace, reference] = chainId.split(":");
@ -415,7 +472,11 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
throw new Error(`Missing chain data for chainId: ${chainId}`);
}
const valid = await verifyAminoSignature(address, result.signature, signDoc);
const valid = await verifyAminoSignature(
address,
result.signature,
signDoc
);
// format displayed result
return {
@ -424,21 +485,27 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
valid,
result: result.signature,
};
}),
}
),
};
// -------- SOLANA RPC METHODS --------
const solanaRpc = {
testSignTransaction: _createJsonRpcRequestHandler(
async (chainId: string, address: string): Promise<IFormattedRpcResponse> => {
async (
chainId: string,
address: string
): Promise<IFormattedRpcResponse> => {
if (!solanaPublicKeys) {
throw new Error("Could not find Solana PublicKeys.");
}
const senderPublicKey = solanaPublicKeys[address];
const connection = new Connection(clusterApiUrl(isTestnet ? "testnet" : "mainnet-beta"));
const connection = new Connection(
clusterApiUrl(isTestnet ? "testnet" : "mainnet-beta")
);
// Using deprecated `getRecentBlockhash` over `getLatestBlockhash` here, since `mainnet-beta`
// cluster only seems to support `connection.getRecentBlockhash` currently.
@ -452,7 +519,7 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
fromPubkey: senderPublicKey,
toPubkey: Keypair.generate().publicKey,
lamports: 1,
}),
})
);
try {
@ -464,10 +531,10 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
params: {
feePayer: transaction.feePayer!.toBase58(),
recentBlockhash: transaction.recentBlockhash,
instructions: transaction.instructions.map(i => ({
instructions: transaction.instructions.map((i) => ({
programId: i.programId.toBase58(),
data: bs58.encode(i.data),
keys: i.keys.map(k => ({
keys: i.keys.map((k) => ({
isSigner: k.isSigner,
isWritable: k.isWritable,
pubkey: k.pubkey.toBase58(),
@ -479,7 +546,10 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
// We only need `Buffer.from` here to satisfy the `Buffer` param type for `addSignature`.
// The resulting `UInt8Array` is equivalent to just `bs58.decode(...)`.
transaction.addSignature(senderPublicKey, Buffer.from(bs58.decode(result.signature)));
transaction.addSignature(
senderPublicKey,
Buffer.from(bs58.decode(result.signature))
);
const valid = transaction.verifySignatures();
@ -492,10 +562,13 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
} catch (error: any) {
throw new Error(error);
}
},
}
),
testSignMessage: _createJsonRpcRequestHandler(
async (chainId: string, address: string): Promise<IFormattedRpcResponse> => {
async (
chainId: string,
address: string
): Promise<IFormattedRpcResponse> => {
if (!solanaPublicKeys) {
throw new Error("Could not find Solana PublicKeys.");
}
@ -504,7 +577,9 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
// Encode message to `UInt8Array` first via `TextEncoder` so we can pass it to `bs58.encode`.
const message = bs58.encode(
new TextEncoder().encode(`This is an example message to be signed - ${Date.now()}`),
new TextEncoder().encode(
`This is an example message to be signed - ${Date.now()}`
)
);
try {
@ -523,7 +598,7 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
const valid = verifyMessageSignature(
senderPublicKey.toBase58(),
result.signature,
message,
message
);
return {
@ -535,7 +610,7 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
} catch (error: any) {
throw new Error(error);
}
},
}
),
};

View File

@ -101,7 +101,10 @@ const api: AxiosInstance = axios.create({
},
});
export async function apiGetAccountBalance(address: string, chainId: string): Promise<AssetData> {
export async function apiGetAccountBalance(
address: string,
chainId: string
): Promise<AssetData> {
const ethChainId = chainId.split(":")[1];
const rpc = rpcProvidersByChainId[Number(ethChainId)];
if (!rpc) {
@ -119,7 +122,10 @@ export async function apiGetAccountBalance(address: string, chainId: string): Pr
return { balance, ...token };
}
export const apiGetAccountNonce = async (address: string, chainId: string): Promise<number> => {
export const apiGetAccountNonce = async (
address: string,
chainId: string
): Promise<number> => {
const ethChainId = chainId.split(":")[1];
const { baseURL } = rpcProvidersByChainId[Number(ethChainId)];
const response = await api.post(baseURL, {

View File

@ -35,13 +35,13 @@ async function isValidSignature(
data: string,
provider: providers.Provider,
abi = eip1271.spec.abi,
magicValue = eip1271.spec.magicValue,
magicValue = eip1271.spec.magicValue
): Promise<boolean> {
let returnValue;
try {
returnValue = await new Contract(address, abi, provider).isValidSignature(
utils.arrayify(data),
sig,
sig
);
} catch (e) {
return false;

View File

@ -10,7 +10,7 @@ import {
export const getNamespacesFromChains = (chains: string[]) => {
const supportedNamespaces: string[] = [];
chains.forEach(chainId => {
chains.forEach((chainId) => {
const [namespace] = chainId.split(":");
if (!supportedNamespaces.includes(namespace)) {
supportedNamespaces.push(namespace);
@ -46,18 +46,20 @@ export const getSupportedEventsByNamespace = (namespace: string) => {
}
};
export const getRequiredNamespaces = (chains: string[]): ProposalTypes.RequiredNamespaces => {
export const getRequiredNamespaces = (
chains: string[]
): ProposalTypes.RequiredNamespaces => {
const selectedNamespaces = getNamespacesFromChains(chains);
console.log("selected namespaces:", selectedNamespaces);
return Object.fromEntries(
selectedNamespaces.map(namespace => [
selectedNamespaces.map((namespace) => [
namespace,
{
methods: getSupportedMethodsByNamespace(namespace),
chains: chains.filter(chain => chain.startsWith(namespace)),
chains: chains.filter((chain) => chain.startsWith(namespace)),
events: getSupportedEventsByNamespace(namespace) as any[],
},
]),
])
);
};

View File

@ -4,12 +4,14 @@ export function getPublicKeysFromAccounts(accounts: string[]) {
return (
accounts
// Filter out any non-solana accounts.
.filter(account => account.startsWith("solana:"))
.filter((account) => account.startsWith("solana:"))
// Create a map of Solana address -> publicKey.
.reduce((map: Record<string, PublicKey>, account) => {
const address = account.split(":").pop();
if (!address) {
throw new Error(`Could not derive Solana address from CAIP account: ${account}`);
throw new Error(
`Could not derive Solana address from CAIP account: ${account}`
);
}
map[address] = new PublicKey(address);
return map;

View File

@ -10,7 +10,9 @@ export async function formatTestTransaction(account: string) {
try {
_nonce = await apiGetAccountNonce(address, chainId);
} catch (error) {
throw new Error(`Failed to fetch nonce for address ${address} on chain ${chainId}`);
throw new Error(
`Failed to fetch nonce for address ${address} on chain ${chainId}`
);
}
const nonce = encoding.sanitizeHex(encoding.numberToHex(_nonce));
@ -27,7 +29,15 @@ export async function formatTestTransaction(account: string) {
const _value = 0;
const value = encoding.sanitizeHex(encoding.numberToHex(_value));
const tx = { from: address, to: address, data: "0x", nonce, gasPrice, gasLimit, value };
const tx = {
from: address,
to: address,
data: "0x",
nonce,
gasPrice,
gasLimit,
value,
};
return tx;
}

View File

@ -9,7 +9,7 @@ import { DEFAULT_CHAINS } from "../constants";
export function capitalize(string: string): string {
return string
.split(" ")
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(" ");
}
@ -23,7 +23,7 @@ export function ellipseText(text = "", maxLength = 9999): string {
const result =
text
.split(" ")
.filter(word => {
.filter((word) => {
currentLength += word.length;
if (ellipse || currentLength >= _maxLength) {
ellipse = true;
@ -64,10 +64,10 @@ export function isMobile(): boolean {
function hasMobileUserAgent(): boolean {
if (
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
navigator.userAgent,
navigator.userAgent
) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(
navigator.userAgent.substr(0, 4),
navigator.userAgent.substr(0, 4)
)
) {
return true;
@ -85,7 +85,10 @@ export function isMobile(): boolean {
export function encodePersonalMessage(msg: string): string {
const data = encoding.utf8ToBuffer(msg);
const buf = Buffer.concat([
Buffer.from("\u0019Ethereum Signed Message:\n" + data.length.toString(), "utf8"),
Buffer.from(
"\u0019Ethereum Signed Message:\n" + data.length.toString(),
"utf8"
),
data,
]);
return ethUtil.bufferToHex(buf);
@ -103,7 +106,11 @@ export function encodeTypedDataMessage(msg: string): string {
const buf = Buffer.concat([
Buffer.from("1901", "hex"),
TypedDataUtils.hashStruct("EIP712Domain", data.domain, data.types),
TypedDataUtils.hashStruct(data.primaryType as string, data.message, data.types),
TypedDataUtils.hashStruct(
data.primaryType as string,
data.message,
data.types
),
]);
return ethUtil.bufferToHex(buf);
}
@ -117,7 +124,12 @@ export function hashTypedDataMessage(msg: string): string {
export function recoverAddress(sig: string, hash: string): string {
const params = ethUtil.fromRpcSig(sig);
const result = ethUtil.ecrecover(ethUtil.toBuffer(hash), params.v, params.r, params.s);
const result = ethUtil.ecrecover(
ethUtil.toBuffer(hash),
params.v,
params.r,
params.s
);
const signer = ethUtil.bufferToHex(ethUtil.publicToAddress(result));
return signer;
}
@ -138,11 +150,16 @@ export async function verifySignature(
address: string,
sig: string,
hash: string,
rpcUrl: string,
rpcUrl: string
): Promise<boolean> {
const provider = new providers.JsonRpcProvider(rpcUrl);
const bytecode = await provider.getCode(address);
if (!bytecode || bytecode === "0x" || bytecode === "0x0" || bytecode === "0x00") {
if (
!bytecode ||
bytecode === "0x" ||
bytecode === "0x0" ||
bytecode === "0x00"
) {
const signer = recoverAddress(sig, hash);
return signer.toLowerCase() === address.toLowerCase();
} else {
@ -186,10 +203,13 @@ export const LOCALSTORAGE_KEY_TESTNET = "TESTNET";
export const INITIAL_STATE_TESTNET_DEFAULT = true;
export function setLocaleStorageTestnetFlag(value: boolean): void {
if (typeof window !== "undefined") {
window.localStorage.setItem(LOCALSTORAGE_KEY_TESTNET, `${value}`);
}
}
export function getLocalStorageTestnetFlag(): boolean {
if (typeof window === "undefined") return false;
let value = INITIAL_STATE_TESTNET_DEFAULT;
const persisted = window.localStorage.getItem(LOCALSTORAGE_KEY_TESTNET);
if (!persisted) {
@ -202,7 +222,7 @@ export function getLocalStorageTestnetFlag(): boolean {
export const getAllChainNamespaces = () => {
const namespaces: string[] = [];
DEFAULT_CHAINS.forEach(chainId => {
DEFAULT_CHAINS.forEach((chainId) => {
const [namespace] = chainId.split(":");
if (!namespaces.includes(namespace)) {
namespaces.push(namespace);

View File

@ -1,34 +0,0 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { createGlobalStyle } from "styled-components";
import { ClientContextProvider } from "./contexts/ClientContext";
import { JsonRpcContextProvider } from "./contexts/JsonRpcContext";
import { ChainDataContextProvider } from "./contexts/ChainDataContext";
import App from "./App";
import { globalStyle } from "./styles";
const GlobalStyle = createGlobalStyle`
${globalStyle}
`;
declare global {
// tslint:disable-next-line
interface Window {
blockies: any;
}
}
ReactDOM.render(
<>
<GlobalStyle />
<ChainDataContextProvider>
<ClientContextProvider>
<JsonRpcContextProvider>
<App />
</JsonRpcContextProvider>
</ClientContextProvider>
</ChainDataContextProvider>
</>,
document.getElementById("root"),
);

View File

@ -19,7 +19,7 @@ const PairingModal = (props: PairingModalProps) => {
<SModalContainer>
<SModalTitle>{"Select available pairing or create new one"}</SModalTitle>
<STable>
{pairings.map(pairing => (
{pairings.map((pairing) => (
<Pairing
key={pairing.topic}
pairing={pairing}

View File

@ -19,16 +19,20 @@ const RequestModal = (props: RequestModalProps) => {
<SModalTitle>{"Pending JSON-RPC Request"}</SModalTitle>
<SContainer>
<Loader />
<SModalParagraph>{"Approve or reject request using your wallet"}</SModalParagraph>
<SModalParagraph>
{"Approve or reject request using your wallet"}
</SModalParagraph>
</SContainer>
</SModalContainer>
) : result ? (
<SModalContainer>
<SModalTitle>
{result.valid ? "JSON-RPC Request Approved" : "JSON-RPC Request Failed"}
{result.valid
? "JSON-RPC Request Approved"
: "JSON-RPC Request Failed"}
</SModalTitle>
<STable>
{Object.keys(result).map(key => (
{Object.keys(result).map((key) => (
<SRow key={key}>
<SKey>{key}</SKey>
<SValue>{result[key].toString()}</SValue>

View File

@ -0,0 +1,3 @@
export default function FourOhFour() {
return <h1>404 Page Not Found</h1>;
}

View File

@ -0,0 +1,29 @@
import "../styles/globals.css";
import type { AppProps } from "next/app";
import { createGlobalStyle } from "styled-components";
import { ClientContextProvider } from "../contexts/ClientContext";
import { JsonRpcContextProvider } from "../contexts/JsonRpcContext";
import { ChainDataContextProvider } from "../contexts/ChainDataContext";
import { globalStyle } from "../styles";
const GlobalStyle = createGlobalStyle`
${globalStyle}
`;
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<GlobalStyle />
<ChainDataContextProvider>
<ClientContextProvider>
<JsonRpcContextProvider>
<Component {...pageProps} />
</JsonRpcContextProvider>
</ClientContextProvider>
</ChainDataContextProvider>
</>
);
}
export default MyApp;

View File

@ -0,0 +1,3 @@
export default function Error() {
return <div>An error as occured</div>;
}

View File

@ -1,23 +1,23 @@
import type { NextPage } from "next";
import React, { useEffect, useState } from "react";
import { version } from "@walletconnect/sign-client/package.json";
import Banner from "./components/Banner";
import Blockchain from "./components/Blockchain";
import Column from "./components/Column";
import Header from "./components/Header";
import Modal from "./components/Modal";
import Banner from "../components/Banner";
import Blockchain from "../components/Blockchain";
import Column from "../components/Column";
import Header from "../components/Header";
import Modal from "../components/Modal";
import {
DEFAULT_COSMOS_METHODS,
DEFAULT_EIP155_METHODS,
DEFAULT_MAIN_CHAINS,
DEFAULT_SOLANA_METHODS,
DEFAULT_TEST_CHAINS,
} from "./constants";
import { AccountAction, setLocaleStorageTestnetFlag } from "./helpers";
import Toggle from "./components/Toggle";
import RequestModal from "./modals/RequestModal";
import PairingModal from "./modals/PairingModal";
import PingModal from "./modals/PingModal";
} from "../constants";
import { AccountAction, setLocaleStorageTestnetFlag } from "../helpers";
import Toggle from "../components/Toggle";
import RequestModal from "../modals/RequestModal";
import PairingModal from "../modals/PairingModal";
import PingModal from "../modals/PingModal";
import {
SAccounts,
SAccountsContainer,
@ -27,12 +27,15 @@ import {
SLanding,
SLayout,
SToggleContainer,
} from "./components/app";
import { useWalletConnectClient } from "./contexts/ClientContext";
import { useJsonRpc } from "./contexts/JsonRpcContext";
import { useChainData } from "./contexts/ChainDataContext";
} from "../components/app";
import { useWalletConnectClient } from "../contexts/ClientContext";
import { useJsonRpc } from "../contexts/JsonRpcContext";
import { useChainData } from "../contexts/ChainDataContext";
export default function App() {
// Normal import does not work here
const { version } = require("@walletconnect/sign-client/package.json");
const Home: NextPage = () => {
const [modal, setModal] = useState("");
const closeModal = () => setModal("");
@ -117,11 +120,26 @@ export default function App() {
};
return [
{ method: DEFAULT_EIP155_METHODS.ETH_SEND_TRANSACTION, callback: onSendTransaction },
{ method: DEFAULT_EIP155_METHODS.ETH_SIGN_TRANSACTION, callback: onSignTransaction },
{ method: DEFAULT_EIP155_METHODS.PERSONAL_SIGN, callback: onSignPersonalMessage },
{ method: DEFAULT_EIP155_METHODS.ETH_SIGN + " (standard)", callback: onEthSign },
{ method: DEFAULT_EIP155_METHODS.ETH_SIGN_TYPED_DATA, callback: onSignTypedData },
{
method: DEFAULT_EIP155_METHODS.ETH_SEND_TRANSACTION,
callback: onSendTransaction,
},
{
method: DEFAULT_EIP155_METHODS.ETH_SIGN_TRANSACTION,
callback: onSignTransaction,
},
{
method: DEFAULT_EIP155_METHODS.PERSONAL_SIGN,
callback: onSignPersonalMessage,
},
{
method: DEFAULT_EIP155_METHODS.ETH_SIGN + " (standard)",
callback: onEthSign,
},
{
method: DEFAULT_EIP155_METHODS.ETH_SIGN_TYPED_DATA,
callback: onSignTypedData,
},
];
};
@ -135,8 +153,14 @@ export default function App() {
await cosmosRpc.testSignAmino(chainId, address);
};
return [
{ method: DEFAULT_COSMOS_METHODS.COSMOS_SIGN_DIRECT, callback: onSignDirect },
{ method: DEFAULT_COSMOS_METHODS.COSMOS_SIGN_AMINO, callback: onSignAmino },
{
method: DEFAULT_COSMOS_METHODS.COSMOS_SIGN_DIRECT,
callback: onSignDirect,
},
{
method: DEFAULT_COSMOS_METHODS.COSMOS_SIGN_AMINO,
callback: onSignAmino,
},
];
};
@ -150,8 +174,14 @@ export default function App() {
await solanaRpc.testSignMessage(chainId, address);
};
return [
{ method: DEFAULT_SOLANA_METHODS.SOL_SIGN_TRANSACTION, callback: onSignTransaction },
{ method: DEFAULT_SOLANA_METHODS.SOL_SIGN_MESSAGE, callback: onSignMessage },
{
method: DEFAULT_SOLANA_METHODS.SOL_SIGN_TRANSACTION,
callback: onSignTransaction,
},
{
method: DEFAULT_SOLANA_METHODS.SOL_SIGN_MESSAGE,
callback: onSignMessage,
},
];
};
@ -178,7 +208,7 @@ export default function App() {
const handleChainSelectionClick = (chainId: string) => {
if (chains.includes(chainId)) {
setChains(chains.filter(chain => chain !== chainId));
setChains(chains.filter((chain) => chain !== chainId));
} else {
setChains([...chains, chainId]);
}
@ -193,7 +223,9 @@ export default function App() {
}
return <PairingModal pairings={pairings} connect={connect} />;
case "request":
return <RequestModal pending={isRpcRequestPending} result={rpcResult} />;
return (
<RequestModal pending={isRpcRequestPending} result={rpcResult} />
);
case "ping":
return <PingModal pending={isRpcRequestPending} result={rpcResult} />;
default:
@ -213,7 +245,7 @@ export default function App() {
<p>Testnets Only?</p>
<Toggle active={isTestnet} onClick={toggleTestnets} />
</SToggleContainer>
{chainOptions.map(chainId => (
{chainOptions.map((chainId) => (
<Blockchain
key={chainId}
chainId={chainId}
@ -231,7 +263,7 @@ export default function App() {
<SAccountsContainer>
<h3>Accounts</h3>
<SAccounts>
{accounts.map(account => {
{accounts.map((account) => {
const [namespace, reference, address] = account.split(":");
const chainId = `${namespace}:${reference}`;
return (
@ -263,4 +295,6 @@ export default function App() {
</Modal>
</SLayout>
);
}
};
export default Home;

View File

@ -1 +0,0 @@
/// <reference types="react-scripts" />

View File

@ -0,0 +1,129 @@
.container {
padding: 0 2rem;
}
.main {
min-height: 100vh;
padding: 4rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.footer {
display: flex;
flex: 1;
padding: 2rem 0;
border-top: 1px solid #eaeaea;
justify-content: center;
align-items: center;
}
.footer a {
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
}
.title a {
color: #0070f3;
text-decoration: none;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
}
.title,
.description {
text-align: center;
}
.description {
margin: 4rem 0;
line-height: 1.5;
font-size: 1.5rem;
}
.code {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
}
.card {
margin: 1rem;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
max-width: 300px;
}
.card:hover,
.card:focus,
.card:active {
color: #0070f3;
border-color: #0070f3;
}
.card h2 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
.logo {
height: 1em;
margin-left: 0.5rem;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}
@media (prefers-color-scheme: dark) {
.card,
.footer {
border-color: #222;
}
.code {
background: #111;
}
.logo img {
filter: invert(1);
}
}

View File

@ -0,0 +1,26 @@
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
body {
color: white;
background: black;
}
}

View File

@ -1,26 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
"jsx": "preserve",
"incremental": true
},
"include": [
"src"
]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

File diff suppressed because it is too large Load Diff