@accret/bridge-sdk
Version:
227 lines • 9.44 kB
JavaScript
;
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