-
Notifications
You must be signed in to change notification settings - Fork 592
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 ERC-XXXX #523
base: master
Are you sure you want to change the base?
Add ERC-XXXX #523
Changes from all commits
667f304
2ab4c6d
686d7d8
ff3be40
154e08a
9c33a99
6226647
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,198 @@ | ||||||
--- | ||||||
eip: - | ||||||
title: Custom data access model | ||||||
description: Custom data access model is a design model that supports any form of access to the contract's storage to obtain the corresponding data. | ||||||
author: Elon Lee (@1999321) | ||||||
discussions-to: https://ethereum-magicians.org/t/custom-data-access-model/20337 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
status: Draft | ||||||
type: Standards Track | ||||||
category: ERC | ||||||
created: 2024-06 | ||||||
--- | ||||||
|
||||||
## Abstract | ||||||
|
||||||
The custom data access model uses solidity's delegate mode to obtain the contract's data read permissions. Corresponding reading logic can be developed through any third-party contract to obtain the desired data form. This model can save gas costs when it requires multiple accesses to the memory of a contract to obtain the final data form. It can even embed the required data processing logic directly into the agent contract, which is equivalent to native execution of data. Access and compute without making external calls. | ||||||
|
||||||
## Motivation | ||||||
|
||||||
When you need to access the data of other contracts, you are often limited by the data access logic customized by the contract. If you only need to access the data once, then this will not have a big impact, but when you need to access the data of other contracts in a function, When data is accessed multiple times, each access needs to consume the gas of an external call, which is a huge consumption compared to accessing data. In order to save the cost of external calls when frequently accessing the same contract data, and to have more freedom to perform customized data logic processing on native data, custom data access model was introduced. | ||||||
|
||||||
When developing a contract, there is no need to reserve a large amount of code specifically for data access (such as numerous `public` variables and `view` functions), which can also greatly expand the logic code that a contract can accommodate. | ||||||
|
||||||
## Specification | ||||||
|
||||||
Two contracts need to be implemented: | ||||||
|
||||||
1. `StaticCallTransfer`:This is a global contract, which is the entrance contract for obtaining data. All contracts that implement custom data access models can be accessed through this entrance. The functions forwarded by this contract are all static calls. | ||||||
|
||||||
|
||||||
|
||||||
```solidity | ||||||
// CC0 1.0 Universal (CC0 1.0) Public Domain Dedication | ||||||
pragma solidity ^0.8.24; | ||||||
|
||||||
interface IStaticCallTransfer { | ||||||
|
||||||
/** | ||||||
* @dev Retrieves custom data from a data contract using a static call to a logic contract. | ||||||
* @param dataContract The address of the data contract. | ||||||
* @param logicContract The address of the logic contract. | ||||||
* @param _data The data to be passed to the logic contract. | ||||||
* @return _re The result of the static call to the logic contract. | ||||||
*/ | ||||||
function getCustomData( | ||||||
address dataContract, | ||||||
address logicContract, | ||||||
bytes memory _data | ||||||
) external returns (bytes memory _re); | ||||||
} | ||||||
``` | ||||||
|
||||||
|
||||||
|
||||||
`getCustomData`:Data processing logic can be embedded through this interface to obtain the data form of any contract. | ||||||
|
||||||
2. `_Data`:The contract implements a custom data access model by inheriting `_Data`. | ||||||
|
||||||
|
||||||
|
||||||
```solidity | ||||||
// CC0 1.0 Universal (CC0 1.0) Public Domain Dedication | ||||||
pragma solidity ^0.8.24; | ||||||
|
||||||
interface I_Data { | ||||||
/** | ||||||
* @dev Executes a data function on a logic contract. | ||||||
* @param logicContract The address of the logic contract. | ||||||
* @param _data The data to be executed on the logic contract. | ||||||
* @return _re The result of executing the data function. | ||||||
*/ | ||||||
function _data_(address logicContract, bytes memory _data) external returns (bytes memory _re); | ||||||
} | ||||||
``` | ||||||
|
||||||
|
||||||
|
||||||
`_data_`:The function accessed by `StaticCallTransfer` uses this function to make proxy calls to achieve the purpose of embedding data processing logic. | ||||||
|
||||||
## Rationale | ||||||
|
||||||
The subsequent `_data_` function access controlled through `StaticCallTransfer` can only be a proxy with read permissions, but not write permissions. Embed data processing logic through proxy mode. | ||||||
|
||||||
## Backwards Compatibility | ||||||
|
||||||
Fully compatible. | ||||||
|
||||||
## Reference Implementation | ||||||
|
||||||
```solidity | ||||||
// CC0 1.0 Universal (CC0 1.0) Public Domain Dedication | ||||||
pragma solidity ^0.8.24; | ||||||
|
||||||
import "./interfaces/IStaticCallTransfer.sol"; | ||||||
import {I_Data} from "./interfaces/I_Data.sol"; | ||||||
|
||||||
contract StaticCallTransfer is IStaticCallTransfer { | ||||||
function transferStaticCall(address to, bytes memory data) public view returns (bytes memory _re) { | ||||||
|
||||||
assembly { | ||||||
let size := mload(data) | ||||||
let ptr := add(data, 0x20) | ||||||
let result := staticcall(gas(), to, ptr, size, 0, 0) | ||||||
let size2 := returndatasize() | ||||||
_re := mload(0x40) | ||||||
mstore(0x40, add(_re, add(size2, 0x20))) | ||||||
mstore(_re, size2) | ||||||
returndatacopy(add(_re, 0x20), 0, size2) | ||||||
switch result | ||||||
case 0 { | ||||||
revert(0, 0) | ||||||
} | ||||||
} | ||||||
_re = abi.decode(_re, (bytes)); | ||||||
} | ||||||
|
||||||
function getCustomData(address dataContract, address logicContract, bytes memory _data) external view returns (bytes memory _re) { | ||||||
bytes memory data = abi.encodeWithSelector(I_Data._data_.selector, logicContract, _data); | ||||||
return transferStaticCall(dataContract, data); | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
|
||||||
|
||||||
```solidity | ||||||
// CC0 1.0 Universal (CC0 1.0) Public Domain Dedication | ||||||
pragma solidity ^0.8.24; | ||||||
|
||||||
import {I_Data} from "./interfaces/I_Data.sol"; | ||||||
|
||||||
contract _Data { | ||||||
address immutable staticCallTransfer; | ||||||
|
||||||
constructor(address _staticCallTransfer) { | ||||||
staticCallTransfer = _staticCallTransfer; | ||||||
} | ||||||
|
||||||
function _data_(address logicContract, bytes memory _data) external returns (bytes memory _re) { | ||||||
require(msg.sender == staticCallTransfer,"_Data: only Static Call"); | ||||||
(bool success, bytes memory result) = logicContract.delegatecall( | ||||||
abi.encodeWithSelector( | ||||||
I_Data._data_.selector, _data) | ||||||
); | ||||||
require(success, "_Data: _data_ failed"); | ||||||
return result; | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
|
||||||
|
||||||
The contract `Dapp` inherits `_Data` to implement a custom data access model: | ||||||
|
||||||
|
||||||
|
||||||
```solidity | ||||||
// CC0 1.0 Universal (CC0 1.0) Public Domain Dedication | ||||||
pragma solidity ^0.8.24; | ||||||
|
||||||
import {_Data} from "./_Data.sol"; | ||||||
|
||||||
contract Dapp is _Data { | ||||||
|
||||||
address private private_data; | ||||||
|
||||||
constructor(address _staticCallTransfer) _Data(_staticCallTransfer) {} | ||||||
|
||||||
|
||||||
} | ||||||
``` | ||||||
|
||||||
|
||||||
|
||||||
Although `private_data` is declared `private`, it can also be accessed through the following `AccessDapp` contract: | ||||||
|
||||||
|
||||||
|
||||||
```solidity | ||||||
// CC0 1.0 Universal (CC0 1.0) Public Domain Dedication | ||||||
pragma solidity ^0.8.24; | ||||||
|
||||||
contract AccessDapp { | ||||||
|
||||||
address public private_data; | ||||||
|
||||||
} | ||||||
``` | ||||||
|
||||||
|
||||||
|
||||||
## Security Considerations | ||||||
|
||||||
Data security is ensured through `staticcall`. It only has read permission and cannot pose any threat to the contract. | ||||||
|
||||||
## Copyright | ||||||
|
||||||
Copyright and related rights waived via [CC0](../LICENSE.md). | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Compiler files | ||
cache/ | ||
out/ | ||
|
||
# Ignores development broadcast logs | ||
!/broadcast | ||
/broadcast/*/31337/ | ||
/broadcast/**/dry-run/ | ||
|
||
# Docs | ||
docs/ | ||
|
||
# Dotenv file | ||
.env |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't add submodules. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "lib/forge-std"] | ||
path = lib/forge-std | ||
url = https://github.com/foundry-rs/forge-std |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not related to your EIP |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
## Foundry | ||
|
||
**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** | ||
|
||
Foundry consists of: | ||
|
||
- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). | ||
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. | ||
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. | ||
- **Chisel**: Fast, utilitarian, and verbose solidity REPL. | ||
|
||
## Documentation | ||
|
||
https://book.getfoundry.sh/ | ||
|
||
## Usage | ||
|
||
### Build | ||
|
||
```shell | ||
$ forge build | ||
``` | ||
|
||
### Test | ||
|
||
```shell | ||
$ forge test | ||
``` | ||
|
||
### Format | ||
|
||
```shell | ||
$ forge fmt | ||
``` | ||
|
||
### Gas Snapshots | ||
|
||
```shell | ||
$ forge snapshot | ||
``` | ||
|
||
### Anvil | ||
|
||
```shell | ||
$ anvil | ||
``` | ||
|
||
### Deploy | ||
|
||
```shell | ||
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key> | ||
``` | ||
|
||
### Cast | ||
|
||
```shell | ||
$ cast <subcommand> | ||
``` | ||
|
||
### Help | ||
|
||
```shell | ||
$ forge --help | ||
$ anvil --help | ||
$ cast --help | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[profile.default] | ||
src = "src" | ||
out = "out" | ||
libs = ["lib"] | ||
|
||
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
src/Vm.sol linguist-generated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assigning next sequential EIP/ERC/RIP number.
Please also update the filename.