@coinbase/cdp-sdk 1.15.0 → 1.16.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 +14 -0
- package/README.md +82 -20
- package/_cjs/accounts/evm/toEvmServerAccount.js +13 -7
- package/_cjs/accounts/evm/toEvmServerAccount.js.map +1 -1
- package/_cjs/accounts/evm/toEvmSmartAccount.js +18 -0
- package/_cjs/accounts/evm/toEvmSmartAccount.js.map +1 -1
- 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/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 +14 -8
- package/_esm/accounts/evm/toEvmServerAccount.js.map +1 -1
- package/_esm/accounts/evm/toEvmSmartAccount.js +18 -0
- package/_esm/accounts/evm/toEvmSmartAccount.js.map +1 -1
- 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/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/types.d.ts +4 -4
- 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 +32 -6
- package/_types/client/evm/evm.types.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 +33 -15
- package/accounts/evm/toEvmSmartAccount.ts +25 -0
- package/accounts/evm/types.ts +14 -6
- 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 +33 -6
- package/package.json +1 -1
- package/utils/uuidV4.ts +23 -0
- package/version.ts +1 -1
|
@@ -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
|
+
};
|
package/actions/evm/types.ts
CHANGED
|
@@ -13,7 +13,16 @@ 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 {
|
|
16
|
+
import type {
|
|
17
|
+
AccountSwapOptions,
|
|
18
|
+
AccountSwapResult,
|
|
19
|
+
AccountQuoteSwapOptions,
|
|
20
|
+
AccountQuoteSwapResult,
|
|
21
|
+
SmartAccountSwapOptions,
|
|
22
|
+
SmartAccountSwapResult,
|
|
23
|
+
SmartAccountQuoteSwapOptions,
|
|
24
|
+
SmartAccountQuoteSwapResult,
|
|
25
|
+
} from "./swap/types.js";
|
|
17
26
|
import type { TransferOptions } from "./transfer/types.js";
|
|
18
27
|
import type {
|
|
19
28
|
WaitForUserOperationOptions,
|
|
@@ -256,11 +265,11 @@ export type AccountActions = Actions & {
|
|
|
256
265
|
* This is useful when you need to get swap details before executing the swap.
|
|
257
266
|
* The taker is automatically set to the account's address.
|
|
258
267
|
*
|
|
259
|
-
* @param {
|
|
268
|
+
* @param {AccountQuoteSwapOptions} options - Configuration options for creating the swap quote.
|
|
260
269
|
* @param {string} options.network - The network to create the quote on
|
|
261
|
-
* @param {string} options.fromToken - The token address to
|
|
262
|
-
* @param {string} options.toToken - The token address to
|
|
263
|
-
* @param {bigint} options.fromAmount - The amount of fromToken to
|
|
270
|
+
* @param {string} options.fromToken - The token address to send
|
|
271
|
+
* @param {string} options.toToken - The token address to receive
|
|
272
|
+
* @param {bigint} options.fromAmount - The amount of fromToken to send
|
|
264
273
|
* @param {string} [options.signerAddress] - The signer address (only needed if taker is a smart contract)
|
|
265
274
|
* @param {bigint} [options.gasPrice] - The gas price in Wei
|
|
266
275
|
* @param {number} [options.slippageBps] - The slippage tolerance in basis points (0-10000)
|
|
@@ -281,30 +290,33 @@ export type AccountActions = Actions & {
|
|
|
281
290
|
* }
|
|
282
291
|
* ```
|
|
283
292
|
*/
|
|
284
|
-
quoteSwap: (options:
|
|
293
|
+
quoteSwap: (options: AccountQuoteSwapOptions) => Promise<AccountQuoteSwapResult>;
|
|
285
294
|
|
|
286
295
|
/**
|
|
287
296
|
* Executes a token swap on the specified network.
|
|
288
297
|
* This method handles all the steps required for a swap, including Permit2 signatures if needed.
|
|
298
|
+
* The taker is automatically set to the account's address.
|
|
289
299
|
*
|
|
290
|
-
* @param {
|
|
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 {
|
|
294
|
-
* @param {string} [options.
|
|
300
|
+
* @param {AccountSwapOptions} options - Configuration options for the swap.
|
|
301
|
+
* @param {string} [options.network] - The network to execute the swap on (required for inline swaps)
|
|
302
|
+
* @param {CreateSwapQuoteResult} [options.swapQuote] - The swap quote returned by the createSwapQuote method
|
|
303
|
+
* @param {string} [options.fromToken] - The token address to send (required for inline swaps)
|
|
304
|
+
* @param {string} [options.toToken] - The token address to receive (required for inline swaps)
|
|
305
|
+
* @param {bigint} [options.fromAmount] - The amount of fromToken to send (required for inline swaps)
|
|
306
|
+
* @param {string} [options.idempotencyKey] - Optional idempotency key for the request
|
|
295
307
|
*
|
|
296
308
|
* @returns A promise that resolves to the transaction hash.
|
|
297
309
|
*
|
|
298
|
-
* @throws {Error} If liquidity is not available when using
|
|
310
|
+
* @throws {Error} If liquidity is not available when using inline options.
|
|
299
311
|
*
|
|
300
312
|
* @example **Using a pre-created swap quote**
|
|
301
313
|
* ```ts
|
|
302
314
|
* // First create a swap quote
|
|
303
315
|
* const swapQuote = await cdp.evm.createSwapQuote({
|
|
304
316
|
* network: "base",
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
317
|
+
* toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
318
|
+
* fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
|
|
319
|
+
* fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
|
|
308
320
|
* taker: account.address
|
|
309
321
|
* });
|
|
310
322
|
*
|
|
@@ -322,21 +334,20 @@ export type AccountActions = Actions & {
|
|
|
322
334
|
* console.log(`Swap executed with transaction hash: ${transactionHash}`);
|
|
323
335
|
* ```
|
|
324
336
|
*
|
|
325
|
-
* @example **Using
|
|
337
|
+
* @example **Using inline options (all-in-one)**
|
|
326
338
|
* ```ts
|
|
327
339
|
* // Create and execute swap in one call
|
|
328
340
|
* const { transactionHash } = await account.swap({
|
|
329
341
|
* network: "base",
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
* taker: account.address
|
|
342
|
+
* toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
343
|
+
* fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
|
|
344
|
+
* fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
|
|
334
345
|
* });
|
|
335
346
|
*
|
|
336
347
|
* console.log(`Swap executed with transaction hash: ${transactionHash}`);
|
|
337
348
|
* ```
|
|
338
349
|
*/
|
|
339
|
-
swap: (options:
|
|
350
|
+
swap: (options: AccountSwapOptions) => Promise<AccountSwapResult>;
|
|
340
351
|
};
|
|
341
352
|
|
|
342
353
|
export type SmartAccountActions = Actions & {
|
|
@@ -499,4 +510,95 @@ export type SmartAccountActions = Actions & {
|
|
|
499
510
|
getUserOperation: (
|
|
500
511
|
options: Omit<GetUserOperationOptions, "smartAccount">,
|
|
501
512
|
) => Promise<UserOperation>;
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Creates a swap quote without executing the transaction.
|
|
516
|
+
* This is useful when you need to get swap details before executing the swap.
|
|
517
|
+
* The taker is automatically set to the smart account's address.
|
|
518
|
+
*
|
|
519
|
+
* @param {SmartAccountQuoteSwapOptions} options - Configuration options for creating the swap quote.
|
|
520
|
+
* @param {string} options.network - The network to create the quote on
|
|
521
|
+
* @param {string} options.fromToken - The token address to send
|
|
522
|
+
* @param {string} options.toToken - The token address to receive
|
|
523
|
+
* @param {bigint} options.fromAmount - The amount of fromToken to send
|
|
524
|
+
* @param {string} [options.signerAddress] - The signer address (only needed if taker is a smart contract)
|
|
525
|
+
* @param {bigint} [options.gasPrice] - The gas price in Wei
|
|
526
|
+
* @param {number} [options.slippageBps] - The slippage tolerance in basis points (0-10000)
|
|
527
|
+
*
|
|
528
|
+
* @returns A promise that resolves to the swap quote or a response indicating that liquidity is unavailable.
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* ```ts
|
|
532
|
+
* const swapQuote = await smartAccount.quoteSwap({
|
|
533
|
+
* network: "base",
|
|
534
|
+
* fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
|
|
535
|
+
* toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
536
|
+
* fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
|
|
537
|
+
* });
|
|
538
|
+
*
|
|
539
|
+
* if (swapQuote.liquidityAvailable) {
|
|
540
|
+
* console.log(`Can swap for ${swapQuote.toAmount} USDC`);
|
|
541
|
+
* }
|
|
542
|
+
* ```
|
|
543
|
+
*/
|
|
544
|
+
quoteSwap: (options: SmartAccountQuoteSwapOptions) => Promise<SmartAccountQuoteSwapResult>;
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Executes a token swap on the specified network via a user operation.
|
|
548
|
+
* This method handles all the steps required for a swap, including Permit2 signatures if needed.
|
|
549
|
+
* The taker is automatically set to the smart account's address.
|
|
550
|
+
*
|
|
551
|
+
* @param {SmartAccountSwapOptions} options - Configuration options for the swap.
|
|
552
|
+
* @param {string} [options.network] - The network to execute the swap on (required for inline swaps)
|
|
553
|
+
* @param {CreateSwapQuoteResult} [options.swapQuote] - The swap quote returned by the createSwapQuote method
|
|
554
|
+
* @param {string} [options.fromToken] - The token address to send (required for inline swaps)
|
|
555
|
+
* @param {string} [options.toToken] - The token address to receive (required for inline swaps)
|
|
556
|
+
* @param {bigint} [options.fromAmount] - The amount of fromToken to send (required for inline swaps)
|
|
557
|
+
* @param {string} [options.paymasterUrl] - Optional paymaster URL for gas sponsorship
|
|
558
|
+
* @param {string} [options.idempotencyKey] - Optional idempotency key for the request
|
|
559
|
+
*
|
|
560
|
+
* @returns A promise that resolves to the user operation result.
|
|
561
|
+
*
|
|
562
|
+
* @throws {Error} If liquidity is not available when using inline options.
|
|
563
|
+
*
|
|
564
|
+
* @example **Using a pre-created swap quote**
|
|
565
|
+
* ```ts
|
|
566
|
+
* // First create a swap quote
|
|
567
|
+
* const swapQuote = await cdp.evm.createSwapQuote({
|
|
568
|
+
* network: "base",
|
|
569
|
+
* toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
570
|
+
* fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
|
|
571
|
+
* fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
|
|
572
|
+
* taker: smartAccount.address,
|
|
573
|
+
* signerAddress: smartAccount.owners[0].address
|
|
574
|
+
* });
|
|
575
|
+
*
|
|
576
|
+
* // Check if liquidity is available
|
|
577
|
+
* if (!swapQuote.liquidityAvailable) {
|
|
578
|
+
* console.error("Insufficient liquidity for swap");
|
|
579
|
+
* return;
|
|
580
|
+
* }
|
|
581
|
+
*
|
|
582
|
+
* // Execute the swap
|
|
583
|
+
* const { userOpHash } = await smartAccount.swap({
|
|
584
|
+
* swapQuote: swapQuote
|
|
585
|
+
* });
|
|
586
|
+
*
|
|
587
|
+
* console.log(`Swap executed with user op hash: ${userOpHash}`);
|
|
588
|
+
* ```
|
|
589
|
+
*
|
|
590
|
+
* @example **Using inline options (all-in-one)**
|
|
591
|
+
* ```ts
|
|
592
|
+
* // Create and execute swap in one call
|
|
593
|
+
* const { userOpHash } = await smartAccount.swap({
|
|
594
|
+
* network: "base",
|
|
595
|
+
* toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
596
|
+
* fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
|
|
597
|
+
* fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
|
|
598
|
+
* });
|
|
599
|
+
*
|
|
600
|
+
* console.log(`Swap executed with user op hash: ${userOpHash}`);
|
|
601
|
+
* ```
|
|
602
|
+
*/
|
|
603
|
+
swap: (options: SmartAccountSwapOptions) => Promise<SmartAccountSwapResult>;
|
|
502
604
|
};
|
package/client/evm/evm.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { constants, publicEncrypt } from "crypto";
|
|
2
2
|
|
|
3
|
-
import { Address } from "viem";
|
|
3
|
+
import { type Address, getTypesForEIP712Domain } from "viem";
|
|
4
4
|
|
|
5
5
|
import { ImportEvmAccountPublicRSAKey } from "./constants.js";
|
|
6
6
|
import {
|
|
@@ -62,7 +62,10 @@ import {
|
|
|
62
62
|
} from "../../actions/evm/waitForUserOperation.js";
|
|
63
63
|
import { Analytics } from "../../analytics.js";
|
|
64
64
|
import { APIError } from "../../openapi-client/errors.js";
|
|
65
|
-
import {
|
|
65
|
+
import {
|
|
66
|
+
CdpOpenApiClient,
|
|
67
|
+
EIP712Message as OpenAPIEIP712Message,
|
|
68
|
+
} from "../../openapi-client/index.js";
|
|
66
69
|
import { Hex } from "../../types/misc.js";
|
|
67
70
|
import { decryptWithPrivateKey, generateExportEncryptionKeyPair } from "../../utils/export.js";
|
|
68
71
|
|
|
@@ -978,14 +981,22 @@ export class EvmClient implements EvmClientInterface {
|
|
|
978
981
|
* ```
|
|
979
982
|
*/
|
|
980
983
|
async signTypedData(options: SignTypedDataOptions): Promise<SignatureResult> {
|
|
984
|
+
const { domain, message, primaryType } = options;
|
|
985
|
+
const types = {
|
|
986
|
+
EIP712Domain: getTypesForEIP712Domain({ domain }),
|
|
987
|
+
...options.types,
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
const openApiMessage: OpenAPIEIP712Message = {
|
|
991
|
+
domain,
|
|
992
|
+
types,
|
|
993
|
+
primaryType,
|
|
994
|
+
message,
|
|
995
|
+
};
|
|
996
|
+
|
|
981
997
|
const signature = await CdpOpenApiClient.signEvmTypedData(
|
|
982
998
|
options.address,
|
|
983
|
-
|
|
984
|
-
domain: options.domain,
|
|
985
|
-
types: options.types,
|
|
986
|
-
primaryType: options.primaryType,
|
|
987
|
-
message: options.message,
|
|
988
|
-
},
|
|
999
|
+
openApiMessage,
|
|
989
1000
|
options.idempotencyKey,
|
|
990
1001
|
);
|
|
991
1002
|
|