Skip to content

Commit

Permalink
Merge branch 'master' into pxrl/fillRelay
Browse files Browse the repository at this point in the history
  • Loading branch information
pxrl committed Oct 4, 2023
2 parents b0c31ae + 9da0a15 commit bf13217
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 48 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"dependencies": {
"@across-protocol/contracts-v2": "2.4.3",
"@across-protocol/constants-v2": "1.0.4",
"@across-protocol/sdk-v2": "0.16.4",
"@arbitrum/sdk": "^3.1.3",
"@defi-wonderland/smock": "^2.3.5",
Expand Down
3 changes: 1 addition & 2 deletions src/clients/AcrossAPIClient.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { winston, BigNumber, getL2TokenAddresses } from "../utils";
import axios, { AxiosError } from "axios";
import { HubPoolClient } from "./HubPoolClient";
import { constants } from "@across-protocol/sdk-v2";
import { TOKEN_SYMBOLS_MAP, CHAIN_IDs } from "@across-protocol/constants-v2";
import { SpokePoolClientsByChain } from "../interfaces";
import _ from "lodash";
const { TOKEN_SYMBOLS_MAP, CHAIN_IDs } = constants;

export interface DepositLimits {
maxDeposit: BigNumber;
Expand Down
4 changes: 2 additions & 2 deletions src/clients/ProfitClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import * as constants from "../common/Constants";
import { assert, BigNumber, formatFeePct, max, winston, toBNWei, toBN, assign } from "../utils";
import { HubPoolClient } from ".";
import { Deposit, DepositWithBlock, L1Token, SpokePoolClientsByChain } from "../interfaces";
import { constants as sdkConstants, priceClient, relayFeeCalculator, utils as sdkUtils } from "@across-protocol/sdk-v2";
import { priceClient, relayFeeCalculator, utils as sdkUtils } from "@across-protocol/sdk-v2";
import { TOKEN_SYMBOLS_MAP, CHAIN_IDs } from "@across-protocol/constants-v2";

const { formatEther } = ethersUtils;
const { TOKEN_SYMBOLS_MAP, CHAIN_IDs } = sdkConstants;
const { fixedPointAdjustment: fixedPoint } = sdkUtils;

// We use wrapped ERC-20 versions instead of the native tokens such as ETH, MATIC for ease of computing prices.
Expand Down
3 changes: 1 addition & 2 deletions src/clients/bridges/ArbitrumAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ import {
import { SpokePoolClient } from "../../clients";
import { BaseAdapter } from "./BaseAdapter";
import { SortableEvent, OutstandingTransfers } from "../../interfaces";
import { constants } from "@across-protocol/sdk-v2";
import { CONTRACT_ADDRESSES } from "../../common";
const { TOKEN_SYMBOLS_MAP, CHAIN_IDs } = constants;
import { TOKEN_SYMBOLS_MAP, CHAIN_IDs } from "@across-protocol/constants-v2";

// TODO: Move to ../../common/ContractAddresses.ts
// These values are obtained from Arbitrum's gateway router contract.
Expand Down
3 changes: 1 addition & 2 deletions src/clients/bridges/PolygonAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ import {
import { SpokePoolClient } from "../../clients";
import { BaseAdapter } from "./";
import { SortableEvent, OutstandingTransfers } from "../../interfaces";
import { constants } from "@across-protocol/sdk-v2";
import { CONTRACT_ADDRESSES } from "../../common";
const { TOKEN_SYMBOLS_MAP, CHAIN_IDs } = constants;
import { TOKEN_SYMBOLS_MAP, CHAIN_IDs } from "@across-protocol/constants-v2";

// ether bridge = 0x8484Ef722627bf18ca5Ae6BcF031c23E6e922B30
// erc20 bridge = 0x40ec5B33f54e0E8A33A975908C5BA1c14e5BbbDf
Expand Down
104 changes: 67 additions & 37 deletions src/finalizer/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import assert from "assert";
import { typeguards, utils as sdkUtils } from "@across-protocol/sdk-v2";
import { providers } from "ethers";
import { groupBy } from "lodash";
import {
Wallet,
Expand Down Expand Up @@ -27,7 +28,11 @@ import {
FINALIZER_TOKENBRIDGE_LOOKBACK,
Multicall2Call,
} from "../common";
import { ChainFinalizer, Withdrawal } from "./types";
import { ChainFinalizer, Withdrawal as _Withdrawal } from "./types";

type TransactionReceipt = providers.TransactionReceipt;

type Withdrawal = _Withdrawal & { txns: Multicall2Call[] };

const { isError, isEthersError } = typeguards;

Expand Down Expand Up @@ -70,10 +75,7 @@ export async function finalize(
// Note: Could move this into a client in the future to manage # of calls and chunk calls based on
// input byte length.
const multicall2 = getMultisender(hubChainId, hubSigner);
const finalizationsToBatch: {
callData: Multicall2Call;
withdrawal: Withdrawal;
}[] = [];
const finalizationsToBatch: Withdrawal[] = [];

// For each chain, delegate to a handler to look up any TokensBridged events and attempt finalization.
for (const chainId of configuredChainIds) {
Expand All @@ -100,75 +102,103 @@ export async function finalize(

const network = getNetworkName(chainId);
logger.debug({ at: "finalize", message: `Spawning ${network} finalizer.`, latestBlockToFinalize });
const { callData, withdrawals } = await chainFinalizer(
const { callData: txns, withdrawals: _withdrawals } = await chainFinalizer(
logger,
hubSigner,
hubPoolClient,
client,
latestBlockToFinalize
);
logger.debug({ at: "finalize", message: `Found ${callData.length} ${network} withdrawals for finalization.` });
logger.debug({
at: "finalize",
message: `Found ${_withdrawals.length} ${network} withdrawals for finalization.`,
});

const txns = callData.map((callData, i) => {
return { callData, withdrawal: withdrawals[i] };
if (_withdrawals.length === 0) {
continue;
}

if (![1, 2].includes(txns.length / _withdrawals.length)) {
logger.warn({
at: "finalize",
message: `Unexpected ${network} txn/withdrawal ratio (${txns.length / _withdrawals.length}).`,
txns,
withdrawals: _withdrawals,
});
continue;
}

// Normalise withdawals, such that 1 withdrawal has an array of calldata (usually only 1 call), but can be more.
// @todo: Refactor the underlying adapters so they return in this data structure.
const withdrawals: Withdrawal[] = _withdrawals.map((withdrawal) => {
return { ...withdrawal, txns: [] };
});

finalizationsToBatch.push(...txns);
// Append calldata. If multiple calls are needed per withdrawal (i.e. Polygon),
// require that the 2nd batch is appended to the first.
txns.forEach((txn, i) => withdrawals[i % withdrawals.length].txns.push(txn));

finalizationsToBatch.push(...withdrawals);
}

// Ensure each transaction would succeed in isolation.
const finalizations = await sdkUtils.filterAsync(finalizationsToBatch, async (finalization) => {
const finalizations = await sdkUtils.filterAsync(finalizationsToBatch, async (withdrawal) => {
const { txns } = withdrawal;
try {
const { target: to, callData: data } = finalization.callData;
await multicall2.provider.estimateGas({ to, data });
const txn = await multicall2.populateTransaction.aggregate(txns);
await multicall2.provider.estimateGas(txn);
return true;
} catch (err) {
const { l2ChainId, type, l1TokenSymbol, amount } = finalization.withdrawal;
const { l2ChainId, type, l1TokenSymbol, amount } = withdrawal;
const network = getNetworkName(l2ChainId);
logger.info({
at: "finalizer",
message: `Failed to estimate gas for ${network} ${amount} ${l1TokenSymbol} ${type}.`,
txns,
reason: isEthersError(err) ? err.reason : isError(err) ? err.message : "unknown error",
});
return false;
}
});

if (finalizations.length > 0) {
let txn: TransactionReceipt;
try {
// Note: If the sum of finalizations approaches the gas limit, consider slicing them up.
const callData = finalizations.map(({ callData }) => callData);
const txn = await (await multicall2.aggregate(callData)).wait();

const { withdrawals = [], proofs = [] } = groupBy(
finalizations.map(({ withdrawal }) => withdrawal),
({ type }) => (type === "withdrawal" ? "withdrawals" : "proofs")
);
proofs.forEach(({ l2ChainId, amount, l1TokenSymbol: symbol }) => {
const spokeChain = getNetworkName(l2ChainId);
logger.info({
at: "Finalizer",
message: `Submitted proof on chain ${hubChain} to initiate ${spokeChain} withdrawal of ${amount} ${symbol} 🔜`,
transactionHash: blockExplorerLink(txn.transactionHash, hubChainId),
});
});
withdrawals.forEach(({ l2ChainId, amount, l1TokenSymbol: symbol }) => {
const spokeChain = getNetworkName(l2ChainId);
logger.info({
at: "Finalizer",
message: `Finalized ${spokeChain} withdrawal for ${amount} ${symbol} 🪃`,
transactionHash: blockExplorerLink(txn.transactionHash, hubChainId),
});
});
const txns = finalizations.map(({ txns }) => txns).flat();
txn = await (await multicall2.aggregate(txns)).wait();
} catch (_error) {
const error = _error as Error;
logger.warn({
at: "Finalizer",
message: "Error creating aggregateTx",
reason: error.stack || error.message || error.toString(),
notificationPath: "across-error",
finalizations,
});

return;
}

const { withdrawals = [], proofs = [] } = groupBy(finalizations, ({ type }) =>
type === "withdrawal" ? "withdrawals" : "proofs"
);
proofs.forEach(({ l2ChainId, amount, l1TokenSymbol: symbol }) => {
const spokeChain = getNetworkName(l2ChainId);
logger.info({
at: "Finalizer",
message: `Submitted proof on chain ${hubChain} to initiate ${spokeChain} withdrawal of ${amount} ${symbol} 🔜`,
transactionHash: blockExplorerLink(txn.transactionHash, hubChainId),
});
});
withdrawals.forEach(({ l2ChainId, amount, l1TokenSymbol: symbol }) => {
const spokeChain = getNetworkName(l2ChainId);
logger.info({
at: "Finalizer",
message: `Finalized ${spokeChain} withdrawal for ${amount} ${symbol} 🪃`,
transactionHash: blockExplorerLink(txn.transactionHash, hubChainId),
});
});
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/utils/TokenUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { constants, utils } from "@across-protocol/sdk-v2";
import { CONTRACT_ADDRESSES } from "../common";
import { BigNumberish, utils as ethersUtils } from "ethers";
const { TOKEN_SYMBOLS_MAP, CHAIN_IDs, ZERO_ADDRESS } = constants;
const { ZERO_ADDRESS } = constants;
import { TOKEN_SYMBOLS_MAP, CHAIN_IDs } from "@across-protocol/constants-v2";

export const { fetchTokenInfo } = utils;

Expand Down
3 changes: 1 addition & 2 deletions test/AdapterManager.SendTokensCrossChain.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { constants } from "@across-protocol/sdk-v2";
import * as zksync from "zksync-web3";
import { SpokePoolClient } from "../src/clients";
import { AdapterManager } from "../src/clients/bridges"; // Tested
Expand All @@ -18,7 +17,7 @@ import {
toBN,
winston,
} from "./utils";
const { TOKEN_SYMBOLS_MAP, CHAIN_IDs } = constants;
import { TOKEN_SYMBOLS_MAP, CHAIN_IDs } from "@across-protocol/constants-v2";

let hubPoolClient: MockHubPoolClient;
const mockSpokePoolClients: {
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
"@uma/common" "^2.17.0"
hardhat "^2.9.3"

"@across-protocol/constants-v2@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@across-protocol/constants-v2/-/constants-v2-1.0.4.tgz#df31c81038982a25de2b1b8f7604875f3de1186c"
integrity sha512-Nzl8Z1rZFvcpuKQu7CmBVfvgB13/NoulcsRVYBSkG90imS/e6mugxzqD9UrUb+WOL0ODMCANCAoDw54ZBBzNiQ==

"@across-protocol/contracts-v2@2.4.3", "@across-protocol/contracts-v2@^2.4.3":
version "2.4.3"
resolved "https://registry.yarnpkg.com/@across-protocol/contracts-v2/-/contracts-v2-2.4.3.tgz#9cc0b1f52b4f819b32ca1524ef84af9dfed8687a"
Expand Down

0 comments on commit bf13217

Please sign in to comment.