Skip to content

Commit

Permalink
Sync with upstream changes (#80)
Browse files Browse the repository at this point in the history
### What was the problem?

This PR resolves #LISK-1355

### How was it solved?

- [x] Merge upstream changes to current master branch
- [x] Resolve package.json conflicts

### How was it tested?

```sh
sh ./scripts/lisk/docker/dev/composeStart.sh
sh ./scripts/lisk/docker/dev/composeStop.sh
```
  • Loading branch information
sameersubudhi authored Nov 28, 2024
2 parents 9c1f9a5 + aaef6aa commit 514900b
Show file tree
Hide file tree
Showing 26 changed files with 940 additions and 191 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"node": ">=20"
},
"dependencies": {
"@across-protocol/constants": "^3.1.19",
"@across-protocol/constants": "^3.1.20",
"@across-protocol/contracts": "^3.0.16",
"@across-protocol/sdk": "^3.2.13",
"@arbitrum/sdk": "^3.1.3",
"@across-protocol/sdk": "^3.3.21",
"@arbitrum/sdk": "^4.0.2",
"@aws-sdk/client-kms": "^3.592.0",
"@aws-sdk/client-s3": "^3.592.0",
"@consensys/linea-sdk": "^0.2.1",
Expand Down
7 changes: 4 additions & 3 deletions scripts/spokepool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { groupBy } from "lodash";
import { config } from "dotenv";
import { Contract, ethers, Signer } from "ethers";
import { LogDescription } from "@ethersproject/abi";
import { CHAIN_IDs } from "@across-protocol/constants";
import { constants as sdkConsts, utils as sdkUtils } from "@across-protocol/sdk";
import { ExpandedERC20__factory as ERC20 } from "@across-protocol/contracts";
import { RelayData } from "../src/interfaces";
import { getAcrossHost } from "../src/clients";
import {
BigNumber,
formatFeePct,
Expand All @@ -33,8 +35,6 @@ type RelayerFeeQuery = {
timestamp?: number;
};

const { ACROSS_API_HOST = "across.to" } = process.env;

const { NODE_SUCCESS, NODE_INPUT_ERR, NODE_APP_ERR } = utils;
const { fixedPointAdjustment: fixedPoint } = sdkUtils;
const { AddressZero } = ethers.constants;
Expand Down Expand Up @@ -87,8 +87,9 @@ function printFill(destinationChainId: number, log: LogDescription): void {
}

async function getSuggestedFees(params: RelayerFeeQuery, timeout: number) {
const hubChainId = sdkUtils.chainIsProd(params.originChainId) ? CHAIN_IDs.MAINNET : CHAIN_IDs.SEPOLIA;
const path = "api/suggested-fees";
const url = `https://${ACROSS_API_HOST}/${path}`;
const url = `https://${getAcrossHost(hubChainId)}/${path}`;

try {
const quote = await axios.get(url, { timeout, params });
Expand Down
117 changes: 117 additions & 0 deletions scripts/withdrawFromArbitrumOrbit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Submits a bridge from Arbitrum Orbit L2 to L1.
// For now, this script only supports WETH withdrawals on AlephZero.

import {
ethers,
retrieveSignerFromCLIArgs,
getProvider,
ERC20,
TOKEN_SYMBOLS_MAP,
assert,
getL1TokenInfo,
Contract,
fromWei,
blockExplorerLink,
getNativeTokenSymbol,
} from "../src/utils";
import { CONTRACT_ADDRESSES } from "../src/common";
import { askYesNoQuestion } from "./utils";

import minimist from "minimist";

const cliArgs = ["amount", "chainId", "token"];
const args = minimist(process.argv.slice(2), {
string: cliArgs,
});

// Example run:
// ts-node ./scripts/withdrawFromArbitrumOrbit.ts
// \ --amount 3000000000000000000
// \ --chainId 41455
// \ --token WETH
// \ --wallet gckms
// \ --keys bot1

export async function run(): Promise<void> {
assert(
cliArgs.every((cliArg) => Object.keys(args).includes(cliArg)),
`Missing cliArg, expected: ${cliArgs}`
);
const baseSigner = await retrieveSignerFromCLIArgs();
const signerAddr = await baseSigner.getAddress();
const chainId = parseInt(args.chainId);
const connectedSigner = baseSigner.connect(await getProvider(chainId));
const l2Token = TOKEN_SYMBOLS_MAP[args.token]?.addresses[chainId];
assert(l2Token, `${args.token} not found on chain ${chainId} in TOKEN_SYMBOLS_MAP`);
const l1TokenInfo = getL1TokenInfo(l2Token, chainId);
console.log("Fetched L1 token info:", l1TokenInfo);
const amount = args.amount;
const amountFromWei = ethers.utils.formatUnits(amount, l1TokenInfo.decimals);
console.log(`Amount to bridge from chain ${chainId}: ${amountFromWei} ${l2Token}`);

const erc20 = new Contract(l2Token, ERC20.abi, connectedSigner);
const currentBalance = await erc20.balanceOf(signerAddr);
const nativeTokenSymbol = getNativeTokenSymbol(chainId);
const currentNativeBalance = await connectedSigner.getBalance();
console.log(
`Current ${l1TokenInfo.symbol} balance for account ${signerAddr}: ${fromWei(
currentBalance,
l1TokenInfo.decimals
)} ${l2Token}`
);
console.log(
`Current native ${nativeTokenSymbol} token balance for account ${signerAddr}: ${fromWei(currentNativeBalance, 18)}`
);

// Now, submit a withdrawal:
let contract: Contract, functionName: string, functionArgs: any[];
if (l1TokenInfo.symbol !== nativeTokenSymbol) {
const arbErc20GatewayObj = CONTRACT_ADDRESSES[chainId].erc20Gateway;
contract = new Contract(arbErc20GatewayObj.address, arbErc20GatewayObj.abi, connectedSigner);
functionName = "outboundTransfer";
functionArgs = [
l1TokenInfo.address, // l1Token
signerAddr, // to
amount, // amount
"0x", // data
];

console.log(
`Submitting ${functionName} on the Arbitrum ERC20 gateway router @ ${contract.address} with the following args: `,
...functionArgs
);
} else {
const arbSys = CONTRACT_ADDRESSES[chainId].arbSys;
contract = new Contract(arbSys.address, arbSys.abi, connectedSigner);
functionName = "withdrawEth";
functionArgs = [
signerAddr, // to
{ value: amount },
];
console.log(
`Submitting ${functionName} on the ArbSys contract @ ${contract.address} with the following args: `,
...functionArgs
);
}

if (!(await askYesNoQuestion("\nDo you want to proceed?"))) {
return;
}
const withdrawal = await contract[functionName](...functionArgs);
console.log(`Submitted withdrawal: ${blockExplorerLink(withdrawal.hash, chainId)}.`);
const receipt = await withdrawal.wait();
console.log("Receipt", receipt);
}

if (require.main === module) {
run()
.then(async () => {
// eslint-disable-next-line no-process-exit
process.exit(0);
})
.catch(async (error) => {
console.error("Process exited with", error);
// eslint-disable-next-line no-process-exit
process.exit(1);
});
}
67 changes: 45 additions & 22 deletions scripts/withdrawFromOpStack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import {
fromWei,
blockExplorerLink,
CHAIN_IDs,
ZERO_ADDRESS,
} from "../src/utils";
import { CONTRACT_ADDRESSES } from "../src/common";
import { askYesNoQuestion, getOvmSpokePoolContract } from "./utils";

import minimist from "minimist";

const cliArgs = ["amount", "chainId"];
const cliArgs = ["amount", "chainId", "token"];
const args = minimist(process.argv.slice(2), {
string: cliArgs,
});
Expand All @@ -30,6 +31,7 @@ const args = minimist(process.argv.slice(2), {
// \ --amount 3000000000000000000
// \ --chainId 1135
// \ --wallet gckms
// \ --token WETH
// \ --keys bot1

export async function run(): Promise<void> {
Expand All @@ -41,11 +43,10 @@ export async function run(): Promise<void> {
const signerAddr = await baseSigner.getAddress();
const chainId = parseInt(args.chainId);
const connectedSigner = baseSigner.connect(await getProvider(chainId));
const l2Token = TOKEN_SYMBOLS_MAP.WETH?.addresses[chainId];
assert(l2Token, `WETH not found on chain ${chainId} in TOKEN_SYMBOLS_MAP`);
const l2Token = TOKEN_SYMBOLS_MAP[args.token]?.addresses[chainId];
assert(l2Token, `${args.token} not found on chain ${chainId} in TOKEN_SYMBOLS_MAP`);
const l1TokenInfo = getL1TokenInfo(l2Token, chainId);
console.log("Fetched L1 token info:", l1TokenInfo);
assert(l1TokenInfo.symbol === "ETH", "Only WETH withdrawals are supported for now.");
const amount = args.amount;
const amountFromWei = ethers.utils.formatUnits(amount, l1TokenInfo.decimals);
console.log(`Amount to bridge from chain ${chainId}: ${amountFromWei} ${l2Token}`);
Expand All @@ -54,33 +55,51 @@ export async function run(): Promise<void> {
const currentBalance = await erc20.balanceOf(signerAddr);
const currentEthBalance = await connectedSigner.getBalance();
console.log(
`Current WETH balance for account ${signerAddr}: ${fromWei(currentBalance, l1TokenInfo.decimals)} ${l2Token}`
`Current ${l1TokenInfo.symbol} balance for account ${signerAddr}: ${fromWei(
currentBalance,
l1TokenInfo.decimals
)} ${l2Token}`
);
console.log(`Current ETH balance for account ${signerAddr}: ${fromWei(currentEthBalance, l1TokenInfo.decimals)}`);
console.log(`Current ETH balance for account ${signerAddr}: ${fromWei(currentEthBalance)}`);

// First offer user option to unwrap WETH into ETH.
const weth = new Contract(l2Token, WETH9.abi, connectedSigner);
if (await askYesNoQuestion(`\nUnwrap ${amount} of WETH @ ${weth.address}?`)) {
const unwrap = await weth.withdraw(amount);
console.log(`Submitted transaction: ${blockExplorerLink(unwrap.hash, chainId)}.`);
const receipt = await unwrap.wait();
console.log("Unwrap complete...", receipt);
// First offer user option to unwrap WETH into ETH
if (l1TokenInfo.symbol === "ETH") {
const weth = new Contract(l2Token, WETH9.abi, connectedSigner);
if (await askYesNoQuestion(`\nUnwrap ${amount} of WETH @ ${weth.address}?`)) {
const unwrap = await weth.withdraw(amount);
console.log(`Submitted transaction: ${blockExplorerLink(unwrap.hash, chainId)}.`);
const receipt = await unwrap.wait();
console.log("Unwrap complete...", receipt);
}
}

// Now, submit a withdrawal:
// Now, submit a withdrawal. This might fail if the ERC20 uses a non-standard OVM bridge to withdraw.
const ovmStandardBridgeObj = CONTRACT_ADDRESSES[chainId].ovmStandardBridge;
assert(CONTRACT_ADDRESSES[chainId].ovmStandardBridge, "ovmStandardBridge for chain not found in CONTRACT_ADDRESSES");
const ovmStandardBridge = new Contract(ovmStandardBridgeObj.address, ovmStandardBridgeObj.abi, connectedSigner);
const bridgeETHToArgs = [
signerAddr, // to
200_000, // minGasLimit
"0x", // extraData
{ value: amount }, // msg.value
];
const bridgeArgs =
l1TokenInfo.symbol === "ETH"
? [
signerAddr, // to
200_000, // minGasLimit
"0x", // extraData
{ value: amount }, // msg.value
]
: [
l2Token, // _localToken
TOKEN_SYMBOLS_MAP[args.token]?.addresses[CHAIN_IDs.MAINNET], // Remote token to be received on L1 side. If the
// remoteL1Token on the other chain does not recognize the local token as the correct
// pair token, the ERC20 bridge will fail and the tokens will be returned to sender on
// this chain.
signerAddr, // _to
amount, // _amount
200_000, // minGasLimit
"0x", // _data
];

console.log(
`Submitting bridgeETHTo on the OVM standard bridge @ ${ovmStandardBridge.address} with the following args: `,
...bridgeETHToArgs
...bridgeArgs
);

// Sanity check that the ovmStandardBridge contract is the one we expect by comparing its stored addresses
Expand All @@ -98,10 +117,14 @@ export async function run(): Promise<void> {
l1StandardBridge === expectedL1StandardBridge,
`Unexpected L1 standard bridge address in ovmStandardBridge contract, expected: ${expectedL1StandardBridge}, got: ${l1StandardBridge}`
);
const customTokenBridge = await spokePool.tokenBridges(l2Token);
assert(customTokenBridge === ZERO_ADDRESS, `Custom token bridge set for token ${l2Token} (${customTokenBridge})`);
if (!(await askYesNoQuestion("\nDo you want to proceed?"))) {
return;
}
const withdrawal = await ovmStandardBridge.bridgeETHTo(...bridgeETHToArgs);
const withdrawal = await ovmStandardBridge[l1TokenInfo.symbol === "ETH" ? "bridgeETHTo" : "bridgeERC20To"](
...bridgeArgs
);
console.log(`Submitted withdrawal: ${blockExplorerLink(withdrawal.hash, chainId)}.`);
const receipt = await withdrawal.wait();
console.log("Receipt", receipt);
Expand Down
6 changes: 5 additions & 1 deletion src/clients/AcrossAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export interface DepositLimits {

const API_UPDATE_RETENTION_TIME = 60; // seconds

export function getAcrossHost(hubChainId: number): string {
return process.env.ACROSS_API_HOST ?? (hubChainId === CHAIN_IDs.MAINNET ? "app.across.to" : "testnet.across.to");
}

export class AcrossApiClient {
private endpoint: string;
private chainIds: number[];
Expand All @@ -36,7 +40,7 @@ export class AcrossApiClient {
readonly timeout: number = 3000
) {
const hubChainId = hubPoolClient.chainId;
this.endpoint = `https://${hubChainId === CHAIN_IDs.MAINNET ? "app.across.to" : "testnet.across.to"}/api`;
this.endpoint = `https://${getAcrossHost(hubChainId)}/api`;
if (Object.keys(tokensQuery).length === 0) {
this.tokensQuery = dedupArray(Object.values(TOKEN_SYMBOLS_MAP).map(({ addresses }) => addresses[hubChainId]));
}
Expand Down
38 changes: 22 additions & 16 deletions src/clients/InventoryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
assert,
compareAddressesSimple,
getUsdcSymbol,
Profiler,
getNativeTokenSymbol,
} from "../utils";
import { HubPoolClient, TokenClient, BundleDataClient } from ".";
Expand Down Expand Up @@ -59,6 +60,7 @@ export class InventoryClient {
private readonly formatWei: ReturnType<typeof createFormatFunction>;
private bundleRefundsPromise: Promise<CombinedRefunds[]> = undefined;
private excessRunningBalancePromises: { [l1Token: string]: Promise<{ [chainId: number]: BigNumber }> } = {};
private profiler: InstanceType<typeof Profiler>;

constructor(
readonly relayer: string,
Expand All @@ -75,6 +77,10 @@ export class InventoryClient {
) {
this.scalar = sdkUtils.fixedPointAdjustment;
this.formatWei = createFormatFunction(2, 4, false, 18);
this.profiler = new Profiler({
logger: this.logger,
at: "InventoryClient",
});
}

/**
Expand Down Expand Up @@ -299,13 +305,15 @@ export class InventoryClient {
async getBundleRefunds(l1Token: string): Promise<{ [chainId: string]: BigNumber }> {
let refundsToConsider: CombinedRefunds[] = [];

let mark: ReturnType<typeof this.profiler.start>;
// Increase virtual balance by pending relayer refunds from the latest valid bundle and the
// upcoming bundle. We can assume that all refunds from the second latest valid bundle have already
// been executed.
let startTimer: number;
if (!isDefined(this.bundleRefundsPromise)) {
startTimer = performance.now();
// @dev Save this as a promise so that other parallel calls to this function don't make the same call.
mark = this.profiler.start("bundleRefunds", {
l1Token,
});
this.bundleRefundsPromise = this.getAllBundleRefunds();
}
refundsToConsider = lodash.cloneDeep(await this.bundleRefundsPromise);
Expand All @@ -327,12 +335,12 @@ export class InventoryClient {
},
{}
);
if (startTimer) {
this.log(`Time taken to get bundle refunds: ${Math.round((performance.now() - startTimer) / 1000)}s`, {
l1Token,
totalRefundsPerChain,
});
}

mark?.stop({
message: "Time to calculate total refunds per chain",
l1Token,
});

return totalRefundsPerChain;
}

Expand Down Expand Up @@ -618,7 +626,8 @@ export class InventoryClient {
): Promise<{ [chainId: number]: BigNumber }> {
const { root: latestPoolRebalanceRoot, blockRanges } = await this.bundleDataClient.getLatestPoolRebalanceRoot();
const chainIds = this.hubPoolClient.configStoreClient.getChainIdIndicesForBlock();
const start = performance.now();

const mark = this.profiler.start("getLatestRunningBalances");
const runningBalances = Object.fromEntries(
await sdkUtils.mapAsync(chainsToEvaluate, async (chainId) => {
const chainIdIndex = chainIds.indexOf(chainId);
Expand Down Expand Up @@ -674,13 +683,10 @@ export class InventoryClient {
];
})
);
this.log(
`Approximated latest (abs. val) running balance for ORU chains for token ${l1Token} in ${
Math.round(performance.now() - start) / 1000
}s`,
{ runningBalances }
);

mark.stop({
message: "Time to get running balances",
runningBalances,
});
return Object.fromEntries(Object.entries(runningBalances).map(([k, v]) => [k, v.absLatestRunningBalance]));
}

Expand Down
Loading

0 comments on commit 514900b

Please sign in to comment.