diff --git a/src/App.tsx b/src/App.tsx index 92b34b2..0a94d35 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -64,6 +64,16 @@ const App = (): React.JSX.Element => { const requestSessionData = web3wallet!.engine.signClient.session.get(topic); switch (request.method) { + case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION: + navigation.navigate('SignRequest', { + network: 'eth', + address: request.params[0].from, + message: JSON.stringify(request.params[0], undefined, 2), + requestEvent, + requestSessionData, + }); + break; + case EIP155_SIGNING_METHODS.PERSONAL_SIGN: navigation.navigate('SignRequest', { network: 'eth', diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 89d1ace..ac7a234 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -84,10 +84,10 @@ const HomeScreen = () => { cosmosAccounts: [], }); setCurrentIndex(0); - const sessions = await web3wallet.getActiveSessions(); + const sessions = web3wallet!.getActiveSessions(); Object.keys(sessions).forEach(async sessionId => { - await web3wallet.disconnectSession({ + await web3wallet!.disconnectSession({ topic: sessionId, reason: getSdkError('USER_DISCONNECTED'), }); diff --git a/src/screens/SignRequest.tsx b/src/screens/SignRequest.tsx index 8248d2c..d22c932 100644 --- a/src/screens/SignRequest.tsx +++ b/src/screens/SignRequest.tsx @@ -19,6 +19,7 @@ import { rejectWalletConnectRequest, } from '../utils/wallet-connect/WalletConnectRequests'; import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils'; +import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data'; type SignRequestProps = NativeStackScreenProps; @@ -46,6 +47,19 @@ const SignRequest = ({ route }: SignRequestProps) => { return requestParams.params.request.method === 'cosmos_signDirect'; }, [route.params]); + const isEthSendTransaction = useMemo(() => { + const requestParams = route.params!.requestEvent; + + if (!requestParams) { + return false; + } + + return ( + requestParams.params.request.method === + EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION + ); + }, [route.params]); + const retrieveData = async ( requestNetwork: string, requestAddress: string, @@ -128,7 +142,7 @@ const SignRequest = ({ route }: SignRequestProps) => { ); const { topic } = requestEvent; - await web3wallet.respondSessionRequest({ topic, response }); + await web3wallet!.respondSessionRequest({ topic, response }); }; const handleIntent = async () => { @@ -159,7 +173,7 @@ const SignRequest = ({ route }: SignRequestProps) => { if (route.params?.requestEvent) { const response = rejectWalletConnectRequest(route.params?.requestEvent); const { topic } = route.params?.requestEvent; - await web3wallet.respondSessionRequest({ + await web3wallet!.respondSessionRequest({ topic, response, }); @@ -211,7 +225,7 @@ const SignRequest = ({ route }: SignRequestProps) => { - {isCosmosSignDirect ? ( + {isCosmosSignDirect || isEthSendTransaction ? ( {message} diff --git a/src/utils/wallet-connect/EIP155Data.ts b/src/utils/wallet-connect/EIP155Data.ts new file mode 100644 index 0000000..b47948a --- /dev/null +++ b/src/utils/wallet-connect/EIP155Data.ts @@ -0,0 +1,140 @@ +/** + * @desc Refference list of eip155 chains + * @url https://chainlist.org + */ + +// Taken from https://github.com/WalletConnect/web-examples/blob/main/advanced/wallets/react-wallet-v2/src/data/EIP155Data.ts + +/** + * Types + */ +export type TEIP155Chain = keyof typeof EIP155_CHAINS; + +export type EIP155Chain = { + chainId: number; + name: string; + logo: string; + rgb: string; + rpc: string; + namespace: string; + smartAccountEnabled?: boolean; +}; + +/** + * Chains + */ +export const EIP155_MAINNET_CHAINS: Record = { + 'eip155:1': { + chainId: 1, + name: 'Ethereum', + logo: '/chain-logos/eip155-1.png', + rgb: '99, 125, 234', + rpc: 'https://cloudflare-eth.com/', + namespace: 'eip155', + }, + 'eip155:43114': { + chainId: 43114, + name: 'Avalanche C-Chain', + logo: '/chain-logos/eip155-43113.png', + rgb: '232, 65, 66', + rpc: 'https://api.avax.network/ext/bc/C/rpc', + namespace: 'eip155', + }, + 'eip155:137': { + chainId: 137, + name: 'Polygon', + logo: '/chain-logos/eip155-137.png', + rgb: '130, 71, 229', + rpc: 'https://polygon-rpc.com/', + namespace: 'eip155', + }, + 'eip155:10': { + chainId: 10, + name: 'Optimism', + logo: '/chain-logos/eip155-10.png', + rgb: '235, 0, 25', + rpc: 'https://mainnet.optimism.io', + namespace: 'eip155', + }, + 'eip155:324': { + chainId: 324, + name: 'zkSync Era', + logo: '/chain-logos/eip155-324.svg', + rgb: '242, 242, 242', + rpc: 'https://mainnet.era.zksync.io/', + namespace: 'eip155', + }, +}; + +export const EIP155_TEST_CHAINS: Record = { + 'eip155:5': { + chainId: 5, + name: 'Ethereum Goerli', + logo: '/chain-logos/eip155-1.png', + rgb: '99, 125, 234', + rpc: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161', + namespace: 'eip155', + smartAccountEnabled: true, + }, + 'eip155:11155111': { + chainId: 11155111, + name: 'Ethereum Sepolia', + logo: '/chain-logos/eip155-1.png', + rgb: '99, 125, 234', + rpc: 'https://gateway.tenderly.co/public/sepolia', + namespace: 'eip155', + smartAccountEnabled: true, + }, + 'eip155:43113': { + chainId: 43113, + name: 'Avalanche Fuji', + logo: '/chain-logos/eip155-43113.png', + rgb: '232, 65, 66', + rpc: 'https://api.avax-test.network/ext/bc/C/rpc', + namespace: 'eip155', + }, + 'eip155:80001': { + chainId: 80001, + name: 'Polygon Mumbai', + logo: '/chain-logos/eip155-137.png', + rgb: '130, 71, 229', + rpc: 'https://matic-mumbai.chainstacklabs.com', + namespace: 'eip155', + smartAccountEnabled: true, + }, + 'eip155:420': { + chainId: 420, + name: 'Optimism Goerli', + logo: '/chain-logos/eip155-10.png', + rgb: '235, 0, 25', + rpc: 'https://goerli.optimism.io', + namespace: 'eip155', + }, + 'eip155:280': { + chainId: 280, + name: 'zkSync Era Testnet', + logo: '/chain-logos/eip155-324.svg', + rgb: '242, 242, 242', + rpc: 'https://testnet.era.zksync.dev/', + namespace: 'eip155', + }, +}; + +export const EIP155_CHAINS = { + ...EIP155_MAINNET_CHAINS, + ...EIP155_TEST_CHAINS, +}; + +/** + * Methods + */ +export const EIP155_SIGNING_METHODS = { + PERSONAL_SIGN: 'personal_sign', + ETH_SIGN: 'eth_sign', + ETH_SIGN_TRANSACTION: 'eth_signTransaction', + ETH_SIGN_TYPED_DATA: 'eth_signTypedData', + ETH_SIGN_TYPED_DATA_V3: 'eth_signTypedData_v3', + ETH_SIGN_TYPED_DATA_V4: 'eth_signTypedData_v4', + ETH_SEND_RAW_TRANSACTION: 'eth_sendRawTransaction', + ETH_SEND_TRANSACTION: 'eth_sendTransaction', +}; diff --git a/src/utils/wallet-connect/WalletConnectRequests.ts b/src/utils/wallet-connect/WalletConnectRequests.ts index a4d42ff..8f023c4 100644 --- a/src/utils/wallet-connect/WalletConnectRequests.ts +++ b/src/utils/wallet-connect/WalletConnectRequests.ts @@ -1,4 +1,5 @@ // Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a +import { Wallet, providers } from 'ethers'; import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils'; import { SignClientTypes } from '@walletconnect/types'; @@ -9,6 +10,7 @@ import { signDirectMessage, signEthMessage } from '../sign-message'; import { Account } from '../../types'; import { getMnemonic, getPathKey } from '../misc'; import { getCosmosAccounts } from '../accounts'; +import { TEIP155Chain, EIP155_CHAINS } from './EIP155Data'; export async function approveWalletConnectRequest( requestEvent: SignClientTypes.EventArguments['session_request'], @@ -17,7 +19,7 @@ export async function approveWalletConnectRequest( message: string, ) { const { params, id } = requestEvent; - const { request } = params; + const { request, chainId } = params; const path = (await getPathKey(network, account.counterId)).path; const mnemonic = await getMnemonic(); @@ -66,6 +68,19 @@ export async function approveWalletConnectRequest( return formatJsonRpcResult(id, { signature: cosmosAminoSignature.signature.signature, }); + + case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION: + const provider = new providers.JsonRpcProvider( + EIP155_CHAINS[chainId as TEIP155Chain].rpc, + ); + + const privKey = (await getPathKey('eth', account.counterId)).privKey; + const wallet = new Wallet(privKey); + const sendTransaction = request.params[0]; + const connectedWallet = await wallet.connect(provider); + const hash = await connectedWallet.sendTransaction(sendTransaction); + const receipt = typeof hash === 'string' ? hash : hash?.hash; + return formatJsonRpcResult(id, receipt); default: throw new Error(getSdkError('INVALID_METHOD').message); }