diff --git a/advanced/dapps/react-dapp-v2/.env.local.example b/advanced/dapps/react-dapp-v2/.env.local.example index 3dc345e..edf2764 100644 --- a/advanced/dapps/react-dapp-v2/.env.local.example +++ b/advanced/dapps/react-dapp-v2/.env.local.example @@ -1,2 +1,2 @@ NEXT_PUBLIC_PROJECT_ID=39bc... -NEXT_PUBLIC_RELAY_URL=wss://relay.walletconnect.com +NEXT_PUBLIC_RELAY_URL=wss://relay.walletconnect.com \ No newline at end of file diff --git a/advanced/dapps/react-dapp-v2/src/chains/eip155.ts b/advanced/dapps/react-dapp-v2/src/chains/eip155.ts index 06f4816..5f670fb 100644 --- a/advanced/dapps/react-dapp-v2/src/chains/eip155.ts +++ b/advanced/dapps/react-dapp-v2/src/chains/eip155.ts @@ -35,6 +35,13 @@ export const EIP155ChainData: ChainsMap = { slip44: 60, testnet: true, }, + "11155111": { + name: "Ethereum Sepolia", + id: "eip155:11155111", + rpc: ["https://gateway.tenderly.co/public/sepolia "], + slip44: 60, + testnet: true, + }, "10": { name: "Optimism Mainnet", id: "eip155:10", @@ -138,6 +145,10 @@ export const EIP155Metadata: NamespaceMetadata = { logo: "/assets/" + "eip155-1.png", rgb: EIP155Colors.ethereum, }, + "11155111": { + logo: "/assets/" + "eip155-1.png", + rgb: EIP155Colors.ethereum, + }, "10": { name: "Optimism", logo: "/assets/" + "eip155-10.png", diff --git a/advanced/dapps/react-dapp-v2/src/constants/default.ts b/advanced/dapps/react-dapp-v2/src/constants/default.ts index 6f04577..6407ee4 100644 --- a/advanced/dapps/react-dapp-v2/src/constants/default.ts +++ b/advanced/dapps/react-dapp-v2/src/constants/default.ts @@ -24,6 +24,7 @@ export const DEFAULT_MAIN_CHAINS = [ export const DEFAULT_TEST_CHAINS = [ // testnets "eip155:5", + "eip155:11155111", "eip155:280", "eip155:420", "eip155:80001", diff --git a/advanced/dapps/react-dapp-v2/src/helpers/api.ts b/advanced/dapps/react-dapp-v2/src/helpers/api.ts index 4842050..59f304c 100644 --- a/advanced/dapps/react-dapp-v2/src/helpers/api.ts +++ b/advanced/dapps/react-dapp-v2/src/helpers/api.ts @@ -35,6 +35,14 @@ export const rpcProvidersByChainId: RpcProvidersByChainId = { symbol: "ETH", }, }, + 11155111: { + name: "Ethereum Sepolia", + baseURL: WALLETCONNECT_RPC_BASE_URL + "&chainId=eip155:11155111", + token: { + name: "Ether", + symbol: "ETH", + }, + }, 137: { name: "Polygon Mainnet", baseURL: WALLETCONNECT_RPC_BASE_URL + "&chainId=eip155:137", diff --git a/advanced/wallets/react-wallet-v2/src/components/ChainAddressMini.tsx b/advanced/wallets/react-wallet-v2/src/components/ChainAddressMini.tsx index abc70c8..9431b2b 100644 --- a/advanced/wallets/react-wallet-v2/src/components/ChainAddressMini.tsx +++ b/advanced/wallets/react-wallet-v2/src/components/ChainAddressMini.tsx @@ -16,7 +16,7 @@ interface Props { } export default function ChainAddressMini({ address }: Props) { - if (!address) return <> + if (!address || address === 'N/A') return <> return ( <> diff --git a/advanced/wallets/react-wallet-v2/src/components/ChainSmartAddressMini.tsx b/advanced/wallets/react-wallet-v2/src/components/ChainSmartAddressMini.tsx index 51f0f6b..6c79ad6 100644 --- a/advanced/wallets/react-wallet-v2/src/components/ChainSmartAddressMini.tsx +++ b/advanced/wallets/react-wallet-v2/src/components/ChainSmartAddressMini.tsx @@ -4,11 +4,15 @@ import ChainAddressMini from './ChainAddressMini' import { createOrRestoreEIP155Wallet, eip155Wallets } from '@/utils/EIP155WalletUtil' import { Spinner } from '@nextui-org/react' import { Chain, allowedChains } from '@/utils/SmartAccountUtils' -import { useSnapshot } from 'valtio' -import SettingsStore from '@/store/SettingsStore' interface Props { - namespace: string + chain: { + chainId: string; + name: string; + logo: string; + rgb: string; + namespace: string; + } | undefined } const getKey = (namespace?: string) => { @@ -20,10 +24,11 @@ const getKey = (namespace?: string) => { } } -export default function ChainSmartAddressMini({ namespace }: Props) { - const { activeChainId } = useSnapshot(SettingsStore.state) - const { address } = useSmartAccount(getKey(namespace) as `0x${string}`, allowedChains.find((c) => c.id.toString() === activeChainId) as Chain) - +export default function ChainSmartAddressMini({chain}: Props) { + const { address } = useSmartAccount( + getKey(chain?.namespace) as `0x${string}`, + allowedChains.find((c) => c.id.toString() === chain?.chainId.toString()) as Chain + ) if (!address) return return ( diff --git a/advanced/wallets/react-wallet-v2/src/components/SmartAccountCard.tsx b/advanced/wallets/react-wallet-v2/src/components/SmartAccountCard.tsx index dc71677..e38015f 100644 --- a/advanced/wallets/react-wallet-v2/src/components/SmartAccountCard.tsx +++ b/advanced/wallets/react-wallet-v2/src/components/SmartAccountCard.tsx @@ -44,11 +44,6 @@ export default function SmartAccountCard({ setTimeout(() => setCopied(false), 1500) } - async function onChainChanged(chainId: string, address: string) { - SettingsStore.setActiveChainId(chainId) - await updateSignClientChainId(chainId.toString(), address) - } - async function onCreateSmartAccount() { try { if (!isDeployed) { @@ -58,6 +53,11 @@ export default function SmartAccountCard({ console.error(error) } } + + async function onChainChanged(chainId: string, address: string) { + SettingsStore.setActiveChainId(chainId) + await updateSignClientChainId(chainId.toString(), address) + } return ( diff --git a/advanced/wallets/react-wallet-v2/src/data/EIP155Data.ts b/advanced/wallets/react-wallet-v2/src/data/EIP155Data.ts index 0c16d6b..f532ac1 100644 --- a/advanced/wallets/react-wallet-v2/src/data/EIP155Data.ts +++ b/advanced/wallets/react-wallet-v2/src/data/EIP155Data.ts @@ -79,7 +79,7 @@ export const EIP155_TEST_CHAINS: Record = { name: 'Ethereum Sepolia', logo: '/chain-logos/eip155-1.png', rgb: '99, 125, 234', - rpc: 'https://rpc.sepolia.org', + rpc: 'https://gateway.tenderly.co/public/sepolia', namespace: 'eip155', smartAccountEnabled: true, }, diff --git a/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccount.ts b/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccount.ts index f8e4d08..e7e84c8 100644 --- a/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccount.ts +++ b/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccount.ts @@ -6,6 +6,7 @@ import { useSnapshot } from "valtio"; import { Hex } from "viem"; import { styledToast } from "@/utils/HelperUtil"; import { TransactionExecutionError } from "viem"; +import { SmartAccount } from "permissionless/accounts"; export default function useSmartAccount(signerPrivateKey: Hex, chain: Chain) { const [loading, setLoading] = useState(false) @@ -33,11 +34,6 @@ export default function useSmartAccount(signerPrivateKey: Hex, chain: Chain) { } }, [setLoading]) - const deploy = useCallback(async () => { - if (!client) return - execute(client?.deploySmartAccount) - }, [client, execute]) - const sendTestTransaction = useCallback(async () => { if (!client) return execute(() => client?.sendTransaction({ @@ -47,6 +43,11 @@ export default function useSmartAccount(signerPrivateKey: Hex, chain: Chain) { })) }, [client, execute]) + const deploy = useCallback(async () => { + if (!client) return + execute(client?.deploySmartAccount) + }, [client, execute]) + useEffect(() => { if (!signerPrivateKey || !chain) return const smartAccountClient = new SmartAccountLib({ @@ -58,19 +59,17 @@ export default function useSmartAccount(signerPrivateKey: Hex, chain: Chain) { }, [signerPrivateKey, smartAccountSponsorshipEnabled, chain]) useEffect(() => { - client?.init() - .then(() => { - setIsDeployed(client?.isDeployed) - setAddress(client?.address) + client?.getAccount() + .then((account: SmartAccount) => { + setAddress(account.address) }) }, [client, chain]) - return { address, isDeployed, - deploy, loading, sendTestTransaction, + deploy } } \ No newline at end of file diff --git a/advanced/wallets/react-wallet-v2/src/lib/SmartAccountLib.ts b/advanced/wallets/react-wallet-v2/src/lib/SmartAccountLib.ts index 31dcf9b..5518a80 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/SmartAccountLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/SmartAccountLib.ts @@ -39,6 +39,8 @@ export class SmartAccountLib { throw new Error('A Pimlico API Key is required') } + console.log('investigate 2', chain, privateKey, sponsored) + this.chain = chain this.sponsored = sponsored this.#signerPrivateKey = privateKey diff --git a/advanced/wallets/react-wallet-v2/src/utils/EIP155RequestHandlerUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/EIP155RequestHandlerUtil.ts index 93c494e..ac1df10 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/EIP155RequestHandlerUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/EIP155RequestHandlerUtil.ts @@ -11,12 +11,15 @@ import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils' import { SignClientTypes } from '@walletconnect/types' import { getSdkError } from '@walletconnect/utils' import { providers } from 'ethers' +import { chains } from './SmartAccountUtils' import { Hex } from 'viem' import { Chain, allowedChains } from './SmartAccountUtils' +import SettingsStore from '@/store/SettingsStore' type RequestEventArgs = Omit const getWallet = async (params: any) => { + const typedChains: Record = chains; console.log('get wallet params', params) const chainId = params?.chainId?.split(':')[1] console.log('chain id', chainId) @@ -28,12 +31,18 @@ const getWallet = async (params: any) => { const smartAccountEnabledChain = allowedChains.find((chain) => chain.id.toString() === chainId) as Chain console.log('smart account enabled chain', smartAccountEnabledChain) const smartAccounts = await Promise.all(Object.values(eip155Wallets).map(async (wallet) => { + console.log('typeed chains', typedChains[chainId]) + const smartAccount = new SmartAccountLib({ privateKey: wallet.getPrivateKey() as Hex, - chain: smartAccountEnabledChain, + chain: typedChains[chainId], sponsored: true, // TODO: Sponsor for now but should be dynamic according to SettingsStore }) - await smartAccount.init() + + const isDeployed = await smartAccount.checkIfSmartAccountDeployed() + if (!isDeployed) { + await smartAccount.deploySmartAccount() + } return smartAccount })); @@ -46,6 +55,11 @@ const getWallet = async (params: any) => { export async function approveEIP155Request(requestEvent: RequestEventArgs) { const { params, id } = requestEvent const { chainId, request } = params + + console.log(requestEvent, chainId, "tests") + + SettingsStore.setActiveChainId(chainId) + const wallet = await getWallet(params) switch (request.method) { diff --git a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtils.ts b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtils.ts index 4ea710e..a9ec88c 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtils.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtils.ts @@ -5,6 +5,11 @@ const apiKey = process.env.NEXT_PUBLIC_PIMLICO_KEY // Types export const allowedChains = [sepolia, polygonMumbai, goerli] as const +// build chains so I can access them by id +export const chains = allowedChains.reduce((acc, chain) => { + acc[chain.id] = chain + return acc +}, {} as Record) export type Chain = (typeof allowedChains)[number] export type UrlConfig = { chain: Chain @@ -56,7 +61,11 @@ export const USDC_FAUCET_URL = 'https://faucet.circle.com/' export const VITALIK_ADDRESS = '0xd8da6bf26964af9d7eed9e03e53415d37aa96045' as Hex -export const publicRPCUrl = ({ chain }: UrlConfig) => RPC_URLS[chain.name] +export const publicRPCUrl = ({ chain }: UrlConfig) => { + console.log("investigate", chain) + return RPC_URLS[chain?.name] +} + export const paymasterUrl = ({ chain }: UrlConfig) => `https://api.pimlico.io/v2/${PIMLICO_NETWORK_NAMES[chain.name]}/rpc?apikey=${apiKey}` export const bundlerUrl = ({ chain }: UrlConfig) => diff --git a/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx b/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx index 165fe6b..701b17c 100644 --- a/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx +++ b/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx @@ -32,11 +32,12 @@ import ChainAddressMini from '@/components/ChainAddressMini' import { getChainData } from '@/data/chainsUtil' import RequestModal from './RequestModal' import { SmartAccountLib } from '@/lib/SmartAccountLib' -import { Hex } from 'viem' import ChainSmartAddressMini from '@/components/ChainSmartAddressMini' import { useSnapshot } from 'valtio' import SettingsStore from '@/store/SettingsStore' import { Chain, allowedChains } from '@/utils/SmartAccountUtils' +import { Hex } from 'viem' +import useSmartAccount from '@/hooks/useSmartAccount' const StyledText = styled(Text, { fontWeight: 400 @@ -51,7 +52,6 @@ export default function SessionProposalModal() { // Get proposal data and wallet address from store const data = useSnapshot(ModalStore.state) const proposal = data?.data?.proposal as SignClientTypes.EventArguments['session_proposal'] - const [isLoadingApprove, setIsLoadingApprove] = useState(false) const [isLoadingReject, setIsLoadingReject] = useState(false) console.log('proposal', data.data?.proposal) @@ -170,12 +170,19 @@ export default function SessionProposalModal() { optional.push(chains) } console.log('requestedChains', [...new Set([...required.flat(), ...optional.flat()])]) + return [...new Set([...required.flat(), ...optional.flat()])] }, [proposal]) // the chains that are supported by the wallet from the proposal const supportedChains = useMemo( - () => requestedChains.map(chain => getChainData(chain!)), + () => requestedChains.map(chain => { + const chainData = getChainData(chain!) + + if (!chainData) return null + + return chainData + }), [requestedChains] ) @@ -226,43 +233,50 @@ export default function SessionProposalModal() { } }, []) + const namespaces = buildApprovedNamespaces({ + proposal: proposal.params, + supportedNamespaces + }) + // Hanlde approve action, construct session namespace const onApprove = useCallback(async () => { if (proposal) { setIsLoadingApprove(true) - const namespaces = buildApprovedNamespaces({ - proposal: proposal.params, - supportedNamespaces - }) + // get keys of namespaces + const namespaceKeys = Object.keys(namespaces) + const [nameSpaceKey] = namespaceKeys - // TODO: improve for multi network - console.log('namespaces', namespaces['eip155']) - const namespaceChains = namespaces['eip155']?.chains?.map((c: string) => c.split(':')[1]) - const smartAccountEnabledChains: Chain[] = allowedChains.filter(chain => namespaceChains?.includes(chain.id.toString())) - // We find a request for a chain that is enabled for smart account - if (smartAccountEnabledChains.length) { - const signerAddress = namespaces['eip155'].accounts[0].split(':')[2] + // get chain ids from namespaces + const [chainIds] = namespaceKeys.map(key => namespaces[key].chains) + + if (chainIds) { + const allowedChainIds = chainIds.filter(id => { + const chainId = id.replace(`${nameSpaceKey}:`, '') + return allowedChains.map(chain => chain.id.toString()).includes(chainId) + }) + + console.log('allowedChainIds', allowedChainIds) + + const chainIdParsed = allowedChainIds[0].replace(`${nameSpaceKey}:`, '') + const signerAddress = namespaces[nameSpaceKey].accounts[0].split(':')[2] const wallet = eip155Wallets[signerAddress] - const chain = smartAccountEnabledChains[0] - if (wallet) { - const smartAccountClient = new SmartAccountLib({ - privateKey: wallet.getPrivateKey() as Hex, - chain, - sponsored: smartAccountSponsorshipEnabled, - }) - await smartAccountClient.init() - const isDeployed = await smartAccountClient.checkIfSmartAccountDeployed() - console.log('isDeployed', isDeployed, smartAccountClient.address) - - if (isDeployed) { - namespaces.eip155.accounts = [...namespaces.eip155.accounts, `eip155:${chain.id}:${smartAccountClient.address}`] - } + const chain = allowedChains.find(chain => chain.id.toString() === chainIdParsed)! + + const smartAccountClient = new SmartAccountLib({ + privateKey: wallet.getPrivateKey() as Hex, + chain: allowedChains.find(chain => chain.id.toString() === chainIdParsed)!, + sponsored: smartAccountSponsorshipEnabled, + }) + + const smartAccountAddress = await smartAccountClient.getAccount() + if (wallet && smartAccountAddress) { + namespaces.eip155.accounts = [...namespaces.eip155.accounts, `${nameSpaceKey}:${chain.id}:${smartAccountAddress.address}`] } + + console.log('approving namespaces:', namespaces.eip155.accounts) } - console.log('approving namespaces:', namespaces) - - try { + try { await web3wallet.approveSession({ id: proposal.id, namespaces @@ -276,7 +290,7 @@ export default function SessionProposalModal() { } setIsLoadingApprove(false) ModalStore.close() - }, [proposal, supportedNamespaces, smartAccountSponsorshipEnabled]) + }, [namespaces, proposal, smartAccountSponsorshipEnabled]) // Hanlde reject action // eslint-disable-next-line react-hooks/rules-of-hooks @@ -339,7 +353,7 @@ export default function SessionProposalModal() { supportedChains.map((chain, i) => { return ( - + ) })} @@ -347,9 +361,13 @@ export default function SessionProposalModal() { Smart Accounts {smartAccountChains.length && smartAccountChains.map((chain, i) => { + if (!chain) { + return <> + } + return ( - + ) })}