Skip to content
Open
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: 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