Skip to content

Commit

Permalink
add foundry
Browse files Browse the repository at this point in the history
  • Loading branch information
youssefea committed Sep 1, 2024
1 parent a3b46b5 commit b6f1c3a
Show file tree
Hide file tree
Showing 14 changed files with 23,399 additions and 1,760 deletions.
34 changes: 34 additions & 0 deletions foundry/.github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: test

on: workflow_dispatch

env:
FOUNDRY_PROFILE: ci

jobs:
check:
strategy:
fail-fast: true

name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Run Forge build
run: |
forge --version
forge build --sizes
id: build

- name: Run Forge tests
run: |
forge test -vvv
id: test
14 changes: 14 additions & 0 deletions foundry/.gitignore
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
66 changes: 66 additions & 0 deletions foundry/README.md
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
```
11 changes: 11 additions & 0 deletions foundry/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[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

remappings = [
"@openzeppelin/=lib/openzeppelin-contracts/",
"@superfluid-finance/=lib/superfluid-protocol-monorepo/packages"
]
2 changes: 1 addition & 1 deletion foundry/lib/forge-std
Submodule forge-std updated 1 files
+2 −2 src/StdCheats.sol
2 changes: 1 addition & 1 deletion foundry/lib/openzeppelin-contracts
12 changes: 12 additions & 0 deletions foundry/script/Counter.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script, console2} from "forge-std/Script.sol";

contract CounterScript is Script {
function setUp() public {}

function run() public {
vm.broadcast();
}
}
41 changes: 41 additions & 0 deletions foundry/src/CustomERC20Wrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.19;

// This file contains show how to create a custom ERC20 Wrapper

// This abstract contract provides storage padding for the proxy
import {CustomSuperTokenBase} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/CustomSuperTokenBase.sol";
// Implementation of UUPSProxy (see https://eips.ethereum.org/EIPS/eip-1822)
import {UUPSProxy} from "./base/UUPSProxy.sol";
// Superfluid framework interfaces we need
import {ISuperToken, ISuperTokenFactory, IERC20} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
// for the underlying token
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/// @title The Proxy contract for a Custom ERC20 Wrapper
contract CustomERC20WrapperProxy is CustomSuperTokenBase, UUPSProxy {
// This shall be invoked exactly once after deployment, needed for the token contract to become operational.
function initialize(
IERC20Metadata underlyingToken,
ISuperTokenFactory factory,
string memory name,
string memory symbol
) external {
// This call to the factory invokes `UUPSProxy.initialize`, which connects the proxy to the canonical SuperToken implementation.
// It also emits an event which facilitates discovery of this token.
ISuperTokenFactory(factory).initializeCustomSuperToken(address(this));

// This initializes the token storage and sets the `initialized` flag of OpenZeppelin Initializable.
// This makes sure that it will revert if invoked more than once.
ISuperToken(address(this)).initialize(
underlyingToken,
underlyingToken.decimals(),
name,
symbol
);
}

// add custom functionality here...
}

interface ICustomERC20Wrapper is ISuperToken {}
43 changes: 43 additions & 0 deletions foundry/src/PureSuperToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.19;

// This file contains everything we need for a minimal Pure SuperToken.

// This abstract contract provides storage padding for the proxy
import {CustomSuperTokenBase} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/CustomSuperTokenBase.sol";
// Implementation of UUPSProxy (see https://eips.ethereum.org/EIPS/eip-1822)
import {UUPSProxy} from "./base/UUPSProxy.sol";
// Superfluid framework interfaces we need
import {ISuperToken, ISuperTokenFactory, IERC20} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";

/// @title The Proxy contract for a Pure SuperToken with preminted initial supply.
contract PureSuperTokenProxy is CustomSuperTokenBase, UUPSProxy {
// This shall be invoked exactly once after deployment, needed for the token contract to become operational.
function initialize(
ISuperTokenFactory factory,
string memory name,
string memory symbol,
address receiver,
uint256 initialSupply
) external {
// This call to the factory invokes `UUPSProxy.initialize`, which connects the proxy to the canonical SuperToken implementation.
// It also emits an event which facilitates discovery of this token.
ISuperTokenFactory(factory).initializeCustomSuperToken(address(this));

// This initializes the token storage and sets the `initialized` flag of OpenZeppelin Initializable.
// This makes sure that it will revert if invoked more than once.
ISuperToken(address(this)).initialize(
IERC20(address(0)),
18,
name,
symbol
);

// This mints the specified initial supply to the specified receiver.
ISuperToken(address(this)).selfMint(receiver, initialSupply, "");
}
}

// The token interface is just an alias of ISuperToken
// since we need no custom logic (other than for initialization) in the proxy.
interface IPureSuperToken is ISuperToken {}
48 changes: 48 additions & 0 deletions foundry/src/base/UUPSProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.0;

import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol";

/// @title UUPS Proxy implementation contract
/// @author jtriley.eth
/// @notice Stores the logic contract's address at the _IMPLEMENTATION_SLOT
/// @dev `initializeProxy(address)` is called by the Super Token Factory
/// The call to the factory should be in the same transaction to avoid being
/// front run
contract UUPSProxy is Proxy {
/// @notice Thrown when the logic contract address is zero
error ZeroAddress();

/// @notice Thrown when the logic contract has been set
error Initialized();

/// @notice Precomputed from the following for gas savings
/// bytes32(uint256(keccak256("eip1967.proxy.implementation") - 1));
bytes32 internal constant _IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

/// @notice Stores the logic contract address only once.
/// @dev Called by the SuperTokenFactory contract on upgrade
/// @param initialAddress logic contract address
function initializeProxy(address initialAddress) external {
if (initialAddress == address(0)) revert ZeroAddress();
if (_implementation() != address(0)) revert Initialized();
assembly {
sstore(_IMPLEMENTATION_SLOT, initialAddress)
}
}

/// @notice Reads logic contract from precomputed slot
/// @return impl Logic contract address
function _implementation()
internal
view
virtual
override
returns (address impl)
{
assembly {
impl := sload(_IMPLEMENTATION_SLOT)
}
}
}
41 changes: 41 additions & 0 deletions foundry/test/PureSuperToken.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console2} from "forge-std/Test.sol";
import {PureSuperTokenProxy} from "../src/PureSuperToken.sol";
import {ERC1820RegistryCompiled} from "@superfluid-finance/ethereum-contracts/contracts/libs/ERC1820RegistryCompiled.sol";
import "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
import "@superfluid-finance/ethereum-contracts/contracts/utils/SuperfluidFrameworkDeployer.sol";
import "@superfluid-finance/ethereum-contracts/contracts/utils/SuperfluidFrameworkDeploymentSteps.sol";

contract PureSuperTokenProxyTest is Test {
PureSuperTokenProxy public pureSuperToken;
SuperfluidFrameworkDeployer.Framework public sf;
address public owner;

function setUp() public {
vm.etch(ERC1820RegistryCompiled.at, ERC1820RegistryCompiled.bin);
SuperfluidFrameworkDeployer sfDeployer = new SuperfluidFrameworkDeployer();
sfDeployer.deployTestFramework();
sf = sfDeployer.getFramework();
owner = address(this);
}

function testDeploy() public {
pureSuperToken = new PureSuperTokenProxy();
assertTrue(address(deployedContract) != address(0));
}

function testSuperTokenBalance(uint256 x) public {
pureSuperToken = new PureSuperTokenProxy();
pureSuperToken.initialize(
sf.superTokenFactory,
"TestToken",
"TST",
owner,
1000
);
uint balance = pureSuperToken.balanceOf(owner);
assertTrue(balance == 1000);
}
}
Loading

0 comments on commit b6f1c3a

Please sign in to comment.