Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/testapp/src/pages/add-sub-account/index.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function SubAccounts() {
const sdk = createCoinbaseWalletSDK({
appName: 'CryptoPlayground',
preference: {
keysUrl: 'https://keys-dev.coinbase.com/connect',
keysUrl: 'http://localhost:3005/connect',
options: 'smartWalletOnly',
},
toSubAccountSigner: getCryptoKeyAccount,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Box, Button } from "@chakra-ui/react";
import { createCoinbaseWalletSDK } from "@coinbase/wallet-sdk";
import { useCallback, useState } from "react";
import {
Client,
createPublicClient,
encodeFunctionData,
http,
toHex,
} from "viem";
import {
createBundlerClient,
createPaymasterClient,
SmartAccount,
} from "viem/account-abstraction";
import { baseSepolia } from "viem/chains";
import { abi } from "../../../constants";

export function AddGlobalOwner({
sdk,
subAccount,
}: {
sdk: ReturnType<typeof createCoinbaseWalletSDK>;
subAccount: SmartAccount;
}) {
const [state, setState] = useState<string>();

const handleAddGlobalOwner = useCallback(async () => {
if (!sdk) {
return;
}

const provider = sdk.getProvider();
const accounts = await provider.request({
method: "eth_accounts",
});

console.log("customlogs: accounts", accounts);

try {
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const paymasterClient = createPaymasterClient({
transport: http(
"https://api.developer.coinbase.com/rpc/v1/base-sepolia/S-fOd2n2Oi4fl4e1Crm83XeDXZ7tkg8O"
),
});
const bundlerClient = createBundlerClient({
account: subAccount,
client: client as Client,
transport: http(
"https://api.developer.coinbase.com/rpc/v1/base-sepolia/S-fOd2n2Oi4fl4e1Crm83XeDXZ7tkg8O"
),
paymaster: paymasterClient,
});
// @ts-expect-error
const hash = await bundlerClient.sendUserOperation({
calls: [
{
to: subAccount.address,
data: encodeFunctionData({
abi,
functionName: "addOwnerAddress",
args: [accounts[0]] as const,
}),
value: toHex(0),
},
],
});

console.info("response", hash);
setState(hash as string);
} catch (e) {
console.error("error", e);
}
}, [sdk, subAccount]);

return (
<>
<Button w="full" onClick={handleAddGlobalOwner}>
Add Global Owner
</Button>
{state && (
<Box
as="pre"
w="full"
p={2}
bg="gray.900"
borderRadius="md"
border="1px solid"
borderColor="gray.700"
overflow="auto"
whiteSpace="pre-wrap"
>
{JSON.stringify(state, null, 2)}
</Box>
)}
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Box, Button } from '@chakra-ui/react';
import { createCoinbaseWalletSDK } from '@coinbase/wallet-sdk';
import { useCallback, useState } from 'react';
import { numberToHex } from 'viem';
import { SmartAccount } from 'viem/account-abstraction';
import { baseSepolia } from 'viem/chains';

type AddSubAccountProps = {
sdk: ReturnType<typeof createCoinbaseWalletSDK>;
subAccount: SmartAccount;
};

export function AddSubAccountDeployed({ sdk, subAccount }: AddSubAccountProps) {
const [subAccountAddress, setSubAccountAddress] = useState<string | null>(null);

const handleAddSubAccount = useCallback(async () => {
if (!sdk) {
return;
}
const provider = sdk.getProvider();
await provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: numberToHex(84532) }],
});

const response = (await provider.request({
method: 'wallet_addSubAccount',
params: [
{
version: '1',
account: {
type: 'deployed',
address: subAccount.address,
chainId: baseSepolia.id,
},
},
],
})) as { address: string };

console.info('response', response);
setSubAccountAddress(response.address);
}, [sdk, subAccount]);

return (
<>
<Button w="full" onClick={handleAddSubAccount}>
Add Address Deployed
</Button>
{subAccountAddress && (
<Box
as="pre"
w="full"
p={2}
bg="gray.900"
borderRadius="md"
border="1px solid"
borderColor="gray.700"
>
{subAccountAddress}
</Box>
)}
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Box, Button } from '@chakra-ui/react';
import { createCoinbaseWalletSDK } from '@coinbase/wallet-sdk';
import { useCallback, useState } from 'react';
import { numberToHex } from 'viem';
import { SmartAccount } from 'viem/account-abstraction';

type AddSubAccountUndeployedProps = {
sdk: ReturnType<typeof createCoinbaseWalletSDK>;
subAccount: SmartAccount;
};

export function AddSubAccountUndeployed({ sdk, subAccount }: AddSubAccountUndeployedProps) {
const [subAccountAddress, setSubAccountAddress] = useState<string | null>(null);

const handleAddSubAccount = useCallback(async () => {
if (!sdk) {
return;
}
const provider = sdk.getProvider();
await provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: numberToHex(84532) }],
});

const factoryArgs = await subAccount.getFactoryArgs();

const response = (await provider.request({
method: 'wallet_addSubAccount',
params: [
{
version: '1',
account: {
type: 'undeployed',
address: subAccount.address,
factory: factoryArgs?.factory,
factoryData: factoryArgs?.factoryData,
},
},
],
})) as { address: string };

console.info('response', response);
setSubAccountAddress(response.address);
}, [sdk, subAccount]);

return (
<>
<Button w="full" onClick={handleAddSubAccount}>
Add Address Undeployed
</Button>
{subAccountAddress && (
<Box
as="pre"
w="full"
p={2}
bg="gray.900"
borderRadius="md"
border="1px solid"
borderColor="gray.700"
>
{subAccountAddress}
</Box>
)}
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Box, Button } from '@chakra-ui/react';
import { createCoinbaseWalletSDK } from '@coinbase/wallet-sdk';
import React, { useCallback, useEffect, useState } from 'react';

export function Connect({ sdk }: { sdk: ReturnType<typeof createCoinbaseWalletSDK> }) {
const [state, setState] = useState<string[]>();
const handleConnect = useCallback(async () => {
if (!sdk) {
return;
}

const provider = sdk.getProvider();
const response = await provider.request({
method: 'eth_requestAccounts',
});

console.info('response', response);
setState(response as string[]);
}, [sdk]);

useEffect(() => {
if (!sdk) {
return;
}

const provider = sdk.getProvider();
provider.on('accountsChanged', (accounts) => {
if (accounts.length === 0) {
setState(undefined);
} else {
setState(accounts as string[]);
}
});
}, [sdk]);

return (
<>
<Button w="full" onClick={handleConnect}>
Connect
</Button>
{state && (
<Box
as="pre"
w="full"
p={2}
bg="gray.900"
borderRadius="md"
border="1px solid"
borderColor="gray.700"
>
{JSON.stringify(state, null, 2)}
</Box>
)}
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Box, Button } from "@chakra-ui/react";
import { createCoinbaseWalletSDK } from "@coinbase/wallet-sdk";
import { useCallback, useState } from "react";
import { Client, createPublicClient, http } from "viem";
import { createBundlerClient, createPaymasterClient, SmartAccount } from "viem/account-abstraction";
import { baseSepolia } from "viem/chains";

export function DeploySubAccount({
sdk,
subAccount,
}: {
sdk: ReturnType<typeof createCoinbaseWalletSDK>;
subAccount: SmartAccount;
}) {
const [state, setState] = useState<string>();

const handleDeploySubAccount = useCallback(async () => {
if (!sdk) {
return;
}

try {
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const paymasterClient = createPaymasterClient({
transport: http('https://api.developer.coinbase.com/rpc/v1/base-sepolia/S-fOd2n2Oi4fl4e1Crm83XeDXZ7tkg8O'),
})
const bundlerClient = createBundlerClient({
account: subAccount,
client: client as Client,
transport: http("https://api.developer.coinbase.com/rpc/v1/base-sepolia/S-fOd2n2Oi4fl4e1Crm83XeDXZ7tkg8O"),
paymaster: paymasterClient,
});
// @ts-expect-error
const hash = await bundlerClient.sendUserOperation({
calls: [],
});

console.info("response", hash);
setState(hash as string);
} catch (e) {
console.error("error", e);
}
}, [sdk, subAccount]);

return (
<>
<Button w="full" onClick={handleDeploySubAccount}>
Deploy SubAccount
</Button>
{state && (
<Box
as="pre"
w="full"
p={2}
bg="gray.900"
borderRadius="md"
border="1px solid"
borderColor="gray.700"
overflow="auto"
whiteSpace="pre-wrap"
>
{JSON.stringify(state, null, 2)}
</Box>
)}
</>
);
}
Loading
Loading