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

Cap withdrawal amounts #10

Merged
merged 1 commit into from
Oct 8, 2024
Merged
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
12 changes: 11 additions & 1 deletion deploy/L2TokenGatewaySpell.sol
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ interface L2TokenGatewayLike {
function deny(address) external;
function close() external;
function registerToken(address, address) external;
function setMaxWithdraw(address, uint256) external;
}

interface AuthLike {
@@ -49,13 +50,21 @@ contract L2TokenGatewaySpell {
unchecked { ++i; }
}
}

function setMaxWithdraws(address[] memory l2Tokens, uint256[] memory maxWithdraws) public {
for (uint256 i; i < l2Tokens.length;) {
l2Gateway.setMaxWithdraw(l2Tokens[i], maxWithdraws[i]);
unchecked { ++i; }
}
}

function init(
address l2Gateway_,
address counterpartGateway,
address l2Router,
address[] calldata l1Tokens,
address[] calldata l2Tokens
address[] calldata l2Tokens,
uint256[] calldata maxWithdraws
) external {
// sanity checks
require(address(l2Gateway) == l2Gateway_, "L2TokenGatewaySpell/l2-gateway-mismatch");
@@ -64,5 +73,6 @@ contract L2TokenGatewaySpell {
require(l2Gateway.l2Router() == l2Router, "L2TokenGatewaySpell/l2-router-mismatch");

registerTokens(l1Tokens, l2Tokens);
setMaxWithdraws(l2Tokens, maxWithdraws);
}
}
6 changes: 5 additions & 1 deletion deploy/TokenGatewayInit.sol
Original file line number Diff line number Diff line change
@@ -60,6 +60,7 @@ struct GatewaysConfig {
address inbox;
address[] l1Tokens;
address[] l2Tokens;
uint256[] maxWithdraws;
MessageParams xchainMsg;
}

@@ -81,6 +82,7 @@ library TokenGatewayInit {
require(l1Gateway.l1Router() == cfg.l1Router, "TokenGatewayInit/l1-router-mismatch");
require(l1Gateway.inbox() == cfg.inbox, "TokenGatewayInit/inbox-mismatch");
require(cfg.l1Tokens.length == cfg.l2Tokens.length, "TokenGatewayInit/token-arrays-mismatch");
require(cfg.maxWithdraws.length == cfg.l2Tokens.length, "TokenGatewayInit/max-withdraws-length-mismatch");

uint256 l1CallValue = cfg.xchainMsg.maxSubmissionCost + cfg.xchainMsg.maxGas * cfg.xchainMsg.gasPriceBid;

@@ -94,6 +96,7 @@ library TokenGatewayInit {
(address l1Token, address l2Token) = (cfg.l1Tokens[i], cfg.l2Tokens[i]);
require(l1Token != address(0), "TokenGatewayInit/invalid-l1-token");
require(l2Token != address(0), "TokenGatewayInit/invalid-l2-token");
require(cfg.maxWithdraws[i] > 0, "TokenGatewayInit/max-withdraw-not-set");
require(l1Gateway.l1ToL2Token(l1Token) == address(0), "TokenGatewayInit/existing-l1-token");

l1Gateway.registerToken(l1Token, l2Token);
@@ -107,7 +110,8 @@ library TokenGatewayInit {
l1Gateway_,
l1Router.counterpartGateway(),
cfg.l1Tokens,
cfg.l2Tokens
cfg.l2Tokens,
cfg.maxWithdraws
)),
l1CallValue: l1CallValue,
maxGas: cfg.xchainMsg.maxGas,
7 changes: 6 additions & 1 deletion script/Init.s.sol
Original file line number Diff line number Diff line change
@@ -55,6 +55,10 @@ contract Init is Script {
cfg.inbox = deps.readAddress(".inbox");
cfg.l1Tokens = deps.readAddressArray(".l1Tokens");
cfg.l2Tokens = deps.readAddressArray(".l2Tokens");
cfg.maxWithdraws = new uint256[](cfg.l2Tokens.length);
for (uint256 i; i < cfg.maxWithdraws.length; ++i) {
cfg.maxWithdraws[i] = 10_000_000 ether;
}

bytes memory initCalldata = abi.encodeCall(L2GovernanceRelay.relay, (
deps.readAddress(".l2GatewaySpell"),
@@ -63,7 +67,8 @@ contract Init is Script {
l1Gateway,
deps.readAddress(".l2Router"),
cfg.l1Tokens,
cfg.l2Tokens
cfg.l2Tokens,
cfg.maxWithdraws
))
));
cfg.xchainMsg = MessageParams({
8 changes: 8 additions & 0 deletions src/L2TokenGateway.sol
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ contract L2TokenGateway is ITokenGateway, ICustomGateway, L2ArbitrumMessenger {

mapping(address => uint256) public wards;
mapping(address => address) public l1ToL2Token;
mapping(address => uint256) public maxWithdraws;
uint256 public isOpen = 1;

// --- immutables ---
@@ -44,6 +45,7 @@ contract L2TokenGateway is ITokenGateway, ICustomGateway, L2ArbitrumMessenger {
event Rely(address indexed usr);
event Deny(address indexed usr);
event Closed();
event MaxWithdrawSet(address indexed l2Token, uint256 maxWithdraw);
event DepositFinalized(
address indexed l1Token,
address indexed _from,
@@ -109,6 +111,11 @@ contract L2TokenGateway is ITokenGateway, ICustomGateway, L2ArbitrumMessenger {
emit TokenSet(l1Token, l2Token);
}

function setMaxWithdraw(address l2Token, uint256 maxWithdraw) external auth {
maxWithdraws[l2Token] = maxWithdraw;
emit MaxWithdrawSet(l2Token, maxWithdraw);
}

// --- outbound transfers ---

function outboundTransfer(
@@ -139,6 +146,7 @@ contract L2TokenGateway is ITokenGateway, ICustomGateway, L2ArbitrumMessenger {
require(isOpen == 1, "L2TokenGateway/closed");
address l2Token = l1ToL2Token[l1Token];
require(l2Token != address(0), "L2TokenGateway/invalid-token");
require(amount <= maxWithdraws[l2Token], "L2TokenGateway/amount-too-large");
address from;
bytes memory extraData = data;
(from, extraData) = msg.sender == l2Router ? abi.decode(extraData, (address, bytes)) : (msg.sender, extraData);
14 changes: 9 additions & 5 deletions test/Integration.t.sol
Original file line number Diff line number Diff line change
@@ -123,17 +123,20 @@ contract IntegrationTest is DssTest {
l1Tokens[0] = address(l1Token);
address[] memory l2Tokens = new address[](1);
l2Tokens[0] = address(l2Token);
uint256[] memory maxWithdraws = new uint256[](1);
maxWithdraws[0] = 10_000_000 ether;
MessageParams memory xchainMsg = MessageParams({
gasPriceBid: 0.1 gwei,
maxGas: 300_000,
maxSubmissionCost: 0.01 ether
});
GatewaysConfig memory cfg = GatewaysConfig({
l1Router: L1_ROUTER,
inbox: INBOX,
l1Tokens: l1Tokens,
l2Tokens: l2Tokens,
xchainMsg: xchainMsg
l1Router: L1_ROUTER,
inbox: INBOX,
l1Tokens: l1Tokens,
l2Tokens: l2Tokens,
maxWithdraws: maxWithdraws,
xchainMsg: xchainMsg
});

l1Domain.selectFork();
@@ -150,6 +153,7 @@ contract IntegrationTest is DssTest {

// test L2 side of initGateways
assertEq(l2Gateway.l1ToL2Token(address(l1Token)), address(l2Token));
assertEq(l2Gateway.maxWithdraws(address(l2Token)), 10_000_000 ether);
assertEq(l2Token.wards(address(l2Gateway)), 1);

// Register L1 & L2 gateways in L1 & L2 routers
18 changes: 17 additions & 1 deletion test/L2TokenGateway.t.sol
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ import { AddressAliasHelper } from "src/arbitrum/AddressAliasHelper.sol";
contract L2TokenGatewayTest is DssTest {

event TokenSet(address indexed l1Address, address indexed l2Address);
event MaxWithdrawSet(address indexed l2Token, uint256 maxWithdraw);
event Closed();
event DepositFinalized(
address indexed l1Token,
@@ -57,6 +58,7 @@ contract L2TokenGatewayTest is DssTest {
l2Token.rely(address(gateway));
l2Token.deny(address(this));
gateway.registerToken(l1Token, address(l2Token));
gateway.setMaxWithdraw(address(l2Token), 1_000_000 ether);
vm.etch(ARB_SYS_ADDRESS, address(new ArbSysMock()).code);
}

@@ -80,7 +82,8 @@ contract L2TokenGatewayTest is DssTest {

checkModifier(address(gateway), string(abi.encodePacked("L2TokenGateway", "/not-authorized")), [
gateway.close.selector,
gateway.registerToken.selector
gateway.registerToken.selector,
gateway.setMaxWithdraw.selector
]);
}

@@ -96,6 +99,16 @@ contract L2TokenGatewayTest is DssTest {
assertEq(gateway.calculateL2TokenAddress(address(11)), address(22));
}

function testSetmaxWithdraw() public {
assertEq(gateway.maxWithdraws(address(22)), 0);

vm.expectEmit(true, true, true, true);
emit MaxWithdrawSet(address(22), 123);
gateway.setMaxWithdraw(address(22), 123);

assertEq(gateway.maxWithdraws(address(22)), 123);
}

function testClose() public {
assertEq(gateway.isOpen(), 1);

@@ -123,6 +136,9 @@ contract L2TokenGatewayTest is DssTest {

vm.expectRevert("L2TokenGateway/invalid-token");
gateway.outboundTransfer(address(0xbad), address(0xb0b), 100 ether, 0, 0, "");

vm.expectRevert("L2TokenGateway/amount-too-large");
gateway.outboundTransfer(l1Token, address(0xb0b), 1_000_000 ether + 1, 0, 0, "");

vm.expectRevert("L2TokenGateway/extra-data-not-allowed");
gateway.outboundTransfer(l1Token, address(0xb0b), 100 ether, 0, 0, "bad");