generated from gnkz/forge-nix-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
47 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Copyright 2024 Gonzalo Sánchez | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,16 @@ | ||
## Foundry | ||
# Read-Only Solidity Proxy | ||
|
||
**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** | ||
> [!CAUTION] | ||
> This is a just-for-fun idea I had and the contract is not audited so use with caution. | ||
Foundry consists of: | ||
`ReadOnlyProxy` is a smart contract that enables call delegation while preserving the state context, ensuring that any calls made through the proxy are restricted to read-only access to the storage. | ||
|
||
- **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. | ||
This read-only access is enforced by using `staticcall` to call a function that performs a `delegatecall` to the target contract. | ||
|
||
## Documentation | ||
## Build | ||
|
||
https://book.getfoundry.sh/ | ||
`forge build` | ||
|
||
## Usage | ||
## Tests | ||
|
||
### 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 | ||
``` | ||
`forge test` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,57 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.4; | ||
|
||
/// @title A read-only proxy contract | ||
/// @author Gonzalo Sánchez <[email protected]> | ||
/// @notice The purpose of this contract is to have a way to proxy calls to another contract but | ||
/// making sure those calls are not able to modify the state. This could be useful in scenarios | ||
/// where there are needs to decrease smart contract sizes by removing public read only functions | ||
/// @dev To make sure the call is read only there is a `delegatecall` that's invoked inside a | ||
/// `staticcall`, this way the delegated contract has access to the storage but is limited by the | ||
/// `staticcall` so if there is any modification to the state the call will revert | ||
abstract contract ReadOnlyProxy { | ||
error DelegateCallNotAllowed(); | ||
|
||
function _delegate(bytes memory _data) public { | ||
receive() external payable { | ||
_beforeFallback(); | ||
_fallback(); | ||
} | ||
|
||
fallback() external payable { | ||
_beforeFallback(); | ||
_fallback(); | ||
} | ||
|
||
/// @notice function in charge of doing a `delegatecall` to the `reader` contract | ||
/// @param _data the abi encoded method to call on `reader` | ||
function _delegate(bytes memory _data) external { | ||
// can only be called from self | ||
if (msg.sender != address(this)) { | ||
revert DelegateCallNotAllowed(); | ||
} | ||
|
||
address reader = _reader(); | ||
|
||
assembly { | ||
// Call the implementation. | ||
// out and outsize are 0 because we don't know the size yet. | ||
// delegate the call to the `reader` | ||
let result := delegatecall(gas(), reader, add(_data, 0x20), mload(_data), 0, 0) | ||
|
||
// Copy the returned data. | ||
returndatacopy(0, 0, returndatasize()) | ||
|
||
switch result | ||
// delegatecall returns 0 on error. | ||
case 0 { revert(0, returndatasize()) } | ||
default { return(0, returndatasize()) } | ||
} | ||
} | ||
|
||
/// @notice called inside the `fallback` or `receive` functions. It does an `staticcall` on | ||
/// `_delegate` proxying `msg.data` | ||
function _fallback() internal view { | ||
address self = address(this); | ||
bytes memory data = abi.encodeWithSignature("_delegate(bytes)", msg.data); | ||
bytes memory data = abi.encodeWithSelector(ReadOnlyProxy._delegate.selector, msg.data); | ||
|
||
assembly { | ||
// do an static call to `self._delegate` using `msg.data` | ||
let result := staticcall(gas(), self, add(data, 0x20), mload(data), 0, 0) | ||
|
||
returndatacopy(0, 0, returndatasize()) | ||
|
@@ -41,13 +62,10 @@ abstract contract ReadOnlyProxy { | |
} | ||
} | ||
|
||
/// @notice function used to get the `reader` contract address | ||
function _reader() internal view virtual returns (address); | ||
|
||
fallback() external payable { | ||
_fallback(); | ||
} | ||
|
||
receive() external payable { | ||
_fallback(); | ||
} | ||
/// @notice called before the `_fallback` function | ||
/// @dev just for flexibility | ||
function _beforeFallback() internal virtual {} | ||
} |