BuildersV4

Overview

The BuildersV4 protocol is an on-chain coordination layer designed for distributing MOR token rewards to subnet builders in a scalable and fair manner. It allows anyone to create a Subnet with specific staking parameters and enables users to stake MOR tokens under a particular Subnet. Rewards are calculated globally and distributed to Subnets based on their effective contribution, determined through staked amounts.

Each Subnet is identified by a unique ID derived from its name and the chain ID, allowing for identical Subnet names across multiple blockchains (v1 and v2 Subnets will have previous ID). Subnet builders (admins) and subnet claim admin are the only ones authorized to claim MOR rewards for their Subnets, while users’ rewards are handled off-chain by the builders. The reward calculation is powered by an external emission curve (RewardPool) and uses a rate model to determine distribution over time.

Unlike previous versions, v4 removes virtual deposits and multipliers, simplifying staking logic and removing dependency on the claim lock end. Additionally, the system introduces a networkShare parameter to control how much of the RewardPool output flows to Builders overall.

Key Features

  1. Subnet creation and configuration: anyone can create a pool with custom parameters; the builder (admin) is assigned at creation.

  2. User staking: users stake tokens into any subnet.

  3. Subnet-controlled reward claiming: only the Subnet admin or Subnet claim admin can claim the MOR rewards, enabling flexible distribution outside the contract scope.

  4. Reward calculation: uses a dynamic rate updated with each user action and proportional to the protocol-wide deposits.

  5. Fee system: integrated with FeeConfig for applying customizable claim and creation fees.

  6. Upgradeable and permissioned: built with UUPS and Ownable patterns for controlled upgrades and ownership-based access.

Storage

feeConfig

Address of the FeeConfig contract used to calculate protocol fees.

address public feeConfig;

buildersTreasury

Address of the BuildersTreasury contract which stores and distributes reward tokens.

address public buildersTreasury;

depositToken

Address of the MOR token users deposit into Subnets.

address public depositToken;

unusedStorage1_V4Update (old editPoolDeadline)

The time window (in seconds) before the pool starts, during which pool parameters can still be edited by the pool admin. Deprecated in v4.

uint128 public editPoolDeadline;

minimalWithdrawLockPeriod

Minimum duration (in seconds) after a deposit during which withdrawals are locked.

uint256 public minimalWithdrawLockPeriod;

allSubnetsData (old totalPoolData)

Aggregated data for all Subnets pools in the system.

AllSubnetsData public allSubnetsData;

struct TotalPoolData {
  uint256 distributedRewards;
  uint256 rate;
  uint256 totalDeposited;
  uint256 totalVirtualDeposited;
}
Name
Description

distributedRewards

Total amount of rewards distributed across all pools. Wei.

rate

The current reward rate

totalDeposited

Total tokens deposited without applying any multiplier. Wei.

totalVirtualDeposited

Total tokens deposited with multiplier (used in reward calculations). Wei

subnets (old builderPools)

Mapping of subnetID to Subnet configuration.

mapping(bytes32 subnetId => Subnet) public subnets;

struct Subnet {
  string name;
  address admin;
  uint128 unusedStorage1_V4Update;
  uint128 withdrawLockPeriodAfterDeposit;
  uint128 unusedStorage2_V4Update;
  uint256 minimalDeposit;
  address claimAdmin;
}
Name
Description

name

The name of the Subnet.

admin

The address of the Subnet administrator.

unusedStorage1_V4Update (old poolStart)

Timestamp when the pool becomes active. Deprecated in v4.

withdrawLockPeriodAfterDeposit

Time period after deposit during which withdrawal is locked. Seconds.

unusedStorage2_V4Update (old claimLockEnd)

Timestamp after which rewards can be claimed by the Subnets admin. Seconds. Deprecated in v4.

minimalDeposit

Minimum amount a user must deposit to participate. Wei.

claimAdmin

This address can claim the Subnet rewards against admin.

subnetsData (old buildersPoolData)

Mapping of pool ID to the current dynamic pool state.

mapping(bytes32 subnetId => SubnetData) public subnetsData;

struct SubnetData {
  uint128 unusedStorage1_V4Update;
  uint256 deposited;
  uint256 unusedStorage2_V4Update;
  uint256 rate;
  uint256 pendingRewards;
}
Name
Description

unusedStorage1_V4Update (old lastDeposit)

Timestamp in seconds of the last deposit into the pool. Deprecated in v4.

deposited

Total amount deposited MOR. Wei.

unusedStorage2_V4Update (old virtualDeposited)

Total amount deposited with multipliers applied. Wei. Deprecated in v4.

rate

Current reward rate for the Subnet

pendingRewards

Number of rewards accrued to the Subnet. Is not the final reward at a given time. Used for internal calculations. Wei.

usersData

Mapping of user address and pool ID to their user-specific data.

mapping(address => mapping(bytes32 => UserData)) public usersData;

struct UserData {
  uint128 lastDeposit;
  uint128 unusedStorage1_V4Update;
  uint256 deposited;
  uint256 unusedStorage2_V4Update;
}
Name
Description

lastDeposit

Timestamp in seconds of the user's last deposit.

unusedStorage1_V4Update ( old claimLockStart)

Timestamp when the user activated the reward lock. Seconds. Deprecated in v4.

deposited

Total tokens the user deposited (raw amount). Wei.

unusedStorage2_V4Update (old virtualDeposited)

Tokens deposited by user with multiplier applied. Wei. Deprecated in v4.

rewardPool

The address of the RewardPool contract address.

address public rewardPool;

subnetCreationFeeAmount

This value in wei is taken from the tx sender when the Subnet created.

uint256 public subnetCreationFeeAmount;

networkShare

The networkShare is the share of the network rewards that will be distributed to Subnets, e.g. 100% = 10^25. If global reward curve return X amount of rewards, then all Subnets will * receive X * networkShare / 10^25

uint256 public networkShare;

networkShareOwner

This address can change the networkShare value.

address public networkShareOwner;

subnetsMetadata

Contain the metadata about Subnets

mapping(bytes32 subnetId => SubnetMetadata) public subnetsMetadata;

struct SubnetMetadata {
  string slug;
  string description;
  string website;
  string image;
}
Name
Description

slug

The Subnet slug string.

description

The Subnet description string.

website

The Subnet website string.

image

The Subnet image link.

allSubnetsDataV4

The variable that stores all Subnets data, addition for allSubnetsData.

AllSubnetsDataV4 public allSubnetsDataV4;

struct AllSubnetsDataV4 {
  uint256 distributedRewards;
  uint256 undistributedRewards;
  uint256 claimedRewards;
  uint128 lastUpdate;
}
Name
Description

distributedRewards

The amount of rewards that calculated and virtually distributed between Subnets. Wei.

undistributedRewards

The amount of rewards that calculated and stored for the contract owner. Wei

claimedRewards

The total amount of claimed rewards. Wei.

lastUpdate

The AllSubnetsData.rate last update timestamp.

Fee oprations

The lables for fee operations, uses for fee and treasury receiving in the FeeConfig contract. FEE_SUBNET_CREATE uses only treasury address from the FeeConfig contract.

// bytes32 private constant FEE_WITHDRAW_OPERATION = "withdraw";
bytes32 public constant FEE_CLAIM_OPERATION = "claim";
bytes32 public constant FEE_SUBNET_CREATE = "buildersV4.fee.subnet.create";

Write functions for the contract owner

BuildersV4_init

Initializes the contract with essential parameters and configuration contracts. This function must be called once after deployment.

function BuildersV4_init(
  address depositToken_,
  address feeConfig_,
  address treasury_,
  address rewardPool_,
  address networkShareOwner_,
  uint256 minimalWithdrawLockPeriod_
) external initializer
Name
Description

depositToken_

Address of the token that users will deposit in builder pools. MOR token.

feeConfig_

Address of the FeeConfig contract used to calculate protocol fees.

treasury_

Address of the BuildersTreasuryV2 contract used to store and distribute rewards.

rewardPool_

Address of the RewardPool contract.

networkShareOwner_

The address wich can edit the networkShare.

minimalWithdrawLockPeriod_

Minimum lock duration after deposit in seconds before users can withdraw staked MOR.

setFeeConfig

Sets the address of the FeeConfig contract.

function setFeeConfig(address feeConfig_) public onlyOwner
Name
Description

feeConfig_

Address of a contract that implements IFeeConfig

setBuildersTreasury

Sets the address of the BuildersTreasuryV2 contract.

function setBuildersTreasury(address buildersTreasury_) public onlyOwner
Name
Description

buildersTreasury_

Address of a contract that implements IBuildersTreasury

setMinimalWithdrawLockPeriod

Sets the minimum lock period that must pass before a user can withdraw MOR after depositing.

function setMinimalWithdrawLockPeriod(
  uint256 minimalWithdrawLockPeriod_
) public onlyOwner
Name
Description

minimalWithdrawLockPeriod_

Minimum lock period (in seconds) after deposit before withdrawal

setRewardPool

Sets the address of the RewardPool contract.

function setRewardPool(address rewardPool_) external onlyOwner;
Name
Description

rewardPool_

Address of the RewardPool contract to set.

setNetworkShareOwner

Sets the address that is authorized to change the networkShare value.

function setNetworkShareOwner(address networkShareOwner_) external onlyOwner;
Name
Description

networkShareOwner_

Address that will be allowed to update the networkShare value.

setNetworkShare

Sets the percentage of rewards allocated to all Subnets.

function setNetworkShare(uint256 networkShare_) external onlyOwner;
Name
Description

networkShare_

Percentage value where 100% = 1e25. Only owner or networkShareOwner.

setSubnetCreationFeeAmount

Sets the fee amount (in MOR tokens) required to create a Subnet.

function setSubnetCreationFeeAmount(
  uint256 subnetCreationFeeAmount_
) external onlyOwner;
Name
Description

subnetCreationFeeAmount_

MOR amount in wei.

Write functions

createBuilderPool

Creates a new Subnet with specific parameters. If subnetCreationFee set, it will be charged from the tx caller.

function createSubnet(
  Subnet calldata subnet_,
  SubnetMetadata calldata metadata_
) external;
Name
Description

subnet_

The Subnet struct.

metadata_

The SubnetMetadata struct.

editSubnet

Edits an existing Subnet's main configuration. The caller should be the Subnet admin.

function editSubnet(
  bytes32 subnetId_,
  Subnet calldata newSubnet_
) external onlySubnetOwner(subnetId_)
Name
Description

subnetId_

ID of the Subnet.

newSubnet_

The Subnet struct.

editSubnet

Edits an existing Subnet's metadata configuration. The caller should be the Subnet admin.

function editSubnetMetadata(
  bytes32 subnetId_,
  SubnetMetadata calldata metadata_
) public onlySubnetOwner(subnetId_)
Name
Description

subnetId_

ID of the Subnet.

metadata_

The SubnetMetadata struct.

deposit

Allows a user to deposit tokens into a specific Subnet.

function deposit(bytes32 subnetId_, uint256 amount_) external
Name
Description

subnetId_

ID of the Subnet.

amount_

Amount of tokens to deposit. Wei.

withdraw

Allows a user to withdraws tokens from a specific Subnet.

function withdraw(bytes32 subnetId_, uint256 amount_) external
Name
Description

subnetId_

ID of the Subnet.

amount_

Amount of tokens to withdraw. Wei.

claim

Allows the Subnet admin or Subnet claim admin to claim the accumulated MOR rewards.

function claim(bytes32 subnetId_, address receiver_) external
Name
Description

subnetId_

ID of the Subnet.

receiver_

Address to receive the claimed MOR rewards

Read functions

getCurrentSubnetsRewards

The function calculates the potential claim reward amount for ALL Subnets at the current moment in time, based on the current contract parameters. It estimates the unclaimed rewards that would be distributed if a claim were made now.

function getCurrentSubnetsRewards() external view returns (uint256)

getCurrentSubnetRewards

The function calculates the potential claim reward amount for the SPECIFIC Subnet at the current moment in time, based on the current contract parameters. It estimates the unclaimed rewards that would be distributed if a claim were made now.

function getCurrentSubnetRewards(
  bytes32 subnetId_
) external view returns (uint256)
Name
Description

subnetId_

ID of the Subnet.

getSubnetId

Computes the unique ID of a Subnet based on its name and chain ID.

function getSubnetId(string memory subnetName_) public view returns (bytes32)
Name
Description

subnetName_

Name of the Subnet

getSubnetIdOld

Computes the unique ID of a Subnet based on its name. Subnets created in the v1 and v2 used this ID.

function getSubnetIdOld(string memory subnetName_) public view returns (bytes32)
Name
Description

subnetName_

Name of the Subnet

supportsInterface

Used for interface detection (ERC165). Returns true if the contract supports a specific interfaceId_. Supports IBuildersV2, IERC165.

function supportsInterface(bytes4 interfaceId_) external pure returns (bool)

version

Returns the current version of the contract.

function version() external pure returns (uint256

Last updated

Was this helpful?