UNPKG

@robertprp/intents-sdk

Version:

Shogun Network Intent-based cross-chain swaps SDK

144 lines (123 loc) 4.74 kB
// src/core/orders/dca-single-chain.ts import { ChainID, chainIdToChainTypeMap, isEvmChain, type SupportedChain } from '../../chains.js'; import { ValidationError } from '../../errors/index.js'; import type { ApiResponse } from '../../types/api.js'; import type { DcaSingleChainPreparedData, DcaSingleChainUserIntentRequest } from '../../types/intent.js'; import { DcaSingleChainOrderValidator } from '../../utils/order-validator.js'; import { BaseSDK } from '../sdk.js'; import { getEVMDcaSingleChainOrderTypedData } from '../evm/order-signature.js'; import { type ExtraTransfer } from './common.js'; import { getSolanaDcaSingleChainOrderInstructions } from '../solana/dca/create-order.js'; export type CreateDcaSingleChainOrderParams = { chainId: SupportedChain; tokenIn: string; tokenOut: string; destinationAddress: string; startTime: number; amountInPerInterval: bigint; totalIntervals: number; intervalDuration: number; amountOutMin?: bigint; extraTransfers?: ExtraTransfer[]; deadline: number; }; export class DcaSingleChainOrder { public user: string; public chainId: SupportedChain; public tokenIn: string; public tokenOut: string; public destinationAddress: string; public startTime: number; public amountInPerInterval: bigint | number; public totalIntervals: number; public intervalDuration: number; public amountOutMin: bigint | number; public extraTransfers?: ExtraTransfer[]; public deadline: number; private constructor(params: CreateDcaSingleChainOrderParams & { user: string }) { this.user = params.user; this.chainId = params.chainId; this.tokenIn = params.tokenIn; this.tokenOut = params.tokenOut; this.destinationAddress = params.destinationAddress; this.startTime = params.startTime; this.amountInPerInterval = params.amountInPerInterval; this.totalIntervals = params.totalIntervals; this.intervalDuration = params.intervalDuration; this.amountOutMin = params.amountOutMin ?? 1n; this.extraTransfers = params.extraTransfers; this.deadline = params.deadline; } public static async create(input: CreateDcaSingleChainOrderParams & { user: string }): Promise<DcaSingleChainOrder> { new DcaSingleChainOrderValidator().validateOrder(input); const order = new DcaSingleChainOrder({ ...input, user: input.user, }); if (isEvmChain(order.chainId)) { return order; } const preparedRandomData = order.getRandomPreparedData(); const intentRequest = order.toIntentRequest(preparedRandomData); await BaseSDK.validateDcaSingleChainOrder(intentRequest); return order; } /// This is needed because API requires the prepared data to be sent /// In the cases of Solana and Sui, if we want the real data, we must send the order on-chain before validating. /// And that is something we cannot do before validating the order on the API side. private getRandomPreparedData(): DcaSingleChainPreparedData { const chainId = this.chainId; const chainType = chainIdToChainTypeMap[chainId]; const randomNumber = String(Math.floor(Math.random() * 10000000)); switch (chainType) { case 'EVM': { return { nonce: randomNumber, signature: '0x0000000000000000000000000000000000000000000000000000000000000000', }; } case 'Solana': { return { secretNumber: randomNumber, orderPubkey: 'DFNAjFAvS4GF98Tp1kiyLvEHM3wjGXibCfF86nnmhuVc', }; } case 'Sui': { throw new ValidationError('DCA orders are not supported on Sui'); } default: { throw new Error('Chain type not supported'); } } } public getTotalAmountIn(): bigint { return BigInt(this.amountInPerInterval) * BigInt(this.totalIntervals); } public toIntentRequest(preparedData: DcaSingleChainPreparedData): DcaSingleChainUserIntentRequest { const sourceChainType = chainIdToChainTypeMap[this.chainId]; return { genericData: this, chainSpecificData: { [sourceChainType]: preparedData, }, }; } public sendToAuctioneer(preparedData: DcaSingleChainPreparedData): Promise<ApiResponse> { return BaseSDK.sendDcaSingleChainOrder({ order: this, preparedData, }); } public async toEVMTypedData() { if (!isEvmChain(this.chainId)) { throw new ValidationError('Chain id is not an Ethereum compatible chain'); } return getEVMDcaSingleChainOrderTypedData(this); } public async toSolanaInstructionsByteArray() { if (this.chainId !== ChainID.Solana) { throw new ValidationError('Chain id is not Solana'); } return getSolanaDcaSingleChainOrderInstructions(this); } }