diff --git a/src/Gateway.sol b/src/Gateway.sol index 1e59a23..3da6348 100644 --- a/src/Gateway.sol +++ b/src/Gateway.sol @@ -70,7 +70,7 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 { mapping(bytes32 => GmpInfo) private _messages; // Hash of the previous GMP message submitted. - uint256 private _nonce; + mapping(address => uint256) private _nonces; // Replay protection mechanism, stores the hash of the executed messages // messageHash => shardId @@ -92,13 +92,8 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 { // EIP-712 typed hash function initialize(address proxyAdmin, TssKey[] calldata keys, Network[] calldata networks) external { require(PROXY_ADDRESS == address(this) || msg.sender == FACTORY, "only proxy can be initialize"); - require(_nonce == 0, "already initialized"); ERC1967.setAdmin(proxyAdmin); - // Initialize the `nonce` as one to avoid the first GMP to spent more gas, - // once initialize the storage cost 21k gas, while alter it cost just 2800 gas. - _nonce = 1; - // Register networks RouteStore.getMainStorage().initialize(networks, NetworkID.wrap(NETWORK_ID)); @@ -111,7 +106,7 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 { } function nonce() external view returns (uint256) { - return _nonce; + return _nonces[msg.sender]; } function gmpInfo(bytes32 id) external view returns (GmpInfo memory) { @@ -301,8 +296,8 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 { // We use 20 bytes for represent the address and 1 bit for the contract flag GmpSender source = msg.sender.toSender(false); - // Salt is equal to the previous message id (EIP-712 hash), this allows us to establish a sequence and eaily query the message history. - uint256 nextNonce = _nonce++; + // Nonce is per sender, it's incremented for every message sent. + uint256 nextNonce = _nonces[msg.sender]++; // Create GMP message and update nonce GmpMessage memory message = diff --git a/src/utils/GasUtils.sol b/src/utils/GasUtils.sol index 03bf887..da46e83 100644 --- a/src/utils/GasUtils.sol +++ b/src/utils/GasUtils.sol @@ -25,7 +25,12 @@ library GasUtils { /** * @dev Base cost of the `IGateway.submitMessage` method. */ - uint256 internal constant SUBMIT_BASE_COST = 23449 + 19; + uint256 internal constant SUBMIT_BASE_COST = 23525; + + /** + * @dev Extra gas cost of the first `IGateway.submitMessage` method. + */ + uint256 internal constant FIRST_MESSAGE_EXTRA_COST = 17100; /** * @dev Compute the gas cost of memory expansion. diff --git a/test/Example.t.sol b/test/Example.t.sol index 94c0376..a7de3db 100644 --- a/test/Example.t.sol +++ b/test/Example.t.sol @@ -109,7 +109,7 @@ contract ExampleTest is Test { dest: address(dstToken), destNetwork: DEST_NETWORK_ID, gasLimit: 100_000, - salt: 1, + salt: 0, data: abi.encode(MockERC20.CrossChainTransfer({from: ALICE, to: BOB, amount: 100})) }); diff --git a/test/Gateway.t.sol b/test/Gateway.t.sol index 135bbba..43d3146 100644 --- a/test/Gateway.t.sol +++ b/test/Gateway.t.sol @@ -403,7 +403,7 @@ contract GatewayBase is Test { assertEq( ctx.executionCost, - GasUtils.submitMessageGasCost(uint16(gmp.data.length)) - 4500, + GasUtils.submitMessageGasCost(uint16(gmp.data.length)) - 4500 + 17100, "unexpected submit message gas cost" ); } @@ -420,7 +420,7 @@ contract GatewayBase is Test { dest: address(bytes20(keccak256("dummy_address"))), destNetwork: DEST_NETWORK_ID, gasLimit: 0, - salt: 1, + salt: 0, data: new bytes(messageSize) }); @@ -458,12 +458,13 @@ contract GatewayBase is Test { emit IGateway.GmpCreated( id, GmpSender.unwrap(gmp.source), gmp.dest, gmp.destNetwork, gmp.gasLimit, gmp.salt, gmp.data ); + ctx.gasLimit += 17100; assertEq(ctx.submitMessage(gmp), id, "unexpected GMP id"); // Verify the execution cost assertEq( ctx.executionCost, - GasUtils.submitMessageGasCost(uint16(gmp.data.length)), + GasUtils.submitMessageGasCost(uint16(gmp.data.length)) + 17100, "unexpected submit message gas cost" ); @@ -622,7 +623,7 @@ contract GatewayBase is Test { ctx.execute(sig, gmp); } - function testSubmitGmpMessage() external { + function test_submitGmpMessage() external { vm.txGasPrice(1); GmpSender gmpSender = TestUtils.createTestAccount(1000 ether).toSender(false); GmpMessage memory gmp = GmpMessage({ @@ -631,13 +632,13 @@ contract GatewayBase is Test { dest: address(receiver), destNetwork: DEST_NETWORK_ID, gasLimit: 100_000, - salt: 1, + salt: 0, data: abi.encodePacked(uint256(100_000)) }); bytes32 id = gmp.eip712hash(); // Check the previous message hash - assertEq(gateway.nonce(), 1, "wrong previous message hash"); + assertEq(gateway.nonce(), 0, "wrong previous message hash"); CallOptions memory ctx = CallOptions({ from: gmpSender.toAddress(), @@ -671,8 +672,8 @@ contract GatewayBase is Test { assertEq(ctx.submitMessage(gmp), id, "unexpected GMP id"); // Verify the gas cost - uint256 expectedCost = GasUtils.submitMessageGasCost(uint16(gmp.data.length)) - 6500; - assertEq(ctx.executionCost, expectedCost, "unexpected execution gas cost in first call"); + uint256 expectedCost = GasUtils.submitMessageGasCost(uint16(gmp.data.length)) - 4500; + assertEq(ctx.executionCost, expectedCost + 17100, "unexpected execution gas cost in first call"); // Now the second GMP message should have the salt equals to previous gmp hash gmp.salt += 1; @@ -684,7 +685,7 @@ contract GatewayBase is Test { id, GmpSender.unwrap(gmp.source), gmp.dest, gmp.destNetwork, gmp.gasLimit, gmp.salt, gmp.data ); assertEq(ctx.submitMessage(gmp), id, "unexpected GMP id"); - assertEq(ctx.executionCost, expectedCost - 6800, "unexpected execution gas cost in second call"); + assertEq(ctx.executionCost, expectedCost - 8800, "unexpected execution gas cost in second call"); } }