Skip to content

Commit 570edad

Browse files
authored
Merge branch 'coinbase:master' into snyk-fix-b294a14ead2952e287b6eab0732fb828
2 parents 4dc3aee + 713a7d8 commit 570edad

40 files changed

+2596
-471
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { Box, Button } from '@chakra-ui/react';
2+
import { createCoinbaseWalletSDK } from '@coinbase/wallet-sdk';
3+
import { useCallback, useState } from 'react';
4+
5+
type GetSubAccountsProps = {
6+
sdk: ReturnType<typeof createCoinbaseWalletSDK>;
7+
};
8+
9+
export function GetSubAccounts({ sdk }: GetSubAccountsProps) {
10+
const [subAccounts, setSubAccounts] = useState<{
11+
subAccounts: {
12+
address: string;
13+
factory: string;
14+
factoryCalldata: string;
15+
}[];
16+
}>();
17+
const [error, setError] = useState<string>();
18+
const [isLoading, setIsLoading] = useState(false);
19+
20+
const handleGetSubAccounts = useCallback(async () => {
21+
if (!sdk) {
22+
return;
23+
}
24+
25+
setIsLoading(true);
26+
try {
27+
const provider = sdk.getProvider();
28+
const accounts = await provider.request({
29+
method: 'eth_requestAccounts',
30+
}) as string[];
31+
if (accounts.length < 2) {
32+
throw new Error('Create a sub account first by clicking the Add Address button');
33+
}
34+
const response = await provider.request({
35+
method: 'wallet_getSubAccounts',
36+
params: [{
37+
account: accounts[1],
38+
domain: window.location.origin,
39+
}],
40+
});
41+
42+
console.info('getSubAccounts response', response);
43+
setSubAccounts(response as { subAccounts: { address: string; factory: string; factoryCalldata: string; }[] });
44+
} catch (error) {
45+
console.error('Error getting sub accounts:', error);
46+
setError(error instanceof Error ? error.message : 'Unknown error');
47+
} finally {
48+
setIsLoading(false);
49+
}
50+
}, [sdk]);
51+
52+
return (
53+
<>
54+
<Button w="full" onClick={handleGetSubAccounts} isLoading={isLoading} loadingText="Getting Sub Accounts...">
55+
Get Sub Accounts
56+
</Button>
57+
{subAccounts && (
58+
<Box
59+
as="pre"
60+
w="full"
61+
p={2}
62+
bg="gray.900"
63+
borderRadius="md"
64+
border="1px solid"
65+
borderColor="gray.700"
66+
overflow="auto"
67+
whiteSpace="pre-wrap"
68+
>
69+
{JSON.stringify(subAccounts, null, 2)}
70+
</Box>
71+
)}
72+
{error && (
73+
<Box
74+
as="pre"
75+
w="full"
76+
p={2}
77+
bg="red.900"
78+
borderRadius="md"
79+
border="1px solid"
80+
borderColor="red.700"
81+
overflow="auto"
82+
whiteSpace="pre-wrap"
83+
>
84+
{error}
85+
</Box>
86+
)}
87+
</>
88+
);
89+
}

examples/testapp/src/pages/add-sub-account/components/GrantSpendPermission.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,13 @@ export function GrantSpendPermission({
9191
],
9292
})) as string[];
9393

94+
const universalAddress = accounts[0] as Address;
9495
const data = {
9596
chainId: baseSepolia.id,
96-
account: accounts[1] as Address,
97+
account: universalAddress,
9798
spender: subAccountAddress as Address,
9899
token: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
99-
allowance: '0x2386F26FC10000',
100+
allowance: '0x5AF3107A4000',
100101
period: 86400,
101102
start: 1724264802,
102103
end: 17242884802,
@@ -108,7 +109,7 @@ export function GrantSpendPermission({
108109

109110
const response = await provider?.request({
110111
method: 'eth_signTypedData_v4',
111-
params: [accounts[0] as Address, spendPermission],
112+
params: [universalAddress, spendPermission],
112113
});
113114
console.info('response', response);
114115
localStorage.setItem('cbwsdk.demo.spend-permission.signature', response as Hex);

examples/testapp/src/pages/add-sub-account/components/SpendPermissions.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Box, Button } from '@chakra-ui/react';
22
import { createCoinbaseWalletSDK, getCryptoKeyAccount } from '@coinbase/wallet-sdk';
33
import { useCallback, useState } from 'react';
4-
import { Hex, numberToHex } from 'viem';
4+
import { encodeFunctionData, Hex, numberToHex } from 'viem';
55
import { baseSepolia } from 'viem/chains';
66

77
import {
@@ -45,6 +45,7 @@ export function SpendPermissions({
4545
extraData: data.extraData,
4646
};
4747

48+
4849
try {
4950
const response = await provider?.request({
5051
method: 'wallet_sendCalls',
@@ -56,15 +57,21 @@ export function SpendPermissions({
5657
calls: [
5758
{
5859
to: SPEND_PERMISSION_MANAGER_ADDRESS,
59-
abi: spendPermissionManagerAbi,
60-
functionName: 'approveWithSignature',
61-
args: [spendPermission, signature],
60+
data: encodeFunctionData({
61+
abi: spendPermissionManagerAbi,
62+
functionName: 'approveWithSignature',
63+
args: [spendPermission, signature],
64+
}),
65+
value: '0x0',
6266
},
6367
{
6468
to: SPEND_PERMISSION_MANAGER_ADDRESS,
65-
abi: spendPermissionManagerAbi,
66-
functionName: 'spend',
67-
args: [spendPermission, BigInt(1)],
69+
data: encodeFunctionData({
70+
abi: spendPermissionManagerAbi,
71+
functionName: 'spend',
72+
args: [spendPermission, BigInt(1)],
73+
}),
74+
value: '0x0',
6875
},
6976
// extra calls...
7077
],

examples/testapp/src/pages/add-sub-account/index.page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { AddSubAccount } from './components/AddSubAccount';
1919
import { AddSubAccountWithoutKeys } from './components/AddSubAccountWithoutKeys';
2020
import { Connect } from './components/Connect';
2121
import { GenerateNewSigner } from './components/GenerateNewSigner';
22+
import { GetSubAccounts } from './components/GetSubAccounts';
2223
import { GrantSpendPermission } from './components/GrantSpendPermission';
2324
import { PersonalSign } from './components/PersonalSign';
2425
import { SendCalls } from './components/SendCalls';
@@ -81,6 +82,7 @@ export default function SubAccounts() {
8182
onAddSubAccount={setSubAccountAddress}
8283
signerFn={getSubAccountSigner}
8384
/>
85+
<GetSubAccounts sdk={sdk} />
8486
<AddSubAccountWithoutKeys
8587
sdk={sdk}
8688
onAddSubAccount={setSubAccountAddress}

examples/testapp/src/pages/auto-sub-account/index.page.tsx

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
VStack,
1313
} from '@chakra-ui/react';
1414
import { getCryptoKeyAccount } from '@coinbase/wallet-sdk';
15-
import { SpendLimitConfig } from '@coinbase/wallet-sdk/dist/core/provider/interface';
1615
import React, { useEffect, useState } from 'react';
1716
import { createPublicClient, http, numberToHex, parseEther } from 'viem';
1817
import { privateKeyToAccount } from 'viem/accounts';
@@ -81,6 +80,22 @@ export default function AutoSubAccount() {
8180
}
8281
};
8382

83+
const handleEthAccounts = async () => {
84+
if (!provider) return;
85+
86+
try {
87+
const response = await provider.request({
88+
method: 'eth_accounts',
89+
params: [],
90+
});
91+
setAccounts(response as string[]);
92+
setLastResult(JSON.stringify(response, null, 2));
93+
} catch (e) {
94+
console.error('error', e);
95+
setLastResult(JSON.stringify(e, null, 2));
96+
}
97+
};
98+
8499
const handleSendTransaction = async () => {
85100
if (!provider || !accounts.length) return;
86101

@@ -215,24 +230,6 @@ export default function AutoSubAccount() {
215230
}
216231
};
217232

218-
const handleSetDefaultSpendLimits = (value: string) => {
219-
const defaultSpendLimits = {
220-
[baseSepolia.id]: [
221-
{
222-
token: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
223-
allowance: '0x2386F26FC10000',
224-
period: 86400,
225-
} as SpendLimitConfig,
226-
],
227-
};
228-
229-
if (value === 'true') {
230-
setSubAccountsConfig((prev) => ({ ...prev, defaultSpendLimits }));
231-
} else {
232-
setSubAccountsConfig((prev) => ({ ...prev, defaultSpendLimits: {} }));
233-
}
234-
};
235-
236233
const handleEthSend = async (amount: string) => {
237234
if (!provider || !accounts.length) return;
238235

@@ -326,18 +323,6 @@ export default function AutoSubAccount() {
326323
</Stack>
327324
</RadioGroup>
328325
</FormControl>
329-
<FormControl>
330-
<FormLabel>Default Spend Limit</FormLabel>
331-
<RadioGroup
332-
value={subAccountsConfig?.defaultSpendLimits?.[baseSepolia.id] ? 'true' : 'false'}
333-
onChange={handleSetDefaultSpendLimits}
334-
>
335-
<Stack direction="row">
336-
<Radio value="true">Enabled</Radio>
337-
<Radio value="false">Disabled</Radio>
338-
</Stack>
339-
</RadioGroup>
340-
</FormControl>
341326
<FormControl>
342327
<FormLabel>Attribution</FormLabel>
343328
<RadioGroup value={getAttributionMode()} onChange={handleAttributionModeChange}>
@@ -385,6 +370,9 @@ export default function AutoSubAccount() {
385370
<Button w="full" onClick={handleRequestAccounts}>
386371
eth_requestAccounts
387372
</Button>
373+
<Button w="full" onClick={handleEthAccounts}>
374+
eth_accounts
375+
</Button>
388376
<Button w="full" onClick={handleSendTransaction} isDisabled={!accounts.length}>
389377
eth_sendTransaction
390378
</Button>

packages/wallet-sdk/src/CoinbaseWalletProvider.test.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,18 @@ describe('Ephemeral methods', () => {
109109
expect(provider['signer']).toBeNull();
110110
});
111111

112-
it('should pass args to SCWSigner', async () => {
113-
const args = { method: 'wallet_sendCalls', params: ['0xdeadbeef'] };
114-
expect(provider['signer']).toBeNull();
115-
await provider.request(args);
116-
expect(mockHandshake).toHaveBeenCalledWith({ method: 'handshake' });
117-
expect(mockRequest).toHaveBeenCalledWith(args);
118-
expect(mockCleanup).toHaveBeenCalled();
119-
expect(provider['signer']).toBeNull();
120-
});
112+
it.each(['wallet_sendCalls', 'wallet_sign'])(
113+
'should perform a successful request after handshake',
114+
async (method) => {
115+
const args = { method, params: ['0xdeadbeef'] };
116+
expect(provider['signer']).toBeNull();
117+
await provider.request(args);
118+
expect(mockHandshake).toHaveBeenCalledWith({ method: 'handshake' });
119+
expect(mockRequest).toHaveBeenCalledWith(args);
120+
expect(mockCleanup).toHaveBeenCalled();
121+
expect(provider['signer']).toBeNull();
122+
}
123+
);
121124
});
122125

123126
describe('Signer configuration', () => {

0 commit comments

Comments
 (0)