UNPKG

@robertprp/intents-sdk

Version:

Shogun Network Intent-based cross-chain swaps SDK

175 lines 7.79 kB
import { privateKeyToAccount } from 'viem/accounts'; import { BaseSDK } from '../sdk.js'; import { EVMIntentProvider } from './intent-provider.js'; import {} from 'viem'; import { PERMIT2_ADDRESS, CROSS_CHAIN_GUARD_ADDRESSES, SINGLE_CHAIN_GUARD_ADDRESSES, MAX_UINT_256, } from '../../constants.js'; import { fetchJWTToken, fetchSiweMessage } from '../../auth/siwe.js'; /** * Handles EVM-specific aspects of cross-chain swaps for Ethereum-compatible chains: */ export class EVMSDK extends BaseSDK { constructor(config) { super(); Object.defineProperty(this, "config", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "evmIntentProvider", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.config = config; this.evmIntentProvider = new EVMIntentProvider(config); } async cancelCrossChainOrder(params) { const chainId = this.config.chainId; const auctioneerAddress = CROSS_CHAIN_GUARD_ADDRESSES[chainId]; const userAddress = (await this.getUserAddress()); const auctioneerContract = this.evmIntentProvider.provider.getCrossChainAuctioneerContract(auctioneerAddress); const permit2Contract = this.evmIntentProvider.provider.getPermit2Contract(PERMIT2_ADDRESS[chainId]); const orderIdHex = params.orderId; let [initialized, deactivated] = await auctioneerContract.read.orderData([orderIdHex]); let txHash; if (initialized) { if (deactivated) { throw new Error('Order is already deactivated'); } const orderInfo = { user: params.user, tokenIn: params.tokenIn, srcChainId: params.srcChainId, deadline: params.deadline, amountIn: BigInt(params.amountIn), minStablecoinsAmount: BigInt(params.minStablecoinsAmount), executionDetailsHash: params.executionDetailsHash, nonce: BigInt(params.nonce), }; const tx = await auctioneerContract.write.cancelOrder([orderInfo]); txHash = tx; } else { const nonce = BigInt(params.nonce); let nonceWordPos = nonce >> 8n; let nonceBitPos = nonce - nonceWordPos * 256n; let mask = 1n << nonceBitPos; const currentNonceBitmap = await permit2Contract.read.nonceBitmap([userAddress, nonceWordPos]); if ((currentNonceBitmap & (1n << nonceBitPos)) !== 0n) { throw new Error('Nonce is already invalidated'); } const tx = await permit2Contract.write.invalidateUnorderedNonces([nonceWordPos, mask]); txHash = tx; } return txHash; } async cancelSingleChainOrder(params) { const chainId = this.config.chainId; const auctioneerAddress = SINGLE_CHAIN_GUARD_ADDRESSES[chainId]; const userAddress = (await this.getUserAddress()); const auctioneerContract = this.evmIntentProvider.provider.getSingleChainAuctioneerContract(auctioneerAddress); const orderHash = params.orderId; const wasManuallyInitialized = await auctioneerContract.read.orderManuallyInitialized([orderHash]); let txHash; if (wasManuallyInitialized) { txHash = await auctioneerContract.write.cancelManuallyCreatedOrder([ { amountIn: params.amountIn, tokenIn: params.tokenIn, deadline: params.deadline, nonce: params.nonce, encodedExternalCallData: params.encodedExternalCallData, extraTransfers: params.extraTransfers.map((transfer) => { return { amount: transfer.amount, receiver: transfer.receiver, token: transfer.token, }; }), requestedOutput: { amount: params.requestedOutput.amount, receiver: params.requestedOutput.receiver, token: params.requestedOutput.token, }, user: params.user, }, ]); } else { const permit2Contract = this.evmIntentProvider.provider.getPermit2Contract(PERMIT2_ADDRESS[chainId]); const nonce = BigInt(params.nonce); const nonceWordPos = nonce >> 8n; const nonceBitPos = nonce - nonceWordPos * 256n; const mask = 1n << nonceBitPos; // Check if nonce is already invalidated const currentNonceBitmap = await permit2Contract.read.nonceBitmap([userAddress, nonceWordPos]); if ((currentNonceBitmap & (1n << nonceBitPos)) !== 0n) { throw new Error('Nonce is already invalidated'); } txHash = await permit2Contract.write.invalidateUnorderedNonces([nonceWordPos, mask]); } return txHash; } async authenticate(token) { const chainId = this.config.chainId; const wallet = await this.getUserAddress(); const response = await fetchSiweMessage({ chainId, wallet, }); const message = response.data; // Sign the message using the wallet client const signature = await this.evmIntentProvider.provider.walletClient.signMessage({ message, }); const jwt = await fetchJWTToken({ message, signature, }, token); const newToken = jwt.data; return newToken; } /** * Gets the user's Ethereum address derived from their private key * @returns Promise resolving to the user's Ethereum address as a 0x-prefixed string */ async getUserAddress() { return privateKeyToAccount(this.config.privateKey).address; } async approveAllowanceIfNeeded(tokenAddress, amount) { const permit2Address = PERMIT2_ADDRESS[this.config.chainId]; const ERC20Contract = this.evmIntentProvider.provider.getERC20Contract(tokenAddress); const userAddress = (await this.getUserAddress()); const allowance = await ERC20Contract.read.allowance([userAddress, permit2Address]); if (allowance < amount) { await ERC20Contract.write.approve([permit2Address, MAX_UINT_256]); } } /** * Prepares an EVM order for submission * * This method: * 1. Validates token balances and allowances * 2. Creates and signs the EIP-712 typed data for Permit2 * 3. Prepares the order for submission to the auctioneer * * @param order The validated order to prepare * @returns Promise resolving to a prepared order with EVM-specific signature data */ async prepareCrossChainOrder(order) { await this.approveAllowanceIfNeeded(order.sourceTokenAddress, order.sourceTokenAmount); return this.evmIntentProvider.prepareCrossChainOrder(order); } async prepareSingleChainOrder(order) { await this.approveAllowanceIfNeeded(order.tokenIn, order.amountIn); return this.evmIntentProvider.prepareSingleChainOrder(order); } async prepareDcaSingleChainOrder(order) { const totalAmountIn = order.getTotalAmountIn(); await this.approveAllowanceIfNeeded(order.tokenIn, totalAmountIn); return this.evmIntentProvider.prepareDcaSingleChainOrder(order); } } //# sourceMappingURL=sdk.js.map