From 65c0b76c65c8cb15432aa799ffc6fc8e4d77c934 Mon Sep 17 00:00:00 2001 From: Jack Lee <280147597@qq.com> Date: Thu, 27 Jun 2024 23:56:52 +0800 Subject: [PATCH] update demo code --- .../gas-optimization/multicall-in-router.md | 97 ++++++++++++------- 1 file changed, 62 insertions(+), 35 deletions(-) diff --git a/docs/general/build/smart-contracts/gas-optimization/multicall-in-router.md b/docs/general/build/smart-contracts/gas-optimization/multicall-in-router.md index a527d44223..3956999e90 100644 --- a/docs/general/build/smart-contracts/gas-optimization/multicall-in-router.md +++ b/docs/general/build/smart-contracts/gas-optimization/multicall-in-router.md @@ -1,20 +1,24 @@ + --- displayed_sidebar: generalSidebar --- # Implement multicall in router-like contracts -In Solidity, implementing multicall functionality in router-like contracts can significantly reduce gas costs by batching multiple calls into a single transaction. This is a common feature in contracts like the Uniswap Router and the Compound Bulker. Multicall allows users to execute a sequence of calls within a single transaction, thereby optimizing gas usage and improving efficiency. +In Solidity, implementing multicall functionality in router-like contracts can significantly reduce gas costs by batching multiple state-modifying calls into a single transaction. This technique is invaluable in contracts similar to those used by platforms like Uniswap and Compound. -**Demo Code** +**Demo code** -Below, we have a sample contract `MulticallRouter` that demonstrates how to implement multicall functionality. This contract includes a `multicall` function that accepts an array of encoded function calls and executes them in sequence. +Below, we have a sample contract `MulticallRouter` that demonstrates how to implement multicall functionality with state-modifying operations. This contract includes a `multicall` function that executes an array of encoded function calls, modifying contract state in an efficient manner. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract MulticallRouter { + uint256 public counter; // State variable to demonstrate state changes + mapping(uint256 => uint256) public data; // Mapping to store arbitrary data + function multicall(bytes[] calldata data) external returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; i++) { @@ -24,56 +28,79 @@ contract MulticallRouter { } } - // Example function to demonstrate usage - function exampleFunction1(uint256 x) external pure returns (uint256) { - return x * 2; + function incrementCounter(uint256 amount) external { + counter += amount; // Increment the counter by a specified amount } - function exampleFunction2(uint256 y) external pure returns (uint256) { - return y + 3; + function updateData(uint256 key, uint256 value) external { + data[key] = value; // Update the mapping with a key-value pair } } ``` -In this example, the `multicall` function uses `delegatecall` to execute each function call in the context of the current contract. This ensures that the state changes and storage modifications occur within the same contract. +To verify the functionality and efficiency of the `MulticallRouter`, here is the corresponding testing script: -### Usage Example +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; -To use this `MulticallRouter` contract, you can encode the function calls and pass them to the `multicall` function. Here's an example of how to encode and call the functions using Solidity: +import "forge-std/Test.sol"; +import "./MulticallRouter.sol"; -```solidity -contract MulticallExample { +contract MulticallTest is Test { MulticallRouter public router; - - constructor(address _router) { - router = MulticallRouter(_router); + bytes[] callData; + + function setUp() public { + router = new MulticallRouter(); + callData = new bytes[](4); + callData[0] = abi.encodeWithSelector( + router.incrementCounter.selector, + 1 + ); + callData[1] = abi.encodeWithSelector( + router.updateData.selector, + 0, + 100 + ); + callData[2] = abi.encodeWithSelector( + router.incrementCounter.selector, + 2 + ); + callData[3] = abi.encodeWithSelector( + router.updateData.selector, + 1, + 200 + ); } - function performMulticall() external { - bytes[] memory callData = new bytes[](2); - callData[0] = abi.encodeWithSelector(router.exampleFunction1.selector, 42); - callData[1] = abi.encodeWithSelector(router.exampleFunction2.selector, 21); - - bytes[] memory results = router.multicall(callData); - - // Handle the results - uint256 result1 = abi.decode(results[0], (uint256)); - uint256 result2 = abi.decode(results[1], (uint256)); + function testIndividualCalls() public { + uint256 gasStart = gasleft(); + router.incrementCounter(1); + router.updateData(0, 100); + router.incrementCounter(2); + router.updateData(1, 200); + uint256 gasEnd = gasleft(); + uint256 gasUsed = gasStart - gasEnd; + emit log_named_uint("Gas used for individual calls", gasUsed); + } - // Do something with the results + function testMulticall() public { + uint256 gasStart = gasleft(); + router.multicall(callData); + uint256 gasEnd = gasleft(); + uint256 gasUsed = gasStart - gasEnd; + emit log_named_uint("Gas used for multicall", gasUsed); } } ``` -**Gas Optimization Analysis** - -The primary advantage of using multicall is the reduction in gas costs by avoiding multiple transaction overheads. Here’s a comparison of gas usage between individual transactions and a single multicall: +By running the tests in the Foundry project, we found that `testIndividualCalls()` consumes `166259` gas, while `testMulticall()` consumes `139753` gas, indicating that using Multicall can save gas to some extent. -- **Individual Transactions**: Each call incurs base transaction costs plus the gas for executing the function. -- **Single Multicall**: Incurs only one base transaction cost plus the gas for executing each function within a loop. +**Gas Optimization Analysis** -By batching calls, multicall can significantly reduce the cumulative gas cost, especially when performing multiple operations. +The primary advantage of using multicall in this context is the reduction in gas costs by consolidating the execution of multiple state-modifying operations into a single transaction. This approach minimizes the overhead associated with transaction execution, making it highly efficient for scenarios where multiple operations need to be performed simultaneously. -**Recommendations for Gas Optimization** +**Conclusion** -Implementing multicall in router-like contracts can save gas by reducing the number of transactions and leveraging the lower cumulative gas cost of executing multiple operations in a single transaction. \ No newline at end of file +Implementing multicall functionality in contracts that manage multiple state changes is a robust strategy for optimizing gas usage and enhancing transaction efficiency on the Ethereum network.