refactor(dapp-v2): splits requested methods between required/optional (#196)

This commit is contained in:
Ben Kremer 2023-06-28 09:51:13 +02:00 committed by GitHub
parent dd54b0b6ed
commit 03155d1c23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 125 additions and 58 deletions

View File

@ -51,9 +51,12 @@ export const DEFAULT_APP_METADATA = {
*/
export enum DEFAULT_EIP155_METHODS {
ETH_SEND_TRANSACTION = "eth_sendTransaction",
PERSONAL_SIGN = "personal_sign",
}
export enum DEFAULT_EIP155_OPTIONAL_METHODS {
ETH_SIGN_TRANSACTION = "eth_signTransaction",
ETH_SIGN = "eth_sign",
PERSONAL_SIGN = "personal_sign",
ETH_SIGN_TYPED_DATA = "eth_signTypedData",
}

View File

@ -22,7 +22,10 @@ import {
DEFAULT_RELAY_URL,
} from "../constants";
import { AccountBalances, apiGetAccountBalance } from "../helpers";
import { getRequiredNamespaces } from "../helpers/namespaces";
import {
getOptionalNamespaces,
getRequiredNamespaces,
} from "../helpers/namespaces";
import { getPublicKeysFromAccounts } from "../helpers/solana";
/**
@ -144,10 +147,16 @@ export function ClientContextProvider({
"requiredNamespaces config for connect:",
requiredNamespaces
);
const optionalNamespaces = getOptionalNamespaces(chains);
console.log(
"optionalNamespaces config for connect:",
optionalNamespaces
);
const { uri, approval } = await client.connect({
pairingTopic: pairing?.topic,
requiredNamespaces,
optionalNamespaces,
});
// Open QRCode modal if a URI was returned (i.e. we're not connecting an existing pairing).

View File

@ -39,6 +39,7 @@ import {
DEFAULT_MULTIVERSX_METHODS,
DEFAULT_TRON_METHODS,
DEFAULT_TEZOS_METHODS,
DEFAULT_EIP155_OPTIONAL_METHODS,
} from "../constants";
import { useChainData } from "./ChainDataContext";
import { rpcProvidersByChainId } from "../../src/helpers/api";
@ -261,7 +262,7 @@ export function JsonRpcContextProvider({
topic: session!.topic,
chainId,
request: {
method: DEFAULT_EIP155_METHODS.ETH_SIGN_TRANSACTION,
method: DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN_TRANSACTION,
params: [tx],
},
});
@ -284,7 +285,7 @@ export function JsonRpcContextProvider({
}
return {
method: DEFAULT_EIP155_METHODS.ETH_SIGN_TRANSACTION,
method: DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN_TRANSACTION,
address,
valid,
result: signedTx,
@ -352,7 +353,7 @@ export function JsonRpcContextProvider({
topic: session!.topic,
chainId,
request: {
method: DEFAULT_EIP155_METHODS.ETH_SIGN,
method: DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN,
params,
},
});
@ -377,7 +378,7 @@ export function JsonRpcContextProvider({
// format displayed result
return {
method: DEFAULT_EIP155_METHODS.ETH_SIGN + " (standard)",
method: DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN + " (standard)",
address,
valid,
result: signature,
@ -396,7 +397,7 @@ export function JsonRpcContextProvider({
topic: session!.topic,
chainId,
request: {
method: DEFAULT_EIP155_METHODS.ETH_SIGN_TYPED_DATA,
method: DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN_TYPED_DATA,
params,
},
});
@ -420,7 +421,7 @@ export function JsonRpcContextProvider({
);
return {
method: DEFAULT_EIP155_METHODS.ETH_SIGN_TYPED_DATA,
method: DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN_TYPED_DATA,
address,
valid,
result: signature,

View File

@ -16,6 +16,7 @@ import {
DEFAULT_TRON_EVENTS,
DEFAULT_TEZOS_METHODS,
DEFAULT_TEZOS_EVENTS,
DEFAULT_EIP155_OPTIONAL_METHODS,
} from "../constants";
export const getNamespacesFromChains = (chains: string[]) => {
@ -30,7 +31,7 @@ export const getNamespacesFromChains = (chains: string[]) => {
return supportedNamespaces;
};
export const getSupportedMethodsByNamespace = (namespace: string) => {
export const getSupportedRequiredMethodsByNamespace = (namespace: string) => {
switch (namespace) {
case "eip155":
return Object.values(DEFAULT_EIP155_METHODS);
@ -49,7 +50,28 @@ export const getSupportedMethodsByNamespace = (namespace: string) => {
case "tezos":
return Object.values(DEFAULT_TEZOS_METHODS);
default:
throw new Error(`No default methods for namespace: ${namespace}`);
throw new Error(
`No default required methods for namespace: ${namespace}`
);
}
};
export const getSupportedOptionalMethodsByNamespace = (namespace: string) => {
switch (namespace) {
case "eip155":
return Object.values(DEFAULT_EIP155_OPTIONAL_METHODS);
case "cosmos":
case "solana":
case "polkadot":
case "near":
case "mvx":
case "tron":
case "tezos":
return [];
default:
throw new Error(
`No default optional methods for namespace: ${namespace}`
);
}
};
@ -80,16 +102,34 @@ export const getRequiredNamespaces = (
chains: string[]
): ProposalTypes.RequiredNamespaces => {
const selectedNamespaces = getNamespacesFromChains(chains);
console.log("selected namespaces:", selectedNamespaces);
console.log("selected required namespaces:", selectedNamespaces);
return Object.fromEntries(
selectedNamespaces.map((namespace) => [
namespace,
{
methods: getSupportedMethodsByNamespace(namespace),
methods: getSupportedRequiredMethodsByNamespace(namespace),
chains: chains.filter((chain) => chain.startsWith(namespace)),
events: getSupportedEventsByNamespace(namespace) as any[],
},
])
);
};
export const getOptionalNamespaces = (
chains: string[]
): ProposalTypes.OptionalNamespaces => {
const selectedNamespaces = getNamespacesFromChains(chains);
console.log("selected optional namespaces:", selectedNamespaces);
return Object.fromEntries(
selectedNamespaces.map((namespace) => [
namespace,
{
methods: getSupportedOptionalMethodsByNamespace(namespace),
chains: chains.filter((chain) => chain.startsWith(namespace)),
events: [],
},
])
);
};

View File

@ -18,6 +18,7 @@ import {
DEFAULT_NEAR_METHODS,
DEFAULT_TRON_METHODS,
DEFAULT_TEZOS_METHODS,
DEFAULT_EIP155_OPTIONAL_METHODS,
} from "../constants";
import { AccountAction, setLocaleStorageTestnetFlag } from "../helpers";
import Toggle from "../components/Toggle";
@ -123,49 +124,55 @@ const Home: NextPage = () => {
}
const getEthereumActions = (): AccountAction[] => {
const onSendTransaction = async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testSendTransaction(chainId, address);
};
const onSignTransaction = async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testSignTransaction(chainId, address);
};
const onSignPersonalMessage = async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testSignPersonalMessage(chainId, address);
};
const onEthSign = async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testEthSign(chainId, address);
};
const onSignTypedData = async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testSignTypedData(chainId, address);
const actions = {
[DEFAULT_EIP155_METHODS.ETH_SEND_TRANSACTION]: {
method: DEFAULT_EIP155_METHODS.ETH_SEND_TRANSACTION,
callback: async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testSendTransaction(chainId, address);
},
},
[DEFAULT_EIP155_METHODS.PERSONAL_SIGN]: {
method: DEFAULT_EIP155_METHODS.PERSONAL_SIGN,
callback: async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testSignPersonalMessage(chainId, address);
},
},
[DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN_TRANSACTION]: {
method: DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN_TRANSACTION,
callback: async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testSignTransaction(chainId, address);
},
},
[DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN]: {
method: DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN + " (standard)",
callback: async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testEthSign(chainId, address);
},
},
[DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN_TYPED_DATA]: {
method: DEFAULT_EIP155_OPTIONAL_METHODS.ETH_SIGN_TYPED_DATA,
callback: async (chainId: string, address: string) => {
openRequestModal();
await ethereumRpc.testSignTypedData(chainId, address);
},
},
};
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,
},
];
let availableActions: AccountAction[] = [];
session?.namespaces?.["eip155"].methods.forEach((methodName) => {
const action: AccountAction | undefined =
actions[methodName as keyof typeof actions];
if (action) {
availableActions.push(action);
}
});
return availableActions;
};
const getCosmosActions = (): AccountAction[] => {

View File

@ -23,7 +23,7 @@ import { solanaAddresses } from '@/utils/SolanaWalletUtil'
import { signClient } from '@/utils/WalletConnectUtil'
import { Button, Divider, Modal, Text } from '@nextui-org/react'
import { SessionTypes } from '@walletconnect/types'
import { getSdkError } from '@walletconnect/utils'
import { getSdkError, mergeArrays } from '@walletconnect/utils'
import { Fragment, useEffect, useState } from 'react'
import { nearAddresses } from '@/utils/NearWalletUtil'
@ -89,18 +89,24 @@ export default function SessionProposalModal() {
}
if (optionalNamespaces[key] && selectedAccounts[`optional:${key}`]) {
optionalNamespaces[key].chains?.map(chain => {
selectedAccounts[`optional:${key}`].map(acc => accounts.push(`${chain}:${acc}`))
selectedAccounts[`optional:${key}`].forEach(acc => {
if (!accounts.includes(`${chain}:${acc}`)) {
accounts.push(`${chain}:${acc}`)
}
})
})
namespaces[key] = {
...namespaces[key],
accounts,
methods: optionalNamespaces[key].methods,
events: optionalNamespaces[key].events,
chains: namespaces[key]?.chains?.concat(optionalNamespaces[key].chains || [])
methods: mergeArrays(namespaces[key].methods, optionalNamespaces[key].methods),
events: mergeArrays(namespaces[key].events, optionalNamespaces[key].events),
chains: mergeArrays(namespaces[key].chains, optionalNamespaces[key].chains)
}
}
})
console.log('approving namespaces:', namespaces)
await signClient.approve({
id,
relayProtocol: relays[0].protocol,

View File

@ -13,6 +13,7 @@
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"downlevelIteration": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,