diff --git a/src/dataworker/Dataworker.ts b/src/dataworker/Dataworker.ts index bc23fc220a..2a4ace6595 100644 --- a/src/dataworker/Dataworker.ts +++ b/src/dataworker/Dataworker.ts @@ -1,4 +1,3 @@ -import assert from "assert"; import { utils as ethersUtils } from "ethers"; import { winston, @@ -9,8 +8,6 @@ import { MerkleTree, sortEventsAscending, isDefined, - buildPoolRebalanceLeafTree, - updateTotalRefundAmountRaw, toBNWei, getFillsInRange, ZERO_ADDRESS, @@ -19,8 +16,6 @@ import { DepositWithBlock, FillsToRefund, FillWithBlock, - isUbaOutflow, - outflowIsFill, ProposedRootBundle, RootBundleRelayWithBlock, SlowFillLeaf, @@ -32,7 +27,7 @@ import { RelayerRefundLeaf, } from "../interfaces"; import { DataworkerClients } from "./DataworkerClientHelper"; -import { SpokePoolClient, UBAClient, BalanceAllocator } from "../clients"; +import { SpokePoolClient, BalanceAllocator } from "../clients"; import * as PoolRebalanceUtils from "./PoolRebalanceUtils"; import { blockRangesAreInvalidForSpokeClients, @@ -334,8 +329,7 @@ export class Dataworker { spokePoolClients: { [chainId: number]: SpokePoolClient }, usdThresholdToSubmitNewBundle?: BigNumber, submitProposals = true, - earliestBlocksInSpokePoolClients: { [chainId: number]: number } = {}, - ubaClient?: UBAClient + earliestBlocksInSpokePoolClients: { [chainId: number]: number } = {} ): Promise { // TODO: Handle the case where we can't get event data or even blockchain data from any chain. This will require // some changes to override the bundle block range here, and _loadData to skip chains with zero block ranges. @@ -352,28 +346,24 @@ export class Dataworker { } const { chainId: hubPoolChainId, latestBlockSearched } = this.clients.hubPoolClient; - const [mainnetBundleStartBlock, mainnetBundleEndBlock] = getBlockRangeForChain( + const mainnetBundleEndBlock = getBlockRangeForChain( blockRangesForProposal, hubPoolChainId, this.chainIdListForBundleEvaluationBlockNumbers - ); - - const isUBA = sdk.clients.isUBAActivatedAtBlock( - this.clients.hubPoolClient, - mainnetBundleStartBlock, - hubPoolChainId - ); - - const rootBundleDataProducer = isUBA - ? this.UBA_proposeRootBundle(blockRangesForProposal, ubaClient, spokePoolClients, true) - : this.Legacy_proposeRootBundle(blockRangesForProposal, spokePoolClients, latestBlockSearched, true); + )[1]; this.logger.debug({ at: "Dataworker#propose", - message: `Proposing ${isUBA ? "UBA" : "Legacy"} root bundle`, + message: "Proposing root bundle", blockRangesForProposal, }); - const rootBundleData = { ...(await rootBundleDataProducer) }; + const logData = true; + const rootBundleData = await this._proposeRootBundle( + blockRangesForProposal, + spokePoolClients, + latestBlockSearched, + logData + ); if (usdThresholdToSubmitNewBundle !== undefined) { // Exit early if volume of pool rebalance leaves exceeds USD threshold. Volume includes netSendAmounts only since @@ -441,7 +431,7 @@ export class Dataworker { slowRelayRoot: rootBundleData.slowFillTree.getHexRoot(), }); if (submitProposals) { - this._proposeRootBundle( + this.enqueueRootBundleProposal( hubPoolChainId, blockRangesForProposal, rootBundleData.poolRebalanceLeaves, @@ -454,7 +444,7 @@ export class Dataworker { } } - async Legacy_proposeRootBundle( + async _proposeRootBundle( blockRangesForProposal: number[][], spokePoolClients: SpokePoolClientsByChain, latestMainnetBundleEndBlock: number, @@ -532,285 +522,10 @@ export class Dataworker { }; } - async UBA_proposeRootBundle( - blockRangesForProposal: number[][], - ubaClient: UBAClient, - spokePoolClients: SpokePoolClientsByChain, - logData = false - ): Promise { - if (!ubaClient) { - throw new Error("UBA_proposeRootBundle#Undefined UBA client"); - } - const timerStart = Date.now(); - const enabledChainIds = Object.keys(spokePoolClients) - .filter((chainId) => { - const blockRangeForChain = getBlockRangeForChain( - blockRangesForProposal, - Number(chainId), - this.chainIdListForBundleEvaluationBlockNumbers - ); - return !PoolRebalanceUtils.isChainDisabled(blockRangeForChain); - }) - .map((x) => Number(x)); - - const { poolRebalanceLeaves } = this._UBA_buildPoolRebalanceLeaves( - blockRangesForProposal, - enabledChainIds, - ubaClient - ); - const poolRebalanceTree = buildPoolRebalanceLeafTree(poolRebalanceLeaves); - - // Load data for slow fill and relayer refund leaves. Set UBA mode to true to include - // refund requests in the fills to refund list. - const { fillsToRefund, unfilledDeposits } = await this.clients.bundleDataClient._loadData( - blockRangesForProposal, - ubaClient.spokePoolClients, - true, - logData - ); - // Build RelayerRefundRoot: - // 1. Get all fills in range from SpokePoolClient - // 2. Get all flows from UBA Client - // 3. Validate fills by matching them with a deposit flow. Partial and Full fills should be validated the same (?) - // 4. Validate refunds by matching them with a refund flow and checking that they were the first refund. - const relayerRefundTree = this._UBA_buildRelayerRefundLeaves( - fillsToRefund, - poolRebalanceLeaves, - blockRangesForProposal, - enabledChainIds, - ubaClient - ); - - // Build SlowRelayRoot: - // 1. Get all initial partial fills in range from SpokePoolClient that weren't later fully filled. - // 2. Get all flows from UBA Client - // 3. Validate fills by matching them with a deposit flow. - const slowFillTree = this._UBA_buildSlowRelayLeaves(ubaClient, blockRangesForProposal, unfilledDeposits); - - if (logData) { - this.logger.debug({ - at: "Dataworker", - message: `Time to build root bundles for block ranges ${JSON.stringify(blockRangesForProposal)} : ${ - Date.now() - timerStart - }ms`, - }); - PoolRebalanceUtils.prettyPrintLeaves(this.logger, poolRebalanceTree, poolRebalanceLeaves, "Pool rebalance"); - PoolRebalanceUtils.prettyPrintLeaves( - this.logger, - relayerRefundTree.tree, - relayerRefundTree.leaves, - "Relayer refund" - ); - PoolRebalanceUtils.prettyPrintLeaves(this.logger, slowFillTree.tree, slowFillTree.leaves, "Slow relay"); - } - - return { - poolRebalanceLeaves, - poolRebalanceTree, - relayerRefundLeaves: relayerRefundTree.leaves, - relayerRefundTree: relayerRefundTree.tree, - slowFillLeaves: slowFillTree.leaves, - slowFillTree: slowFillTree.tree, - }; - } - - /** - * Builds pool rebalance leaves for the given block ranges and enabled chains. - * @param blockRanges Marks the event range that should be used to form pool rebalance leaf data - * @param enabledChainIds Chains that we should create pool rebalance leaves for - * @param ubaClient - * @returns pool rebalance leaves to propose for `blockRanges` - */ - _UBA_buildPoolRebalanceLeaves( - blockRanges: number[][], - enabledChainIds: number[], - ubaClient: UBAClient - ): { poolRebalanceLeaves: PoolRebalanceLeaf[]; runningBalances: RunningBalances } { - const mainnetBundleEndBlock = getBlockRangeForChain( - blockRanges, - this.clients.hubPoolClient.chainId, - this.chainIdListForBundleEvaluationBlockNumbers - )[1]; - - // Build PoolRebalanceRoot: - // 1. Get all flows in range from UBA Client - // 2. Set running balances to closing running balances from latest flows in range per token per chain - // 3. Set bundleLpFees to sum of SystemFee.LPFee for all flows in range per token per chain - // 4. Set netSendAmount to sum of netRunningBalanceAdjustments for all flows in range per token per chain - const poolRebalanceLeafData: { - runningBalances: RunningBalances; - bundleLpFees: RunningBalances; - incentivePoolBalances: RunningBalances; - netSendAmounts: RunningBalances; - } = { - runningBalances: {}, - bundleLpFees: {}, - incentivePoolBalances: {}, - netSendAmounts: {}, - }; - for (const chainId of enabledChainIds) { - const blockRangeForChain = getBlockRangeForChain( - blockRanges, - Number(chainId), - this.chainIdListForBundleEvaluationBlockNumbers - ); - poolRebalanceLeafData.runningBalances[chainId] = {}; - poolRebalanceLeafData.bundleLpFees[chainId] = {}; - poolRebalanceLeafData.incentivePoolBalances[chainId] = {}; - poolRebalanceLeafData.netSendAmounts[chainId] = {}; - for (const tokenSymbol of ubaClient.tokens) { - const l1TokenAddress = this.clients.hubPoolClient - .getL1Tokens() - .find((l1Token) => l1Token.symbol === tokenSymbol)?.address; - if (!l1TokenAddress) { - throw new Error("Could not find l1 token address for token symbol: " + tokenSymbol); - } - - const flowsForChain = ubaClient.getModifiedFlows( - Number(chainId), - tokenSymbol, - blockRangeForChain[0], - blockRangeForChain[1] - ); - - // If no flows for chain, we won't create a pool rebalance leaf for it. The next time there is a flow for this - // chain, we'll find the previous running balance for it and use that as the starting point. - if (flowsForChain.length > 0) { - const closingRunningBalance = flowsForChain[flowsForChain.length - 1].runningBalance; - const closingIncentiveBalance = flowsForChain[flowsForChain.length - 1].incentiveBalance; - const bundleLpFees = flowsForChain.reduce((sum, flow) => sum.add(flow.lpFee), BigNumber.from(0)); - const netSendAmount = flowsForChain[flowsForChain.length - 1].netRunningBalanceAdjustment; - poolRebalanceLeafData.runningBalances[chainId][l1TokenAddress] = closingRunningBalance; - poolRebalanceLeafData.bundleLpFees[chainId][l1TokenAddress] = bundleLpFees; - poolRebalanceLeafData.incentivePoolBalances[chainId][l1TokenAddress] = closingIncentiveBalance; - poolRebalanceLeafData.netSendAmounts[chainId][l1TokenAddress] = netSendAmount; - } - } - } - const poolRebalanceLeaves = PoolRebalanceUtils.constructPoolRebalanceLeaves( - mainnetBundleEndBlock, - poolRebalanceLeafData.runningBalances, - poolRebalanceLeafData.bundleLpFees, - this.clients.configStoreClient, - this.maxL1TokenCountOverride, - poolRebalanceLeafData.incentivePoolBalances, - poolRebalanceLeafData.netSendAmounts, - true - ); - const runningBalances = poolRebalanceLeafData.runningBalances; - return { - poolRebalanceLeaves, - runningBalances, - }; - } - - /** - * Builds relayer refund leaves for the given block ranges and enabled chains. - * @param poolRebalanceLeaves Used to determine how to set amountToReturn in relayer refund leaves - * @param runningBalances - * @param blockRanges - * @returns - */ - _UBA_buildRelayerRefundLeaves( - fillsToRefund: FillsToRefund, - poolRebalanceLeaves: PoolRebalanceLeaf[], - blockRanges: number[][], - enabledChainIds: number[], - ubaClient: UBAClient - ): { leaves: RelayerRefundLeaf[]; tree: MerkleTree } { - const hubPoolChainId = this.clients.hubPoolClient.chainId; - const mainnetBundleEndBlock = getBlockRangeForChain( - blockRanges, - hubPoolChainId, - this.chainIdListForBundleEvaluationBlockNumbers - )[1]; - - // Create roots using constructed block ranges. - const timerStart = Date.now(); - this.logger.debug({ - at: "Dataworker", - message: `Time to load data from BundleDataClient: ${Date.now() - timerStart}ms`, - }); - - // Go through all flows in range. For each outflow, add the refund balancing fee to the refund entry - // of the relayer. - for (const chainId of enabledChainIds) { - const [startBlock, endBlock] = getBlockRangeForChain( - blockRanges, - Number(chainId), - this.chainIdListForBundleEvaluationBlockNumbers - ); - for (const tokenSymbol of ubaClient.tokens) { - const flowsForChain = ubaClient.getModifiedFlows(Number(chainId), tokenSymbol, startBlock, endBlock); - flowsForChain.forEach(({ flow, balancingFee }) => { - // All flows in here are assumed to be valid, so we can use the flow's - // repayment chain to pay out the refund. But we need to check which - // token should be repaid in. - if (isUbaOutflow(flow)) { - assert(outflowIsFill(flow)); - const refundToken = flow.destinationToken; - updateTotalRefundAmountRaw(fillsToRefund, balancingFee, flow.repaymentChainId, flow.relayer, refundToken); - } - }); - } - } - const relayerRefundRoot = _buildRelayerRefundRoot( - mainnetBundleEndBlock, - fillsToRefund, - poolRebalanceLeaves, - {}, // runningBalancess unused in UBA model. - this.clients, - this.maxRefundCountOverride - ? this.maxRefundCountOverride - : this.clients.configStoreClient.getMaxRefundCountForRelayerRefundLeafForBlock(mainnetBundleEndBlock), - true // Instruct function to always set amountToReturn = -netSendAmount iff netSendAmount < 0 - ); - return relayerRefundRoot; - } - - _UBA_buildSlowRelayLeaves( - ubaClient: UBAClient, - blockRanges: number[][], - unfilledDeposits: UnfilledDeposit[] - ): { leaves: SlowFillLeaf[]; tree: MerkleTree } { - // In UBA mode, each unfilled deposit must be matched with a payout adjustment percent, - // which is set equal to the refund balancing fee for the partial fill that triggered the slow fill. - const unfilledDepositsWithPayoutAdjustmentPcts: UnfilledDeposit[] = unfilledDeposits.map( - (unfilledDeposit: UnfilledDeposit) => { - const deposit = unfilledDeposit.deposit; - const destinationChainId = deposit.destinationChainId; - const [startBlock, endBlock] = getBlockRangeForChain( - blockRanges, - destinationChainId, - this.chainIdListForBundleEvaluationBlockNumbers - ); - const tokenSymbol = this.clients.hubPoolClient.getL1TokenInfoForL2Token( - deposit.destinationToken, - destinationChainId - ).symbol; - // There should only be one outflow on the deposit.destinationchain matching this deposit ID. - const matchingOutflow = ubaClient - .getModifiedFlows(destinationChainId, tokenSymbol, startBlock, endBlock) - .find((flow) => flow.flow.depositId === deposit.depositId); - if (!matchingOutflow || !matchingOutflow?.balancingFee) { - throw new Error(`No matching outflow with refund balancing fee found for deposit ID ${deposit.depositId}`); - } - return { - ...unfilledDeposit, - relayerBalancingFee: matchingOutflow.balancingFee, - }; - } - ); - const slowRelayRoot = _buildSlowRelayRoot(unfilledDepositsWithPayoutAdjustmentPcts); - - return slowRelayRoot; - } - async validatePendingRootBundle( spokePoolClients: { [chainId: number]: SpokePoolClient }, submitDisputes = true, - earliestBlocksInSpokePoolClients: { [chainId: number]: number } = {}, - ubaClient?: UBAClient + earliestBlocksInSpokePoolClients: { [chainId: number]: number } = {} ): Promise { if (!this.clients.hubPoolClient.isUpdated || this.clients.hubPoolClient.currentTime === undefined) { throw new Error("HubPoolClient not updated"); @@ -858,8 +573,7 @@ export class Dataworker { widestPossibleExpectedBlockRange, pendingRootBundle, spokePoolClients, - earliestBlocksInSpokePoolClients, - ubaClient + earliestBlocksInSpokePoolClients ); if (!valid) { // In the case where the Dataworker config is improperly configured, emit an error level alert so bot runner @@ -894,8 +608,7 @@ export class Dataworker { widestPossibleExpectedBlockRange: number[][], rootBundle: PendingRootBundle, spokePoolClients: { [chainId: number]: SpokePoolClient }, - earliestBlocksInSpokePoolClients: { [chainId: number]: number }, - ubaClient?: UBAClient + earliestBlocksInSpokePoolClients: { [chainId: number]: number } ): Promise< // If valid is false, we get a reason and we might get expected trees. | { @@ -1093,7 +806,6 @@ export class Dataworker { }; } - let rootBundleData: ProposeRootBundleReturnType; const mainnetBundleStartBlock = getBlockRangeForChain( blockRangesImpliedByBundleEndBlocks, hubPoolChainId, @@ -1112,37 +824,14 @@ export class Dataworker { ); } - let isUBA = false; - if ( - sdk.clients.isUBAActivatedAtBlock( - this.clients.hubPoolClient, - mainnetBundleStartBlock, - this.clients.hubPoolClient.chainId - ) - ) { - isUBA = true; - } - if (!isUBA) { - const _rootBundleData = await this.Legacy_proposeRootBundle( - blockRangesImpliedByBundleEndBlocks, - spokePoolClients, - rootBundle.proposalBlockNumber, - true - ); - rootBundleData = { - ..._rootBundleData, - }; - } else { - const _rootBundleData = await this.UBA_proposeRootBundle( - blockRangesImpliedByBundleEndBlocks, - ubaClient, - spokePoolClients, - true - ); - rootBundleData = { - ..._rootBundleData, - }; - } + const logData = true; + const rootBundleData = await this._proposeRootBundle( + blockRangesImpliedByBundleEndBlocks, + spokePoolClients, + rootBundle.proposalBlockNumber, + logData + ); + const expectedPoolRebalanceRoot = { leaves: rootBundleData.poolRebalanceLeaves, tree: rootBundleData.poolRebalanceTree, @@ -1238,8 +927,7 @@ export class Dataworker { spokePoolClients: { [chainId: number]: SpokePoolClient }, balanceAllocator: BalanceAllocator = new BalanceAllocator(spokePoolClientsToProviders(spokePoolClients)), submitExecution = true, - earliestBlocksInSpokePoolClients: { [chainId: number]: number } = {}, - ubaClient?: UBAClient + earliestBlocksInSpokePoolClients: { [chainId: number]: number } = {} ): Promise { this.logger.debug({ at: "Dataworker#executeSlowRelayLeaves", @@ -1332,38 +1020,12 @@ export class Dataworker { continue; } - let rootBundleData: ProposeRootBundleReturnType; - - const mainnetBundleStartBlock = getBlockRangeForChain( + const rootBundleData = await this._proposeRootBundle( blockNumberRanges, - this.clients.hubPoolClient.chainId, - this.chainIdListForBundleEvaluationBlockNumbers - )[0]; - let isUBA = false; - if ( - sdk.clients.isUBAActivatedAtBlock( - this.clients.hubPoolClient, - mainnetBundleStartBlock, - this.clients.hubPoolClient.chainId - ) - ) { - isUBA = true; - } - if (!isUBA) { - const _rootBundleData = await this.Legacy_proposeRootBundle( - blockNumberRanges, - spokePoolClients, - matchingRootBundle.blockNumber - ); - rootBundleData = { - ..._rootBundleData, - }; - } else { - const _rootBundleData = await this.UBA_proposeRootBundle(blockNumberRanges, ubaClient, spokePoolClients); - rootBundleData = { - ..._rootBundleData, - }; - } + spokePoolClients, + matchingRootBundle.blockNumber + ); + const { slowFillLeaves: leaves, slowFillTree: tree } = rootBundleData; if (tree.getHexRoot() !== rootBundleRelay.slowRelayRoot) { this.logger.warn({ @@ -1582,8 +1244,7 @@ export class Dataworker { spokePoolClients: { [chainId: number]: SpokePoolClient }, balanceAllocator: BalanceAllocator = new BalanceAllocator(spokePoolClientsToProviders(spokePoolClients)), submitExecution = true, - earliestBlocksInSpokePoolClients: { [chainId: number]: number } = {}, - ubaClient?: UBAClient + earliestBlocksInSpokePoolClients: { [chainId: number]: number } = {} ): Promise { this.logger.debug({ at: "Dataworker#executePoolRebalanceLeaves", @@ -1625,8 +1286,7 @@ export class Dataworker { widestPossibleExpectedBlockRange, pendingRootBundle, spokePoolClients, - earliestBlocksInSpokePoolClients, - ubaClient + earliestBlocksInSpokePoolClients ); if (!valid) { @@ -1929,8 +1589,7 @@ export class Dataworker { spokePoolClients: { [chainId: number]: SpokePoolClient }, balanceAllocator: BalanceAllocator = new BalanceAllocator(spokePoolClientsToProviders(spokePoolClients)), submitExecution = true, - earliestBlocksInSpokePoolClients: { [chainId: number]: number } = {}, - ubaClient?: UBAClient + earliestBlocksInSpokePoolClients: { [chainId: number]: number } = {} ): Promise { const { configStoreClient, hubPoolClient } = this.clients; this.logger.debug({ at: "Dataworker#executeRelayerRefundLeaves", message: "Executing relayer refund leaves" }); @@ -2015,39 +1674,11 @@ export class Dataworker { continue; } - let rootBundleData: ProposeRootBundleReturnType; - - const mainnetBundleStartBlock = getBlockRangeForChain( + const { relayerRefundLeaves: leaves, relayerRefundTree: tree } = await this._proposeRootBundle( blockNumberRanges, - hubPoolClient.chainId, - this.chainIdListForBundleEvaluationBlockNumbers - )[0]; - let isUBA = false; - if ( - sdk.clients.isUBAActivatedAtBlock( - this.clients.hubPoolClient, - mainnetBundleStartBlock, - this.clients.hubPoolClient.chainId - ) - ) { - isUBA = true; - } - if (!isUBA) { - const _rootBundleData = await this.Legacy_proposeRootBundle( - blockNumberRanges, - spokePoolClients, - matchingRootBundle.blockNumber - ); - rootBundleData = { - ..._rootBundleData, - }; - } else { - const _rootBundleData = await this.UBA_proposeRootBundle(blockNumberRanges, ubaClient, spokePoolClients); - rootBundleData = { - ..._rootBundleData, - }; - } - const { relayerRefundLeaves: leaves, relayerRefundTree: tree } = rootBundleData; + spokePoolClients, + matchingRootBundle.blockNumber + ); if (tree.getHexRoot() !== rootBundleRelay.relayerRefundRoot) { this.logger.warn({ @@ -2172,7 +1803,7 @@ export class Dataworker { }); } - _proposeRootBundle( + enqueueRootBundleProposal( hubPoolChainId: number, bundleBlockRange: number[][], poolRebalanceLeaves: PoolRebalanceLeaf[], diff --git a/src/dataworker/index.ts b/src/dataworker/index.ts index 30daefedfb..c1f0ad5f98 100644 --- a/src/dataworker/index.ts +++ b/src/dataworker/index.ts @@ -1,12 +1,4 @@ -import { - processEndPollingLoop, - winston, - config, - startupLogLevel, - Signer, - disconnectRedisClients, - getRedisCache, -} from "../utils"; +import { processEndPollingLoop, winston, config, startupLogLevel, Signer, disconnectRedisClients } from "../utils"; import { spokePoolClientsToProviders } from "../common"; import { Dataworker } from "./Dataworker"; import { DataworkerConfig } from "./DataworkerConfig"; @@ -17,8 +9,6 @@ import { DataworkerClients, } from "./DataworkerClientHelper"; import { BalanceAllocator } from "../clients/BalanceAllocator"; -import { UBAClient } from "../clients/UBAClient"; -import { utils as sdkUtils, clients as sdkClients } from "@across-protocol/sdk-v2"; config(); let logger: winston.Logger; @@ -109,27 +99,9 @@ export async function runDataworker(_logger: winston.Logger, baseSigner: Signer) toBlocks ); - const ubaClient = new UBAClient( - // @dev: Consider customizing this config when using the UBAClient in prod. - new sdkClients.UBAClientConfig(), - clients.hubPoolClient.getL1Tokens().map((token) => token.symbol), - clients.hubPoolClient, - spokePoolClients, - await getRedisCache(logger) - ); - const version = clients.configStoreClient.getConfigStoreVersionForTimestamp(); - if (sdkUtils.isUBA(version)) { - await ubaClient.update(); - } - // Validate and dispute pending proposal before proposing a new one if (config.disputerEnabled) { - await dataworker.validatePendingRootBundle( - spokePoolClients, - config.sendingDisputesEnabled, - fromBlocks, - ubaClient - ); + await dataworker.validatePendingRootBundle(spokePoolClients, config.sendingDisputesEnabled, fromBlocks); } else { logger[startupLogLevel(config)]({ at: "Dataworker#index", message: "Disputer disabled" }); } @@ -139,8 +111,7 @@ export async function runDataworker(_logger: winston.Logger, baseSigner: Signer) spokePoolClients, config.rootBundleExecutionThreshold, config.sendingProposalsEnabled, - fromBlocks, - ubaClient + fromBlocks ); } else { logger[startupLogLevel(config)]({ at: "Dataworker#index", message: "Proposer disabled" }); @@ -153,8 +124,7 @@ export async function runDataworker(_logger: winston.Logger, baseSigner: Signer) spokePoolClients, balanceAllocator, config.sendingExecutionsEnabled, - fromBlocks, - ubaClient + fromBlocks ); // Execute slow relays before relayer refunds to give them priority for any L2 funds. @@ -162,15 +132,13 @@ export async function runDataworker(_logger: winston.Logger, baseSigner: Signer) spokePoolClients, balanceAllocator, config.sendingExecutionsEnabled, - fromBlocks, - ubaClient + fromBlocks ); await dataworker.executeRelayerRefundLeaves( spokePoolClients, balanceAllocator, config.sendingExecutionsEnabled, - fromBlocks, - ubaClient + fromBlocks ); } else { logger[startupLogLevel(config)]({ at: "Dataworker#index", message: "Executor disabled" }); diff --git a/test/Dataworker.buildRoots.ts b/test/Dataworker.buildRoots.ts index c6bd51be66..3a5c7c9c63 100644 --- a/test/Dataworker.buildRoots.ts +++ b/test/Dataworker.buildRoots.ts @@ -1,5 +1,5 @@ import { ConfigStoreClient, HubPoolClient, SpokePoolClient } from "../src/clients"; -import { Deposit, DepositWithBlock, Fill, RunningBalances } from "../src/interfaces"; +import { Deposit, Fill, RunningBalances } from "../src/interfaces"; import { EMPTY_MERKLE_ROOT, compareAddresses, @@ -35,7 +35,6 @@ import { buildSlowRelayTree, constructPoolRebalanceTree, createSpyLogger, - deepEqualsWithBigNumber, deployNewTokenMapping, enableRoutesOnHubPool, ethers, @@ -51,14 +50,13 @@ import { // Tested import { Dataworker } from "../src/dataworker/Dataworker"; -import { MockUBAClient } from "./mocks"; let spokePool_1: Contract, erc20_1: Contract, spokePool_2: Contract, erc20_2: Contract; let l1Token_1: Contract, hubPool: Contract, timer: Contract, configStore: Contract; let depositor: SignerWithAddress, relayer: SignerWithAddress, dataworker: SignerWithAddress; -let hubPoolClient: HubPoolClient, configStoreClient: ConfigStoreClient, spokePoolClient_1: SpokePoolClient; -let dataworkerInstance: Dataworker, spokePoolClient_2: SpokePoolClient; +let hubPoolClient: HubPoolClient, configStoreClient: ConfigStoreClient; +let dataworkerInstance: Dataworker; let spokePoolClients: { [chainId: number]: SpokePoolClient }; let spy: sinon.SinonSpy; @@ -85,8 +83,6 @@ describe("Dataworker: Build merkle roots", async function () { timer, spokePoolClients, spy, - spokePoolClient_1, - spokePoolClient_2, updateAllClients, } = fastDataworkerResult); }); @@ -1269,610 +1265,4 @@ describe("Dataworker: Build merkle roots", async function () { expect(merkleRoot2.leaves).excludingEvery(["groupIndex", "leafId"]).to.deep.equal(expectedLeaves2); }); }); - describe("UBA Root Bundles", function () { - let ubaClient: MockUBAClient; - const l1TokenSymbol = "L1Token1"; - beforeEach(async function () { - await updateAllClients(); - ubaClient = new MockUBAClient([l1TokenSymbol], hubPoolClient, spokePoolClients); - }); - describe("Build pool rebalance root", function () { - it("> 0 flows", async function () { - // Test that the running balance, incentive balance, and the net running balance adjustment of only - // the latest flow are used, since these values are accumulated by the UBA client. So, add two - // flows. - ubaClient.setFlows(originChainId, l1TokenSymbol, [ - { - flow: { - ...spokePoolClient_1.getFills()[0], - matchedDeposit: {} as unknown as DepositWithBlock, - }, - runningBalance: toBNWei("2"), - incentiveBalance: toBNWei("2"), - netRunningBalanceAdjustment: toBNWei("1"), - lpFee: toBNWei("1"), - balancingFee: toBNWei("0.2"), - }, - { - flow: { - ...spokePoolClient_1.getFills()[0], - matchedDeposit: {} as unknown as DepositWithBlock, - }, - runningBalance: toBNWei("1"), - incentiveBalance: toBNWei("1"), - netRunningBalanceAdjustment: toBNWei("2"), - lpFee: toBNWei("1"), - balancingFee: toBNWei("0.2"), - }, - ]); - - const blockRanges = dataworkerInstance._getNextProposalBlockRanges(spokePoolClients); - if (!blockRanges) { - throw new Error("Can't propose new bundle"); - } - const { poolRebalanceLeaves } = dataworkerInstance._UBA_buildPoolRebalanceLeaves( - blockRanges, - [originChainId, destinationChainId], - ubaClient - ); - // The running balance, incentive balance, and net running balance adjustment are not accumulated from - // all flows, only the latest values are used. - expect( - deepEqualsWithBigNumber(poolRebalanceLeaves[0], { - chainId: originChainId, - // Sum of LP fees in flows - bundleLpFees: [toBNWei("2")], - // Last flow's net running balance amount - netSendAmounts: [toBNWei("2")], - // Last flow's running balance concatenated with last flow's incentive balance - runningBalances: [toBNWei("1"), toBNWei("1")], - groupIndex: 0, - leafId: 0, - l1Tokens: [l1Token_1.address], - }) - ).to.be.true; - }); - it("0 flows", async function () { - const blockRanges = dataworkerInstance._getNextProposalBlockRanges(spokePoolClients); - if (!blockRanges) { - throw new Error("Can't propose new bundle"); - } - const { poolRebalanceLeaves } = dataworkerInstance._UBA_buildPoolRebalanceLeaves( - blockRanges, - [originChainId, destinationChainId], - ubaClient - ); - expect(poolRebalanceLeaves.length).to.be.equal(0); - }); - }); - describe("Build relayer refund root", function () { - it("amountToReturn is 0", async function () { - // Set spoke target balance thresholds above deposit amounts so that amountToReturn is always 0. - await configStore.updateTokenConfig( - l1Token_1.address, - JSON.stringify({ - rateModel: sampleRateModel, - spokeTargetBalances: { - [originChainId]: { - // Threshold above the deposit amount. - threshold: amountToDeposit.mul(10).toString(), - target: amountToDeposit.div(2).toString(), - }, - [destinationChainId]: { - // Threshold above the deposit amount. - threshold: amountToDeposit.mul(10).toString(), - target: amountToDeposit.div(2).toString(), - }, - }, - }) - ); - await updateAllClients(); - // No UBA flows in this test so all amounts to return will be 0 - const { poolRebalanceLeaves } = dataworkerInstance._UBA_buildPoolRebalanceLeaves( - getDefaultBlockRange(0), - [originChainId, destinationChainId], - ubaClient - ); - const data1 = await dataworkerInstance.clients.bundleDataClient._loadData( - getDefaultBlockRange(0), - spokePoolClients, - true - ); - expect( - dataworkerInstance._UBA_buildRelayerRefundLeaves( - data1.fillsToRefund, - poolRebalanceLeaves, - getDefaultBlockRange(0), - [originChainId, destinationChainId], - ubaClient - ).leaves - ).to.deep.equal([]); - - // Submit deposits for multiple L2 tokens. - const deposit1 = await buildDeposit( - hubPoolClient, - spokePool_1, - erc20_1, - l1Token_1, - depositor, - destinationChainId, - amountToDeposit - ); - const deposit2 = await buildDeposit( - hubPoolClient, - spokePool_1, - erc20_1, - l1Token_1, - depositor, - destinationChainId, - amountToDeposit - ); - - // Submit fills for two relayers on one repayment chain and one destination token. Note: we know that - // depositor address is alphabetically lower than relayer address, so submit fill from depositor first and test - // that data worker sorts on refund address. - await updateAllClients(); - await buildFillForRepaymentChain(spokePool_2, depositor, deposit2, 0.25, destinationChainId); - await buildFillForRepaymentChain(spokePool_2, depositor, deposit2, 1, destinationChainId); - await buildFillForRepaymentChain(spokePool_2, relayer, deposit1, 0.25, destinationChainId); - await buildFillForRepaymentChain(spokePool_2, relayer, deposit1, 1, destinationChainId); - - const depositorBeforeRelayer = toBN(depositor.address).lt(toBN(relayer.address)); - const leaf1 = { - amountToReturn: toBN(0), - chainId: destinationChainId, - refundAmounts: [ - getRefund(deposit1.amount, deposit1.realizedLpFeePct), - getRefund(deposit2.amount, deposit2.realizedLpFeePct), - ], // Refund amounts should aggregate across all fills. - leafId: 0, - l2TokenAddress: erc20_2.address, - refundAddresses: [ - depositorBeforeRelayer ? depositor.address : relayer.address, - depositorBeforeRelayer ? relayer.address : depositor.address, - ], // Sorted ascending alphabetically - }; - - await updateAllClients(); - const data2 = await dataworkerInstance.clients.bundleDataClient._loadData( - getDefaultBlockRange(1), - spokePoolClients, - true - ); - const relayerRefundLeaves1 = dataworkerInstance._UBA_buildRelayerRefundLeaves( - data2.fillsToRefund, - poolRebalanceLeaves, - getDefaultBlockRange(1), - [originChainId, destinationChainId], - ubaClient - ); - expect(relayerRefundLeaves1.leaves.length).to.equal(1); - expect(relayerRefundLeaves1.leaves[0].amountToReturn).to.deep.equal(leaf1.amountToReturn); - relayerRefundLeaves1.leaves[0].refundAmounts.forEach((refundAmount, i) => { - expect(refundAmount).to.equal(leaf1.refundAmounts[i]); - }); - - // Splits leaf into multiple leaves if refunds > MAX_REFUNDS_PER_RELAYER_REFUND_LEAF. - const deposit4 = await buildDeposit( - hubPoolClient, - spokePool_1, - erc20_1, - l1Token_1, - depositor, - destinationChainId, - amountToDeposit - ); - // @dev: slice(10) so we don't have duplicates between allSigners and depositor/relayer/etc. - const allSigners: SignerWithAddress[] = (await ethers.getSigners()).slice(10); - expect( - allSigners.length >= MAX_REFUNDS_PER_RELAYER_REFUND_LEAF + 1, - "ethers.getSigners doesn't have enough signers" - ).to.be.true; - for (let i = 0; i < MAX_REFUNDS_PER_RELAYER_REFUND_LEAF + 1; i++) { - await setupTokensForWallet(spokePool_2, allSigners[i], [erc20_2]); - await buildFillForRepaymentChain(spokePool_2, allSigners[i], deposit4, 0.01 + i * 0.01, destinationChainId); - } - // Note: Higher refund amounts for same chain and L2 token should come first, so we test that by increasing - // the fill amount in the above loop for each fill. Ultimately, the latest fills send the most tokens and - // should come in the first leaf. - await updateAllClients(); - const data4 = await dataworkerInstance.clients.bundleDataClient._loadData( - getDefaultBlockRange(3), - spokePoolClients, - true - ); - const relayerRefundLeaves3 = dataworkerInstance._UBA_buildRelayerRefundLeaves( - data4.fillsToRefund, - poolRebalanceLeaves, - getDefaultBlockRange(3), - [originChainId, destinationChainId], - ubaClient - ); - expect(relayerRefundLeaves3.leaves.length).to.equal(2); - - // The order should be: - // - Sort by repayment chain ID in ascending order, so leaf2 goes first since its the only one with an overridden - // repayment chain ID. - // - Sort by refund amount. So, the refund addresses in leaf1 go first, then the latest refunds. Each leaf can - // have a maximum number of refunds so add the latest refund (recall the latest fills from the last loop - // were the largest). - leaf1.refundAddresses.push(allSigners[3].address); - leaf1.refundAmounts.push( - getRefund(deposit4.amount, deposit4.realizedLpFeePct).mul(toBNWei("0.04")).div(toBNWei("1")) - ); - const leaf3 = { - chainId: destinationChainId, - amountToReturn: toBN(0), - l2TokenAddress: erc20_2.address, - refundAddresses: [allSigners[2].address, allSigners[1].address, allSigners[0].address], - refundAmounts: [ - getRefund(deposit4.amount, deposit4.realizedLpFeePct).mul(toBNWei("0.03")).div(toBNWei("1")), - getRefund(deposit4.amount, deposit4.realizedLpFeePct).mul(toBNWei("0.02")).div(toBNWei("1")), - getRefund(deposit4.amount, deposit4.realizedLpFeePct).mul(toBNWei("0.01")).div(toBNWei("1")), - ], - }; - expect(relayerRefundLeaves3.leaves[0].amountToReturn).to.deep.equal(leaf1.amountToReturn); - relayerRefundLeaves3.leaves[0].refundAmounts.forEach((refundAmount, i) => { - expect(refundAmount).to.equal(leaf1.refundAmounts[i]); - }); - expect(relayerRefundLeaves3.leaves[1].amountToReturn).to.deep.equal(leaf3.amountToReturn); - relayerRefundLeaves3.leaves[1].refundAmounts.forEach((refundAmount, i) => { - expect(refundAmount).to.equal(leaf3.refundAmounts[i]); - }); - }); - it("amountToReturn is non 0", async function () { - await updateAllClients(); - - ubaClient.setFlows(originChainId, l1TokenSymbol, [ - { - flow: { - ...spokePoolClient_1.getFills()[0], - matchedDeposit: spokePoolClient_2.getDeposits()[0], - }, - lpFee: toBNWei("0.5"), - balancingFee: toBNWei("0.2"), - runningBalance: toBNWei("1"), - incentiveBalance: toBNWei("2"), - netRunningBalanceAdjustment: toBNWei("-1"), - }, - ]); - - const blockRanges = dataworkerInstance._getNextProposalBlockRanges(spokePoolClients); - if (!blockRanges) { - throw new Error("Can't propose new bundle"); - } - const { poolRebalanceLeaves } = dataworkerInstance._UBA_buildPoolRebalanceLeaves( - blockRanges, - [originChainId, destinationChainId], - ubaClient - ); - - // This leaf's amountToReturn should be non zero since the UBA client was injected with a flow - // with a negative netRunningBalanceAdjustment - const leaf1 = { - chainId: originChainId, - amountToReturn: poolRebalanceLeaves[0].netSendAmounts[0].mul(toBN(-1)), - l2TokenAddress: erc20_1.address, - refundAddresses: [], - refundAmounts: [], - }; - const relayerRefundLeaves1 = await dataworkerInstance._UBA_buildRelayerRefundLeaves( - {}, - poolRebalanceLeaves, - blockRanges, - [originChainId], - ubaClient - ); - expect(relayerRefundLeaves1.leaves.length).to.equal(1); - deepEqualsWithBigNumber(relayerRefundLeaves1.leaves[0], { ...leaf1, leafId: 0 }); - - // Now, submit fills on the origin chain such that the refunds for the origin chain need to be split amongst - // more than one leaves. - const deposit1 = await buildDeposit( - hubPoolClient, - spokePool_2, - erc20_2, - l1Token_1, - depositor, - originChainId, - amountToDeposit - ); - const allSigners: SignerWithAddress[] = await ethers.getSigners(); - expect( - allSigners.length >= MAX_REFUNDS_PER_RELAYER_REFUND_LEAF + 1, - "ethers.getSigners doesn't have enough signers" - ).to.be.true; - const sortedAllSigners = [...allSigners].sort((x, y) => compareAddresses(x.address, y.address)); - const fills = []; - for (let i = 0; i < MAX_REFUNDS_PER_RELAYER_REFUND_LEAF + 1; i++) { - await setupTokensForWallet(spokePool_1, sortedAllSigners[i], [erc20_1]); - fills.push(await buildFillForRepaymentChain(spokePool_1, sortedAllSigners[i], deposit1, 0.1, originChainId)); - } - const refundAmountPerFill = getRefund(deposit1.amount, deposit1.realizedLpFeePct) - .mul(toBNWei("0.1")) - .div(toBNWei("1")); - const newLeaf1 = { - chainId: originChainId, - // amountToReturn should be deposit amount minus ALL fills for origin chain minus unfilled amount for slow fill. - amountToReturn: poolRebalanceLeaves[0].netSendAmounts[0].mul(toBN(-1)), - l2TokenAddress: erc20_1.address, - refundAddresses: sortedAllSigners.slice(0, MAX_REFUNDS_PER_RELAYER_REFUND_LEAF).map((x) => x.address), - refundAmounts: Array(MAX_REFUNDS_PER_RELAYER_REFUND_LEAF).fill(refundAmountPerFill), - }; - const leaf2 = { - chainId: originChainId, - amountToReturn: toBN(0), - l2TokenAddress: erc20_1.address, - refundAddresses: [sortedAllSigners[MAX_REFUNDS_PER_RELAYER_REFUND_LEAF].address], - refundAmounts: [refundAmountPerFill], - }; - - await updateAllClients(); - const data1 = await dataworkerInstance.clients.bundleDataClient._loadData( - getDefaultBlockRange(1), - spokePoolClients, - true - ); - const relayerRefundLeaves2 = dataworkerInstance._UBA_buildRelayerRefundLeaves( - data1.fillsToRefund, - poolRebalanceLeaves, - getDefaultBlockRange(1), - [originChainId], - ubaClient - ); - expect(relayerRefundLeaves2.leaves.length).to.equal(2); - expect(relayerRefundLeaves2.leaves[0].amountToReturn).to.deep.equal(newLeaf1.amountToReturn); - relayerRefundLeaves2.leaves[0].refundAmounts.forEach((refundAmount, i) => { - expect(refundAmount).to.equal(newLeaf1.refundAmounts[i]); - }); - expect(relayerRefundLeaves2.leaves[1].amountToReturn).to.deep.equal(leaf2.amountToReturn); - relayerRefundLeaves2.leaves[1].refundAmounts.forEach((refundAmount, i) => { - expect(refundAmount).to.equal(leaf2.refundAmounts[i]); - }); - }); - it("Refunds are included in UBA mode", async function () { - await updateAllClients(); - const { poolRebalanceLeaves } = dataworkerInstance._UBA_buildPoolRebalanceLeaves( - getDefaultBlockRange(0), - [originChainId, destinationChainId], - ubaClient - ); - - // Fill deposit and request refund on origin chain which is NOT the destination chain. - const deposit1 = await buildDeposit( - hubPoolClient, - spokePool_1, - erc20_1, - l1Token_1, - depositor, - destinationChainId, - amountToDeposit - ); - - await updateAllClients(); - - // In UBA Mode, realized Lp fee is not set during update(), so we need to manually overwrite it. - const ubaRealizedLpFeePct = toBNWei("0.1"); - spokePoolClient_1.updateDepositRealizedLpFeePct(deposit1, ubaRealizedLpFeePct); - await buildFillForRepaymentChain( - spokePool_2, - relayer, - { ...deposit1, realizedLpFeePct: ubaRealizedLpFeePct }, - 1, - originChainId - ); - - await updateAllClients(); - const data1 = await dataworkerInstance.clients.bundleDataClient._loadData( - getDefaultBlockRange(1), - spokePoolClients, - true - ); - const relayerRefundLeaves1 = dataworkerInstance._UBA_buildRelayerRefundLeaves( - data1.fillsToRefund, - poolRebalanceLeaves, - getDefaultBlockRange(1), - [originChainId], - ubaClient - ); - expect(relayerRefundLeaves1.leaves.length).to.equal(0); - - await updateAllClients(); - const data2 = await dataworkerInstance.clients.bundleDataClient._loadData( - getDefaultBlockRange(2), - spokePoolClients, - true - ); - const relayerRefundLeaves2 = dataworkerInstance._UBA_buildRelayerRefundLeaves( - data2.fillsToRefund, - poolRebalanceLeaves, - getDefaultBlockRange(2), - [originChainId], - ubaClient - ); - expect(relayerRefundLeaves2.leaves.length).to.equal(0); - }); - it("Relayer balancing fees are added to refunded amounts to relayers", async function () { - // Submit 1 deposit and 1 fill on same chain: - await updateAllClients(); - const deposit1 = await buildDeposit( - hubPoolClient, - spokePool_2, - erc20_2, - l1Token_1, - depositor, - originChainId, - amountToDeposit - ); - await updateAllClients(); - await buildFillForRepaymentChain(spokePool_2, relayer, deposit1, 1, originChainId); - await updateAllClients(); - const deposit = spokePoolClient_2.getDeposits()[0]; - const refund = spokePoolClient_2.getFills()[0]; - - ubaClient.setFlows(originChainId, l1TokenSymbol, [ - { - flow: { - ...deposit, - }, - lpFee: toBNWei("0.5"), - balancingFee: toBNWei("0.2"), - runningBalance: toBNWei("0"), - incentiveBalance: toBNWei("0"), - netRunningBalanceAdjustment: toBNWei("0"), - }, - { - flow: { - ...refund, - matchedDeposit: deposit, - }, - runningBalance: toBNWei("0"), - balancingFee: toBNWei("0.2"), - lpFee: toBNWei("0.5"), - incentiveBalance: toBNWei("0"), - netRunningBalanceAdjustment: toBNWei("0"), - }, - ]); - - const blockRanges = dataworkerInstance._getNextProposalBlockRanges(spokePoolClients); - if (!blockRanges) { - throw new Error("Can't propose new bundle"); - } - const { poolRebalanceLeaves } = dataworkerInstance._UBA_buildPoolRebalanceLeaves( - blockRanges, - [originChainId, destinationChainId], - ubaClient - ); - await updateAllClients(); - - // Can pass in a fills to refund object that is empty for the refund chain and token, or - // has an existing value. The relayer balancing fee is tacked on to that. - const relayerRefundLeaves1 = await dataworkerInstance._UBA_buildRelayerRefundLeaves( - {}, - poolRebalanceLeaves, - blockRanges, - [originChainId], - ubaClient - ); - // Balancing fee for refund above - let expectedRefundAmount = toBNWei("0.2"); - expect(relayerRefundLeaves1.leaves.length).to.equal(1); - expect(relayerRefundLeaves1.leaves[0]) - .excludingEvery(["amountToReturn", "leafId"]) - .to.deep.equal({ - chainId: originChainId, - refundAmounts: [expectedRefundAmount], - l2TokenAddress: erc20_1.address, - refundAddresses: [relayer.address], - }); - // Try again while passing in an already populated fills to refund object. - const relayerRefundLeaves2 = await dataworkerInstance._UBA_buildRelayerRefundLeaves( - { - [originChainId.toString()]: { - [erc20_1.address]: { - totalRefundAmount: ethers.constants.Zero, - fills: [], - realizedLpFees: ethers.constants.Zero, - refunds: { - [relayer.address]: toBNWei("1"), - }, - }, - }, - }, - poolRebalanceLeaves, - blockRanges, - [originChainId], - ubaClient - ); - // Expected refund amount is now 1 + new balancing fee. - expectedRefundAmount = expectedRefundAmount.add(toBNWei("1")); - expect(relayerRefundLeaves2.leaves.length).to.equal(1); - expect(relayerRefundLeaves2.leaves[0]) - .excludingEvery(["amountToReturn", "leafId"]) - .to.deep.equal({ - chainId: originChainId, - refundAmounts: [expectedRefundAmount], - l2TokenAddress: erc20_1.address, - refundAddresses: [relayer.address], - }); - - const relayerRefundLeaves3 = dataworkerInstance._UBA_buildRelayerRefundLeaves( - {}, - poolRebalanceLeaves, - blockRanges, - [], - ubaClient - ); - // Expected refund amount is now 1 + new balancing fee. - expectedRefundAmount = toBNWei("0.1"); - expect(relayerRefundLeaves3.leaves.length).to.equal(0); - }); - }); - describe("Build slow relay root", function () { - it("Maps unfilled deposit to UBA flow", async function () { - await updateAllClients(); - - // Submit deposits for multiple destination chain IDs. - const deposit1 = await buildDeposit( - hubPoolClient, - spokePool_1, - erc20_1, - l1Token_1, - depositor, - destinationChainId, - amountToDeposit - ); - - // Add fills for each deposit so dataworker includes deposits as slow relays: - await buildFill(spokePool_2, erc20_2, depositor, relayer, deposit1, 0.1); - - // Returns expected merkle root where leaves are ordered by origin chain ID and then deposit ID - // (ascending). - await updateAllClients(); - - const { unfilledDeposits } = await dataworkerInstance.clients.bundleDataClient._loadData( - getDefaultBlockRange(0), - spokePoolClients - ); - - // If the flow that triggered the slow relay is not in the UBA flows, then expect an error. This should - // never happen in production because each flow that could trigger a slow relay will be included in the UBA - // flows. If that deposit never gets fully filled, then it will appear as an `unfilledDeposit` returned by - // `loadData`. If it does get fully filled, it will remain a UBA flow but not returned by `loadData`. - expect(() => - dataworkerInstance._UBA_buildSlowRelayLeaves(ubaClient, getDefaultBlockRange(0), unfilledDeposits) - ).to.throw(`No matching outflow with refund balancing fee found for deposit ID ${deposit1.depositId}`); - - const expectedRelayerBalancingFee = toBNWei("0.025"); - ubaClient.setFlows(deposit1.destinationChainId, l1TokenSymbol, [ - { - flow: { - ...spokePoolClient_2.getFills()[0], - matchedDeposit: {} as unknown as DepositWithBlock, // Not used in this test. - }, - lpFee: toBNWei("0.5"), - balancingFee: expectedRelayerBalancingFee, - runningBalance: toBNWei("1"), - incentiveBalance: expectedRelayerBalancingFee, - netRunningBalanceAdjustment: toBNWei("1"), - }, - ]); - const slowRelayLeaves = dataworkerInstance._UBA_buildSlowRelayLeaves( - ubaClient, - getDefaultBlockRange(0), - unfilledDeposits - ); - expect(slowRelayLeaves.leaves.length).to.equal(1); - const expectedSlowRelayLeaves = buildSlowRelayLeaves([deposit1], [expectedRelayerBalancingFee]); - const expectedMerkleRoot = await buildSlowRelayTree(expectedSlowRelayLeaves); - expect(expectedSlowRelayLeaves[0].payoutAdjustmentPct).to.equal(slowRelayLeaves.leaves[0].payoutAdjustmentPct); - expect(expectedMerkleRoot.getHexRoot()).to.equal(slowRelayLeaves.tree.getHexRoot()); - - // If loadData doesn't return unfilled deposits, then no slow fill leaves. - expect( - dataworkerInstance._UBA_buildSlowRelayLeaves(ubaClient, getDefaultBlockRange(0), []).leaves.length - ).to.equal(0); - }); - }); - }); });