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",
|
"mnemonic-keyring": "1.4.0",
|
||||||
"near-api-js": "^0.45.0",
|
"near-api-js": "^0.45.0",
|
||||||
"next": "12.1.5",
|
"next": "12.1.5",
|
||||||
|
"permissionless": "0.0.0-main.20231120T173047",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-code-blocks": "0.0.9-0",
|
"react-code-blocks": "0.0.9-0",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
@ -46,15 +47,16 @@
|
|||||||
"react-qr-reader-es6": "2.2.1-2",
|
"react-qr-reader-es6": "2.2.1-2",
|
||||||
"solana-wallet": "^1.0.2",
|
"solana-wallet": "^1.0.2",
|
||||||
"tronweb": "^4.4.0",
|
"tronweb": "^4.4.0",
|
||||||
"valtio": "1.6.0"
|
"valtio": "1.6.0",
|
||||||
|
"viem": "1.19.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "17.0.35",
|
"@types/node": "17.0.35",
|
||||||
"@types/react": "18.0.9",
|
"@types/react": "17.0.2",
|
||||||
"eslint": "8.15.0",
|
"eslint": "8.15.0",
|
||||||
"eslint-config-next": "12.1.6",
|
"eslint-config-next": "12.1.6",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint-config-prettier": "8.5.0",
|
||||||
"prettier": "2.6.2",
|
"prettier": "2.6.2",
|
||||||
"typescript": "4.6.4"
|
"typescript": "5.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,10 @@ interface Props {
|
|||||||
rgb: string
|
rgb: string
|
||||||
flexDirection: 'row' | 'col'
|
flexDirection: 'row' | 'col'
|
||||||
alignItems: 'center' | 'flex-start'
|
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 (
|
return (
|
||||||
<Card
|
<Card
|
||||||
bordered
|
bordered
|
||||||
@ -23,6 +24,7 @@ export default function ChainCard({ rgb, children, flexDirection, alignItems }:
|
|||||||
>
|
>
|
||||||
<Card.Body
|
<Card.Body
|
||||||
css={{
|
css={{
|
||||||
|
flexWrap,
|
||||||
flexDirection,
|
flexDirection,
|
||||||
alignItems,
|
alignItems,
|
||||||
justifyContent: 'space-between',
|
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 TEIP155Chain = keyof typeof EIP155_CHAINS
|
||||||
|
|
||||||
|
export type EIP155TestChain = {
|
||||||
|
chainId: number
|
||||||
|
name: string
|
||||||
|
logo: string
|
||||||
|
rgb: string
|
||||||
|
rpc: string
|
||||||
|
namespace: string
|
||||||
|
smartAccountEnabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chains
|
* 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': {
|
'eip155:5': {
|
||||||
chainId: 5,
|
chainId: 5,
|
||||||
name: 'Ethereum Goerli',
|
name: 'Ethereum Goerli',
|
||||||
logo: '/chain-logos/eip155-1.png',
|
logo: '/chain-logos/eip155-1.png',
|
||||||
rgb: '99, 125, 234',
|
rgb: '99, 125, 234',
|
||||||
rpc: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
|
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': {
|
'eip155:43113': {
|
||||||
chainId: 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
|
return this.wallet.mnemonic.phrase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPrivateKey() {
|
||||||
|
return this.wallet.privateKey
|
||||||
|
}
|
||||||
|
|
||||||
getAddress() {
|
getAddress() {
|
||||||
return this.wallet.address
|
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 { Text } from '@nextui-org/react'
|
||||||
import { Fragment } from 'react'
|
import { Fragment } from 'react'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
|
import SmartAccountCard from '@/components/SmartAccountCard'
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
const {
|
const {
|
||||||
testNets,
|
testNets,
|
||||||
eip155Address,
|
eip155Address,
|
||||||
|
activeChainId,
|
||||||
cosmosAddress,
|
cosmosAddress,
|
||||||
solanaAddress,
|
solanaAddress,
|
||||||
polkadotAddress,
|
polkadotAddress,
|
||||||
@ -131,7 +133,23 @@ export default function HomePage() {
|
|||||||
<Text h4 css={{ marginBottom: '$5' }}>
|
<Text h4 css={{ marginBottom: '$5' }}>
|
||||||
Testnets
|
Testnets
|
||||||
</Text>
|
</Text>
|
||||||
{Object.entries(EIP155_TEST_CHAINS).map(([caip10, { name, logo, rgb }]) => (
|
{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
|
<AccountCard
|
||||||
key={name}
|
key={name}
|
||||||
name={name}
|
name={name}
|
||||||
@ -141,7 +159,8 @@ export default function HomePage() {
|
|||||||
chainId={caip10.toString()}
|
chainId={caip10.toString()}
|
||||||
data-testid={'chain-card-' + caip10.toString()}
|
data-testid={'chain-card-' + caip10.toString()}
|
||||||
/>
|
/>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
{Object.entries(SOLANA_TEST_CHAINS).map(([caip10, { name, logo, rgb }]) => (
|
{Object.entries(SOLANA_TEST_CHAINS).map(([caip10, { name, logo, rgb }]) => (
|
||||||
<AccountCard
|
<AccountCard
|
||||||
key={name}
|
key={name}
|
||||||
|
@ -102,7 +102,7 @@ const SettingsStore = {
|
|||||||
} else {
|
} else {
|
||||||
localStorage.removeItem('TEST_NETS')
|
localStorage.removeItem('TEST_NETS')
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SettingsStore
|
export default SettingsStore
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "ES2020",
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
@ -21,7 +21,8 @@
|
|||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"],
|
||||||
|
"react": ["node_modules/@types/react"],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user