feat: Goerli Smart Accounts. (#347)
* feat: added Goerli smart accounts * chore: remove viem version modifier. Split useSmartAccount usage into multiple lines * feat: fix error handling
This commit is contained in:
parent
deb123f283
commit
61b181b9d4
@ -39,6 +39,7 @@
|
||||
"mnemonic-keyring": "1.4.0",
|
||||
"near-api-js": "^0.45.0",
|
||||
"next": "12.1.5",
|
||||
"permissionless": "0.0.0-main.20231120T173047",
|
||||
"react": "17.0.2",
|
||||
"react-code-blocks": "0.0.9-0",
|
||||
"react-dom": "17.0.2",
|
||||
@ -46,15 +47,16 @@
|
||||
"react-qr-reader-es6": "2.2.1-2",
|
||||
"solana-wallet": "^1.0.2",
|
||||
"tronweb": "^4.4.0",
|
||||
"valtio": "1.6.0"
|
||||
"valtio": "1.6.0",
|
||||
"viem": "1.19.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "17.0.35",
|
||||
"@types/react": "18.0.9",
|
||||
"@types/react": "17.0.2",
|
||||
"eslint": "8.15.0",
|
||||
"eslint-config-next": "12.1.6",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"prettier": "2.6.2",
|
||||
"typescript": "4.6.4"
|
||||
"typescript": "5.2.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="katman_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 800 600" style="enable-background:new 0 0 800 600;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#2C7DF7;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M399.8,559.9c143.8,0,260.3-116.6,260.3-260.3S543.6,39.2,399.8,39.2c-143.8,0-260.3,116.6-260.3,260.3
|
||||
S256,559.9,399.8,559.9z"/>
|
||||
<path class="st1" d="M424.8,449.1c-21.1,0-36.4-5.1-46.1-15.2c-9-8.6-14.2-20.4-14.5-32.8c-0.1-3.8,0.8-7.5,2.5-10.8
|
||||
c1.7-2.9,4-5.3,6.9-6.9c3.3-1.7,7-2.5,10.8-2.5c3.7,0,7.4,0.9,10.8,2.5c2.9,1.6,5.2,4,6.9,6.9c1.8,3.3,2.7,7.1,2.6,10.8
|
||||
c0.2,4.5-1.1,9-3.7,12.7c-2.2,3-5.2,5.3-8.8,6.4c3.5,4.4,8.4,7.5,13.8,8.6c6.1,1.8,12.5,2.7,18.9,2.7c8.4,0.1,16.7-2.4,23.7-7.1
|
||||
c7.3-5.1,12.7-12.5,15.5-21c3.5-10.1,5.2-20.7,5.1-31.4c0.3-11.1-1.6-22.2-5.5-32.6c-3-8.3-8.6-15.5-16-20.3
|
||||
c-6.9-4.4-14.9-6.6-23-6.6c-7.1,0.6-14,2.9-20,6.8l-14.8,7.4v-7.4l66.5-89.2h-92.2v92.6c-0.3,6.7,1.5,13.3,5.1,18.9
|
||||
c1.7,2.5,4.1,4.4,6.8,5.7c2.7,1.3,5.7,1.9,8.7,1.7c5.5-0.1,10.9-2,15.3-5.4c5-3.6,9.3-8.1,12.8-13.2c0.3-0.9,0.9-1.7,1.7-2.2
|
||||
c0.6-0.5,1.4-0.8,2.2-0.8c1.6,0.1,3.1,0.8,4.3,1.8c1.5,1.7,2.4,4,2.3,6.3c-0.2,1.6-0.5,3.1-0.8,4.6c-3.3,7.9-8.8,14.7-15.8,19.6
|
||||
c-6.5,4.4-14.3,6.8-22.2,6.7c-20,0-33.8-3.9-41.5-11.8c-4-4.3-7-9.4-9-15c-2-5.5-2.8-11.4-2.5-17.3v-92.4h-46.9v-17.2H332v-39.2
|
||||
l-10.8-10.8v-8.8h31.3l11.8,6.1v52.7l121.8-0.4l12.1,12.2l-74.7,75c4.5-1.8,9.3-3,14.1-3.4c9.6,0.4,18.9,3.1,27.3,7.8
|
||||
c9.7,4.6,17.8,11.9,23.5,21c5.1,7.7,8.7,16.3,10.6,25.3c1.6,7.1,2.4,14.4,2.5,21.7c0,13.9-3.1,27.6-9.2,40.1
|
||||
c-5.8,12.1-15.6,21.9-27.7,27.7C452.3,446,438.6,449.1,424.8,449.1L424.8,449.1z"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="katman_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 800 600" style="enable-background:new 0 0 800 600;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#2C7DF7;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M399.8,559.9c143.8,0,260.3-116.6,260.3-260.3S543.6,39.2,399.8,39.2c-143.8,0-260.3,116.6-260.3,260.3
|
||||
S256,559.9,399.8,559.9z"/>
|
||||
<path class="st1" d="M424.8,449.1c-21.1,0-36.4-5.1-46.1-15.2c-9-8.6-14.2-20.4-14.5-32.8c-0.1-3.8,0.8-7.5,2.5-10.8
|
||||
c1.7-2.9,4-5.3,6.9-6.9c3.3-1.7,7-2.5,10.8-2.5c3.7,0,7.4,0.9,10.8,2.5c2.9,1.6,5.2,4,6.9,6.9c1.8,3.3,2.7,7.1,2.6,10.8
|
||||
c0.2,4.5-1.1,9-3.7,12.7c-2.2,3-5.2,5.3-8.8,6.4c3.5,4.4,8.4,7.5,13.8,8.6c6.1,1.8,12.5,2.7,18.9,2.7c8.4,0.1,16.7-2.4,23.7-7.1
|
||||
c7.3-5.1,12.7-12.5,15.5-21c3.5-10.1,5.2-20.7,5.1-31.4c0.3-11.1-1.6-22.2-5.5-32.6c-3-8.3-8.6-15.5-16-20.3
|
||||
c-6.9-4.4-14.9-6.6-23-6.6c-7.1,0.6-14,2.9-20,6.8l-14.8,7.4v-7.4l66.5-89.2h-92.2v92.6c-0.3,6.7,1.5,13.3,5.1,18.9
|
||||
c1.7,2.5,4.1,4.4,6.8,5.7c2.7,1.3,5.7,1.9,8.7,1.7c5.5-0.1,10.9-2,15.3-5.4c5-3.6,9.3-8.1,12.8-13.2c0.3-0.9,0.9-1.7,1.7-2.2
|
||||
c0.6-0.5,1.4-0.8,2.2-0.8c1.6,0.1,3.1,0.8,4.3,1.8c1.5,1.7,2.4,4,2.3,6.3c-0.2,1.6-0.5,3.1-0.8,4.6c-3.3,7.9-8.8,14.7-15.8,19.6
|
||||
c-6.5,4.4-14.3,6.8-22.2,6.7c-20,0-33.8-3.9-41.5-11.8c-4-4.3-7-9.4-9-15c-2-5.5-2.8-11.4-2.5-17.3v-92.4h-46.9v-17.2H332v-39.2
|
||||
l-10.8-10.8v-8.8h31.3l11.8,6.1v52.7l121.8-0.4l12.1,12.2l-74.7,75c4.5-1.8,9.3-3,14.1-3.4c9.6,0.4,18.9,3.1,27.3,7.8
|
||||
c9.7,4.6,17.8,11.9,23.5,21c5.1,7.7,8.7,16.3,10.6,25.3c1.6,7.1,2.4,14.4,2.5,21.7c0,13.9-3.1,27.6-9.2,40.1
|
||||
c-5.8,12.1-15.6,21.9-27.7,27.7C452.3,446,438.6,449.1,424.8,449.1L424.8,449.1z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@ -6,9 +6,10 @@ interface Props {
|
||||
rgb: string
|
||||
flexDirection: 'row' | 'col'
|
||||
alignItems: 'center' | 'flex-start'
|
||||
flexWrap?: 'wrap' | 'nowrap'
|
||||
}
|
||||
|
||||
export default function ChainCard({ rgb, children, flexDirection, alignItems }: Props) {
|
||||
export default function ChainCard({ rgb, children, flexDirection, alignItems, flexWrap }: Props) {
|
||||
return (
|
||||
<Card
|
||||
bordered
|
||||
@ -23,6 +24,7 @@ export default function ChainCard({ rgb, children, flexDirection, alignItems }:
|
||||
>
|
||||
<Card.Body
|
||||
css={{
|
||||
flexWrap,
|
||||
flexDirection,
|
||||
alignItems,
|
||||
justifyContent: 'space-between',
|
||||
|
@ -0,0 +1,143 @@
|
||||
import ChainCard from '@/components/ChainCard'
|
||||
import SettingsStore from '@/store/SettingsStore'
|
||||
import { truncate } from '@/utils/HelperUtil'
|
||||
import { updateSignClientChainId } from '@/utils/WalletConnectUtil'
|
||||
import { Avatar, Button, Text, Tooltip, Loading } from '@nextui-org/react'
|
||||
import { eip155Wallets } from '@/utils/EIP155WalletUtil'
|
||||
import Image from 'next/image'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import useSmartAccount from '@/hooks/useSmartAccount'
|
||||
|
||||
interface Props {
|
||||
name: string
|
||||
logo: string
|
||||
rgb: string
|
||||
address: string
|
||||
chainId: string
|
||||
isActiveChain: boolean
|
||||
}
|
||||
|
||||
export default function SmartAccountCard({
|
||||
name,
|
||||
logo,
|
||||
rgb,
|
||||
address = '',
|
||||
chainId,
|
||||
isActiveChain
|
||||
}: Props) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const { activeChainId } = useSnapshot(SettingsStore.state)
|
||||
const {
|
||||
deploy,
|
||||
isDeployed,
|
||||
address: smartAccountAddress,
|
||||
loading,
|
||||
sendTestTransaction,
|
||||
} = useSmartAccount(eip155Wallets[address].getPrivateKey() as `0x${string}`)
|
||||
|
||||
function onCopy() {
|
||||
navigator?.clipboard?.writeText(address)
|
||||
setCopied(true)
|
||||
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) {
|
||||
await deploy()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const getFaucetUrl = () => `https://${name?.toLowerCase()?.replace('ethereum', '')?.trim()}faucet.com`
|
||||
|
||||
return (
|
||||
<ChainCard rgb={rgb} flexDirection="row" alignItems="center" flexWrap="wrap">
|
||||
<Avatar src={logo} />
|
||||
<div style={{ flex: 1 }}>
|
||||
<Text h5 css={{ marginLeft: '$9' }}>
|
||||
{name}
|
||||
</Text>
|
||||
<Text weight="light" size={13} css={{ marginLeft: '$9' }}>
|
||||
{address ? truncate(address, 19) : '<no address available>'}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<Tooltip content={copied ? 'Copied!' : 'Copy'} placement="left">
|
||||
<Button
|
||||
size="sm"
|
||||
css={{ minWidth: 'auto', backgroundColor: 'rgba(255, 255, 255, 0.15)' }}
|
||||
data-testid={'chain-copy-button' + chainId}
|
||||
onClick={onCopy}
|
||||
>
|
||||
<Image
|
||||
src={copied ? '/icons/checkmark-icon.svg' : '/icons/copy-icon.svg'}
|
||||
width={15}
|
||||
height={15}
|
||||
alt="copy icon"
|
||||
/>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button
|
||||
size="sm"
|
||||
css={{
|
||||
minWidth: 'auto',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.15)',
|
||||
marginLeft: '$5'
|
||||
}}
|
||||
data-testid={'chain-switch-button' + chainId}
|
||||
onPress={() => {
|
||||
onChainChanged(chainId, address)
|
||||
}}
|
||||
>
|
||||
{activeChainId === chainId ? `✅` : `🔄`}
|
||||
</Button>
|
||||
|
||||
{smartAccountAddress ? (
|
||||
<>
|
||||
<Text h5 css={{ marginTop: 20 }}>
|
||||
Smart Account:
|
||||
</Text>
|
||||
<Text small css={{ marginTop: 5 }}>
|
||||
{smartAccountAddress}
|
||||
</Text>
|
||||
<Button
|
||||
size="md"
|
||||
css={{ marginTop: 20, width: '100%' }}
|
||||
onClick={sendTestTransaction}
|
||||
disabled={!isActiveChain || loading}
|
||||
>
|
||||
{loading ? <Loading size="sm" /> : 'Send Test TX'}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
disabled={!isActiveChain || loading}
|
||||
size="sm"
|
||||
css={{ marginTop: 20, width: '100%' }}
|
||||
onClick={() => window.open(getFaucetUrl(), '_blank')}
|
||||
>
|
||||
{name} Faucet
|
||||
</Button>
|
||||
<Button
|
||||
disabled={!isActiveChain || loading}
|
||||
size="sm"
|
||||
css={{ marginTop: 10, width: '100%' }}
|
||||
onClick={onCreateSmartAccount}
|
||||
>
|
||||
{loading ? <Loading size="sm" /> : 'Create Smart Account'}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</ChainCard>
|
||||
)
|
||||
}
|
@ -8,6 +8,16 @@
|
||||
*/
|
||||
export type TEIP155Chain = keyof typeof EIP155_CHAINS
|
||||
|
||||
export type EIP155TestChain = {
|
||||
chainId: number
|
||||
name: string
|
||||
logo: string
|
||||
rgb: string
|
||||
rpc: string
|
||||
namespace: string
|
||||
smartAccountEnabled?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Chains
|
||||
*/
|
||||
@ -54,14 +64,23 @@ export const EIP155_MAINNET_CHAINS = {
|
||||
}
|
||||
}
|
||||
|
||||
export const EIP155_TEST_CHAINS = {
|
||||
export const EIP155_TEST_CHAINS: Record<string,EIP155TestChain> = {
|
||||
'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'
|
||||
namespace: 'eip155',
|
||||
smartAccountEnabled: true,
|
||||
},
|
||||
'eip155:11155111': {
|
||||
chainId: 11155111,
|
||||
name: 'Ethereum Sepolia',
|
||||
logo: '/chain-logos/eip155-1.png',
|
||||
rgb: '99, 125, 234',
|
||||
rpc: 'https://rpc.sepolia.org',
|
||||
namespace: 'eip155',
|
||||
},
|
||||
'eip155:43113': {
|
||||
chainId: 43113,
|
||||
|
@ -0,0 +1,53 @@
|
||||
import { SmartAccountLib } from "@/lib/SmartAccountLib";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
export default function useSmartAccount(signerPrivateKey: `0x${string}`) {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [client, setClient] = useState<SmartAccountLib>();
|
||||
const [isDeployed, setIsDeployed] = useState(false)
|
||||
const [address, setAddress] = useState<`0x${string}`>()
|
||||
|
||||
const execute = useCallback(async (callback: () => void) => {
|
||||
try {
|
||||
setLoading(true)
|
||||
await callback()
|
||||
setLoading(false)
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e)
|
||||
setLoading(false)
|
||||
}
|
||||
}, [setLoading])
|
||||
|
||||
const deploy = useCallback(async () => {
|
||||
if (!client) return
|
||||
execute(client?.deploySmartAccount)
|
||||
}, [client, execute])
|
||||
|
||||
const sendTestTransaction = useCallback(async () => {
|
||||
if (!client) return
|
||||
execute(client?.sendTestTransaction)
|
||||
}, [client, execute])
|
||||
|
||||
useEffect(() => {
|
||||
const smartAccountClient = new SmartAccountLib(signerPrivateKey, 'goerli')
|
||||
setClient(smartAccountClient)
|
||||
}, [signerPrivateKey])
|
||||
|
||||
useEffect(() => {
|
||||
client?.checkIfSmartAccountDeployed()
|
||||
.then((deployed: boolean) => {
|
||||
setIsDeployed(deployed)
|
||||
setAddress(client?.address)
|
||||
})
|
||||
}, [client])
|
||||
|
||||
|
||||
return {
|
||||
address,
|
||||
isDeployed,
|
||||
deploy,
|
||||
loading,
|
||||
sendTestTransaction,
|
||||
}
|
||||
}
|
@ -27,6 +27,10 @@ export default class EIP155Lib {
|
||||
return this.wallet.mnemonic.phrase
|
||||
}
|
||||
|
||||
getPrivateKey() {
|
||||
return this.wallet.privateKey
|
||||
}
|
||||
|
||||
getAddress() {
|
||||
return this.wallet.address
|
||||
}
|
||||
|
153
advanced/wallets/react-wallet-v2/src/lib/SmartAccountLib.ts
Normal file
153
advanced/wallets/react-wallet-v2/src/lib/SmartAccountLib.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import { createSmartAccountClient } from 'permissionless'
|
||||
import { privateKeyToSafeSmartAccount } from 'permissionless/accounts'
|
||||
import * as chains from 'viem/chains'
|
||||
import { privateKeyToAccount } from 'viem/accounts'
|
||||
import { type Chain, createWalletClient, formatEther, createPublicClient, http } from 'viem'
|
||||
import { createPimlicoBundlerClient } from 'permissionless/clients/pimlico'
|
||||
|
||||
export type SmartAccountEnabledChains = 'sepolia' | 'goerli'
|
||||
|
||||
// -- Helpers -----------------------------------------------------------------
|
||||
const bundlerApiKey = process.env.NEXT_PUBLIC_PIMLICO_KEY
|
||||
const projectId = process.env.NEXT_PUBLIC_PROJECT_ID
|
||||
|
||||
// -- Sdk ---------------------------------------------------------------------
|
||||
export class SmartAccountLib {
|
||||
public chain: Chain
|
||||
private bundlerApiKey: string
|
||||
#signerPrivateKey: `0x${string}`;
|
||||
public isDeployed: boolean = false;
|
||||
public address?: `0x${string}`;
|
||||
|
||||
public constructor(privateKey: `0x${string}`, chain: SmartAccountEnabledChains = 'goerli') {
|
||||
if (!bundlerApiKey) {
|
||||
throw new Error('Missing required data in SmartAccountSdk')
|
||||
}
|
||||
this.bundlerApiKey = bundlerApiKey
|
||||
this.chain = chains[chain] as Chain
|
||||
this.#signerPrivateKey = privateKey
|
||||
}
|
||||
|
||||
// -- Public ------------------------------------------------------------------
|
||||
|
||||
|
||||
// -- Private -----------------------------------------------------------------
|
||||
private getWalletConnectTransport = () => http(
|
||||
`https://rpc.walletconnect.com/v1/?chainId=EIP155:${this.chain.id}&projectId=${projectId}`,
|
||||
{ retryDelay: 1000 }
|
||||
);
|
||||
|
||||
private getBundlerTransport = () => http(
|
||||
`https://api.pimlico.io/v1/${this.chain.name.toLowerCase()}/rpc?apikey=${this.bundlerApiKey}`,
|
||||
{ retryDelay: 1000 }
|
||||
);
|
||||
|
||||
|
||||
private getBundlerClient = () => createPimlicoBundlerClient({
|
||||
chain: this.chain,
|
||||
transport: this.getBundlerTransport()
|
||||
})
|
||||
|
||||
private getPublicClient = () => createPublicClient({
|
||||
chain: this.chain,
|
||||
transport: this.getWalletConnectTransport()
|
||||
})
|
||||
|
||||
private getSignerClient = () => {
|
||||
const signerAccount = privateKeyToAccount(this.#signerPrivateKey)
|
||||
return createWalletClient({
|
||||
account: signerAccount,
|
||||
chain: this.chain,
|
||||
transport: this.getWalletConnectTransport()
|
||||
})
|
||||
}
|
||||
|
||||
private getSmartAccountClient = async () => {
|
||||
const smartAccount = await privateKeyToSafeSmartAccount(this.getPublicClient(), {
|
||||
privateKey: this.#signerPrivateKey,
|
||||
safeVersion: '1.4.1',
|
||||
entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'
|
||||
})
|
||||
|
||||
return createSmartAccountClient({
|
||||
account: smartAccount,
|
||||
chain: this.chain,
|
||||
transport: this.getBundlerTransport()
|
||||
})
|
||||
}
|
||||
|
||||
private prefundSmartAccount = async (address: `0x${string}`) => {
|
||||
const signerAccountViemClient = this.getSignerClient();
|
||||
const publicClient = this.getPublicClient();
|
||||
const bundlerClient = this.getBundlerClient();
|
||||
const smartAccountBalance = await publicClient.getBalance({ address })
|
||||
|
||||
console.log(`Smart Account Balance: ${formatEther(smartAccountBalance)} ETH`)
|
||||
if (smartAccountBalance < 1n) {
|
||||
console.log(`Smart Account has no balance. Starting prefund`)
|
||||
const { fast: fastPrefund } = await bundlerClient.getUserOperationGasPrice()
|
||||
const prefundHash = await signerAccountViemClient.sendTransaction({
|
||||
to: address,
|
||||
value: 10000000000000000n,
|
||||
maxFeePerGas: fastPrefund.maxFeePerGas,
|
||||
maxPriorityFeePerGas: fastPrefund.maxPriorityFeePerGas
|
||||
})
|
||||
|
||||
await publicClient.waitForTransactionReceipt({ hash: prefundHash })
|
||||
console.log(`Prefunding Success`)
|
||||
|
||||
const newSmartAccountBalance = await publicClient.getBalance({ address })
|
||||
console.log(
|
||||
`Smart Account Balance: ${formatEther(newSmartAccountBalance)} ETH`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// By default first transaction will deploy the smart contract if it hasn't been deployed yet
|
||||
public sendTestTransaction = async () => {
|
||||
const publicClient = this.getPublicClient();
|
||||
const bundlerClient = this.getBundlerClient();
|
||||
const smartAccountClient = await this.getSmartAccountClient();
|
||||
const { fast: testGas, } = await bundlerClient.getUserOperationGasPrice()
|
||||
|
||||
const testHash = await smartAccountClient.sendTransaction({
|
||||
to: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045' as `0x${string}`,
|
||||
value: 0n,
|
||||
maxFeePerGas: testGas.maxFeePerGas,
|
||||
maxPriorityFeePerGas: testGas.maxPriorityFeePerGas,
|
||||
})
|
||||
|
||||
console.log(`Sending Test Transaction With Hash: ${testHash}`)
|
||||
|
||||
await publicClient.waitForTransactionReceipt({ hash: testHash })
|
||||
console.log(`Test Transaction Success`)
|
||||
}
|
||||
|
||||
public checkIfSmartAccountDeployed = async () => {
|
||||
console.log('checking if deployed')
|
||||
const smartAccountClient = await this.getSmartAccountClient();
|
||||
const publicClient = this.getPublicClient();
|
||||
const bytecode = await publicClient.getBytecode({ address: smartAccountClient.account.address })
|
||||
this.isDeployed = Boolean(bytecode)
|
||||
console.log(`Smart Account Deployed: ${this.isDeployed}`)
|
||||
if (this.isDeployed) {
|
||||
this.address = smartAccountClient.account.address
|
||||
}
|
||||
return this.isDeployed;
|
||||
}
|
||||
|
||||
public deploySmartAccount = async () => {
|
||||
const smartAccountClient = await this.getSmartAccountClient();
|
||||
const isDeployed = await this.checkIfSmartAccountDeployed()
|
||||
if (!isDeployed) {
|
||||
// If not deployed, prefund smart account from signer
|
||||
// Step 3: Send prefund transaction from signer to smart account if empty
|
||||
await this.prefundSmartAccount(smartAccountClient.account.address)
|
||||
|
||||
// Step 4: Create account by sending test tx
|
||||
await this.sendTestTransaction()
|
||||
await this.checkIfSmartAccountDeployed()
|
||||
console.log(`Account Created`)
|
||||
}
|
||||
}
|
||||
}
|
@ -14,11 +14,13 @@ import SettingsStore from '@/store/SettingsStore'
|
||||
import { Text } from '@nextui-org/react'
|
||||
import { Fragment } from 'react'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import SmartAccountCard from '@/components/SmartAccountCard'
|
||||
|
||||
export default function HomePage() {
|
||||
const {
|
||||
testNets,
|
||||
eip155Address,
|
||||
activeChainId,
|
||||
cosmosAddress,
|
||||
solanaAddress,
|
||||
polkadotAddress,
|
||||
@ -131,17 +133,34 @@ export default function HomePage() {
|
||||
<Text h4 css={{ marginBottom: '$5' }}>
|
||||
Testnets
|
||||
</Text>
|
||||
{Object.entries(EIP155_TEST_CHAINS).map(([caip10, { name, logo, rgb }]) => (
|
||||
<AccountCard
|
||||
key={name}
|
||||
name={name}
|
||||
logo={logo}
|
||||
rgb={rgb}
|
||||
address={eip155Address}
|
||||
chainId={caip10.toString()}
|
||||
data-testid={'chain-card-' + caip10.toString()}
|
||||
/>
|
||||
))}
|
||||
{Object.entries(EIP155_TEST_CHAINS).map(([caip10, { name, logo, rgb, chainId, smartAccountEnabled }]) => {
|
||||
if (smartAccountEnabled) {
|
||||
return (
|
||||
<SmartAccountCard
|
||||
key={name}
|
||||
name={name}
|
||||
logo={logo}
|
||||
rgb={rgb}
|
||||
address={eip155Address}
|
||||
chainId={caip10.toString()}
|
||||
data-testid={'chain-card-' + caip10.toString()}
|
||||
isActiveChain={activeChainId === `eip155:${chainId}`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<AccountCard
|
||||
key={name}
|
||||
name={name}
|
||||
logo={logo}
|
||||
rgb={rgb}
|
||||
address={eip155Address}
|
||||
chainId={caip10.toString()}
|
||||
data-testid={'chain-card-' + caip10.toString()}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{Object.entries(SOLANA_TEST_CHAINS).map(([caip10, { name, logo, rgb }]) => (
|
||||
<AccountCard
|
||||
key={name}
|
||||
|
@ -102,7 +102,7 @@ const SettingsStore = {
|
||||
} else {
|
||||
localStorage.removeItem('TEST_NETS')
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default SettingsStore
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "ES2020",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
@ -21,7 +21,8 @@
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
"@/*": ["src/*"],
|
||||
"react": ["node_modules/@types/react"],
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user