@robertprp/intents-sdk
Version:
Shogun Network Intent-based cross-chain swaps SDK
82 lines (67 loc) • 2.82 kB
text/typescript
import { Transaction } from '@mysten/sui/transactions';
import { normalizeSuiObjectId } from '@mysten/sui/utils';
import { ChainID } from '../../chains.js';
import {
CROSS_CHAIN_GUARD_ADDRESSES,
NATIVE_SUI_TOKEN_ADDRESS,
SUI_GUARD_COLLATERAL_TYPE,
SUI_GUARD_STABLECOIN_TYPE,
SUI_PACKAGE_ID,
} from '../../constants.js';
import { ValidationError } from '../../errors/index.js';
import { createSuiClient } from './client.js';
import type { CrossChainOrder } from '../orders/cross-chain.js';
/**
* Generates Sui order transaction for creating an order
* @param order The order to generate transaction for
* @returns Object containing the transaction
*/
export async function getSuiOrderTransaction(order: CrossChainOrder): Promise<Transaction> {
const userAddress = order.user;
const client = createSuiClient();
const coins = await client.getCoins({
owner: userAddress,
coinType: order.sourceTokenAddress,
});
const guardAddress = CROSS_CHAIN_GUARD_ADDRESSES[ChainID.Sui];
const sourceTokenInCoins = coins.data.sort((a, b) => Number(b.balance) - Number(a.balance));
if (sourceTokenInCoins.length === 0) {
throw new ValidationError(`No token (${order.sourceTokenAddress}) to create order`);
}
const coinsInSum = sourceTokenInCoins.reduce((acc, coin) => acc + BigInt(coin.balance), 0n);
if (coinsInSum < order.sourceTokenAmount) {
throw new ValidationError(
`Insufficient balance to create order. Total coins is ${coinsInSum}, but order requires ${order.sourceTokenAmount}`,
);
}
const spendingGasCoin =
normalizeSuiObjectId(order.sourceTokenAddress) === normalizeSuiObjectId(NATIVE_SUI_TOKEN_ADDRESS);
const tx = new Transaction();
const coinInArg = spendingGasCoin ? tx.gas : sourceTokenInCoins[0]!.coinObjectId;
if (sourceTokenInCoins.length > 1) {
// Merging coins IN into a single object just in case
tx.mergeCoins(
// merge into 1st coin object
coinInArg,
// merge the rest of coins
sourceTokenInCoins.slice(1).map((coin) => coin.coinObjectId),
);
}
const [coinIn] = tx.splitCoins(coinInArg, [order.sourceTokenAmount]);
tx.moveCall({
package: SUI_PACKAGE_ID,
module: 'source_chain_guard',
function: 'create_order',
arguments: [
tx.object(guardAddress), // arg0: &Guard<T0, T2>
coinIn, // arg1: Coin<T1>
tx.pure.u64(order.minStablecoinAmount), // arg2: min_stablecoins_amount
tx.pure.u64(order.deadline * 1000), // arg3: deadline (converted to milliseconds)
tx.pure.vector('u8', order.executionDetailsHashToBytes()), // arg4: execution_details_hash
tx.object.clock(), // arg5: &Clock
],
typeArguments: [SUI_GUARD_COLLATERAL_TYPE, order.sourceTokenAddress, SUI_GUARD_STABLECOIN_TYPE],
});
tx.setSender(userAddress);
return tx;
}