Auto Allocator that assigns data cap to each client and semi-random storage providers (SPs) in a round-robin fashion on the Filecoin network.
- Overview
- Architecture
- Mechanism
- Demo
- Usage
- Deployment
Round Robin Allocator is a smart contract system for the Filecoin network that enables fair and efficient allocation of data cap across multiple storage providers. It automates the process of granting data cap and selects storage providers in a round-robin fashion, ensuring fair distribution of storage deals while maintaining security through a collateral system.
The contract automatically handles the complex process of encoding allocation requests in CBOR format and interacting with Filecoin's built-in actors (VerifReg, DataCap) to manage allocations and claims.
The project utilizes the Diamond pattern (EIP-2535) for upgradeability and modularity, with the following components:
- Diamond Contract: Central contract that delegates calls to various facets
- Storage Library: Central storage for all facets, using namespaced storage patterns
- Facets: Specialized modules for different functionalities
- AllocateFacet: Handles allocation of data cap to storage providers
- RetrieveCollateralFacet: Manages collateral retrieval after successful claims
- StorageEntityManagerFacet: Manages storage entities and their providers
- AllocatorManagerFacet: Manages allocators who can create storage entities
- OwnerFacet: Administrative functions for the contract owner
- OwnershipFacet: Ownership management (transfer, accept)
- FilecoinFacet: Handles Filecoin-specific methods (FRC-46 token receiver)
- ViewFacet: Read-only functions to view contract state
- AllocationRequestCbor: CBOR encoding for allocation requests
- AllocationResponseCbor: CBOR decoding for allocation responses
- StorageEntityPicker: Logic for fair selection of storage providers
- FilecoinEpochCalculator: Utility for Filecoin-specific time calculations
- Storage: Central storage structure and access methods
The Round Robin Allocator works through a sequence of operations:
-
Setup Phase:
- Contract owner or allocators register storage entities with their storage providers
- Each storage entity can manage multiple storage providers
- Storage entities can be activated or deactivated
-
Allocation Phase:
- Clients submit allocation requests with data CIDs and sizes
- Client provides collateral for each CID × replicas
- Contract selects storage providers in a semi-random round-robin fashion
- Data cap is transferred to the VerifReg actor with provider-specific CBOR payloads
- All allocations from a single request are bundled into an "Allocation Package"
- The Allocation Package tracks allocation IDs per storage provider and serves as the unit for claiming collateral
-
Claim Phase:
- Storage providers claim the allocations by storing the data
- Claims are verified through the VerifReg actor
- Once all providers have claimed their allocations, the client can retrieve their collateral
-
Collateral Retrieval:
- Client calls
retrieveCollateralto recover their deposit - Contract verifies all claims are completed before releasing funds
- In emergency cases, the owner can force-release collateral
- Client calls
The semi-random selection works by starting from a random index in the storage entities list and picking providers in a round-robin fashion, ensuring that:
- Each CID is only replicated once per provider
- Allocations are distributed fairly across all active providers
You can view and try out the Round Robin Allocator on the Filecoin Calibration network using the Louper explorer.
Direct link for the deployed contract: https://louper.dev/diamond/0x71B65138aceBe6c010C366586B58BB01D5D97f4E?network=filecoinCalibration
Demo Staging Frontend: https://round-robin.staging.allocator.tech
Video of the demo Frontend: YouTube Video
- Prepare CAR files
- Prepare input data for allocation: Prepare
CommCIDandData Sizefor each file. - Call
allocatecontract function - Provide files to storage providers: SPs will store the data and claim their allocations.
- Retrieve collateral: After all claims are verified, call
retrieveCollateralto get your collateral back.
Use CAR file preparation tools (e.g. Singularity, lotus, etc.) to create CAR files from your data.
To transfer data cap, for each file, you need to prepare the following:
- CommCID: The CommP content identifier for the data you want to allocate, in HEX format
- Example:
0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
- Example:
- Data Size: The padded size of the data in bytes
- Example:
2048
- Example:
To prepare HEX format of CommP, you can use the following docker image: mmach/car-processor:0.1.0
This image will require directory with your CAR files to be mounted as a volume.
Output will be created in form of CSV file in the same directory named output.csv with the following columns: CommPCID_InHex,DataSizeInBytes without header.
Example docker image usage:
docker run --rm -it -v $(pwd)/cars:/data mmach/car-processor:0.1.0To interact with the Round Robin AutoAllocator contract, you need an Ethereum wallet with sufficient balance to cover gas fees and collateral. You can use tools like MetaMask (frontend) or Foundry (CLI) to manage your wallet.
The Easiest way to get current collateral value per CID is to use contract Frontend app where this information is displayed.
Alternatively, you can use the Louper explorer app (or CLI) to invoke the getAppConfig function of the contract (in read tab, choose ViewFacet here). This function returns current contract config including the collateral value per CID.
To create allocation package and transfer Data Cap, you can either:
- Upload the CSV directly in the frontend demo application (refer to the video in demo section)
- Use the CSV content and invoke
allocatefunction through command line e.g. using Foundrycastcommand to interact with the contract
- Open the Round Robin Allocator Frontend.
- Connect your Ethereum wallet (e.g. MetaMask).
- Upload CSV file with your prepared data.
- Specify the number of replicas you want to allocate.
- Click "Allocate" to send the transaction to the contract.
- Format CSV content into the required format for the
allocatefunction.- params: replicaAmount, [(CommPCIDinHex, DataPaddedSize)]
- Prepare private key and RPC URL in your environment variables or directly in the command.
- Calculate collateral value based on the number of CIDs and replicas.
- Use the following command to send the allocation request:
cast send --json --value 0.1ether --gas-limit 9000000000 --private-key $(PRIVATE_KEY) --rpc-url $(RPC_URL) $(DIAMOND_CONTRACT_ADDRESS) 'allocate(uint256,(bytes,uint64)[])' 1 '[(0x0181e203922020ab68b07850bae544b4e720ff59fdc7de709a8b5a8e83d6b7ab3ac2fa83e8461b, 2048)]' The Easiest way to monitor your allocation packages is through the Frontend app, where you can connect your wallet and view your allocations.
Alternatively, you can use the Louper explorer to check your allocation packages by invoking the getClientPackagesWithClaimStatus function (in read tab, choose ViewFacet here). This function will return all allocation packages associated with your wallet address, along with their claim status.
Collateral can be retrieved after all storage providers have successfully claimed their allocations.
You can view the status of your allocation packages and their claims using the Louper explorer or the Frontend app.
The Frontend app will let you call retrieveCollateral function directly when conditions are met.
In the Louper explorer, you can invoke the retrieveCollateral function (in write tab, choose RetrieveCollateralFacet here) to retrieve your collateral.
- Foundry
- Solidity version 0.8.25
- Access to a Filecoin network (Devnet/Calibnet/Mainnet)
- DataCap allocation for the contract account
- Environment file (.env) with appropriate configuration
Copy the .env.example file to .env and fill in the required values:
# Network RPC URLs
RPC_TEST=http://127.0.0.1:1234/rpc/v1
RPC_CALIBNET=https://filecoin-calibration.chainup.net/rpc/v1
RPC_MAINNET=https://api.node.glif.io/rpc/v1
# Private keys
PRIVATE_KEY_TEST=<your_private_key>
PRIVATE_KEY_CALIBNET=<your_private_key>
PRIVATE_KEY_MAINNET=<your_private_key>
# Contract addresses (will be filled after deployment)
PROXY_ADDRESS_TEST=
PROXY_ADDRESS_CALIBNET=
PROXY_ADDRESS_MAINNET=
# For Mainnet deployment
# Collateral deposit per CID in wei (1 FIL = 10^18 wei)
COLLATERAL_PER_CID=100000000000000000 # 0.1 FIL
# Minimum required storage providers and maximum replicas
# Note: This is a critical configuration that should be set according to your contract requirements.
# It defines the number of storage providers picked for each allocation package
MIN_REQUIRED_STORAGE_PROVIDERS=2
# Maximum replicas to available for users for each CID in an allocation package
# Minimum is 1, maximum has to be less than or equal to the number of storage providers
MAX_REPLICAS=3
# For Devnet bash deploy scripts
MY_FIL_WALLET=t410...
MY_ETH_WALLET=0xEB...
Deployment command will:
- Clean and build the project
- Deploy the Diamond contract with all facets
- Initialize the contract with default parameters
- Output the contract address
- Verify the contract on Calibnet using
calibnet_verify.sh
make calibnet_deployBefore proceeding with mainnet deployment, ensure you have:
- Sufficient funds for deployment and gas fees
- A secure private key
- Properly configured environment variables
- Thoroughly tested on Calibnet
Ensure your .env file contains the following variables with appropriate values:
RPC_MAINNET=https://api.node.glif.io/rpc/v1
PRIVATE_KEY_MAINNET=<your_secure_private_key>
COLLATERAL_PER_CID=<amount_in_FIL>
MIN_REQUIRED_STORAGE_PROVIDERS=<minimum_number>
MAX_REPLICAS=<maximum_replicas>
Deploy the contract to mainnet:
make mainnet_deployFirst, make sure you have PROXY_ADDRESS_MAINNET set in your .env file after deployment. This address will be used to interact with the contract.
Then, source your environment variables:
source .envCalibnet Example:
cast send --json --gas-limit 9000000000 --private-key $PRIVATE_KEY_CALIBNET --rpc-url $RPC_CALIBNET $PROXY_ADDRESS_CALIBNET "addAllocator(address)" <ALLOCATOR_ADDRESS>Mainnet Example:
cast send --json --gas-limit 9000000000 --private-key $PRIVATE_KEY_MAINNET --rpc-url $RPC_MAINNET $PROXY_ADDRESS_MAINNET "addAllocator(address)" <ALLOCATOR_ADDRESS>Calibnet Example:
cast send --json --gas-limit 9000000000 --private-key $PRIVATE_KEY_CALIBNET --rpc-url $RPC_CALIBNET $PROXY_ADDRESS_CALIBNET "createStorageEntity(address,uint64[])" <ENTITY_OWNER_ADDRESS> "[<PROVIDER_ID>]"Mainnet Example:
cast send --json --gas-limit 9000000000 --private-key $PRIVATE_KEY_MAINNET --rpc-url $RPC_MAINNET $PROXY_ADDRESS_MAINNET "createStorageEntity(address,uint64[])" <ENTITY_OWNER_ADDRESS> "[<PROVIDER_ID>]"Repeat this step for all required storage entities and their providers.