@coinbase/cdp-sdk 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +82 -20
- package/_cjs/accounts/evm/toEvmServerAccount.js +20 -7
- package/_cjs/accounts/evm/toEvmServerAccount.js.map +1 -1
- package/_cjs/accounts/evm/toEvmSmartAccount.js +26 -0
- package/_cjs/accounts/evm/toEvmSmartAccount.js.map +1 -1
- package/_cjs/accounts/evm/toNetworkScopedEvmServerAccount.js +28 -0
- package/_cjs/accounts/evm/toNetworkScopedEvmServerAccount.js.map +1 -0
- package/_cjs/accounts/evm/toNetworkScopedEvmSmartAccount.js +26 -0
- package/_cjs/accounts/evm/toNetworkScopedEvmSmartAccount.js.map +1 -0
- package/_cjs/actions/evm/signAndWrapTypedDataForSmartAccount.js +186 -0
- package/_cjs/actions/evm/signAndWrapTypedDataForSmartAccount.js.map +1 -0
- package/_cjs/actions/evm/swap/createSwapQuote.js +26 -8
- package/_cjs/actions/evm/swap/createSwapQuote.js.map +1 -1
- package/_cjs/actions/evm/swap/getSwapPrice.js +3 -2
- package/_cjs/actions/evm/swap/getSwapPrice.js.map +1 -1
- package/_cjs/actions/evm/swap/sendSwapOperation.js +153 -0
- package/_cjs/actions/evm/swap/sendSwapOperation.js.map +1 -0
- package/_cjs/actions/evm/swap/sendSwapTransaction.js +16 -2
- package/_cjs/actions/evm/swap/sendSwapTransaction.js.map +1 -1
- package/_cjs/client/evm/evm.js +13 -6
- package/_cjs/client/evm/evm.js.map +1 -1
- package/_cjs/client/policies/policies.js +6 -2
- package/_cjs/client/policies/policies.js.map +1 -1
- package/_cjs/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js +45 -1
- package/_cjs/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js.map +1 -1
- package/_cjs/openapi-client/generated/evm-accounts/evm-accounts.msw.js +48 -0
- package/_cjs/openapi-client/generated/evm-accounts/evm-accounts.msw.js.map +1 -1
- package/_cjs/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.js +35 -0
- package/_cjs/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.js.map +1 -1
- package/_cjs/openapi-client/generated/index.msw.js +5 -5
- package/_cjs/openapi-client/generated/index.msw.js.map +1 -1
- package/_cjs/openapi-client/generated/policy-engine/policy-engine.msw.js +342 -1
- package/_cjs/openapi-client/generated/policy-engine/policy-engine.msw.js.map +1 -1
- package/_cjs/openapi-client/generated/solana-accounts/solana-accounts.msw.js +40 -0
- package/_cjs/openapi-client/generated/solana-accounts/solana-accounts.msw.js.map +1 -1
- package/_cjs/policies/schema.js +83 -2
- package/_cjs/policies/schema.js.map +1 -1
- package/_cjs/utils/uuidV4.js +26 -0
- package/_cjs/utils/uuidV4.js.map +1 -0
- package/_cjs/version.js +1 -1
- package/_esm/accounts/evm/toEvmServerAccount.js +21 -8
- package/_esm/accounts/evm/toEvmServerAccount.js.map +1 -1
- package/_esm/accounts/evm/toEvmSmartAccount.js +26 -0
- package/_esm/accounts/evm/toEvmSmartAccount.js.map +1 -1
- package/_esm/accounts/evm/toNetworkScopedEvmServerAccount.js +25 -0
- package/_esm/accounts/evm/toNetworkScopedEvmServerAccount.js.map +1 -0
- package/_esm/accounts/evm/toNetworkScopedEvmSmartAccount.js +23 -0
- package/_esm/accounts/evm/toNetworkScopedEvmSmartAccount.js.map +1 -0
- package/_esm/actions/evm/signAndWrapTypedDataForSmartAccount.js +181 -0
- package/_esm/actions/evm/signAndWrapTypedDataForSmartAccount.js.map +1 -0
- package/_esm/actions/evm/swap/createSwapQuote.js +26 -8
- package/_esm/actions/evm/swap/createSwapQuote.js.map +1 -1
- package/_esm/actions/evm/swap/getSwapPrice.js +3 -2
- package/_esm/actions/evm/swap/getSwapPrice.js.map +1 -1
- package/_esm/actions/evm/swap/sendSwapOperation.js +150 -0
- package/_esm/actions/evm/swap/sendSwapOperation.js.map +1 -0
- package/_esm/actions/evm/swap/sendSwapTransaction.js +16 -2
- package/_esm/actions/evm/swap/sendSwapTransaction.js.map +1 -1
- package/_esm/client/evm/evm.js +14 -7
- package/_esm/client/evm/evm.js.map +1 -1
- package/_esm/client/policies/policies.js +7 -3
- package/_esm/client/policies/policies.js.map +1 -1
- package/_esm/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js +44 -0
- package/_esm/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js.map +1 -1
- package/_esm/openapi-client/generated/evm-accounts/evm-accounts.msw.js +48 -0
- package/_esm/openapi-client/generated/evm-accounts/evm-accounts.msw.js.map +1 -1
- package/_esm/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.js +35 -0
- package/_esm/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.js.map +1 -1
- package/_esm/openapi-client/generated/index.msw.js +5 -5
- package/_esm/openapi-client/generated/index.msw.js.map +1 -1
- package/_esm/openapi-client/generated/policy-engine/policy-engine.msw.js +321 -0
- package/_esm/openapi-client/generated/policy-engine/policy-engine.msw.js.map +1 -1
- package/_esm/openapi-client/generated/solana-accounts/solana-accounts.msw.js +40 -0
- package/_esm/openapi-client/generated/solana-accounts/solana-accounts.msw.js.map +1 -1
- package/_esm/policies/schema.js +83 -2
- package/_esm/policies/schema.js.map +1 -1
- package/_esm/utils/uuidV4.js +23 -0
- package/_esm/utils/uuidV4.js.map +1 -0
- package/_esm/version.js +1 -1
- package/_types/accounts/evm/toEvmServerAccount.d.ts.map +1 -1
- package/_types/accounts/evm/toEvmSmartAccount.d.ts.map +1 -1
- package/_types/accounts/evm/toNetworkScopedEvmServerAccount.d.ts +23 -0
- package/_types/accounts/evm/toNetworkScopedEvmServerAccount.d.ts.map +1 -0
- package/_types/accounts/evm/toNetworkScopedEvmSmartAccount.d.ts +27 -0
- package/_types/accounts/evm/toNetworkScopedEvmSmartAccount.d.ts.map +1 -0
- package/_types/accounts/evm/types.d.ts +26 -9
- package/_types/accounts/evm/types.d.ts.map +1 -1
- package/_types/actions/evm/signAndWrapTypedDataForSmartAccount.d.ts +133 -0
- package/_types/actions/evm/signAndWrapTypedDataForSmartAccount.d.ts.map +1 -0
- package/_types/actions/evm/swap/createSwapQuote.d.ts.map +1 -1
- package/_types/actions/evm/swap/getSwapPrice.d.ts +2 -1
- package/_types/actions/evm/swap/getSwapPrice.d.ts.map +1 -1
- package/_types/actions/evm/swap/sendSwapOperation.d.ts +64 -0
- package/_types/actions/evm/swap/sendSwapOperation.d.ts.map +1 -0
- package/_types/actions/evm/swap/sendSwapTransaction.d.ts.map +1 -1
- package/_types/actions/evm/swap/types.d.ts +131 -5
- package/_types/actions/evm/swap/types.d.ts.map +1 -1
- package/_types/actions/evm/types.d.ts +112 -21
- package/_types/actions/evm/types.d.ts.map +1 -1
- package/_types/client/evm/evm.d.ts.map +1 -1
- package/_types/client/evm/evm.types.d.ts +33 -7
- package/_types/client/evm/evm.types.d.ts.map +1 -1
- package/_types/client/policies/policies.d.ts.map +1 -1
- package/_types/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.d.ts +182 -2
- package/_types/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.d.ts.map +1 -1
- package/_types/openapi-client/generated/evm-accounts/evm-accounts.msw.d.ts.map +1 -1
- package/_types/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.d.ts.map +1 -1
- package/_types/openapi-client/generated/index.msw.d.ts +5 -5
- package/_types/openapi-client/generated/index.msw.d.ts.map +1 -1
- package/_types/openapi-client/generated/policy-engine/policy-engine.msw.d.ts +21 -1
- package/_types/openapi-client/generated/policy-engine/policy-engine.msw.d.ts.map +1 -1
- package/_types/openapi-client/generated/solana-accounts/solana-accounts.msw.d.ts.map +1 -1
- package/_types/openapi-client/index.d.ts.map +1 -1
- package/_types/policies/schema.d.ts +3780 -7
- package/_types/policies/schema.d.ts.map +1 -1
- package/_types/utils/uuidV4.d.ts +10 -0
- package/_types/utils/uuidV4.d.ts.map +1 -0
- package/_types/version.d.ts +1 -1
- package/accounts/evm/toEvmServerAccount.ts +40 -15
- package/accounts/evm/toEvmSmartAccount.ts +33 -0
- package/accounts/evm/toNetworkScopedEvmServerAccount.ts +42 -0
- package/accounts/evm/toNetworkScopedEvmSmartAccount.ts +41 -0
- package/accounts/evm/types.ts +46 -17
- package/actions/evm/signAndWrapTypedDataForSmartAccount.ts +246 -0
- package/actions/evm/swap/createSwapQuote.ts +42 -17
- package/actions/evm/swap/getSwapPrice.ts +15 -11
- package/actions/evm/swap/sendSwapOperation.ts +182 -0
- package/actions/evm/swap/sendSwapTransaction.ts +18 -2
- package/actions/evm/swap/types.ts +154 -5
- package/actions/evm/types.ts +123 -21
- package/client/evm/evm.ts +19 -8
- package/client/evm/evm.types.ts +34 -7
- package/client/policies/policies.ts +12 -3
- package/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.ts +221 -2
- package/openapi-client/generated/evm-accounts/evm-accounts.msw.ts +48 -0
- package/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.ts +35 -0
- package/openapi-client/generated/faucets/faucets.ts +4 -4
- package/openapi-client/generated/index.msw.ts +5 -5
- package/openapi-client/generated/policy-engine/policy-engine.msw.ts +434 -0
- package/openapi-client/generated/solana-accounts/solana-accounts.msw.ts +40 -0
- package/package.json +1 -1
- package/policies/schema.ts +94 -6
- package/utils/uuidV4.ts +23 -0
- package/version.ts +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sendSwapOperation } from "./sendSwapOperation.js";
|
|
1
2
|
import { sendSwapTransaction } from "./sendSwapTransaction.js";
|
|
2
3
|
import {
|
|
3
4
|
CreateSwapQuoteOptions,
|
|
@@ -10,9 +11,12 @@ import {
|
|
|
10
11
|
CdpOpenApiClientType,
|
|
11
12
|
CreateSwapQuoteResponse,
|
|
12
13
|
type SendEvmTransactionBodyNetwork,
|
|
14
|
+
type EvmUserOperationNetwork,
|
|
13
15
|
} from "../../../openapi-client/index.js";
|
|
14
16
|
import { Address, Hex } from "../../../types/misc.js";
|
|
15
17
|
|
|
18
|
+
import type { EvmSmartAccount } from "../../../accounts/evm/types.js";
|
|
19
|
+
|
|
16
20
|
/**
|
|
17
21
|
* Creates a quote for a swap between two tokens on an EVM network.
|
|
18
22
|
*
|
|
@@ -45,16 +49,19 @@ export async function createSwapQuote(
|
|
|
45
49
|
const taker = options.taker as Address;
|
|
46
50
|
|
|
47
51
|
// Call the createEvmSwapQuote function directly with the client's configured API
|
|
48
|
-
const response = await client.createEvmSwapQuote(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
const response = await client.createEvmSwapQuote(
|
|
53
|
+
{
|
|
54
|
+
network: options.network,
|
|
55
|
+
toToken: options.toToken,
|
|
56
|
+
fromToken: options.fromToken,
|
|
57
|
+
fromAmount: options.fromAmount.toString(),
|
|
58
|
+
taker: taker,
|
|
59
|
+
signerAddress: options.signerAddress,
|
|
60
|
+
gasPrice: options.gasPrice?.toString(),
|
|
61
|
+
slippageBps: options.slippageBps,
|
|
62
|
+
},
|
|
63
|
+
options.idempotencyKey,
|
|
64
|
+
);
|
|
58
65
|
|
|
59
66
|
// Check if liquidity is unavailable
|
|
60
67
|
if (!response.liquidityAvailable) {
|
|
@@ -134,13 +141,31 @@ export async function createSwapQuote(
|
|
|
134
141
|
execute: async (
|
|
135
142
|
executeOptions: ExecuteSwapQuoteOptions = {},
|
|
136
143
|
): Promise<ExecuteSwapQuoteResult> => {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
if (options.smartAccount) {
|
|
145
|
+
// Smart account execution - use sendSwapOperation
|
|
146
|
+
const userOpResult = await sendSwapOperation(client, {
|
|
147
|
+
smartAccount: options.smartAccount as EvmSmartAccount,
|
|
148
|
+
network: result.network as EvmUserOperationNetwork,
|
|
149
|
+
swapQuote: result,
|
|
150
|
+
idempotencyKey: executeOptions.idempotencyKey,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
userOpHash: userOpResult.userOpHash,
|
|
155
|
+
smartAccountAddress: userOpResult.smartAccountAddress,
|
|
156
|
+
status: userOpResult.status,
|
|
157
|
+
};
|
|
158
|
+
} else {
|
|
159
|
+
// EOA execution - use sendSwapTransaction
|
|
160
|
+
const { transactionHash } = await sendSwapTransaction(client, {
|
|
161
|
+
address: taker,
|
|
162
|
+
network: result.network as SendEvmTransactionBodyNetwork,
|
|
163
|
+
swapQuote: result,
|
|
164
|
+
idempotencyKey: executeOptions.idempotencyKey,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return { transactionHash };
|
|
168
|
+
}
|
|
144
169
|
},
|
|
145
170
|
};
|
|
146
171
|
|
|
@@ -21,7 +21,8 @@ import { Address } from "../../../types/misc.js";
|
|
|
21
21
|
* toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
22
22
|
* fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
|
|
23
23
|
* fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
|
|
24
|
-
* taker: "0x1234567890123456789012345678901234567890"
|
|
24
|
+
* taker: "0x1234567890123456789012345678901234567890",
|
|
25
|
+
* idempotencyKey: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" // Optional: for request deduplication
|
|
25
26
|
* });
|
|
26
27
|
* ```
|
|
27
28
|
*/
|
|
@@ -30,16 +31,19 @@ export async function getSwapPrice(
|
|
|
30
31
|
options: GetSwapPriceOptions,
|
|
31
32
|
): Promise<GetSwapPriceResult | SwapUnavailableResult> {
|
|
32
33
|
// Call the getEvmSwapPrice function directly with the client's configured API
|
|
33
|
-
const response = await client.getEvmSwapPrice(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
const response = await client.getEvmSwapPrice(
|
|
35
|
+
{
|
|
36
|
+
network: options.network,
|
|
37
|
+
toToken: options.toToken,
|
|
38
|
+
fromToken: options.fromToken,
|
|
39
|
+
fromAmount: options.fromAmount.toString(),
|
|
40
|
+
taker: options.taker,
|
|
41
|
+
signerAddress: options.signerAddress,
|
|
42
|
+
gasPrice: options.gasPrice?.toString(),
|
|
43
|
+
slippageBps: options.slippageBps,
|
|
44
|
+
},
|
|
45
|
+
options.idempotencyKey,
|
|
46
|
+
);
|
|
43
47
|
|
|
44
48
|
// Check if liquidity is unavailable
|
|
45
49
|
if (!response.liquidityAvailable) {
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { concat, numberToHex, size } from "viem";
|
|
2
|
+
|
|
3
|
+
import { createSwapQuote } from "./createSwapQuote.js";
|
|
4
|
+
import { createDeterministicUuidV4 } from "../../../utils/uuidV4.js";
|
|
5
|
+
import { sendUserOperation } from "../sendUserOperation.js";
|
|
6
|
+
import { signAndWrapTypedDataForSmartAccount } from "../signAndWrapTypedDataForSmartAccount.js";
|
|
7
|
+
|
|
8
|
+
import type { SendSwapOperationOptions, SendSwapOperationResult } from "./types.js";
|
|
9
|
+
import type { EvmSmartAccount } from "../../../accounts/evm/types.js";
|
|
10
|
+
import type {
|
|
11
|
+
CreateSwapQuoteResult,
|
|
12
|
+
CreateSwapQuoteOptions,
|
|
13
|
+
SwapUnavailableResult,
|
|
14
|
+
} from "../../../client/evm/evm.types.js";
|
|
15
|
+
import type {
|
|
16
|
+
CdpOpenApiClientType,
|
|
17
|
+
EvmUserOperationNetwork,
|
|
18
|
+
} from "../../../openapi-client/index.js";
|
|
19
|
+
import type { Hex } from "../../../types/misc.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Sends a swap operation to the blockchain via a smart account user operation.
|
|
23
|
+
* Handles any permit2 signatures required for the swap.
|
|
24
|
+
*
|
|
25
|
+
* If you encounter token allowance issues, you'll need to perform a token approval transaction first to allow
|
|
26
|
+
* the Permit2 contract to spend the appropriate amount of your fromToken.
|
|
27
|
+
* See examples for code on handling token approvals.
|
|
28
|
+
*
|
|
29
|
+
* @param {CdpOpenApiClientType} client - The client to use for sending the swap operation.
|
|
30
|
+
* @param {SendSwapOperationOptions} options - The options for the swap submission.
|
|
31
|
+
*
|
|
32
|
+
* @returns {Promise<SendSwapOperationResult>} A promise that resolves to the user operation result.
|
|
33
|
+
*
|
|
34
|
+
* @throws {Error} If liquidity is not available for the swap.
|
|
35
|
+
* @throws {Error} If there are insufficient token allowances. In this case, you need to approve the
|
|
36
|
+
* Permit2 contract to spend your tokens before attempting the swap. The error message
|
|
37
|
+
* will include the current allowance and the spender address that needs approval.
|
|
38
|
+
* @throws {Error} If no transaction data is found in the swap result.
|
|
39
|
+
*
|
|
40
|
+
* @example **Sending a swap with pre-created swap quote object**
|
|
41
|
+
* ```ts
|
|
42
|
+
* // First create a swap quote
|
|
43
|
+
* const swapQuote = await cdp.evm.createSwapQuote({
|
|
44
|
+
* network: "base",
|
|
45
|
+
* toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
46
|
+
* fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
|
|
47
|
+
* fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
|
|
48
|
+
* taker: smartAccount.address
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* // Check if liquidity is available
|
|
52
|
+
* if (!swapQuote.liquidityAvailable) {
|
|
53
|
+
* console.error("Insufficient liquidity for swap");
|
|
54
|
+
* return;
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* // Send the swap operation
|
|
58
|
+
* const result = await sendSwapOperation(client, {
|
|
59
|
+
* smartAccount: smartAccount,
|
|
60
|
+
* swapQuote: swapQuote
|
|
61
|
+
* });
|
|
62
|
+
*
|
|
63
|
+
* console.log(`Swap operation sent with user op hash: ${result.userOpHash}`);
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @example **Sending a swap with inline options (all-in-one)**
|
|
67
|
+
* ```ts
|
|
68
|
+
* // Send swap operation in one call
|
|
69
|
+
* const result = await sendSwapOperation(client, {
|
|
70
|
+
* smartAccount: smartAccount,
|
|
71
|
+
* network: "base",
|
|
72
|
+
* toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
73
|
+
* fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
|
|
74
|
+
* fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
|
|
75
|
+
* taker: smartAccount.address
|
|
76
|
+
* });
|
|
77
|
+
*
|
|
78
|
+
* console.log(`Swap operation sent with user op hash: ${result.userOpHash}`);
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export async function sendSwapOperation(
|
|
82
|
+
client: CdpOpenApiClientType,
|
|
83
|
+
options: SendSwapOperationOptions,
|
|
84
|
+
): Promise<SendSwapOperationResult> {
|
|
85
|
+
const { smartAccount, paymasterUrl, idempotencyKey } = options;
|
|
86
|
+
|
|
87
|
+
let swapResult: CreateSwapQuoteResult | SwapUnavailableResult;
|
|
88
|
+
|
|
89
|
+
// Determine if we need to create the swap quote or use the provided one
|
|
90
|
+
if ("swapQuote" in options) {
|
|
91
|
+
// Use the provided swap quote
|
|
92
|
+
swapResult = options.swapQuote;
|
|
93
|
+
} else {
|
|
94
|
+
// Create the swap quote using the provided options (inline options)
|
|
95
|
+
/**
|
|
96
|
+
* Deterministically derive a new idempotency key from the provided idempotency key for swap quote creation to avoid key reuse.
|
|
97
|
+
*/
|
|
98
|
+
const swapQuoteIdempotencyKey = idempotencyKey
|
|
99
|
+
? createDeterministicUuidV4(idempotencyKey, "createSwapQuote")
|
|
100
|
+
: undefined;
|
|
101
|
+
|
|
102
|
+
swapResult = await createSwapQuote(client, {
|
|
103
|
+
network: options.network as CreateSwapQuoteOptions["network"],
|
|
104
|
+
toToken: options.toToken,
|
|
105
|
+
fromToken: options.fromToken,
|
|
106
|
+
fromAmount: options.fromAmount,
|
|
107
|
+
taker: options.taker,
|
|
108
|
+
signerAddress: options.signerAddress,
|
|
109
|
+
gasPrice: options.gasPrice,
|
|
110
|
+
slippageBps: options.slippageBps,
|
|
111
|
+
idempotencyKey: swapQuoteIdempotencyKey,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check if liquidity is available
|
|
116
|
+
if (!swapResult.liquidityAvailable) {
|
|
117
|
+
throw new Error("Insufficient liquidity for swap");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// At this point, we know that swapResult is CreateSwapQuoteResult
|
|
121
|
+
const swap = swapResult as CreateSwapQuoteResult;
|
|
122
|
+
|
|
123
|
+
// Check for allowance issues
|
|
124
|
+
if (swap.issues?.allowance) {
|
|
125
|
+
const { currentAllowance, spender } = swap.issues.allowance;
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Insufficient token allowance for swap. Current allowance: ${currentAllowance}. ` +
|
|
128
|
+
`Please approve the Permit2 contract (${spender}) to spend your tokens.`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// If the transaction doesn't exist, throw an error
|
|
133
|
+
if (!swap.transaction) {
|
|
134
|
+
throw new Error("No transaction data found in the swap");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Get the transaction data and modify it if needed for Permit2
|
|
138
|
+
let txData = swap.transaction.data as Hex;
|
|
139
|
+
|
|
140
|
+
if (swap.permit2?.eip712) {
|
|
141
|
+
// Create the permit2 idempotency key
|
|
142
|
+
const permit2IdempotencyKey = idempotencyKey
|
|
143
|
+
? createDeterministicUuidV4(idempotencyKey, "permit2")
|
|
144
|
+
: undefined;
|
|
145
|
+
|
|
146
|
+
// Sign and wrap the permit2 typed data according to the Coinbase Smart Wallet contract requirements for EIP-712 signatures
|
|
147
|
+
const { signature: wrappedSignature } = await signAndWrapTypedDataForSmartAccount(client, {
|
|
148
|
+
smartAccount,
|
|
149
|
+
chainId: BigInt(swap.permit2.eip712.domain.chainId || 1),
|
|
150
|
+
typedData: swap.permit2.eip712,
|
|
151
|
+
ownerIndex: 0n,
|
|
152
|
+
idempotencyKey: permit2IdempotencyKey,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Calculate the Permit2 signature length as a 32-byte hex value
|
|
156
|
+
const permit2SignatureLengthInHex = numberToHex(size(wrappedSignature), {
|
|
157
|
+
signed: false,
|
|
158
|
+
size: 32,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Append the Permit2 signature length and signature to the transaction data
|
|
162
|
+
txData = concat([txData, permit2SignatureLengthInHex, wrappedSignature]);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Send the swap as a user operation
|
|
166
|
+
const result = await sendUserOperation(client, {
|
|
167
|
+
smartAccount: smartAccount as EvmSmartAccount,
|
|
168
|
+
network: swap.network as EvmUserOperationNetwork,
|
|
169
|
+
paymasterUrl,
|
|
170
|
+
idempotencyKey,
|
|
171
|
+
calls: [
|
|
172
|
+
{
|
|
173
|
+
to: swap.transaction.to,
|
|
174
|
+
data: txData,
|
|
175
|
+
// Only include value if it exists
|
|
176
|
+
...(swap.transaction.value ? { value: BigInt(swap.transaction.value) } : {}),
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { concat, numberToHex, size } from "viem";
|
|
2
2
|
|
|
3
3
|
import { createSwapQuote } from "./createSwapQuote.js";
|
|
4
|
+
import { createDeterministicUuidV4 } from "../../../utils/uuidV4.js";
|
|
4
5
|
import { sendTransaction } from "../sendTransaction.js";
|
|
5
6
|
|
|
6
7
|
import type { SendSwapTransactionOptions, SendSwapTransactionResult } from "./types.js";
|
|
@@ -90,6 +91,13 @@ export async function sendSwapTransaction(
|
|
|
90
91
|
swapResult = options.swapQuote;
|
|
91
92
|
} else {
|
|
92
93
|
// Create the swap quote using the provided options (InlineSendSwapTransactionOptions)
|
|
94
|
+
/**
|
|
95
|
+
* Deterministically derive a new idempotency key from the provided idempotency key for swap quote creation to avoid key reuse.
|
|
96
|
+
*/
|
|
97
|
+
const swapQuoteIdempotencyKey = idempotencyKey
|
|
98
|
+
? createDeterministicUuidV4(idempotencyKey, "createSwapQuote")
|
|
99
|
+
: undefined;
|
|
100
|
+
|
|
93
101
|
swapResult = await createSwapQuote(client, {
|
|
94
102
|
network: options.network as CreateSwapQuoteOptions["network"],
|
|
95
103
|
toToken: options.toToken,
|
|
@@ -99,6 +107,7 @@ export async function sendSwapTransaction(
|
|
|
99
107
|
signerAddress: options.signerAddress,
|
|
100
108
|
gasPrice: options.gasPrice,
|
|
101
109
|
slippageBps: options.slippageBps,
|
|
110
|
+
idempotencyKey: swapQuoteIdempotencyKey,
|
|
102
111
|
});
|
|
103
112
|
}
|
|
104
113
|
|
|
@@ -128,7 +137,14 @@ export async function sendSwapTransaction(
|
|
|
128
137
|
let txData = swap.transaction.data as Hex;
|
|
129
138
|
|
|
130
139
|
if (swap.permit2?.eip712) {
|
|
131
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Sign the Permit2 EIP-712 message.
|
|
142
|
+
* Deterministically derive a new idempotency key from the provided idempotency key for permit2 signing to avoid key reuse.
|
|
143
|
+
*/
|
|
144
|
+
const permit2IdempotencyKey = idempotencyKey
|
|
145
|
+
? createDeterministicUuidV4(idempotencyKey, "permit2")
|
|
146
|
+
: undefined;
|
|
147
|
+
|
|
132
148
|
const signature = await client.signEvmTypedData(
|
|
133
149
|
address,
|
|
134
150
|
{
|
|
@@ -137,7 +153,7 @@ export async function sendSwapTransaction(
|
|
|
137
153
|
primaryType: swap.permit2.eip712.primaryType,
|
|
138
154
|
message: swap.permit2.eip712.message,
|
|
139
155
|
},
|
|
140
|
-
|
|
156
|
+
permit2IdempotencyKey,
|
|
141
157
|
);
|
|
142
158
|
|
|
143
159
|
// Calculate the signature length as a 32-byte hex value
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import type { EvmSmartAccount } from "../../../accounts/evm/types.js";
|
|
1
2
|
import type {
|
|
2
3
|
CreateSwapQuoteOptions,
|
|
3
4
|
CreateSwapQuoteResult,
|
|
4
5
|
SwapUnavailableResult,
|
|
5
6
|
} from "../../../client/evm/evm.types.js";
|
|
6
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
SendEvmTransactionBodyNetwork,
|
|
9
|
+
EvmUserOperationNetwork,
|
|
10
|
+
EvmUserOperationStatus,
|
|
11
|
+
} from "../../../openapi-client/index.js";
|
|
7
12
|
import type { Address, Hex } from "../../../types/misc.js";
|
|
8
13
|
|
|
9
14
|
/**
|
|
@@ -76,12 +81,12 @@ export interface SendSwapTransactionResult {
|
|
|
76
81
|
/**
|
|
77
82
|
* Options for creating a swap quote (account-level).
|
|
78
83
|
*/
|
|
79
|
-
export type
|
|
84
|
+
export type AccountQuoteSwapOptions = Omit<CreateSwapQuoteOptions, "taker">;
|
|
80
85
|
|
|
81
86
|
/**
|
|
82
87
|
* Result of creating a swap quote (account-level).
|
|
83
88
|
*/
|
|
84
|
-
export type
|
|
89
|
+
export type AccountQuoteSwapResult = CreateSwapQuoteResult | SwapUnavailableResult;
|
|
85
90
|
|
|
86
91
|
/**
|
|
87
92
|
* Options when providing an already created swap quote (account-level).
|
|
@@ -129,9 +134,153 @@ interface AccountInlineSwapOptions {
|
|
|
129
134
|
* Options for executing a token swap (account-level).
|
|
130
135
|
* The taker is automatically set to the account's address.
|
|
131
136
|
*/
|
|
132
|
-
export type
|
|
137
|
+
export type AccountSwapOptions = AccountQuoteBasedSwapOptions | AccountInlineSwapOptions;
|
|
133
138
|
|
|
134
139
|
/**
|
|
135
140
|
* Result of executing a token swap (account-level).
|
|
136
141
|
*/
|
|
137
|
-
export type
|
|
142
|
+
export type AccountSwapResult = SendSwapTransactionResult;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Options for creating a swap quote (smart account-level).
|
|
146
|
+
*/
|
|
147
|
+
export type SmartAccountQuoteSwapOptions = Omit<CreateSwapQuoteOptions, "taker">;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Result of creating a swap quote (smart account-level).
|
|
151
|
+
*/
|
|
152
|
+
export type SmartAccountQuoteSwapResult = CreateSwapQuoteResult | SwapUnavailableResult;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Options when providing an already created swap quote (smart account-level).
|
|
156
|
+
*/
|
|
157
|
+
interface SmartAccountQuoteBasedSwapOptions {
|
|
158
|
+
/**
|
|
159
|
+
* The swap quote returned by the createSwapQuote method.
|
|
160
|
+
*/
|
|
161
|
+
swapQuote: CreateSwapQuoteResult;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Optional URL of the paymaster service to use for gas sponsorship.
|
|
165
|
+
*/
|
|
166
|
+
paymasterUrl?: string;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Optional idempotency key for the request.
|
|
170
|
+
*/
|
|
171
|
+
idempotencyKey?: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Options when creating a swap quote inline (smart account-level).
|
|
176
|
+
*/
|
|
177
|
+
interface SmartAccountInlineSwapOptions {
|
|
178
|
+
/**
|
|
179
|
+
* The network to execute the swap on (e.g., "ethereum", "base").
|
|
180
|
+
*/
|
|
181
|
+
network: SendEvmTransactionBodyNetwork;
|
|
182
|
+
/** The token to receive (destination token). */
|
|
183
|
+
toToken: Address;
|
|
184
|
+
/** The token to send (source token). */
|
|
185
|
+
fromToken: Address;
|
|
186
|
+
/** The amount to send in atomic units of the token. */
|
|
187
|
+
fromAmount: bigint;
|
|
188
|
+
/** The signer address (only needed if taker is a smart contract). */
|
|
189
|
+
signerAddress?: Address;
|
|
190
|
+
/** The gas price in Wei. */
|
|
191
|
+
gasPrice?: bigint;
|
|
192
|
+
/** The slippage tolerance in basis points (0-10000). */
|
|
193
|
+
slippageBps?: number;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Optional URL of the paymaster service to use for gas sponsorship.
|
|
197
|
+
*/
|
|
198
|
+
paymasterUrl?: string;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Optional idempotency key for the request.
|
|
202
|
+
*/
|
|
203
|
+
idempotencyKey?: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Options for executing a token swap (smart account-level).
|
|
208
|
+
* The taker is automatically set to the smart account's address.
|
|
209
|
+
*/
|
|
210
|
+
export type SmartAccountSwapOptions =
|
|
211
|
+
| SmartAccountQuoteBasedSwapOptions
|
|
212
|
+
| SmartAccountInlineSwapOptions;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Result of executing a token swap (smart account-level).
|
|
216
|
+
*/
|
|
217
|
+
export type SmartAccountSwapResult = {
|
|
218
|
+
/** The address of the smart wallet. */
|
|
219
|
+
smartAccountAddress: Address;
|
|
220
|
+
/** The status of the user operation. */
|
|
221
|
+
status: string;
|
|
222
|
+
/** The hash of the user operation. */
|
|
223
|
+
userOpHash: Hex;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Base options for sending a swap operation via smart account.
|
|
228
|
+
*/
|
|
229
|
+
interface BaseSendSwapOperationOptions {
|
|
230
|
+
/** The smart account that will execute the swap. */
|
|
231
|
+
smartAccount: EvmSmartAccount;
|
|
232
|
+
/** Optional URL of the paymaster service to use for gas sponsorship. */
|
|
233
|
+
paymasterUrl?: string;
|
|
234
|
+
/** Optional idempotency key for the request. */
|
|
235
|
+
idempotencyKey?: string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Options when providing an already created swap quote (for sendSwapOperation).
|
|
240
|
+
*/
|
|
241
|
+
interface QuoteBasedSendSwapOperationOptions extends BaseSendSwapOperationOptions {
|
|
242
|
+
/** The swap quote returned by the createSwapQuote method. */
|
|
243
|
+
swapQuote: CreateSwapQuoteResult;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Options when creating a swap quote inline (for sendSwapOperation).
|
|
248
|
+
*/
|
|
249
|
+
interface InlineSendSwapOperationOptions extends BaseSendSwapOperationOptions {
|
|
250
|
+
/** The network to execute the swap on. */
|
|
251
|
+
network: EvmUserOperationNetwork;
|
|
252
|
+
/** The token to receive (destination token). */
|
|
253
|
+
toToken: Address;
|
|
254
|
+
/** The token to send (source token). */
|
|
255
|
+
fromToken: Address;
|
|
256
|
+
/** The amount to send in atomic units of the token. */
|
|
257
|
+
fromAmount: bigint;
|
|
258
|
+
/** The address that will perform the swap. */
|
|
259
|
+
taker: Address;
|
|
260
|
+
/** The signer address (required since taker is a smart contract). */
|
|
261
|
+
signerAddress: Address;
|
|
262
|
+
/** The gas price in Wei. */
|
|
263
|
+
gasPrice?: bigint;
|
|
264
|
+
/** The slippage tolerance in basis points (0-10000). */
|
|
265
|
+
slippageBps?: number;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Options for sending a swap operation via smart account.
|
|
270
|
+
* Either provide a pre-created swap quote result or inline swap parameters.
|
|
271
|
+
*/
|
|
272
|
+
export type SendSwapOperationOptions =
|
|
273
|
+
| QuoteBasedSendSwapOperationOptions
|
|
274
|
+
| InlineSendSwapOperationOptions;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Result of sending a swap operation via smart account.
|
|
278
|
+
*/
|
|
279
|
+
export type SendSwapOperationResult = {
|
|
280
|
+
/** The address of the smart wallet. */
|
|
281
|
+
smartAccountAddress: Address;
|
|
282
|
+
/** The status of the user operation. */
|
|
283
|
+
status: typeof EvmUserOperationStatus.broadcast;
|
|
284
|
+
/** The hash of the user operation. */
|
|
285
|
+
userOpHash: Hex;
|
|
286
|
+
};
|