Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,6 @@ solana-verify remote submit-job \

## Miscellaneous topics

### Manually Finalizing Scroll Claims from L2 -> L1 (Mainnet | Sepolia)

```shell
yarn hardhat finalize-scroll-claims --l2-address {operatorAddress}
```

### Slither

[Slither](https://github.com/crytic/slither) is a Solidity static analysis framework written in Python 3. It runs a
Expand Down
15 changes: 0 additions & 15 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,6 @@ const getDefaultHardhatConfig = (chainId: number, isTestnet: boolean = false): a
};
};

// Custom tasks to add to HRE.
const tasks = [
"enableL1TokenAcrossEcosystem",
"finalizeScrollClaims",
"rescueStuckScrollTxn",
"verifySpokePool",
"verifyBytecode",
"evmRelayMessageWithdrawal",
"testChainAdapter",
"upgradeSpokePool",
];

// eslint-disable-next-line node/no-missing-require
tasks.forEach((task) => require(`./tasks/${task}`));

const isTest = process.env.IS_TEST === "true";

// To compile with zksolc, `hardhat` must be the default network and its `zksync` property must be true.
Expand Down
111 changes: 111 additions & 0 deletions script/tasks/TestChainAdapter.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { Script } from "forge-std/Script.sol";
import { console } from "forge-std/console.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Constants } from "../utils/Constants.sol";
import { DeployedAddresses } from "../utils/DeployedAddresses.sol";

/**
* @title TestChainAdapter
* @notice Foundry script to test a chain adapter by bridging tokens from L1 to L2
* @dev Equivalent to the Hardhat task `testChainAdapter`
*
* Requires MNEMONIC to be set in .env file.
*
* Usage:
* forge script script/tasks/TestChainAdapter.s.sol:TestChainAdapter \
* --sig "run(uint256,address,address,uint256,address)" \
* <spokeChainId> <adapterAddress> <l1Token> <amount> <l2Token> \
* --rpc-url mainnet --broadcast
*
* Example (bridge 1 USDC to Optimism):
* forge script script/tasks/TestChainAdapter.s.sol:TestChainAdapter \
* --sig "run(uint256,address,address,uint256,address)" \
* 10 0x... 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 1000000 0x... \
* --rpc-url mainnet --broadcast
*/
contract TestChainAdapter is Script, Constants, DeployedAddresses {
function run(
uint256 spokeChainId,
address adapterAddress,
address l1Token,
uint256 amount,
address l2Token
) external {
uint256 hubChainId = block.chainid;
require(hubChainId == 1 || hubChainId == 11155111, "Must run on mainnet (1) or sepolia (11155111)");

// Derive signer from mnemonic in .env
string memory mnemonic = vm.envString("MNEMONIC");
uint256 privateKey = vm.deriveKey(mnemonic, 0);
address sender = vm.addr(privateKey);

console.log("");
console.log("=============== Test Chain Adapter ===============");
console.log("Hub Chain ID:", hubChainId);
console.log("Spoke Chain ID:", spokeChainId);
console.log("Adapter:", adapterAddress);
console.log("L1 Token:", l1Token);
console.log("L2 Token:", l2Token);
console.log("Amount:", amount);
console.log("Sender/Recipient:", sender);
console.log("--------------------------------------------------");

IERC20 token = IERC20(l1Token);
uint256 adapterBalance = token.balanceOf(adapterAddress);

console.log("Adapter token balance:", adapterBalance);

vm.startBroadcast(privateKey);

// If adapter doesn't have enough tokens, transfer them
if (adapterBalance < amount) {
uint256 needed = amount - adapterBalance;
console.log("Transferring tokens to adapter:", needed);

// Note: This transfer comes from the broadcasting wallet (the signer)
// The signer must have approved or have sufficient balance
bool success = token.transfer(adapterAddress, needed);
require(success, "Token transfer failed");
console.log("Transfer complete");

// Re-check balance after transfer is confirmed
adapterBalance = token.balanceOf(adapterAddress);
console.log("Adapter balance after transfer:", adapterBalance);
}

// Call relayTokens on the adapter
console.log("Calling relayTokens...");
IAdapter(adapterAddress).relayTokens(l1Token, l2Token, adapterBalance, sender);

console.log("--------------------------------------------------");
console.log("[SUCCESS] Tokens relayed to chain", spokeChainId);
console.log("=================================================");

vm.stopBroadcast();
}

/// @notice Simplified version that looks up adapter from deployed addresses
function runWithLookup(
uint256 spokeChainId,
string calldata adapterName,
address l1Token,
uint256 amount,
address l2Token
) external {
uint256 hubChainId = block.chainid;

// Try to get adapter from deployed addresses
address adapterAddress = getAddress(hubChainId, adapterName);
require(adapterAddress != address(0), string.concat("Adapter not found: ", adapterName));

this.run(spokeChainId, adapterAddress, l1Token, amount, l2Token);
}
}

/// @notice Minimal interface for chain adapter
interface IAdapter {
function relayTokens(address l1Token, address l2Token, uint256 amount, address to) external payable;
}
66 changes: 66 additions & 0 deletions script/tasks/UpgradeSpokePool.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { Script } from "forge-std/Script.sol";
import { console } from "forge-std/console.sol";

// Minimal interface for SpokePool upgrade functions
interface ISpokePoolUpgradeable {
function pauseDeposits(bool pause) external;
function multicall(bytes[] calldata data) external returns (bytes[] memory results);
function upgradeToAndCall(address newImplementation, bytes memory data) external payable;
}

/**
* @title UpgradeSpokePool
* @notice Generate calldata to upgrade a SpokePool deployment
* @dev This script generates the calldata needed to call relaySpokePoolAdminFunction()
* on the HubPool from the owner's account.
*
* Usage:
* forge script script/tasks/UpgradeSpokePool.s.sol:UpgradeSpokePool \
* --sig "run(address)" <NEW_IMPLEMENTATION_ADDRESS> \
* -vvvv
*
* Example:
* forge script script/tasks/UpgradeSpokePool.s.sol:UpgradeSpokePool \
* --sig "run(address)" 0x1234567890123456789012345678901234567890 \
* -vvvv
*/
contract UpgradeSpokePool is Script {
function run(address implementation) external view {
require(implementation != address(0), "Implementation address cannot be zero");

/**
* We perform this seemingly unnecessary pause/unpause sequence because we want to ensure that the
* upgrade is successful and the new implementation gets forwarded calls by the proxy contract as expected.
*
* Since the upgrade and call happens atomically, the upgrade will revert if the new implementation
* is not functioning correctly.
*/
bytes[] memory multicallData = new bytes[](2);
multicallData[0] = abi.encodeWithSelector(ISpokePoolUpgradeable.pauseDeposits.selector, true);
multicallData[1] = abi.encodeWithSelector(ISpokePoolUpgradeable.pauseDeposits.selector, false);

bytes memory data = abi.encodeWithSelector(ISpokePoolUpgradeable.multicall.selector, multicallData);
bytes memory calldata_ = abi.encodeWithSelector(
ISpokePoolUpgradeable.upgradeToAndCall.selector,
implementation,
data
);

console.log("=======================================================");
console.log("SpokePool Upgrade Calldata Generator");
console.log("=======================================================");
console.log("");
console.log("New Implementation Address:", implementation);
console.log("");
console.log("To upgrade a SpokePool on chain <chainId>:");
console.log("Call relaySpokePoolAdminFunction() on the HubPool with:");
console.log(" - chainId: <TARGET_CHAIN_ID>");
console.log(" - calldata:");
console.logBytes(calldata_);
console.log("");
console.log("=======================================================");
}
}
2 changes: 1 addition & 1 deletion scripts/veryfyBytecode.sh → scripts/verifyBytecode.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ if [[ $CODE_ONCHAIN == $CREATION ]]; then
echo "✅ Code match"
else
echo "❌ Code mismatch"
fi
fi
Loading
Loading