UNPKG

@accret/bridge-sdk

Version:
227 lines 9.44 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildTransaction = buildTransaction; const swap_sdk_1 = require("@mayanfinance/swap-sdk"); const web3_js_1 = require("@solana/web3.js"); const bs58_1 = __importDefault(require("bs58")); const ethers_1 = require("ethers"); const ethers_2 = require("ethers"); const ethers_3 = require("ethers"); const ERC20Permit_json_1 = require("./contracts/ERC20Permit.json"); const ethers_4 = require("ethers"); const MayanForwarderArtifact_1 = __importDefault(require("./contracts/MayanForwarderArtifact")); const ethers_5 = require("ethers"); const getRpcUrl_1 = require("../../utils/getRpcUrl"); const utils_1 = require("../../utils"); const types_1 = require("../../types"); /** * @description Builds a transaction for the given quote * @param quote - The quote object containing swap details * @param destAddr - The destination address for the swap * @param privateKey - The private key of the wallet initiating the swap * @returns The transaction hash of the built transaction */ async function buildTransaction(quote, destAddr, privateKey) { let txHash; const referrerAddress = { solana: "69izdTrBfvhpuq8LgWifstGbHTZC6DKn1w5wLpdjapfF", evm: "0xD0208Bfe9Ae201Cc2baE4e4b5a74561472A7a910", }; if (quote.fromChain === "solana") { txHash = await swapSolana({ quote, destAddr, privateKey, referrerAddress, }); } else { txHash = await swapEVM({ quote, destAddr, privateKey, referrerAddress, }); } return txHash; } /** * @description Executes a swap on the Solana network * @param param0 - The parameters for the Solana swap * @returns The transaction hash of the executed swap */ async function swapSolana({ quote, destAddr, privateKey, referrerAddress, }) { const privateKeyArray = bs58_1.default.decode(privateKey); const wallet = web3_js_1.Keypair.fromSecretKey(privateKeyArray); const connection = new web3_js_1.Connection((0, getRpcUrl_1.getRpcUrl)({ chainId: types_1.AccretSupportedChain.SOLANA_CHAIN }), "confirmed"); // eslint-disable-next-line @typescript-eslint/no-explicit-any const signer = async (trx) => { if ("version" in trx) { trx.sign([wallet]); return trx; } else { trx.sign(wallet); return trx; } }; try { const swapRes = await (0, swap_sdk_1.swapFromSolana)(quote, wallet.publicKey.toString(), destAddr, referrerAddress, signer, connection, [], { skipPreflight: true }); if (!swapRes.signature) { throw new Error("Failed to execute Solana swap: No signature returned"); } if (!swapRes.serializedTrx) { throw new Error("Failed to execute Solana swap: No serialized transaction returned"); } return bs58_1.default.encode(swapRes.serializedTrx); } catch (error) { console.error("Failed to execute Solana swap:", error); throw new Error("Failed to execute Solana swap"); } } /** * @description Executes a swap on the EVM network * @param quote - The quote object containing swap details * @param destAddr - The destination address for the swap * @param privateKey - The private key of the wallet initiating the swap * @param referrerAddress - The referrer address for the swap * @returns The transaction hash of the executed swap */ async function swapEVM({ quote, destAddr, privateKey, referrerAddress, }) { const wallet = new ethers_3.Wallet(privateKey); const chainId = quote.fromChain; let chain = types_1.AccretSupportedChain.ETHEREUM_CHAIN; switch (chainId) { case "ethereum": chain = types_1.AccretSupportedChain.ETHEREUM_CHAIN; break; case "polygon": chain = types_1.AccretSupportedChain.POLYGON_CHAIN; break; case "base": chain = types_1.AccretSupportedChain.BASE_CHAIN; break; case "arbitrum": chain = types_1.AccretSupportedChain.ARBITRUM_CHAIN; break; default: throw new Error(`Unsupported EVM chain ID: ${chainId}`); } const rpcUrl = (0, getRpcUrl_1.getRpcUrl)({ chainId: chain }); const provider = new ethers_2.JsonRpcProvider(rpcUrl); const signer = wallet.connect(provider); const walletSrcAddr = await wallet.getAddress(); // Get permit or allowance for token const permit = await getErcPermitOrAllowance(quote, signer, walletSrcAddr); try { const swapRes = await (0, swap_sdk_1.swapFromEvm)(quote, walletSrcAddr, destAddr, referrerAddress, signer, permit, null, null); if (typeof swapRes === "string") { throw new Error(swapRes); } return swapRes.hash; } catch (error) { console.log("Failed to execute EVM swap:", error); throw new Error("Failed to execute EVM swap"); } } /** * @description Gets the ERC20 permit or allowance for the token * @param quote - The quote object containing swap details * @param signer - The signer for the transaction * @param walletSrcAddr - The source address of the wallet * @returns The ERC20 permit or undefined if not applicable */ async function getErcPermitOrAllowance(quote, signer, walletSrcAddr) { try { const tokenContract = new ethers_1.Contract(quote.fromToken.contract, ERC20Permit_json_1.abi, signer); const amountIn = (0, utils_1.fromReadableAmount)(quote.effectiveAmountIn64, quote.fromToken.decimals); if (quote.fromToken.supportsPermit) { const nonce = await tokenContract.nonces(walletSrcAddr); const deadline = Math.floor(Date.now() / 1000) + 60 * 10; // 10 minutes const domain = { name: await tokenContract.name(), version: "1", chainId: quote.fromToken.chainId, verifyingContract: await tokenContract.getAddress(), }; // Get domain separator const domainSeparator = await tokenContract.DOMAIN_SEPARATOR(); // Try different versions to match the domain separator for (let i = 1; i < 11; i++) { domain.version = String(i); const hash = ethers_4.TypedDataEncoder.hashDomain(domain); if (hash.toLowerCase() === domainSeparator.toLowerCase()) { break; } } let spender = swap_sdk_1.addresses.MAYAN_FORWARDER_CONTRACT; if (quote.type === "SWIFT" && quote.gasless) { const forwarderContract = new ethers_1.Contract(swap_sdk_1.addresses.MAYAN_FORWARDER_CONTRACT, MayanForwarderArtifact_1.default.abi, signer.provider); const isValidSwiftContract = await forwarderContract.mayanProtocols(quote.swiftMayanContract); if (!isValidSwiftContract) { throw new Error("Invalid Swift contract for gasless swap"); } if (!quote.swiftMayanContract) { throw new Error("Swift contract not found"); } spender = quote.swiftMayanContract; } // Permit data structure const types = { Permit: [ { name: "owner", type: "address" }, { name: "spender", type: "address" }, { name: "value", type: "uint256" }, { name: "nonce", type: "uint256" }, { name: "deadline", type: "uint256" }, ], }; const value = { owner: walletSrcAddr, spender, value: amountIn, nonce, deadline, }; // Sign the permit const signature = await signer.signTypedData(domain, types, value); const { v, r, s } = ethers_5.Signature.from(signature); return { value: amountIn, deadline, v, r, s, }; } else { // For tokens without permit, skip allowance check and go straight to approve try { // Direct approval without checking allowance first const approveTx = await tokenContract.approve(swap_sdk_1.addresses.MAYAN_FORWARDER_CONTRACT, amountIn); await approveTx.wait(); return undefined; } catch (approveError) { console.error("Error during token approval:", approveError); let errorMessage = "Failed to approve token transfer"; if (approveError && typeof approveError === "object" && "message" in approveError) { errorMessage += ": " + approveError.message; } throw new Error(errorMessage); } } } catch (error) { console.error("Error in getErcPermitOrAllowance:", error); throw error; } } //# sourceMappingURL=buildTransaction.js.map