@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.
Files changed (145) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +82 -20
  3. package/_cjs/accounts/evm/toEvmServerAccount.js +20 -7
  4. package/_cjs/accounts/evm/toEvmServerAccount.js.map +1 -1
  5. package/_cjs/accounts/evm/toEvmSmartAccount.js +26 -0
  6. package/_cjs/accounts/evm/toEvmSmartAccount.js.map +1 -1
  7. package/_cjs/accounts/evm/toNetworkScopedEvmServerAccount.js +28 -0
  8. package/_cjs/accounts/evm/toNetworkScopedEvmServerAccount.js.map +1 -0
  9. package/_cjs/accounts/evm/toNetworkScopedEvmSmartAccount.js +26 -0
  10. package/_cjs/accounts/evm/toNetworkScopedEvmSmartAccount.js.map +1 -0
  11. package/_cjs/actions/evm/signAndWrapTypedDataForSmartAccount.js +186 -0
  12. package/_cjs/actions/evm/signAndWrapTypedDataForSmartAccount.js.map +1 -0
  13. package/_cjs/actions/evm/swap/createSwapQuote.js +26 -8
  14. package/_cjs/actions/evm/swap/createSwapQuote.js.map +1 -1
  15. package/_cjs/actions/evm/swap/getSwapPrice.js +3 -2
  16. package/_cjs/actions/evm/swap/getSwapPrice.js.map +1 -1
  17. package/_cjs/actions/evm/swap/sendSwapOperation.js +153 -0
  18. package/_cjs/actions/evm/swap/sendSwapOperation.js.map +1 -0
  19. package/_cjs/actions/evm/swap/sendSwapTransaction.js +16 -2
  20. package/_cjs/actions/evm/swap/sendSwapTransaction.js.map +1 -1
  21. package/_cjs/client/evm/evm.js +13 -6
  22. package/_cjs/client/evm/evm.js.map +1 -1
  23. package/_cjs/client/policies/policies.js +6 -2
  24. package/_cjs/client/policies/policies.js.map +1 -1
  25. package/_cjs/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js +45 -1
  26. package/_cjs/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js.map +1 -1
  27. package/_cjs/openapi-client/generated/evm-accounts/evm-accounts.msw.js +48 -0
  28. package/_cjs/openapi-client/generated/evm-accounts/evm-accounts.msw.js.map +1 -1
  29. package/_cjs/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.js +35 -0
  30. package/_cjs/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.js.map +1 -1
  31. package/_cjs/openapi-client/generated/index.msw.js +5 -5
  32. package/_cjs/openapi-client/generated/index.msw.js.map +1 -1
  33. package/_cjs/openapi-client/generated/policy-engine/policy-engine.msw.js +342 -1
  34. package/_cjs/openapi-client/generated/policy-engine/policy-engine.msw.js.map +1 -1
  35. package/_cjs/openapi-client/generated/solana-accounts/solana-accounts.msw.js +40 -0
  36. package/_cjs/openapi-client/generated/solana-accounts/solana-accounts.msw.js.map +1 -1
  37. package/_cjs/policies/schema.js +83 -2
  38. package/_cjs/policies/schema.js.map +1 -1
  39. package/_cjs/utils/uuidV4.js +26 -0
  40. package/_cjs/utils/uuidV4.js.map +1 -0
  41. package/_cjs/version.js +1 -1
  42. package/_esm/accounts/evm/toEvmServerAccount.js +21 -8
  43. package/_esm/accounts/evm/toEvmServerAccount.js.map +1 -1
  44. package/_esm/accounts/evm/toEvmSmartAccount.js +26 -0
  45. package/_esm/accounts/evm/toEvmSmartAccount.js.map +1 -1
  46. package/_esm/accounts/evm/toNetworkScopedEvmServerAccount.js +25 -0
  47. package/_esm/accounts/evm/toNetworkScopedEvmServerAccount.js.map +1 -0
  48. package/_esm/accounts/evm/toNetworkScopedEvmSmartAccount.js +23 -0
  49. package/_esm/accounts/evm/toNetworkScopedEvmSmartAccount.js.map +1 -0
  50. package/_esm/actions/evm/signAndWrapTypedDataForSmartAccount.js +181 -0
  51. package/_esm/actions/evm/signAndWrapTypedDataForSmartAccount.js.map +1 -0
  52. package/_esm/actions/evm/swap/createSwapQuote.js +26 -8
  53. package/_esm/actions/evm/swap/createSwapQuote.js.map +1 -1
  54. package/_esm/actions/evm/swap/getSwapPrice.js +3 -2
  55. package/_esm/actions/evm/swap/getSwapPrice.js.map +1 -1
  56. package/_esm/actions/evm/swap/sendSwapOperation.js +150 -0
  57. package/_esm/actions/evm/swap/sendSwapOperation.js.map +1 -0
  58. package/_esm/actions/evm/swap/sendSwapTransaction.js +16 -2
  59. package/_esm/actions/evm/swap/sendSwapTransaction.js.map +1 -1
  60. package/_esm/client/evm/evm.js +14 -7
  61. package/_esm/client/evm/evm.js.map +1 -1
  62. package/_esm/client/policies/policies.js +7 -3
  63. package/_esm/client/policies/policies.js.map +1 -1
  64. package/_esm/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js +44 -0
  65. package/_esm/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js.map +1 -1
  66. package/_esm/openapi-client/generated/evm-accounts/evm-accounts.msw.js +48 -0
  67. package/_esm/openapi-client/generated/evm-accounts/evm-accounts.msw.js.map +1 -1
  68. package/_esm/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.js +35 -0
  69. package/_esm/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.js.map +1 -1
  70. package/_esm/openapi-client/generated/index.msw.js +5 -5
  71. package/_esm/openapi-client/generated/index.msw.js.map +1 -1
  72. package/_esm/openapi-client/generated/policy-engine/policy-engine.msw.js +321 -0
  73. package/_esm/openapi-client/generated/policy-engine/policy-engine.msw.js.map +1 -1
  74. package/_esm/openapi-client/generated/solana-accounts/solana-accounts.msw.js +40 -0
  75. package/_esm/openapi-client/generated/solana-accounts/solana-accounts.msw.js.map +1 -1
  76. package/_esm/policies/schema.js +83 -2
  77. package/_esm/policies/schema.js.map +1 -1
  78. package/_esm/utils/uuidV4.js +23 -0
  79. package/_esm/utils/uuidV4.js.map +1 -0
  80. package/_esm/version.js +1 -1
  81. package/_types/accounts/evm/toEvmServerAccount.d.ts.map +1 -1
  82. package/_types/accounts/evm/toEvmSmartAccount.d.ts.map +1 -1
  83. package/_types/accounts/evm/toNetworkScopedEvmServerAccount.d.ts +23 -0
  84. package/_types/accounts/evm/toNetworkScopedEvmServerAccount.d.ts.map +1 -0
  85. package/_types/accounts/evm/toNetworkScopedEvmSmartAccount.d.ts +27 -0
  86. package/_types/accounts/evm/toNetworkScopedEvmSmartAccount.d.ts.map +1 -0
  87. package/_types/accounts/evm/types.d.ts +26 -9
  88. package/_types/accounts/evm/types.d.ts.map +1 -1
  89. package/_types/actions/evm/signAndWrapTypedDataForSmartAccount.d.ts +133 -0
  90. package/_types/actions/evm/signAndWrapTypedDataForSmartAccount.d.ts.map +1 -0
  91. package/_types/actions/evm/swap/createSwapQuote.d.ts.map +1 -1
  92. package/_types/actions/evm/swap/getSwapPrice.d.ts +2 -1
  93. package/_types/actions/evm/swap/getSwapPrice.d.ts.map +1 -1
  94. package/_types/actions/evm/swap/sendSwapOperation.d.ts +64 -0
  95. package/_types/actions/evm/swap/sendSwapOperation.d.ts.map +1 -0
  96. package/_types/actions/evm/swap/sendSwapTransaction.d.ts.map +1 -1
  97. package/_types/actions/evm/swap/types.d.ts +131 -5
  98. package/_types/actions/evm/swap/types.d.ts.map +1 -1
  99. package/_types/actions/evm/types.d.ts +112 -21
  100. package/_types/actions/evm/types.d.ts.map +1 -1
  101. package/_types/client/evm/evm.d.ts.map +1 -1
  102. package/_types/client/evm/evm.types.d.ts +33 -7
  103. package/_types/client/evm/evm.types.d.ts.map +1 -1
  104. package/_types/client/policies/policies.d.ts.map +1 -1
  105. package/_types/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.d.ts +182 -2
  106. package/_types/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.d.ts.map +1 -1
  107. package/_types/openapi-client/generated/evm-accounts/evm-accounts.msw.d.ts.map +1 -1
  108. package/_types/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.d.ts.map +1 -1
  109. package/_types/openapi-client/generated/index.msw.d.ts +5 -5
  110. package/_types/openapi-client/generated/index.msw.d.ts.map +1 -1
  111. package/_types/openapi-client/generated/policy-engine/policy-engine.msw.d.ts +21 -1
  112. package/_types/openapi-client/generated/policy-engine/policy-engine.msw.d.ts.map +1 -1
  113. package/_types/openapi-client/generated/solana-accounts/solana-accounts.msw.d.ts.map +1 -1
  114. package/_types/openapi-client/index.d.ts.map +1 -1
  115. package/_types/policies/schema.d.ts +3780 -7
  116. package/_types/policies/schema.d.ts.map +1 -1
  117. package/_types/utils/uuidV4.d.ts +10 -0
  118. package/_types/utils/uuidV4.d.ts.map +1 -0
  119. package/_types/version.d.ts +1 -1
  120. package/accounts/evm/toEvmServerAccount.ts +40 -15
  121. package/accounts/evm/toEvmSmartAccount.ts +33 -0
  122. package/accounts/evm/toNetworkScopedEvmServerAccount.ts +42 -0
  123. package/accounts/evm/toNetworkScopedEvmSmartAccount.ts +41 -0
  124. package/accounts/evm/types.ts +46 -17
  125. package/actions/evm/signAndWrapTypedDataForSmartAccount.ts +246 -0
  126. package/actions/evm/swap/createSwapQuote.ts +42 -17
  127. package/actions/evm/swap/getSwapPrice.ts +15 -11
  128. package/actions/evm/swap/sendSwapOperation.ts +182 -0
  129. package/actions/evm/swap/sendSwapTransaction.ts +18 -2
  130. package/actions/evm/swap/types.ts +154 -5
  131. package/actions/evm/types.ts +123 -21
  132. package/client/evm/evm.ts +19 -8
  133. package/client/evm/evm.types.ts +34 -7
  134. package/client/policies/policies.ts +12 -3
  135. package/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.ts +221 -2
  136. package/openapi-client/generated/evm-accounts/evm-accounts.msw.ts +48 -0
  137. package/openapi-client/generated/evm-smart-accounts/evm-smart-accounts.msw.ts +35 -0
  138. package/openapi-client/generated/faucets/faucets.ts +4 -4
  139. package/openapi-client/generated/index.msw.ts +5 -5
  140. package/openapi-client/generated/policy-engine/policy-engine.msw.ts +434 -0
  141. package/openapi-client/generated/solana-accounts/solana-accounts.msw.ts +40 -0
  142. package/package.json +1 -1
  143. package/policies/schema.ts +94 -6
  144. package/utils/uuidV4.ts +23 -0
  145. 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
- network: options.network,
50
- toToken: options.toToken,
51
- fromToken: options.fromToken,
52
- fromAmount: options.fromAmount.toString(),
53
- taker: taker,
54
- signerAddress: options.signerAddress,
55
- gasPrice: options.gasPrice?.toString(),
56
- slippageBps: options.slippageBps,
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
- const { transactionHash } = await sendSwapTransaction(client, {
138
- address: options.signerAddress || taker, // Use signerAddress for smart accounts, taker for EOAs
139
- network: result.network as SendEvmTransactionBodyNetwork,
140
- swapQuote: result,
141
- idempotencyKey: executeOptions.idempotencyKey,
142
- });
143
- return { transactionHash };
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
- network: options.network,
35
- toToken: options.toToken,
36
- fromToken: options.fromToken,
37
- fromAmount: options.fromAmount.toString(),
38
- taker: options.taker,
39
- signerAddress: options.signerAddress,
40
- gasPrice: options.gasPrice?.toString(),
41
- slippageBps: options.slippageBps,
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
- // Sign the Permit2 EIP-712 message
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
- idempotencyKey,
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 { SendEvmTransactionBodyNetwork } from "../../../openapi-client/index.js";
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 QuoteSwapOptions = Omit<CreateSwapQuoteOptions, "taker">;
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 QuoteSwapResult = CreateSwapQuoteResult | SwapUnavailableResult;
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 SwapOptions = AccountQuoteBasedSwapOptions | AccountInlineSwapOptions;
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 SwapResult = SendSwapTransactionResult;
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
+ };