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

feat: hardhat task to test chain adapter token bridging #669

Merged
merged 5 commits into from
Oct 28, 2024
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
8 changes: 7 additions & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ import "hardhat-deploy";
import "@openzeppelin/hardhat-upgrades";

// Custom tasks to add to HRE.
const tasks = ["enableL1TokenAcrossEcosystem", "finalizeScrollClaims", "rescueStuckScrollTxn", "verifySpokePool"];
const tasks = [
"enableL1TokenAcrossEcosystem",
"finalizeScrollClaims",
"rescueStuckScrollTxn",
"verifySpokePool",
"testChainAdapter",
];
// eslint-disable-next-line node/no-missing-require
tasks.forEach((task) => require(`./tasks/${task}`));

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"prepublish": "yarn build && hardhat export --export-all ./cache/massExport.json && ts-node ./scripts/processHardhatExport.ts && prettier --write ./deployments/deployments.json && yarn generate-contract-types"
},
"dependencies": {
"@across-protocol/constants": "^3.1.16",
"@across-protocol/constants": "^3.1.17",
"@coral-xyz/anchor": "^0.30.1",
"@defi-wonderland/smock": "^2.3.4",
"@eth-optimism/contracts": "^0.5.40",
Expand Down
89 changes: 89 additions & 0 deletions tasks/testChainAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import assert from "assert";
import { getMnemonic } from "@uma/common";
import { task } from "hardhat/config";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "../utils/constants";
import { askYesNoQuestion } from "./utils";

// Chain adapter names are not 1:1 consistent with chain names, so some overrides are needed.
const chains = {
[CHAIN_IDs.ARBITRUM]: "Arbitrum_Adapter",
[CHAIN_IDs.ALEPH_ZERO]: "Arbitrum_CustomGasToken_Adapter",
[CHAIN_IDs.WORLD_CHAIN]: "WorldChain_Adapter",
[CHAIN_IDs.ZK_SYNC]: "ZkSync_Adapter",
};

task("testChainAdapter", "Verify a chain adapter")
.addParam("chain", "chain ID of the adapter being tested")
.addParam("token", "Token to bridge to the destination chain")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably addParam("amount", ...) here to allow the user to specify the amount to bridge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.addParam("amount", "Amount to bridge to the destination chain")
.setAction(async function (args, hre: HardhatRuntimeEnvironment) {
const { deployments, ethers, getChainId, network } = hre;
const provider = new ethers.providers.StaticJsonRpcProvider(network.config.url);
const signer = new ethers.Wallet.fromMnemonic(getMnemonic()).connect(provider);

const hubChainId = await getChainId();
const { address: hubPoolAddress, abi: hubPoolAbi } = await deployments.get("HubPool");
const hubPool = new ethers.Contract(hubPoolAddress, hubPoolAbi, provider);
const spokeChainId = parseInt(args.chain);

const [spokeName] = Object.entries(CHAIN_IDs).find(([, chainId]) => chainId === spokeChainId) ?? [];
assert(spokeName, `Could not find any chain entry for chainId ${spokeChainId}.`);
const adapterName =
chains[spokeChainId] ?? `${spokeName[0].toUpperCase()}${spokeName.slice(1).toLowerCase()}_Adapter`;

const { address: adapterAddress, abi: adapterAbi } = await deployments.get(adapterName);
const adapter = new ethers.Contract(adapterAddress, adapterAbi, provider);
const tokenSymbol = args.token.toUpperCase();
const tokenAddress = TOKEN_SYMBOLS_MAP[tokenSymbol].addresses[hubChainId];

// For USDC this will resolve to native USDC on CCTP-enabled chains.
const l2Token = await hubPool.poolRebalanceRoute(spokeChainId, tokenAddress);
if (l2Token === ethers.constants.AddressZero) {
const proceed = await askYesNoQuestion(
`\t\nWARNING: ${tokenSymbol} maps to address ${l2Token} on chain ${spokeChainId}\n\t\nProceed ?`
);
if (!proceed) process.exit(0);
} else {
console.log(`Resolved ${tokenSymbol} l2 token address on chain ${spokeChainId}: ${l2Token}.`);
}

const erc20 = (await ethers.getContractFactory("ExpandedERC20")).attach(tokenAddress);
let balance = await erc20.balanceOf(adapterAddress);
const decimals = await erc20.decimals();
const { amount } = args;
const scaledAmount = ethers.utils.parseUnits(amount, decimals);

if (balance.lt(amount)) {
const proceed = await askYesNoQuestion(
`\t\nWARNING: ${amount} ${tokenSymbol} may be lost.\n` +
`\t\nProceed to send ${amount} ${tokenSymbol} to chain adapter ${adapterAddress} ?`
);
if (!proceed) process.exit(0);

const txn = await erc20.connect(signer).transfer(adapterAddress, scaledAmount);
console.log(`Transferring ${amount} ${tokenSymbol} -> ${adapterAddress}: ${txn.hash}`);
await txn.wait();
}

balance = await erc20.balanceOf(adapterAddress);
const recipient = await signer.getAddress();

let populatedTxn = await adapter.populateTransaction.relayTokens(tokenAddress, l2Token, balance, recipient);
const gasLimit = await provider.estimateGas(populatedTxn);

// Any adapter requiring msg.value > 0 (i.e. Scroll) will fail here.
const txn = await adapter.connect(signer).relayTokens(
tokenAddress,
l2Token,
balance,
recipient,
{ gasLimit: gasLimit.mul(2) } // 2x the gas limit; this helps on OP stack bridges.
);

console.log(
`Relaying ${balance} ${tokenSymbol} from ${adapterAddress}` +
` to chain ${spokeChainId} recipient ${recipient}: ${txn.hash}.`
);
await txn.wait();
});
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
# yarn lockfile v1


"@across-protocol/constants@^3.1.16":
version "3.1.16"
resolved "https://registry.yarnpkg.com/@across-protocol/constants/-/constants-3.1.16.tgz#c126085d29d4d051fd02a04c833d804d37c3c219"
integrity sha512-+U+AecGWnfY4b4sSfKBvsDj/+yXKEqpTXcZgI8GVVmUTkUhs1efA0kN4q3q10yy5TXI5TtagaG7R9yZg1zgKKg==
"@across-protocol/constants@^3.1.17":
version "3.1.17"
resolved "https://registry.yarnpkg.com/@across-protocol/constants/-/constants-3.1.17.tgz#1e43c87fbab06df3a1aab8993d7c5f746838e25d"
integrity sha512-mW++45vP04ahogsHFM5nE7ZKV8vmdxSO8gbBeP24VFxijrIYWqhISG67EU9pnAbgAIB58pT+ZmqdXI25iDmctA==

"@across-protocol/contracts@^0.1.4":
version "0.1.4"
Expand Down
Loading