UNPKG

@hyperlane-xyz/sdk

Version:

The official SDK for the Hyperlane Network

127 lines 7.19 kB
import { BaseFee__factory, } from '@hyperlane-xyz/core'; import { assert, eqAddress } from '@hyperlane-xyz/utils'; import { HyperlaneDeployer, } from '../deploy/HyperlaneDeployer.js'; import { EvmTokenFeeReader } from './EvmTokenFeeReader.js'; import { evmTokenFeeFactories } from './contracts.js'; import { TokenFeeConfigSchema, TokenFeeType, } from './types.js'; export class EvmTokenFeeDeployer extends HyperlaneDeployer { multiProvider; chain; tokenFeeReader; constructor(multiProvider, chain, options = {}) { super(multiProvider, evmTokenFeeFactories, options); this.multiProvider = multiProvider; this.chain = chain; this.tokenFeeReader = new EvmTokenFeeReader(multiProvider, chain); } async deployContracts(chain, config) { const deployedContract = {}; // This is a partial HyperlaneContracts<EvmTokenFeeFactories> const parsedConfig = TokenFeeConfigSchema.parse(config); switch (parsedConfig.type) { case TokenFeeType.LinearFee: case TokenFeeType.ProgressiveFee: case TokenFeeType.RegressiveFee: deployedContract[parsedConfig.type] = await this.deployFee(chain, parsedConfig); break; case TokenFeeType.OffchainQuotedLinearFee: deployedContract[parsedConfig.type] = await this.deployOffchainQuotedLinearFee(chain, parsedConfig); break; case TokenFeeType.RoutingFee: { deployedContract[TokenFeeType.RoutingFee] = await this.deployRoutingFee(chain, parsedConfig); break; } case TokenFeeType.CrossCollateralRoutingFee: { deployedContract[TokenFeeType.CrossCollateralRoutingFee] = await this.deployCrossCollateralRoutingFee(chain, parsedConfig); break; } } return deployedContract; } async deployFee(chain, config) { let { maxFee, halfAmount } = config; if (config.type === TokenFeeType.LinearFee && config.bps && (!maxFee || !halfAmount)) { const { maxFee: calculatedMaxFee, halfAmount: calculatedHalfAmount } = this.tokenFeeReader.convertFromBps(config.bps); maxFee = calculatedMaxFee; halfAmount = calculatedHalfAmount; } return this.deployContract(chain, config.type, [ config.token, maxFee, halfAmount, config.owner, ]); } async deployOffchainQuotedLinearFee(chain, config) { let { maxFee, halfAmount } = config; if (config.bps && (!maxFee || !halfAmount)) { const derived = this.tokenFeeReader.convertFromBps(config.bps); maxFee = derived.maxFee; halfAmount = derived.halfAmount; } assert(config.quoteSigners?.length, 'At least one quote signer is required for OffchainQuotedLinearFee'); const signerAddress = await this.multiProvider.getSignerAddress(chain); const [firstSigner, ...additionalSigners] = config.quoteSigners; // addQuoteSigner is onlyOwner, so deploy with signer as temporary owner const contract = await this.deployContract(chain, TokenFeeType.OffchainQuotedLinearFee, [firstSigner, config.token, maxFee, halfAmount, signerAddress]); for (const signer of additionalSigners) { await this.multiProvider.handleTx(chain, contract.addQuoteSigner(signer, this.multiProvider.getTransactionOverrides(chain))); } if (!eqAddress(signerAddress, config.owner)) { await this.multiProvider.handleTx(chain, contract.transferOwnership(config.owner, this.multiProvider.getTransactionOverrides(chain))); } return contract; } async deployRoutingFee(chain, config) { const signerAddress = await this.multiProvider.getSignerAddress(chain); // RoutingFee.setFeeContract is onlyOwner, so we deploy with the signer as a // temporary owner to allow setup, then transfer to the configured owner. const routingFee = await this.deployContract(chain, TokenFeeType.RoutingFee, [config.token, signerAddress]); // Deploy each fee contract & set each fee for the routing fee for (const [destinationChain, feeConfig] of Object.entries(config.feeContracts)) { // Sub-fee configs inherit the routing fee's token if not explicitly set const resolvedFeeConfig = { ...feeConfig, token: feeConfig.token ?? config.token, }; const deployedFeeContract = resolvedFeeConfig.type === TokenFeeType.OffchainQuotedLinearFee ? BaseFee__factory.connect((await this.deployOffchainQuotedLinearFee(chain, resolvedFeeConfig)).address, this.multiProvider.getSigner(chain)) : await this.deployFee(chain, resolvedFeeConfig); await this.multiProvider.handleTx(chain, routingFee.setFeeContract(this.multiProvider.getDomainId(destinationChain), deployedFeeContract.address, this.multiProvider.getTransactionOverrides(chain))); } if (!eqAddress(signerAddress, config.owner)) { this.logger.debug(`Transferring ownership of RoutingFee to ${config.owner} on ${chain}`); await this.multiProvider.handleTx(chain, routingFee.transferOwnership(config.owner, this.multiProvider.getTransactionOverrides(chain))); } return routingFee; } async deployCrossCollateralRoutingFee(chain, config) { const signerAddress = await this.multiProvider.getSignerAddress(chain); const routingFee = await this.deployContract(chain, TokenFeeType.CrossCollateralRoutingFee, [signerAddress]); const destinationDomains = []; const routerKeys = []; const feeAddresses = []; for (const [destinationChain, destinationConfig] of Object.entries(config.feeContracts)) { for (const [routerKey, routerFeeConfig] of Object.entries(destinationConfig)) { const { address } = routerFeeConfig.type === TokenFeeType.OffchainQuotedLinearFee ? await this.deployOffchainQuotedLinearFee(chain, routerFeeConfig) : await this.deployFee(chain, routerFeeConfig); destinationDomains.push(this.multiProvider.getDomainId(destinationChain)); routerKeys.push(routerKey); feeAddresses.push(address); } } if (destinationDomains.length > 0) { await this.multiProvider.handleTx(chain, routingFee.setCrossCollateralRouterFeeContracts(destinationDomains, routerKeys, feeAddresses, this.multiProvider.getTransactionOverrides(chain))); } if (!eqAddress(signerAddress, config.owner)) { this.logger.debug(`Transferring ownership of CrossCollateralRoutingFee to ${config.owner} on ${chain}`); await this.multiProvider.handleTx(chain, routingFee.transferOwnership(config.owner, this.multiProvider.getTransactionOverrides(chain))); } return routingFee; } } //# sourceMappingURL=EvmTokenFeeDeployer.js.map