@robertprp/intents-sdk
Version:
Shogun Network Intent-based cross-chain swaps SDK
175 lines • 7.79 kB
JavaScript
import { privateKeyToAccount } from 'viem/accounts';
import { BaseSDK } from '../sdk.js';
import { EVMIntentProvider } from './intent-provider.js';
import {} from 'viem';
import { PERMIT2_ADDRESS, CROSS_CHAIN_GUARD_ADDRESSES, SINGLE_CHAIN_GUARD_ADDRESSES, MAX_UINT_256, } from '../../constants.js';
import { fetchJWTToken, fetchSiweMessage } from '../../auth/siwe.js';
/**
* Handles EVM-specific aspects of cross-chain swaps for Ethereum-compatible chains:
*/
export class EVMSDK extends BaseSDK {
constructor(config) {
super();
Object.defineProperty(this, "config", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "evmIntentProvider", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.config = config;
this.evmIntentProvider = new EVMIntentProvider(config);
}
async cancelCrossChainOrder(params) {
const chainId = this.config.chainId;
const auctioneerAddress = CROSS_CHAIN_GUARD_ADDRESSES[chainId];
const userAddress = (await this.getUserAddress());
const auctioneerContract = this.evmIntentProvider.provider.getCrossChainAuctioneerContract(auctioneerAddress);
const permit2Contract = this.evmIntentProvider.provider.getPermit2Contract(PERMIT2_ADDRESS[chainId]);
const orderIdHex = params.orderId;
let [initialized, deactivated] = await auctioneerContract.read.orderData([orderIdHex]);
let txHash;
if (initialized) {
if (deactivated) {
throw new Error('Order is already deactivated');
}
const orderInfo = {
user: params.user,
tokenIn: params.tokenIn,
srcChainId: params.srcChainId,
deadline: params.deadline,
amountIn: BigInt(params.amountIn),
minStablecoinsAmount: BigInt(params.minStablecoinsAmount),
executionDetailsHash: params.executionDetailsHash,
nonce: BigInt(params.nonce),
};
const tx = await auctioneerContract.write.cancelOrder([orderInfo]);
txHash = tx;
}
else {
const nonce = BigInt(params.nonce);
let nonceWordPos = nonce >> 8n;
let nonceBitPos = nonce - nonceWordPos * 256n;
let mask = 1n << nonceBitPos;
const currentNonceBitmap = await permit2Contract.read.nonceBitmap([userAddress, nonceWordPos]);
if ((currentNonceBitmap & (1n << nonceBitPos)) !== 0n) {
throw new Error('Nonce is already invalidated');
}
const tx = await permit2Contract.write.invalidateUnorderedNonces([nonceWordPos, mask]);
txHash = tx;
}
return txHash;
}
async cancelSingleChainOrder(params) {
const chainId = this.config.chainId;
const auctioneerAddress = SINGLE_CHAIN_GUARD_ADDRESSES[chainId];
const userAddress = (await this.getUserAddress());
const auctioneerContract = this.evmIntentProvider.provider.getSingleChainAuctioneerContract(auctioneerAddress);
const orderHash = params.orderId;
const wasManuallyInitialized = await auctioneerContract.read.orderManuallyInitialized([orderHash]);
let txHash;
if (wasManuallyInitialized) {
txHash = await auctioneerContract.write.cancelManuallyCreatedOrder([
{
amountIn: params.amountIn,
tokenIn: params.tokenIn,
deadline: params.deadline,
nonce: params.nonce,
encodedExternalCallData: params.encodedExternalCallData,
extraTransfers: params.extraTransfers.map((transfer) => {
return {
amount: transfer.amount,
receiver: transfer.receiver,
token: transfer.token,
};
}),
requestedOutput: {
amount: params.requestedOutput.amount,
receiver: params.requestedOutput.receiver,
token: params.requestedOutput.token,
},
user: params.user,
},
]);
}
else {
const permit2Contract = this.evmIntentProvider.provider.getPermit2Contract(PERMIT2_ADDRESS[chainId]);
const nonce = BigInt(params.nonce);
const nonceWordPos = nonce >> 8n;
const nonceBitPos = nonce - nonceWordPos * 256n;
const mask = 1n << nonceBitPos;
// Check if nonce is already invalidated
const currentNonceBitmap = await permit2Contract.read.nonceBitmap([userAddress, nonceWordPos]);
if ((currentNonceBitmap & (1n << nonceBitPos)) !== 0n) {
throw new Error('Nonce is already invalidated');
}
txHash = await permit2Contract.write.invalidateUnorderedNonces([nonceWordPos, mask]);
}
return txHash;
}
async authenticate(token) {
const chainId = this.config.chainId;
const wallet = await this.getUserAddress();
const response = await fetchSiweMessage({
chainId,
wallet,
});
const message = response.data;
// Sign the message using the wallet client
const signature = await this.evmIntentProvider.provider.walletClient.signMessage({
message,
});
const jwt = await fetchJWTToken({
message,
signature,
}, token);
const newToken = jwt.data;
return newToken;
}
/**
* Gets the user's Ethereum address derived from their private key
* @returns Promise resolving to the user's Ethereum address as a 0x-prefixed string
*/
async getUserAddress() {
return privateKeyToAccount(this.config.privateKey).address;
}
async approveAllowanceIfNeeded(tokenAddress, amount) {
const permit2Address = PERMIT2_ADDRESS[this.config.chainId];
const ERC20Contract = this.evmIntentProvider.provider.getERC20Contract(tokenAddress);
const userAddress = (await this.getUserAddress());
const allowance = await ERC20Contract.read.allowance([userAddress, permit2Address]);
if (allowance < amount) {
await ERC20Contract.write.approve([permit2Address, MAX_UINT_256]);
}
}
/**
* Prepares an EVM order for submission
*
* This method:
* 1. Validates token balances and allowances
* 2. Creates and signs the EIP-712 typed data for Permit2
* 3. Prepares the order for submission to the auctioneer
*
* @param order The validated order to prepare
* @returns Promise resolving to a prepared order with EVM-specific signature data
*/
async prepareCrossChainOrder(order) {
await this.approveAllowanceIfNeeded(order.sourceTokenAddress, order.sourceTokenAmount);
return this.evmIntentProvider.prepareCrossChainOrder(order);
}
async prepareSingleChainOrder(order) {
await this.approveAllowanceIfNeeded(order.tokenIn, order.amountIn);
return this.evmIntentProvider.prepareSingleChainOrder(order);
}
async prepareDcaSingleChainOrder(order) {
const totalAmountIn = order.getTotalAmountIn();
await this.approveAllowanceIfNeeded(order.tokenIn, totalAmountIn);
return this.evmIntentProvider.prepareDcaSingleChainOrder(order);
}
}
//# sourceMappingURL=sdk.js.map