Documents Product Categories SperaxOS

SperaxOS

Dec 18, 2025
CollateralData) public collateralInfo; collateralStrategyInfo mapping(address => mapping(address => StrategyData)) private collateralStrategyInfo; collateralStrategies mapping(address => address[]) private collateralStrategies; Functions constructor Constructor to initialize the Collateral Manager constructor(address _vault); Parameters Name Type Description Address of the Vault _vault address contract addCollateral Register a collateral for mint & redeem in USDs 69function addCollateral(address _collateral, CollateralBaseData memory _data) external onlyOwner; Parameters Name Type Description _collateral address Address of the collateral _data CollateralBaseData Collateral configuration data updateCollateralData Update existing collateral configuration function updateCollateralData(address _collateral, CollateralBaseData memory _updateData) external onlyOwner; Parameters Name Type Description _collateral address Address of the collateral Updated configuration for _updateData CollateralBaseData the collateral removeCollateral Un-list a collateral function removeCollateral(address _collateral) external onlyOwner; Parameters 70Name Type Description _collateral address Address of the collateral addCollateralStrategy Add a new strategy to collateral function addCollateralStrategy(address _collateral, address _strategy, uint16 _allocationCap) external onlyOwner; Parameters Name Type Description _collateral address Address of the collateral _strategy address Address of the strategy _allocationCap uint16 Allocation capacity updateCollateralStrategy Update existing strategy for collateral function updateCollateralStrategy(address _collateral, address _strategy, uint16 _allocationCap) external onlyOwner; Parameters Name Type Description _collateral address Address of the collateral _strategy address Address of the strategy _allocationCap uint16 Allocation capacity 71removeCollateralStrategy Remove an existing strategy from collateral Ensure all the collateral is removed from the strategy before calling this Otherwise it will create error in collateral accounting function removeCollateralStrategy(address _collateral, address _strategy) external onlyOwner; Parameters Name Type Description _collateral address Address of the collateral _strategy address Address of the strategy updateCollateralDefaultStrategy function updateCollateralDefaultStrategy(address _collateral, address _strategy) external onlyOwner; validateAllocation Validate allocation for a collateral function validateAllocation(address _collateral, address _strategy, uint256 _amount) external view returns (bool); Parameters 72Name Type Description _collateral address Address of the collateral Address of the desired _strategy address strategy _amount uint256 Amount to be allocated. Returns Name Type Description True for valid allocation bool request. getFeeCalibrationData Get the required data for mint function getFeeCalibrationData(address _collateral) external view returns (uint16, uint16, uint16, uint256); Parameters Name Type Description _collateral address Address of the collateral Returns 73Name Type Description Base fee config for collateral (baseMintFee, uint16 baseRedeemFee, composition, totalCollateral) uint16 uint16 uint256 getMintParams Get the required data for mint function getMintParams(address _collateral) external view returns (CollateralMintData memory mintData); Parameters Name Type Description _collateral address Address of the collateral Returns Name Type Description mintData CollateralMintData mintData getRedeemParams Get the required data for USDs redemption 74function getRedeemParams(address _collateral) external view returns (CollateralRedeemData memory redeemData); Parameters Name Type Description _collateral address Address of the collateral Returns Name Type Description redeemData CollateralRedeemData redeemData getAllCollaterals Gets a list of all listed collaterals function getAllCollaterals() external view returns (address[] memory); Returns Name Type Description List of addresses address[] representing all listed collaterals getCollateralStrategies Gets a list of all strategies linked to a collateral 75function getCollateralStrategies(address _collateral) external view returns (address[] memory); Parameters Name Type Description _collateral address Address of the collateral Returns Name Type Description List of addresses address[] representing available strategies for the collateral isValidStrategy Verifies if a strategy is linked to a collateral function isValidStrategy(address _collateral, address _strategy) external view returns (bool); Parameters Name Type Description _collateral address Address of the collateral _strategy address Address of the strategy Returns 76Name Type Description True if the strategy is linked bool to the collateral, otherwise False getCollateralInStrategies Get the amount of collateral in all Strategies function getCollateralInStrategies(address _collateral) public view returns (uint256 amountInStrategies); Parameters Name Type Description _collateral address Address of the collateral Returns Name Type Description amountInStrategies uint256 amountInStrategies getCollateralInVault Get the amount of collateral in vault function getCollateralInVault(address _collateral) public view returns (uint256 amountInVault); Parameters 77Name Type Description _collateral address Address of the collateral Returns Name Type Description amountInVault uint256 amountInVault getCollateralInAStrategy Get the amount of collateral allocated in a strategy function getCollateralInAStrategy(address _collateral, address _strategy) public view returns (uint256 allocatedAmt); Parameters Name Type Description _collateral address Address of the collateral _strategy address Address of the strategy Returns Name Type Description allocatedAmt uint256 Allocated amount Events CollateralAdded 78event CollateralAdded(address collateral, CollateralBaseData data); CollateralRemoved event CollateralRemoved(address collateral); CollateralInfoUpdated event CollateralInfoUpdated(address collateral, CollateralBaseData data); CollateralStrategyAdded event CollateralStrategyAdded(address collateral, address strategy); CollateralStrategyUpdated event CollateralStrategyUpdated(address collateral, address strategy); CollateralStrategyRemoved event CollateralStrategyRemoved(address collateral, address strategy); Errors CollateralExists 79error CollateralExists(); CollateralDoesNotExist error CollateralDoesNotExist(); CollateralStrategyExists error CollateralStrategyExists(); CollateralStrategyMapped error CollateralStrategyMapped(); CollateralStrategyNotMapped error CollateralStrategyNotMapped(); CollateralNotSupportedByStrategy error CollateralNotSupportedByStrategy(); CollateralAllocationPaused error CollateralAllocationPaused(); 80CollateralStrategyInUse error CollateralStrategyInUse(); AllocationPercentageLowerThanAllocatedAmt error AllocationPercentageLowerThanAllocatedAmt(); IsDefaultStrategy error IsDefaultStrategy(); Structs CollateralData struct CollateralData { bool mintAllowed; bool redeemAllowed; bool allocationAllowed; bool exists; address defaultStrategy; uint16 baseMintFee; uint16 baseRedeemFee; uint16 downsidePeg; uint16 desiredCollateralComposition; uint16 collateralCapacityUsed; uint256 conversionFactor; } StrategyData 81struct StrategyData { uint16 allocationCap; bool exists; } 82SPA Buyback Git Source Inherits: Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable Author: Sperax Foundation This contract allows users to exchange SPA tokens for USDs tokens. Users can provide SPA tokens and receive USDs tokens in return based on the current exchange rate. A percentage of the provided SPA tokens are distributed as rewards, and the rest are burned. State Variables veSpaRewarder address public veSpaRewarder; rewardPercentage uint256 public rewardPercentage; oracle address public oracle; 83Functions constructor constructor(); initialize Contract initializer function initialize(address _veSpaRewarder, uint256 _rewardPercentage) external initializer; Parameters Name Type Description _veSpaRewarder address Rewarder''s address Percentage of SPA to be _rewardPercentage uint256 rewarded withdraw Emergency withdrawal function for unexpected situations Can only be called by the owner function withdraw(address _token, address _receiver, uint256 _amount) external onlyOwner; Parameters 84Name Type Description Address of the asset to be _token address withdrawn Address of the receiver of _receiver address tokens Amount of tokens to be _amount uint256 withdrawn updateRewardPercentage Changes the reward percentage Example value for _newRewardPercentage = 5000 for 50% function updateRewardPercentage(uint256 _newRewardPercentage) external onlyOwner; Parameters Name Type Description _newRewardPercentage uint256 New Reward Percentage updateVeSpaRewarder Update veSpaRewarder address function updateVeSpaRewarder(address _newVeSpaRewarder) external onlyOwner; Parameters 85Name Type Description is the address of desired _newVeSpaRewarder address veSpaRewarder updateOracle Update oracle address function updateOracle(address _newOracle) external onlyOwner; Parameters Name Type Description is the address of desired _newOracle address oracle buyUSDs Function to buy USDs for SPA for frontend function buyUSDs(uint256 _spaIn, uint256 _minUSDsOut) external; Parameters Name Type Description _spaIn uint256 Amount of SPA tokens Minimum amount out in _minUSDsOut uint256 USDs getSPAReqdForUSDs 86Calculates and returns SPA amount required for _usdsAmount function getSPAReqdForUSDs(uint256 _usdsAmount) external view returns (uint256); Parameters Name Type Description USDs amount the user _usdsAmount uint256 wants Returns Name Type Description uint256 Amount of SPA required buyUSDs Buy USDs for SPA if you want a different receiver function buyUSDs(address _receiver, uint256 _spaIn, uint256 _minUSDsOut) public nonReentrant; Parameters Name Type Description _receiver address Receiver of USDs _spaIn uint256 Amount of SPA tokens Minimum amount out in _minUSDsOut uint256 USDs 87distributeAndBurnSPA Sends available SPA in this contract to rewarder based on rewardPercentage and burns the rest function distributeAndBurnSPA() public; getUsdsOutForSpa Returns the amount of USDS for SPA amount in function getUsdsOutForSpa(uint256 _spaIn) public view returns (uint256); Parameters Name Type Description _spaIn uint256 Amount of SPA tokens Returns Name Type Description Amount of USDs user will uint256 get _getUsdsOutForSpa Returns the amount of USDS for SPA amount in function _getUsdsOutForSpa(uint256 _spaIn) private view returns (uint256, uint256); 88Parameters Name Type Description _spaIn uint256 Amount of SPA tokens Returns Name Type Description Amount of USDs user will uint256 get uint256 _getOracleData Retrieves price data from the oracle contract for SPA and USDS tokens. function _getOracleData() private view returns (uint256, uint256, uint256, uint256); Returns Name Type Description The price of USDS in SPA, the price of SPA in USDS, uint256 and their respective precisions. uint256 uint256 uint256 _isValidRewardPercentage 89Checks if the provided reward percentage is valid. The reward percentage must be a non-zero value and should not exceed the maximum percentage value. function _isValidRewardPercentage(uint256 _rewardPercentage) private pure; Parameters Name Type Description The reward percentage to _rewardPercentage uint256 validate. Events BoughtBack event BoughtBack( address indexed receiverOfUSDs, address indexed senderOfSPA, uint256 spaPrice, uint256 spaAmount, uint256 usdsAmount ); Withdrawn event Withdrawn(address indexed token, address indexed receiver, uint256 amount); SPARewarded event SPARewarded(uint256 spaAmount); 90SPABurned event SPABurned(uint256 spaAmount); RewardPercentageUpdated event RewardPercentageUpdated(uint256 newRewardPercentage); VeSpaRewarderUpdated event VeSpaRewarderUpdated(address newVeSpaRewarder); OracleUpdated event OracleUpdated(address newOracle); Errors CannotWithdrawSPA error CannotWithdrawSPA(); InsufficientUSDsBalance error InsufficientUSDsBalance(uint256 toSend, uint256 bal); 91MasterPriceOracle Git Source Inherits: Ownable, IOracle Author: Sperax Foundation Communicates with different price feeds to get the price State Variables tokenPriceFeed Store price feed data for tokens. mapping(address => PriceFeedData) public tokenPriceFeed; Functions updateTokenPriceFeed Add/Update price feed for _token Have to be extra cautious while updating the price feed. function updateTokenPriceFeed(address _token, address _source, bytes memory _data) external onlyOwner; Parameters 92Name Type Description address of the desired _token address token. _source address price feed source. call data for fetching the _data bytes price feed. removeTokenPriceFeed Remove an existing price feed for _token . function removeTokenPriceFeed(address _token) external onlyOwner; Parameters Name Type Description _token address address of the token. getPrice Gets the price feed for _token . Function reverts if the price feed does not exists. function getPrice(address _token) external view returns (PriceData memory); Parameters 93Name Type Description address of the desired _token address token. Returns Name Type Description (uint256 price, uint256 PriceData precision). priceFeedExists Validates if price feed exists for a _token Function reverts if price feed not set. function priceFeedExists(address _token) external view returns (bool); Parameters Name Type Description address of the desired _token address token. Returns Name Type Description bool bool if price feed exists. _getPriceFeed 94Gets the price feed for a _token given the feed data. function _getPriceFeed(address _token, address _source, bytes memory _msgData) private view returns (PriceData memory priceData); Parameters Name Type Description address of the desired _token address token. _source address price feed source. _msgData bytes call data for fetching feed. Returns Name Type Description (uint256 price, uint256 priceData PriceData precision). Events PriceFeedUpdated event PriceFeedUpdated(address indexed token, address indexed source, bytes msgData); PriceFeedRemoved 95event PriceFeedRemoved(address indexed token); Errors InvalidAddress error InvalidAddress(); UnableToFetchPriceFeed error UnableToFetchPriceFeed(address token); InvalidPriceFeed error InvalidPriceFeed(address token); PriceFeedNotFound error PriceFeedNotFound(address token); 96Yield Reserve Git Source Inherits: ReentrancyGuard, Ownable Author: Sperax Foundation This contract allows users to swap supported stable-coins for yield earned by the USDs protocol. It sends USDs to the Dripper contract for rebase and to the Buyback Contract for buyback. State Variables vault address public vault; oracle address public oracle; buyback address public buyback; dripper address public dripper; 97buybackPercentage uint256 public buybackPercentage; tokenData mapping(address => TokenData) public tokenData; Functions constructor Constructor of the YieldReserve contract. constructor(address _buyback, address _vault, address _oracle, address _dripper); Parameters Name Type Description Address of the Buyback _buyback address contract. _vault address Address of the Vault. _oracle address Address of the Oracle. Address of the Dripper _dripper address contract. swap 98Swap function to be called by frontend users. function swap(address _srcToken, address _dstToken, uint256 _amountIn, uint256 _minAmountOut) external; Parameters Name Type Description _srcToken address Source/Input token. _dstToken address Destination/Output token. _amountIn uint256 Input token amount. Minimum output tokens _minAmountOut uint256 expected. toggleSrcTokenPermission Allow or disallow a specific token for use as a source/input token. function toggleSrcTokenPermission(address _token, bool _isAllowed) external onlyOwner; Parameters Name Type Description Address of the token to be _token address allowed or disallowed. If set to true, the token will be allowed as a _isAllowed bool source/input token; otherwise, it will be disallowed. 99toggleDstTokenPermission Allow or disallow a specific token for use as a destination/output token. Reverts if caller is not owner. function toggleDstTokenPermission(address _token, bool _isAllowed) external onlyOwner; Parameters Name Type Description Address of the token to be _token address allowed or disallowed. If set to true, the token will be allowed as a _isAllowed bool destination/output token; otherwise, it will be disallowed. withdraw Emergency withdrawal function for unexpected situations. function withdraw(address _token, address _receiver, uint256 _amount) external onlyOwner; Parameters 100Name Type Description Address of the asset to be _token address withdrawn. Address of the receiver of _receiver address tokens. Amount of tokens to be _amount uint256 withdrawn. updateBuybackPercentage Set the percentage of newly minted USDs to be sent to the Buyback contract. Reverts if caller is not owner. The remaining USDs are sent to VaultCore for rebase. function updateBuybackPercentage(uint256 _toBuyback) public onlyOwner; Parameters Name Type Description The percentage of USDs _toBuyback uint256 sent to Buyback (e.g., 3000 for 30%). updateBuyback Update the address of the Buyback contract. Reverts if caller is not owner. function updateBuyback(address _newBuyBack) public onlyOwner; 101Parameters Name Type Description New address of the _newBuyBack address Buyback contract. updateOracle Update the address of the Oracle contract. Reverts if caller is not owner. function updateOracle(address _newOracle) public onlyOwner; Parameters Name Type Description New address of the Oracle _newOracle address contract. updateDripper Update the address of the Dripper contract. Reverts if caller is not owner. function updateDripper(address _newDripper) public onlyOwner; Parameters 102Name Type Description New address of the Dripper _newDripper address contract. updateVault Update the address of the VaultCore contract. Reverts if caller is not owner. function updateVault(address _newVault) public onlyOwner; Parameters Name Type Description New address of the _newVault address VaultCore contract. swap Swap allowed source token for allowed destination token. function swap(address _srcToken, address _dstToken, uint256 _amountIn, uint256 _minAmountOut, address _receiver) public nonReentrant; Parameters 103Name Type Description _srcToken address Source/Input token. _dstToken address Destination/Output token. _amountIn uint256 Input token amount. Minimum output tokens _minAmountOut uint256 expected. _receiver address Receiver of the tokens. mintUSDs Mints USDs directly with the allowed collaterals for USDs. Only collaterals configured in USDs vault are allowed to be used for minting. function mintUSDs(address _token) public nonReentrant; Parameters Name Type Description Address of token to mint _token address USDs with getTokenBForTokenA Get an estimate of the output token amount for a given input token amount. function getTokenBForTokenA(address _srcToken, address _dstToken, uint256 _amountIn) public view returns (uint256); Parameters 104Name Type Description _srcToken address Input token address. _dstToken address Output token address. _amountIn uint256 Input amount of _srcToken. Returns Name Type Description Estimated output token uint256 amount. _sendUSDs Distributes USDs to the Buyback and Dripper contracts based on buybackPercentage. Sends a portion of the USDs balance to the Buyback contract and the remaining to the Dripper contract for rebase. function _sendUSDs() private; Events Swapped event Swapped( address indexed srcToken, address indexed dstToken, address indexed dstReceiver, uint256 amountIn, uint256 amountOut ); 105USDsMintedViaSwapper event USDsMintedViaSwapper(address indexed collateralAddr, uint256 usdsMinted); Withdrawn event Withdrawn(address indexed token, address indexed receiver, uint256 amount); BuybackPercentageUpdated event BuybackPercentageUpdated(uint256 toBuyback); BuybackUpdated event BuybackUpdated(address newBuyback); OracleUpdated event OracleUpdated(address newOracle); VaultUpdated event VaultUpdated(address newVault); DripperUpdated 106event DripperUpdated(address newDripper); USDsSent event USDsSent(uint256 toBuyback, uint256 toDripper); SrcTokenPermissionUpdated event SrcTokenPermissionUpdated(address indexed token, bool isAllowed); DstTokenPermissionUpdated event DstTokenPermissionUpdated(address indexed token, bool isAllowed); Errors InvalidSourceToken error InvalidSourceToken(); InvalidDestinationToken error InvalidDestinationToken(); AlreadyInDesiredState 107error AlreadyInDesiredState(); TokenPriceFeedMissing error TokenPriceFeedMissing(); Structs TokenData struct TokenData { bool srcAllowed; bool dstAllowed; uint160 conversionFactor; } 108Fee Calculator Git Source Inherits: IFeeCalculator Author: Sperax Foundation A contract that calculates fees for minting and redeeming USDs. State Variables LOWER_THRESHOLD uint16 private constant LOWER_THRESHOLD = 5000; UPPER_THRESHOLD uint16 private constant UPPER_THRESHOLD = 15000; DISCOUNT_FACTOR uint16 private constant DISCOUNT_FACTOR = 2; PENALTY_MULTIPLIER uint16 private constant PENALTY_MULTIPLIER = 2; 109CALIBRATION_GAP uint32 private constant CALIBRATION_GAP = 1 days; COLLATERAL_MANAGER ICollateralManager public immutable COLLATERAL_MANAGER; collateralFee mapping(address => FeeData) public collateralFee; Functions constructor constructor(address _collateralManager); calibrateFee Calibrates fee for a particular collateral function calibrateFee(address _collateral) external; Parameters 110Name Type Description Address of the desired _collateral address collateral getMintFee Calculates fee to be collected for minting function getMintFee(address _collateral) external view returns (uint256); Parameters Name Type Description _collateral address Returns Name Type Description uint256 (uint256) baseFeeIn getRedeemFee Calculates fee to be collected for redeeming function getRedeemFee(address _collateral) external view returns (uint256); Parameters 111Name Type Description _collateral address Returns Name Type Description uint256 (uint256) baseFeeOut calibrateFeeForAll Calibrates fee for all the collaterals registered function calibrateFeeForAll() public; _calibrateFee Helper function for calibrating fee for a collateral function _calibrateFee(address _collateral) private; Parameters Name Type Description Address of the desired _collateral address collateral Events FeeCalibrated 112event FeeCalibrated(address indexed collateral, uint16 mintFee, uint16 redeemFee); Errors InvalidCalibration error InvalidCalibration(); Structs FeeData struct FeeData { uint32 nextUpdate; uint16 mintFee; uint16 redeemFee; } 113RebaseManager Git Source Inherits: IRebaseManager, Ownable Author: Sperax Foundation This contract handles the configuration and execution of the rebasing mechanism for the USDs stablecoin. It ensures that rebases occur only when certain prerequisites are fulfilled, such as the time gap between rebases and acceptable APR (Annual Percentage Rate) ranges. The Rebase Manager coordinates with the Vault and Dripper contracts to manage the rebase process. State Variables ONE_YEAR uint256 private constant ONE_YEAR = 365 days; vault address public vault; dripper address public dripper; 114gap uint256 public gap; aprCap uint256 public aprCap; aprBottom uint256 public aprBottom; lastRebaseTS uint256 public lastRebaseTS; Functions onlyVault modifier onlyVault(); constructor Constructor to initialize the Rebase Manager 115constructor(address _vault, address _dripper, uint256 _gap, uint256 _aprCap, uint256 _aprBottom); Parameters Name Type Description Address of the vault _vault address contract Address of the dripper _dripper address contract for collecting USDs Minimum time gap required _gap uint256 between two consecutive rebases Maximum allowed APR for a _aprCap uint256 rebase Minimum allowed APR for a _aprBottom uint256 rebase fetchRebaseAmt Get the current amount valid for rebase Function is called by the vault while rebasing function fetchRebaseAmt() external onlyVault returns (uint256); Returns Name Type Description The available amount for uint256 rebasing USDs 116updateVault Updates the vault address function updateVault(address _newVault) public onlyOwner; Parameters Name Type Description Address of the new vault _newVault address contract updateDripper Updates the dripper contract for USDs vault function updateDripper(address _dripper) public onlyOwner; Parameters Name Type Description Address of the new dripper _dripper address contract updateGap Update the minimum time gap required between two rebases function updateGap(uint256 _gap) public onlyOwner; Parameters 117Name Type Description _gap uint256 Updated gap time updateAPR Update the APR requirements for each rebase function updateAPR(uint256 _aprBottom, uint256 _aprCap) public onlyOwner; Parameters Name Type Description New minimum APR for a _aprBottom uint256 rebase New maximum APR for a _aprCap uint256 rebase getAvailableRebaseAmt Gets the current available rebase fund function getAvailableRebaseAmt() public view returns (uint256); Returns Name Type Description Current balance in the vault uint256 plus collectable dripped USDs amount 118getMinAndMaxRebaseAmt Gets the minimum and maximum rebase USDs amount based on the APR config function getMinAndMaxRebaseAmt() public view returns (uint256, uint256); Returns Name Type Description Minimum and maximum uint256 rebase amounts uint256 Events VaultUpdated event VaultUpdated(address vault); DripperUpdated event DripperUpdated(address dripper); GapUpdated event GapUpdated(uint256 gap); 119APRUpdated event APRUpdated(uint256 aprBottom, uint256 aprCap); Errors CallerNotVault error CallerNotVault(address caller); InvalidAPRConfig error InvalidAPRConfig(uint256 aprBottom, uint256 aprCap); 120Dripper Git Source Inherits: IDripper, Ownable Author: Sperax Foundation This contract releases tokens at a steady rate to the Vault contract, for rebasing the USDs stablecoin. The Dripper contract ensures that tokens are released gradually over time, allowing for consistent and controlled distribution. State Variables vault address public vault; dripRate uint256 public dripRate; dripDuration uint256 public dripDuration; lastCollectTS 121uint256 public lastCollectTS; Functions constructor Constructor to initialize the Dripper. constructor(address _vault, uint256 _dripDuration); Parameters Name Type Description Address of the contract that _vault address receives the dripped tokens. The duration over which _dripDuration uint256 tokens are dripped. recoverTokens Emergency fund recovery function. Transfers the asset to the owner of the contract. function recoverTokens(address _asset) external onlyOwner; Parameters 122Name Type Description Address of the asset to _asset address recover. addUSDs Function to be used to send USDs to dripper and update dripRate . function addUSDs(uint256 _amount) external; Parameters Name Type Description Amount of USDs to be sent _amount uint256 form caller to this contract. collect Transfers the dripped tokens to the vault. This function also updates the dripRate based on the fund state. function collect() public returns (uint256); Returns Name Type Description The amount of tokens uint256 collected and transferred to the vault. 123updateVault Update the vault address. function updateVault(address _vault) public onlyOwner; Parameters Name Type Description Address of the new vault _vault address contract. updateDripDuration Updates the dripDuration. function updateDripDuration(uint256 _dripDuration) public onlyOwner; Parameters Name Type Description The desired drip duration to _dripDuration uint256 be set. getCollectableAmt Gets the collectible amount of tokens at the current time. function getCollectableAmt() public view returns (uint256); Returns 124Name Type Description The amount of tokens that uint256 can be collected. Events Collected event Collected(uint256 amount); Recovered event Recovered(address owner, uint256 amount); VaultUpdated event VaultUpdated(address vault); DripDurationUpdated event DripDurationUpdated(uint256 dripDuration); USDsAdded event USDsAdded(uint256 _amount); 125Errors NothingToRecover error NothingToRecover(); 126BaseStrategy Git Source Inherits: Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable Author: Sperax Foundation Contract acts as a single interface for implementing specific yield-earning strategies. State Variables vault address public vault; withdrawSlippage uint16 public withdrawSlippage; depositSlippage uint16 public depositSlippage; harvestIncentiveRate uint16 public harvestIncentiveRate; 127assetsMapped address[] internal assetsMapped; rewardTokenAddress address[] public rewardTokenAddress; assetToPToken mapping(address => address) public assetToPToken; gap uint256[40] private __gap__; Functions onlyVault modifier onlyVault(); onlyVaultOrOwner modifier onlyVaultOrOwner(); 128constructor constructor(); updateVault Update the linked vault contract. function updateVault(address _newVault) external onlyOwner; Parameters Name Type Description _newVault address Address of the new Vault. updateHarvestIncentiveRate Updates the HarvestIncentive rate for the user. function updateHarvestIncentiveRate(uint16 _newRate) external onlyOwner; Parameters Name Type Description _newRate uint16 new Desired rate. recoverERC20 A function to recover any erc20 token sent to this strategy mistakenly. 129Only callable by owner. reverts if amount > balance. function recoverERC20(address token, address receiver, uint256 amount) external onlyOwner; Parameters Name Type Description token address Address of the token. receiver address Receiver of the token. amount uint256 Amount to be recovered. deposit Deposit an amount of asset into the platform. function deposit(address _asset, uint256 _amount) external virtual; Parameters Name Type Description _asset address Address for the asset. _amount uint256 Units of asset to deposit. withdraw Withdraw an amount of asset from the platform. 130function withdraw(address _recipient, address _asset, uint256 _amount) external virtual returns (uint256 amountReceived); Parameters Name Type Description Address to which the asset _recipient address should be sent. _asset address Address of the asset. _amount uint256 Units of asset to withdraw. Returns Name Type Description The actual amount amountReceived uint256 received. withdrawToVault Withdraw an amount of asset from the platform to vault. function withdrawToVault(address _asset, uint256 _amount) external virtual returns (uint256 amount); Parameters Name Type Description _asset address Address of the asset. _amount uint256 Units of asset to withdraw. 131collectInterest Withdraw the interest earned of asset from the platform. function collectInterest(address _asset) external virtual; Parameters Name Type Description _asset address Address of the asset. collectReward Collect accumulated reward token and send to Vault. function collectReward() external virtual; checkBalance Get the amount of a specific asset held in the strategy, excluding the interest. Curve: assuming balanced withdrawal. function checkBalance(address _asset) external view virtual returns (uint256); Parameters Name Type Description _asset address Address of the asset. 132Returns Name Type Description uint256 Balance of _asset in uint256 the strategy. checkAvailableBalance Get the amount of a specific asset held in the strategy, excluding the interest and any locked liquidity that is not available for instant withdrawal. Curve: assuming balanced withdrawal. function checkAvailableBalance(address _asset) external view virtual returns (uint256); Parameters Name Type Description _asset address Address of the asset. Returns Name Type Description uint256 Available balance uint256 inside the strategy for _asset. checkInterestEarned AAVE: Get the interest earned on a specific asset. Curve: Get the total interest earned. 133Curve: to avoid double-counting, _asset has to be of index ''entryTokenIndex''. function checkInterestEarned(address _asset) external view virtual returns (uint256); Parameters Name Type Description _asset address Address of the asset. Returns Name Type Description uint256 Amount of interest uint256 earned. checkRewardEarned Get the amount of claimable reward. function checkRewardEarned() external view virtual returns (RewardData[] memory); Returns Name Type Description struct array of type RewardData[] RewardData (address token, uint256 amount). checkLPTokenBalance 134Get the total LP token balance for a asset. function checkLPTokenBalance(address _asset) external view virtual returns (uint256); Parameters Name Type Description _asset address Address of the asset. supportsCollateral Check if an asset/collateral is supported. function supportsCollateral(address _asset) external view virtual returns (bool); Parameters Name Type Description _asset address Address of the asset. Returns Name Type Description bool Whether asset is bool supported. updateSlippage Change to a new depositSlippage & withdrawSlippage. 135function updateSlippage(uint16 _depositSlippage, uint16 _withdrawSlippage) public onlyOwner; Parameters Name Type Description Slippage tolerance for _depositSlippage uint16 allocation. Slippage tolerance for _withdrawSlippage uint16 withdrawal. _initialize Initialize the base properties of the strategy. function _initialize(address _vault, uint16 _depositSlippage, uint16 _withdrawSlippage) internal; Parameters Name Type Description _vault address Address of the USDs Vault. Allowed max slippage for _depositSlippage uint16 Deposit. Allowed max slippage for _withdrawSlippage uint16 withdraw. _setPTokenAddress Provide support for asset by passing its pToken address. Add to internal mappings and execute the platform specific, abstract method _abstractSetPToken . 136function _setPTokenAddress(address _asset, address _pToken) internal; Parameters Name Type Description _asset address Address for the asset. Address for the _pToken address corresponding platform token. _removePTokenAddress Remove a supported asset by passing its index. This method can only be called by the system owner. function _removePTokenAddress(uint256 _assetIndex) internal returns (address asset); Parameters Name Type Description Index of the asset to be _assetIndex uint256 removed. Returns Name Type Description asset address address which is removed. _splitAndSendReward 137Splits and sends the accumulated rewards to harvestor and yield receiver. Sends the amount to harvestor as per harvestIncentiveRate and sends the rest to yield receiver. function _splitAndSendReward(address _token, address _yieldReceiver, address _harvestor, uint256 _amount) internal returns (uint256); Parameters Name Type Description Address of the reward _token address token. Address of the yield _yieldReceiver address receiver. _harvestor address Address of the harvestor. _amount uint256 to be split and sent. Returns Name Type Description uint256 Harvested amount uint256 sent to yield receiver. _abstractSetPToken Call the necessary approvals for the underlying strategy. function _abstractSetPToken(address _asset, address _pToken) internal virtual; 138Parameters Name Type Description _asset address Address of the asset. Address of the _pToken address corresponding receipt token. Events VaultUpdated event VaultUpdated(address newVaultAddr); YieldReceiverUpdated event YieldReceiverUpdated(address newYieldReceiver); PTokenAdded event PTokenAdded(address indexed asset, address pToken); PTokenRemoved event PTokenRemoved(address indexed asset, address pToken); Deposit 139event Deposit(address indexed asset, uint256 amount); Withdrawal event Withdrawal(address indexed asset, uint256 amount); SlippageUpdated event SlippageUpdated(uint16 depositSlippage, uint16 withdrawSlippage); HarvestIncentiveCollected event HarvestIncentiveCollected(address indexed token, address indexed harvestor, uint256 amount); HarvestIncentiveRateUpdated event HarvestIncentiveRateUpdated(uint16 newRate); InterestCollected event InterestCollected(address indexed asset, address indexed recipient, uint256 amount); RewardTokenCollected event RewardTokenCollected(address indexed rwdToken, address indexed recipient, uint256 amount); 140Errors CallerNotVault error CallerNotVault(address caller); CallerNotVaultOrOwner error CallerNotVaultOrOwner(address caller); PTokenAlreadySet error PTokenAlreadySet(address collateral, address pToken); InvalidIndex error InvalidIndex(); CollateralNotSupported error CollateralNotSupported(address asset); InvalidAssetLpPair error InvalidAssetLpPair(address asset, address lpToken); 141CollateralAllocated error CollateralAllocated(address asset); Structs RewardData struct RewardData { address token; uint256 amount; } 142Deployed contracts Name Explorer link https://arbiscan.io/address/0xD74f5255D55 USDs 7944cf7Dd0E45FF521520002D5748 https://arbiscan.io/address/0x6Bbc476Ee3 Vault 5CBA9e9c3A59fc5b10d7a0BC6f74Ca https://arbiscan.io/address/0xdA423BFa1E1 CollateralManager 96598190deEfbAFC28aDb36FaeDF0 https://arbiscan.io/address/0xd122840Fa5b FeeCalculator 48B2ddB723cCC5928f88dcb558AFC https://arbiscan.io/address/0x14D99412dAB MasterPrice Oracle 1878dC01Fe7a1664cdE85896e8E50 https://arbiscan.io/address/0xFbc0d3cA777 SPABuyback 722d234FE01dba94DeDeDb277AFe3 https://arbiscan.io/address/0xfD14C8ef099 YieldReserve 3fd9409f7820BA8BA80370529d861 https://arbiscan.io/address/0xd50193e8fFb Dripper 00beA274bD2b11d0a7Ea08dA044c1 https://arbiscan.io/address/0x297331A0155 RebaseManager B1e30bBFA85CF3609eC0fF037BEEC https://arbiscan.io/address/0x974993ee8df AaveStrategy 7f5c4f3f9aa4eb5b4534f359f3388 https://arbiscan.io/address/0xb9c9100720d StargateStrategy 8c6e35eb8dd0f9c1abef320daa136 https://arbiscan.io/address/0xBCeb486257 CompoundStrategy 71E35420076f79Ec6921E783a82442 143Buyback Contract Buyback contract is designed to decentralize the SPA buyback process while adding a boosting mechanism to continuously grow USDs TVL with protocol revenue (Yield + Fee). This contract allows users to directly purchase USDs with SPA. The available-for-purchase USDs comes from two sources - USDs mint/redeem fees and USDs yield. The contract distributes 70% of the yield to USDs holders and the rest 30% goes to SPA buyback and burn. 1. Protocol Yield - The yield is collected in multiple tokens through pool fee (USDC, etc.) and farm rewards. This yield is converted to USDs. A portion is sent to buyback contract and remaining to auto-yield reserve. • Swap yield tokens to eligible collateral tokens (for minting USDs) using DEXes based on the path of lowest slippage • Mint USDs using the collateral tokens • Deposit 30% of the yield (USDs received in previous step) that will be used to buyback SPA into the buyback contract and the rest of the USDs is transferred back to the USDs vaultʼs auto-yield reserve. Yield share percentage is set to 30% currently and can be changed in future through governance. 2. Protocol Fee - Mint and redemption fee from the USDs protocol is collected in the form of USDs. This component is directly deposited to the Buyback contract. 3. Buyback SPA - Any users or external wallet can view the USDs balance in the Buyback contract and sell their SPA tokens for USDs. 4. Burn 2.0 - 30% of the SPA received in step 3 is burnt and remaining 70% is sent to USDs holding wallets. The burn percentage can be changed through governance. Example flow of the buyback contract: Sperax protocol deposits USDC.e to Stargate strategy (Stargate-LP USDC.e) and stakes LP tokens. Protocol earns STG token. 1441. Sell STG tokens for USDC.e 2. Mint USDs from USDC.e 3. Transfer USDs to the Buyback contract 4. Deposit USDs Mint and Redemption Fees directly into the Buyback contract as and when they are collected 5. Users can view the amount of USDs left in the contract. 6. USDs is bought by the users using their SPA and contract receives SPA against the USDs. Technical Specification Buyback Architecture View Functions 1451. getSPAReqdForUSDs - Input the target USDs amount to receive and get an estimate of how many SPA tokens are needed. a. Function: getSPAReqdForUSDs(uint256 _usdsAmount) external view returns (uint256) b. Input : _usdsAmount is the amount of USDs to purchase c. Output: Estimated amount of SPA required d. Example: Suppose getSPAReqdForUSDs(1e18) returns 100*1e18. It means the contract estimates that one will need to speed 100 SPA to purchase 1 USDs at this moment. 2. getUsdsOutForSpa - Input the amount of SPA to be sold and get an estimate of how many USDs can be purchased. a. Function: getUsdsOutForSpa(uint256 _spaIn) external view returns (uint256) b. Input : _spaIn is the amount of SPA to be sold c. Output: Estimated amount of USDs to be received d. Example: Suppose getSPAReqdForUSDs(100*1e18) returns 1e18. It means the contract estimates that one will receive 1 USDs after spending 100 SPA. User End Write Functions 1461. buyUSDs - Purchase USDs with SPA a. Function: buyUSDs(uint256 _spaIn, uint256 _minUSDsOut) external b. Input : _spaIn is the amount of SPA to be sold _minUSDsOut is the minimum amount of USDs to be received c. Output : The contract will extract _spaIn amount of SPA from the wallet executing the transaction, exchange it to USDs, and send USDs back to the wallet d. Example : Suppose wallet A triggers buyUSDs(100*1e18, 1e18) (after he has already approved the Buyback contract to spend its SPA). The contract will extract 100 SPA from A, exchange it to USDs, and send USDs back to A. 2. buyUSDs - Purchase USDs with SPA (arbitrary USDs receipt) a. Function: buyUSDs(address _receiver, uint256 _spaIn, uint256 _minUSDsOut) external b. Input : _receiver is the receiver of the purchased USDs _spaIn is the amount of SPA to be sold _minUSDsOut is the minimum amount of USDs to be received c. Output : The contract will extract _spaIn amount of SPA from the wallet executing the transaction, exchange it to USDs, and send USDs to the _receiver wallet d. Example : Suppose wallet A triggers buyUSDs(B, 100*1e18, 1e18) (after he has already approved the Buyback contract to spend its SPA). The contract will extract 100 SPA from A, exchange it to USDs, and send USDs to B. 147Staking Protocol Stake SPA to earn rewards from fees and yield SPA holders can stake SPA tokens and receive veSPA tokens which are non- transferable. veSPA balance is proportional to the lockup period, meaning if user locks up for higher duration they receive proportionally more veSPA tokens. veSPA balance determines the share of staking reward and voting power 1. Staking rewards: Rewards will be distributed proportionally to the user''s veSPA balance. Users who lock SPA for longer periods are eligible for proportionally more rewards (accrued on a weekly base) than users who locked the same amount of SPA for shorter periods. Rewards are accumulated through: • As per SIP-66 , the emission of xSPA for veSPA holders has been increased to 420,000 xSPA tokens per week from the treasury to account for the decrement in APR due to cutting the allocation from bought-back SPA. • Fee Rewards - 100% of the Fee income from USDs mints and redemptions. The yield and fee income are originally generated in USDs. It is then swapped for SPA tokens before distribution. This makes the reward claiming process simple for stakers and maintains a constant buying pressure on the SPA token. 2. Voting power: once governance protocol is launched, voting power will depend on users'' veSPA balance. Users who are committed to longer lockups will own more votes, whatever they are voting for. veSPA balance at the moment of staking will not stay the same all the time - it will decay/reduce linearly. User''s rewards per week and voting power will also decrease over time, together with the veSPA balance. Stakers can increase their veSPA balance and thereby their staking rewards and voting power by either extending the locking period or locking more SPA or re-staking SPA rewards. At the end of the lockup period, veSPA balance will reduce depending on the pre- selected withdrawal option: 1481. For Auto-cooldown option - to zero. 2. For the "Staying Staked" option (manual cooldown) - to [0.01917*staked SPA tokens]. Please note that for new stakers the "Staying Staked option" is not available. To stake SPA through our dApp, checkout Staking SPA 149Locking SPA veSPA is SPA locked with the "vote escrow" mechanism (ve-). This is the mechanism of locking tokens for relatively long pre-defined time periods. On staking SPA, the staker receives non-tranferrable tokens veSPA. The veSPA balance will depend on the amount of SPA tokens locked and the user''s chosen lockup period. Stakers can lock their tokens for a maximum of 4 years or a minimum of 7 days. A higher lockup period means a higher veSPA balance for the same amount of SPA staked. veSPA value = SPA tokens staked * (Lockup Period in days / 365) Below table shows how veSPA value will be determined Lock-up Period SPA Tokens staked veSPA value 4 year 1 4 3 year 1 3 2 year 1 2 1 year 1 1 6 months 1 0.5 veSPA balance will decay linearly with time. At the end of lockup period, veSPA balance will reduce to 0 or (0.01917*staked SPA tokens) based on the withdrawal method user chooses while staking the SPA. Two withdrawal methods were available to the early stakers, they will work till the end of their lockup period. For all the new stakers method 1 (Auto-cooldown) stays the only available method. 1501. Auto-cooldown - veSPA balance reduces to 0 at the end of lockup period and staker is able to withdraw locked SPA balance immediately after expiry date 2. Stay Staked at Residual Value - Stakers can opt to remain staked at the end of the unlock date at a residual veSPA balance (0.01917*staked SPA tokens). They can initiate cooldown when 7 days are left in the staking period or anytime after. After the cooldown period of 7 days is over, they can withdraw the staked SPA tokens. Check outStay Staked at Residual Valuefor more details. Example of veSPA balance decay over time User stakes 1000 SPA for 4 years • At Day 0 user will have 4000 veSPA • At day 365 user will have 3000 veSPA • At day 365*2 user will have 2000 veSPA • At day 365*3 user will have 1000 veSPA • At Day 365*4 user will have 0 veSPA or 19.17 veSPA depending on withdrawal method chosen Some important points to consider before locking your SPA tokens: 1. Staking is an irreversible process, once locked the tokens cannot be unlocked before the unlock date. Users cannot prepone the expiry date or reduce the amount of locked SPA. 2. Due to precision issues it may not always be possible to choose an exact unlock date. Users can select the lockup period but the exact unlock date is rounded down to nearest Thursday UTC. Stakers will be able to choose these eligible unlock dates in our dapp . 3. Users rewards per week and voting power will also decrease over time, together with the veSPA balance. The users who are committed to long-term staking can increase their rewards per week and voting power by: • Extending the locking period • Locking more SPA • Claiming SPA staking rewards and restaking them into veSPA (compounding) 151Extension of Lockup Stakers can extend their lockup such that the lockup expiry date is <= 4 years from the current date. At no point, lockup period can be more than 4 years. The lockup period cannot be extended in the following scenarios: 1. When a stakerʼs lockup period has expired and they only have residual veSPA balance 2. When a staker has already initiated cooldown period Example: If a user has 100 veSPA tokens which expire in 3 years, the user can increase the lockup by a maximum of 1 year. Increasing Staked Balance Users can also increase their veSPA balance by staking any additional amount of SPA tokens for the same lockup as their existing veSPA balance. Example: if a user has 100 veSPA tokens which are expiring in 34 days, they can stake any amount of additional SPA for 34 days. The increase in veSPA balance will be calculated based on the same lockup period as the previously staked tokens. 152Withdrawing SPA Users who initiate staking will get the auto-cooldown feature. Users who initiated staking in the past were able select one of two cooldown options that will stay active till the end of the staking period: 1. Auto-cooldown: the protocol will automatically initiate a cooldown 7 days before expiry. In this case, the assets will be available immediately after the lock period, but they will not bring any more rewards post-expiry. 2. Stay staked at residual value (or manual cooldown): After the lock expires, the assets remain locked with the residual value of [0.01917*staked SPA tokens], and will continue to bring rewards to the user. If one day the user decides to take their assets out - they need to initiate the cooldown manually. After a one- week cooldown, the assets will become available. Withdrawing from the protocol does not automatically claim all the SPA rewards. The users would be required to claim the rewards separately. In case the staking rewards are not distributed till that point of time, users can still claim the rewards and fee earnings accrued to them after they have withdrawn their staked SPA tokens. Auto-Cooldown When new users start staking, the auto-cooldown feature is applied. Auto- cooldown means that the veSPA balance decays to zero linearly and the stakers are able to withdraw their staked SPA balance at the end of their lockup period. Stay Staked at Residual Value Currently, this option is not available. If a user has chosen this option while staking in the past, their final veSPA balance was set at the residual veSPA balance. They would continue to earn all the staking rewards at this veSPA balance for as long as the protocol keeps distributing rewards. The residual veSPA balance is equivalent to the veSPA balance of a staking position that unlocks in 7 days. 153Residual veSPA balance = (7/365)*Staked SPA tokens = 0.01917*Staked SPA tokens Initiate Cooldown Stakers can initiate cooldown when 7 days are left in lockup expiry or anytime after that. When the stakerʼs veSPA balance is set to the residual veSPA balance or when only 7 days are left in the lockup period, the staker can initiate a transaction to start a cooldown period. When the cooldown period is initiated the lockup expiry date is updated to a new date which would be 7 days from the date on which the cooldown was initiated. Stakerʼs veSPA balance decays from the residual veSPA balance to zero during the cooldown period. At the end of the cooldown period, the staker can withdraw the SPA they had deposited. Stakers receive rewards during the cooldown period in proportion to their veSPA balance. At the end of the cooldown period, the veSPA balance remains zero and the user doesnʼt get any rewards. The users can unstake at any point in time after the cooldown period has ended. 154Example On Day 0, user staked 1000 SPA for 365 days. Their veSPA balance will be 1000 veSPA On the 358th day the userʼs veSPA balance will be ~ 19.17 (1000x(7/365)). At this point of time, the user can initiate a cooldown period. On the 365th day, if the user hasnʼt yet initiated a cooldown period the userʼs veSPA balance will be ~ 19.17. At this point the lockup has expired and the user can initiate a cooldown period. On the 400th day, if the user hasnʼt yet initiated a cooldown period the userʼs veSPA balance will be ~ 19.17 and they can initiate a cooldown period. After the end of the cooldown period the user can unstake the 1000 SPA tokens. Letʼs say on the 400th day the user initiates a cooldown, they would be able to withdraw the tokens on the 407th day. 155Staking Rewards veSPA holders or stakers get SPA token rewards in proportion to their veSPA balance. Users have an option to stake their SPA rewards directly into the staking protocol instead of claiming, thereby compounding their veSPA balances. veSPA holders or stakers will receive 2 kinds of rewards. Both kinds of rewards are distributed with the same mechanism and in the form of SPA tokens: 1. 100% of the USDs Fees generated by USDs protocol. 2. Incentives sponsored by Treasury to bootstrap the Staking protocol - This emission has been set to 0 through governance. 1. USDs Fee Rewards The USDs protocol fees collected will be distributed across all veSPA holders on a pro rata basis. With the fees collected by the USDs protocol the Staking protocol will purchase SPA tokens from the market and distribute the SPA tokens amongst all veSPA holders. 2. Incentive Rewards A separate SPA reward budget was set aside from Treasury to bootstrap staking protocol. Incentive rewards for staking were initially set at a fixed daily distribution number of 54.794 K SPA. This number was later changed through governance and set to 0 to reduce SPA inflation, and xSPA rewards have been increased to 420,000 per week as per SIP-66 . The yield and fee income are swapped for SPA tokens before distribution. The SPA that is bought back from the open markets using 30% (SIP-66) of the auto-yield and 100% of the fees is stored in: 0xA61a0719e9714c95345e89a2f1C83Fae6f5745ef (Arbitrum One). Distribution of Staking Rewards 156Staking reward distribution happens on a weekly basis. New weeks start on Thursdays at 00:00:00 UTC. Rewards are distributed at the end of each week. ‘Stake-Reward-Claim-Repeatʼ is a four-step cycle: 1. Stake SPA - In Week 1, you stake SPA and obtain veSPA. Reward accumulation starts from Week 2. 2. Reward distribution for Week 2 occurs at the end of the Week 2 • Total staking reward available for distribution in Week 2 is based on USDs fee income that was collected, the yield that was generated by the USDs protocol and the daily distribution rate of the SPA staking incentives for Week 2 • Userʼs share of staking rewards for Week 2 is calculated based on their veSPA balance at the starting of Week 2 relative to the total veSPA supply at the starting of week 2 3. Rewards for Week 2 can be claimed by users from Week 3 onwards. Users can also stake their SPA rewards and increase their veSPA balance and future rewards. 4. Step 2 and 3 repeat every week till expiry • Reward distribution for Week X occurs at the end of the Week X • Rewards for Week X can be claimed by users from Week X+1 onwards. Calculation of Staking Rewards Total SPA Rewards for the week = Staking Incentive Rewards + USDs Fees Rewards + USDs Yield Share Rewards where Staking Incentive Rewards = Daily Incentive Rewards*7 USDs Fees Rewards = USDs Fee earned*USDs Price / SPA price USDs Yield Share Rewards = 50%*USDs yield earned in the week * USDs price/ SPA price 157The above formula is an assumption, actual SPA rewards from USDs fees and yield would differ based on the asset prices in the open market Total SPA Rewards for the week = R Total veSPA balance of the protocol at the start of the week = V Userʼs veSPA balance at the start of the week = v Userʼs SPA staked at the start of the week = s User''s staking duration in years = d = v/s Weekly Reward Earned by a user = r = (R/V)*v Staking APR = (Weekly Reward Earned by a user /SPA staked by user)*(365/7)*100% = (r/s)*(365/7)*100% = (R/V)*(v/s)*(365/7)*100% = (R/V)*d *(365/7)*100% Staking APY = [{1+ (Weekly Reward Earned by a user /SPA staked by user)}^(365/7) -1] * 100% = [{1+ (r/s)}^(365/7) -1] * 100% = [{1+ (R/V)*(v/s)}^(365/7) -1] * 100% = [{1+ (R/V)*d}^(365/7) -1] * 100% Calculation of APY assumes that the weekly rewards are re-staked in the protocol. 158Sperax Farms Protocol Works on Arbitrum Uniswap V2, Uniswap V3, Camelot V2, Camelot V3 and Balancer V2. Sperax Farms protocol is a protocol for DAOs to launch and manage decentralized exchange liquidity - without needing to know how to code. Sperax Farms give users the power to launch incentivized liquidity pools on Uniswap V3 and Camelot V2. Future versions will support custom liquidity shapes on major DEXs such as Balancer, Sushiswap or anything veSPA holders prefer. Sperax Farms is launched on Arbitrum and will be expanded to Optimism, Polygon and Ethereum soon. Additional blockchains will be added in future versions. Sperax Farms automates the fundamental aspects of launching and managing decentralized exchange liquidity for the DAOs native token: • Engineering support to launch and manage the farm - The Audited Farm Factory contract will generate the pool and farm contracts for the Sperax Farms user. • Marketing support to make the community aware of the new farm - Protocols that launch their farm through Sperax Farms benefit from being whitelisted on the Sperax Farms active farms dashboard. This exclusive list features all of the farms that are actively distributing rewards that were deployed with Sperax Farms. Farmers will regularly look to this dashboard for new projects and become users of these protocols. On Arbitrum, Uniswap has less liquidity than Balancer and Sushiswap. This is because Sushiswap uses the simple Uniswap V2, x*y=k approach. This is simple because all LP tokens are the same but penalizes the LP because they are forced to provide liquidity from 0 to infinity. Incentivized liquidity pools on Uniswap V3 lets DAOs benefit from concentrated liquidity - the same TVL offers less slippage compared to diluted liquidity. This directly translates to a lower emissions budget for other protocols and much more fees for LPs. 159To launch a Uniswap V3 or Camelot V3 farm, DAOs are currently expected to write complicated V3 farm contracts, get the contracts audited, incentivize LP deposits with only their native token, then promote this new pool. With Sperax Farms, DAOs can launch these farms without knowing how to code and get marketing and technical support from Sperax. DAOs can also launch their farms on Camelot V2, their farms can get rewards both in SPA and Camelot tokens (xGRAIL/GRAIL) that decrease DAO''s token spend and decrease sale pressure on it. Sperax Farms V2 In Sperax Farms Protocol V2, we have focused more on the protocol''s multichain vision and to support that, we have discontinued incentives on pairing with SPA and USDs as these tokens are not available on all the chains. Now the fee params are configurable in the farm registry contract. More details are shared in the next section. 160How does Sperax Farms work? Overview Sperax Farms is a protocol that enables DAOs to launch and manage decentralized exchange (DEX) liquidity pools and farms on platforms like Uniswap V2, Uniswap V3, Camelot V2, Camelot V3 and Balancer V2. The protocol simplifies the process of incentivizing liquidity for the DAOs'' native tokens without requiring coding expertise. Sperax Farms automates the creation, management, and reward distribution for liquidity pools, providing support in engineering, marketing, and financial incentives. Launching Farm on Sperax Farms (For Farm Admins) Steps to Launch a Farm 1611. Approve Fees Spend: Users must first approve the spending of the fee token for the farm creation fee. The fee is 100 USDs on Arbitrum. On any chain not supporting USDs, it can be any other stable coin supported on that chain. 2. Input Pool and Farm Parameters: Users must provide necessary parameters for the pool and farm. 3. Execute Transaction: A transaction is executed to create the farm and pay the farm creation fee. • Reversion Conditions: ◦ If the pool does not exist, the transaction reverts. ◦ If the user does not have enough fee tokens to pay the fee, the transaction reverts. Pool Parameters 1. Token address • Token A • Token B 2. Fee Tier (for Uniswap V3 Pools) 3. Liquidity Parameter L - An LP token parameter which is set based on the userʼs liquidity inside the pool. Updating this parameter will allow the farm users to add or remove liquidity. When the LP token is updated, the LP token should start receiving rewards based on the new L parameter. This will allow users to add or remove liquidity without having to unstake their entire LP position. 4. Active Liquidity Time Check - (a Boolean value of true or false which determines whether the position is in the current price range) - To check if the position is in the current price range, as only that liquidity will be rewarded which is in range. However, users will be allowed to remove any locked liquidity during the period when the liquidity is not in the current price range. Base Farm Parameters 1621. Farm admin: It is the address that will have admin control on the farm. It can be the same as the deployerʼs address or any other desired address which will be used to manage the farm. 2. Price range for the LP 3. Reward tokens • Token addresses • Token address managers: Each token will have its own token manager. • Reward tokens have to be added at the time of farm creation and can''t be added or removed after the farm is created. Maximum 4 reward tokens are possible for a farm. • For reward tokens emitting fixed APR, Token manager will be the Rewarder contract deployed through the Rewarder factory. 4. Cooldown Period for Locked Liquidity - It is the number of days users have to wait after initiating cooldown before they can unstake from a locked position. Only whole numbers are allowed for this parameter. • If Cooldown Period = 0, then the farm only allows creation of unlocked positions. Unlocked positions can be unstaked anytime. • If Cooldown Period > =1, the farm will allow creation of both locked and unlocked positions. For unstaking a locked position, users have to initiate cooldown and wait for cooldown period before unstaking. 5. Start date time stamp - Reward emission starts from this date. This date can be changed by the farm admin using admin functions (updateFarmStartTime). However, date change is not allowed after the farm starts. • The farms start accepting liquidity immediately after the creation of the farm contract. However, the reward accrual starts from the farm start date time stamp. 6. Annual Percentage Return APR - The farm admin can set a fixed APR which will guarantee a reward to the LPs based on the current price of the reward tokens. The farm admin will also have to choose one or more base tokens out of the tokens present in the farm for calculation of the daily number of reward tokens emitted. 163• Letʼs say APR set = APR%. • Total value of Base Tokens in USD = Σ (Number of Base tokens in farm x USD value of Base Token) • No. of Reward tokens per day = [(APR x Total value of Base Tokens in USD) / (100 x 365)] / (Price of 1 reward token based on the oracle)] 7. Maximum number of Rewards Tokens Emitted Per Day - The farm admin would be able to add the maximum number of reward tokens per day that the farm can distribute, to prevent any deficiency in the farmʼs reward tokens in cases when many LPs deposit on the same day since then all the withdrawals may also happen on the same day. • It will override the reward tokens value calculated using the formula mentioned above in point no 6. The rewards emitted per day will be the minimum of the amount calculated above or the max reward rate set by the reward manager. Expirable Farm Parameters This is a new feature which has all the above parameters along with the expiry date feature: 1641. Expiry Date - Farms will have an expiry date associated with them. Users can specify the expiry date while creating the farms. The initial launch fee of 100 USDs (or some other stable coin if USDs is not present on that chain) will add 100 days to the farm expiry. After that users have the option to extend the farm expiry date. Post that users will have to extend the farm in the multiple of 1 USDs/day with minimum of 100 days at a time and a maximum of 300 days. • Farm admins will not be able to update any farm parameters once the farm has expired. However, they can remove any unclaimed reward tokens from the contract. • Farm users can still claim any accrued rewards from the farm or remove liquidity from them once that farm has expired. • Farms that have expired will be available on the Dashboard for removing liquidity up to 30 days beyond expiry. After that the farms will only appear on the expired farm list and admins cannot make the expired farms active again. Users or admins will be able to apply all actions through the smart contract. • Expired farms shall be removed from the Sperax Gauge and would not be eligible for SPA rewards. • Farm does not accrue rewards after the expiration so users can call updateFarmRewardData function on the farm to accrue rewards before farmEndTime to avoid any loss of rewards. Fee • Sperax Farms will charge a flat $100 fee to launch the farm, which will add 100 days to the expiry date set while the creation of the farm. • After that users have the option to extend the farm expiry date. They can do so in the multiple of 1 USDs/day with minimum of 100 days at a time and a maximum of 300 days. The fees can be paid in either USDs or in any other stable coin provided USDs is not present in that chain. • The fee collected belongs to the SPA stakers and can be transferred directly to the wallet address where all Sperax protocol fees are collected. Fee amount can be changed in future through governance. • The Fee details can be fetched from getFeeParams function on the Farm registry contract. 165Farm Management No one can make changes to the farm contract once deployed. Farm admins can do the following: 1. Transfer farm ownership to another address 2. Change start date of the farm - Farm will emit rewards from this date. The date can be changed after farm creation. However, date change is not allowed after the farm starts. 3. Update cooldown period of the locked positions 4. Pause or Unpause the farm • Pause the farm - All reward distributions are paused, LPs do not earn any rewards during this period. Withdrawals are allowed (including lockup LPs) and users can also claim previously accrued rewards. Admin/managers can make changes to the distribution rates and the other parameters when the farm is paused. • Unpause the farm - Resume the reward distribution. The reward distribution rate remains the same as set by the reward managers. 5. Update Expiry Date of the Farm by paying Subscription Fees • Farm admins can choose and update the expiry date of farms as mentioned in the Farm Parameters. 6. Close the farm - The farms can be closed before the expiry date and will automatically get closed once the expiry date is reached. Once the farm is closed, all liquidity providers including lockup users can now unstake their liquidity pool tokens and claim the accrued rewards from the farm. Reward Management Each reward token will be assigned a reward token manager. Farm admin cannot update the reward token manager once the farms are deployed. Reward token managers can do the following: 1661. Add reward token balance 2. Update reward distribution rate per second for each token. Only future distribution rates can be affected through this. Reward distribution can be paused by setting the rate to 0. These actions can be done: a. For all liquidity providers. b. For lockup liquidity providers (If cooldown period is greater than 0) 3. Changing the maximum number of token rewards per day - Farm admins can increase or decrease the reward tokens limit in the fixed APR model as per their choice. 4. Withdraw reward tokens (Any rewards already accrued to LPs cannot be removed). 5. Transfer reward token management to another address. Setting up a Farm Position (For Liquidity Providers/Retail Users) Adding a Farm Position: 1. Select the farm: LPs need to choose the required farm from the whitelisted farms which are present on the dashboard, based on their choice of tokens and price range. 2. Enter the number of farm tokens: Post that, they can enter the number of one of the tokens for the position, the other tokens are automatically calculated. 3. Execute Transaction: The user then needs to approve the wallet transactions for the spending of tokens and creation of a farm position. • Reversion Conditions: ◦ If the user does not have the required tokens in their wallet, the transaction reverts. 4. LP Token: On successful execution of the transaction, the Liquidity Provider will receive the LP token(s) in their wallet. 5. Depositing the LP Token: Users then need to deposit the lp tokens inside the farm to create a position. Updating Liquidity in the LP Tokens: 1671. Updating the Liquidity Balance - This will allow LPs to add to or remove liquidity from the LP Token without having to unstake their entire LP position. When the LP token is updated, the LP token should start receiving rewards based on the new L parameter. Rewards Emission: 1. Fixed APR Rewards - The LPs will receive fixed APR rewards (this fixed APR will be set by the farm admin) generated from their invested farms. Each farm will have some base reward tokens which will be the base for calculating the number of reward tokens to be emitted per day. The LP will receive the reward tokens (selected by the farm admin) in their wallet, which will be calculated as: • Letʼs say APR set = APR%. • Total value of Base Tokens in USD = Σ (Number of Base tokens in farm x USD value of Base Token) • No. of Reward tokens per day = [(APR x Total value of Base Tokens in USD) / (100 x 365)] / (Price of 1 reward token based on the oracle)] 2. Maximum number of Rewards Tokens Emitted Per Day - However, the number of reward tokens to be emitted by the farm has been capped. The farm admin would be able to add the maximum number of reward tokens per day that the farm can distribute, to prevent any deficiency in the farmʼs reward tokens in cases when many LPs deposit on the same day since then all the withdrawals may also happen on the same day. 168Technical documents This technical document is about the upgrade to Sperax Farms v2. It details the changes made to enhance the protocol''s multi chain vision, transparency, security, and scalability. The document covers new features and functionalities. Sperax Farms protocol farm admin and user interaction diagram FIxed APR rewarder flow 169170Smart contracts High level documentation of smart contracts 171E721 Farms E721 farms include all the farms built for pools in which the liquidity provider has an NFT (ERC721) position. 172E721Farm Git Source Inherits: Farm, IERC721Receiver Author: Sperax Foundation. This contract contains the core logic for E721 farms. State Variables nftContract address public nftContract; depositToTokenId mapping(uint256 => uint256) public depositToTokenId; Functions onERC721Received Function is called when user transfers the NFT to this farm. 173function onERC721Received(address, address _from, uint256 _tokenId, bytes calldata _data) external override returns (bytes4); Parameters Name Type Description address _from address The address of the owner. NFT Id generated by other _tokenId uint256 protocol (e.g. Camelot or Uniswap). The data should be the _data bytes lockup flag (bool). Returns Name Type Description bytes4 The bytes4 onERC721Received selector. withdraw Function to withdraw a deposit from the farm. function withdraw(uint256 _depositId) external override nonReentrant; Parameters 174Name Type Description The id of the deposit to be _depositId uint256 withdrawn. _getLiquidity Function to get the liquidity. Must be defined by the farm. This function should be overridden to add the respective logic. function _getLiquidity(uint256 _tokenId) internal view virtual returns (uint256); Parameters Name Type Description _tokenId uint256 The nft tokenId. Returns Name Type Description The liquidity of the nft uint256 position. Errors UnauthorisedNFTContract error UnauthorisedNFTContract(); 175NoData error NoData(); 176Camelot V3 Pools in camelot V3 are very similar to Uniswap V3. When a liquidity provider supplies assets to the pool, the LP receives an NFT position in return. Next, you can see the specification for Camelot V3 farm and Camelot V3 farm deployer. 177CamelotV3FarmDeployer Git Source Inherits: FarmDeployer Author: Sperax Foundation. This contract allows anyone to calculate fees, pay fees and create farms. State Variables CAMELOT_V3_FACTORY address public immutable CAMELOT_V3_FACTORY; NFPM address public immutable NFPM; CAMELOT_UTILS address public immutable CAMELOT_UTILS; CAMELOT_NFPM_UTILS address public immutable CAMELOT_NFPM_UTILS; 178Functions constructor Constructor of the contract. constructor( address _farmRegistry, string memory _farmId, address _camelotV3Factory, address _nfpm, address _camelotUtils, address _nfpmUtils ) FarmDeployer(_farmRegistry, _farmId); Parameters Name Type Description Address of the Farm _farmRegistry address Registry. _farmId string Id of the farm. Address of CamelotV3 _camelotV3Factory address factory. Address of Camelot _nfpm address NonfungiblePositionManage r contract. Address of CamelotUtils _camelotUtils address (Camelot helper) contract. Address of Camelot INonfungiblePositionManag _nfpmUtils address erUtils (NonfungiblePositionManag er helper) contract. 179createFarm Deploys a new CamelotV3 farm. The caller of this function should approve feeAmount to this contract before calling this function. function createFarm(FarmData memory _data) external nonReentrant returns (address); Parameters Name Type Description _data FarmData Data for deployment. Returns Name Type Description Address of the deployed address farm. Structs FarmData struct FarmData { address farmAdmin; uint256 farmStartTime; uint256 cooldownPeriod; CamelotPoolData camelotPoolData; RewardTokenData[] rewardData; } 180CamelotV3Farm Git Source Inherits: E721Farm, OperableDeposit, ClaimableFee Author: Sperax Foundation. This contract is the implementation of the Camelot V3 farm. State Variables tickLowerAllowed int24 public tickLowerAllowed; tickUpperAllowed int24 public tickUpperAllowed; camelotPool address public camelotPool; camelotV3Factory address public camelotV3Factory; 181camelotUtils address public camelotUtils; nfpmUtils address public nfpmUtils; MIN_TICK int256 internal constant MIN_TICK = -887272; MAX_TICK int256 internal constant MAX_TICK = 887272; Functions initialize Initializer function of this farm. function initialize(InitializeInput calldata _input) external; Parameters 182Name Type Description A struct having all the input _input InitializeInput params. increaseDeposit Allow user to increase liquidity for a deposit. function increaseDeposit(uint256 _depositId, uint256[2] calldata _amounts, uint256[2] calldata _minAmounts) external nonReentrant; Parameters Name Type Description The id of the deposit to be _depositId uint256 increased. Desired amount of tokens to _amounts uint256[2] be increased. Minimum amount of tokens _minAmounts uint256[2] to be added as liquidity. decreaseDeposit Withdraw liquidity partially from an existing deposit. function decreaseDeposit(uint256 _depositId, uint128 _liquidityToWithdraw, uint256[2] calldata _minAmounts) external nonReentrant; Parameters 183Name Type Description _depositId uint256 Deposit index for the user. _liquidityToWithdraw uint128 Amount to be withdrawn. Minimum amount of tokens _minAmounts uint256[2] to be received. getTokenAmounts Function to be called by Rewarder to get tokens and amounts associated with the farm''s liquidity. function getTokenAmounts() external view override returns (address[] memory, uint256[] memory); Returns Name Type Description tokens An array of token address[] addresses. amounts An array of token uint256[] amounts. _claimPoolFee Claim pool fee implementation from ClaimableFee feature. function _claimPoolFee(uint256 _depositId) internal override returns (uint256 tokenId, uint256 amt0Recv, uint256 amt1Recv); Parameters 184Name Type Description Deposit ID of the deposit in _depositId uint256 the farm. _getLiquidity Validate the position for the pool and get Liquidity. The position must adhere to the price ranges. Only allow specific pool token to be staked. function _getLiquidity(uint256 _tokenId) internal view override returns (uint256); Parameters Name Type Description _tokenId uint256 The tokenId of the position. Returns Name Type Description uint256 The liquidity of the position. _validateTickRange Validate the ticks (upper and lower). Get the info of the required token. Check if the token belongs to correct pool. 185Check if the token adheres to the tick range. The ticks must be within the max range and must be multiple of tickSpacing. function _validateTickRange(int24 _tickLower, int24 _tickUpper) private view; Parameters Name Type Description _tickLower int24 The lower tick of the range. _tickUpper int24 The upper tick of the range. Errors InvalidCamelotPoolConfig error InvalidCamelotPoolConfig(); IncorrectPoolToken error IncorrectPoolToken(); IncorrectTickRange error IncorrectTickRange(); InvalidTickRange 186error InvalidTickRange(); InvalidAmount error InvalidAmount(); 187Base contracts These contracts are base for all the other contracts and they have the common logic for functionalities like deposit, withdraw, createFarm, etc. 188Farm Git Source Inherits: FarmStorage, OwnableUpgradeable, ReentrancyGuardUpgradeable, MulticallUpgradeable Author: Sperax Foundation. This contract contains the core logic for the Sperax farms. Functions constructor constructor(); withdraw Function to be called to withdraw deposit. function withdraw(uint256 _depositId) external virtual; Parameters Name Type Description _depositId uint256 The id of the deposit. claimRewards 189A function to be called by the depositor to claim rewards. function claimRewards(uint256 _depositId) external; Parameters Name Type Description _depositId uint256 The id of the deposit. initiateCooldown Function to be called to initiate cooldown for a staked deposit. _depositId is corresponding to the user''s deposit. function initiateCooldown(uint256 _depositId) external nonReentrant; Parameters Name Type Description The id of the deposit to be _depositId uint256 locked. addRewards Add rewards to the farm. function addRewards(address _rwdToken, uint256 _amount) external nonReentrant; Parameters 190Name Type Description The reward token''s _rwdToken address address. The amount of reward _amount uint256 tokens to add. updateCooldownPeriod Update the cooldown period. function updateCooldownPeriod(uint256 _newCooldownPeriod) external onlyOwner; Parameters Name Type Description The new cooldown period _newCooldownPeriod uint256 (in days). E.g: 7 means 7 days. farmPauseSwitch Pause / UnPause the farm. function farmPauseSwitch(bool _isPaused) external onlyOwner; Parameters Name Type Description Desired state of the farm _isPaused bool (true to pause the farm). 191closeFarm A function to explicitly close the farm. Recovers remaining non accrued rewards. function closeFarm() external onlyOwner nonReentrant; recoverERC20 Recover erc20 tokens other than the reward tokens. function recoverERC20(address _token) external onlyOwner nonReentrant; Parameters Name Type Description Address of token to be _token address recovered. recoverRewardFunds Get the remaining reward balance out of the farm. Function recovers minOf(_amount, rewardsLeft). function recoverRewardFunds(address _rwdToken, uint256 _amount) external nonReentrant; Parameters 192Name Type Description The reward token''s _rwdToken address address. The amount of the reward _amount uint256 tokens to be withdrawn. setRewardRate Function to update reward params for a fund. function setRewardRate(address _rwdToken, uint128[] memory _newRewardRates) external; Parameters Name Type Description The reward token''s _rwdToken address address. The new reward rate for the _newRewardRates uint128[] fund (includes the precision). updateRewardData Transfer the tokenManagerRole to other user. Only the existing tokenManager for a reward can call this function. function updateRewardData(address _rwdToken, address _newTknManager) external; Parameters 193Name Type Description The reward token''s _rwdToken address address. Address of the new token _newTknManager address manager. computeRewards Function to compute the total accrued rewards for a deposit for each subscription. function computeRewards(address _account, uint256 _depositId) external view virtual returns (uint256[][] memory rewards); Parameters Name Type Description _account address The user''s address. _depositId uint256 The id of the deposit. Returns Name Type Description The total accrued rewards rewards uint256[][] for the deposit for each subscription (uint256[][]). getRewardFunds Get the reward fund details. 194function getRewardFunds() external view returns (RewardFund[] memory); Returns Name Type Description The available reward funds'' RewardFund[] details for all the reward funds. getRewardData Get the reward details for specified reward token. function getRewardData(address _rwdToken) external view returns (RewardData memory); Parameters Name Type Description The address of the reward _rwdToken address token. Returns Name Type Description The available reward details RewardData for the specified reward token. getDepositInfo Get deposit info for a deposit id. 195function getDepositInfo(uint256 _depositId) external view returns (Deposit memory); Parameters Name Type Description _depositId uint256 The id of the deposit. Returns Name Type Description Deposit The deposit info (Deposit). getNumSubscriptions Get number of subscriptions for an account. function getNumSubscriptions(uint256 _depositId) external view returns (uint256); Parameters Name Type Description _depositId uint256 The deposit id. Returns Name Type Description The number of uint256 subscriptions for the deposit. 196getSubscriptionInfo Get subscription stats for a deposit. function getSubscriptionInfo(uint256 _depositId, uint256 _subscriptionId) external view returns (Subscription memory); Parameters Name Type Description _depositId uint256 The deposit id. _subscriptionId uint256 The subscription''s id. Returns Name Type Description The subscription info Subscription (Subscription). getRewardRates Get reward rates for a rewardToken. function getRewardRates(address _rwdToken) external view returns (uint256[] memory); Parameters Name Type Description The reward token''s _rwdToken address address. 197Returns Name Type Description The reward rates for the uint256[] reward token (uint256[]). getRewardFundInfo Get farm reward fund info. function getRewardFundInfo(uint8 _fundId) external view returns (RewardFund memory); Parameters Name Type Description _fundId uint8 The fund''s id. Returns Name Type Description The reward fund info RewardFund (RewardFund). getRewardTokens Function to get the reward tokens added in the farm. function getRewardTokens() external view returns (address[] memory); Returns 198Name Type Description The reward tokens added in address[] the farm. getTokenAmounts Function to be called by Rewarder to get tokens and amounts associated with the farm''s liquidity. This function should be overridden to add the respective logic. function getTokenAmounts() external view virtual returns (address[] memory, uint256[] memory); Returns Name Type Description Tokens associated with the address[] farm''s pool. Amounts associated with uint256[] the farm''s liquidity. updateFarmRewardData Function to update the FarmRewardData for all funds. function updateFarmRewardData() public virtual; claimRewardsTo Claim rewards and send it to another account. 199Only the depositor can call this function. function claimRewardsTo(address _account, uint256 _depositId) public nonReentrant; Parameters Name Type Description _account address To receive the rewards. _depositId uint256 The id of the deposit. updateFarmStartTime Update the farm start time. Can be updated only before the farm start. New start time should be in future. function updateFarmStartTime(uint256 _newStartTime) public virtual onlyOwner; Parameters Name Type Description _newStartTime uint256 The new farm start time. isFarmOpen Returns if farm is open. Farm is open if it is not closed. This function can be overridden to add any new/additional logic. function isFarmOpen() public view virtual returns (bool); 200Returns Name Type Description bool bool True if farm is open. isFarmActive Returns if farm is active. Farm is active if it is not paused and not closed. This function can be overridden to add any new/additional logic. function isFarmActive() public view virtual returns (bool); Returns Name Type Description bool bool True if farm is active. getRewardBalance Get the reward balance for specified reward token. This function calculates the available reward balance by considering the accrued rewards and the token supply. function getRewardBalance(address _rwdToken) public view returns (uint256); Parameters 201Name Type Description The address of the reward _rwdToken address token. Returns Name Type Description The available reward uint256 balance for the specified reward token. _recoverERC20 function _recoverERC20(address _token) internal virtual; _deposit Common logic for deposit in the farm. function _deposit(address _account, bool _lockup, uint256 _liquidity) internal returns (uint256); Parameters Name Type Description _account address Address of the depositor. Lockup option for the _lockup bool deposit. Liquidity amount to be _liquidity uint256 added to the pool. 202Returns Name Type Description uint256 The deposit id. _initiateCooldown Common logic for initiating cooldown. function _initiateCooldown(uint256 _depositId) internal; Parameters Name Type Description _depositId uint256 User''s deposit Id. _withdraw Common logic for withdraw. function _withdraw(uint256 _depositId) internal; Parameters Name Type Description _depositId uint256 User''s deposit id. _updateAndClaimFarmRewards Claim rewards for the user. 203NOTE: any function calling this private function should be marked as non-reentrant. function _updateAndClaimFarmRewards(uint256 _depositId) internal; Parameters Name Type Description _depositId uint256 The id of the deposit. _updateAndClaimFarmRewardsTo Claim rewards for the user and send it to another account. NOTE: any function calling this private function should be marked as non-reentrant. function _updateAndClaimFarmRewardsTo(uint256 _depositId, address _receiver) internal; Parameters Name Type Description _depositId uint256 The id of the deposit. The receiver of the rewards _receiver address (Could be different from depositor) _recoverRewardFunds Get the remaining reward balance out of the farm. Function recovers minOf(_amount, rewardsLeft). 204In case of partial withdraw of funds, the reward rate has to be set manually again. function _recoverRewardFunds(address _rwdToken, uint256 _amount) internal; Parameters Name Type Description The reward token''s _rwdToken address address. The amount of the reward _amount uint256 token to be withdrawn. _setRewardRate Function to update reward params for a fund. function _setRewardRate(address _rwdToken, uint128[] memory _newRewardRates) internal; Parameters Name Type Description The reward token''s _rwdToken address address. The new reward rate for the _newRewardRates uint128[] fund (includes the precision). _setupFarm Function to setup the reward funds and initialize the farm global params during construction. 205function _setupFarm( string calldata _farmId, uint256 _farmStartTime, uint256 _cooldownPeriod, RewardTokenData[] memory _rwdTokenData ) internal initializer; Parameters Name Type Description ID of the farm. E.g: _farmId string Demeter_Camelot_V2 . _farmStartTime uint256 - Farm start time. - Cooldown period in days _cooldownPeriod uint256 for locked deposits. E.g: 7 means 7 days. - Reward data for each _rwdTokenData RewardTokenData[] reward token. _addRewardData Adds new reward token to the farm. function _addRewardData(address _token, address _tknManager) internal; Parameters Name Type Description Address of the reward _token address token to be added. Address of the reward _tknManager address token Manager. 206_updateLastRewardAccrualTime Update the last reward accrual time. function _updateLastRewardAccrualTime() internal virtual; _getAccRewards Computes the accrued reward for a given fund id and time interval. _alreadyAccRewardBal is useful when this function called from computeRewards function. As computeReward is a view function and it doesn''t update the accRewardBal in the rewardData . function _getAccRewards(uint8 _rwdId, uint8 _fundId, uint256 _time, uint256 _alreadyAccRewardBal) internal view returns (uint256); Parameters Name Type Description _rwdId uint8 Id of the reward token. _fundId uint8 Id of the reward fund. Time interval for the reward _time uint256 computation. Already accrued reward _alreadyAccRewardBal uint256 balance. Returns 207Name Type Description accRewards Accrued rewards for the given uint256 _rwdId , _fundId and _time . _validateDeposit Validate the deposit for account. function _validateDeposit(address _account, uint256 _depositId) internal view; Parameters Name Type Description Address of the caller to be _account address checked against depositor. _depositId uint256 Id of the deposit. _validateNotRecentDeposit A function to validate deposit ts to prevent flash loan vulnerabilities Reverts when deposit made in the same transaction. function _validateNotRecentDeposit(uint256 _depositTs) internal view; Parameters 208Name Type Description depositTs of user''s deposit. _depositTs uint256 (It represents deposit ts or increaseDeposit ts) _validateFarmOpen Validate if farm is open. Revert otherwise. This function can be overridden to add any new/additional logic. function _validateFarmOpen() internal view; _validateFarmActive Validate if farm is active. Revert otherwise. Farm is active if it is not paused and not closed. This function can be overridden to add any new/additional logic. function _validateFarmActive() internal view; _validateTokenManager Validate the caller is the token Manager. Revert otherwise. function _validateTokenManager(address _rwdToken) internal view; Parameters 209Name Type Description _rwdToken address Address of reward token. _validateRewardToken Validate the reward token is valid. function _validateRewardToken(address _rwdToken) internal view; Parameters Name Type Description _rwdToken address Address of reward token. _getRewardAccrualTimeElapsed Get the time elapsed since the last reward accrual. function _getRewardAccrualTimeElapsed() internal view virtual returns (uint256); Returns Name Type Description time The time elapsed since uint256 the last reward accrual. _validateCooldownPeriod An internal function to validate cooldown period. 210function _validateCooldownPeriod(uint256 _cooldownPeriod) internal pure; Parameters Name Type Description _cooldownPeriod uint256 Period to be validated. _validateNonZeroAddr Validate address. function _validateNonZeroAddr(address _addr) internal pure; Parameters Name Type Description _addr address Address to be validated. _subscribeRewardFund Add subscription to the reward fund for a deposit. function _subscribeRewardFund(uint8 _fundId, uint256 _depositId, uint256 _liquidity) private; Parameters 211Name Type Description _fundId uint8 The reward fund id. The unique ID of the _depositId uint256 deposit. _liquidity uint256 The liquidity of the deposit. _unsubscribeRewardFund Unsubscribe a reward fund from a deposit. The rewards claimed from the reward fund is persisted in the event. function _unsubscribeRewardFund(uint8 _fundId, uint256 _depositId) private; Parameters Name Type Description _fundId uint8 The reward fund id. The deposit id _depositId uint256 corresponding to the user. 212FarmStorage Git Source Inherits: IFarm Author: Sperax Foundation. This contract contains the base storage variables for farms. State Variables COMMON_FUND_ID uint8 public constant COMMON_FUND_ID = 0; LOCKUP_FUND_ID uint8 public constant LOCKUP_FUND_ID = 1; PRECISION uint256 public constant PRECISION = 1e18; MAX_COOLDOWN_PERIOD uint256 public constant MAX_COOLDOWN_PERIOD = 30; 213MAX_NUM_REWARDS uint256 public constant MAX_NUM_REWARDS = 4; farmId string public farmId; isPaused bool internal isPaused; isClosed bool internal isClosed; cooldownPeriod uint256 public cooldownPeriod; lastFundUpdateTime uint256 public lastFundUpdateTime; farmStartTime 214uint256 public farmStartTime; totalDeposits uint256 public totalDeposits; rewardFunds RewardFund[] internal rewardFunds; rewardTokens address[] internal rewardTokens; rewardData mapping(address => RewardData) internal rewardData; deposits mapping(uint256 => Deposit) internal deposits; subscriptions mapping(uint256 => Subscription[]) internal subscriptions; 215FarmRegistry Git Source Inherits: IFarmRegistry, OwnableUpgradeable Author: Sperax Foundation. This contract tracks fee details, privileged users, deployed farms and farm deployers. State Variables farms address[] internal farms; deployerList address[] internal deployerList; feeReceiver address public feeReceiver; feeToken address public feeToken; 216feeAmount uint256 public feeAmount; extensionFeePerDay uint256 public extensionFeePerDay; farmRegistered mapping(address => bool) public farmRegistered; deployerRegistered mapping(address => bool) public deployerRegistered; isPrivilegedUser mapping(address => bool) public isPrivilegedUser; Functions constructor constructor(); 217initialize constructor function initialize(address _feeReceiver, address _feeToken, uint256 _feeAmount, uint256 _extensionFeePerDay) external initializer; Parameters Name Type Description _feeReceiver address Receiver of the fees. The fee token for farm _feeToken address creation. The fee amount to be paid _feeAmount uint256 by the creator. _extensionFeePerDay uint256 Extension fee per day. registerFarm Register a farm created by registered Deployer. Only registered deployer can register a farm. function registerFarm(address _farm, address _creator) external; Parameters 218Name Type Description Address of the created farm _farm address contract _creator address Address of the farm creator. registerFarmDeployer Register a new farm deployer. Only owner can call this function. function registerFarmDeployer(address _deployer) external onlyOwner; Parameters Name Type Description Address of deployer to be _deployer address registered. removeDeployer Remove an existing deployer from registry. Only owner can call this function. function removeDeployer(uint16 _id) external onlyOwner; Parameters 219Name Type Description ID of the deployer to be _id uint16 removed (0 index based). updatePrivilege Function to add/remove privileged User. Only callable by the owner. function updatePrivilege(address _user, bool _privilege) external onlyOwner; Parameters Name Type Description User Address for which _user address privilege is to be updated. Privilege(bool) whether true _privilege bool or false. getFarmDeployerList Get list of registered deployer. function getFarmDeployerList() external view returns (address[] memory); Returns 220Name Type Description Returns array of registered address[] deployer addresses. getFarmList Get list of farms created via registered deployer. function getFarmList() external view returns (address[] memory); Returns Name Type Description Returns array of farm address[] addresses. getFeeParams Get all the fee parameters for creating farm. It returns fee amount and extension fee as 0 if _user is privileged. function getFeeParams(address _user) external view returns (address, address, uint256, uint256); Parameters Name Type Description The account creating the _user address farm. Returns 221Name Type Description address Receiver of the fees. Token in which fee is to be address paid. Amount of fees to be paid uint256 for creation of farm. Extension fee per day in uint256 case of extending a farm. updateFeeParams Update the fee params for registry. function updateFeeParams(address _receiver, address _feeToken, uint256 _amount, uint256 _extensionFeePerDay) public onlyOwner; Parameters Name Type Description _receiver address FeeReceiver address. _feeToken address Token address for fee. Amount of token to be _amount uint256 collected. _extensionFeePerDay uint256 Extension fee per day. _validateNonZeroAddr Validate address. 222function _validateNonZeroAddr(address _addr) private pure; 223FarmDeployer Git Source Inherits: Ownable, ReentrancyGuard Author: Sperax Foundation. Exposes base functionalities which will be useful in every deployer. State Variables FARM_REGISTRY address public immutable FARM_REGISTRY; farmImplementation address public farmImplementation; farmId string public farmId; Functions constructor 224Constructor. constructor(address _farmRegistry, string memory _farmId) Ownable(msg.sender); Parameters Name Type Description Address of the Farm _farmRegistry address Registry. _farmId string Id of the farm. updateFarmImplementation Update farm implementation''s address. Only callable by the owner. Ensure that _newFarmId is correct for the new farm implementation. function updateFarmImplementation(address _newFarmImplementation, string calldata _newFarmId) external onlyOwner; Parameters Name Type Description New farm implementation''s _newFarmImplementation address address. _newFarmId string ID of the new farm. _collectFee 225Collect fee and transfer it to feeReceiver. Function fetches all the fee params from farmRegistry. function _collectFee() internal virtual; _validateNonZeroAddr Validate address. function _validateNonZeroAddr(address _addr) internal pure; Events FarmCreated event FarmCreated(address indexed farm, address indexed creator, address indexed admin); FeeCollected event FeeCollected(address indexed creator, address indexed token, uint256 amount); FarmImplementationUpdated event FarmImplementationUpdated(address indexed newFarmImplementation, string newFarmId); 226Errors InvalidAddress error InvalidAddress(); NewFarmImplementationSameAsOld error NewFarmImplementationSameAsOld(); 227Features Features contracts can be considered as plugins, which are used in farms only where they are needed. 228ClaimableFee Git Source Inherits: Farm Author: Sperax Foundation. Farms build for pairs/ pools in which fee can be claimed can extend and override _claimPoolFee function of this contract. Functions claimPoolFee A function to claim the pool fee earned by lp. Only the deposit owner can call this function. function claimPoolFee(uint256 _depositId) external nonReentrant; Parameters Name Type Description _depositId uint256 ID of the deposit. _claimPoolFee Claim pool fee internal logic to be implemented by child farm contract. Just override this function and write the logic to claim fee, validation and other checks are handled in claimPoolFee . 229function _claimPoolFee(uint256 _depositId) internal virtual returns (uint256 tokenId, uint256 amt0Recv, uint256 amt1Recv); Parameters Name Type Description Deposit ID of the deposit in _depositId uint256 the farm. Returns Name Type Description Token ID of the deposit for tokenId uint256 E721 farms, for other farms return depositId. amt0Recv uint256 Amount 0 received as fee. amt1Recv uint256 Amount 1 received as fee. Events PoolFeeCollected event PoolFeeCollected(address indexed recipient, uint256 tokenId, uint256 amt0Recv, uint256 amt1Recv); Errors 230NoFeeToClaim error NoFeeToClaim(); 231ExpirableFarm Git Source Inherits: Farm Author: Sperax Foundation. This contract helps in creating farms with expiry feature. State Variables MIN_EXTENSION uint256 public constant MIN_EXTENSION = 100; MAX_EXTENSION uint256 public constant MAX_EXTENSION = 300; farmEndTime uint256 public farmEndTime; farmRegistry address public farmRegistry; 232Functions extendFarmDuration Update the farm end time. Can be updated only before the farm expired or closed. Extension should be incremented in multiples of 1 USDs/day with minimum of 100 days at a time and a maximum of 300 days. Extension is possible only after farm started. function extendFarmDuration(uint256 _extensionDays) external onlyOwner nonReentrant; Parameters Name Type Description The number of days to _extensionDays uint256 extend the farm. Example: 150 means 150 days. updateFarmStartTime Update the farm start time. Can be updated only before the farm start. New start time should be in future. Adjusts the farm end time accordingly. function updateFarmStartTime(uint256 _newStartTime) public virtual override; Parameters 233Name Type Description _newStartTime uint256 The new farm start time. isFarmOpen Returns bool status if farm is open. Farm is open if it is not closed and not expired. function isFarmOpen() public view virtual override returns (bool); Returns Name Type Description bool bool True if farm is open. _setupFarmExpiry Setup the farm data for farm expiry. function _setupFarmExpiry(uint256 _farmStartTime, address _farmRegistry) internal; Parameters Name Type Description _farmStartTime uint256 Start time of the farm. Address of the farm _farmRegistry address registry. _collectExtensionFee 234Collects farm extension fee and transfers it to feeReceiver. Function fetches all the fee params from farmRegistry. function _collectExtensionFee(uint256 _extensionDays) private; Parameters Name Type Description The number of days to _extensionDays uint256 extend the farm. Example: 150 means 150 days. Events FarmEndTimeUpdated event FarmEndTimeUpdated(uint256 newEndTime); ExtensionFeeCollected event ExtensionFeeCollected(address indexed token, uint256 extensionFee); Errors InvalidExtension 235error InvalidExtension(); DurationExceeded error DurationExceeded(); FarmNotYetStarted error FarmNotYetStarted(); 236OperableDeposit Git Source Inherits: Farm Author: Sperax Foundation. This contract helps in creating farms with increase/decrease deposit functionality. Functions _updateSubscriptionForIncrease Update subscription data of a deposit for increase in liquidity. function _updateSubscriptionForIncrease(uint256 _depositId, uint256 _amount) internal; Parameters Name Type Description Unique deposit id for the _depositId uint256 deposit. _amount uint256 _amount to be increased. _updateSubscriptionForDecrease Update subscription data of a deposit after decrease in liquidity. 237function _updateSubscriptionForDecrease(uint256 _depositId, uint256 _amount) internal; Parameters Name Type Description Unique deposit id for the _depositId uint256 deposit _amount uint256 _amount to be decreased. _increaseDeposit Common logic for increasing a deposit. function _increaseDeposit(uint256 _depositId, uint256 _amount) internal; Parameters Name Type Description Unique deposit id for the _depositId uint256 deposit _amount uint256 _amount to be decreased. _decreaseDeposit Common logic for decreasing a deposit. function _decreaseDeposit(uint256 _depositId, uint256 _amount) internal; Parameters 238Name Type Description Unique deposit id for the _depositId uint256 deposit _amount uint256 _amount to be decreased. Events DepositIncreased event DepositIncreased(uint256 indexed depositId, uint256 liquidity); DepositDecreased event DepositDecreased(uint256 indexed depositId, uint256 liquidity); Errors DecreaseDepositNotPermitted error DecreaseDepositNotPermitted(); InsufficientLiquidity error InsufficientLiquidity(); 239Rewarder Rewarder is a contract which can be used by farm admins when they want to emit rewards in fixed APR instead of fixed token amounts (by setting reward rate). 240Rewarder Git Source Inherits: IRewarder, OwnableUpgradeable, ReentrancyGuardUpgradeable Author: Sperax Foundation. This contract tracks farms, their APR and other data for a specific reward token. Farms for UniV3 pools using Rewarder contract must have a minimum observationCardinality of 20. It can be updated by calling increaseObservationCardinalityNext function on the pool. State Variables MAX_PERCENTAGE uint256 public constant MAX_PERCENTAGE = 1e4; APR_PRECISION uint256 public constant APR_PRECISION = 1e8; REWARD_PERIOD uint256 public constant REWARD_PERIOD = 1 weeks; DENOMINATOR 241uint256 public constant DENOMINATOR = 100; ONE_YEAR uint256 public constant ONE_YEAR = 365 days; REWARD_TOKEN address public REWARD_TOKEN; REWARD_TOKEN_DECIMALS uint8 public REWARD_TOKEN_DECIMALS; totalRewardRate uint256 public totalRewardRate; rewarderFactory address public rewarderFactory; calibrationRestricted mapping(address => bool) public calibrationRestricted; 242farmRewardConfigs mapping(address => FarmRewardConfig) internal farmRewardConfigs; _decimals mapping(address => uint8) private _decimals; Functions constructor constructor(); initialize Initializer function of this contract. function initialize(address _rwdToken, address _oracle, address _admin) external initializer; Parameters 243Name Type Description Address of the reward _rwdToken address token. Address of the USDs _oracle address Master Price Oracle. Admin/ deployer of this _admin address contract. calibrateReward Function to calibrate rewards for this rewarder''s reward token for a farm. Calculates based on APR, caps based on maxRewardPerSec or balance rewards, sends and sets the rewardRate in the farm. function calibrateReward(address _farm) external nonReentrant returns (uint256 rewardsToSend); Parameters Name Type Description Address of the farm for _farm address which the rewards are to be calibrated. Returns Name Type Description Rewards which are sent to rewardsToSend uint256 the farm. updateTokenManagerOfFarm 244Function to update the token manager''s address in the farm. function updateTokenManagerOfFarm(address _farm, address _newManager) external onlyOwner; Parameters Name Type Description Farm''s address in which the _farm address token manager is to be updated. Address of the new token _newManager address manager. recoverRewardFundsOfFarm Function to recover reward funds from the farm. function recoverRewardFundsOfFarm(address _farm, uint256 _amount) external onlyOwner; Parameters Name Type Description Farm''s address from which _farm address reward funds is to be recovered. Amount which is to be _amount uint256 recovered. updateAPR Function to update APR. 245function updateAPR(address _farm, uint256 _apr) external onlyOwner nonReentrant; Parameters Name Type Description _farm address Address of the farm. _apr uint256 APR in 1e8 precision. toggleCalibrationRestriction Function to toggle calibration restriction. function toggleCalibrationRestriction(address _farm) external onlyOwner; Parameters Name Type Description Address of farm for which _farm address calibration restriction is to be toggled. recoverERC20 Function to recover ERC20 tokens from this contract. function recoverERC20(address _token, uint256 _amount) external onlyOwner; Parameters 246Name Type Description _token address Address of the token. _amount uint256 Amount of the tokens. getTokenAmounts Function to get token amounts value of underlying pool of the farm. function getTokenAmounts(address _farm) external view returns (address[] memory, uint256[] memory); Parameters Name Type Description _farm address Address of the farm. Returns Name Type Description address[] Array of token addresses. uint256[] getFarmRewardConfig Function to get reward config for a farm. function getFarmRewardConfig(address _farm) external view returns (FarmRewardConfig memory); Parameters 247Name Type Description _farm address Address of the farm. Returns Name Type Description FarmRewardConfig Farm FarmRewardConfig reward config. rewardsEndTime Function to calculate the time till which rewards are there for an LP. function rewardsEndTime(address _farm) external view returns (uint256 rewardsEndingOn); Parameters Name Type Description Address of the farm for _farm address which the end time is to be calculated. Returns Name Type Description Timestamp in seconds till rewardsEndingOn uint256 which the rewards are there in farm and in rewarder. updateRewardConfig 248Function to update the REWARD_TOKEN configuration. This function calibrates reward so token manager must be updated to address of this contract in the farm. function updateRewardConfig(address _farm, FarmRewardConfigInput memory _rewardConfig) public onlyOwner nonReentrant; Parameters Name Type Description Address of the farm for _farm address which the config is to be updated. The config which is to be _rewardConfig FarmRewardConfigInput set. _initialize Internal initialize function. function _initialize(address _rwdToken, address _oracle, address _admin, address _rewarderFactory) internal; Parameters Name Type Description Address of the reward _rwdToken address token. Address of the USDs _oracle address Master Price Oracle. Admin/ deployer of this _admin address contract. Address of Rewarder _rewarderFactory address factory contract. 249_isConfigured Function to check if the farm''s reward is configured. function _isConfigured(address _farm) internal view; Parameters Name Type Description _farm address Address of the farm. _getTokenAmounts An internal function to get token amounts for the farm. function _getTokenAmounts(address _farm) internal view virtual returns (address[] memory, uint256[] memory); Parameters Name Type Description _farm address Address of the farm. Returns Name Type Description address[] Array of token addresses. uint256[] Array of token amounts. _hasRewardToken 250Function to check if the reward token of this contract is one of farm''s reward token. function _hasRewardToken(address _farm) internal view virtual returns (bool); Parameters Name Type Description _farm address Address of the farm. Returns Name Type Description If farm has one of the bool reward token as reward token of this contract. _validateNonZeroAddr Validate address. function _validateNonZeroAddr(address _addr) internal pure; Parameters Name Type Description _addr address Address to be validated. _calibrateReward 251function _calibrateReward(address _farm) private returns (uint256 rewardsToSend); _setRewardRate Function to set reward rate in the farm. function _setRewardRate(address _farm, uint128 _rwdRate, uint256 _nonLockupRewardPer) private; Parameters Name Type Description _farm address Address of the farm. Reward per second to be _rwdRate uint128 emitted. Reward percentage to be _nonLockupRewardPer uint256 allocated to no lockup fund. _adjustGlobalRewardRate Function to adjust global rewards per second emitted for a reward token. function _adjustGlobalRewardRate(uint256 _oldRewardRate, uint256 _newRewardRate) private; Parameters Name Type Description _oldRewardRate uint256 Old emission rate. _newRewardRate uint256 New emission rate. 252_isValidFarm Function to validate farm. It checks that the farm should implement getTokenAmounts and have REWARD_TOKEN. as one of the reward tokens. function _isValidFarm(address _farm, address[] memory _baseTokens) private returns (bool); Parameters Name Type Description Address of the farm to be _farm address validated. _baseTokens address[] Array of base tokens. Returns Name Type Description bool bool True if farm is valid. _hasBaseTokens Function to check whether the base tokens are a subset of farm''s assets. It handles repeated base tokens as well and pushes indexed in farmRewardConfigs. function _hasBaseTokens(address _farm, address[] memory _baseTokens) private returns (bool); Parameters 253Name Type Description _farm address Address of the farm. Array of base token _baseTokens address[] addresses to be considered for value calculation. Returns Name Type Description hasBaseTokens True if baseTokens are non bool redundant and are a subset of assets. _normalizeAmount Function to normalize asset amounts to be of precision REWARD_TOKEN_DECIMALS. function _normalizeAmount(address _token, uint256 _amount) private view returns (uint256); Parameters Name Type Description _token address Address of the asset token. _amount uint256 Amount of the token. Returns 254Name Type Description Normalized amount of the uint256 token in _desiredPrecision. _getPrice Function to fetch and get the price of a token. function _getPrice(address _token, address _oracle) private view returns (IOracle.PriceData memory priceData); Parameters Name Type Description Token for which the the _token address price is to be fetched. Address of the oracle _oracle address contract. Returns Name Type Description priceData IOracle.PriceData Price data of the token. _validatePriceFeed Function to validate price feed. function _validatePriceFeed(address _token, address _oracle) private view; Parameters 255Name Type Description _token address Token to be validated. _oracle address Address of the oracle. _validateRewardPer Function to validate the no lockup fund''s reward percentage. function _validateRewardPer(uint256 _percentage) private pure; Parameters Name Type Description No lockup fund''s reward _percentage uint256 percentage to be validated. 256RewarderFactory Git Source Inherits: IRewarderFactory, Ownable Author: Sperax Foundation. This contract deploys new rewarders and keeps track of them. State Variables oracle address public oracle; rewarderImplementation address public rewarderImplementation; Functions constructor Constructor. constructor(address _oracle) Ownable(msg.sender); Parameters 257Name Type Description Address of the master price _oracle address oracle of USDs. deployRewarder Function to deploy new rewarder. function deployRewarder(address _rwdToken) external returns (address rewarder); Parameters Name Type Description Address of the reward _rwdToken address token for which the rewarder is to be deployed. Returns Name Type Description rewarder address Rewarder''s address. updateRewarderImplementation Update rewarder implementation''s address function updateRewarderImplementation(address _newRewarderImplementation) external onlyOwner; Parameters 258Name Type Description _newRewarderImplementati New Rewarder address on Implementation updateOracle Function to update the oracle''s address. function updateOracle(address _newOracle) public onlyOwner; Parameters Name Type Description _newOracle address Address of the new oracle. _validateNonZeroAddr Validate address. function _validateNonZeroAddr(address _addr) private pure; Parameters Name Type Description _addr address Address to be validated. 259Deployed contracts Name Explorer link https://arbiscan.io/address/0x45bC6B4410 FarmRegistry 7837E7aBB21E2CaCbe7612Fce222e0 https://arbiscan.io/address/0x926477bAF6 RewarderFactory 0C25857419CC9Bf52E914881E1bDD3 https://arbiscan.io/address/0x212208daF12 CamelotV3Deployer D7612e65fb39eE9a07172b08226B8 260Getting Started on Our DApp Visit our dApp to Mint, Redeem and Farm. Go through this YouTube Playlist for step-by-step tutorials on how to use and navigate the Sperax ecosystem. 261Minting & Redeeming USDs Users can mint USDs from their collateral (USDC.e, USDC or USDT). The Mint page will automatically reflect the amount of collateral and SPA required to mint USDs (Currently, no SPA required). Users can redeem USDs for a collateral of their choice. Minting USDs • Make sure your wallet holds eligible collateral (USDC, USDT or USDC.e). • Go to the dApp homepage and connect your wallet. • Select the collateral and enter the amount of collateral that you want to deposit. • View the Latest Auto-Yield APY before proceeding. • The app will automatically show how much USDs you can mint. Then click on ‘Mint USDsʼ and review ‘Max Slippage .̓ • Then, you have to approve 2 transactions on your wallet - Approve and Sell USDC - after clicking ‘Confirm .̓ • You can now view your USDs balance in your wallet. 🚀 How to Mint USDs with Stablecoins (USDC, USDT & USDC.e) 262Redeeming USDs 1. Go to the dApp homepage and connect your wallet. 2. Select the Redeem tab and enter the amount of USDs you would like to redeem. 3. You can also select the maximum slippage for the transaction and then select the token in which you want to redeem. You can view the redemption fee and redemption amount as well. 4. Now, click on ''Redeem USDs'' to redeem. 5. Click on ''Confirm'' and unlock USDs transfer by providing required consent. 6. Then approve the transaction and your USDs will be redeemed succesfully. 🎥 How to Redeem USDs on Sperax dApp | Step-by-Step Guide 263Staking SPA Stake | Sperax USD - 1 April 2022 0 2 min 299 views Powered by 1.2× 1 min 49 sec⚡ 1 min 30 sec Stake SPA 1. Visit the stake page . 2. Choose amount of SPA you want to stake, lockup period and you can check veSPA balance corresponding to that. 3. Click on Stake to stake your SPA. 4. Once the transaction is processed, you can see your • veSPA balance • SPA staked • Expiry date • Button to extend lockup period Extend Lockup for staked SPA 2641. Click on the ''Extend Lockup'' button. 2. Select how much you want to extend your lockup period. You can see the updated expiry date and veSPA balance. 3. Click on Extend Lockup. Increase Staked Balance 1. Select additional amount of SPA tokens you want to stake. 2. You can see veSPA balance corresponding the same lockup as your existing veSPA balance. 3. Click on Stake to stake additional amount of SPA. The new veSPA balance will also expire at the same time as the previous balance. 265Governance veSPA holders will deliberate on protocol governance Sperax has launched its off-chain governance process. On-chain governance protocol will be launched next. Through governance, community can make changes to the USDs protocol parameters, bring in new partnerships, new yield opportunities etc. Sperax Off-Chain Governance Process: The Sperax DAO governance process primarily utilizes the Sperax DAO Governance Forum . In order for a proposal to be accepted, it must go through the following phases: Phase 0: Casual Ideation (Discord): If you have an idea youʼd like to share, please feel free to post it in #DAO- discussion channel on Sperax Discord, or, if you prefer to submit a proposal, you can use the SIP Template and submit your concept in the Proposal (Active Discussion) channel. Ideation Flow: 1. Start a conversation in the official governance channel in Discord. 2. Gather feedback from the community. 3. (Optional) Create a poll on discord to gauge community sentiment. 4. (Optional) If youʼd like to talk about your idea on a Sperax Community Call, feel free to contact a team member via Discord to coordinate. Phase 1: Governance Proposal (On Forum) 266If you are ready to submit a formal proposal, you may do so on the Proposal (Active Discussion) channel using the SIP Template . Here youʼll begin to receive constructive feedback from the community as well as the Sperax team. Discussion will continue for a minimum of 48 hours. Make sure to add the correct tag to the proposal(see below for definitions): 1. USDs parameter: Proposals related to adjusting/modifying USDs components. 2. New Assets and Yield Strategies: Proposals related to adding new forms of collateral and yield strategy schemes/methodologies. 3. Liquidity Mining: Proposals related to the improvement of Sperax farming & liquidity mining mechanisms. 4. Product Feature: Proposals related to the improvement, addition, or modification of new & existing Sperax products. 5. Partnership(s): Proposals related to inquiring & establishing potential partnerships with the Sperax Protocol. 6. Other: Miscellaneous proposals that have yet to be assigned a defined tag/category. How to Vote on Sperax DAO Proposals using veSPA! Tutorial for using Sperax Forum Phase 2: Snapshot vote: 267Once a proposal has gained traction, a snapshot poll will be created for voting. A Moderator will proceed to create the snapshot poll, link it to the corresponding forum post, and submit it on the Snapshot Voting channel on Forum. Votes can be cast directly through Sperax Snapshot Space . All snapshot polls will last 3 days upon initiation. Votes will be weighed by the voters'' veSPA balance. A snapshot poll will include 2 vote options (Yes/ No) by default. If proposal creator wants 3 vote options (Yes/ No/ Yes with modification) then they can inform that to the moderators while snapshot poll is created. If ''Yes with modification'' option gets max votes, then the proposal is not subject to cooldown period. Proposal Passing Criteria: 1. Acceptance Threshold: Proposal must receive more than 50% in "Yes" votes 2. Minimum Quorum: At least 200 Million veSPA should vote in the snapshot poll Possible outcomes at this stage: 1. Both the proposal passing criteria are met: the proposal passes and escalates to a Sperax Improvement Plan (SIP). 2. Quorum is not met: The proposal does not meet minimum quorum and is marked “Defeated” by the moderators. The proposal undergoes a 7-day cooldown period. At the conclusion of this period, the proposer can resubmit the proposal and proceed with the governance process. 3. Quorum is met but does not receive more than 50% in ''Yes'' votes: The post is marked as ‘Defeatedʼ by the moderators. • If “No” votes > “Yes with modification” votes - The proposal must then undergo a 7-day cooldown period. Afterward, the proposer must then resubmit the proposal, along with any necessary modifications, and proceed with the governance process. • If “Yes with modification” votes >= “No” votes - The proposal can be returned to the deliberation phase for modifications and is not subject to the 7-day cooldown period. Once modifications are made to the proposal it can be elevated to a Snapshot vote once again. 268Phase 3: Sperax Improvement Plan (SIP): When a governance proposal passes the snapshot vote in the previous stage, the proposal moves to Sperax Improvement Plan. This will have the list of all accepted proposals. The Sperax engineering team will pick up proposals from SIP for implementation based on their priorities and bandwidth. Community can also help write the code for implementing proposals from SIP. All codes will have to be audited before implementation. Sperax Foundation will help in facilitating the audits. Check the repository of all the winning proposals Approved SIPs Sperax DAO Track the implementation of these proposals Sperax Improvement Plan – Asana Asana 269Bug Bounty Program Introduction The Security of Speraxʼs USDs and Demeter users is paramount. The engineering team and our auditors have invested significant time and resources to ensure that USDs and Demeter are secure and dependable. The USDs and Demeter smart contracts are publicly verifiable. The details and statistics of circulating supply, underlying collateral, collateral strategies, Farms etc are publicly available. On 1st March 2024 we are launching our bug bounty program. Security researchers, fulfilling the eligibility criteria as mentioned in this document, are eligible for a bug bounty for reporting undiscovered vulnerabilities. The Program aims to incentivize responsible disclosure and enhance the security of the USDs protocol and Demeter. Bug Bounty Program Security is one of our core values. We value the input of hackers acting in good faith to help us maintain the highest standard for the security and safety of the Sperax ecosystem. The USDs protocol and Demeter, while it has gone through a professional audit, depends on new technology that may contain undiscovered vulnerabilities. The Sperax team encourages the community to audit our contracts and security. We also encourage the responsible disclosure of any issues. This program is intended to recognize the value of working with the community of independent security researchers. It sets out our definition of good faith in the context of finding and reporting vulnerabilities, as well as, what you can expect from us in return. Scope 270The Program includes the vulnerabilities and bugs in the USDs protocol core repository (located in the GitHub repositories, primarily at: https://github.com/Sperax/USDs-v2/tree/main/contracts and https://github.com/Sperax/Demeter-Protocol-Contracts . This list may change as new contracts are deployed or existing contracts are removed from usage. The following are not within the scope of the Program: 1. Bugs in any third-party contract or platform that interacts with USDs protocol; 2. Vulnerabilities related to domains, DNS, or servers of websites; 3. Vulnerabilities already reported or discovered in contracts built by third parties on USDs; 4. Any already-reported bugs or other vulnerabilities. 5. Test contracts and staging servers unless the discovered vulnerability also affects the USDs Protocol or could otherwise be exploited in a way that risks user funds. Disclosure A researcher needs to submit all bug bounty disclosures to here . The disclosure must include clear and concise steps to reproduce the discovered vulnerability in written or video format. The Sperax team will follow up promptly with acknowledgment of the disclosure. Terms and Conditions To be eligible for a reward under this Program, you must: 271• Discover a previously unreported, non-public vulnerability within the scope of this Program. Vulnerabilities must be distinct from the issues covered in the previously conducted publicly available audits. • Include sufficient detail in your disclosure to enable our engineers to quickly reproduce, understand, and fix the vulnerability. • Be the first to disclose the unique vulnerability to the Team by the disclosure requirements below. If similar vulnerabilities are reported, the first submission shall be rewarded (if determined valid and otherwise in the scope of this Program) • Be reporting in an individual capacity, or if employed by a company, reporting with the companyʼs written approval to submit a disclosure to Sperax • Not be a current or former Sperax team member, vendor, contractor, or employee of a SperaxDAO vendor or contractor. • Not be subject to any international, national, or state-level sanctions. • Be at least 18 years of age or, if younger, submit your vulnerability with the consent of your parent or guardian. • Not exploit the vulnerability in any way, including by making it public or obtaining a profit (other than a reward under this Program). Any publicity in any way, whether direct or indirect, relating to any bug or vulnerability will automatically disqualify it and you from the Program. To encourage vulnerability research and to avoid any confusion between good-faith hacking and malicious attacks, we require that you: 272• Play by the rules, including following the terms and conditions of this program and any other relevant agreements. If there is any inconsistency between this program and any other relevant agreements, the terms of this program will prevail. • Report any vulnerability youʼve discovered promptly. • Make a good faith effort to avoid privacy violations, data destruction, harming user experience, interruption, or degradation of the Sperax ecosystem and services. • Use only the google form to submit vulnerabilities with us. • Keep the details of any discovered vulnerabilities confidential until they are fixed. • Perform testing only on in-scope systems, and respect systems and activities which are out-of-scope. • Not submit a separate vulnerability caused by an underlying issue that is the same as an issue on which a reward has been paid under this Program. • Only interact with accounts you own or with explicit permission from the account holder. • Not engage in any unlawful conduct when disclosing the bug, including through threats, demands, or any other coercive tactics. When working with us according to this program, you can expect us to: 273• Pay generous rewards for eligible discoveries based on the severity and exploitability of the discovery, at The Sperax team''s sole discretion • Extend Safe Harbor for your vulnerability research related to this program, meaning we will not threaten or bring any legal action against anyone who makes a good faith effort to comply with our bug bounty program. • Work with you to understand and validate your report, including a timely initial response to the submission. • Work to remediate discovered vulnerabilities promptly. • Recognize your contribution to improving our security if you are the first to report a unique vulnerability, and your report triggers a code or configuration change. • All reward determinations, including eligibility and payment amount, are made at Speraxʼs sole discretion. The Sperax team reserves the right to reject submissions and alter the terms and conditions of this program. Rewards Sperax Treasury offers rewards for discoveries that can prevent the loss of assets, the freezing of assets, or harm to a user, commensurate with the severity and exploitability of the vulnerability. Sperax Treasury will pay a reward of $500 to $15,000 for eligible discoveries according to the terms and conditions provided below. The Team evaluates all submissions on a case-by-case basis. Rewards are allocated based on the severity of the issue, and other variables, including, but not limited to a) the quality of the issue description, b) the instructions for reproducibility, and c) the quality of the fix (if included). A detailed report of a vulnerability increases the likelihood of a reward and may increase the reward amount. Therefore, please provide as much information about the vulnerability as possible. The Program intends to follow a similar approach as the Ethereum Bug Bounty, where the severity of the issues will be based according to the OWASP risk rating model based on “Impact” and “Likelihood”. The evaluation of scoring is however at the sole discretion of the Sperax Team. 274All rewards are paid in SPA and xSPA tokens with a 50-50 split (15-day TWAP) via a transfer to the wallet address provided by the participant to the Team. As a condition of participating in this Program, the participants give the Sperax Team permission to share their wallet addresses and other information provided by them to third parties to administer this Program and comply with applicable laws, regulations, and rules. The reward will be received in SPA token based on the following severity scheme: • Note = Up to 100 US dollars • Very low = Up to 500 US dollars • Low = Up to 1,000 US dollars • Medium = Up to 2,500 US dollars • High = Up to 5,000 US dollars • Very High = Up to 10,000 US dollars • Critical = Up to 15,000 US dollars Likelihood/ Very Low Low Moderate High Critical Severity Almost $1000 $2500 $5000 $10000 $15000 certain Likely $500 $1000 $2500 $5000 $10000 Possible $100 $500 $1000 $2500 $5000 Unlikely $100 $100 $500 $1000 $2500 Almost $100 $100 $100 $500 $1000 Possible Other terms The decisions made regarding rewards are final and binding. 275By submitting your report, you grant the Company all rights, including without limitation intellectual property rights, needed to validate, mitigate, and disclose the vulnerability. All reward decisions, including eligibility for and amounts of the rewards and how such rewards will be paid, are made at the Company''s sole discretion. Terms and conditions of the Program may be altered at any time. The company may change or cancel this Program at any time, for any reason. 276FAQ 277SPA Tokenomics Token distribution schedule, details of tokens held by the foundation and community treasury SPA Circulating Supply Calculation Logic Circulating Supply = Total Supply of SPA on Ethereum + Total Supply of SPA on Arbitrum + Total Supply of wSPA on Ethereum - wSPA locked on Arbitrum bridge - SPA balance held by major wallets - SPA locked in SPA Staking Protocol Important contract addresses: SPA token address (Ethereum): 0xb4a3b0faf0ab53df58001804dda5bfc6a3d59008 SPA token address (Arbitrum): 0x5575552988A3A80504bBaeB1311674fCFd40aD4B wSPA (wrapped SPA) token address (Ethereum): 0x2a95FE4c7e64e09856989F9eA0b57B9AB5f770CB Arbitrum bridge where wSPA is locked (Ethereum): 0xcEe284F754E854890e311e3280b767F80797180d Major wallets with SPA tokens 278Name Address Vesting & Initial % of Initial Purpose Allocation Supply This vests linearly over a 0x4a692fD139 4-year time- 259a5b94Cad lock, beginning 7753E3C9635 from the 0b7F2B9f launch date of 0xBA6ca0B9e governance 7333f5e66781 protocol. The Treasury 6b85704c024 DAO will 1,250,000,000 25% AB250C9D control this fund and utilize 0x8898A38Eb for future 8E3104f7c986 partnerships, 22b55260E014 marketing B3a0217 incentives, liquidity mining rewards, etc. 279Foundation funds are being used to make initial markets and for protocol development. Since the foundation lends the tokens to third parties for market making 0xD95791bcab activities, the 484C0552833 actual token cB558d18d4D3 balance held in F198AF9 the foundation wallet will 0xb56e5620A fluctuate. 79cfe59aF7c0 Foundation FcaE95aADbE Foundation has burnt 1,250,783,000 25.02% A8ac32A1 375MM SPA in 2022 to 0xC6e00e0E3 further 544C93460cd decentralise Fb53E85C452 the protocol - 8EF348265 250MM SPA in May and 125MM SPA in September. 0xC6e00e0E3 544C93460cd Fb53E85C452 8EF348265 is an operator wallet which is sometimes used to move tokens from Layer 1 to Layer 2. 280This will be 0x8B65CE3b4 used to Eaa89583460 provide 96C3a9303b7 rewards for 3f2012aCc liquidity mining Bootstrap during protocol 500,000,000 10% Liquidity 0xAF64e027D genesis. Future 42bAc1C14277 liquidity mining fd295De9Ae31 rewards will be 8eEF17E funded from treasury funds This will be used to reward users who stake $SPA in 0xCD1B1ce6ce the staking 877a9315E73E protocol. 2E4Ba3137228 Stakers will Staking 068A59 earn fees from 500,000,000 10% Rewards 0x3702E3e2D the minting B2b5d037c1db and redeeming B23Ab7A51d0 of $USDs, as Cc90BD0e well as staking rewards from the allocated rewards budget. This vests linearly over 4 years with a 6 month cliff. Token unlock schedule will 0xE10b88d70b start from Team & 01b956782Dc9 4/1/2022 for 499,217,000 9.98% Advisor 8d7D4f3a931F existing team F59Fc7 members. For new members, vesting would start at least three months from the day they join. 281All SPA tokens have been 0x2Fc8d8BCf4 vested under a b2c0fc659447 strict vesting Private Sale 750,000,000 15% 5E44c473AC3 schedule of 1 E844B6a year starting from 9/18/2021. Staking Protocol Related Title Chain Contract / Wallet Address 0xbF82a3212e13b2d407D1 veSPA L1 Ethereum 0f5107b5C8404dE7F403 0x2e2071180682Ce6C247B veSPA L2 Arbitrum 1eF93d382D509F5F6A17 0xa61DD4480BE2582283Af RewardDistributor L1 Ethereum a54E461A1d3643b36040 0x2c07bc934974BbF413a4 RewardDistributor L2 Arbitrum a4CeDA98713DCb8d9e16 0x3702E3e2DB2b5d037c1d Staking Reward Arbitrum bB23Ab7A51d0Cc90BD0e Bootstrap Liquidity Related Tokens from Bootstrap Liquidity have been moved to a lot of Farm rewarder contracts and deployer wallet addresses for operational reasons. Please find below the list of those addresses. We will try our best to keep this list constantly updated. 282Title Chain Contract / Wallet Address 0x8B65CE3b4Eaa8958346 Bootstrap Liquidity Ethereum 096C3a9303b73f2012aCc 0xc28c6970D8A345988e8 Bootstrap liquidity deployer Ethereum and Arbitrum 335b1C229dEA3c802e0a6 USDs/USDC Farm Rewarder 0x1733c5bc884090C73D89 Arbitrum (SPA) 303467461693c54Ba58B SPA/USDs Farm Rewarder 1 0x136C218Ff8E87eD68f851 Arbitrum (SPA) 433685960819F06b1fE USDs/USDC Farm Vesting 0x638d76763dE9492b609 Arbitrum (SPA) b0d8830D8F626C5933A4D SPA/USDs Farm Vesting 0x03b35477cFD400dEdfAc Arbitrum (SPA) 06f40422491500cbc663 SPA/USDs Farm Rewarder 2 0x36033594EC23E0f0B187 Arbitrum (SPA) f767889Eb4C539F4aE03 SPA/USDs Farm Vesting 2 0xC0F0484a216AfF20E0ea Arbitrum (SPA) d1a1513cE40fe0AFe0fe 0xb56e5620A79cfe59aF7c SPA-Reserve-L2 multi-sig Arbitrum 0FcaE95aADbEA8ac32A1 0xc150cbdDC5932258fAc7 SPA Farm Arbitrum 68bEB4d2352D127039fd 0x852afF031bb282C054B2 SPA Farm rewarder Arbitrum 6628A799D7F3a896873e 0xAF64e027D42bAc1C1427 Bootstrap liquidity Arbitrum Arbitrum 7fd295De9Ae318eEF17E SPA Buyback 283The SPA that is bought back from the open markets using 30% of the auto-yield and 100% of the fees is stored in : 0xA61a0719e9714c95345e89a2f1C83Fae6f5745ef (Arbitrum One) SPA circulating supply sheet which is in Beta. It can be used to view the circulating supply breakdown for the token. 284xSPA token xSPA is a reward token of the Sperax ecosystem. xSPA can be either staked for veSPA with a lockup of 180 days or more, or redeemed within 15 to 180 days giving 50% to 100% SPA upon redemption. Summary / Abstract Earlier in 2023, SperaxDAO decided on the SPA budget for emission through Gauge (SIP-32 ) and redirecting veSPA emissions to bribes on Gauge (SIP-33 ). Every week, Gauge emits 2.9 M SPA and veSPA voters are bribed 383K SPA. This SPA should be used more prominently and assertively to drive USDs growth and bring new adoption. Motivation Since the start of SPA Gauge, USDs total supply has decreased from $2M to $1.78M. In the meantime, SPA Gauge has emitted about 80M SPA amounting to $392K. The SPA circulating supply has increased from 1.593B to 1.658B. Emitting SPA in a predetermined manner and without assessing the market dynamics is improper utilization of resources. Since our target is to increase USDs adoption which will help in growing the Sperax ecosystem and its participants, we must rethink our overall strategy. Overview Apart from maintaining the target to make SPA deflationary, SperaxDAO should ensure that the emission is invested back into the ecosystem and contributes directly towards increasing the USDs adoption and supply. 285All SPA emissions should have a looping effect such that a good portion of SPA distributed should be invested back into the ecosystem in the form of veSPA and increase the burning rate of SPA. Since the emissions are not helping in increasing either USDs adoption or the locked tokens. The recent increment in veSPA numbers is primarily driven by the team token allocation in veSPA. The Sperax ecosystem is steadily moving towards complete decentralization of protocols and hence should have more governance participation. The Sperax ecosystem should have a new reward token. A token which can be staked as veSPA or redeemed for SPA. After drawing some inspiration from the Camelot emission strategy, the core team proposes the launch of a new token called xSPA. Users can either stake 1 xSPA for one veSPA or redeem 1 xSPA for 1 SPA. Technical overview Users can redeem 1 xSPA token for SPA by depositing their xSPA token through the redemption contract. Users can stake 1 xSPA in veSPA to increase their staked SPA balance. The relation between xSPA, SPA, and veSPA will be governed by the following rules 286• 1 xSPA will be equivalent to 1 SPA. • Users can redeem 1 xSPA for 1 SPA if they lock the xSPA in the redemption contract for 180 days, the maximum redemption period. • Users can redeem 1 xSPA for 0.5 SPA if they lock the xSPA in the redemption contract of 15 days, the minimum redemption period. • If the redemption period is ‘x ,̓ between 15 and 180 days, the amount of SPA a user gets is governed by the following equation: Receivable spaAmount = (_xSpaAmount * (_redeemDuration + 150 days)) / 330 days • A redemption request cannot be modified or canceled. • The redemption contract will instantly burn any differential SPA ◦ In case of the minimum locking period of 15 days, half the SPA tokens will be burnt right away and users can claim their SPA tokens after 15 days. ◦ In case of a maximum locking period of 180 days, no SPA will be burnt and users can claim their SPA token after 180 days. ◦ In case of a period between 15 and 180 days, SPA burnt is: SpaBurnt = _xSpaAmount - Receivable spaAmount Users can claim their SPA tokens after the locking period. • Users can stake 1 xSPA token in the veSPA contract to increase their staked SPA balance by 1 SPA token for the existing lockup period if the lockup period is greater than 180 days. ◦ If the user has 0 staked balance, the system will throw an error and will ask the user to create a staked position with a minimum staking period of 180 days ◦ If the user has staked balance but the lockup is less than 180 days, the system will throw an error and ask the user to increase the locking period to a minimum of 180 days ◦ Users will be able to increase their staked position if the lockup period is above 180 days. • xSPA token is transferrable. • The staking and redemption criteria can be updated/modified through governance. 287How to get, deposit and redeem xSPA token. Getting xSPA from SPA: 1. Allow xSPA token contract to transfer SPA from your account by calling approve function on SPA token contract by passing spender as 0x0966E72256d6055145902F72F9D3B6a194B9cCc3 xSPAʼs address and amount as the desired amount of xSPA you would like to have. Note: To allow 1 SPA pass 1000000000000000000 i.e 1e18 as amount in the approve function. 2. Call this mint function on xSPA contract by passing the amount if you want to receive xSPA on your account or this mint function by passing the address of the receiver account and amount if you want to receive the xSPA on another account. 3. Check your xSPA balance by calling balanceOf function on by passing in your accountʼs address. Depositing xSPA for redeeming SPA: • The minimum redemption period is 15 days in which you will receive only 100/2 = 50 SPA after 15 days. • The maximum redemption period is 180 days in which you will receive all your 100 SPA back for your 100 xSPA. • If you select any period between 15 days to 180 days, the SPA amount redeemed would be calculated on pro rata basis between 50% to 100% of SPA for your xSPA. You can get this amount by calling getSpaforxSPA function on the xSPA contract by passing the xSPA amount with precision and your redeemDuration in seconds between 1296000 (15 days) to 15552000 (180 days) and it will return the SPA amount you will receive at the end of your redemption period. • To create a redemption request you can call createRedemptionRequest on the xSPA contract by passing the xSPA amount with precision and the redeem duration between the range specified above. It returns the redemption request ID which will be used to track and claim later. 288Redeeming xSPA for SPA: • Once you have created a redemption request in the above step, you can see and track your redemption request by calling redemptionRequests function on the xSPA contract by passing your redemption request ID, it returns the requesterʼs address, unlock time in unix epoch and spa amount which will be unlocked. • Once the current unix epoch time is more than the unlock time of the redemption request, you can call the redeemSPA function by passing in your redemption request ID if you want to receive the SPA tokens on your account otherwise you can call this function by passing an address of another account as receiver and your request ID to send the SPA to another account. Redeeming xSPA for veSPA: • For this redemption you must have an existing veSPA lock for at least 180 days or more, if you do not have, you can call createLock function on the veSPA contract by passing the amount, lock duration (minimum 180 days) and auto cooldown preference. • If you have an active veSPA lock for at least 180 days or more, you can call stakeXSpa function on the xSPA token contract and your veSPA balance would be increased immediately. 289Smart Contract Addresses USDs Token Address USDs L2 (Arbitrum) address: 0xD74f5255D557944cf7Dd0E45FF521520002D5748 USDs Contract Addresses: • SPA Token Addresses Arbitrum One: SPA L2 address: 0x5575552988A3A80504bBaeB1311674fCFd40aD4B Ethereum SPA L1 address: 0xB4A3B0Faf0Ab53df58001804DdA5Bfc6a3D59008 wSPA L1 address: 0x2a95FE4c7e64e09856989F9eA0b57B9AB5f770CB Binance Smart Chain SPA BSC address: 0x1A9Fd6eC3144Da3Dd6Ea13Ec1C25C58423a379b1 xSPA Contract Address xSPA L2 address: 0x0966E72256d6055145902F72F9D3B6a194B9cCc3 veSPA Contract Address 290Arbitrum One: Proxy contract deployed at: 0x2e2071180682Ce6C247B1eF93d382D509F5F6A17 Implementation contract deployed at: 0xD16f5343FDDD2DcF6A8791e302A204c13069D165 Ethereum: Proxy contract deployed at: 0xbF82a3212e13b2d407D10f5107b5C8404dE7F403 Implementation contract deployed at: 0xA3F8745548A98ee67545Abcb0Cc8ED3129b8fF8D 291How to Transfer SPA from Ethereum to Arbitrum Though all the Sperax and SPA functionality lives on Arbitrum now - it might happen that some early users still have SPA on the chain Sperax started with - Ethereum. From today''s perspective, SPA held on Ethereum can be used only for transfers between on-chain accounts or for depositing SPA to CEXs supporting SPA deposits on Ethereum. SPA on Ethereum can''t be used for staking, yield farming (Farms on Sperax DApp), or for voting on Snapshot. If you still have SPA on Ethereum Mainnet, you need to bridge it from Ethereum to Arbitrum in order to unlock SPA potential in DeFi. The Arbitrum bridge accepts a wrapped form of SPA (wSPA), so first of all you must wrap your SPA on Ethereum in order to bridge it. Donʼt worry, the value of your tokens will not change. Below you can find instructions for transferring SPA to Arbitrum. Please make sure you have some ETH in your wallet to manage gas fees when wrapping and bridging SPA. Please remember: wSPA has no other function except being an intermediary token for bridging. Please don''t try to send wSPA to any CEX or sell it - the transaction may fail or you can lose your tokens. Use wSPA only to bridge it to Arbitrum and get your SPA on Arbitrum. Additionally, if your tokens do not appear after swapping or bridging, make sure you manually add SPA token addresses to your wallet. Step 1: SPA (Ethereum Mainnet) → wSPA (Ethereum Mainnet) 2921. Open this link . 2. Connect to Ethereum mainnet using your wallet using the appropriate provider. 3. Import wSPA token into your wallet using this token address: 0x2a95FE4c7e64e09856989F9eA0b57B9AB5f770CB 4. Enter the amount of SPA you want to convert to wSPA. Note that 1 SPA = 1 wSPA. 5. Then click on ''Swap'', provide consent and send the transaction. 6. You can now see the wSPA tokens in your wallet. How to Convert SPA on Ethereum to wSPA Step 2: wSPA (Ethereum Mainnet) → SPA (Arbitrum) 2931. Navigate to Arbitrum Bridge . (Make sure that you are bridging from Ethereum to Arbitrum One.) 2. Connect your wallet using the appropriate provider. 3. Select wSPA token by pasting its token address: (0x2a95FE4c7e64e09856989F9eA0b57B9AB5f770CB) 4. Enter the amount of wSPA that you want to bridge. You will receive the same amount of SPA on Arbitrum. (Note that the website may show that you will receive wSPA, but be assured that you will receive SPA on Arbitrum. You can verify that using SPA Arbitrum''s token address: 0x5575552988A3A80504bBaeB1311674fCFd40aD4B) 5. Click on ''Move funds to Arbitrum One'' and then send the transaction to give permission to transfer the capped amount of wSPA. 6. Send the transaction now to bridge and wait for some time for the transcation to be completed successfully. 7. You can track the transaction status/history on the bridge page. In some time, you should see the SPA tokens on Arbitrum in your wallet. How to Convert wSPA to SPA on Arbitrum 294Quick Links 295">
To view the full page, please visit: SperaxOS Product Userguide

SperaxOS

AI powered financial operating system. Smart agents manage capital with programmable logic, optimizing yield, enabling payments across chains. USDs is a liquid, yield-bearing stablecoin, collateralized. SPA powers agent actions and value flows with programmable, intelligent finance across chains.
Buy now