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

fix(protocol): fix a bug in ForkRouter #18831

Merged
merged 3 commits into from
Jan 24, 2025
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
9 changes: 7 additions & 2 deletions packages/protocol/contracts/layer1/based/ForkRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ contract ForkRouter is UUPSUpgradeable, Ownable2StepUpgradeable {
address public immutable newFork;

error InvalidParams();
error NewForkInvalid();
error NewForkNotActive();
error ZeroAddress();

Expand All @@ -39,8 +40,12 @@ contract ForkRouter is UUPSUpgradeable, Ownable2StepUpgradeable {
_fallback();
}

function currentFork() public view returns (address) {
return IFork(newFork).isForkActive() ? newFork : oldFork;
function currentFork() public returns (address) {
(bool success, bytes memory isActive) =
newFork.delegatecall(abi.encodeCall(IFork.isForkActive, ()));

require(success, NewForkInvalid());
return abi.decode(isActive, (bool)) ? newFork : oldFork;
}

function isForkRouter() public pure returns (bool) {
Expand Down
80 changes: 80 additions & 0 deletions packages/protocol/test/layer1/based/ForkRouter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,61 @@ contract Fork is EssentialContract, IFork {
}
}

contract ForkFooA is EssentialContract, IFork {
struct State {
uint64 a;
}

State private _state;

constructor() EssentialContract(address(0)) { }

function init() external initializer {
__Essential_init(address(0));
_state.a = 1;
}

function setCounter(uint64 _a) external returns (uint64) {
_state.a = _a;
return _state.a;
}

function counter() external view returns (uint64) {
return _state.a;
}

function isForkActive() external view override returns (bool) {
return _state.a < 10;
}
}

contract ForkFooB is EssentialContract, IFork {
struct State {
uint64 b;
}

State private _state;

constructor() EssentialContract(address(0)) { }

function init() external initializer {
__Essential_init(address(0));
}

function setCounter(uint64 _b) external returns (uint64) {
_state.b = _b;
return _state.b;
}

function counter() external view returns (uint64) {
return _state.b;
}

function isForkActive() external view override returns (bool) {
return _state.b >= 10;
}
}

contract TestForkRouter is Layer1Test {
function test_ForkManager_default_routing() public transactBy(deployer) {
address fork1 = address(new Fork("fork1", true));
Expand Down Expand Up @@ -69,4 +124,29 @@ contract TestForkRouter is Layer1Test {
assertTrue(ForkRouter(payable(router)).isForkRouter());
assertEq(Fork(router).name(), "fork2");
}

function test_ForkManager_readStorage() public transactBy(deployer) {
address fork1 = address(new ForkFooA());
address fork2 = address(new ForkFooB());

address router = deploy({
name: "fork_router",
impl: address(new ForkRouter(fork1, fork2)),
data: abi.encodeCall(Fork.init, ())
});

assertEq(ForkRouter(payable(router)).currentFork(), fork1);
assertEq(ForkFooA(router).counter(), 1);
assertEq(ForkFooA(router).setCounter(2), 2);
assertEq(ForkFooA(router).counter(), 2);
assertEq(ForkRouter(payable(router)).currentFork(), fork1);

assertEq(ForkFooA(router).setCounter(10), 10);
assertEq(ForkRouter(payable(router)).currentFork(), fork2);
assertEq(ForkFooA(router).setCounter(11), 11);
assertEq(ForkRouter(payable(router)).currentFork(), fork2);

assertEq(ForkFooA(router).setCounter(9), 9);
assertEq(ForkRouter(payable(router)).currentFork(), fork1);
}
}
Loading