UNPKG

@wormhole-foundation/sdk-evm-tokenbridge

Version:

SDK for EVM chains, used in conjunction with @wormhole-foundation/sdk

111 lines 5.99 kB
import { isNative, serialize, toNative, nativeChainIds, toChainId, } from '@wormhole-foundation/sdk-connect'; import { EvmAddress, EvmPlatform, EvmUnsignedTransaction, WETH_CONTRACTS, addChainId, addFrom, } from '@wormhole-foundation/sdk-evm'; import { ethers_contracts } from './index.js'; import '@wormhole-foundation/sdk-evm-core'; import { EvmWormholeCore } from '@wormhole-foundation/sdk-evm-core'; export class EvmAutomaticTokenBridge { network; chain; provider; contracts; tokenBridgeRelayer; tokenBridge; core; chainId; constructor(network, chain, provider, contracts) { this.network = network; this.chain = chain; this.provider = provider; this.contracts = contracts; if (network === 'Devnet') throw new Error('AutomaticTokenBridge not supported on Devnet'); this.chainId = nativeChainIds.networkChainToNativeChainId.get(network, chain); const tokenBridgeAddress = this.contracts.tokenBridge; if (!tokenBridgeAddress) throw new Error(`Wormhole Token Bridge contract for domain ${chain} not found`); this.tokenBridge = ethers_contracts.Bridge__factory.connect(tokenBridgeAddress, provider); const relayerAddress = this.contracts.tokenBridgeRelayer; if (!relayerAddress) throw new Error(`Wormhole Token Bridge Relayer contract for domain ${chain} not found`); this.tokenBridgeRelayer = ethers_contracts.TokenBridgeRelayer__factory.connect(relayerAddress, provider); this.core = new EvmWormholeCore(network, chain, provider, contracts); } async *redeem(sender, vaa) { const senderAddr = new EvmAddress(sender).toString(); const txReq = await this.tokenBridgeRelayer.completeTransferWithRelay.populateTransaction(serialize(vaa)); yield this.createUnsignedTx(addFrom(txReq, senderAddr), 'TokenBridgeRelayer.completeTransferWithRelay'); } static async fromRpc(provider, config) { const [network, chain] = await EvmPlatform.chainFromRpc(provider); const conf = config[chain]; if (conf.network !== network) throw new Error(`Network mismatch: ${conf.network} != ${network}`); return new EvmAutomaticTokenBridge(network, chain, provider, conf.contracts); } //alternative naming: initiateTransfer async *transfer(sender, recipient, token, amount, nativeGas) { const senderAddr = new EvmAddress(sender).toString(); const recipientChainId = toChainId(recipient.chain); const messageFee = await this.core.getMessageFee(); const recipientAddress = recipient.address .toUniversalAddress() .toUint8Array(); const nativeTokenGas = nativeGas ? nativeGas : 0n; if (isNative(token)) { const txReq = await this.tokenBridgeRelayer.wrapAndTransferEthWithRelay.populateTransaction(nativeTokenGas, recipientChainId, recipientAddress, 0, // skip batching { value: amount + messageFee }); yield this.createUnsignedTx(addFrom(txReq, senderAddr), 'TokenBridgeRelayer.wrapAndTransferETHWithRelay'); } else { //TODO check for ERC-2612 (permit) support on token? const tokenAddr = new EvmAddress(token).toString(); const tokenContract = EvmPlatform.getTokenImplementation(this.provider, tokenAddr); const allowance = await tokenContract.allowance(senderAddr, this.tokenBridgeRelayer.target); if (allowance < amount) { const txReq = await tokenContract.approve.populateTransaction(this.tokenBridgeRelayer.target, amount); yield this.createUnsignedTx(addFrom(txReq, senderAddr), 'AutomaticTokenBridge.Approve'); } const txReq = await this.tokenBridgeRelayer.transferTokensWithRelay.populateTransaction(tokenAddr, amount, nativeTokenGas, recipientChainId, recipientAddress, 0, { value: messageFee }); yield this.createUnsignedTx(addFrom(txReq, senderAddr), 'TokenBridgeRelayer.TransferTokensWithRelay'); } } async getRelayerFee(destination, token) { const destChainId = toChainId(destination); const srcTokenAddress = await this.tokenAddress(token); const tokenContract = EvmPlatform.getTokenImplementation(this.provider, srcTokenAddress); const decimals = await tokenContract.decimals(); return await this.tokenBridgeRelayer.calculateRelayerFee(destChainId, srcTokenAddress, decimals); } // Return the amount of native gas that will be // received when the incoming bridge transfer is redeemed // Note: for a quote, this should be called on the destination chain async nativeTokenAmount(token, amount) { const address = await this.tokenAddress(token); return this.tokenBridgeRelayer.calculateNativeSwapAmountOut(address, amount); } async maxSwapAmount(token) { const address = await this.tokenAddress(token); return this.tokenBridgeRelayer.calculateMaxSwapAmountIn(address); } async getRegisteredTokens() { const tokens = await this.tokenBridgeRelayer.getAcceptedTokensList(); return tokens.map((address) => toNative(this.chain, address)); } async isRegisteredToken(token) { const address = await this.tokenAddress(token); return await this.tokenBridgeRelayer.isAcceptedToken(address); } async tokenAddress(token) { return isNative(token) ? await this.getWeth() : new EvmAddress(token).toString(); } async getWeth() { return WETH_CONTRACTS[this.network]?.[this.chain] ?? this.tokenBridge.WETH(); } createUnsignedTx(txReq, description, parallelizable = false) { return new EvmUnsignedTransaction(addChainId(txReq, this.chainId), this.network, this.chain, description, parallelizable); } } //# sourceMappingURL=automaticTokenBridge.js.map