A set of Solidity smart contracts, to implement builder incentives and staker rewards mechanisms to be integrated with the DAO. For technical details, please refer to the specifications.
- 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
direnvpackaged, 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
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 depsWarning
.env.<chain_id> could be public, don't put your private key or mnemonic there. Use .env for that.
cp .env.private.example .envand 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 allowupon 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 allowOr non-persistently (deleted upon reboot) with:
echo "31" > $(mktemp .chain_id) && direnv allowThis 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.42and change the values to
# .env.42
# mynet specific configuration
# Required
export DEPLOYMENT_CONTEXT="regtest"
export RPC_URL="https://dolphinnet.node"
export RIF_TOKEN_ADDRESS="0x14f6504A7ca4e574868cf8b49e85187d3Da9FA70"
export USDRIF_TOKEN_ADDRESS="0x2ca054b47a89d9E8C2956Fefe5e569FC3796eC0E"
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 anyThen 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
Security Note: For CI/CD and production environments, always use bun run install:ci instead of bun install to prevent supply chain attacks by ensuring only locked dependency versions are installed.
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 compileDelete the build artifacts, typechain types and cache directories:
bun run cleanThe simples way to deploy is to use the command
bun run deployThis will also copy the ABI files unless OMIT_ABI_COPY is set to true:
OMIT_ABI_COPY=true bun run deployWhen 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:8545For 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:
- RIF token - see glossary - use environment variable
RIF_TOKEN_ADDRESS - USDRIF token - see glossary - use environment variable
USDRIF_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/test_mock/MockToken.s.sol #the rest of the command argumentsshould 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/test_mock/MockToken.s.sol #the rest of the command argumentsor a function signature that accepts this parameter:
forge script script/test_mock/MockToken.s.sol -s "run(uint)" 42 #the rest of the command argumentsforge script script/test_mock/ChangeExecutorMock.s.sol #the rest of the command argumentsto 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 argumentsor 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 argumentsThe 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:checkLint the contracts:
bun run lintRun the foundry tests:
bun run testRun the hardhat tests:
bun run test:integrationYou can test against RSKj locally:
bun run test:integration --network regtestGenerate test coverage and output result to the terminal:
bun run test:coverageGenerate coverage report:
-
Install lcov
apt-get install lcov
-
Generate report
bun run test:coverage:report
-
Open
coverage/index.html
The reward tokens for the collective incentives program are RIF and USDRIF. Only tokens that adhere to the ERC-20 standard are supported.
Unsupported Token Types:
- Fee-on-transfer tokens (e.g., those that deduct a fee per transfer).
- Rebasing tokens (e.g., tokens that dynamically adjust balances).
Using these unsupported token types may disrupt the reward distribution logic, leading to unexpected behavior. Ensure that only standard ERC-20 tokens are used.
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 ban 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.