@coinbase/cdp-sdk 1.11.0 → 1.13.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 (103) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +126 -0
  3. package/_cjs/accounts/evm/toEvmServerAccount.js +15 -0
  4. package/_cjs/accounts/evm/toEvmServerAccount.js.map +1 -1
  5. package/_cjs/actions/evm/swap/createSwapQuote.js +126 -0
  6. package/_cjs/actions/evm/swap/createSwapQuote.js.map +1 -0
  7. package/_cjs/actions/evm/swap/getSwapPrice.js +86 -0
  8. package/_cjs/actions/evm/swap/getSwapPrice.js.map +1 -0
  9. package/_cjs/actions/evm/swap/sendSwapTransaction.js +141 -0
  10. package/_cjs/actions/evm/swap/sendSwapTransaction.js.map +1 -0
  11. package/_cjs/actions/evm/swap/types.js +3 -0
  12. package/_cjs/actions/evm/swap/types.js.map +1 -0
  13. package/_cjs/client/evm/evm.js +46 -1
  14. package/_cjs/client/evm/evm.js.map +1 -1
  15. package/_cjs/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js +28 -1
  16. package/_cjs/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js.map +1 -1
  17. package/_cjs/openapi-client/generated/evm-swaps/evm-swaps.js +26 -0
  18. package/_cjs/openapi-client/generated/evm-swaps/evm-swaps.js.map +1 -0
  19. package/_cjs/openapi-client/generated/evm-swaps/evm-swaps.msw.js +179 -0
  20. package/_cjs/openapi-client/generated/evm-swaps/evm-swaps.msw.js.map +1 -0
  21. package/_cjs/openapi-client/generated/index.msw.js +5 -4
  22. package/_cjs/openapi-client/generated/index.msw.js.map +1 -1
  23. package/_cjs/openapi-client/generated/policy-engine/policy-engine.msw.js +59 -1
  24. package/_cjs/openapi-client/generated/policy-engine/policy-engine.msw.js.map +1 -1
  25. package/_cjs/openapi-client/index.js +4 -0
  26. package/_cjs/openapi-client/index.js.map +1 -1
  27. package/_cjs/policies/schema.js +59 -1
  28. package/_cjs/policies/schema.js.map +1 -1
  29. package/_cjs/version.js +1 -1
  30. package/_esm/accounts/evm/toEvmServerAccount.js +15 -0
  31. package/_esm/accounts/evm/toEvmServerAccount.js.map +1 -1
  32. package/_esm/actions/evm/swap/createSwapQuote.js +123 -0
  33. package/_esm/actions/evm/swap/createSwapQuote.js.map +1 -0
  34. package/_esm/actions/evm/swap/getSwapPrice.js +83 -0
  35. package/_esm/actions/evm/swap/getSwapPrice.js.map +1 -0
  36. package/_esm/actions/evm/swap/sendSwapTransaction.js +138 -0
  37. package/_esm/actions/evm/swap/sendSwapTransaction.js.map +1 -0
  38. package/_esm/actions/evm/swap/types.js +2 -0
  39. package/_esm/actions/evm/swap/types.js.map +1 -0
  40. package/_esm/client/evm/evm.js +46 -1
  41. package/_esm/client/evm/evm.js.map +1 -1
  42. package/_esm/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js +27 -0
  43. package/_esm/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.js.map +1 -1
  44. package/_esm/openapi-client/generated/evm-swaps/evm-swaps.js +21 -0
  45. package/_esm/openapi-client/generated/evm-swaps/evm-swaps.js.map +1 -0
  46. package/_esm/openapi-client/generated/evm-swaps/evm-swaps.msw.js +167 -0
  47. package/_esm/openapi-client/generated/evm-swaps/evm-swaps.msw.js.map +1 -0
  48. package/_esm/openapi-client/generated/index.msw.js +5 -4
  49. package/_esm/openapi-client/generated/index.msw.js.map +1 -1
  50. package/_esm/openapi-client/generated/policy-engine/policy-engine.msw.js +54 -0
  51. package/_esm/openapi-client/generated/policy-engine/policy-engine.msw.js.map +1 -1
  52. package/_esm/openapi-client/index.js +4 -0
  53. package/_esm/openapi-client/index.js.map +1 -1
  54. package/_esm/policies/schema.js +58 -0
  55. package/_esm/policies/schema.js.map +1 -1
  56. package/_esm/version.js +1 -1
  57. package/_types/accounts/evm/toEvmServerAccount.d.ts.map +1 -1
  58. package/_types/actions/evm/swap/createSwapQuote.d.ts +23 -0
  59. package/_types/actions/evm/swap/createSwapQuote.d.ts.map +1 -0
  60. package/_types/actions/evm/swap/getSwapPrice.d.ts +23 -0
  61. package/_types/actions/evm/swap/getSwapPrice.d.ts.map +1 -0
  62. package/_types/actions/evm/swap/sendSwapTransaction.d.ts +64 -0
  63. package/_types/actions/evm/swap/sendSwapTransaction.d.ts.map +1 -0
  64. package/_types/actions/evm/swap/types.d.ts +119 -0
  65. package/_types/actions/evm/swap/types.d.ts.map +1 -0
  66. package/_types/actions/evm/types.d.ts +86 -0
  67. package/_types/actions/evm/types.d.ts.map +1 -1
  68. package/_types/client/evm/evm.d.ts +39 -1
  69. package/_types/client/evm/evm.d.ts.map +1 -1
  70. package/_types/client/evm/evm.types.d.ts +207 -3
  71. package/_types/client/evm/evm.types.d.ts.map +1 -1
  72. package/_types/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.d.ts +364 -4
  73. package/_types/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.d.ts.map +1 -1
  74. package/_types/openapi-client/generated/evm-swaps/evm-swaps.d.ts +24 -0
  75. package/_types/openapi-client/generated/evm-swaps/evm-swaps.d.ts.map +1 -0
  76. package/_types/openapi-client/generated/evm-swaps/evm-swaps.msw.d.ts +12 -0
  77. package/_types/openapi-client/generated/evm-swaps/evm-swaps.msw.d.ts.map +1 -0
  78. package/_types/openapi-client/generated/index.msw.d.ts +5 -4
  79. package/_types/openapi-client/generated/index.msw.d.ts.map +1 -1
  80. package/_types/openapi-client/generated/policy-engine/policy-engine.msw.d.ts +5 -1
  81. package/_types/openapi-client/generated/policy-engine/policy-engine.msw.d.ts.map +1 -1
  82. package/_types/openapi-client/index.d.ts +5 -0
  83. package/_types/openapi-client/index.d.ts.map +1 -1
  84. package/_types/policies/schema.d.ts +338 -0
  85. package/_types/policies/schema.d.ts.map +1 -1
  86. package/_types/version.d.ts +1 -1
  87. package/accounts/evm/toEvmServerAccount.ts +21 -0
  88. package/actions/evm/swap/createSwapQuote.ts +148 -0
  89. package/actions/evm/swap/getSwapPrice.ts +95 -0
  90. package/actions/evm/swap/sendSwapTransaction.ts +173 -0
  91. package/actions/evm/swap/types.ts +137 -0
  92. package/actions/evm/types.ts +88 -0
  93. package/client/evm/evm.ts +58 -1
  94. package/client/evm/evm.types.ts +225 -1
  95. package/openapi-client/generated/coinbaseDeveloperPlatformAPIs.schemas.ts +422 -4
  96. package/openapi-client/generated/evm-swaps/evm-swaps.msw.ts +217 -0
  97. package/openapi-client/generated/evm-swaps/evm-swaps.ts +51 -0
  98. package/openapi-client/generated/index.msw.ts +5 -4
  99. package/openapi-client/generated/policy-engine/policy-engine.msw.ts +87 -0
  100. package/openapi-client/index.ts +4 -0
  101. package/package.json +1 -1
  102. package/policies/schema.ts +70 -0
  103. package/version.ts +1 -1
@@ -0,0 +1,148 @@
1
+ import { sendSwapTransaction } from "./sendSwapTransaction.js";
2
+ import {
3
+ CreateSwapQuoteOptions,
4
+ CreateSwapQuoteResult,
5
+ ExecuteSwapQuoteOptions,
6
+ ExecuteSwapQuoteResult,
7
+ SwapUnavailableResult,
8
+ } from "../../../client/evm/evm.types.js";
9
+ import {
10
+ CdpOpenApiClientType,
11
+ CreateSwapQuoteResponse,
12
+ type SendEvmTransactionBodyNetwork,
13
+ } from "../../../openapi-client/index.js";
14
+ import { Address, Hex } from "../../../types/misc.js";
15
+
16
+ /**
17
+ * Creates a quote for a swap between two tokens on an EVM network.
18
+ *
19
+ * @param {CdpOpenApiClientType} client - The client to use to create the swap quote.
20
+ * @param {CreateSwapQuoteOptions} options - The options for creating a swap quote.
21
+ *
22
+ * @returns {Promise<CreateSwapQuoteResult | SwapUnavailableResult>} A promise that resolves to the swap quote result or a response indicating that liquidity is unavailable.
23
+ *
24
+ * @example **Creating a swap quote**
25
+ * ```ts
26
+ * const swapQuote = await createSwapQuote(client, {
27
+ * network: "ethereum",
28
+ * toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
29
+ * fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
30
+ * fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
31
+ * taker: "0x1234567890123456789012345678901234567890"
32
+ * });
33
+ * ```
34
+ */
35
+ export async function createSwapQuote(
36
+ client: CdpOpenApiClientType,
37
+ options: CreateSwapQuoteOptions,
38
+ ): Promise<CreateSwapQuoteResult | SwapUnavailableResult> {
39
+ // Validate required parameters
40
+ if (!options.taker) {
41
+ throw new Error("taker is required for createSwapQuote");
42
+ }
43
+
44
+ // Store validated taker for type safety
45
+ const taker = options.taker as Address;
46
+
47
+ // 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
+ });
58
+
59
+ // Check if liquidity is unavailable
60
+ if (!response.liquidityAvailable) {
61
+ // Return the SwapUnavailableResult
62
+ return {
63
+ liquidityAvailable: false,
64
+ };
65
+ }
66
+
67
+ // At this point we know it's a CreateSwapQuoteResponse with liquidityAvailable as true
68
+ const swapResponse = response as CreateSwapQuoteResponse;
69
+ const result: CreateSwapQuoteResult = {
70
+ liquidityAvailable: true,
71
+ network: options.network,
72
+ toToken: swapResponse.toToken as Address,
73
+ fromToken: swapResponse.fromToken as Address,
74
+ fromAmount: BigInt(swapResponse.fromAmount),
75
+ toAmount: BigInt(swapResponse.toAmount),
76
+ minToAmount: BigInt(swapResponse.minToAmount),
77
+ blockNumber: BigInt(swapResponse.blockNumber),
78
+ fees: {
79
+ gasFee: swapResponse.fees.gasFee
80
+ ? {
81
+ amount: BigInt(swapResponse.fees.gasFee.amount),
82
+ token: swapResponse.fees.gasFee.token as Address,
83
+ }
84
+ : undefined,
85
+ protocolFee: swapResponse.fees.protocolFee
86
+ ? {
87
+ amount: BigInt(swapResponse.fees.protocolFee.amount),
88
+ token: swapResponse.fees.protocolFee.token as Address,
89
+ }
90
+ : undefined,
91
+ },
92
+ issues: {
93
+ allowance: swapResponse.issues.allowance
94
+ ? {
95
+ currentAllowance: BigInt(swapResponse.issues.allowance.currentAllowance),
96
+ spender: swapResponse.issues.allowance.spender as Address,
97
+ }
98
+ : undefined,
99
+ balance: swapResponse.issues.balance
100
+ ? {
101
+ token: swapResponse.issues.balance.token as Address,
102
+ currentBalance: BigInt(swapResponse.issues.balance.currentBalance),
103
+ requiredBalance: BigInt(swapResponse.issues.balance.requiredBalance),
104
+ }
105
+ : undefined,
106
+ simulationIncomplete: swapResponse.issues.simulationIncomplete,
107
+ },
108
+ transaction: swapResponse.transaction
109
+ ? {
110
+ to: swapResponse.transaction.to as Address,
111
+ data: swapResponse.transaction.data as Hex,
112
+ value: BigInt(swapResponse.transaction.value),
113
+ gas: BigInt(swapResponse.transaction.gas),
114
+ gasPrice: BigInt(swapResponse.transaction.gasPrice),
115
+ }
116
+ : undefined,
117
+ permit2: swapResponse.permit2
118
+ ? {
119
+ eip712: {
120
+ domain: {
121
+ ...swapResponse.permit2.eip712.domain,
122
+ verifyingContract: swapResponse.permit2.eip712.domain.verifyingContract as
123
+ | Address
124
+ | undefined,
125
+ salt: swapResponse.permit2.eip712.domain.salt as Hex | undefined,
126
+ },
127
+ types: swapResponse.permit2.eip712.types,
128
+ primaryType: swapResponse.permit2.eip712.primaryType,
129
+ message: swapResponse.permit2.eip712.message,
130
+ },
131
+ }
132
+ : undefined,
133
+ // Add the execute method
134
+ execute: async (
135
+ executeOptions: ExecuteSwapQuoteOptions = {},
136
+ ): 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
+ },
145
+ };
146
+
147
+ return result;
148
+ }
@@ -0,0 +1,95 @@
1
+ import {
2
+ GetSwapPriceOptions,
3
+ GetSwapPriceResult,
4
+ SwapUnavailableResult,
5
+ } from "../../../client/evm/evm.types.js";
6
+ import { CdpOpenApiClientType, GetSwapPriceResponse } from "../../../openapi-client/index.js";
7
+ import { Address } from "../../../types/misc.js";
8
+
9
+ /**
10
+ * Gets the price for a swap between two tokens on an EVM network.
11
+ *
12
+ * @param {CdpOpenApiClientType} client - The client to use to get the swap price.
13
+ * @param {GetSwapPriceOptions} options - The options for getting a swap price.
14
+ *
15
+ * @returns {Promise<GetSwapPriceResult | SwapUnavailableResult>} A promise that resolves to the swap price result or a response indicating that liquidity is unavailable.
16
+ *
17
+ * @example **Getting a swap price**
18
+ * ```ts
19
+ * const price = await getSwapPrice(client, {
20
+ * network: "ethereum-mainnet",
21
+ * toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
22
+ * fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
23
+ * fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
24
+ * taker: "0x1234567890123456789012345678901234567890"
25
+ * });
26
+ * ```
27
+ */
28
+ export async function getSwapPrice(
29
+ client: CdpOpenApiClientType,
30
+ options: GetSwapPriceOptions,
31
+ ): Promise<GetSwapPriceResult | SwapUnavailableResult> {
32
+ // 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
+ });
43
+
44
+ // Check if liquidity is unavailable
45
+ if (!response.liquidityAvailable) {
46
+ // Return the SwapUnavailableResult
47
+ return {
48
+ liquidityAvailable: false,
49
+ };
50
+ }
51
+
52
+ // At this point we know it's a GetSwapPriceResponse with liquidityAvailable as true
53
+ const quoteResponse = response as GetSwapPriceResponse;
54
+ return {
55
+ blockNumber: BigInt(quoteResponse.blockNumber),
56
+ toAmount: BigInt(quoteResponse.toAmount),
57
+ toToken: quoteResponse.toToken as Address,
58
+ fees: {
59
+ gasFee: quoteResponse.fees.gasFee
60
+ ? {
61
+ amount: BigInt(quoteResponse.fees.gasFee.amount),
62
+ token: quoteResponse.fees.gasFee.token as Address,
63
+ }
64
+ : undefined,
65
+ protocolFee: quoteResponse.fees.protocolFee
66
+ ? {
67
+ amount: BigInt(quoteResponse.fees.protocolFee.amount),
68
+ token: quoteResponse.fees.protocolFee.token as Address,
69
+ }
70
+ : undefined,
71
+ },
72
+ issues: {
73
+ allowance: quoteResponse.issues.allowance
74
+ ? {
75
+ currentAllowance: BigInt(quoteResponse.issues.allowance.currentAllowance),
76
+ spender: quoteResponse.issues.allowance.spender as Address,
77
+ }
78
+ : undefined,
79
+ balance: quoteResponse.issues.balance
80
+ ? {
81
+ token: quoteResponse.issues.balance.token as Address,
82
+ currentBalance: BigInt(quoteResponse.issues.balance.currentBalance),
83
+ requiredBalance: BigInt(quoteResponse.issues.balance.requiredBalance),
84
+ }
85
+ : undefined,
86
+ simulationIncomplete: quoteResponse.issues.simulationIncomplete,
87
+ },
88
+ liquidityAvailable: true,
89
+ minToAmount: BigInt(quoteResponse.minToAmount),
90
+ fromAmount: BigInt(quoteResponse.fromAmount),
91
+ fromToken: quoteResponse.fromToken as Address,
92
+ gas: quoteResponse.gas ? BigInt(quoteResponse.gas) : undefined,
93
+ gasPrice: quoteResponse.gasPrice ? BigInt(quoteResponse.gasPrice) : undefined,
94
+ };
95
+ }
@@ -0,0 +1,173 @@
1
+ import { concat, numberToHex, size } from "viem";
2
+
3
+ import { createSwapQuote } from "./createSwapQuote.js";
4
+ import { sendTransaction } from "../sendTransaction.js";
5
+
6
+ import type { SendSwapTransactionOptions, SendSwapTransactionResult } from "./types.js";
7
+ import type {
8
+ CreateSwapQuoteResult,
9
+ CreateSwapQuoteOptions,
10
+ SwapUnavailableResult,
11
+ } from "../../../client/evm/evm.types.js";
12
+ import type {
13
+ CdpOpenApiClientType,
14
+ SendEvmTransactionBodyNetwork,
15
+ } from "../../../openapi-client/index.js";
16
+ import type { Hex } from "../../../types/misc.js";
17
+ import type { TransactionRequestEIP1559 } from "viem";
18
+
19
+ /**
20
+ * Sends a swap transaction to the blockchain.
21
+ * Handles any permit2 signatures required for the swap.
22
+ *
23
+ * If you encounter token allowance issues, you'll need to perform a token approval transaction first to allow
24
+ * the Permit2 contract to spend the appropriate amount of your fromToken.
25
+ * See `examples/typescript/evm/account.sendSwapTransaction.ts` for example code on handling token approvals.
26
+ *
27
+ * @param {CdpOpenApiClientType} client - The client to use for sending the swap.
28
+ * @param {SendSwapTransactionOptions} options - The options for the swap submission.
29
+ *
30
+ * @returns {Promise<SendSwapTransactionResult>} A promise that resolves to the transaction hash.
31
+ *
32
+ * @throws {Error} If liquidity is not available for the swap.
33
+ * @throws {Error} If there are insufficient token allowances. In this case, you need to approve the
34
+ * Permit2 contract to spend your tokens before attempting the swap. The error message
35
+ * will include the current allowance and the spender address that needs approval.
36
+ * @throws {Error} If no transaction data is found in the swap result.
37
+ *
38
+ * @example **Sending a swap with pre-created swap quote object**
39
+ * ```ts
40
+ * // First create a swap quote
41
+ * const swapQuote = await cdp.evm.createSwapQuote({
42
+ * network: "base",
43
+ * toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
44
+ * fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
45
+ * fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
46
+ * taker: account.address
47
+ * });
48
+ *
49
+ * // Check if liquidity is available
50
+ * if (!swapQuote.liquidityAvailable) {
51
+ * console.error("Insufficient liquidity for swap");
52
+ * return;
53
+ * }
54
+ *
55
+ * // Send the swap
56
+ * const result = await sendSwapTransaction(client, {
57
+ * address: account.address,
58
+ * swapQuote: swapQuote
59
+ * });
60
+ *
61
+ * console.log(`Swap sent with transaction hash: ${result.transactionHash}`);
62
+ * ```
63
+ *
64
+ * @example **Sending a swap with inline options (all-in-one)**
65
+ * ```ts
66
+ * // Send swap in one call
67
+ * const result = await sendSwapTransaction(client, {
68
+ * address: account.address,
69
+ * network: "base",
70
+ * toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
71
+ * fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
72
+ * fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
73
+ * taker: account.address
74
+ * });
75
+ *
76
+ * console.log(`Swap sent with transaction hash: ${result.transactionHash}`);
77
+ * ```
78
+ */
79
+ export async function sendSwapTransaction(
80
+ client: CdpOpenApiClientType,
81
+ options: SendSwapTransactionOptions,
82
+ ): Promise<SendSwapTransactionResult> {
83
+ const { address, idempotencyKey } = options;
84
+
85
+ let swapResult: CreateSwapQuoteResult | SwapUnavailableResult;
86
+
87
+ // Determine if we need to create the swap quote or use the provided one
88
+ if ("swapQuote" in options) {
89
+ // Use the provided swap quote
90
+ swapResult = options.swapQuote;
91
+ } else {
92
+ // Create the swap quote using the provided options (InlineSendSwapTransactionOptions)
93
+ swapResult = await createSwapQuote(client, {
94
+ network: options.network as CreateSwapQuoteOptions["network"],
95
+ toToken: options.toToken,
96
+ fromToken: options.fromToken,
97
+ fromAmount: options.fromAmount,
98
+ taker: options.taker,
99
+ signerAddress: options.signerAddress,
100
+ gasPrice: options.gasPrice,
101
+ slippageBps: options.slippageBps,
102
+ });
103
+ }
104
+
105
+ // Check if liquidity is available
106
+ if (!swapResult.liquidityAvailable) {
107
+ throw new Error("Insufficient liquidity for swap");
108
+ }
109
+
110
+ // At this point, we know that swapResult is CreateSwapQuoteResult
111
+ const swap = swapResult as CreateSwapQuoteResult;
112
+
113
+ // Check for allowance issues
114
+ if (swap.issues?.allowance) {
115
+ const { currentAllowance, spender } = swap.issues.allowance;
116
+ throw new Error(
117
+ `Insufficient token allowance for swap. Current allowance: ${currentAllowance}. ` +
118
+ `Please approve the Permit2 contract (${spender}) to spend your tokens.`,
119
+ );
120
+ }
121
+
122
+ // If the transaction doesn't exist, throw an error
123
+ if (!swap.transaction) {
124
+ throw new Error("No transaction data found in the swap");
125
+ }
126
+
127
+ // Get the transaction data and modify it if needed for Permit2
128
+ let txData = swap.transaction.data as Hex;
129
+
130
+ if (swap.permit2?.eip712) {
131
+ // Sign the Permit2 EIP-712 message
132
+ const signature = await client.signEvmTypedData(
133
+ address,
134
+ {
135
+ domain: swap.permit2.eip712.domain,
136
+ types: swap.permit2.eip712.types,
137
+ primaryType: swap.permit2.eip712.primaryType,
138
+ message: swap.permit2.eip712.message,
139
+ },
140
+ idempotencyKey,
141
+ );
142
+
143
+ // Calculate the signature length as a 32-byte hex value
144
+ const signatureLengthInHex = numberToHex(size(signature.signature as Hex), {
145
+ signed: false,
146
+ size: 32,
147
+ });
148
+
149
+ // Append the signature length and signature to the transaction data
150
+ txData = concat([txData, signatureLengthInHex, signature.signature as Hex]);
151
+ }
152
+
153
+ // Create a transaction object
154
+ const transaction: TransactionRequestEIP1559 = {
155
+ to: swap.transaction.to as `0x${string}`,
156
+ data: txData,
157
+ // Only include these properties if they exist
158
+ ...(swap.transaction.value ? { value: BigInt(swap.transaction.value) } : {}),
159
+ ...(swap.transaction.gas ? { gas: BigInt(swap.transaction.gas) } : {}),
160
+ };
161
+
162
+ // Use sendTransaction instead of directly calling client.sendEvmTransaction
163
+ const result = await sendTransaction(client, {
164
+ address,
165
+ network: swap.network as SendEvmTransactionBodyNetwork,
166
+ transaction,
167
+ idempotencyKey,
168
+ });
169
+
170
+ return {
171
+ transactionHash: result.transactionHash,
172
+ };
173
+ }
@@ -0,0 +1,137 @@
1
+ import type {
2
+ CreateSwapQuoteOptions,
3
+ CreateSwapQuoteResult,
4
+ SwapUnavailableResult,
5
+ } from "../../../client/evm/evm.types.js";
6
+ import type { SendEvmTransactionBodyNetwork } from "../../../openapi-client/index.js";
7
+ import type { Address, Hex } from "../../../types/misc.js";
8
+
9
+ /**
10
+ * Base options for sending a swap transaction.
11
+ */
12
+ interface BaseSendSwapTransactionOptions {
13
+ /**
14
+ * The address of the account that will execute the swap.
15
+ */
16
+ address: Address;
17
+
18
+ /**
19
+ * Optional idempotency key for the request.
20
+ */
21
+ idempotencyKey?: string;
22
+ }
23
+
24
+ /**
25
+ * Options when providing an already created swap quote.
26
+ */
27
+ interface QuoteBasedSendSwapTransactionOptions extends BaseSendSwapTransactionOptions {
28
+ /**
29
+ * The swap quote returned by the createSwapQuote method.
30
+ */
31
+ swapQuote: CreateSwapQuoteResult;
32
+ }
33
+
34
+ /**
35
+ * Options when creating a swap quote inline.
36
+ */
37
+ interface InlineSendSwapTransactionOptions extends BaseSendSwapTransactionOptions {
38
+ /**
39
+ * The network to execute the swap on (e.g., "ethereum", "base").
40
+ */
41
+ network: SendEvmTransactionBodyNetwork;
42
+ /** The token to receive (destination token). */
43
+ toToken: Address;
44
+ /** The token to send (source token). */
45
+ fromToken: Address;
46
+ /** The amount to send in atomic units of the token. */
47
+ fromAmount: bigint;
48
+ /** The address that will perform the swap. */
49
+ taker: Address;
50
+ /** The signer address (only needed if taker is a smart contract). */
51
+ signerAddress?: Address;
52
+ /** The gas price in Wei. */
53
+ gasPrice?: bigint;
54
+ /** The slippage tolerance in basis points (0-10000). */
55
+ slippageBps?: number;
56
+ }
57
+
58
+ /**
59
+ * Options for sending a swap transaction.
60
+ * Either provide a pre-created swap quote result or inline swap parameters.
61
+ */
62
+ export type SendSwapTransactionOptions =
63
+ | QuoteBasedSendSwapTransactionOptions
64
+ | InlineSendSwapTransactionOptions;
65
+
66
+ /**
67
+ * Result of sending a swap transaction.
68
+ */
69
+ export interface SendSwapTransactionResult {
70
+ /**
71
+ * The transaction hash of the submitted swap.
72
+ */
73
+ transactionHash: Hex;
74
+ }
75
+
76
+ /**
77
+ * Options for creating a swap quote (account-level).
78
+ */
79
+ export type QuoteSwapOptions = Omit<CreateSwapQuoteOptions, "taker">;
80
+
81
+ /**
82
+ * Result of creating a swap quote (account-level).
83
+ */
84
+ export type QuoteSwapResult = CreateSwapQuoteResult | SwapUnavailableResult;
85
+
86
+ /**
87
+ * Options when providing an already created swap quote (account-level).
88
+ */
89
+ interface AccountQuoteBasedSwapOptions {
90
+ /**
91
+ * The swap quote returned by the createSwapQuote method.
92
+ */
93
+ swapQuote: CreateSwapQuoteResult;
94
+
95
+ /**
96
+ * Optional idempotency key for the request.
97
+ */
98
+ idempotencyKey?: string;
99
+ }
100
+
101
+ /**
102
+ * Options when creating a swap quote inline (account-level).
103
+ */
104
+ interface AccountInlineSwapOptions {
105
+ /**
106
+ * The network to execute the swap on (e.g., "ethereum", "base").
107
+ */
108
+ network: SendEvmTransactionBodyNetwork;
109
+ /** The token to receive (destination token). */
110
+ toToken: Address;
111
+ /** The token to send (source token). */
112
+ fromToken: Address;
113
+ /** The amount to send in atomic units of the token. */
114
+ fromAmount: bigint;
115
+ /** The signer address (only needed if taker is a smart contract). */
116
+ signerAddress?: Address;
117
+ /** The gas price in Wei. */
118
+ gasPrice?: bigint;
119
+ /** The slippage tolerance in basis points (0-10000). */
120
+ slippageBps?: number;
121
+
122
+ /**
123
+ * Optional idempotency key for the request.
124
+ */
125
+ idempotencyKey?: string;
126
+ }
127
+
128
+ /**
129
+ * Options for executing a token swap (account-level).
130
+ * The taker is automatically set to the account's address.
131
+ */
132
+ export type SwapOptions = AccountQuoteBasedSwapOptions | AccountInlineSwapOptions;
133
+
134
+ /**
135
+ * Result of executing a token swap (account-level).
136
+ */
137
+ export type SwapResult = SendSwapTransactionResult;
@@ -13,6 +13,7 @@ import { Hex } from "../../types/misc.js";
13
13
  import type { ListTokenBalancesOptions, ListTokenBalancesResult } from "./listTokenBalances.js";
14
14
  import type { RequestFaucetOptions, RequestFaucetResult } from "./requestFaucet.js";
15
15
  import type { SendTransactionOptions, TransactionResult } from "./sendTransaction.js";
16
+ import type { SwapOptions, SwapResult, QuoteSwapOptions, QuoteSwapResult } from "./swap/types.js";
16
17
  import type { TransferOptions } from "./transfer/types.js";
17
18
  import type {
18
19
  WaitForUserOperationOptions,
@@ -249,6 +250,93 @@ export type AccountActions = Actions & {
249
250
  * ```
250
251
  */
251
252
  sendTransaction: (options: Omit<SendTransactionOptions, "address">) => Promise<TransactionResult>;
253
+
254
+ /**
255
+ * Creates a swap quote without executing the transaction.
256
+ * This is useful when you need to get swap details before executing the swap.
257
+ * The taker is automatically set to the account's address.
258
+ *
259
+ * @param {QuoteSwapOptions} options - Configuration options for creating the swap quote.
260
+ * @param {string} options.network - The network to create the quote on
261
+ * @param {string} options.fromToken - The token address to sell
262
+ * @param {string} options.toToken - The token address to buy
263
+ * @param {bigint} options.fromAmount - The amount of fromToken to sell
264
+ * @param {string} [options.signerAddress] - The signer address (only needed if taker is a smart contract)
265
+ * @param {bigint} [options.gasPrice] - The gas price in Wei
266
+ * @param {number} [options.slippageBps] - The slippage tolerance in basis points (0-10000)
267
+ *
268
+ * @returns A promise that resolves to the swap quote or a response indicating that liquidity is unavailable.
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * const swapQuote = await account.quoteSwap({
273
+ * network: "base",
274
+ * fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
275
+ * toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
276
+ * fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
277
+ * });
278
+ *
279
+ * if (swapQuote.liquidityAvailable) {
280
+ * console.log(`Can swap for ${swapQuote.toAmount} USDC`);
281
+ * }
282
+ * ```
283
+ */
284
+ quoteSwap: (options: QuoteSwapOptions) => Promise<QuoteSwapResult>;
285
+
286
+ /**
287
+ * Executes a token swap on the specified network.
288
+ * This method handles all the steps required for a swap, including Permit2 signatures if needed.
289
+ *
290
+ * @param {SwapOptions} options - Configuration options for the swap.
291
+ * @param {string} [options.network] - The network to execute the swap on
292
+ * @param {CreateSwapQuoteResult} [options.swapQuote] - The swap quote returned by the createSwapQuote method.
293
+ * @param {CreateSwapQuoteOptions} [options.swapOptions] - Options to create a swap quote. The function will call createSwapQuote internally.
294
+ * @param {string} [options.idempotencyKey] - Optional idempotency key for the request.
295
+ *
296
+ * @returns A promise that resolves to the transaction hash.
297
+ *
298
+ * @throws {Error} If liquidity is not available when using swapOptions.
299
+ *
300
+ * @example **Using a pre-created swap quote**
301
+ * ```ts
302
+ * // First create a swap quote
303
+ * const swapQuote = await cdp.evm.createSwapQuote({
304
+ * network: "base",
305
+ * buyToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
306
+ * sellToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
307
+ * sellAmount: BigInt("1000000000000000000"), // 1 WETH in wei
308
+ * taker: account.address
309
+ * });
310
+ *
311
+ * // Check if liquidity is available
312
+ * if (!swapQuote.liquidityAvailable) {
313
+ * console.error("Insufficient liquidity for swap");
314
+ * return;
315
+ * }
316
+ *
317
+ * // Execute the swap
318
+ * const { transactionHash } = await account.swap({
319
+ * swapQuote: swapQuote
320
+ * });
321
+ *
322
+ * console.log(`Swap executed with transaction hash: ${transactionHash}`);
323
+ * ```
324
+ *
325
+ * @example **Using swap options (all-in-one)**
326
+ * ```ts
327
+ * // Create and execute swap in one call
328
+ * const { transactionHash } = await account.swap({
329
+ * network: "base",
330
+ * buyToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
331
+ * sellToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
332
+ * sellAmount: BigInt("1000000000000000000"), // 1 WETH in wei
333
+ * taker: account.address
334
+ * });
335
+ *
336
+ * console.log(`Swap executed with transaction hash: ${transactionHash}`);
337
+ * ```
338
+ */
339
+ swap: (options: SwapOptions) => Promise<SwapResult>;
252
340
  };
253
341
 
254
342
  export type SmartAccountActions = Actions & {