Skip to content

Commit

Permalink
feat: format for bread-chain use
Browse files Browse the repository at this point in the history
  • Loading branch information
daopunk committed Nov 21, 2024
1 parent 20be385 commit 949c774
Show file tree
Hide file tree
Showing 15 changed files with 180 additions and 59 deletions.
9 changes: 6 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
MAINNET_RPC=
MAINNET_DEPLOYER_NAME=
GNOSIS_RPC='https://rpc.gnosis.gateway.fm'
GNOSIS_DEPLOYER_PK=

OPTIMISM_RPC=
OPTIMISM_DEPLOYER_PK=

SEPOLIA_RPC=
SEPOLIA_DEPLOYER_NAME=
SEPOLIA_DEPLOYER_PK=

ETHERSCAN_API_KEY=
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ concurrency:
cancel-in-progress: true

env:
MAINNET_RPC: ${{ secrets.MAINNET_RPC }}
OPTIMISM_RPC: ${{ secrets.OPTIMISM_RPC }}
SEPOLIA_RPC: ${{ secrets.SEPOLIA_RPC }}

jobs:
Expand Down
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,48 @@

<br />

<div align="center">Forked by Breadchain</div>

## Breadchain Developers

<dl>
<dt>Basic Setup</dt>
<dd>Use `yarn add` / `yarn build`, not `forge install` / `forge build` etc.</dd>
<dd>Use yarn scripts, not forge scripts (Look at the `package.json`!).</dd>
<dd>Add scripts to the `package.json` as needed.</dd>

<dt>Required Contracts</dt>
<dd>Every contract is required to have a full interface.</dd>
<dd>The contract should inherit it's own interface.</dd>
<dd>The contract should reference it's interface with `@inheritdoc`</dd>
<dd>The interface should contain other tags, including `@notice, @param, @return, @dev`</dd>
<dd>The contract should be free of clutter, whereas the interface should act as a guide to the contract.</dd>
<dd>Errors, Events, and Structs should be located in interfaces, not in contracts.</dd>

<dt>Required Testing</dt>
<dd><b>Integration/E2E</b> - should fork chain intended for deployment (likely gnosis or optimism) and should use the deploy script found in `Script/Common.sol` in the `test/integration/IntegrationBase.sol`.</dd>
<dd><b>Unit</b> - should test and branch all contract functionality, with mock contracts or mock function calls added as needed to mock inter-contract calls. `.tree` files are there for example, but not necessary.</dd>

<dt>Test Coverage</dt>
<dd>Run `yarn coverage` to generate a coverage report for tests</dd>
<dd>Unit and Integration should be 100%, with branch testing reasonably high</dd>

<dt>Advice for Writing Tests</dt>
<dd>Make use of `setUp` overrides and inheritance to cut down on redundant setups.</dd>
<dd>Make use of helper functions with obvious names (e.g. `_fundUsersWithTokens()`) to reduce complexity for other developers that will review the code.</dd>
<dd>Make use of Modifiers within test contracts to constrain fuzzing variables or any other use case</dd>
<dd>Make multiple testing contracts in one file that all test the same contract (e.g. E2EGreeterTestSetup, E2EGreeterTestAccessControl, E2EGreeterTestCore, etc.) where contracts inherit the same base setup.</dd>
<dd>Make use of constant variables placed in base test setup, so that updating test variables is simple and easy.</dd>
<dd>Keep tests organized! Break complexity down and make it readable!</dd>

<dt>Required Formatting</dt>
<dd>Commits cannot be made without passing the linter!</dd>
<dd>Run `yarn lint:check`</dd>
<dd>Check `package.json` for more linter commands</dd>
<dd>Internal variables start with an underscore ('_exampleOfInternalVar').</dd>
<dd>Run `lint:natspec` to check contracts and interfaces for correct natspec.</dd>
</dl>

## Features

<dl>
Expand Down Expand Up @@ -113,7 +155,7 @@ source .env
Import your private keys into Foundry's encrypted keystore:

```bash
cast wallet import $MAINNET_DEPLOYER_NAME --interactive
cast wallet import $OPTIMISM_DEPLOYER_NAME --interactive
```

```bash
Expand Down
10 changes: 6 additions & 4 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ out = 'out-via-ir'
src = 'src/interfaces/'

[fuzz]
runs = 1000
runs = 256

[rpc_endpoints]
mainnet = "${MAINNET_RPC}"
sepolia = "${SEPOLIA_RPC}"
gnosis = 'https://rpc.gnosis.gateway.fm'
optimism = 'https://mainnet.optimism.io'
sepolia = 'https://rpc.sepolia.io'

[etherscan]
mainnet = { key = "${ETHERSCAN_API_KEY}" }
gnosis = { key = "${ETHERSCAN_API_KEY}" }
optimism = { key = "${ETHERSCAN_API_KEY}" }
sepolia = { key = "${ETHERSCAN_API_KEY}" }
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"build": "forge build",
"build:optimized": "FOUNDRY_PROFILE=optimized forge build",
"coverage": "forge coverage --report summary --report lcov --match-path 'test/unit/*'",
"deploy:mainnet": "bash -c 'source .env && forge script Deploy --rpc-url $MAINNET_RPC --account $MAINNET_DEPLOYER_NAME --broadcast --verify --chain mainnet -vvvvv'",
"deploy:sepolia": "bash -c 'source .env && forge script Deploy --rpc-url $SEPOLIA_RPC --account $SEPOLIA_DEPLOYER_NAME --broadcast --verify --chain sepolia -vvvvv'",
"deploy:gnosis": "bash -c 'source .env && forge script Deploy --rpc-url $GNOSIS_RPC --private-key $GNOSIS_PK --broadcast --verify --chain gnosis -vvvvv'",
"deploy:optimism": "bash -c 'source .env && forge script Deploy --rpc-url $OPTIMISM_RPC --private-key $OPTIMISM_PK --broadcast --verify --chain optimism -vvvvv'",
"deploy:sepolia": "bash -c 'source .env && forge script Deploy --rpc-url $SEPOLIA_RPC --private-key $SEPOLIA_PK --broadcast --verify --chain sepolia -vvvvv'",
"lint:check": "yarn lint:sol && forge fmt --check",
"lint:fix": "sort-package-json && forge fmt && yarn lint:sol --fix",
"lint:natspec": "npx @defi-wonderland/natspec-smells --config natspec-smells.config.js",
Expand All @@ -32,6 +33,10 @@
"(src|test|script)/**/*.sol": "yarn lint:sol",
"package.json": "sort-package-json"
},
"dependencies": {
"@openzeppelin/contracts": "^5.1.0",
"@openzeppelin/contracts-upgradeable": "^5.1.0"
},
"devDependencies": {
"@commitlint/cli": "19.3.0",
"@commitlint/config-conventional": "19.2.2",
Expand Down
3 changes: 3 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
forge-std/=node_modules/forge-std/src
halmos-cheatcodes=node_modules/halmos-cheatcodes

@openzeppelin/=node_modules/@openzeppelin/contracts/
@openzeppelin-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/

contracts/=src/contracts
interfaces/=src/interfaces
40 changes: 40 additions & 0 deletions script/Common.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {IERC20} from '@openzeppelin/token/ERC20/IERC20.sol';
import {Greeter, IGreeter} from 'contracts/Greeter.sol';
import {Script} from 'forge-std/Script.sol';
// solhint-disable-next-line
import 'script/Registry.sol';

/**
* @title Common Contract
* @author Breadchain
* @notice This contract is used to deploy the Greeter contract
* @dev This contract is intended for use in Scripts and Integration Tests
*/
contract Common is Script {
struct DeploymentParams {
string greeting;
IERC20 token;
}

IGreeter public greeter;

/// @notice Deployment parameters for each chain
mapping(uint256 _chainId => DeploymentParams _params) internal _deploymentParams;

function setUp() public virtual {
// Optimism
_deploymentParams[10] = DeploymentParams('Hello, Optimism!', IERC20(OPTIMISM_DAI));

// Gnosis
_deploymentParams[100] = DeploymentParams('Hello, Gnosis!', IERC20(GNOSIS_BREAD));
}

function _deployContracts() internal {
DeploymentParams memory _params = _deploymentParams[block.chainid];

greeter = new Greeter(_params.greeting, _params.token);
}
}
29 changes: 5 additions & 24 deletions script/Deploy.sol
Original file line number Diff line number Diff line change
@@ -1,33 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Greeter} from 'contracts/Greeter.sol';
import {Script} from 'forge-std/Script.sol';
import {IERC20} from 'forge-std/interfaces/IERC20.sol';

contract Deploy is Script {
struct DeploymentParams {
string greeting;
IERC20 token;
}

/// @notice Deployment parameters for each chain
mapping(uint256 _chainId => DeploymentParams _params) internal _deploymentParams;

function setUp() public {
// Mainnet
_deploymentParams[1] = DeploymentParams('Hello, Mainnet!', IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2));

// Sepolia
_deploymentParams[11_155_111] =
DeploymentParams('Hello, Sepolia!', IERC20(0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6));
}
import {Common} from 'script/Common.sol';

contract Deploy is Common {
function run() public {
DeploymentParams memory _params = _deploymentParams[block.chainid];

vm.startBroadcast();
new Greeter(_params.greeting, _params.token);

_deployContracts();

vm.stopBroadcast();
}
}
17 changes: 17 additions & 0 deletions script/Registry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

/// @dev Example of addresses that may be stored in the Registry for use throughout the repository

// Tokens (Optimism Chain)
address constant OPTIMISM_DAI = 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1;

// Tokens (Gnosis Chain)
address constant GNOSIS_BREAD = 0xa555d5344f6FB6c65da19e403Cb4c1eC4a1a5Ee3;
address constant GNOSIS_XDAI = 0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d;

// Curve Factory (Gnosis Chain)
address constant GNOSIS_CURVE_STABLE_SWAP_FACTORY = 0xbC0797015fcFc47d9C1856639CaE50D0e69FbEE8;

// Liquidity Pools (Gnosis Chain)
address constant GNOSIS_CURVE_POOL_XDAI_BREAD = 0xf3D8F3dE71657D342db60dd714c8a2aE37Eac6B4;
4 changes: 2 additions & 2 deletions src/contracts/Greeter.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {IERC20} from 'forge-std/interfaces/IERC20.sol';
import {IERC20} from '@openzeppelin/token/ERC20/IERC20.sol';
import {IGreeter} from 'interfaces/IGreeter.sol';

contract Greeter is IGreeter {
Expand Down Expand Up @@ -44,7 +44,7 @@ contract Greeter is IGreeter {
/// @inheritdoc IGreeter
function greet() external view returns (string memory _greeting, uint256 _balance) {
_greeting = greeting;
_balance = token.balanceOf(msg.sender);
_balance = token.balanceOf(address(this));
}

/// @inheritdoc IGreeter
Expand Down
4 changes: 2 additions & 2 deletions src/interfaces/IGreeter.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {IERC20} from 'forge-std/interfaces/IERC20.sol';
import {IERC20} from '@openzeppelin/token/ERC20/IERC20.sol';

/**
* @title Greeter Contract
* @author Wonderland
* @author Breadchain
* @notice This is a basic contract created in order to portray some
* best practices and foundry functionality.
*/
Expand Down
22 changes: 17 additions & 5 deletions test/integration/Greeter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,25 @@ pragma solidity 0.8.23;
import {IntegrationBase} from 'test/integration/IntegrationBase.sol';

contract IntegrationGreeter is IntegrationBase {
string internal _newGreeting;

function setUp() public override {
/// @dev override for more specific setup
super.setUp();
_newGreeting = 'Hello, Breadchain!';
}

function test_Greet() public {
uint256 _whaleBalance = _dai.balanceOf(_daiWhale);
DeploymentParams memory _params = _deploymentParams[block.chainid];

(string memory _initialGreeting, uint256 _balance) = greeter.greet();
assertEq(_params.greeting, _initialGreeting);
assertEq(INIT_BALANCE, _balance);

vm.prank(_daiWhale);
(string memory _greeting, uint256 _balance) = _greeter.greet();
vm.prank(owner);
greeter.setGreeting(_newGreeting);

assertEq(_whaleBalance, _balance);
assertEq(_initialGreeting, _greeting);
(string memory _currentGreeting,) = greeter.greet();
assertEq(_currentGreeting, greeter.greeting());
}
}
34 changes: 20 additions & 14 deletions test/integration/IntegrationBase.sol
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Greeter, IGreeter} from 'contracts/Greeter.sol';
import {IERC20} from '@openzeppelin/token/ERC20/IERC20.sol';
import {Test} from 'forge-std/Test.sol';
import {IERC20} from 'forge-std/interfaces/IERC20.sol';
import {Common} from 'script/Common.sol';
// solhint-disable-next-line
import 'script/Registry.sol';

contract IntegrationBase is Test {
uint256 internal constant _FORK_BLOCK = 18_920_905;
contract IntegrationBase is Common, Test {
uint256 public constant INIT_BALANCE = 1 ether;

string internal _initialGreeting = 'hola';
address internal _user = makeAddr('user');
address internal _owner = makeAddr('owner');
address internal _daiWhale = 0x42f8CA49E88A8fd8F0bfA2C739e648468b8f9dec;
IERC20 internal _dai = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
IGreeter internal _greeter;
address public user = makeAddr('user');
address public owner = makeAddr('owner');

function setUp() public {
vm.createSelectFork(vm.rpcUrl('mainnet'), _FORK_BLOCK);
vm.prank(_owner);
_greeter = new Greeter(_initialGreeting, _dai);
IERC20 public bread = IERC20(GNOSIS_BREAD);

function setUp() public virtual override {
super.setUp();
vm.createSelectFork(vm.rpcUrl('gnosis'));
vm.startPrank(owner);

/// @dev deploy contracts methods are located in script/Common.sol
_deployContracts();

vm.stopPrank();
deal(GNOSIS_BREAD, address(greeter), INIT_BALANCE);
}
}
2 changes: 1 addition & 1 deletion test/unit/Greeter.t.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.23;

import {IERC20} from '@openzeppelin/token/ERC20/IERC20.sol';
import {Greeter, IGreeter} from 'contracts/Greeter.sol';
import {Test} from 'forge-std/Test.sol';
import {IERC20} from 'forge-std/interfaces/IERC20.sol';

contract UnitGreeter is Test {
address internal _owner = makeAddr('owner');
Expand Down
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,16 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@openzeppelin/contracts-upgradeable@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.1.0.tgz#4d37648b7402929c53e2ff6e45749ecff91eb2b6"
integrity sha512-AIElwP5Ck+cslNE+Hkemf5SxjJoF4wBvvjxc27Rp+9jaPs/CLIaUBMYe1FNzhdiN0cYuwGRmYaRHmmntuiju4Q==

"@openzeppelin/contracts@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.1.0.tgz#4e61162f2a2bf414c4e10c45eca98ce5f1aadbd4"
integrity sha512-p1ULhl7BXzjjbha5aqst+QMLY+4/LCWADXOCsmLHRM77AqiPjnd9vvUN9sosUfhL9JGKpZ0TjEGxgvnizmWGSA==

"@scure/base@~1.1.4":
version "1.1.6"
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d"
Expand Down

0 comments on commit 949c774

Please sign in to comment.