@robertprp/intents-sdk
Version:
Shogun Network Intent-based cross-chain swaps SDK
207 lines (184 loc) • 6.45 kB
text/typescript
import { type Address } from 'viem';
import { type SupportedEvmChain } from '../../chains.js';
import type { EVMConfig } from '../../config.js';
import {
SINGLE_CHAIN_GUARD_ADDRESSES,
CROSS_CHAIN_GUARD_ADDRESSES,
MAX_UINT_32,
DCA_SINGLE_CHAIN_GUARD_ADDRESSES,
} from '../../constants.js';
import {
getEVMCrossChainOrderTypedData,
getEVMDcaSingleChainOrderTypedData,
getEVMSingleChainOrderTypedData,
} from './order-signature.js';
import {
type CrossChainPermitTransferFrom,
type DcaSingleChainPermitTransferFrom,
type SingleChainPermitTransferFrom,
type TransferData,
} from './permit2.js';
import { ChainProvider } from './chain-provider.js';
import type { CrossChainOrder } from '../orders/cross-chain.js';
import type { SingleChainOrder } from '../orders/single-chain.js';
import type {
CrossChainOrderPrepared,
DcaSingleChainOrderPrepared,
SingleChainOrderPrepared,
} from '../../types/intent.js';
import type { DcaSingleChainOrder } from '../orders/dca-single-chain.js';
export class EVMIntentProvider {
provider: ChainProvider;
constructor(config: EVMConfig) {
this.provider = new ChainProvider(config);
}
async prepareSingleChainOrder(order: SingleChainOrder): Promise<SingleChainOrderPrepared> {
const { orderTypedData: dataToSign, nonce } = await getEVMSingleChainOrderTypedData(order);
const signature = await this.provider.walletClient.signTypedData({
message: dataToSign.message,
primaryType: dataToSign.primaryType,
types: dataToSign.types,
domain: dataToSign.domain,
account: this.provider.getAccount(),
});
return {
order,
preparedData: {
nonce: nonce.toString(),
signature: signature.slice(2),
},
};
}
async prepareCrossChainOrder(order: CrossChainOrder): Promise<CrossChainOrderPrepared> {
const { orderTypedData: dataToSign, nonce } = await getEVMCrossChainOrderTypedData(order);
const signature = await this.provider.walletClient.signTypedData({
message: dataToSign.message,
primaryType: dataToSign.primaryType,
types: dataToSign.types,
domain: dataToSign.domain,
account: this.provider.getAccount(),
});
return {
order,
preparedData: {
nonce: nonce.toString(),
signature: signature.slice(2),
},
};
}
static getCrossChainPermissionMessage(order: CrossChainOrder, nonce: bigint): CrossChainPermitTransferFrom {
const spender = CROSS_CHAIN_GUARD_ADDRESSES[order.sourceChainId as SupportedEvmChain] as Address;
return {
permitted: {
token: order.sourceTokenAddress as Address,
amount: order.sourceTokenAmount,
},
spender,
nonce,
deadline: BigInt(order.deadline),
witness: {
user: order.user as Address,
tokenIn: order.sourceTokenAddress as Address,
srcChainId: order.sourceChainId,
deadline: order.deadline,
amountIn: order.sourceTokenAmount,
minStablecoinsAmount: order.minStablecoinAmount,
nonce,
executionDetailsHash: order.getExecutionDetailsHash(),
},
};
}
static getSingleChainLimitPermissionMessage(order: SingleChainOrder, nonce: bigint): SingleChainPermitTransferFrom {
const spender = SINGLE_CHAIN_GUARD_ADDRESSES[order.chainId as SupportedEvmChain] as Address;
const requestedOutput: TransferData = {
token: order.tokenOut as Address,
receiver: order.destinationAddress as Address,
amount: order.amountOutMin,
};
const extraTransfers: TransferData[] =
order.extraTransfers?.map((transfer) => ({
token: transfer.token as Address,
receiver: transfer.receiver as Address,
amount: transfer.amount,
})) || [];
return {
permitted: {
token: order.tokenIn as Address,
amount: order.amountIn,
},
spender,
nonce,
deadline: BigInt(order.deadline),
witness: {
user: order.user as Address,
tokenIn: order.tokenIn as Address,
amountIn: order.amountIn,
requestedOutput,
encodedExternalCallData: '0x', // TODO: Update when external call data is supported
extraTransfers,
deadline: order.deadline,
nonce,
},
};
}
static getRandomNonce(): bigint {
return BigInt(Math.floor(Math.random() * Number(MAX_UINT_32)));
}
async prepareDcaSingleChainOrder(order: DcaSingleChainOrder): Promise<DcaSingleChainOrderPrepared> {
const { orderTypedData: dataToSign, nonce } = await getEVMDcaSingleChainOrderTypedData(order);
const signature = await this.provider.walletClient.signTypedData({
message: dataToSign.message,
primaryType: dataToSign.primaryType,
types: dataToSign.types,
domain: dataToSign.domain,
account: this.provider.getAccount(),
});
return {
order,
preparedData: {
nonce: nonce.toString(),
signature: signature.slice(2),
},
};
}
static getDcaSingleChainPermissionMessage(
order: DcaSingleChainOrder,
nonce: bigint,
): DcaSingleChainPermitTransferFrom {
const spender = DCA_SINGLE_CHAIN_GUARD_ADDRESSES[order.chainId as SupportedEvmChain] as Address;
const requestedOutput: TransferData = {
token: order.tokenOut as Address,
receiver: order.destinationAddress as Address,
amount: BigInt(order.amountOutMin),
};
const extraTransfers: TransferData[] =
order.extraTransfers?.map((transfer) => ({
token: transfer.token as Address,
receiver: transfer.receiver as Address,
amount: transfer.amount,
})) || [];
const totalAmountIn = BigInt(order.amountInPerInterval) * BigInt(order.totalIntervals);
return {
permitted: {
token: order.tokenIn as Address,
amount: totalAmountIn,
},
spender,
nonce,
deadline: BigInt(order.deadline),
witness: {
user: order.user as Address,
tokenIn: order.tokenIn as Address,
startTime: order.startTime,
amountInPerInterval: BigInt(order.amountInPerInterval),
totalIntervals: order.totalIntervals,
intervalDuration: order.intervalDuration,
requestedOutput,
encodedExternalCallData: '0x', // TODO: Update when external call data is supported
extraTransfers,
deadline: order.deadline,
nonce,
},
};
}
}