Skip to content

Commit

Permalink
feat(Dataworker): ZkSync should count ETH and WETH towards balance ne…
Browse files Browse the repository at this point in the history
…eded to execute leaves (#872)

* feat(Dataworker): ZkSync should count ETH and WETH towards balance needed to execute leaves

Current code assumes only OVM chains need to count both ETH and WETH towards balance that can be used to execute spoke leaves

* Update DataworkerUtils.ts

* Update src/clients/BalanceAllocator.ts

Co-authored-by: James Morris, MS <96435344+james-a-morris@users.noreply.github.com>

* Update DataworkerUtils.ts

* Update BalanceAllocator.ts

---------

Co-authored-by: James Morris, MS <96435344+james-a-morris@users.noreply.github.com>
  • Loading branch information
nicholaspai and james-a-morris committed Aug 11, 2023
1 parent 0a6603d commit 3de58d6
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 13 deletions.
9 changes: 8 additions & 1 deletion src/clients/BalanceAllocator.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CONTRACT_ADDRESSES } from "../common/ContractAddresses";
import { BigNumber, ERC20, ethers, ZERO_ADDRESS, min } from "../utils";

// This type is used to map used and current balances of different users.
Expand Down Expand Up @@ -141,9 +142,15 @@ export class BalanceAllocator {
this.balances = {};
}

isEthAddress(chainId: number, tokenAddress: string): boolean {
// If there is an ETH address defined in CONTRACT_ADDRESSES, use it, otherwise assume ETH address is the zero
// address.
return (CONTRACT_ADDRESSES[chainId]?.eth?.address ?? ZERO_ADDRESS) === tokenAddress;
}

// This method is primarily here to be overriden for testing purposes.
protected async _queryBalance(chainId: number, token: string, holder: string): Promise<BigNumber> {
return token === ZERO_ADDRESS
return this.isEthAddress(chainId, token)
? await this.providers[chainId].getBalance(holder)
: await ERC20.connect(token, this.providers[chainId]).balanceOf(holder);
}
Expand Down
2 changes: 0 additions & 2 deletions src/clients/bridges/OptimismAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { constants } from "@across-protocol/sdk-v2";
import { CONTRACT_ADDRESSES } from "../../common";
const { TOKEN_SYMBOLS_MAP } = constants;

export const isOvmChain = (chainId: number): boolean => [10, 288].includes(chainId);

export class OptimismAdapter extends BaseAdapter {
public l2Gas: number;
private txnClient: TransactionClient;
Expand Down
8 changes: 8 additions & 0 deletions src/common/ContractAddresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,14 @@ export const CONTRACT_ADDRESSES: {
],
},
},
324: {
eth: {
address: "0x000000000000000000000000000000000000800A",
},
weth: {
address: "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91",
},
},
42161: {
erc20Gateway: {
abi: [
Expand Down
17 changes: 7 additions & 10 deletions src/dataworker/Dataworker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
blockRangesAreInvalidForSpokeClients,
getBlockRangeForChain,
getImpliedBundleBlockRanges,
l2TokensToCountTowardsSpokePoolLeafExecutionCapital,
} from "../dataworker/DataworkerUtils";
import {
getEndBlockBuffers,
Expand All @@ -46,8 +47,7 @@ import {
} from "./DataworkerUtils";
import { BalanceAllocator } from "../clients";
import _ from "lodash";
import { CONTRACT_ADDRESSES, spokePoolClientsToProviders } from "../common";
import { isOvmChain } from "../clients/bridges";
import { spokePoolClientsToProviders } from "../common";
import * as sdk from "@across-protocol/sdk-v2";

// Internal error reasons for labeling a pending root bundle as "invalid" that we don't want to submit a dispute
Expand All @@ -72,8 +72,6 @@ type ProposeRootBundleReturnType = {
slowFillTree: MerkleTree<SlowFillLeaf>;
};

const ovmWethTokens = [CONTRACT_ADDRESSES[10].weth.address, CONTRACT_ADDRESSES[10].eth.address];

export type PoolRebalanceRoot = {
runningBalances: RunningBalances;
realizedLpFees: RunningBalances;
Expand Down Expand Up @@ -1522,9 +1520,10 @@ export class Dataworker {
const amountRequired = getRefund(relayData.amount.sub(amountFilled), relayData.realizedLpFeePct);
const success = await balanceAllocator.requestBalanceAllocation(
relayData.destinationChainId,
isOvmChain(relayData.destinationChainId) && ovmWethTokens.includes(relayData.destinationToken)
? ovmWethTokens
: [relayData.destinationToken],
l2TokensToCountTowardsSpokePoolLeafExecutionCapital(
relayData.destinationToken,
relayData.destinationChainId
),
client.spokePool.address,
amountRequired
);
Expand Down Expand Up @@ -2096,9 +2095,7 @@ export class Dataworker {
const totalSent = refundSum.add(leaf.amountToReturn.gte(0) ? leaf.amountToReturn : BigNumber.from(0));
const success = await balanceAllocator.requestBalanceAllocation(
leaf.chainId,
isOvmChain(leaf.chainId) && ovmWethTokens.includes(leaf.l2TokenAddress)
? ovmWethTokens
: [leaf.l2TokenAddress],
l2TokensToCountTowardsSpokePoolLeafExecutionCapital(leaf.l2TokenAddress, leaf.chainId),
client.spokePool.address,
totalSent
);
Expand Down
37 changes: 37 additions & 0 deletions src/dataworker/DataworkerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
buildPoolRebalanceLeafTree,
buildRelayerRefundTree,
buildSlowRelayTree,
isDefined,
MerkleTree,
winston,
} from "../utils";
Expand All @@ -28,6 +29,7 @@ import { subtractExcessFromPreviousSlowFillsFromRunningBalances } from "./PoolRe
import { getAmountToReturnForRelayerRefundLeaf } from "./RelayerRefundUtils";
import { sortRefundAddresses, sortRelayerRefundLeaves } from "./RelayerRefundUtils";
import { utils } from "@across-protocol/sdk-v2";
import { CONTRACT_ADDRESSES } from "../common/ContractAddresses";
export const { getImpliedBundleBlockRanges, getBlockRangeForChain, getBlockForChain } = utils;

export function getEndBlockBuffers(
Expand Down Expand Up @@ -387,3 +389,38 @@ export async function _buildPoolRebalanceRoot(
tree: buildPoolRebalanceLeafTree(leaves),
};
}

/**
* @notice Returns WETH and ETH token addresses for chain if defined, or throws an error if they're not
* in the hardcoded dictionary.
* @param chainId chain to check for WETH and ETH addresses
* @returns WETH and ETH addresses.
*/
function getWethAndEth(chainId): string[] {
const wethAndEth = [CONTRACT_ADDRESSES[chainId].weth.address, CONTRACT_ADDRESSES[chainId].eth.address];
if (wethAndEth.some((tokenAddress) => !isDefined(tokenAddress))) {
throw new Error(`WETH or ETH address not defined for chain ${chainId}`);
}
return wethAndEth;
}
/**
* @notice Some SpokePools will contain balances of ETH and WETH, while others will only contain balances of WETH,
* so if the l2TokenAddress is WETH and the SpokePool is one such chain that holds both ETH and WETH,
* then it should return both ETH and WETH. For other chains, it should only return the l2TokenAddress.
* @param l2TokenAddress L2 token address in spoke leaf that we want to get addresses to check spoke balances for
* @param l2ChainId L2 chain of Spoke
* @returns Tokens that we should check the SpokePool balance for in order to execute a spoke leaf for
* `l2TokenAddress` on `l2ChainId`.
*/
export function l2TokensToCountTowardsSpokePoolLeafExecutionCapital(
l2TokenAddress: string,
l2ChainId: number
): string[] {
const spokesThatHoldEthAndWeth = [10, 324];
if (!spokesThatHoldEthAndWeth.includes(l2ChainId)) {
return [l2TokenAddress];
}
// If we get to here, ETH and WETH addresses should be defined, or we'll throw an error.
const ethAndWeth = getWethAndEth(l2ChainId);
return ethAndWeth.includes(l2TokenAddress) ? ethAndWeth : [l2TokenAddress];
}

0 comments on commit 3de58d6

Please sign in to comment.