import { createSwapFromSuiMoveCalls, Quote as MayanQuote, swapFromSolana, swapFromEvm, SolanaTransactionSigner, JitoBundleOptions, Erc20Permit, addresses, } from "@mayanfinance/swap-sdk"; import { BridgeSwapQuote } from "../../../types"; import { SuiClient } from "@mysten/sui/client"; import { Transaction } from "@mysten/sui/transactions"; import { Connection, SendOptions } from "@solana/web3.js"; import { Signer, Overrides, Contract, parseUnits } from "ethers"; const ERC20_ABI = [ "function allowance(address owner, address spender) view returns (uint256)", "function approve(address spender, uint256 amount) returns (bool)", "function balanceOf(address account) view returns (uint256)", ]; type SuiWalletConnection = { provider: SuiClient; signTransaction: (data: { transaction: Transaction }) => Promise<{ bytes: string; signature: string; }>; }; type SolanaWalletConnection = { signTransaction: SolanaTransactionSigner; connection: Connection; extraRpcs?: string[]; sendOptions?: SendOptions; jitoOptions?: JitoBundleOptions; }; type EVMWalletConnection = { overrides: Overrides | null | undefined; signer: Signer; permit: Erc20Permit | null | undefined; waitForTransaction: (data: { hash: string; confirmations: number; }) => Promise; }; export type WalletConnection = { sui?: SuiWalletConnection; solana?: SolanaWalletConnection; evm?: EVMWalletConnection; }; enum BridgeChain { SUI = 1999, SOLANA = 0, } export async function swap( route: BridgeSwapQuote, fromAddress: string, toAddress: string, walletConnection: WalletConnection, referrerAddresses?: { sui?: string; evm?: string; solana?: string; } ): Promise { if (!route) { throw new Error("No route found"); } const mayanQuote = route.info_for_bridge as MayanQuote; let hash: string; if (route.from_token.chainId === BridgeChain.SUI) { if (!walletConnection.sui) { throw new Error("Sui wallet connection not found"); } const client = walletConnection.sui.provider; const swapTrx = await createSwapFromSuiMoveCalls( mayanQuote, fromAddress, toAddress, referrerAddresses, null, client ); const connection = walletConnection.sui; const signed: { bytes: string; signature: string; } = await connection.signTransaction({ transaction: swapTrx }); const resp = await client.executeTransactionBlock({ transactionBlock: signed.bytes, signature: [signed.signature], options: { showEffects: true, showEvents: true, showBalanceChanges: true, }, }); hash = resp.digest; await client.waitForTransaction({ digest: hash, }); } else if (route.from_token.chainId === BridgeChain.SOLANA) { if (!walletConnection.solana) { throw new Error("Solana wallet connection not found"); } const connection = walletConnection.solana; const swapTrx = await swapFromSolana( mayanQuote, fromAddress, toAddress, referrerAddresses, connection.signTransaction, connection.connection, connection.extraRpcs, connection.sendOptions, connection.jitoOptions ); hash = swapTrx.signature; } else { if (!walletConnection.evm) { throw new Error("EVM wallet connection not found"); } const connection = walletConnection.evm; const fromToken = mayanQuote.fromToken; if (fromToken.standard === "erc20") { const erc20Contract = new Contract( fromToken.realOriginContractAddress || fromToken.contract, ERC20_ABI, connection.signer ); const currentAllowance = await erc20Contract.allowance( fromAddress, addresses.MAYAN_FORWARDER_CONTRACT ); const REQUIRED_ALLOWANCE = parseUnits( String(mayanQuote.effectiveAmountIn), fromToken.decimals ); if (currentAllowance < REQUIRED_ALLOWANCE) { const approveTrx = await erc20Contract.approve( addresses.MAYAN_FORWARDER_CONTRACT, REQUIRED_ALLOWANCE ); const receiptApprove = await approveTrx.wait(); if (!receiptApprove) { throw new Error("Failed to approve allowance"); } } } const swapTrx = await swapFromEvm( mayanQuote, fromAddress, toAddress, referrerAddresses, connection.signer, connection.permit, connection.overrides, null ); hash = typeof swapTrx === "string" ? swapTrx : swapTrx.hash; await connection.waitForTransaction({ hash, confirmations: 3, }); } // wait for 2 seconds to make sure the mayan has processed the transaction await new Promise((resolve) => { setTimeout(() => { resolve(true); }, 2000); }); return hash; }