This file is a merged representation of a subset of the codebase, containing files not matching ignore patterns, combined into a single document by Repomix.
The content has been processed where content has been compressed (code blocks are separated by ⋮---- delimiter).
# File Summary
## Purpose
This file contains a packed representation of a subset of the repository's contents that is considered the most important context.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.
## File Format
The content is organized as follows:
1. This summary section
2. Repository information
3. Directory structure
4. Repository files (if enabled)
5. Multiple file entries, each consisting of:
a. A header with the file path (## File: path/to/file)
b. The full contents of the file in a code block
## Usage Guidelines
- This file should be treated as read-only. Any changes should be made to the
original repository files, not this packed version.
- When processing this file, use the file path to distinguish
between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
the same level of security as you would the original repository.
## Notes
- Some files may have been excluded based on .gitignore rules and Repomix's configuration
- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
- Files matching these patterns are excluded: docs/
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Content has been compressed - code blocks are separated by ⋮---- delimiter
- Files are sorted by Git change count (files with more changes are at the bottom)
# Directory Structure
```
.github/
workflows/
lint.yml
test.yml
audits/
ChainSecurity-Rewards.pdf
OtterSec-Core&Rewards.pdf
Statemind-Core&Rewards.pdf
cli/
defaultOperatorRewards/
data/
distribution.json.example
trees.json.example
distributionToProofs.js
distributionToRoots.js
distributionToTrees.js
treesToDistribution.js
treesToProofs.js
treesToRoots.js
script/
deploy/
genesis/
DefaultOperatorRewardsFactory.s.sol
DefaultRewardsFactories.s.sol
DefaultStakerRewardsFactory.s.sol
DefaultOperatorRewards.s.sol
DefaultStakerRewards.s.sol
specs/
OperatorRewards.md
StakerRewards.md
src/
contracts/
defaultOperatorRewards/
DefaultOperatorRewards.sol
DefaultOperatorRewardsFactory.sol
defaultStakerRewards/
DefaultStakerRewards.sol
DefaultStakerRewardsFactory.sol
interfaces/
defaultOperatorRewards/
IDefaultOperatorRewards.sol
IDefaultOperatorRewardsFactory.sol
defaultStakerRewards/
IDefaultStakerRewards.sol
IDefaultStakerRewardsFactory.sol
stakerRewards/
IStakerRewards.sol
test/
defaultOperatorRewards/
DefaultOperatorRewards.t.sol
DefaultOperatorRewardsFactory.t.sol
defaultStakerRewards/
DefaultStakerRewards.t.sol
DefaultStakerRewardsFactory.t.sol
integration/
SymbioticRewardsBindings.sol
SymbioticRewardsConstants.sol
SymbioticRewardsImports.sol
SymbioticRewardsImportsContracts.sol
SymbioticRewardsInit.sol
SymbioticRewardsIntegration.sol
SymbioticRewardsIntegrationExample.sol
mocks/
SimpleStakerRewards.sol
stakerRewards/
StakerRewards.t.sol
.env.example
.gitignore
.gitmodules
foundry.toml
LICENSE
package.json
README.md
remappings.txt
```
# Files
## File: .github/workflows/lint.yml
````yaml
name: lint
on:
push:
branches:
- main
pull_request:
jobs:
run-linters:
name: Run linters
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v3
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- name: Lint
run: forge fmt --check
````
## File: .github/workflows/test.yml
````yaml
name: test
on:
push:
branches:
- main
pull_request:
jobs:
run-tests:
name: Run tests
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 tests
run: |
forge test -vvv
id: test
````
## File: cli/defaultOperatorRewards/data/distribution.json.example
````
[
{
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"operators": [
{
"operator": "0x0000000000000000000000000000000000000001",
"reward": "100000000000000000"
},
{
"operator": "0x0000000000000000000000000000000000000002",
"reward": "300000000000000000"
},
{
"operator": "0x0000000000000000000000000000000000000003",
"reward": "600000000000000000"
}
]
},
{
"token": "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0",
"operators": [
{
"operator": "0x0000000000000000000000000000000000000002",
"reward": "200000000000000000"
},
{
"operator": "0x0000000000000000000000000000000000000003",
"reward": "250000000000000000"
},
{
"operator": "0x0000000000000000000000000000000000000004",
"reward": "500000000000000000"
}
]
}
]
````
## File: cli/defaultOperatorRewards/data/trees.json.example
````
[{"token":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","tree":{"format":"standard-v1","leafEncoding":["address","uint256"],"tree":["0x1421466e8f910cab140a44dd533adb90ebc2d87e0ab91e851737a77ecd394224","0x54ce56851dece12f32823f4bb940800fb9cf57d68bcdda73e2436bf1e5be661d","0xb77d490dc0f9580cc767909bba59fd55900dec274b637ed820a391c018c8858a","0x92864efb389a13533b2b5d94eda464bce7dbe336d06c6d5feb673c30460c10ee","0x1c93c9b111fdf5d2ab331e2ad2de1d3ffe1c8590f544554a1b0247bd46be37a4"],"values":[{"value":["0x0000000000000000000000000000000000000001","100000000000000000"],"treeIndex":2},{"value":["0x0000000000000000000000000000000000000002","300000000000000000"],"treeIndex":3},{"value":["0x0000000000000000000000000000000000000003","600000000000000000"],"treeIndex":4}]}},{"token":"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0","tree":{"format":"standard-v1","leafEncoding":["address","uint256"],"tree":["0x4317f898d0c584ac608ed5c819f16cf5307fa2f07d5e1aa995493e46662a7a7f","0x30f6473979500545c8fa671ffec7060e0504909b4977e2a19af00c1c7c72fb49","0xc0151a5b2dc7e51c8ef30cdc65dfbedf3082c5678bad3ba3354827c6c6f52a45","0x5fa8fb18326605f1825c6988667a5162b2d926adb03d1cb670eddccf6a7ea42e","0x5c1aed882581724b9b52eecbc172dbc8f0ff57dde40b93f18139429763e0ccbd"],"values":[{"value":["0x0000000000000000000000000000000000000002","200000000000000000"],"treeIndex":3},{"value":["0x0000000000000000000000000000000000000003","250000000000000000"],"treeIndex":2},{"value":["0x0000000000000000000000000000000000000004","500000000000000000"],"treeIndex":4}]}}]
````
## File: cli/defaultOperatorRewards/distributionToProofs.js
````javascript
function generateMerkleTrees() {
⋮----
Distribution.forEach((tokenData) => {
const values = tokenData.operators.map(({ operator, reward }) => [
⋮----
reward.toString(),
⋮----
const tree = StandardMerkleTree.of(values, ['address', 'uint256'])
⋮----
function findProof(trees, operator) {
for (const [token, { tree, values }] of Object.entries(trees)) {
for (const [index, value] of values.entries()) {
if (value[0].toLowerCase() === operator.toLowerCase()) {
const proof = tree.getProof(index)
console.log(`-------------------------------`)
console.log(`Token: ${token}`)
console.log(`Proof: ${JSON.stringify(proof)}`)
⋮----
function main() {
const trees = generateMerkleTrees()
⋮----
console.error('Please provide an operator address.')
process.exit(1)
⋮----
findProof(trees, operator)
⋮----
main()
````
## File: cli/defaultOperatorRewards/distributionToRoots.js
````javascript
function generateMerkleTrees() {
⋮----
Distribution.forEach((tokenData) => {
const values = tokenData.operators.map(({ operator, reward }) => [
⋮----
reward.toString(),
⋮----
const tree = StandardMerkleTree.of(values, ['address', 'uint256'])
⋮----
function main() {
const trees = generateMerkleTrees()
⋮----
for (const [token, { tree }] of Object.entries(trees)) {
console.log(`-------------------------------`)
console.log(`Token: ${token}`)
console.log(`Merkle Root: ${tree.root}`)
⋮----
main()
````
## File: cli/defaultOperatorRewards/distributionToTrees.js
````javascript
function generateMerkleTrees() {
⋮----
Distribution.forEach((tokenData) => {
const values = tokenData.operators.map(({ operator, reward }) => [
⋮----
reward.toString(),
⋮----
const tree = StandardMerkleTree.of(values, ['address', 'uint256'])
⋮----
function main() {
const trees = generateMerkleTrees()
⋮----
for (const [token, { tree }] of Object.entries(trees)) {
treesJson.push({ token, tree })
⋮----
const filePath = path.join(__dirname, fileName)
⋮----
fs.writeFileSync(filePath, JSON.stringify(treesJson, null, 2))
console.log(`Trees written to ${fileName}`)
⋮----
main()
````
## File: cli/defaultOperatorRewards/treesToDistribution.js
````javascript
function parseMerkleTrees() {
⋮----
Trees.forEach((tokenData) => {
const values = tokenData.tree.values.map(
⋮----
const tree = StandardMerkleTree.of(values, ['address', 'uint256'])
⋮----
function main() {
const trees = parseMerkleTrees()
⋮----
for (const [token, { values }] of Object.entries(trees)) {
distributionJson.push({
⋮----
operators: values.map(([operator, reward]) => ({
⋮----
const filePath = path.join(__dirname, fileName)
⋮----
fs.writeFileSync(filePath, JSON.stringify(distributionJson, null, 2))
console.log(`Distribution written to ${fileName}`)
⋮----
main()
````
## File: cli/defaultOperatorRewards/treesToProofs.js
````javascript
function parseMerkleTrees() {
⋮----
Trees.forEach((tokenData) => {
const values = tokenData.tree.values.map(
⋮----
const tree = StandardMerkleTree.of(values, ['address', 'uint256'])
⋮----
function findProof(trees, operator) {
for (const [token, { tree, values }] of Object.entries(trees)) {
for (const [index, value] of values.entries()) {
if (value[0].toLowerCase() === operator.toLowerCase()) {
const proof = tree.getProof(index)
console.log(`-------------------------------`)
console.log(`Token: ${token}`)
console.log(`Proof: ${JSON.stringify(proof)}`)
⋮----
function main() {
const trees = parseMerkleTrees()
⋮----
console.error('Please provide an operator address.')
process.exit(1)
⋮----
findProof(trees, operator)
⋮----
main()
````
## File: cli/defaultOperatorRewards/treesToRoots.js
````javascript
function parseMerkleTrees() {
⋮----
Trees.forEach((tokenData) => {
const values = tokenData.tree.values.map(
⋮----
const tree = StandardMerkleTree.load(tokenData.tree);
⋮----
function main() {
const trees = parseMerkleTrees()
⋮----
for (const [token, { tree }] of Object.entries(trees)) {
console.log(`-------------------------------`)
console.log(`Token: ${token}`)
console.log(`Merkle Root: ${tree.root}`)
⋮----
main()
````
## File: script/deploy/genesis/DefaultOperatorRewardsFactory.s.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {console2, Script} from "forge-std/Script.sol";
⋮----
import {SymbioticCoreConstants} from "@symbioticfi/core/test/integration/SymbioticCoreConstants.sol";
⋮----
import {DefaultOperatorRewards} from "../../../src/contracts/defaultOperatorRewards/DefaultOperatorRewards.sol";
import {DefaultOperatorRewardsFactory} from
"../../../src/contracts/defaultOperatorRewards/DefaultOperatorRewardsFactory.sol";
⋮----
contract DefaultOperatorRewardsFactoryScript is Script {
function run() external {
````
## File: script/deploy/genesis/DefaultRewardsFactories.s.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {console2, Script} from "forge-std/Script.sol";
⋮----
import {SymbioticCoreConstants} from "@symbioticfi/core/test/integration/SymbioticCoreConstants.sol";
⋮----
import {DefaultStakerRewards} from "../../../src/contracts/defaultStakerRewards/DefaultStakerRewards.sol";
import {DefaultStakerRewardsFactory} from "../../../src/contracts/defaultStakerRewards/DefaultStakerRewardsFactory.sol";
⋮----
import {DefaultOperatorRewards} from "../../../src/contracts/defaultOperatorRewards/DefaultOperatorRewards.sol";
import {DefaultOperatorRewardsFactory} from
"../../../src/contracts/defaultOperatorRewards/DefaultOperatorRewardsFactory.sol";
⋮----
contract DefaultRewardsFactoriesScript is Script {
function run() external {
````
## File: script/deploy/genesis/DefaultStakerRewardsFactory.s.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {console2, Script} from "forge-std/Script.sol";
⋮----
import {SymbioticCoreConstants} from "@symbioticfi/core/test/integration/SymbioticCoreConstants.sol";
⋮----
import {DefaultStakerRewards} from "../../../src/contracts/defaultStakerRewards/DefaultStakerRewards.sol";
import {DefaultStakerRewardsFactory} from "../../../src/contracts/defaultStakerRewards/DefaultStakerRewardsFactory.sol";
⋮----
contract DefaultStakerRewardsFactoryScript is Script {
function run() external {
````
## File: script/deploy/DefaultOperatorRewards.s.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {console2, Script} from "forge-std/Script.sol";
⋮----
import {SymbioticRewardsConstants} from "../../test/integration/SymbioticRewardsConstants.sol";
⋮----
contract DefaultOperatorRewardsScript is Script {
function run() external {
````
## File: script/deploy/DefaultStakerRewards.s.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {console2, Script} from "forge-std/Script.sol";
⋮----
import {SymbioticRewardsConstants} from "../../test/integration/SymbioticRewardsConstants.sol";
⋮----
import {IDefaultStakerRewards} from "../../src/interfaces/defaultStakerRewards/IDefaultStakerRewards.sol";
⋮----
contract DefaultStakerRewardsScript is Script {
function run(
````
## File: specs/OperatorRewards.md
````markdown
## Operator Rewards
The network distributes the operator rewards at its discretion. Here are three examples:
1. The network performs off-chain calculations to determine the reward distributions. After calculating the rewards, the network executes batch transfers to distribute the rewards in a consolidated manner.
2. The network performs off-chain calculations to determine rewards and generates a Merkle tree, allowing operators to claim their rewards.
3. The network performs on-chain reward calculations within its middleware to determine the distribution of rewards.
##### Source of Data for Network On-Chain Reward Calculations
For operator rewards, the delegator module of the vault provides:
- `Delegator.stakeAt(network, operator, timestamp, hint)` - Active stake of an operator in the network.
Additionally, all operators register through the network, providing necessary details such as commission rates, fixed payments, and other relevant conditions. This registration process ensures that networks have the required data to perform accurate on-chain reward calculations in their middleware.
### Deploy
```shell
source .env
```
#### Deploy factory
Deployment script: [click](../script/deploy/genesis/DefaultOperatorRewardsFactory.s.sol)
```shell
forge script script/deploy/genesis/DefaultOperatorRewardsFactory.s.sol:DefaultOperatorRewardsFactoryScript --broadcast --rpc-url=$ETH_RPC_URL
```
#### Deploy entity
Deployment script: [click](../script/deploy/DefaultOperatorRewards.s.sol)
```shell
forge script script/deploy/DefaultOperatorRewards.s.sol:DefaultOperatorRewardsScript --sig "run()" --broadcast --rpc-url=$ETH_RPC_URL
```
### CLI
#### Install packages
```shell
npm install
```
#### Use commands
**distributionToRoots**
```shell
node cli/defaultOperatorRewards/distributionToRoots.js
```
Get a Merkle root for each rewards distribution by token.
_Make sure to save `distribution.json` to `cli/defaultOperatorRewards/data` folder. You can check [an example format here](../cli/defaultOperatorRewards/data/distribution.json.example)._
---
**distributionToProofs**
```shell
node cli/defaultOperatorRewards/distributionToProofs.js OPERATOR
```
Get proofs for all tokens in a rewards distribution for a given operator.
_Make sure to save `distribution.json` to `cli/defaultOperatorRewards/data` folder. You can check [an example format here](../cli/defaultOperatorRewards/data/distribution.json.example)._
Arguments:
- `OPERATOR` - an address of the operator to get proofs for
---
**distributionToTrees**
```shell
node cli/defaultOperatorRewards/distributionToTrees.js
```
Saves OZ Merkle tree structures constructed from the rewards distribution to `cli/defaultOperatorRewards/data/trees.json`.
_Make sure to save `distribution.json` to `cli/defaultOperatorRewards/data` folder. You can check [an example format here](../cli/defaultOperatorRewards/data/distribution.json.example)._
---
**treesToRoots**
```shell
node cli/defaultOperatorRewards/treesToRoots.js
```
Get a Merkle root for each Merkle tree by token.
_Make sure to save `trees.json` to `cli/defaultOperatorRewards/data` folder. You can check [an example format here](../cli/defaultOperatorRewards/data/trees.json.example)._
---
**treesToProofs**
```shell
node cli/defaultOperatorRewards/treesToProofs.js OPERATOR
```
Get proofs for all tokens in Merkle trees for a given operator.
_Make sure to save `trees.json` to `cli/defaultOperatorRewards/data` folder. You can check [an example format here](../cli/defaultOperatorRewards/data/trees.json.example)._
Arguments:
- `OPERATOR` - an address of the operator to get proofs for
---
**treesToDistribution**
```shell
node cli/defaultOperatorRewards/treesToDistribution.js
```
Saves a rewards distribution constructed from the OZ Merkle trees to `cli/defaultOperatorRewards/data/distribution.json`.
_Make sure to save `trees.json` to `cli/defaultOperatorRewards/data` folder. You can check [an example format here](../cli/defaultOperatorRewards/data/trees.json.example)._
````
## File: specs/StakerRewards.md
````markdown
## Staker Rewards
For staker rewards calculation, the vault provides the following data:
- `activeSharesOfAt(account, timestamp, hint)` - $\text{active}$ shares of the user at a specific timestamp
- `activeSharesAt(timestamp, hint)` - total $\text{active}$ shares at a specific timestamp.
- Other checkpointed getters
Reward processing is not integrated into the vault's functionality. Instead, external reward contracts should manage this using the provided data.
However, we created the first version of the `IStakerRewards` interface to facilitate more generic reward distribution across networks.
- `IStakerRewards.version()` - provides a version of the interface that a particular rewards contract uses
- `IStakerRewards.distributeRewards(network, token, amount, data)` - call to distribute `amount` of `token` on behalf of `network` using an arbitrary `data`
### Deploy
```shell
source .env
```
#### Deploy factory
Deployment script: [click](../script/deploy/genesis/DefaultStakerRewardsFactory.s.sol)
```shell
forge script script/deploy/genesis/DefaultStakerRewardsFactory.s.sol:DefaultStakerRewardsFactoryScript --broadcast --rpc-url=$ETH_RPC_URL
```
#### Deploy entity
Deployment script: [click](../script/deploy/DefaultStakerRewards.s.sol)
```shell
forge script script/deploy/DefaultStakerRewards.s.sol:DefaultStakerRewardsScript 0x0000000000000000000000000000000000000000 0 0x0000000000000000000000000000000000000000 0x0000000000000000000000000000000000000000 0x0000000000000000000000000000000000000000 --sig "run(address,uint256,address,address,address)" --broadcast --rpc-url=$ETH_RPC_URL
```
````
## File: src/contracts/defaultOperatorRewards/DefaultOperatorRewards.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {IDefaultOperatorRewards} from "../../interfaces/defaultOperatorRewards/IDefaultOperatorRewards.sol";
⋮----
import {INetworkMiddlewareService} from "@symbioticfi/core/src/interfaces/service/INetworkMiddlewareService.sol";
⋮----
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
⋮----
contract DefaultOperatorRewards is ReentrancyGuardUpgradeable, IDefaultOperatorRewards {
⋮----
/**
* @inheritdoc IDefaultOperatorRewards
*/
⋮----
function initialize() public initializer {
⋮----
function distributeRewards(address network, address token, uint256 amount, bytes32 root_) external nonReentrant {
⋮----
function claimRewards(
````
## File: src/contracts/defaultOperatorRewards/DefaultOperatorRewardsFactory.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {DefaultOperatorRewards} from "./DefaultOperatorRewards.sol";
⋮----
import {IDefaultOperatorRewardsFactory} from
"../../interfaces/defaultOperatorRewards/IDefaultOperatorRewardsFactory.sol";
⋮----
import {Registry} from "@symbioticfi/core/src/contracts/common/Registry.sol";
⋮----
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
⋮----
contract DefaultOperatorRewardsFactory is Registry, IDefaultOperatorRewardsFactory {
⋮----
/**
* @inheritdoc IDefaultOperatorRewardsFactory
*/
function create() external returns (address) {
````
## File: src/contracts/defaultStakerRewards/DefaultStakerRewards.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {IDefaultStakerRewards} from "../../interfaces/defaultStakerRewards/IDefaultStakerRewards.sol";
import {IStakerRewards} from "../../interfaces/stakerRewards/IStakerRewards.sol";
⋮----
import {INetworkMiddlewareService} from "@symbioticfi/core/src/interfaces/service/INetworkMiddlewareService.sol";
import {IRegistry} from "@symbioticfi/core/src/interfaces/common/IRegistry.sol";
import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
⋮----
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {MulticallUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
⋮----
contract DefaultStakerRewards is
⋮----
/**
* @inheritdoc IStakerRewards
*/
⋮----
/**
* @inheritdoc IDefaultStakerRewards
*/
⋮----
function rewardsLength(address token, address network) external view returns (uint256) {
⋮----
function claimable(
⋮----
// network - a network to claim rewards for
// maxRewards - the maximum amount of rewards to process
⋮----
function initialize(
⋮----
function distributeRewards(
⋮----
// timestamp - a time point stakes must be taken into account at
// maxAdminFee - the maximum admin fee to allow
// activeSharesHint - a hint index to optimize `activeSharesAt()` processing
// activeStakeHint - a hint index to optimize `activeStakeAt()` processing
⋮----
function claimRewards(address recipient, address token, bytes calldata data) external override nonReentrant {
⋮----
// activeSharesOfHints - hint indexes to optimize `activeSharesOf()` processing
⋮----
function claimAdminFee(address recipient, address token) external nonReentrant onlyRole(ADMIN_FEE_CLAIM_ROLE) {
⋮----
function setAdminFee(
⋮----
function _setAdminFee(
````
## File: src/contracts/defaultStakerRewards/DefaultStakerRewardsFactory.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {DefaultStakerRewards} from "./DefaultStakerRewards.sol";
⋮----
import {IDefaultStakerRewardsFactory} from "../../interfaces/defaultStakerRewards/IDefaultStakerRewardsFactory.sol";
⋮----
import {Registry} from "@symbioticfi/core/src/contracts/common/Registry.sol";
⋮----
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
⋮----
contract DefaultStakerRewardsFactory is Registry, IDefaultStakerRewardsFactory {
⋮----
/**
* @inheritdoc IDefaultStakerRewardsFactory
*/
function create(
````
## File: src/interfaces/defaultOperatorRewards/IDefaultOperatorRewards.sol
````
// SPDX-License-Identifier: MIT
⋮----
interface IDefaultOperatorRewards {
⋮----
/**
* @notice Emitted when rewards are distributed by a particular network using a given token by providing a Merkle root.
* @param network address of the network that distributed rewards
* @param token address of the token
* @param amount amount of tokens sent to the contract
* @param root Merkle root of the rewards distribution
* @dev The Merkle tree's leaves must represent an account and a claimable amount (the total amount of the reward tokens for the whole time).
*/
event DistributeRewards(address indexed network, address indexed token, uint256 amount, bytes32 root);
⋮----
/**
* @notice Emitted when rewards are claimed by a particular account for a particular network using a given token.
* @param recipient address of the rewards' recipient
* @param network address of the network
* @param token address of the token
* @param claimer address of the rewards' claimer
* @param amount amount of tokens claimed
*/
event ClaimRewards(
⋮----
/**
* @notice Get the network middleware service's address.
* @return address of the network middleware service
*/
function NETWORK_MIDDLEWARE_SERVICE() external view returns (address);
⋮----
/**
* @notice Get a Merkle root of a reward distribution for a particular network and token.
* @param network address of the network
* @param token address of the token
* @return Merkle root of the reward distribution
*/
function root(address network, address token) external view returns (bytes32);
⋮----
/**
* @notice Get an amount of tokens that can be claimed for a particular network.
* @param network address of the network
* @param token address of the token
* @return amount of tokens that can be claimed
*/
function balance(address network, address token) external view returns (uint256);
⋮----
/**
* @notice Get a claimed amount of rewards for a particular account, network, and token.
* @param network address of the network
* @param token address of the token
* @param account address of the claimer
* @return claimed amount of tokens
*/
function claimed(address network, address token, address account) external view returns (uint256);
⋮----
/**
* @notice Distribute rewards by a particular network using a given token by providing a Merkle root.
* @param network address of the network
* @param token address of the token
* @param amount amount of tokens to send to the contract
* @param root Merkle root of the reward distribution
*/
function distributeRewards(address network, address token, uint256 amount, bytes32 root) external;
⋮----
/**
* @notice Claim rewards for a particular network and token by providing a Merkle proof.
* @param recipient address of the rewards' recipient
* @param network address of the network
* @param token address of the token
* @param totalClaimable total amount of the reward tokens for the whole time
* @param proof Merkle proof of the reward distribution
* @return amount amount of tokens claimed
*/
function claimRewards(
````
## File: src/interfaces/defaultOperatorRewards/IDefaultOperatorRewardsFactory.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {IRegistry} from "@symbioticfi/core/src/interfaces/common/IRegistry.sol";
⋮----
interface IDefaultOperatorRewardsFactory is IRegistry {
/**
* @notice Create a default operator rewards contract.
* @return address of the created operator rewards contract
*/
function create() external returns (address);
````
## File: src/interfaces/defaultStakerRewards/IDefaultStakerRewards.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {IStakerRewards} from "../stakerRewards/IStakerRewards.sol";
⋮----
interface IDefaultStakerRewards is IStakerRewards {
⋮----
/**
* @notice Initial parameters needed for a staker rewards contract deployment.
* @param vault address of the vault to get stakers' data from
* @param adminFee admin fee (up to ADMIN_FEE_BASE inclusively)
* @param defaultAdminRoleHolder address of the initial DEFAULT_ADMIN_ROLE holder
* @param adminFeeClaimRoleHolder address of the initial ADMIN_FEE_CLAIM_ROLE holder
* @param adminFeeSetRoleHolder address of the initial ADMIN_FEE_SET_ROLE holder
*/
⋮----
/**
* @notice Structure for a reward distribution.
* @param amount amount of tokens to be distributed (admin fee is excluded)
* @param timestamp time point stakes must taken into account at
*/
⋮----
/**
* @notice Emitted when a vault is initialized.
* @param vault address of the vault
*/
event InitVault(address vault);
⋮----
/**
* @notice Emitted when a reward is claimed.
* @param network network whose rewards are claimed
* @param token address of the token
* @param claimer address of the claimer
* @param firstClaimedRewardIndex first claimed reward index
* @param rewardsClaimed number of rewards claimed
*/
event ClaimRewardsExtra(
⋮----
/**
* @notice Emitted when an admin fee is claimed.
* @param recipient account that received the fee
* @param amount amount of the fee claimed
*/
event ClaimAdminFee(address indexed recipient, uint256 amount);
⋮----
/**
* @notice Emitted when the admin fee is set.
* @param adminFee new admin fee
*/
event SetAdminFee(uint256 adminFee);
⋮----
/**
* @notice Get the maximum admin fee (= 100%).
* @return maximum admin fee
*/
function ADMIN_FEE_BASE() external view returns (uint256);
⋮----
/**
* @notice Get the admin fee claimer's role.
* @return identifier of the admin fee claimer role
*/
function ADMIN_FEE_CLAIM_ROLE() external view returns (bytes32);
⋮----
/**
* @notice Get the admin fee setter's role.
* @return identifier of the admin fee setter role
*/
function ADMIN_FEE_SET_ROLE() external view returns (bytes32);
⋮----
/**
* @notice Get the vault factory's address.
* @return address of the vault factory
*/
function VAULT_FACTORY() external view returns (address);
⋮----
/**
* @notice Get the network middleware service's address.
* @return address of the network middleware service
*/
function NETWORK_MIDDLEWARE_SERVICE() external view returns (address);
⋮----
/**
* @notice Get the vault's address.
* @return address of the vault
*/
function VAULT() external view returns (address);
⋮----
/**
* @notice Get an admin fee.
* @return admin fee
*/
function adminFee() external view returns (uint256);
⋮----
/**
* @notice Get a total number of rewards using a particular token for a given network.
* @param token address of the token
* @param network address of the network
* @return total number of the rewards using the token by the network
*/
function rewardsLength(address token, address network) external view returns (uint256);
⋮----
/**
* @notice Get a particular reward distribution.
* @param token address of the token
* @param network address of the network
* @param rewardIndex index of the reward distribution using the token
* @return amount amount of tokens to be distributed
* @return timestamp time point stakes must taken into account at
*/
function rewards(
⋮----
/**
* @notice Get the first index of the unclaimed rewards using a particular token by a given account.
* @param account address of the account
* @param token address of the token
* @param network address of the network
* @return first index of the unclaimed rewards
*/
function lastUnclaimedReward(address account, address token, address network) external view returns (uint256);
⋮----
/**
* @notice Get a claimable admin fee amount for a particular token.
* @param token address of the token
* @return claimable admin fee
*/
function claimableAdminFee(
⋮----
/**
* @notice Claim an admin fee.
* @param recipient account that will receive the fee
* @param token address of the token
* @dev Only the vault owner can call this function.
*/
function claimAdminFee(address recipient, address token) external;
⋮----
/**
* @notice Set an admin fee.
* @param adminFee admin fee (up to ADMIN_FEE_BASE inclusively)
* @dev Only the ADMIN_FEE_SET_ROLE holder can call this function.
*/
function setAdminFee(
````
## File: src/interfaces/defaultStakerRewards/IDefaultStakerRewardsFactory.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {IDefaultStakerRewards} from "./IDefaultStakerRewards.sol";
⋮----
import {IRegistry} from "@symbioticfi/core/src/interfaces/common/IRegistry.sol";
⋮----
interface IDefaultStakerRewardsFactory is IRegistry {
/**
* @notice Create a default staker rewards contract for a given vault.
* @param params initial parameters needed for a staker rewards contract deployment
* @return address of the created staker rewards contract
*/
function create(
````
## File: src/interfaces/stakerRewards/IStakerRewards.sol
````
// SPDX-License-Identifier: MIT
⋮----
interface IStakerRewards {
/**
* @notice Emitted when a reward is distributed.
* @param network network on behalf of which the reward is distributed
* @param token address of the token
* @param distributeAmount amount of tokens to distribute
* @param adminFeeAmount amount of tokens to keep as an admin fee
* @param timestamp timestamp of the distribution
*/
event DistributeRewards(
⋮----
/**
* @notice Emitted when a reward is claimed.
* @param network network whose rewards are claimed
* @param token address of the token
* @param claimer address of the claimer
* @param amount amount of tokens
* @param recipient address of the tokens' recipient
*/
event ClaimRewards(
⋮----
/**
* @notice Get a version of the staker rewards contract (different versions mean different interfaces).
* @return version of the staker rewards contract
* @dev Must return 2 for this one.
*/
function version() external view returns (uint64);
⋮----
/**
* @notice Get an amount of rewards claimable by a particular account of a given token.
* @param token address of the token
* @param account address of the claimer
* @param data some data to use
* @return amount of claimable tokens
*/
function claimable(address token, address account, bytes calldata data) external view returns (uint256);
⋮----
/**
* @notice Distribute rewards on behalf of a particular network using a given token.
* @param network network on behalf of which the reward to distribute
* @param token address of the token
* @param amount amount of tokens
* @param data some data to use
*/
function distributeRewards(address network, address token, uint256 amount, bytes calldata data) external;
⋮----
/**
* @notice Claim rewards using a given token.
* @param recipient address of the tokens' recipient
* @param token address of the token
* @param data some data to use
*/
function claimRewards(address recipient, address token, bytes calldata data) external;
````
## File: test/defaultOperatorRewards/DefaultOperatorRewards.t.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {Test, console2} from "forge-std/Test.sol";
⋮----
import {NetworkRegistry} from "@symbioticfi/core/src/contracts/NetworkRegistry.sol";
import {NetworkMiddlewareService} from "@symbioticfi/core/src/contracts/service/NetworkMiddlewareService.sol";
import {Token} from "@symbioticfi/core/test/mocks/Token.sol";
⋮----
import {DefaultOperatorRewardsFactory} from
"../../src/contracts/defaultOperatorRewards/DefaultOperatorRewardsFactory.sol";
import {DefaultOperatorRewards} from "../../src/contracts/defaultOperatorRewards/DefaultOperatorRewards.sol";
import {IDefaultOperatorRewards} from "../../src/interfaces/defaultOperatorRewards/IDefaultOperatorRewards.sol";
⋮----
import {FeeOnTransferToken} from "@symbioticfi/core/test/mocks/FeeOnTransferToken.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
⋮----
contract DefaultOperatorRewardsTest is Test {
⋮----
function setUp() public {
⋮----
function test_Create() public {
⋮----
function test_DitributeRewards(
⋮----
function test_DitributeRewardsFeeOnTransfer(
⋮----
function test_DitributeRewardsFeeOnTransferRevertInsufficientTransfer() public {
⋮----
function test_DitributeRewardsRevertNotNetworkMiddleware(
⋮----
function test_ClaimRewards(uint256 amount1, uint256 amount2) public {
⋮----
function test_ClaimRewardsRevertRootNotSet(
⋮----
function test_ClaimRewardsRevertInvalidProof(
⋮----
function test_ClaimRewardsRevertInsufficientTotalClaimable(
⋮----
function test_ClaimRewardsRevertInsufficientBalance(
⋮----
function test_ClaimRewardsCustom() public {
⋮----
function _getOperatorDefaultRewards() internal returns (IDefaultOperatorRewards) {
⋮----
function _registerNetwork(address user, address middleware) internal {
⋮----
function _distributeRewards(address user, address network, address token, uint256 amount, bytes32 root) internal {
⋮----
function _claimRewards(
````
## File: test/defaultOperatorRewards/DefaultOperatorRewardsFactory.t.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {Test, console2} from "forge-std/Test.sol";
⋮----
import {NetworkRegistry} from "@symbioticfi/core/src/contracts/NetworkRegistry.sol";
import {NetworkMiddlewareService} from "@symbioticfi/core/src/contracts/service/NetworkMiddlewareService.sol";
⋮----
import {DefaultOperatorRewardsFactory} from
"../../src/contracts/defaultOperatorRewards/DefaultOperatorRewardsFactory.sol";
import {DefaultOperatorRewards} from "../../src/contracts/defaultOperatorRewards/DefaultOperatorRewards.sol";
import {IDefaultOperatorRewards} from "../../src/interfaces/defaultOperatorRewards/IDefaultOperatorRewards.sol";
⋮----
contract DefaultOperatorRewardsFactoryTest is Test {
⋮----
function setUp() public {
⋮----
function test_Create() public {
````
## File: test/defaultStakerRewards/DefaultStakerRewards.t.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {Test, console2} from "forge-std/Test.sol";
⋮----
import {VaultFactory} from "@symbioticfi/core/src/contracts/VaultFactory.sol";
import {DelegatorFactory} from "@symbioticfi/core/src/contracts/DelegatorFactory.sol";
import {SlasherFactory} from "@symbioticfi/core/src/contracts/SlasherFactory.sol";
import {NetworkRegistry} from "@symbioticfi/core/src/contracts/NetworkRegistry.sol";
import {OperatorRegistry} from "@symbioticfi/core/src/contracts/OperatorRegistry.sol";
import {MetadataService} from "@symbioticfi/core/src/contracts/service/MetadataService.sol";
import {NetworkMiddlewareService} from "@symbioticfi/core/src/contracts/service/NetworkMiddlewareService.sol";
import {OptInService} from "@symbioticfi/core/src/contracts/service/OptInService.sol";
⋮----
import {Vault} from "@symbioticfi/core/src/contracts/vault/Vault.sol";
import {NetworkRestakeDelegator} from "@symbioticfi/core/src/contracts/delegator/NetworkRestakeDelegator.sol";
import {FullRestakeDelegator} from "@symbioticfi/core/src/contracts/delegator/FullRestakeDelegator.sol";
import {OperatorSpecificDelegator} from "@symbioticfi/core/src/contracts/delegator/OperatorSpecificDelegator.sol";
import {OperatorNetworkSpecificDelegator} from
"@symbioticfi/core/src/contracts/delegator/OperatorNetworkSpecificDelegator.sol";
import {Slasher} from "@symbioticfi/core/src/contracts/slasher/Slasher.sol";
import {VetoSlasher} from "@symbioticfi/core/src/contracts/slasher/VetoSlasher.sol";
⋮----
import {Token} from "@symbioticfi/core/test/mocks/Token.sol";
import {VaultConfigurator, IVaultConfigurator} from "@symbioticfi/core/src/contracts/VaultConfigurator.sol";
import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
import {INetworkRestakeDelegator} from "@symbioticfi/core/src/interfaces/delegator/INetworkRestakeDelegator.sol";
import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol";
import {IBaseSlasher} from "@symbioticfi/core/src/interfaces/slasher/IBaseSlasher.sol";
import {ISlasher} from "@symbioticfi/core/src/interfaces/slasher/ISlasher.sol";
⋮----
import {DefaultStakerRewardsFactory} from "../../src/contracts/defaultStakerRewards/DefaultStakerRewardsFactory.sol";
import {IDefaultStakerRewards} from "../../src/interfaces/defaultStakerRewards/IDefaultStakerRewards.sol";
⋮----
import {DefaultStakerRewards} from "../../src/contracts/defaultStakerRewards/DefaultStakerRewards.sol";
⋮----
import {FeeOnTransferToken} from "@symbioticfi/core/test/mocks/FeeOnTransferToken.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {VaultHints} from "@symbioticfi/core/src/contracts/hints/VaultHints.sol";
⋮----
contract DefaultStakerRewardsTest is Test {
⋮----
function setUp() public {
⋮----
function test_Create() public {
⋮----
function test_CreateRevertMissingRoles1(uint256 adminFee, address adminFeeSetRoleHolder) public {
⋮----
function test_CreateRevertMissingRoles2(
⋮----
function test_CreateRevertMissingRoles3(
⋮----
function test_CreateRevertInvalidAdminFee(
⋮----
function test_ReinitRevert() public {
⋮----
function test_DistributeRewards(
⋮----
function test_DistributeRewardsHints(
⋮----
function test_DistributeRewardsRevertNotNetworkMiddleware(uint256 amount, uint256 ditributeAmount) public {
⋮----
function test_DistributeRewardsRevertInvalidRewardTimestamp1(uint256 amount, uint256 ditributeAmount) public {
⋮----
function test_DistributeRewardsRevertInvalidRewardTimestamp2(uint256 amount, uint256 ditributeAmount) public {
⋮----
function test_DistributeRewardsRevertHighAdminFee(
⋮----
function test_DistributeRewardsRevertInsufficientReward(
⋮----
function test_ClaimRewards(uint256 amount, uint256 ditributeAmount1, uint256 ditributeAmount2) public {
⋮----
function test_ClaimRewardsBoth(uint256 amount, uint256 ditributeAmount1, uint256 ditributeAmount2) public {
⋮----
function test_ClaimRewardsManyWithoutHints(uint256 amount, uint256 ditributeAmount) public {
⋮----
function test_ClaimRewardsManyWithHints(uint256 amount, uint256 ditributeAmount) public {
⋮----
function test_ClaimRewardsRevertInvalidRecipient(uint256 amount, uint256 ditributeAmount) public {
⋮----
function test_ClaimRewardsRevertNoRewardsToClaim1(uint256 amount, uint256 ditributeAmount) public {
⋮----
function test_ClaimRewardsRevertNoRewardsToClaim2(uint256 amount, uint256 ditributeAmount) public {
⋮----
function test_ClaimRewardsRevertInvalidHintsLength(uint256 amount, uint256 ditributeAmount) public {
⋮----
function test_ClaimAdminFee(uint256 amount, uint256 ditributeAmount, uint256 adminFee) public {
⋮----
function test_ClaimAdminFeeRevertInsufficientAdminFee(
⋮----
function test_SetAdminFee(
⋮----
function test_SetAdminFeeRevertAlreadySet(
⋮----
function _getStakerDefaultRewards() internal returns (DefaultStakerRewards) {
⋮----
function _registerOperator(
⋮----
function _registerNetwork(address user, address middleware) internal {
⋮----
function _deposit(address user, uint256 amount) internal returns (uint256 depositedAmount, uint256 mintedShares) {
⋮----
function _withdraw(address user, uint256 amount) internal returns (uint256 burnedShares, uint256 mintedShares) {
⋮----
function _grantAdminFeeSetRole(address user, address account) internal {
⋮----
function _distributeRewards(
⋮----
function _claimRewards(
⋮----
function _setAdminFee(address user, uint256 adminFee) internal {
⋮----
function _claimAdminFee(address user, address token) internal {
````
## File: test/defaultStakerRewards/DefaultStakerRewardsFactory.t.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {Test, console2} from "forge-std/Test.sol";
⋮----
import {VaultFactory} from "@symbioticfi/core/src/contracts/VaultFactory.sol";
import {DelegatorFactory} from "@symbioticfi/core/src/contracts/DelegatorFactory.sol";
import {SlasherFactory} from "@symbioticfi/core/src/contracts/SlasherFactory.sol";
import {NetworkRegistry} from "@symbioticfi/core/src/contracts/NetworkRegistry.sol";
import {OperatorRegistry} from "@symbioticfi/core/src/contracts/OperatorRegistry.sol";
import {MetadataService} from "@symbioticfi/core/src/contracts/service/MetadataService.sol";
import {NetworkMiddlewareService} from "@symbioticfi/core/src/contracts/service/NetworkMiddlewareService.sol";
import {OptInService} from "@symbioticfi/core/src/contracts/service/OptInService.sol";
⋮----
import {Vault} from "@symbioticfi/core/src/contracts/vault/Vault.sol";
import {NetworkRestakeDelegator} from "@symbioticfi/core/src/contracts/delegator/NetworkRestakeDelegator.sol";
import {FullRestakeDelegator} from "@symbioticfi/core/src/contracts/delegator/FullRestakeDelegator.sol";
import {OperatorSpecificDelegator} from "@symbioticfi/core/src/contracts/delegator/OperatorSpecificDelegator.sol";
import {OperatorNetworkSpecificDelegator} from
"@symbioticfi/core/src/contracts/delegator/OperatorNetworkSpecificDelegator.sol";
import {Slasher} from "@symbioticfi/core/src/contracts/slasher/Slasher.sol";
import {VetoSlasher} from "@symbioticfi/core/src/contracts/slasher/VetoSlasher.sol";
⋮----
import {Token} from "@symbioticfi/core/test/mocks/Token.sol";
import {VaultConfigurator, IVaultConfigurator} from "@symbioticfi/core/src/contracts/VaultConfigurator.sol";
import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
import {INetworkRestakeDelegator} from "@symbioticfi/core/src/interfaces/delegator/INetworkRestakeDelegator.sol";
import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol";
import {IBaseSlasher} from "@symbioticfi/core/src/interfaces/slasher/IBaseSlasher.sol";
import {ISlasher} from "@symbioticfi/core/src/interfaces/slasher/ISlasher.sol";
⋮----
import {DefaultStakerRewardsFactory} from "../../src/contracts/defaultStakerRewards/DefaultStakerRewardsFactory.sol";
import {DefaultStakerRewards} from "../../src/contracts/defaultStakerRewards/DefaultStakerRewards.sol";
import {IDefaultStakerRewards} from "../../src/interfaces/defaultStakerRewards/IDefaultStakerRewards.sol";
⋮----
contract DefaultStakerRewardsFactoryTest is Test {
⋮----
function setUp() public {
⋮----
function test_Create(
⋮----
function test_CreateRevertNotVault() public {
⋮----
function _registerOperator(
⋮----
function _registerNetwork(address user, address middleware) internal {
⋮----
function _deposit(address user, uint256 amount) internal returns (uint256 depositedAmount, uint256 mintedShares) {
⋮----
function _withdraw(address user, uint256 amount) internal returns (uint256 burnedShares, uint256 mintedShares) {
````
## File: test/integration/SymbioticRewardsBindings.sol
````
// SPDX-License-Identifier: MIT
⋮----
import "./SymbioticRewardsImports.sol";
⋮----
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
⋮----
import {Test} from "forge-std/Test.sol";
⋮----
contract SymbioticRewardsBindings is Test {
⋮----
function _createDefaultStakerRewards_SymbioticRewards(
⋮----
function _createDefaultOperatorRewards_SymbioticRewards(
⋮----
function _distributeRewards_SymbioticRewards(
⋮----
function _claimRewards_SymbioticRewards(
⋮----
function _claimAdminFee_SymbioticRewards(
⋮----
function _setAdminFee_SymbioticRewards(
⋮----
function _grantRole_SymbioticRewards(address who, address where, bytes32 role, address account) internal virtual {
⋮----
function _grantRoleDefaultAdmin_SymbioticRewards(address who, address where, address account) internal virtual {
⋮----
function _grantRoleAdminFeeClaim_SymbioticRewards(address who, address where, address account) internal virtual {
⋮----
function _grantRoleAdminFeeSet_SymbioticRewards(address who, address where, address account) internal virtual {
````
## File: test/integration/SymbioticRewardsConstants.sol
````
// SPDX-License-Identifier: MIT
⋮----
import "./SymbioticRewardsImports.sol";
⋮----
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
⋮----
function defaultStakerRewardsFactory() internal view returns (ISymbioticDefaultStakerRewardsFactory) {
⋮----
// mainnet
⋮----
// holesky
⋮----
// sepolia
⋮----
// hoodi
⋮----
function defaultOperatorRewardsFactory() internal view returns (ISymbioticDefaultOperatorRewardsFactory) {
````
## File: test/integration/SymbioticRewardsImports.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {
IDefaultStakerRewards as ISymbioticDefaultStakerRewards,
IStakerRewards as ISymbioticStakerRewards
} from "../../src/interfaces/defaultStakerRewards/IDefaultStakerRewards.sol";
import {IDefaultStakerRewardsFactory as ISymbioticDefaultStakerRewardsFactory} from
"../../src/interfaces/defaultStakerRewards/IDefaultStakerRewardsFactory.sol";
import {IDefaultOperatorRewards as ISymbioticDefaultOperatorRewards} from
"../../src/interfaces/defaultOperatorRewards/IDefaultOperatorRewards.sol";
import {IDefaultOperatorRewardsFactory as ISymbioticDefaultOperatorRewardsFactory} from
"../../src/interfaces/defaultOperatorRewards/IDefaultOperatorRewardsFactory.sol";
⋮----
interface SymbioticRewardsImports {}
````
## File: test/integration/SymbioticRewardsImportsContracts.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {DefaultStakerRewards as SymbioticDefaultStakerRewards} from
"../../src/contracts/defaultStakerRewards/DefaultStakerRewards.sol";
import {DefaultStakerRewardsFactory as SymbioticDefaultStakerRewardsFactory} from
"../../src/contracts/defaultStakerRewards/DefaultStakerRewardsFactory.sol";
import {DefaultOperatorRewards as SymbioticDefaultOperatorRewards} from
"../../src/contracts/defaultOperatorRewards/DefaultOperatorRewards.sol";
import {DefaultOperatorRewardsFactory as SymbioticDefaultOperatorRewardsFactory} from
"../../src/contracts/defaultOperatorRewards/DefaultOperatorRewardsFactory.sol";
⋮----
interface SymbioticRewardsImportsContracts {}
````
## File: test/integration/SymbioticRewardsInit.sol
````
// SPDX-License-Identifier: MIT
⋮----
import "@symbioticfi/core/test/integration/SymbioticCoreInit.sol";
⋮----
import "./SymbioticRewardsImports.sol";
⋮----
import {SymbioticRewardsConstants} from "./SymbioticRewardsConstants.sol";
import {SymbioticRewardsBindings} from "./SymbioticRewardsBindings.sol";
⋮----
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
⋮----
contract SymbioticRewardsInit is SymbioticCoreInit, SymbioticRewardsBindings {
⋮----
// General config
⋮----
// Middleware-related config
⋮----
// DefaultStakerRewards-related config
⋮----
function setUp() public virtual override {
⋮----
// ------------------------------------------------------------ GENERAL HELPERS ------------------------------------------------------------ //
⋮----
function _initRewards_SymbioticRewards() internal virtual {
⋮----
function _initRewards_SymbioticRewards(
⋮----
// ------------------------------------------------------------ REWARDS-RELATED HELPERS ------------------------------------------------------------ //
⋮----
function _getDefaultStakerRewards_SymbioticRewards(
⋮----
function _getDefaultStakerRewardsRandom_SymbioticRewards(
⋮----
function _getDefaultOperatorRewards_SymbioticRewards() internal virtual returns (address) {
⋮----
function _getDefaultOperatorRewardsRandom_SymbioticRewards() internal virtual returns (address) {
⋮----
function _fundMiddleware_SymbioticRewards(address token, address middleware) internal virtual {
deal(token, middleware, _normalizeForToken_Symbiotic(SYMBIOTIC_REWARDS_TOKENS_TO_SET_TIMES_1e18, token), true); // should cover most cases
⋮----
// ------------------------------------------------------------ STAKER-RELATED HELPERS ------------------------------------------------------------ //
⋮----
function _stakerClaim_SymbioticRewards(
⋮----
function _stakerClaimWeak_SymbioticRewards(
⋮----
// ------------------------------------------------------------ OPERATOR-RELATED HELPERS ------------------------------------------------------------ //
⋮----
function _operatorClaim_SymbioticRewards(
⋮----
// ------------------------------------------------------------ CURATOR-RELATED HELPERS ------------------------------------------------------------ //
⋮----
function _curatorClaim_SymbioticRewards(
⋮----
function _curatorClaimWeak_SymbioticRewards(
⋮----
function _curatorSetAdminFee_SymbioticRewards(
````
## File: test/integration/SymbioticRewardsIntegration.sol
````
// SPDX-License-Identifier: MIT
⋮----
import "@symbioticfi/core/test/integration/SymbioticCoreIntegration.sol";
⋮----
import "./SymbioticRewardsInit.sol";
⋮----
contract SymbioticRewardsIntegration is SymbioticRewardsInit, SymbioticCoreIntegration {
⋮----
uint256 public SYMBIOTIC_REWARDS_DISTRIBUTE_STAKER_REWARDS_CHANCE = 4; // lower -> higher probability
⋮----
function setUp() public virtual override(SymbioticRewardsInit, SymbioticCoreIntegration) {
⋮----
function _loadExistingEntities_SymbioticRewards() internal virtual {
⋮----
function _loadExistingDefaultStakerRewards_SymbioticRewards() internal virtual {
⋮----
function _loadExistingDefaultOperatorRewards_SymbioticRewards() internal virtual {
⋮----
function _addPossibleTokens_SymbioticRewards() internal virtual {
⋮----
function _addExistingEntities_SymbioticRewards() internal virtual {
⋮----
function _addExistingDefaultStakerRewards_SymbioticRewards() internal virtual {
⋮----
function _addExistingDefaultOperatorRewards_SymbioticRewards() internal virtual {
⋮----
function _createEnvironment_SymbioticRewards() internal virtual {
⋮----
function _createParties_SymbioticRewards() internal virtual {
⋮----
function _createDefaultStakerRewards_SymbioticRewards() internal virtual {
⋮----
function _createDefaultOperatorRewards_SymbioticRewards() internal virtual {
⋮----
function _distributeRewards_SymbioticRewards() internal virtual {
⋮----
function _distributeStakerRewardsOnBehalfOfNetworkRandom_SymbioticRewards(
⋮----
function _distributeStakerRewards_SymbioticRewards() internal virtual {
⋮----
function _distributeOperatorRewards_SymbioticRewards() internal virtual {}
⋮----
function _claimRewards_SymbioticRewards() internal virtual {
⋮----
function _claimStakerRewards_SymbioticRewards() internal virtual {
⋮----
function _claimOperatorRewards_SymbioticRewards() internal virtual {}
````
## File: test/integration/SymbioticRewardsIntegrationExample.sol
````
// SPDX-License-Identifier: MIT
⋮----
import "./SymbioticRewardsIntegration.sol";
⋮----
import {console2} from "forge-std/Test.sol";
⋮----
contract SymbioticRewardsIntegrationExample is SymbioticRewardsIntegration {
⋮----
uint256 public SELECT_OPERATOR_CHANCE = 1; // lower -> higher probability
⋮----
function setUp() public override {
⋮----
// vm.selectFork(vm.createFork(vm.rpcUrl("holesky")));
// SYMBIOTIC_INIT_BLOCK = 2_727_202;
// SYMBIOTIC_CORE_USE_EXISTING_DEPLOYMENT = true;
// SYMBIOTIC_REWARDS_USE_EXISTING_DEPLOYMENT = true;
⋮----
function test_NetworkDistributeRewardsForStake() public {
````
## File: test/mocks/SimpleStakerRewards.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {IStakerRewards} from "../../src/interfaces/stakerRewards/IStakerRewards.sol";
⋮----
contract SimpleStakerRewards is IStakerRewards {
/**
* @inheritdoc IStakerRewards
*/
⋮----
function claimable(address token, address account, bytes memory data) external view override returns (uint256) {}
⋮----
function distributeRewards(address network, address token, uint256 amount, bytes memory data) external override {
⋮----
function claimRewards(address recipient, address token, bytes memory data) external override {}
````
## File: test/stakerRewards/StakerRewards.t.sol
````
// SPDX-License-Identifier: MIT
⋮----
import {Test, console2} from "forge-std/Test.sol";
⋮----
import {NetworkRegistry} from "@symbioticfi/core/src/contracts/NetworkRegistry.sol";
⋮----
import {SimpleStakerRewards} from "test/mocks/SimpleStakerRewards.sol";
⋮----
contract StakerRewardsTest is Test {
⋮----
function setUp() public {
⋮----
function test_Create() public {
````
## File: .env.example
````
ETH_RPC_URL=
ETH_RPC_URL_HOLESKY=
ETHERSCAN_API_KEY=
````
## File: .gitignore
````
# Compiler files
cache/
out/**/
!out/DefaultStakerRewardsFactory.sol/
!out/DefaultStakerRewards.sol/
!out/DefaultOperatorRewardsFactory.sol/
!out/DefaultOperatorRewards.sol/
# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/*/11155111/
/broadcast/**/dry-run/
# Docs
docs/
# Dotenv file
.env
# Other files
.gas-snapshot
lcov.info
node_modules
package-lock.json
````
## File: .gitmodules
````
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
[submodule "lib/core"]
path = lib/core
url = https://github.com/symbioticfi/core
````
## File: foundry.toml
````toml
[profile.default]
evm_version = "cancun"
solc = "0.8.25"
optimizer = true
optimizer_runs = 200
via_ir = true
src = "src"
out = "out"
libs = ["lib"]
fs_permissions = [{ access = "read-write", path = "./"}]
gas_reports = ["*"]
gas_limit = "18446744073709551615"
[rpc_endpoints]
mainnet = "${ETH_RPC_URL}"
holesky = "${ETH_RPC_URL_HOLESKY}"
[fmt]
bracket_spacing = false
int_types = "long"
line_length = 120
multiline_func_header = "params_first"
number_underscore = "thousands"
quote_style = "double"
tab_width = 4
[fuzz]
runs = 4096
max_test_rejects = 262144
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
````
## File: LICENSE
````
MIT License
Copyright (c) 2024 Symbiotic
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.
````
## File: package.json
````json
{
"name": "@symbioticfi/rewards",
"version": "2.0.0",
"description": "Symbiotic Rewards repository contains a Symbiotic Staker Rewards interface providing a standardized way for rewards implementations by different parties, and default implementations of Staker and Operator Rewards.",
"homepage": "https://symbiotic.fi/",
"bugs": "https://github.com/symbioticfi/rewards/issues",
"license": "MIT",
"author": "Symbiotic Team",
"files": [
"src/**/*",
"test/mocks/**/*",
"test/integration/**/*",
"out/**/*.json",
"cli/**/*"
],
"repository": {
"type": "git",
"url": "https://github.com/symbioticfi/rewards.git"
},
"keywords": [
"solidity",
"ethereum",
"smart",
"contracts",
"security"
],
"dependencies": {
"@openzeppelin/contracts": "5.0.2",
"@openzeppelin/contracts-upgradeable": "5.0.2",
"@openzeppelin/merkle-tree": "1.0.7",
"@symbioticfi/core": "1.0.1"
}
}
````
## File: README.md
````markdown
**[Symbiotic Protocol](https://symbiotic.fi) is an extremely flexible and permissionless shared security system.**
This repository contains a Symbiotic Staker Rewards interface, its default implementation, and a default Operator Rewards.
[](https://deepwiki.com/symbioticfi/rewards)
## Documentation
Can be found [here](https://docs.symbiotic.fi/core-modules/rewards).
## Technical Documentation
Can be found [here](./specs).
## Security
Security audits can be found [here](./audits).
## Usage
### Env
Create `.env` file using a template:
```
ETH_RPC_URL=
ETH_RPC_URL_HOLESKY=
ETHERSCAN_API_KEY=
```
\* ETH_RPC_URL is optional.
\* ETH_RPC_URL_HOLESKY is optional.
\* ETHERSCAN_API_KEY is optional.
### Build
```shell
forge build
```
### Test
```shell
forge test
```
### Format
```shell
forge fmt
```
### Gas Snapshots
```shell
forge snapshot
```
````
## File: remappings.txt
````
forge-std/=lib/forge-std/src/
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
@symbioticfi/core/=lib/core/
````