@robertprp/intents-sdk
Version:
Shogun Network Intent-based cross-chain swaps SDK
185 lines • 6.95 kB
JavaScript
import { ChainID, chainIdToChainTypeMap, isEvmChain } from '../../chains.js';
import { ValidationError } from '../../errors/index.js';
import { SingleChainOrderValidator } from '../../utils/order-validator.js';
import { QuoteProvider } from '../../utils/quote/aggregator.js';
import { getEVMSingleChainOrderTypedData } from '../evm/order-signature.js';
import { BaseSDK } from '../sdk.js';
import { getSolanaSingleChainOrderInstructions } from '../solana/order-instructions.js';
import {} from './common.js';
export class SingleChainOrder {
constructor(params) {
Object.defineProperty(this, "user", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "chainId", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "amountIn", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "tokenIn", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "tokenOut", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "amountOutMin", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "destinationAddress", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "extraTransfers", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "deadline", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "stopLossMaxOut", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "takeProfitMinOut", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.user = params.user;
this.chainId = params.chainId;
this.amountIn = params.amountIn;
this.tokenIn = params.tokenIn;
this.tokenOut = params.tokenOut;
this.amountOutMin = params.amountOutMin ?? 1n;
this.destinationAddress = params.destinationAddress;
this.extraTransfers = params.extraTransfers;
this.deadline = params.deadline;
this.stopLossMaxOut = params.stopLossMaxOut;
this.takeProfitMinOut = params.takeProfitMinOut;
}
static async create(input) {
new SingleChainOrderValidator().validateOrder(input);
const amountOutMin = await this.calculateAmountOutMin(input);
const order = new SingleChainOrder({
...input,
amountOutMin,
user: input.user,
});
const preparedRandomData = order.getRandomPreparedData();
const intentRequest = order.toIntentRequest(preparedRandomData);
await BaseSDK.validateSingleChainOrder(intentRequest);
return order;
}
/// This is needed because API requires the prepared data to be send
/// 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.
getRandomPreparedData() {
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': {
const digest = 'FQWndwYJhNQUoHyvR8UuhGURC2EKx9eWErFm9Tc2DggF';
return { transactionHash: digest };
}
default: {
throw new Error('Chain type not supported');
}
}
}
static async calculateAmountOutMin(input) {
const { amountOutMin, stopLossMaxOut } = input;
const scenario = this.getSingleChainOrderScenario({
hasAmountOutMin: !!amountOutMin,
hasStopLoss: !!stopLossMaxOut,
});
switch (scenario) {
case 'QUOTE_REQUIRED':
const quote = await QuoteProvider.getSingleChainQuote({
tokenIn: input.tokenIn,
amount: input.amountIn,
chainId: input.chainId,
tokenOut: input.tokenOut,
});
return (quote.amountOut * 93n) / 100n; // Add 7% reduced to cover fees
case 'USE_PROVIDED_AMOUNT':
return amountOutMin;
case 'STOP_LOSS_ONLY':
case 'BOTH_PROVIDED':
// When stop loss is involved, amountOutMin should be 1
return 1n;
}
}
static getSingleChainOrderScenario({ hasAmountOutMin, hasStopLoss, }) {
if (!hasAmountOutMin && !hasStopLoss)
return 'QUOTE_REQUIRED';
if (hasAmountOutMin && !hasStopLoss)
return 'USE_PROVIDED_AMOUNT';
if (!hasAmountOutMin && hasStopLoss)
return 'STOP_LOSS_ONLY';
return 'BOTH_PROVIDED';
}
toIntentRequest(preparedData) {
const sourceChainType = chainIdToChainTypeMap[this.chainId];
return {
genericData: this,
chainSpecificData: {
[sourceChainType]: preparedData,
},
};
}
sendToAuctioneer(preparedData) {
return BaseSDK.sendSingleChainOrder({
order: this,
preparedData,
});
}
async toEVMTypedData() {
if (!isEvmChain(this.chainId)) {
throw new ValidationError('Chain id is not an Ethereum compatible chain');
}
return getEVMSingleChainOrderTypedData(this);
}
async toSolanaInstructionsByteArray() {
if (this.chainId !== ChainID.Solana) {
throw new ValidationError('Chain id is not Solana');
}
return getSolanaSingleChainOrderInstructions(this);
}
}
//# sourceMappingURL=single-chain.js.map