UNPKG

@deserialize/evm-limit-sdk

Version:

TypeScript client for LimitOrderWithPermit2 smart contract

311 lines 11.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LimitOrderMaker = void 0; const ethers_1 = require("ethers"); const LimitOrderClient_1 = require("./LimitOrderClient"); /** * SDK for creating and managing limit orders (Maker side) * Can be used in both frontend and backend environments */ class LimitOrderMaker { constructor(client, signer, serverUrl = 'https://limit-engine.deserialize.xyz/api/v1') { this.client = client; this.signer = signer; this.serverUrl = serverUrl; } /** * Create a complete limit order with all signatures * @param params Order parameters * @returns Signed order ready to be submitted */ async createOrder(params) { const makerAddress = await this.signer.getAddress(); // Build complete order const order = { maker: makerAddress, makerToken: params.makerToken, takerToken: params.takerToken, makerAmount: params.makerAmount, takerAmount: params.takerAmount, salt: params.salt || (0, LimitOrderClient_1.generateOrderSalt)(), expiry: params.expiry || (0, LimitOrderClient_1.createOrderExpiry)(86400) // 24 hours default }; // Handle native token wrapping if needed let finalOrder = { ...order }; if (order.makerToken.toLowerCase() === this.client.NATIVE_TOKEN.toLowerCase()) { console.log('🔄 Wrapping native token...'); const wrapTx = this.client.getWrapNativeTransaction(order.makerAmount.toString()); const wrapReceipt = await this.signer.sendTransaction(wrapTx); await wrapReceipt.wait(); console.log('✅ Native token wrapped'); finalOrder.makerToken = this.client.W_NATIVE_ADDRESS; } // Check and handle Permit2 approval await this.ensurePermit2Approval(finalOrder.makerToken); // Get signing data const { orderDataToSign, permit2DataToSign } = this.client.getCreateOrderMessageData(finalOrder); // Sign both messages console.log('📝 Signing order...'); const orderSignature = await this.signer.signTypedData(orderDataToSign.domain, orderDataToSign.types, orderDataToSign.value); console.log('📝 Signing Permit2...'); const permit2Signature = await this.signer.signTypedData(permit2DataToSign.domain, permit2DataToSign.types, permit2DataToSign.value); // Get order hash const orderHash = await this.client.getOrderHash(finalOrder); const signedOrder = { order: finalOrder, orderSignature, permit2Signature, orderHash, createdAt: Date.now() }; console.log('✅ Order created and signed:', orderHash); return signedOrder; } /** * Submit a signed order to the external server * @param signedOrder The signed order to submit */ async submitOrder(signedOrder) { return this.saveOrderToServer(signedOrder); } /** * Create and submit an order in one step * @param params Order parameters */ async createAndSubmitOrder(params) { const signedOrder = await this.createOrder(params); const submitResponse = await this.submitOrder(signedOrder); if (!submitResponse.success) { throw new Error(`Failed to submit order: ${submitResponse.error}`); } return { signedOrder, submitResponse }; } /** * Cancel an order * @param orderHash The order hash to cancel * @param salt The order salt to cancel on-chain */ async cancelOrder(orderHash, salt) { console.log('🚫 Cancelling order on-chain...'); const tx = await this.client.cancelOrder(salt, this.signer); await tx.wait(); console.log('✅ Order cancelled on-chain'); // Also notify the server try { await this.notifyServerOfCancellation(orderHash, tx.hash); } catch (error) { console.warn('⚠️ Failed to notify server of cancellation:', error); } return tx; } /** * Check if an order has been filled * @param orderHash The order hash */ async isOrderFilled(orderHash) { // Check on-chain first const onChainFilled = await this.client.isOrderFilled(orderHash); if (onChainFilled) return true; // Also check server try { const serverOrder = await this.getOrderFromServer(orderHash); return serverOrder?.status === 'FILLED'; } catch (error) { return false; } } /** * Check if an order has been cancelled * @param salt The order salt */ async isOrderCancelled(salt) { const makerAddress = await this.signer.getAddress(); return await this.client.isCancelled(makerAddress, salt); } /** * Get all filled orders for the maker */ async getMyFilledOrders(fromBlock = 0) { const makerAddress = await this.signer.getAddress(); return await this.client.getOrderFilledEvents(fromBlock, 'latest', { maker: makerAddress }); } /** * Get maker's orders from server */ async getMyOrders(status, limit = 50) { try { const makerAddress = await this.signer.getAddress(); const url = new URL(`${this.serverUrl}/makers/${makerAddress}/orders`); if (status) url.searchParams.append('status', status); url.searchParams.append('limit', limit.toString()); const response = await fetch(url.toString()); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); return result.data || []; } catch (error) { console.error('Failed to fetch orders from server:', error.message); throw error; } } /** * Get order details from server */ async getOrderFromServer(orderHash) { try { const response = await fetch(`${this.serverUrl}/orders/${orderHash}`); if (!response.ok) { if (response.status === 404) return null; throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); return result.data; } catch (error) { console.error('Failed to get order from server:', error.message); return null; } } /** * Ensure Permit2 has approval for the token * @param tokenAddress The token address */ async ensurePermit2Approval(tokenAddress) { const makerAddress = await this.signer.getAddress(); const isApproved = await this.isPermit2Approved(tokenAddress); if (!isApproved) { console.log('🔄 Approving Permit2...'); const approveTx = await this.approvePermit2(tokenAddress); await approveTx.wait(); console.log('✅ Permit2 approved'); } } /** * Check if Permit2 is approved for a token */ async isPermit2Approved(tokenAddress) { const ERC20_ABI = [ "function allowance(address owner, address spender) external view returns (uint256)" ]; const token = new ethers_1.ethers.Contract(tokenAddress, ERC20_ABI, this.signer); const makerAddress = await this.signer.getAddress(); const allowance = await token.allowance(makerAddress, this.client.PERMIT2_CONTRACT); return allowance > 0; } /** * Approve Permit2 for a token */ async approvePermit2(tokenAddress) { const ERC20_ABI = [ "function approve(address spender, uint256 amount) external returns (bool)" ]; const token = new ethers_1.ethers.Contract(tokenAddress, ERC20_ABI, this.signer); return await token.approve(this.client.PERMIT2_CONTRACT, ethers_1.ethers.MaxUint256); } /** * Save order to external server */ async saveOrderToServer(signedOrder) { console.log('💾 Saving order to server...'); try { const response = await fetch(`${this.serverUrl}/orders`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ order: { maker: signedOrder.order.maker, makerToken: signedOrder.order.makerToken, takerToken: signedOrder.order.takerToken, makerAmount: signedOrder.order.makerAmount.toString(), takerAmount: signedOrder.order.takerAmount.toString(), salt: signedOrder.order.salt.toString(), expiry: signedOrder.order.expiry.toString(), }, orderSignature: signedOrder.orderSignature, permit2Signature: signedOrder.permit2Signature, orderHash: signedOrder.orderHash, }) }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error || `HTTP error! status: ${response.status}`); } const result = await response.json(); console.log('✅ Order saved to server:', result.data?.orderHash); return { success: true, orderId: result.data?.orderHash || signedOrder.orderHash, }; } catch (error) { console.error('❌ Failed to save order to server:', error.message); return { success: false, error: error.message, }; } } /** * Notify server that order was cancelled */ async notifyServerOfCancellation(orderHash, transactionHash) { try { const makerAddress = await this.signer.getAddress(); const response = await fetch(`${this.serverUrl}/orders/${orderHash}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ maker: makerAddress, transactionHash, }) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } console.log('✅ Server notified of cancellation'); } catch (error) { console.error('Failed to notify server:', error.message); throw error; } } /** * Get the maker address */ async getMakerAddress() { return await this.signer.getAddress(); } /** * Get the LimitOrderClient instance */ getClient() { return this.client; } /** * Set server URL */ setServerUrl(url) { this.serverUrl = url; } /** * Get current server URL */ getServerUrl() { return this.serverUrl; } } exports.LimitOrderMaker = LimitOrderMaker; //# sourceMappingURL=LimitMaker.js.map