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 EIP: Minimal Upgradable Proxy Contract #7229

Closed
wants to merge 14 commits into from
195 changes: 195 additions & 0 deletions EIPS/eip-7229.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
---
eip: 7229
title: Minimal Upgradable Proxy Contract
Copy link
Contributor

Choose a reason for hiding this comment

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

Your title doesn't differentiate itself enough form the other proxy contract standards. Maybe something like:

Suggested change
title: Minimal Upgradable Proxy Contract
title: Gas Optimized Proxy Contract

author: xiaobaiskill (@xiaobaiskill)
discussions-to: https://ethereum-magicians.org/t/eip-xxxx-minimal-upgradable-proxy/14754
status: Draft
type: Standards Track
category: ERC
created: 2023-06-24
---
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
---
requires: 3855
---


## Abstract

This proposal introduces the Minimal Upgradable Contract, a lightweight alternative to the existing upgradable contract implementation provided by OpenZeppelin. The Minimal Upgradable Contract aims to significantly reduce gas consumption during deployment while maintaining upgradability features.
Copy link
Contributor

Choose a reason for hiding this comment

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

The abstract should be a terse technical summary of the proposal. The reader should be able to get a high level understanding of how the proposal accomplishes its goal (in this case, handrolled assembly.)


## Motivation

Current upgradable contract solutions, such as OpenZeppelin's [EIP-1967](./eip-1967.md) implementation, often incur high gas costs during deployment. The goal of this proposal is to present an alternative approach that offers a significant reduction in gas consumption while maintaining the ability to upgrade contract logic.

Check failure on line 18 in EIPS/eip-7229.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

references to proposals with a `category` of `ERC` must use a prefix of `ERC`

error[markdown-refs]: references to proposals with a `category` of `ERC` must use a prefix of `ERC` --> EIPS/eip-7229.md | 18 | Current upgradable contract solutions, such as OpenZeppelin's [EIP-1967](./eip-1967.md) implementation, often incur high gas costs during deployment. The goal of this proposal is to present an alternative approach that offers a significant reduction in gas consumption while maintaining the ability to upgrade contract logic. | = help: see https://ethereum.github.io/eipw/markdown-refs/
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved

## Specification

The exact bytecode of the standard minimal upgradable contract is this:
`7fxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx73yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy81556009604c3d396009526010605560293960395ff3365f5f375f5f365f7f545af43d5f5f3e3d5f82603757fd5bf3`; In this bytecode, the 1st to 32nd byte (inclusive) needs to be replaced with a 32-byte slot, and the 34th to 53rd byte (inclusive) needs to be replaced with a 20-byte address.
Please note that the placeholders `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` represent the 32-byte slot and `yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy` represents the 20-byte address.

Copy link
Contributor

Choose a reason for hiding this comment

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

You should probably mention what the "slot" and "address" mean. To someone very familiar with solidity/proxies, it's obvious that the address is the implementation contract, but it's good to be explicit.


## Rationale

The Minimal Upgradeable Contract proposal introduces a novel approach to minimize gas consumption while preserving the upgradability feature of smart contracts. By predefining the slot that stores the logic contract during the contract creation phase, we can optimize gas efficiency during contract execution by directly accessing and performing delegate calls to the logic contract stored in the designated slot.

The rationale behind this approach is to eliminate the need for additional storage operations or costly lookups, which are commonly required in traditional upgradeable contract implementations. By predefining the slot and accessing the logic contract directly, we achieve significant gas savings and improve overall contract performance.

Key considerations for adopting this rationale include:

1、 Gas Optimization: By eliminating redundant storage operations and lookup procedures, we reduce gas consumption during contract execution. This optimization ensures that the contract remains cost-effective, especially when performing frequent contract calls or upgrades.

2、 Deterministic Contract Logic: By fixing the slot for storing the logic contract during contract creation, we establish a deterministic relationship between the contract and its logic. This deterministic mapping enables seamless contract upgrades without requiring additional storage or lookup mechanisms.

3、 Contract Compatibility: The rationale aligns with existing Ethereum Virtual Machine (EVM) environments and does not introduce any compatibility issues or modifications to the underlying infrastructure. It can be easily integrated into the current Ethereum ecosystem, ensuring seamless adoption by developers and users.

4、 Developer-Friendly Implementation: The rationale simplifies the implementation process for upgradeable contracts. By providing a straightforward mechanism to access and delegate calls to the logic contract, developers can focus on contract logic and functionality rather than complex storage management.
Comment on lines +36 to +42
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
1 Gas Optimization: By eliminating redundant storage operations and lookup procedures, we reduce gas consumption during contract execution. This optimization ensures that the contract remains cost-effective, especially when performing frequent contract calls or upgrades.
2 Deterministic Contract Logic: By fixing the slot for storing the logic contract during contract creation, we establish a deterministic relationship between the contract and its logic. This deterministic mapping enables seamless contract upgrades without requiring additional storage or lookup mechanisms.
3 Contract Compatibility: The rationale aligns with existing Ethereum Virtual Machine (EVM) environments and does not introduce any compatibility issues or modifications to the underlying infrastructure. It can be easily integrated into the current Ethereum ecosystem, ensuring seamless adoption by developers and users.
4 Developer-Friendly Implementation: The rationale simplifies the implementation process for upgradeable contracts. By providing a straightforward mechanism to access and delegate calls to the logic contract, developers can focus on contract logic and functionality rather than complex storage management.
1. Gas Optimization: By eliminating redundant storage operations and lookup procedures, we reduce gas consumption during contract execution. This optimization ensures that the contract remains cost-effective, especially when performing frequent contract calls or upgrades.
2. Deterministic Contract Logic: By fixing the slot for storing the logic contract during contract creation, we establish a deterministic relationship between the contract and its logic. This deterministic mapping enables seamless contract upgrades without requiring additional storage or lookup mechanisms.
3. Contract Compatibility: The rationale aligns with existing Ethereum Virtual Machine (EVM) environments and does not introduce any compatibility issues or modifications to the underlying infrastructure. It can be easily integrated into the current Ethereum ecosystem, ensuring seamless adoption by developers and users.
4. Developer-Friendly Implementation: The rationale simplifies the implementation process for upgradeable contracts. By providing a straightforward mechanism to access and delegate calls to the logic contract, developers can focus on contract logic and functionality rather than complex storage management.


By adopting this rationale, we aim to strike a balance between gas efficiency and contract upgradability. It allows developers to deploy upgradeable contracts with minimal gas consumption, making them more accessible and economically viable for a wide range of applications.


## Gas Efficiency

Check failure on line 46 in EIPS/eip-7229.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

body has extra section(s)

error[markdown-order-section]: body has extra section(s) --> EIPS/eip-7229.md | 46 | ## Gas Efficiency | ::: EIPS/eip-7229.md | 54 | ## Implementation | ::: EIPS/eip-7229.md | 187 | ## References | = help: see https://ethereum.github.io/eipw/markdown-order-section/
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved

Compared to existing upgradable contract solutions, the Minimal Upgradable Contract demonstrates a significant reduction in gas consumption during deployment. While OpenZeppelin's [EIP-1967](./eip-1967.md) implementation may consumes nearly several hundred thousand gas for deployment, the Minimal Upgradable Contract can be deployed with just a few tens of thousands of gas, resulting in substantial cost savings.

Check failure on line 48 in EIPS/eip-7229.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

references to proposals with a `category` of `ERC` must use a prefix of `ERC`

error[markdown-refs]: references to proposals with a `category` of `ERC` must use a prefix of `ERC` --> EIPS/eip-7229.md | 48 | Compared to existing upgradable contract solutions, the Minimal Upgradable Contract demonstrates a significant reduction in gas consumption during deployment. While OpenZeppelin's [EIP-1967](./eip-1967.md) implementation may consumes nearly several hundred thousand gas for deployment, the Minimal Upgradable Contract can be deployed with just a few tens of thousands of gas, resulting in substantial cost savings. |
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved

- [Transaction deploying the Minimal Upgradable Contract (32bytes slot)](https://goerli.etherscan.io/tx/0x19cf047783a159fd2f44a4dbee9fe0113c0899c29e54744c6f47d062381621cc)

Check failure on line 50 in EIPS/eip-7229.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> EIPS/eip-7229.md | 50 | - [Transaction deploying the Minimal Upgradable Contract (32bytes slot)](https://goerli.etherscan.io/tx/0x19cf047783a159fd2f44a4dbee9fe0113c0899c29e54744c6f47d062381621cc) | = help: see https://ethereum.github.io/eipw/markdown-rel-links/
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved
- [Transaction deploying the Minimal Upgradable Contract (1bytes slot)](https://goerli.etherscan.io/tx/0x96c86fd3a73c00f4a0a35c9837726c059212efb2c60099dc7697d92986c7cc80)

Check failure on line 51 in EIPS/eip-7229.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> EIPS/eip-7229.md | 51 | - [Transaction deploying the Minimal Upgradable Contract (1bytes slot)](https://goerli.etherscan.io/tx/0x96c86fd3a73c00f4a0a35c9837726c059212efb2c60099dc7697d92986c7cc80) |
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved
- [Transaction deploying using OpenZeppelin's EIP-1967](https://goerli.etherscan.io/tx/0xb4677404ac1d5b56a9ad1dd2a34c869738e6fcda07bcc78501354b309ce6e6b1)

Check failure on line 52 in EIPS/eip-7229.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

references to proposals with a `category` of `ERC` must use a prefix of `ERC`

error[markdown-refs]: references to proposals with a `category` of `ERC` must use a prefix of `ERC` --> EIPS/eip-7229.md | 52 | - [Transaction deploying using OpenZeppelin's EIP-1967](https://goerli.etherscan.io/tx/0xb4677404ac1d5b56a9ad1dd2a34c869738e6fcda07bcc78501354b309ce6e6b1) |

Check failure on line 52 in EIPS/eip-7229.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> EIPS/eip-7229.md | 52 | - [Transaction deploying using OpenZeppelin's EIP-1967](https://goerli.etherscan.io/tx/0xb4677404ac1d5b56a9ad1dd2a34c869738e6fcda07bcc78501354b309ce6e6b1) |
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved

## Implementation
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved

A reference implementation of the Minimal Upgradable Contract, including the proxy contract and an example implementation contract, will be provided as open-source code. This implementation will serve as a starting point for developers to adopt and customize the Minimal Upgradable Contract in their projects.

### Example implementations
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved

- [deploy proxy contract when deploying logic contract (32bytes slot)](https://github.com/xiaobaiskill/minimal-upgradable-proxy/blob/main/contracts/mock/mock32/Example.sol)
- [deploy proxy contract (32bytes slot)](https://github.com/xiaobaiskill/minimal-upgradable-proxy/blob/main/contracts/mock/mock32/DeployContract.sol)

- [deploy proxy contract when deploying logic contract (1bytes slot)](https://github.com/xiaobaiskill/minimal-upgradable-proxy/blob/main/contracts/mock/mock1/Example.sol)
- [deploy proxy contract (1bytes slot)](https://github.com/xiaobaiskill/minimal-upgradable-proxy/blob/main/contracts/mock/mock1/DeployContract.sol)

### Standard Proxy
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved

The disassembly of the standard deployed proxy contract code

```
# store logic address to slot of proxy contract
PUSH32 <slot> [slot]
PUSH20 <logicAddress> [logicAddress slot]
DUP2 [slot logicAddress slot]
SSTORE [slot] => storage(slot => logicAddress)

# return deployedCode
PUSH1 0x9 [0x9 slot]
PUSH1 0x4c [0x4c 0x9 slot]
PUSH0 [00 0x4c 0x9 slot]
CODECOPY [slot] ==> memory(0x00~0x8: 0x4c~0x54(deployedCode1stPart))
PUSH1 0x9 [0x9 slot]
MSTORE [] ==> memory(0x9~0x28: slot(deployedCode2ndPart))
PUSH1 0x10 [0x10]
PUSH1 0x55 [0x55 0x10]
PUSH1 0x29 [0x29 0x55 0x10]
CODECOPY [] ==> memory(0x29~0x38: 0x55~0x64(deployedCode3rdPart))
PUSH1 0x39 [0x39]
PUSH0 [00 0x39]
RETURN

# proxy contract (deployedcode)
CALLDATASIZE [calldatasize]
PUSH0 [00 calldatasize]
PUSH0 [00 00 calldatasize]
CALLDATACOPY [] ==> memory(00~(calldatasize-1) => codedata)
PUSH0 [00]
PUSH0 [00 00]
CALLDATASIZE [calldatasize 00 00]
PUSH0 [00 calldatasize 00 00]
PUSH32 [slot 00 calldatasize 00 00]
SLOAD [logicAddress 00 calldatasize 00 00]
GAS [gas logicAddress 00 calldatasize 00 00]
DELEGATECALL [result]
RETURNDATASIZE [returnDataSize result]
PUSH0 [00 returnDataSize result]
PUSH0 [00 00 returnDataSize result]
RETURNDATACOPY [result] => memory(00~(RETURNDATASIZE - 1) => RETURNDATA)
RETURNDATASIZE [returnDataSize result]
PUSH0 [00 returnDataSize result]
DUP3 [result 00 returnDataSize result]
PUSH1 0x37 [0x37 result 00 returnDataSize result]
JUMPI [00 returnDataSize result]
REVERT [result]
JUMPDEST [00 returnDataSize result]
RETURN [result]
```

NOTE: To push a zero value onto the stack without abusing the `RETURNDATASIZE` opcode, the above code utilizes [EIP-3855](./eip-3855.md). It achieves this by using the `PUSH0` instruction to push the zero value.

### Storage slot of logic address optimization
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved

To further optimize the minimal upgradeable proxy by controlling the slot value for the logic address within the range of 255(inclusive), you can use the following opcode to reduce gas consumption:

```
# store logic address to slot of proxy contract
PUSH1 <slot> [slot]
PUSH20 <logicAddress> [logicAddress slot]
DUP2 [slot logicAddress slot]
SSTORE [slot] => storage(slot => logicAddress)

# return deployedCode
PUSH1 0x9 [0x9 slot]
PUSH1 0x30 [0x30 0x9 slot]
PUSH0 [00 0x30 0x9 slot]
CODECOPY [slot] ==> memory(0x00~0x8: 0x30~0x54(deployedCode1stPart))
PUSH1 0xf8 [0xf8 slot]
SHL [slotAfterShl]
PUSH1 0x9 [0x9 slotAfterShl]
MSTORE [] ==> memory(0x9: slotAfterShl(deployedCode2ndPart))
PUSH1 0x10 [0x10]
PUSH1 0x39 [0x39 0x10]
PUSH1 0xa [0xa 0x39 0x10]
CODECOPY [] ==> memory(0xa~0x38: 0x39~0x64(deployedCode3rdPart))
PUSH1 0x1a [0x1a]
PUSH0 [00 0x1a]
RETURN

# proxy contract (deployedcode)
CALLDATASIZE [calldatasize]
PUSH0 [00 calldatasize]
PUSH0 [00 00 calldatasize]
CALLDATACOPY [] ==> memory(00~(calldatasize-1) => codedata)
PUSH0 [00]
PUSH0 [00 00]
CALLDATASIZE [calldatasize 00 00]
PUSH0 [00 calldatasize 00 00]
PUSH1 [slot 00 calldatasize 00 00]
SLOAD [logicAddress 00 calldatasize 00 00]
GAS [gas logicAddress 00 calldatasize 00 00]
DELEGATECALL [result]
RETURNDATASIZE [returnDataSize result]
PUSH0 [00 returnDataSize result]
PUSH0 [00 00 returnDataSize result]
RETURNDATACOPY [result] => memory(00~(RETURNDATASIZE - 1) => RETURNDATA)
RETURNDATASIZE [returnDataSize result]
PUSH0 [00 returnDataSize result]
DUP3 [result 00 returnDataSize result]
PUSH1 0x18 [0x18 result 00 returnDataSize result]
JUMPI [00 returnDataSize result]
REVERT [result]
JUMPDEST [00 returnDataSize result]
RETURN [result]
```

The bytecode generated by the above opcodes is as follows `60xx73yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy8155600960305f3960f81b60095260106039600a39601a5ff3365f5f375f5f365f60545af43d5f5f3e3d5f82601857fd5bf3`, replace `xx` to a slot of 1byte and replace `yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy` to a address of 20bytes before deploying contract

## Test Cases

Test cases are available at [https://github.com/xiaobaiskill/minimal-upgradable-proxy/tree/main/test](https://github.com/xiaobaiskill/minimal-upgradable-proxy/tree/main/test)


## Security Considerations

None.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm sure there are at least some security considerations 🙃

Suggested change
None.
<!-- needs discussion -->


## References
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved

- [OpenZeppelin EIP-1967: Standard Proxy Storage Slots](./eip-1967.md)

Check failure on line 189 in EIPS/eip-7229.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

references to proposals with a `category` of `ERC` must use a prefix of `ERC`

error[markdown-refs]: references to proposals with a `category` of `ERC` must use a prefix of `ERC` --> EIPS/eip-7229.md | 189 | - [OpenZeppelin EIP-1967: Standard Proxy Storage Slots](./eip-1967.md) |
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved
- [EIP-1822: Universal Upgradeable Proxy Standard (UUPS)](./eip-1822.md)

Check failure on line 190 in EIPS/eip-7229.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

references to proposals with a `category` of `ERC` must use a prefix of `ERC`

error[markdown-refs]: references to proposals with a `category` of `ERC` must use a prefix of `ERC` --> EIPS/eip-7229.md | 190 | - [EIP-1822: Universal Upgradeable Proxy Standard (UUPS)](./eip-1822.md) |
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved
- [EIP-1167: Minimal Proxy Contract](./eip-1167.md)

Check failure on line 191 in EIPS/eip-7229.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

references to proposals with a `category` of `ERC` must use a prefix of `ERC`

error[markdown-refs]: references to proposals with a `category` of `ERC` must use a prefix of `ERC` --> EIPS/eip-7229.md | 191 | - [EIP-1167: Minimal Proxy Contract](./eip-1167.md) |
xiaobaiskill marked this conversation as resolved.
Show resolved Hide resolved

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).