refactor(dapp): support signing from different addresses on same network

This commit is contained in:
Ben Kremer 2022-02-21 11:31:07 +01:00
parent d4c670bdf3
commit 3c2684f851
4 changed files with 81 additions and 101 deletions

View File

@ -77,25 +77,25 @@ export default function App() {
}; };
const getEthereumActions = (): AccountAction[] => { const getEthereumActions = (): AccountAction[] => {
const onSendTransaction = async (chainId: string) => { const onSendTransaction = async (chainId: string, address: string) => {
openRequestModal(); openRequestModal();
await ethereumRpc.testSendTransaction(chainId); await ethereumRpc.testSendTransaction(chainId, address);
}; };
const onSignTransaction = async (chainId: string) => { const onSignTransaction = async (chainId: string, address: string) => {
openRequestModal(); openRequestModal();
await ethereumRpc.testSignTransaction(chainId); await ethereumRpc.testSignTransaction(chainId, address);
}; };
const onSignPersonalMessage = async (chainId: string) => { const onSignPersonalMessage = async (chainId: string, address: string) => {
openRequestModal(); openRequestModal();
await ethereumRpc.testSignPersonalMessage(chainId); await ethereumRpc.testSignPersonalMessage(chainId, address);
}; };
const onEthSign = async (chainId: string) => { const onEthSign = async (chainId: string, address: string) => {
openRequestModal(); openRequestModal();
await ethereumRpc.testEthSign(chainId); await ethereumRpc.testEthSign(chainId, address);
}; };
const onSignTypedData = async (chainId: string) => { const onSignTypedData = async (chainId: string, address: string) => {
openRequestModal(); openRequestModal();
await ethereumRpc.testSignTypedData(chainId); await ethereumRpc.testSignTypedData(chainId, address);
}; };
return [ return [
@ -108,13 +108,13 @@ export default function App() {
}; };
const getCosmosActions = (): AccountAction[] => { const getCosmosActions = (): AccountAction[] => {
const onSignDirect = async (chainId: string) => { const onSignDirect = async (chainId: string, address: string) => {
openRequestModal(); openRequestModal();
await cosmosRpc.testSignDirect(chainId); await cosmosRpc.testSignDirect(chainId, address);
}; };
const onSignAmino = async (chainId: string) => { const onSignAmino = async (chainId: string, address: string) => {
openRequestModal(); openRequestModal();
await cosmosRpc.testSignAmino(chainId); await cosmosRpc.testSignAmino(chainId, address);
}; };
return [ return [
{ method: "cosmos_signDirect", callback: onSignDirect }, { method: "cosmos_signDirect", callback: onSignDirect },

View File

@ -155,7 +155,7 @@ const Blockchain: FC<PropsWithChildren<BlockchainProps>> = (
</Column> </Column>
</SFullWidthContainer> </SFullWidthContainer>
) : null} ) : null}
{!!actions && actions.length ? ( {address && !!actions && actions.length ? (
<SFullWidthContainer> <SFullWidthContainer>
<h6>Methods</h6> <h6>Methods</h6>
{actions.map(action => ( {actions.map(action => (
@ -163,7 +163,7 @@ const Blockchain: FC<PropsWithChildren<BlockchainProps>> = (
key={action.method} key={action.method}
left left
rgb={chain.meta.rgb} rgb={chain.meta.rgb}
onClick={() => action.callback(chainId)} onClick={() => action.callback(chainId, address)}
> >
{action.method} {action.method}
</SAction> </SAction>

View File

@ -28,18 +28,20 @@ interface IRpcResult {
valid: boolean; valid: boolean;
} }
type TRpcRequestCallback = (chainId: string, address: string) => Promise<void>;
interface IContext { interface IContext {
ping: () => Promise<void>; ping: () => Promise<void>;
ethereumRpc: { ethereumRpc: {
testSendTransaction: (chainId: string) => Promise<void>; testSendTransaction: TRpcRequestCallback;
testSignTransaction: (chainId: string) => Promise<void>; testSignTransaction: TRpcRequestCallback;
testEthSign: (chainId: string) => Promise<void>; testEthSign: TRpcRequestCallback;
testSignPersonalMessage: (chainId: string) => Promise<void>; testSignPersonalMessage: TRpcRequestCallback;
testSignTypedData: (chainId: string) => Promise<void>; testSignTypedData: TRpcRequestCallback;
}; };
cosmosRpc: { cosmosRpc: {
testSignDirect: (chainId: string) => Promise<void>; testSignDirect: TRpcRequestCallback;
testSignAmino: (chainId: string) => Promise<void>; testSignAmino: TRpcRequestCallback;
}; };
chainData: ChainNamespaces; chainData: ChainNamespaces;
rpcResult?: IRpcResult | null; rpcResult?: IRpcResult | null;
@ -84,18 +86,9 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
setChainData(chainData); setChainData(chainData);
}; };
const getAddressByChainId = (chainId: string) => {
const account = accounts.find(account => account.startsWith(chainId));
if (account === undefined) throw new Error(`Account for chainId ${chainId} not found.`);
const address = account.split(":").pop();
if (address === undefined) throw new Error(`Address for account ${account} is invalid`);
return address;
};
const _createJsonRpcRequestHandler = const _createJsonRpcRequestHandler =
(rpcRequest: (...requestArgs: [any]) => Promise<IFormattedRpcResponse>) => (rpcRequest: (chainId: string, address: string) => Promise<IFormattedRpcResponse>) =>
async (chainId: string) => { async (chainId: string, address: string) => {
if (typeof client === "undefined") { if (typeof client === "undefined") {
throw new Error("WalletConnect is not initialized"); throw new Error("WalletConnect is not initialized");
} }
@ -105,7 +98,7 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
try { try {
setPending(true); setPending(true);
const result = await rpcRequest(chainId); const result = await rpcRequest(chainId, address);
setResult(result); setResult(result);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
@ -151,12 +144,10 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
// -------- ETHEREUM/EIP155 RPC METHODS -------- // -------- ETHEREUM/EIP155 RPC METHODS --------
const ethereumRpc = { const ethereumRpc = {
testSendTransaction: _createJsonRpcRequestHandler(async (chainId: string) => { testSendTransaction: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
// get ethereum address const caipAccountAddress = `${chainId}:${address}`;
const account = accounts.find(account => account.startsWith(chainId)); const account = accounts.find(account => account === caipAccountAddress);
if (account === undefined) throw new Error("Account is not found"); if (account === undefined) throw new Error(`Account for ${caipAccountAddress} not found`);
const address = account.split(":").pop();
if (address === undefined) throw new Error("Address is invalid");
const tx = await formatTestTransaction(account); const tx = await formatTestTransaction(account);
@ -185,8 +176,6 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
console.error(error); console.error(error);
} }
console.log(result);
// format displayed result // format displayed result
return { return {
method: "eth_sendTransaction", method: "eth_sendTransaction",
@ -195,12 +184,10 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
result, result,
}; };
}), }),
testSignTransaction: _createJsonRpcRequestHandler(async (chainId: string) => { testSignTransaction: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
// get ethereum address const caipAccountAddress = `${chainId}:${address}`;
const account = accounts.find(account => account.startsWith(chainId)); const account = accounts.find(account => account === caipAccountAddress);
if (account === undefined) throw new Error("Account is not found"); if (account === undefined) throw new Error(`Account for ${caipAccountAddress} not found`);
const address = account.split(":").pop();
if (address === undefined) throw new Error("Address is invalid");
const tx = await formatTestTransaction(account); const tx = await formatTestTransaction(account);
@ -220,54 +207,52 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
result, result,
}; };
}), }),
testSignPersonalMessage: _createJsonRpcRequestHandler(async (chainId: string) => { testSignPersonalMessage: _createJsonRpcRequestHandler(
// test message async (chainId: string, address: string) => {
const message = `My email is john@doe.com - ${Date.now()}`; // test message
const message = `My email is john@doe.com - ${Date.now()}`;
// encode message (hex) // encode message (hex)
const hexMsg = encoding.utf8ToHex(message, true); const hexMsg = encoding.utf8ToHex(message, true);
const address = getAddressByChainId(chainId); // personal_sign params
const params = [hexMsg, address];
// personal_sign params // send message
const params = [hexMsg, address]; const result: string = await client!.request({
topic: session!.topic,
chainId,
request: {
method: "personal_sign",
params,
},
});
// send message // split chainId
const result: string = await client!.request({ const [namespace, reference] = chainId.split(":");
topic: session!.topic,
chainId, const targetChainData = chainData[namespace][reference];
request: {
if (typeof targetChainData === "undefined") {
throw new Error(`Missing chain data for chainId: ${chainId}`);
}
const rpcUrl = targetChainData.rpc[0];
// verify signature
const hash = hashPersonalMessage(message);
const valid = await verifySignature(address, result, hash, rpcUrl);
// format displayed result
return {
method: "personal_sign", method: "personal_sign",
params, address,
}, valid,
}); result,
};
// split chainId },
const [namespace, reference] = chainId.split(":"); ),
testEthSign: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
const targetChainData = chainData[namespace][reference];
if (typeof targetChainData === "undefined") {
throw new Error(`Missing chain data for chainId: ${chainId}`);
}
const rpcUrl = targetChainData.rpc[0];
// verify signature
const hash = hashPersonalMessage(message);
const valid = await verifySignature(address, result, hash, rpcUrl);
// format displayed result
return {
method: "personal_sign",
address,
valid,
result,
};
}),
testEthSign: _createJsonRpcRequestHandler(async (chainId: string) => {
const address = getAddressByChainId(chainId);
// test message // test message
const message = `My email is john@doe.com - ${Date.now()}`; const message = `My email is john@doe.com - ${Date.now()}`;
// encode message (hex) // encode message (hex)
@ -308,7 +293,7 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
result, result,
}; };
}), }),
testSignTypedData: _createJsonRpcRequestHandler(async (chainId: string) => { testSignTypedData: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
const typedData = { const typedData = {
types: { types: {
Person: [ Person: [
@ -341,7 +326,6 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
}, },
}; };
const address = getAddressByChainId(chainId);
const message = JSON.stringify(typedData); const message = JSON.stringify(typedData);
// eth_signTypedData params // eth_signTypedData params
@ -372,7 +356,7 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
// -------- COSMOS RPC METHODS -------- // -------- COSMOS RPC METHODS --------
const cosmosRpc = { const cosmosRpc = {
testSignDirect: _createJsonRpcRequestHandler(async (chainId: string) => { testSignDirect: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
// test direct sign doc inputs // test direct sign doc inputs
const inputs = { const inputs = {
fee: [{ amount: "2000", denom: "ucosm" }], fee: [{ amount: "2000", denom: "ucosm" }],
@ -400,8 +384,6 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
reference, reference,
); );
const address = getAddressByChainId(chainId);
// cosmos_signDirect params // cosmos_signDirect params
const params = { const params = {
signerAddress: address, signerAddress: address,
@ -435,7 +417,7 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
result: result.signature.signature, result: result.signature.signature,
}; };
}), }),
testSignAmino: _createJsonRpcRequestHandler(async (chainId: string) => { testSignAmino: _createJsonRpcRequestHandler(async (chainId: string, address: string) => {
// split chainId // split chainId
const [namespace, reference] = chainId.split(":"); const [namespace, reference] = chainId.split(":");
@ -449,8 +431,6 @@ export function JsonRpcContextProvider({ children }: { children: ReactNode | Rea
sequence: "54", sequence: "54",
}; };
const address = getAddressByChainId(chainId);
// cosmos_signAmino params // cosmos_signAmino params
const params = { signerAddress: address, signDoc }; const params = { signerAddress: address, signDoc };

View File

@ -151,7 +151,7 @@ export interface ChainNamespaces {
export interface AccountAction { export interface AccountAction {
method: string; method: string;
callback: (chainId: string) => Promise<void>; callback: (chainId: string, address: string) => Promise<void>;
} }
export interface AccountBalances { export interface AccountBalances {