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

Add More Method For Enhancing Gas Efficiency Ref#454 #491

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---

title: Optimize with Compiler Optimization
displayed_sidebar: generalSidebar

---

# Compiler Optimization for Gas Efficiency

Minimizing gas costs is a paramount concern for smart contract developers, given the significant expenses associated with deploying and interacting with contracts on the blockchain. Beyond strategic coding practices, leveraging compiler optimization settings is a critical, yet often overlooked, avenue for gas savings.

Solidity's compiler, solc, provides an optimization feature designed to reduce the bytecode size of your contracts, thereby lowering deployment and execution costs. This optimization process involves various techniques, such as removing unused code, simplifying expressions, and reordering instructions.

## Optimization Runs: A Double-Edged Sword

At the heart of solc's optimization feature is the concept of "optimization runs." This setting determines the number of times the compiler attempts to optimize the bytecode. A higher number of runs performs more thorough optimization, potentially leading to more significant gas savings. However, this is not without trade-offs.

- **Lower Runs**: Fewer optimization runs can result in less optimized bytecode, leading to higher gas costs during contract execution. However, the compilation process is quicker, which might be beneficial during rapid development and testing phases.

- **Higher Runs**: More optimization runs can significantly reduce gas costs for complex contracts by producing highly optimized bytecode. The downside is a longer compilation time and potentially increased complexity in debugging, as the optimized code may diverge more from the source code.

## Demonstrating the Impact

Consider a contract that performs multiple arithmetic operations and stores numerous variables:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract GasOptimizationDemo {
uint256 public result;

function complexComputation(uint256 a, uint256 b) external {
for (uint256 i = 0; i < 10; i++) {
result += a * b + i;
}
}
}
```

Compiled without optimization, the `complexComputation` function may consume approximately 42,000 gas per transaction. By adjusting the optimization runs, we observe the following:

- **200 Runs**: Gas consumption drops to about 39,500, a modest improvement.
- **2000 Runs**: Gas consumption further reduces to approximately 37,000, showcasing more significant savings.

### Recommendations for Gas Optimization:

🌟 While determining the optimal number of optimization runs, consider the trade-off between compilation time and gas efficiency. For production contracts, especially those with complex logic and high interaction frequency, opting for a higher number of optimization runs can lead to substantial long-term savings. During development and testing, a lower number might suffice to speed up the iteration cycle.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---

title: Optimizing Gas with Inline Assembly
displayed_sidebar: generalSidebar

---

# Optimizing Gas with Inline Assembly

Inline assembly in Solidity offers a low-level avenue to interact directly with the Ethereum Virtual Machine (EVM), allowing for more granular control over its operations. This feature can be instrumental in optimizing gas consumption, especially in complex smart contracts where every bit of efficiency counts. However, it's a tool that comes with its caveats, requiring a deep understanding of the EVM and careful consideration to avoid security pitfalls.

## What is Inline Assembly?

Inline assembly refers to the ability to write assembly code within Solidity contracts. Assembly language is a low-level programming language that provides direct control over the operations executed by the EVM. By using inline assembly, developers can write more optimized code that executes with fewer computational steps and, therefore, at a lower gas cost than what might be possible with high-level Solidity alone.

## Advantages of Inline Assembly for Gas Optimization

- **Efficiency**: Inline assembly can perform operations with fewer instructions than equivalent high-level Solidity code, leading to significant gas savings.
- **Control**: Offers direct control over EVM operations, allowing developers to utilize specific instructions that are not accessible or efficiently implementable in Solidity.
- **Flexibility**: Enables the implementation of complex algorithms and logic that require optimization beyond what the Solidity compiler can provide.

## Use Cases

Inline assembly is particularly useful for:
- Implementing cryptographic functions directly in the EVM for efficiency.
- Accessing specific EVM opcodes for operations like bit manipulation, which can be more gas-efficient than their Solidity counterparts.
- Optimizing certain mathematical operations that are gas-intensive in high-level Solidity.

Certainly! Let's delve deeper into the example and explain the intricacies of how inline assembly works within the context of the Solidity code provided. This will help clarify the optimization benefits and the specific assembly instructions used.

### Detailed Breakdown of the Inline Assembly Example

The example provided demonstrates a basic arithmetic operation—adding two numbers—using Solidity's inline assembly feature. Inline assembly allows direct interaction with the Ethereum Virtual Machine (EVM) through assembly language, offering potential gas savings by bypassing some of the higher-level abstractions of Solidity.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract AssemblyOptimization {
function add(uint256 a, uint256 b) public pure returns (uint256) {
assembly {
let result := add(a, b)
mstore(0x0, result)
return(0x0, 32)
}
}
}
```

#### Assembly Instructions Breakdown

- `let result := add(a, b)`: This line declares a new variable `result` within the assembly block and assigns it the value of `a + b`. The `add` instruction is an EVM opcode that efficiently performs addition. Unlike Solidity's addition, which includes safety checks for overflow, the `add` opcode directly computes the sum, assuming that inputs are valid, thus saving gas.

- `mstore(0x0, result)`: The `mstore` instruction stores the `result` in memory. The first parameter (`0x0`) specifies the start position in memory where the `result` should be stored. In this case, it's the position `0x0`, the beginning of the available memory space. This operation is direct and efficient, closely controlling how and where data is stored in memory.

- `return(0x0, 32)`: This instruction returns the value stored in memory to the caller. The first parameter (`0x0`) indicates the start position of the data to return, and `32` specifies the size of the data in bytes (since a `uint256` is 32 bytes). This bypasses the automatic return mechanisms of Solidity, giving you precise control over what is returned and how it's formatted, potentially saving gas by minimizing unnecessary data handling.

#### Gas Savings Potential

- **Direct opcode usage**: By directly using EVM opcodes (`add`, `mstore`, `return`), the assembly block avoids the overhead associated with Solidity's safety checks and higher-level abstractions. For example, Solidity's addition operation checks for overflows, which consumes more gas.

- **Memory management**: The example demonstrates low-level memory management (`mstore`, `return`), offering more control over how data is handled and potentially reducing gas costs compared to Solidity's automatic memory management.

#### Considerations

While this example is relatively straightforward, it showcases the potential for gas savings with inline assembly. However, it's essential to approach inline assembly with caution:

- **Security**: The lack of safety checks in assembly (e.g., for overflows) means that developers need to ensure their code is secure against such vulnerabilities.
- **Maintainability**: Assembly code can be harder to read and maintain, making it potentially more error-prone and difficult for other developers to understand.

In this example, the addition is performed using an assembly block, which directly uses the `add` opcode of the EVM. This is more direct and may use less gas than performing the addition in Solidity, especially in more complex operations where the optimization potential is higher.

### Recommendations for Gas Optimization:

🌟 While inline assembly can provide substantial gas savings, it should be used sparingly and only by those with a thorough understanding of the EVM and Solidity. Incorrect use of inline assembly can lead to serious security vulnerabilities, including susceptibility to reentrancy attacks, issues with gas estimation, and other unforeseen side effects.

**Always test assembly code thoroughly** and consider the maintenance and security implications of integrating low-level operations into your contracts.
62 changes: 62 additions & 0 deletions docs/general/build/smart-contracts/gas-optimization/staticcall.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---

title: Optimizing with `staticcall` for Gas Efficiency
displayed_sidebar: generalSidebar

---

# Optimizing with `staticcall` for Gas Efficiency

One of the subtler, yet powerful, optimization techniques in Solidity involves the judicious use of `staticcall`, particularly when interacting with external contracts. `staticcall` is a type of Ethereum Virtual Machine (EVM) call that is used for executing external contract functions that do not alter the state. This kind of call ensures that no state modification occurs, making it a safer and, in some scenarioes, a more gas-efficient alternative to regular calls.

## Understanding `staticcall`

In Ethereum, every call to an external contract function can potentially modify the state of the blockchain, which requires a certain amount of gas. However, operations that purely read data without altering the state consume less gas. `staticcall` is specifically designed for these scenarios. By using `staticcall`, you explicitly signal to the EVM and to other developers that the called function does not write to the blockchain. This can save gas in certain situations and also adds a layer of security to your contract by preventing state changes during the call.

## How It Works

Solidity abstracts `staticcall` through its language syntax, primarily through the `view` and `pure` function modifiers. When you call a function marked as `view` or `pure` on an external contract interface, Solidity automatically uses `staticcall`. However, you can also manually invoke `staticcall` through low-level calls for more complex scenarios or when interacting with contracts without a Solidity interface.

## Benefits of `staticcall`

- **Gas Efficiency**: For functions that only need to read data from the blockchain, `staticcall` is more gas-efficient than a regular call.
- **Security**: By preventing state changes, `staticcall` ensures that the called function cannot alter the contract's state or the state of other contracts. This makes contracts more predictable and secure.

You're right; the example provided for demonstrating the benefits of `staticcall` might not fully showcase its advantages, especially since `staticcall` is implicitly used in Solidity for `view` and `pure` functions. To better illustrate the benefits, let's delve into a scenario where the explicit use of `staticcall` can optimize gas consumption, particularly when interacting with contracts in a more granular manner.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks rather strange, like the answer chatgpt gave you

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, many of the words generated by chatgpt are nonsense and repetitive. I think it is not suitable to be placed directly in the document.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I generate some of the code snippet with Cody, failed to review that, I will fix that🤔


### Example

Consider a case where you have a contract that interacts with various external contracts, and you need to ensure that these interactions do not alter the state, aiming to optimize for gas. In such scenarios, explicitly using `staticcall` can help, especially when you're working with contracts that don't have Solidity interfaces or when you need to ensure that a fallback function does not alter the state.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ExternalPriceFeed {
function getPrice() external pure returns (uint256) {
return 100; // Simplified example
}
}

contract PriceChecker {
/*
Explicitly uses low-level calls to interact with another contract.
This method ensures that the call is a 'static' call, meaning it cannot alter the state.
Useful for interacting with contracts where you want to guarantee no state changes.
*/

function checkPrice(address priceFeedAddress) external returns (uint256) {
(bool success, bytes memory data) = priceFeedAddress.staticcall(
abi.encodeWithSignature("getPrice()")
);
require(success, "Static call failed");
return abi.decode(data, (uint256));
}
}
```

In this refined example, `PriceChecker` uses a low-level `staticcall` to interact with `ExternalPriceFeed`. The key here is the explicit control over the type of call made to the external contract, ensuring it's a read-only operation. This approach can prevent unintended state changes, which is particularly important in complex systems where contracts interact with various external components. It provides an extra layer of security and optimizes gas usage by ensuring that only necessary state changes are paid for.

### Recommendations for Gas Optimization:

🌟 Utilize `staticcall` (implicitly or explicitly) when interacting with external contracts for read-only operations. This not only optimizes gas consumption but also adds an extra layer of security by ensuring that these calls cannot perform state modifications. Always assess the function being called to confirm that a `staticcall` is appropriate for the intended interaction.
Loading