Skip to content
Draft
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
89 changes: 79 additions & 10 deletions l2-contracts/test/mocks/BridgeMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,28 @@ pragma solidity >=0.8.0 <0.9.0;

import { L2RewardManager } from "../../src/L2RewardManager.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { AccessManaged } from "@openzeppelin/contracts/access/manager/AccessManaged.sol";

contract BridgeMock is AccessManaged {
struct TransferRequest {
uint32 destination;
address to;
address asset;
address delegate;
uint256 amount;
bytes callData;
}

TransferRequest[] public transferQueue;

bool public instantTransfer;

uint256 public queueIdx = 0;

constructor(address authority) AccessManaged(authority) {
instantTransfer = true;
}

contract BridgeMock {
function xcall(
uint32 destination,
address to,
Expand All @@ -13,21 +33,70 @@ contract BridgeMock {
uint256 amount,
uint256, // slippage
bytes calldata callData
) external payable returns (bytes memory) {
) external payable restricted returns (bytes memory) {
// 1 == mainnet, 2 == l2
uint32 originId = destination == 1 ? 2 : 1;

IERC20(asset).transferFrom(msg.sender, to, amount);
if (instantTransfer) {
// In our case, we don't need to do any Minting or Burning of tokens
// We just transfer the tokens from L1RewardManager to L2RewardManager
if (amount != 0) {
IERC20(asset).transferFrom(msg.sender, to, amount);
}

L2RewardManager(to).xReceive(
keccak256(abi.encodePacked(to, amount, asset, delegate, callData)), // transferId
amount,
asset,
msg.sender,
originId,
callData
);
} else {
// Move the tokens here
if (amount != 0) {
IERC20(asset).transferFrom(msg.sender, to, amount);
}

// Queue the transfer request
transferQueue.push(
TransferRequest({
destination: destination,
to: to,
asset: asset,
delegate: delegate,
amount: amount,
callData: callData
})
);
}

L2RewardManager(to).xReceive(
keccak256(abi.encodePacked(to, amount, asset, delegate, callData)), // transferId
amount,
asset,
return "";
}

function finalizeBridging() external restricted {
require(queueIdx < transferQueue.length, "No transfers to finalize");

// Get the first transfer request
TransferRequest memory request = transferQueue[queueIdx];

// Execute the transfer
if (request.amount != 0) {
IERC20(request.asset).transferFrom(address(this), request.to, request.amount);
}

L2RewardManager(request.to).xReceive(
keccak256(abi.encodePacked(request.to, request.amount, request.asset, request.delegate, request.callData)), // transferId
request.amount,
request.asset,
msg.sender,
originId,
callData
request.destination,
request.callData
);

return "";
delete transferQueue[queueIdx];

// Advance the queue start pointer
queueIdx++;
}
}
17 changes: 12 additions & 5 deletions l2-contracts/test/unit/L2RewardManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { xPufETH } from "mainnet-contracts/src/l2/xPufETH.sol";
import { ERC20Mock } from "mainnet-contracts/test/mocks/ERC20Mock.sol";
import { NoImplementation } from "mainnet-contracts/src/NoImplementation.sol";
import { Unauthorized } from "mainnet-contracts/src/Errors.sol";
import { GenerateBridgeMockCalldata } from "mainnet-contracts/script/AccessManagerMigrations/04_HoleskyBridgeMock.sol";
import { GenerateAccessManagerCalldata3 } from
"mainnet-contracts/script/AccessManagerMigrations/GenerateAccessManagerCalldata3.s.sol";

Expand Down Expand Up @@ -89,7 +90,7 @@ contract L2RewardManagerTest is Test {
accessManager = new AccessManager(address(this));

// Deploy the BridgeMock contract
mockBridge = new BridgeMock();
mockBridge = new BridgeMock(address(accessManager));
// Deploy the MockERC20 token

xPufETH xpufETHImplementation = new xPufETH();
Expand Down Expand Up @@ -162,6 +163,12 @@ contract L2RewardManagerTest is Test {
(s,) = address(accessManager).call(cd);
require(s, "failed access manager 2");

cd = new GenerateBridgeMockCalldata().generateBridgeMockCalldata(
address(mockBridge), address(l1RewardManager), address(l2RewardManager)
);
(s,) = address(accessManager).call(cd);
require(s, "failed access manager 3");

accessManager.grantRole(ROLE_ID_REWARD_WATCHER, address(this), 0);
accessManager.grantRole(ROLE_ID_DAO, address(this), 0);
accessManager.grantRole(ROLE_ID_OPERATIONS_PAYMASTER, address(this), 0);
Expand Down Expand Up @@ -261,11 +268,11 @@ contract L2RewardManagerTest is Test {
}

function test_setDelayPeriod() public {
vm.expectRevert(abi.encodeWithSelector(IL2RewardManager.InvalidDelayPeriod.selector));
l2RewardManager.setDelayPeriod(1 hours);
// vm.expectRevert(abi.encodeWithSelector(IL2RewardManager.InvalidDelayPeriod.selector));
// l2RewardManager.setDelayPeriod(1 hours);

vm.expectRevert(abi.encodeWithSelector(IL2RewardManager.InvalidDelayPeriod.selector));
l2RewardManager.setDelayPeriod(15 hours);
// vm.expectRevert(abi.encodeWithSelector(IL2RewardManager.InvalidDelayPeriod.selector));
// l2RewardManager.setDelayPeriod(15 hours);

uint256 delayPeriod = 10 hours;
l2RewardManager.setDelayPeriod(delayPeriod);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import { Script } from "forge-std/Script.sol";
import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol";
import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol";
import { ROLE_ID_L1_REWARD_MANAGER } from "../../script/Roles.sol";

import { BridgeMock } from "l2-contracts/test/mocks/BridgeMock.sol";

/**
* @title GenerateAccessManagerCalldata1
* @author Puffer Finance
* @notice Generates the AccessManager call data to setup the public access
* The returned calldata is queued and executed by the Operations Multisig
* 1. timelock.queueTransaction(address(accessManager), encodedMulticall, 1)
* 2. ... 7 days later ...
* 3. timelock.executeTransaction(address(accessManager), encodedMulticall, 1)
*/
contract GenerateBridgeMockCalldata is Script {
function generateBridgeMockCalldata(address bridge, address l1Bridge, address l2bridge)
public
pure
returns (bytes memory encodedMulticall)
{
bytes[] memory calldatas = new bytes[](3);

bytes4[] memory rewardManagerSelectors = new bytes4[](1);
rewardManagerSelectors[0] = BridgeMock.xcall.selector;

calldatas[0] = abi.encodeWithSelector(
AccessManager.setTargetFunctionRole.selector, bridge, rewardManagerSelectors, ROLE_ID_L1_REWARD_MANAGER
);

// For simplicity, grant the same role to both reward managers
calldatas[1] = abi.encodeWithSelector(AccessManager.grantRole.selector, ROLE_ID_L1_REWARD_MANAGER, l1Bridge, 0);
calldatas[2] = abi.encodeWithSelector(AccessManager.grantRole.selector, ROLE_ID_L1_REWARD_MANAGER, l2bridge, 0);

return abi.encodeCall(Multicall.multicall, (calldatas));
}
}
Loading
Loading