import { type EnrichedOrder, getQuote, type OrderCreation, OrderKind, OrderStatus, type QuoteResults, SigningScheme, } from '@cowprotocol/cow-sdk'; import { type Account, type Chain, maxUint256, parseUnits, type PublicActions, type Transport, type WalletClient, } from 'viem'; import { ensureTokenApproval } from './ensure-token-approval.js'; import type { SerializableErc20TokenType } from '../currencies/base-currency.js'; type CreateCoWSellTradeDeps = { walletClient: WalletClient & PublicActions; }; type CreateCoWSellTradeParams = { sellTokenAmount: number; sellToken: SerializableErc20TokenType; buyToken: SerializableErc20TokenType; }; const vaultRelayerAddress = '0xC92E8bdf79f0507f65a392b0ab4667716BFE0110' as const; const defaultAppData = '0x0000000000000000000000000000000000000000000000000000000000000000' as `0x${string}`; type CreateCoWSellTradeReturnType = { quote: QuoteResults; execute(): Promise; }; export async function createCowSellTrade( deps: CreateCoWSellTradeDeps, params: CreateCoWSellTradeParams, ): Promise { const { walletClient } = deps; const { sellTokenAmount, sellToken, buyToken } = params; if (!walletClient.account) { throw new Error('No account found in wallet client'); } if (sellToken.address === buyToken.address) { throw new Error('Sell token and buy token cannot be the same'); } if (sellToken.chainId !== buyToken.chainId) { throw new Error('Sell token and buy token must be on the same chain'); } const { result: cowGetQuoteResult, orderBookApi } = await getQuote( { amount: parseUnits(sellTokenAmount.toString(), sellToken.decimals).toString(), sellToken: sellToken.address, buyToken: buyToken.address, kind: OrderKind.SELL, receiver: walletClient.account.address, sellTokenDecimals: sellToken.decimals, buyTokenDecimals: buyToken.decimals, }, { account: walletClient.account.address, chainId: sellToken.chainId, appCode: defaultAppData, }, ); async function execute(): Promise { // Ensure allowance is set for the sell token await ensureTokenApproval( { client: walletClient, }, { tokenAddress: sellToken.address, spenderAddress: vaultRelayerAddress, amount: maxUint256, }, ); const orderTypedData = { ...cowGetQuoteResult.orderTypedData, message: { ...cowGetQuoteResult.orderTypedData.message, appData: defaultAppData, }, }; const signature = await walletClient.signTypedData(orderTypedData as any); const sendOrderParams: OrderCreation = { ...orderTypedData.message, signature, signingScheme: SigningScheme.EIP712, }; const orderId = await orderBookApi.sendOrder(sendOrderParams); // wait for the order to be filled let order = await orderBookApi.getOrder(orderId); while (order.status !== OrderStatus.FULFILLED) { await new Promise((resolve) => setTimeout(resolve, 5000)); // wait 5 seconds before checking again order = await orderBookApi.getOrder(orderId); } return order; } return { quote: cowGetQuoteResult, execute, }; }