Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync with upstream repo #84

Merged
merged 15 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
"node": ">=20"
},
"dependencies": {
"@across-protocol/constants": "^3.1.25",
"@across-protocol/contracts": "^3.0.19",
"@across-protocol/sdk": "^3.3.32",
"@across-protocol/constants": "^3.1.27",
"@across-protocol/contracts": "^3.0.20",
"@across-protocol/sdk": "^3.4.9",
"@arbitrum/sdk": "^4.0.2",
"@consensys/linea-sdk": "^0.2.1",
"@defi-wonderland/smock": "^2.3.5",
Expand Down
2 changes: 1 addition & 1 deletion scripts/zkSyncDemo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export async function run(): Promise<void> {
connectedSigner
);
const l2PubdataByteLimit = zksync.utils.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT;
const l1GasPriceData = await gasPriceOracle.getGasPriceEstimate(l1Provider, l1ChainId);
const l1GasPriceData = await gasPriceOracle.getGasPriceEstimate(l1Provider, { chainId: l1ChainId });
const estimatedL1GasPrice = l1GasPriceData.maxPriorityFeePerGas.add(l1GasPriceData.maxFeePerGas);
// The ZkSync Mailbox contract checks that the msg.value of the transaction is enough to cover the transaction base
// cost. The transaction base cost can be queried from the Mailbox by passing in an L1 "executed" gas price,
Expand Down
68 changes: 0 additions & 68 deletions src/adapter/AdapterManager.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/adapter/bridges/ArbitrumOrbitBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { CONTRACT_ADDRESSES, CUSTOM_ARBITRUM_GATEWAYS, DEFAULT_ARBITRUM_GATEWAY } from "../../common";
import { BridgeTransactionDetails, BaseBridgeAdapter, BridgeEvents } from "./BaseBridgeAdapter";
import { processEvent } from "../utils";
import { PRODUCTION_NETWORKS } from "@across-protocol/constants";
import { PUBLIC_NETWORKS } from "@across-protocol/constants";

const bridgeSubmitValue: { [chainId: number]: BigNumber } = {
[CHAIN_IDs.ARBITRUM]: toWei(0.013),
Expand Down Expand Up @@ -53,7 +53,7 @@ export class ArbitrumOrbitBridge extends BaseBridgeAdapter {

super(l2chainId, hubChainId, l1Signer, l2SignerOrProvider, [l1Address]);

const nativeToken = PRODUCTION_NETWORKS[l2chainId].nativeToken;
const nativeToken = PUBLIC_NETWORKS[l2chainId].nativeToken;
// Only set nonstandard gas tokens.
if (nativeToken !== "ETH") {
this.gasToken = TOKEN_SYMBOLS_MAP[nativeToken].addresses[hubChainId];
Expand Down
30 changes: 15 additions & 15 deletions src/clients/MultiCallerClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export interface TryMulticallTransaction {
export class MultiCallerClient {
protected txnClient: TransactionClient;
protected txns: { [chainId: number]: AugmentedTransaction[] } = {};
protected valueTxns: { [chainId: number]: AugmentedTransaction[] } = {};
protected nonMulticallTxns: { [chainId: number]: AugmentedTransaction[] } = {};
constructor(
readonly logger: winston.Logger,
readonly chunkSize: { [chainId: number]: number } = {},
Expand All @@ -90,8 +90,8 @@ export class MultiCallerClient {

getQueuedTransactions(chainId: number): AugmentedTransaction[] {
const allTxns = [];
if (this.valueTxns?.[chainId]) {
allTxns.push(...this.valueTxns[chainId]);
if (this.nonMulticallTxns?.[chainId]) {
allTxns.push(...this.nonMulticallTxns[chainId]);
}
if (this.txns?.[chainId]) {
allTxns.push(...this.txns[chainId]);
Expand All @@ -101,8 +101,8 @@ export class MultiCallerClient {

// Adds all information associated with a transaction to the transaction queue.
enqueueTransaction(txn: AugmentedTransaction): void {
// Value transactions are sorted immediately because the UMA multicall implementation rejects them.
const txnQueue = txn.value && txn.value.gt(0) ? this.valueTxns : this.txns;
// We do not attempt to batch together transactions that have value or are explicitly nonMulticall.
const txnQueue = (txn.value && txn.value.gt(0)) || txn.nonMulticall ? this.nonMulticallTxns : this.txns;
if (txnQueue[txn.chainId] === undefined) {
txnQueue[txn.chainId] = [];
}
Expand All @@ -111,25 +111,25 @@ export class MultiCallerClient {

transactionCount(): number {
return Object.values(this.txns)
.concat(Object.values(this.valueTxns))
.concat(Object.values(this.nonMulticallTxns))
.reduce((count, txnQueue) => (count += txnQueue.length), 0);
}

clearTransactionQueue(chainId: number | null = null): void {
if (chainId !== null) {
this.txns[chainId] = [];
this.valueTxns[chainId] = [];
this.nonMulticallTxns[chainId] = [];
} else {
this.txns = {};
this.valueTxns = {};
this.nonMulticallTxns = {};
}
}

// For each chain, collate the enqueued transactions and process them in parallel.
async executeTxnQueues(simulate = false, chainIds: number[] = []): Promise<Record<number, string[]>> {
if (chainIds.length === 0) {
chainIds = sdkUtils.dedupArray([
...Object.keys(this.valueTxns).map(Number),
...Object.keys(this.nonMulticallTxns).map(Number),
...Object.keys(this.txns).map(Number),
]);
}
Expand Down Expand Up @@ -174,9 +174,9 @@ export class MultiCallerClient {
// For a single chain, take any enqueued transactions and attempt to execute them.
async executeTxnQueue(chainId: number, simulate = false): Promise<TransactionResponse[]> {
const txns: AugmentedTransaction[] | undefined = this.txns[chainId];
const valueTxns: AugmentedTransaction[] | undefined = this.valueTxns[chainId];
const nonMulticallTxns: AugmentedTransaction[] | undefined = this.nonMulticallTxns[chainId];
this.clearTransactionQueue(chainId);
return this._executeTxnQueue(chainId, txns, valueTxns, simulate);
return this._executeTxnQueue(chainId, txns, nonMulticallTxns, simulate);
}

// For a single chain, simulate all potential multicall txns and group the ones that pass into multicall bundles.
Expand All @@ -185,10 +185,10 @@ export class MultiCallerClient {
protected async _executeTxnQueue(
chainId: number,
txns: AugmentedTransaction[] = [],
valueTxns: AugmentedTransaction[] = [],
nonMulticallTxns: AugmentedTransaction[] = [],
simulate = false
): Promise<TransactionResponse[]> {
const nTxns = txns.length + valueTxns.length;
const nTxns = txns.length + nonMulticallTxns.length;
if (nTxns === 0) {
return [];
}
Expand All @@ -204,7 +204,7 @@ export class MultiCallerClient {
// First try to simulate the transaction as a batch. If the full batch succeeded, then we don't
// need to simulate transactions individually. If the batch failed, then we need to
// simulate the transactions individually and pick out the successful ones.
const batchTxns: AugmentedTransaction[] = valueTxns.concat(
const batchTxns: AugmentedTransaction[] = nonMulticallTxns.concat(
await this.buildMultiCallBundles(txns, this.chunkSize[chainId])
);
const batchSimResults = await this.txnClient.simulate(batchTxns);
Expand All @@ -227,7 +227,7 @@ export class MultiCallerClient {
} else {
const individualTxnSimResults = await Promise.allSettled([
this.simulateTransactionQueue(txns),
this.simulateTransactionQueue(valueTxns),
this.simulateTransactionQueue(nonMulticallTxns),
]);
const [_txns, _valueTxns] = individualTxnSimResults.map((result): AugmentedTransaction[] => {
return isPromiseFulfilled(result) ? result.value : [];
Expand Down
69 changes: 53 additions & 16 deletions src/clients/ProfitClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
TOKEN_EQUIVALENCE_REMAPPING,
ZERO_ADDRESS,
formatGwei,
fixedPointAdjustment,
} from "../utils";
import { Deposit, DepositWithBlock, L1Token, SpokePoolClientsByChain } from "../interfaces";
import { getAcrossHost } from "./AcrossAPIClient";
Expand Down Expand Up @@ -213,7 +214,7 @@ export class ProfitClient {
message: "Failed to simulate fill for deposit.",
reason,
deposit,
notificationPath: "across-unprofitable-fills",
notificationPath: "across-warn",
});
return { nativeGasCost: uint256Max, tokenGasCost: uint256Max, gasPrice: uint256Max };
}
Expand Down Expand Up @@ -543,7 +544,12 @@ export class ProfitClient {
.filter(({ symbol }) => isDefined(TOKEN_SYMBOLS_MAP[symbol]))
.map(({ symbol }) => {
const { addresses } = TOKEN_SYMBOLS_MAP[symbol];
const address = addresses[CHAIN_IDs.MAINNET];
let address = addresses[CHAIN_IDs.MAINNET];
// For testnet only, if we cannot resolve the token address, revert to ETH. On mainnet, if `address` is undefined,
// we will throw an error instead.
if (this.hubPoolClient.chainId === CHAIN_IDs.SEPOLIA && !isDefined(address)) {
address = TOKEN_SYMBOLS_MAP.ETH.addresses[CHAIN_IDs.MAINNET];
}
return [symbol, address];
})
);
Expand All @@ -566,7 +572,12 @@ export class ProfitClient {
// Also ensure all gas tokens are included in the lookup.
this.enabledChainIds.forEach((chainId) => {
const symbol = getNativeTokenSymbol(chainId);
tokens[symbol] ??= TOKEN_SYMBOLS_MAP[symbol].addresses[CHAIN_IDs.MAINNET];
let nativeTokenAddress = TOKEN_SYMBOLS_MAP[symbol].addresses[CHAIN_IDs.MAINNET];
// For testnet only, if the custom gas token has no mainnet address, use ETH.
if (this.hubPoolClient.chainId === CHAIN_IDs.SEPOLIA && !isDefined(nativeTokenAddress)) {
nativeTokenAddress = TOKEN_SYMBOLS_MAP["ETH"].addresses[CHAIN_IDs.MAINNET];
}
tokens[symbol] ??= nativeTokenAddress;
});

this.logger.debug({ at: "ProfitClient", message: "Updating Profit client", tokens });
Expand Down Expand Up @@ -609,6 +620,7 @@ export class ProfitClient {
[CHAIN_IDs.REDSTONE]: "WETH", // Redstone only supports WETH.
[CHAIN_IDs.WORLD_CHAIN]: "WETH", // USDC deferred on World Chain.
[CHAIN_IDs.INK]: "WETH", // USDC deferred on Ink.
[CHAIN_IDs.LENS_SEPOLIA]: "WETH", // No USD token on Lens Sepolia
};
const prodRelayer = process.env.RELAYER_FILL_SIMULATION_ADDRESS ?? PROD_RELAYER;
const [defaultTestSymbol, relayer] =
Expand Down Expand Up @@ -636,24 +648,49 @@ export class ProfitClient {
};

// Pre-fetch total gas costs for relays on enabled chains.
await sdkUtils.mapAsync(enabledChainIds, async (destinationChainId) => {
const symbol = testSymbols[destinationChainId] ?? defaultTestSymbol;
const hubToken = TOKEN_SYMBOLS_MAP[symbol].addresses[this.hubPoolClient.chainId];
const outputToken =
destinationChainId === hubPoolClient.chainId
? hubToken
: hubPoolClient.getL2TokenForL1TokenAtBlock(hubToken, destinationChainId);
assert(isDefined(outputToken), `Chain ${destinationChainId} SpokePool is not configured for ${symbol}`);

const deposit = { ...sampleDeposit, destinationChainId, outputToken };
this.totalGasCosts[destinationChainId] = await this._getTotalGasCost(deposit, relayer);
});
const totalGasCostsToLog = Object.fromEntries(
await sdkUtils.mapAsync(enabledChainIds, async (destinationChainId) => {
const symbol = testSymbols[destinationChainId] ?? defaultTestSymbol;
const hubToken = TOKEN_SYMBOLS_MAP[symbol].addresses[this.hubPoolClient.chainId];
const outputToken =
destinationChainId === hubPoolClient.chainId
? hubToken
: hubPoolClient.getL2TokenForL1TokenAtBlock(hubToken, destinationChainId);
assert(isDefined(outputToken), `Chain ${destinationChainId} SpokePool is not configured for ${symbol}`);

const deposit = { ...sampleDeposit, destinationChainId, outputToken };
const gasCosts = await this._getTotalGasCost(deposit, relayer);
// The scaledNativeGasCost is approximately what the relayer will set as the `gasLimit` when submitting
// fills on the destination chain.
const scaledNativeGasCost = gasCosts.nativeGasCost.mul(this.gasPadding).div(fixedPointAdjustment);
// The scaledTokenGasCost is the estimated gas cost of submitting a fill on the destination chain and is used
// in the this.estimateFillCost function to determine whether a deposit is profitable to fill. Therefore,
// the scaledTokenGasCost should be safely lower than the quote API's tokenGasCosts in order for the relayer
// to consider a deposit is profitable.
const scaledTokenGasCost = gasCosts.tokenGasCost
.mul(this.gasPadding)
.div(fixedPointAdjustment)
.mul(this.gasMultiplier)
.div(fixedPointAdjustment);
this.totalGasCosts[destinationChainId] = gasCosts;
return [
destinationChainId,
{
...gasCosts,
scaledNativeGasCost,
scaledTokenGasCost,
gasPadding: formatEther(this.gasPadding),
gasMultiplier: formatEther(this.gasMultiplier),
},
];
})
);

this.logger.debug({
at: "ProfitClient",
message: "Updated gas cost",
enabledChainIds: this.enabledChainIds,
totalGasCosts: this.totalGasCosts,
totalGasCosts: totalGasCostsToLog,
});
}

Expand Down
3 changes: 3 additions & 0 deletions src/clients/TransactionClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export interface AugmentedTransaction {
canFailInSimulation?: boolean;
// Optional batch ID to use to group transactions
groupId?: string;
// If true, the transaction is being sent to a non Multicall contract so we can't batch it together
// with other transactions.
nonMulticall?: boolean;
}

const { fixedPointAdjustment: fixedPoint } = sdkUtils;
Expand Down
Loading
Loading