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
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
12 changes: 11 additions & 1 deletion deploy/L2TokenGatewaySpell.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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");
Expand All @@ -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
Expand Up @@ -60,6 +60,7 @@ struct GatewaysConfig {
address inbox;
address[] l1Tokens;
address[] l2Tokens;
uint256[] maxWithdraws;
MessageParams xchainMsg;
}

Expand All @@ -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;

Expand All @@ -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);
Expand All @@ -107,7 +110,8 @@ library TokenGatewayInit {
l1Gateway_,
l1Router.counterpartGateway(),
cfg.l1Tokens,
cfg.l2Tokens
cfg.l2Tokens,
cfg.maxWithdraws
)),
l1CallValue: l1CallValue,
maxGas: cfg.xchainMsg.maxGas,
Expand Down
7 changes: 6 additions & 1 deletion script/Init.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -63,7 +67,8 @@ contract Init is Script {
l1Gateway,
deps.readAddress(".l2Router"),
cfg.l1Tokens,
cfg.l2Tokens
cfg.l2Tokens,
cfg.maxWithdraws
))
));
cfg.xchainMsg = MessageParams({
Expand Down
8 changes: 8 additions & 0 deletions src/L2TokenGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 ---
Expand All @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 9 additions & 5 deletions test/Integration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand Down
18 changes: 17 additions & 1 deletion test/L2TokenGateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}

Expand All @@ -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
]);
}

Expand All @@ -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);

Expand Down Expand Up @@ -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");
Expand Down