UNPKG

@arcana/ca-sdk

Version:

Arcana Network's chain abstraction SDK for unified balance in Web3 apps

117 lines (116 loc) 5.13 kB
import Decimal from "decimal.js"; import { bn, CHAIN_IDS } from "fuels"; import { encodeFunctionData } from "viem"; import ERC20ABI from "../abi/erc20"; import { ZERO_ADDRESS } from "../constants"; import { getLogger } from "../logger"; import { convertIntent, equalFold, mulDecimals } from "../utils"; import { fetchBalances } from "../utils"; const logger = getLogger(); class BridgeQuery { constructor(input, init, switchChain, createEVMHandler, createFuelHandler, address, vscDomain, chainList, fuelAccount) { this.input = input; this.init = init; this.switchChain = switchChain; this.createEVMHandler = createEVMHandler; this.createFuelHandler = createFuelHandler; this.address = address; this.vscDomain = vscDomain; this.chainList = chainList; this.fuelAccount = fuelAccount; this.handler = null; this.exec = () => { if (!this.handler) { throw new Error("ca not applicable"); } return this.handler.process(); }; this.simulate = async () => { if (!this.handler) { throw new Error("ca not applicable"); } const response = await this.handler.buildIntent(); if (!response) { throw new Error("ca not applicable"); } return { intent: convertIntent(response.intent, response.token, this.chainList), token: response.token, }; }; } async initHandler() { if (!this.handler) { const input = this.input; await this.init(); if (input.token && input.amount && input.chainID) { const token = this.chainList.getTokenInfoBySymbol(input.chainID, input.token); if (!token) { throw new Error("Token not supported on this chain."); } const bridgeAmount = mulDecimals(input.amount, token.decimals); const balances = await fetchBalances(this.vscDomain, this.address, this.chainList); const asset = balances.assets.find(token.symbol); const currentChainBalance = asset.getBalanceOnChain(input.chainID); const assetBalance = asset.balance ?? 0; const availableBalance = mulDecimals(new Decimal(assetBalance).minus(currentChainBalance), token.decimals); logger.debug("bridge", { asset, assetBalance: assetBalance.toString(), availableBalance: availableBalance.toString(), balances, currentChainBalance: currentChainBalance.toString(), requiredBalance: bridgeAmount.toString(), }); if (availableBalance < bridgeAmount) { throw new Error("Insufficient balance"); } if (input.chainID === CHAIN_IDS.fuel.mainnet) { if (this.fuelAccount) { const tx = await this.fuelAccount.createTransfer( // Random address, since bridge won't call the final tx "0xE78655DfAd552fc3658c01bfb427b9EAb0c628F54e60b54fDA16c95aaAdE797A", bn(bridgeAmount.toString()), token.contractAddress); const handlerResponse = await this.createFuelHandler(tx, { bridge: true, gas: input.gas ?? 0n, skipTx: true, }); this.handler = handlerResponse?.handler; } else { throw new Error("Fuel connector is not set"); } } else { await this.switchChain(input.chainID); const p = { from: this.address, to: this.address, }; const isNative = equalFold(token.contractAddress, ZERO_ADDRESS); if (isNative) { p.value = `0x${bridgeAmount.toString(16)}`; input.gas = 0n; } else { p.to = token.contractAddress; p.data = encodeFunctionData({ abi: ERC20ABI, args: [this.address, BigInt(bridgeAmount.toString())], functionName: "transfer", }); } const handlerResponse = await this.createEVMHandler(p, { bridge: true, gas: input.gas ?? 0n, skipTx: true, }); this.handler = handlerResponse?.handler; } return; } throw new Error("bridge: missing params"); } } } export { BridgeQuery };