A set of Solidity smart contracts, to implement builder incentives and staker rewards mechanisms to be integrated with the DAO.
- Forge: compile, test, fuzz, format, and deploy smart contracts
- Forge Std: collection of helpful contracts and utilities for testing
- Prettier: code formatter for non-Solidity files
- Solhint: linter for Solidity code
- Hardhat: integration testing, interact with RSKj
The following tools are required to be installed:
- bun - For macOS, Linux, and WSL run:
curl -fsSL https://bun.sh/install | bash
- foundry - Simply run:
curl -L https://foundry.paradigm.xyz | bash
- direnv - Although a large variety of OSs comes with
direnv
packaged, the easiest way to install it is by running:curl -sfL https://direnv.net/install.sh | bash
- jq - Again, this is packaged to a variety of OSs, but if you don't have it follow the link to install it for your system
Important
Please make sure to install foundry using the branch f625d0fa7c51e65b4bf1e8f7931cd1c6e2e285e9
. By using
the latest version we experienced the following error on RSKj:
... deserialization error: missing field ``effectiveGasPrice`` ...
That foundry branch works with cargo >=1.76.0 or <= 1.79.0. So first, switch the version
rustup default 1.79.0
foundryup --branch f625d0fa7c51e65b4bf1e8f7931cd1c6e2e285e9
If this is your first time with Foundry, check out the installation instructions.
Clone the repo and install the dependencies
git clone https://github.com/RootstockCollective/collective-rewards-sc.git
cd collective-rewards-sc
bun install # install Solhint, Prettier, Hardhat and other Node.js deps
Warning
.env.<chain_id>
could be public, don't put your private key or mnemonic there. Use .env
for that.
cp .env.private.example .env
and change the values to
# .env
# Required
export PRIVATE_KEY="{your-private-key}"
When you change to the project directory (cd collective-rewards-sc
), the shell will ask you to run
direnv allow
upon which you'll be asked to present the chain id of the network you wish to use. This will be written in a file called
.chain_id
. Alternatively, you can create this file yourself (content of which is only the chain id number itself)
before calling direnv allow
. This could be particularly useful when wishing to load configs for and to deploy to
multiple networks.
echo "31" > .chain_id && direnv allow
Or non-persistently (deleted upon reboot) with:
echo "31" > $(mktemp .chain_id) && direnv allow
This will subsequently create environment variables (will unload after exiting the directory; for more info see
direnv docs) specified for given network inside the .env.<chain_id>
file. If such file does not
exist you will be asked to create one for your network. This can be done by copying/moving the .env.example
file and
changing the example vars to match your desired network. For example:
mv .env.example .env.42
and change the values to
# .env.42
# mynet specific configuration
# Required
export DEPLOYMENT_CONTEXT="regtest"
export RPC_URL="https://dolphinnet.node"
export REWARD_TOKEN_ADDRESS="0x14f6504A7ca4e574868cf8b49e85187d3Da9FA70"
export STAKING_TOKEN_ADDRESS="0x14f6504A7ca4e574868cf8b49e85187d3Da9FA71"
export GOVERNOR_ADDRESS="0x14f6504A7ca4e574868cf8b49e85187d3Da9FA72"
export CHANGE_EXECUTOR_ADDRESS="0x14f6504A7ca4e574868cf8b49e85187d3Da9FA73"
export FOUNDATION_TREASURY_ADDRESS="0x14f6504A7ca4e574868cf8b49e85187d3Da9FA74"
# Optional
export DEPLOYER_ADDRESS="0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
# Custom
export RPC_KEY="someRandomJWTToken" # if any
Then re-run direnv allow
to load the new env vars.
If for development purposes you'd like to avoid using the deterministic deployer (CREATE2), so that your node doesn't
need to be restarted every time you want to redeploy, you can use NO_DD=true
environment variable to deploy using
simple CREATE opcode (default if false, meaning it will use CREATE2).
This project builds upon the frameworks and libraries mentioned above, so please consult their respective documentation for details about their specific features.
For example, if you're interested in exploring Foundry in more detail, you should look at the Foundry Book. In particular, you may be interested in reading the Writing Tests tutorial.
Foundry was integrated with Hardhat following the Integrating with Hardhat guide.
This template comes with a set of sensible default configurations for you to use. These defaults can be found in the following files:
├── .commitlintrc.ts
├── .editorconfig
├── .gitignore
├── .prettierignore
├── .prettierrc.yml
├── .solhint.json
├── foundry.toml
├── hardhat.config.ts
└── remappings.txt
The project is IDE agnostic, but for the best user experience, you may want to use it in VSCode alongside Nomic Foundation's Solidity extension.
For guidance on how to integrate a Foundry project in VSCode, please refer to this guide.
Your contracts will be linted and tested on every push and pull request made to the main
branch.
While foundry generally uses submodules to manage dependencies, this project uses Node.js packages because submodules don't scale.
This is how to install dependencies:
- Install the dependency using your preferred package manager, e.g.
bun install dependency-name
- Use this syntax to install from GitHub:
bun install github:username/repo-name
- Use this syntax to install from GitHub:
- Add a remapping for the dependency in remappings.txt, e.g.
dependency-name=node_modules/dependency-name
Note that OpenZeppelin Contracts is pre-installed, so you can follow that as an example.
To write a new test contract, you can follow the Foundry tests guide.
To write a new integration test, you can follow the Hardhat testing contracts guide.
This is a list of the most frequently needed commands.
Build and compile the contracts:
bun run compile
Delete the build artifacts, typechain types and cache directories:
bun run clean
The simples way to deploy is to use the command
bun run deploy
This will also generate hardhat-style artifacts unless OMIT_HARDHAT_ARTIFACTS
is set to true
:
OMIT_HARDHAT_ARTIFACTS=true bun run deploy
When deploying the contracts to RSKj locally, one of the unlocked accounts can be used:
forge script script/Deploy.s.sol --rpc-url "$RPC_URL" --legacy --broadcast --sender $ACCOUNT --unlocked --chain-id "$CHAIN_ID"
It's also possible to use any private key (as far as the associated account has balance to execute transactions):
forge script script/Deploy.s.sol --rpc-url "$RPC_URL" --legacy --broadcast --private-key "$PRIVATE_KEY" --chain-id "$CHAIN_ID"
Deploy to Anvil:
forge script script/Deploy.s.sol --broadcast --fork-url http://localhost:8545
For this script to work, you need to have a MNEMONIC
environment variable set to a valid
BIP39 mnemonic.
For instructions on how to deploy to a testnet or mainnet, check out the Solidity Scripting tutorial.
In order to use the Deploy script as is, you will need to configure the addresses of:
- Reward token - see glossary - use environment variable
REWARD_TOKEN_ADDRESS
- Staking token - see glossary - use environment variable
STAKING_TOKEN_ADDRESS
- Governor - see glossary - use environment variable
GOVERNOR_ADDRESS
- KYC Approver - see glossary - use environment variable
KYC_APPROVER_ADDRESS
- Foundation treasury - see glossary - use environment variable
FOUNDATION_TREASURY_ADDRESS
For development and testing purposes you may like to deploy some of the mock contracts:
forge script script/MockToken.s.sol #the rest of the command arguments
should you like to number the the token in the name (for multiple tokens) use either an env var:
MOCK_TOKEN_COUNTER=42 forge script script/MockToken.s.sol #the rest of the command arguments
or a function signature that accepts this parameter:
forge script script/MockToken.s.sol -s "run(uint)" 42 #the rest of the command arguments
forge script script/test_mock/ChangeExecutorMock.s.sol #the rest of the command arguments
to pass a governor to the deployemnt you can either use env var:
GOVERNOR_ADDRESS="0xYOUR_GOV" forge script script/test_mock/ChangeExecutorMock.s.sol #the rest of the command arguments
or pass it directly, specifying the function signature that accepts it:
forge script script/test_mock/ChangeExecutorMock.s.sol -s "run(address)" "0xYOUR_GOV" #the rest of the command arguments
The script should create a new directory (if it doesn't exist already) ./deployments/$DEPLOYMENT_CONTEXT
and output
all deployed contract addresses into a file called contract_addresses.json
. Run direnv allow
or re-open the project
directory to load these automatically into environment variables.
Format the contracts:
bun run prettier:check
Lint the contracts:
bun run lint
Run the foundry tests:
bun run test
Run the hardhat tests:
bun run test:integration
You can test against RSKj locally:
bun run test:integration --network regtest
Generate test coverage and output result to the terminal:
bun run test:coverage
Generate coverage report:
-
Install lcov
apt-get install lcov
-
Generate report
bun run test:coverage:report
-
Open
coverage/index.html
The reward token for the collective incentives program is RIF
.
The staking token is stRIF
, where the token balance represents the backing power a backer has to vote for builders.
The Governor represents the DAO’s governance mechanism and is in charge of the community approval and de-whitelisting of builders.
The RoostockCollective Foundation requires builders to got through a KYC process and is in charge of submitting said approval as well as revoking it if necessary.
The RoostockCollective Foundation is in charge of holding and distributing the rewards from the program.
The project is built using the PaulRBerg foundry-template.
This project is licensed under MIT.