@coinbase/cdp-sdk
Version:
SDK for interacting with the Coinbase Developer Platform Wallet API
153 lines • 6.78 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendSwapOperation = sendSwapOperation;
const viem_1 = require("viem");
const createSwapQuote_js_1 = require("./createSwapQuote.js");
const uuidV4_js_1 = require("../../../utils/uuidV4.js");
const sendUserOperation_js_1 = require("../sendUserOperation.js");
const signAndWrapTypedDataForSmartAccount_js_1 = require("../signAndWrapTypedDataForSmartAccount.js");
/**
* Sends a swap operation to the blockchain via a smart account user operation.
* Handles any permit2 signatures required for the swap.
*
* If you encounter token allowance issues, you'll need to perform a token approval transaction first to allow
* the Permit2 contract to spend the appropriate amount of your fromToken.
* See examples for code on handling token approvals.
*
* @param {CdpOpenApiClientType} client - The client to use for sending the swap operation.
* @param {SendSwapOperationOptions} options - The options for the swap submission.
*
* @returns {Promise<SendSwapOperationResult>} A promise that resolves to the user operation result.
*
* @throws {Error} If liquidity is not available for the swap.
* @throws {Error} If there are insufficient token allowances. In this case, you need to approve the
* Permit2 contract to spend your tokens before attempting the swap. The error message
* will include the current allowance and the spender address that needs approval.
* @throws {Error} If no transaction data is found in the swap result.
*
* @example **Sending a swap with pre-created swap quote object**
* ```ts
* // First create a swap quote
* const swapQuote = await cdp.evm.createSwapQuote({
* network: "base",
* toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
* fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
* fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
* taker: smartAccount.address
* });
*
* // Check if liquidity is available
* if (!swapQuote.liquidityAvailable) {
* console.error("Insufficient liquidity for swap");
* return;
* }
*
* // Send the swap operation
* const result = await sendSwapOperation(client, {
* smartAccount: smartAccount,
* swapQuote: swapQuote
* });
*
* console.log(`Swap operation sent with user op hash: ${result.userOpHash}`);
* ```
*
* @example **Sending a swap with inline options (all-in-one)**
* ```ts
* // Send swap operation in one call
* const result = await sendSwapOperation(client, {
* smartAccount: smartAccount,
* network: "base",
* toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
* fromToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
* fromAmount: BigInt("1000000000000000000"), // 1 WETH in wei
* taker: smartAccount.address
* });
*
* console.log(`Swap operation sent with user op hash: ${result.userOpHash}`);
* ```
*/
async function sendSwapOperation(client, options) {
const { smartAccount, paymasterUrl, idempotencyKey } = options;
let swapResult;
// Determine if we need to create the swap quote or use the provided one
if ("swapQuote" in options) {
// Use the provided swap quote
swapResult = options.swapQuote;
}
else {
// Create the swap quote using the provided options (inline options)
/**
* Deterministically derive a new idempotency key from the provided idempotency key for swap quote creation to avoid key reuse.
*/
const swapQuoteIdempotencyKey = idempotencyKey
? (0, uuidV4_js_1.createDeterministicUuidV4)(idempotencyKey, "createSwapQuote")
: undefined;
swapResult = await (0, createSwapQuote_js_1.createSwapQuote)(client, {
network: options.network,
toToken: options.toToken,
fromToken: options.fromToken,
fromAmount: options.fromAmount,
taker: options.taker,
signerAddress: options.signerAddress,
gasPrice: options.gasPrice,
slippageBps: options.slippageBps,
idempotencyKey: swapQuoteIdempotencyKey,
});
}
// Check if liquidity is available
if (!swapResult.liquidityAvailable) {
throw new Error("Insufficient liquidity for swap");
}
// At this point, we know that swapResult is CreateSwapQuoteResult
const swap = swapResult;
// Check for allowance issues
if (swap.issues?.allowance) {
const { currentAllowance, spender } = swap.issues.allowance;
throw new Error(`Insufficient token allowance for swap. Current allowance: ${currentAllowance}. ` +
`Please approve the Permit2 contract (${spender}) to spend your tokens.`);
}
// If the transaction doesn't exist, throw an error
if (!swap.transaction) {
throw new Error("No transaction data found in the swap");
}
// Get the transaction data and modify it if needed for Permit2
let txData = swap.transaction.data;
if (swap.permit2?.eip712) {
// Create the permit2 idempotency key
const permit2IdempotencyKey = idempotencyKey
? (0, uuidV4_js_1.createDeterministicUuidV4)(idempotencyKey, "permit2")
: undefined;
// Sign and wrap the permit2 typed data according to the Coinbase Smart Wallet contract requirements for EIP-712 signatures
const { signature: wrappedSignature } = await (0, signAndWrapTypedDataForSmartAccount_js_1.signAndWrapTypedDataForSmartAccount)(client, {
smartAccount,
chainId: BigInt(swap.permit2.eip712.domain.chainId || 1),
typedData: swap.permit2.eip712,
ownerIndex: 0n,
idempotencyKey: permit2IdempotencyKey,
});
// Calculate the Permit2 signature length as a 32-byte hex value
const permit2SignatureLengthInHex = (0, viem_1.numberToHex)((0, viem_1.size)(wrappedSignature), {
signed: false,
size: 32,
});
// Append the Permit2 signature length and signature to the transaction data
txData = (0, viem_1.concat)([txData, permit2SignatureLengthInHex, wrappedSignature]);
}
// Send the swap as a user operation
const result = await (0, sendUserOperation_js_1.sendUserOperation)(client, {
smartAccount: smartAccount,
network: swap.network,
paymasterUrl,
idempotencyKey,
calls: [
{
to: swap.transaction.to,
data: txData,
// Only include value if it exists
...(swap.transaction.value ? { value: BigInt(swap.transaction.value) } : {}),
},
],
});
return result;
}
//# sourceMappingURL=sendSwapOperation.js.map