UNPKG

@wormhole-foundation/sdk-connect

Version:

The core package for the Connect SDK, used in conjunction with 1 or more of the chain packages

142 lines 5.92 kB
import { amount, circle, contracts } from "@wormhole-foundation/sdk-base"; import { isSameToken, } from "@wormhole-foundation/sdk-definitions"; import { CircleTransfer } from "../../protocols/cctp/cctpTransfer.js"; import { TransferState } from "../../types.js"; import { Wormhole } from "../../wormhole.js"; import { AutomaticRoute } from "../route.js"; import { MinAmountError } from "../types.js"; export class AutomaticCCTPRoute extends AutomaticRoute { static NATIVE_GAS_DROPOFF_SUPPORTED = true; static meta = { name: "AutomaticCCTP", provider: "Circle", }; static supportedNetworks() { return ["Mainnet", "Testnet"]; } // get the list of chains this route supports static supportedChains(network) { if (contracts.circleContractChains.has(network)) { const circleSupportedChains = contracts.circleContractChains.get(network); return circleSupportedChains.filter((c) => { return contracts.circleContracts.get(network, c)?.wormholeRelayer; }); } return []; } // get the list of destination tokens that may be received on the destination chain static async supportedDestinationTokens(sourceToken, fromChain, toChain) { // Ensure the source token is USDC const sourceChainUsdcContract = circle.usdcContract.get(fromChain.network, fromChain.chain); if (!sourceChainUsdcContract) return []; if (!isSameToken(sourceToken, Wormhole.tokenId(fromChain.chain, sourceChainUsdcContract))) { return []; } const { network, chain } = toChain; if (!circle.usdcContract.has(network, chain)) return []; return [Wormhole.chainAddress(chain, circle.usdcContract.get(network, chain))]; } getDefaultOptions() { return { nativeGas: 0.0, }; } async validate(request, params) { try { const options = params.options ?? this.getDefaultOptions(); const normalizedParams = await this.normalizeTransferParams(request, params); const validatedParams = { normalizedParams, options, ...params, }; return { valid: true, params: validatedParams }; } catch (e) { return { valid: false, params, error: e, }; } } async quote(request, params) { try { return request.displayQuote(await CircleTransfer.quoteTransfer(request.fromChain, request.toChain, { automatic: true, amount: amount.units(params.normalizedParams.amount), nativeGas: amount.units(params.normalizedParams.nativeGasAmount), }), params); } catch (e) { return { success: false, error: e, }; } } async normalizeTransferParams(request, params) { const amt = request.parseAmount(params.amount); const ctb = await request.fromChain.getAutomaticCircleBridge(); const fee = await ctb.getRelayerFee(request.toChain.chain); const minAmount = (fee * 105n) / 100n; if (amount.units(amt) < minAmount) { throw new MinAmountError(amount.fromBaseUnits(minAmount, amt.decimals)); } const redeemableAmount = amount.units(amt) - fee; const options = params.options ?? this.getDefaultOptions(); const nativeGasPerc = options.nativeGas ?? 0.0; if (nativeGasPerc > 1.0 || nativeGasPerc < 0.0) throw new Error("Native gas must be between 0.0 and 1.0 (0% and 100%)"); let nativeGasAmount = 0n; if (nativeGasPerc > 0.0) { const dcb = await request.toChain.getAutomaticCircleBridge(); let maxSwapAmount = await dcb.maxSwapAmount(); if (redeemableAmount < maxSwapAmount) { // can't swap more than the receivable amount maxSwapAmount = redeemableAmount; } const scale = 10000; const scaledGas = BigInt(Math.floor(nativeGasPerc * scale)); // the native gas percentage is applied to the max swap amount nativeGasAmount = (maxSwapAmount * scaledGas) / BigInt(scale); if (nativeGasAmount === redeemableAmount && nativeGasAmount > 0n) { // edge case: transfer will revert if the native gas amount is equal to the redeemable amount nativeGasAmount -= 1n; } } return { fee: request.amountFromBaseUnits(fee), amount: amt, nativeGasAmount: request.amountFromBaseUnits(nativeGasAmount), }; } toTransferDetails(params, from, to) { return { from, to, amount: amount.units(params.normalizedParams.amount), automatic: true, nativeGas: amount.units(params.normalizedParams.nativeGasAmount), }; } async initiate(request, signer, quote, to) { const { params } = quote; let transfer = this.toTransferDetails(params, Wormhole.chainAddress(signer.chain(), signer.address()), to); let txids = await CircleTransfer.transfer(request.fromChain, transfer, signer); const msg = await CircleTransfer.getTransferMessage(request.fromChain, txids[txids.length - 1].txid); return { from: transfer.from.chain, to: transfer.to.chain, state: TransferState.SourceFinalized, originTxs: txids, attestation: { id: msg.id, attestation: { message: msg.message } }, }; } async *track(receipt, timeout) { yield* CircleTransfer.track(this.wh, receipt, timeout); } } //# sourceMappingURL=automatic.js.map