Skip to content

Commit

Permalink
Merge branch 'develop' into custom-fee-bold-script
Browse files Browse the repository at this point in the history
  • Loading branch information
godzillaba committed Feb 7, 2025
2 parents 96dd27a + 8bde272 commit 934d04f
Show file tree
Hide file tree
Showing 14 changed files with 765 additions and 8 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/contract-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
version: stable

- name: Setup node/yarn
uses: actions/setup-node@v3
Expand Down Expand Up @@ -50,7 +50,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
version: stable

- name: Setup nodejs
uses: actions/setup-node@v2
Expand All @@ -62,7 +62,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
version: stable

- name: Check Contracts Format
run: forge fmt --check
Expand Down Expand Up @@ -128,7 +128,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
version: stable

- uses: OffchainLabs/actions/run-nitro-test-node@main
with:
Expand Down Expand Up @@ -257,7 +257,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
version: stable

- name: Setup node/yarn
uses: actions/setup-node@v3
Expand Down
20 changes: 20 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ const solidity = {
evmVersion: 'cancun',
},
},
'src/mocks/ArbOS11To32UpgradeTest.sol': {
version: '0.8.24',
settings: {
optimizer: {
enabled: true,
runs: 100,
},
evmVersion: 'cancun',
},
},
},
}

Expand Down Expand Up @@ -89,6 +99,16 @@ if (process.env['INTERFACE_TESTER_SOLC_VERSION']) {
evmVersion: 'cancun',
},
},
'src/mocks/ArbOS11To32UpgradeTest.sol': {
version: '0.8.24',
settings: {
optimizer: {
enabled: true,
runs: 100,
},
evmVersion: 'cancun',
},
},
}
}

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@arbitrum/nitro-contracts",
"version": "3.0.0",
"version": "3.0.1-beta.0",
"description": "Layer 2 precompiles and rollup for Arbitrum Nitro",
"author": "Offchain Labs, Inc.",
"license": "BUSL-1.1",
Expand Down Expand Up @@ -40,6 +40,7 @@
"test:storage": "./test/storage/test.bash",
"test:signatures": "./test/signatures/test-sigs.bash",
"test:e2e": "hardhat test test/e2e/*.ts",
"test:e2e:stylus": "hardhat test test/e2e/stylusDeployer.ts",
"test:upgrade": "./scripts/testUpgrade.bash",
"test:foundry": "forge test --gas-limit 10000000000",
"test:update": "yarn run test:signatures || yarn run test:storage",
Expand Down
4 changes: 3 additions & 1 deletion scripts/printMetadataHashes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ async function main() {
'Outbox',
'SequencerInbox',
'Bridge',
'RollupEventInbox',
'ERC20Inbox',
'ERC20Outbox',
'SequencerInbox',
'ERC20Bridge',
'ERC20RollupEventInbox',
'RollupProxy',
'RollupAdminLogic',
'RollupUserLogic',
'ChallengeManager',
'EdgeChallengeManager',
]

// Print the current git tag
Expand Down
2 changes: 1 addition & 1 deletion slither.db.json

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions src/mocks/ArbOS11To32UpgradeTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2024, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.24;

import "../precompiles/ArbSys.sol";

contract ArbOS11To32UpgradeTest {
function mcopy() external returns (bytes32 x) {
assembly {
mstore(0x20, 0x9) // Store 0x9 at word 1 in memory
mcopy(0, 0x20, 0x20) // Copies 0x9 to word 0 in memory
x := mload(0) // Returns 32 bytes "0x9"
}
require(ArbSys(address(0x64)).arbOSVersion() == 55 + 32, "EXPECTED_ARBOS_32");
}
}
23 changes: 23 additions & 0 deletions src/mocks/BigMap.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2025, Offchain Labs, Inc.
// For license information, see:
// https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.0;

contract BigMap {
mapping(uint256 => uint256) public data;
uint256 size;

function clearAndAddValues(uint256 clear, uint256 add) external {
uint256 i = size;
while (i < size + add) {
data[i] = 8675309;
i++;
}
size = i;
for (uint256 j = 0; j < clear; j++) {
data[j] = 0;
}
}
}
164 changes: 164 additions & 0 deletions src/stylus/StylusDeployer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {ArbWasm} from "../precompiles/ArbWasm.sol";

/// @title A Stylus contract deployer, activator and initializer
/// @author The name of the author
/// @notice Stylus contracts do not support constructors. Instead, Stylus devs can use this contract to deploy and
/// initialize their contracts atomically
contract StylusDeployer {
ArbWasm constant ARB_WASM = ArbWasm(0x0000000000000000000000000000000000000071);

event ContractDeployed(address deployedContract);

error ContractDeploymentError(bytes bytecode);
error ContractInitializationError(address newContract);
error RefundExcessValueError(uint256 excessValue);
error EmptyBytecode();
error InitValueButNotInitData();

/// @notice Deploy, activate and initialize a stylus contract
/// In order to call a stylus contract, and therefore initialize it, it must first be activated.
/// This contract provides an atomic way of deploying, activating and initializing a stylus contract.
///
/// Initialisation will be called if initData is supplied, other initialization is skipped
/// Activation is not always necessary. If a contract has the same code has as another recently activated
/// contract then activation will be skipped.
/// If additional value remains in the contract after activation it will be transferred to the msg.sender
/// to that end the caller must ensure that they can receive eth.
///
/// The caller should do the following before calling this contract:
/// 1. Check whether the contract will require activation, and if so what the cost will be.
/// This can be done by spoofing an address with the contract code, then calling ArbWasm.programVersion to compare the
/// the returned result against ArbWasm.stylusVersion. If activation is required ArbWasm.activateProgram can then be called
/// to find the returned dataFee.
/// 2. Next this deploy function can be called. The value of the call must be set to the previously ascertained dataFee + initValue
/// If activation is not require, the value of the call should be set to initValue
///
/// Note: Stylus contract caching is not done by the deployer, and will have to be done separately after deployment.
/// See https://docs.arbitrum.io/stylus/how-tos/caching-contracts for more details on caching
/// @param bytecode The bytecode of the stylus contract to be deployed
/// @param initData Initialisation call data. After deployment the contract will be called with this data
/// If no initialisation data is provided then the newly deployed contract will not be called.
/// This means that the receive or dataless fallback function cannot be called from this contract.
/// @param initValue Initialisation value. After deployment, the contract will be called with the init data and this value.
/// At least as much eth as init value must be provided with this call. Init value is specified here separately
/// rather than using the msg.value since the msg.value may need to be greater than the init value to accomodate activation data fee.
/// See the @notice block above for more details.
/// @param salt If a non zero salt is provided the contract will be created using CREATE2 instead of CREATE
/// The supplied salt will be hashed with the initData so that wherever the address is observed
/// it was initialised with the same variables.
/// @return The address of the deployed conract
function deploy(
bytes calldata bytecode,
bytes calldata initData,
uint256 initValue,
bytes32 salt
) public payable returns (address) {
if (salt != 0) {
// if a salt was supplied, hash the salt with the init data. This guarantees that
// anywhere the address of this contract is seen the same init data was used
salt = initSalt(salt, initData);
}

address newContractAddress = deployContract(bytecode, salt);
bool shouldActivate = requiresActivation(newContractAddress);
uint256 dataFee = 0;
if (shouldActivate) {
// ensure there will be enough left over for init
// activateProgram will return unused value back to this contract without an EVM call
uint256 activationValue = msg.value - initValue;
(, dataFee) = ARB_WASM.activateProgram{value: activationValue}(newContractAddress);
}

// initialize - this will fail if the program wasn't activated by this point
// we check if initData exists to avoid calling contracts unnecessarily
if (initData.length != 0) {
(bool success,) = address(newContractAddress).call{value: initValue}(initData);
if (!success) {
revert ContractInitializationError(newContractAddress);
}
} else if (initValue != 0) {
// if initValue exists init data should too
revert InitValueButNotInitData();
}

// refund any remaining value
uint256 bal = msg.value - dataFee - initValue;
if (bal != 0) {
// the caller must be payable
(bool sent,) = payable(msg.sender).call{value: bal}("");
if (!sent) {
revert RefundExcessValueError(bal);
}
}

// activation already emits the following event:
// event ProgramActivated(bytes32 indexed codehash, bytes32 moduleHash, address program, uint256 dataFee, uint16 version);
emit ContractDeployed(newContractAddress);

return newContractAddress;
}

/// @notice When using CREATE2 the deployer includes the init data and value in the salt so that callers
/// can be sure that wherever they encourter this address it was initialized with the same data and value
/// @param salt A user supplied salt
/// @param initData The init data that will be used to init the deployed contract
function initSalt(bytes32 salt, bytes calldata initData) public pure returns (bytes32) {
return keccak256(abi.encodePacked(salt, initData));
}

/// @notice Checks whether a contract requires activation
function requiresActivation(
address addr
) public view returns (bool) {
// currently codeHashVersion returns an error when codeHashVersion != stylus version
// so we do a try/catch to to check it
uint16 codeHashVersion;
try ARB_WASM.codehashVersion(addr.codehash) returns (uint16 version) {
codeHashVersion = version;
} catch {
// stylus version is always >= 1
codeHashVersion = 0;
}

// due to the bug in ARB_WASM.codeHashVersion we know that codeHashVersion will either be 0 or the current
// version. We still check that is not equal to the stylusVersion
return codeHashVersion != ARB_WASM.stylusVersion();
}

/// @notice Deploy the a contract with the supplied bytecode.
/// Will create2 if the supplied salt is non zero
function deployContract(bytes memory bytecode, bytes32 salt) internal returns (address) {
if (bytecode.length == 0) {
revert EmptyBytecode();
}

address newContractAddress;
if (salt != 0) {
assembly {
newContractAddress := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
}
} else {
assembly {
newContractAddress := create(0, add(bytecode, 0x20), mload(bytecode))
}
}

// bubble up the revert if there was one
assembly {
if and(iszero(newContractAddress), not(iszero(returndatasize()))) {
let p := mload(0x40)
returndatacopy(p, 0, returndatasize())
revert(p, returndatasize())
}
}

if (newContractAddress == address(0)) {
revert ContractDeploymentError(bytecode);
}

return newContractAddress;
}
}
38 changes: 38 additions & 0 deletions src/test-helpers/NoReceiveForwarder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice A call forward that doesnt implement the receive or fallback functions, so cant receive value
contract NoReceiveForwarder {
function forward(address to, bytes calldata data) public payable {
(bool success,) = address(to).call{value: msg.value}(data);
require(success, "call forward failed");
}
}

/// @notice A call forward that does implement the receive or fallback functions, so cant receive value
contract ReceivingForwarder {
function forward(address to, bytes calldata data) public payable {
(bool success,) = address(to).call{value: msg.value}(data);
require(success, "call forward failed");
}

receive() external payable {}
}

/// @notice Errors upon construction
contract ConstructorError {
constructor() {
require(false, "test error in constructor");
}
}

/// @notice Errors upon construction
contract ConstructorFine {
constructor() {
require(true, "test error in constructor");
}

function number() public pure returns (uint256) {
return 0;
}
}
Loading

0 comments on commit 934d04f

Please sign in to comment.