Only this pageAll pages
Powered by GitBook
1 of 59

SperaxOS

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

FAQ

Loading...

Loading...

Loading...

Loading...

Quick Links

Smart contracts

High level documentation of smart contracts

Smart contracts

High level documentation of smart contracts

Camelot 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.

Base contracts

These contracts are base for all the other contracts and they have the common logic for functionalities like deposit, withdraw, createFarm, etc.

Rewarder

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).

E721 Farms

E721 farms include all the farms built for pools in which the liquidity provider has an NFT (ERC721) position.

Features

Features contracts can be considered as plugins, which are used in farms only where they are needed.

Sequence diagrams

Mint Flow
USDs Redeem Flow
Yield generation
Rebase Flow

Deployed contracts

Name
Explorer link

FarmRegistry

RewarderFactory

CamelotV3Deployer

Technical 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

Technical documents

This technical document is about the upgrade to USDs-v2. It details the changes made to enhance the protocol's decentralization, transparency, security, and scalability. The document covers new features and functionalities.

Smart Contracts can opt in for the rebase feature (Auto-yield).

Externally owned accounts (EOAs) holding USDs were always eligible for the Auto-yield feature, but smart contract / smart wallets were not.

Sperax has introduced a new function: now smart accounts (or smart wallets) can call to opt in for auto rebase in USDs contract and to get Auto-yield.

An owners of a smart contract / smart wallet can opt in for rebase in USDs contract. The users will not need to claim (or send out any transactions to claim) the interest earned.

This is how to opt in your smart contract / smart wallet for rebase in USDs:

  1. Go to Arbiscan and search for USDs. Verify the correct USDs contract address: 0xD74f5255D557944cf7Dd0E45FF521520002D5748

  2. Go to the Token contract. ensure it is Sperax USD (USDs) deployed by Sperax Deployer 0x42d2f9f84EeB86574aA4E9FCccfD74066d809600

Staking 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 , 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.

Getting Started on Our DApp

Visit our to Mint, Redeem and Farm.

Go through for step-by-step tutorials on how to use and navigate the Sperax ecosystem.

https://arbiscan.io/address/0x45bC6B44107837E7aBB21E2CaCbe7612Fce222e0
https://arbiscan.io/address/0x926477bAF60C25857419CC9Bf52E914881E1bDD3
https://arbiscan.io/address/0x212208daF12D7612e65fb39eE9a07172b08226B8

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.

  • 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:

    1. 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

    SIP-66
    dApp
    this YouTube Playlist

    Key parameters and functions

    Key parameters upgradeable via governance

    • Collateral types to mint/ redeem USDs.

    • Desired collateral composition, oracle used for price feed of the collateral, base Mint Fee, base Redemption Fee and price floor for each collateral type. Updating the price floor for collateral used to mint USDs. Setting the price floors for each collateral will allow the protocol to remain solvent when collaterals depeg. The protocol will not mint USDs with the collateral when the price of the collateral falls below the price floor.

    • Yield generation strategies for each collateral type. For example, adding new delta-neutral yield farming strategies based on other decentralized exchanges.

    • Harvesting incentive.

    • The USDs dripping rate from the dripper contract.

    • Minimum and maximum APR for distribution of Auto-Yield.

    Key functions available to the public

    • Harvesting or claiming all kinds of yield tokens from the yield generation strategies. The harvester will be incentivized with a portion of the yield-farmed tokens.

    • Purchase harvested yield tokens for market price. We will use the respective oracles for the yield tokens to determine the market price. This experience will be similar to the currently implemented buyback contract.

    • Distributing the auto-yield to all addresses eligible to collect it. Rebase will also be triggered when someone Mints or Redeems.

    Sperax 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.

    To 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.

    Withdrawing 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.

    Residual 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.

    Example

    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.

    Smart 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

    Arbitrum One:

    Proxy contract deployed at: 0x2e2071180682Ce6C247B1eF93d382D509F5F6A17

    Implementation contract deployed at: 0xD16f5343FDDD2DcF6A8791e302A204c13069D165

    Ethereum:

    Proxy contract deployed at: 0xbF82a3212e13b2d407D10f5107b5C8404dE7F403

    Implementation contract deployed at: 0xA3F8745548A98ee67545Abcb0Cc8ED3129b8fF8D

  • Click on the contract and make sure your account is connected (Metamask, Gnosis Safe, or any other smart account).

  • Go to “Write as Proxy”.

  • Connect your wallet (choose WalletConnect, Coinbase Wallet, or any other wallet).

  • Once connected, you will see two functions with the same name: rebaseOptIn You don’t have to call the first function because it’s an owner-only function (can be called only by the USDs owner). Instead, click the second function rebaseOptIn. This allows you to opt in for rebase for your own account (your smart contract account).

  • Confirm the transaction, and your smart account will be opted in for auto rebase.

  • That’s it. Your smart account / smart wallet is eligible for auto-yield!

    Auto Yield

    USDs earns organic yield for holders, and the yield is paid out in USDs using a public rebase function that can be executed using a button every 24 hours if the yield is more than 3%.

    USDs stands out, thanks to its inflation fighting auto-yield feature. USDs is novel in the stablecoin ecosystem, because it requires no action by the user. Users do not need to stake USDs nor spend gas to claim their yield. One simply holds USDs, and their wallet balance will grow.

    The collateral received for minting USDs is deployed in other audited DeFi projects to generate organic yield. USDs collateral earns yield in the form of reward tokens and swapping fees from the pools. This yield is shared between USDs holders and SPA stakers (The split is 70% for auto-yield and 30% for SPA buyback and burns. This share can be changed through governance).

    Gas free yield: 70% of yield generated on USDs collateral is used to mint USDs. This USDs is distributed to anyone who holds USDs in their wallet.

    The actual yield generated depends on the yield rate of the strategies where collateral is deployed. The yield cap has been set to 25%. Any yield generated over 25% is stored in the protocol to help fund the APY for lean periods when actual yield generated is less than 25%.

    Yield Strategies

    Different yield generation strategies are proposed by the community/team and evaluated by the community based on their yield generation potential, risk profile etc. Community then votes to choose pools/farms where collateral will be deployed. Following strategies are being used for yield generation. We will keep updating this list to include newer strategies approved by the community.

    • USDC:

      • Compound (USDC)

      • Aave (USDC)

      • Fluid (USDC)

      Maximum cap of 75% of total USDC collateral for each strategy.

    Old strategies that are not in use / deprecated:

    • Curve | Curve-LP USDC/USDT [Not in use]

    • Saddle | Saddle-LP FRAX/USDC [Saddle sunset]

    • Frax | Curve-LP FRAX/VST [Vesta sunset]

    Eligibility

    USDs held in EOA wallets, i.e. non-contract addresses, will receive auto-yield on a regular basis. For now, USDs that are deployed by users to yield-earning strategies within the Sperax suite of yield farms will not receive auto-yield.

    By default, smart contracts can opt for auto-yield via a public Rebase Function.

    Auto-yield distribution mechanism

    USDs follows the ERC20 token standard. A wallet holding USDs should expect its USDs balance to increase automatically over time. This increase is triggered by a distribution event that can be triggered whenever the yield is greater than 3%. On average, the event gets triggered approximately every 24 hours. The earned USDs are not explicitly transferred into the user’s wallet, instead a global parameter is changed to update the holder's balance.

    Unlike most ERC20, where the token contract directly stores the amount of tokens each wallet holds, USDs’ token contract has a shared state variable creditPerToken, and the contract stores each wallet’s credit. A wallet’s balance = credit / creditPerToken.

    When yield is generated:

    1. The yield is swapped for USDs in the open market

    2. The USDs from step (1) is burned

    3. The value of creditPerToken is *decreased globally and therefore every wallet’s balance increases (since every wallet’s credit is unchanged during this process)

    *creditPerToken is decreased according to the number of USDs burnt in step (2) such that total supply of USDs remains unchanged after steps (2) and (3)

    The circulating supply of USDs remains unchanged through this process as the USDs that are bought from the market are burnt and then the USDs balance of the holders increase proportionally. As a result, a user can expect its USDs balance to increase automatically over time without any additional USDs explicitly being transferred to the user’s wallet.

    Yield distribution frequency

    Yield is distributed approximately every 24 hours. The page has a rebase button, which can be triggered at a time gap of 24 hours, whenever the yield is greater than 3%.

    Stability Mechanism

    How does USDs keep the peg?

    1. Cap/Floor collateral value to $1 while minting/redeeming 1 USDs

    Minting 1 USDs - If a collateral token is worth less than $1, it will be treated with its actual market price. However, if it is worth more than 1 dollar, it will be treated as $1.

    Redeeming 1 USDs - If a collateral token is worth more than $1, it will be treated with its actual market price. However, if it is worth less than 1 dollar, it will be treated as 1 units of collateral at its market price.

    Hence, USDs will always be fully collateralized or over-collateralized. For more information on how mint and redeem functions work check out

    2. Redemption Fee

    The protocol collects a redemption fee whenever USDs is redeemed. This fee is passed on to the SPA stakers. Redemption fee is levied so that protocol does not work like a free token swap instrument. The fee is static for each collateral token but can be upgraded through governance by the community. It is a percentage of the transaction value.

    1. Mint Fee = 0

    2. Redemption Fee = x% of the amount of USDs redeemed.

    x depends on the selected collateral. Check the current redemption fee for the USDs in

    3. Pause minting USDs with de-pegged collateral

    If the collateral being used to mint USDs has de-pegged by 3% or more to the downside, then the protocol will stop minting USDs using that collateral.

    4. Auto-yield for USDs holders

    As more people use USDs, the yield rate will serve as a second-layer protection from a high selling pressure, further empowering a mass adoption in various use cases including payment, derivatives, and portfolio construction. For more information on how auto-yield works check out

    5. SPA is given on Redemption when sufficient collateral is not available

    When sufficient collateral is not available, USDs redeemers would get $1 worth of collateral in the form of SPA tokens from the treasury. This would be the collateral of last resort.

    FarmStorage

    Git Source

    Inherits: IFarm

    Author: Sperax Foundation.

    This contract contains the base storage variables for farms.

    State Variables

    COMMON_FUND_ID

    LOCKUP_FUND_ID

    PRECISION

    MAX_COOLDOWN_PERIOD

    MAX_NUM_REWARDS

    farmId

    isPaused

    isClosed

    cooldownPeriod

    lastFundUpdateTime

    farmStartTime

    totalDeposits

    rewardFunds

    rewardTokens

    rewardData

    deposits

    subscriptions

    Minting & 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 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.

    Redeeming USDs

    1. Go to the 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.

    Staking SPA

    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

    Extend Lockup for staked SPA

    1. 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.

    SperaxOS

    Smart Agent Infrastructure for Decentralized Finance.

    The End of Middlemen. The Birth of Financial Autonomy.

    Imagine a world where you never need a banker, broker, or financial advisor again.

    Where your finances run on logic, not paperwork.

    Where you are in full control.

    SperaxOS is that world.

    It’s not just another crypto app. It’s an AI-powered financial operating system that replaces middlemen with autonomous agents. Built on smart contracts and real-time logic, it empowers users to earn, invest, and manage capital without intermediaries.

    No friction.

    No gatekeepers.

    No hidden fees.

    Just smart contracts working for you—24/7.


    USDs (Sperax USD)

    A stablecoin yield aggregator on the Arbitrum network

    USDs is a yield aggregator which generates auto-yield natively. Currently, USDs is live on Arbitrum, the largest Layer-2 Ecosystem of Ethereum. Eventually, Sperax will build a system of interoperability so that USDs will be natively deployed to all major blockchain platforms.

    The highlights of this protocol:

    1. Auto-yield - Users holding USDs in their wallets automatically earn organic yield. No staking is required by the end user. Users do not need to spend gas calling the smart contract to claim their yield.

    2. Layer 2 native — Cheaper transaction fees on Arbitrum make this protocol retail investor-friendly.

    How 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 to your wallet.

    ClaimableFee

    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

    Why SperaxOS?

    SperaxOS empowers you to:

    • Earn passive income with yield-bearing stablecoins

    • Send money globally in seconds

    • Invest, lend, and manage assets without intermediaries

    • Access tools previously reserved for institutions

    • Automate your finances—from wills to complex trades

    All powered by AI agents and transparent code.

    No approvals needed. No trust required.


    Key Benefits

    • No gatekeepers — Full financial control

    • Always earning — Idle capital deployed automatically

    • Global-ready — Cross-chain, borderless, and instant

    • AI-driven — Smarter decisions, faster execution

    • Institutional tools for everyone — No-code, plug-and-play agents


    What Agents Can Do

    Feature

    Description

    Programmable Logic

    Set goals like “maintain 5% yield” or “automate monthly payments”

    Autonomous Yield Farming

    Agents find and rebalance into best opportunities in DeFi and RWA markets

    Automated Payments

    Scheduled, conditional, and metered payments — all in $USDs

    Token Sniping Engine

    Detect, evaluate, and act on new token listings in real time

    Idle Capital Deployment

    Auto-invest idle funds; maintain custom liquidity buffers

    Risk Control Layer

    Built-in limits, safety scores, depeg alerts, and bridge risk protections


    Built on Experience

    Founded in 2019, Sperax has weathered every market cycle—and come out stronger.

    We’ve built reliable products and earned the trust of top-tier investors and exchanges.

    🛡️ 2021: USDs

    A fully on-chain, yield-generating stablecoin—launched before many of today’s leading models.

    🧰 2022: Demeter

    A no-code toolkit that helps DAOs deploy liquidity farms across major DEXs, with zero developer effort.

    🌉 Ongoing: Bridging Web2 + Web3

    Partnered with regulated fintech app Streetbeat to bring decentralized finance to traditional users.


    Who It’s For

    • Users: Hands-free, high-yield finance with full transparency

    • Developers: Build plug-ins and custom agents using open SDK

    • Protocols: Tap into intelligent, autonomous capital to boost liquidity


    Join the Autonomous Finance Movement

    No approvals. No middlemen. Just programmable, intelligent capital.

    USDC.e:

    • Compound (USDC.e)

    • Stargate (Stargate-LP USDC.e)

    • Aave (USDC.e)

    • Curve (FRAX-USDC.E)

    • Fluid (USDC.e)

    Maximum cap of 50% of total USDC.e collateral for each strategy.

  • USDT:

    • Aave (USDT)

    • Stargate (Stargate-LP USDT)

    • Fluid (USDT)

    Maximum cap of 75% of total USDT collateral for each strategy.

  • admin
    Minting and Redeeming
    app.sperax.io
    Auto Yield
    claimPoolFee

    A function to claim the pool fee earned by lp.

    Only the deposit owner can call this function.

    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.

    Parameters

    Name
    Type
    Description

    _depositId

    uint256

    Deposit ID of the deposit in the farm.

    Returns

    Name
    Type
    Description

    tokenId

    uint256

    Token ID of the deposit for E721 farms, for other farms return depositId.

    amt0Recv

    uint256

    Amount 0 received as fee.

    amt1Recv

    uint256

    Amount 1 received as fee.

    Events

    PoolFeeCollected

    Errors

    NoFeeToClaim

    Git Source
    uint8 public constant COMMON_FUND_ID = 0;
    uint8 public constant LOCKUP_FUND_ID = 1;
    uint256 public constant PRECISION = 1e18;
    uint256 public constant MAX_COOLDOWN_PERIOD = 30;
    uint256 public constant MAX_NUM_REWARDS = 4;
    string public farmId;
    bool internal isPaused;
    bool internal isClosed;
    uint256 public cooldownPeriod;
    uint256 public lastFundUpdateTime;
    uint256 public farmStartTime;
    uint256 public totalDeposits;
    RewardFund[] internal rewardFunds;
    address[] internal rewardTokens;
    mapping(address => RewardData) internal rewardData;
    mapping(uint256 => Deposit) internal deposits;
    mapping(uint256 => Subscription[]) internal subscriptions;
    function claimPoolFee(uint256 _depositId) external nonReentrant;
    function _claimPoolFee(uint256 _depositId)
        internal
        virtual
        returns (uint256 tokenId, uint256 amt0Recv, uint256 amt1Recv);
    event PoolFeeCollected(address indexed recipient, uint256 tokenId, uint256 amt0Recv, uint256 amt1Recv);
    error NoFeeToClaim();

    Click on 'Confirm' and unlock USDs transfer by providing required consent.

  • Then approve the transaction and your USDs will be redeemed succesfully.

  • dApp
    dApp

    Button to extend lockup period

    Fully Backed Model - USDs is 100% backed by a diversified basket of whitelisted crypto assets (stablecoins).

    The rise of decentralized finance (DeFi) has led to a huge increase in the use of stablecoins—cryptocurrencies designed to keep a steady value, usually around $1.

    They generally fall into three categories. Fiat-backed stablecoins like USDC, etc. are pegged to traditional currencies and backed by reserves held by centralized issuers. They’re widely used but raise concerns around transparency and control. Crypto-collateralized stablecoins like DAI are backed by on-chain assets. They aim for decentralization but can face scaling limits. Algorithmic stablecoins rely on smart contracts and supply adjustments to maintain their peg without direct collateral. They remain sensitive to market shifts and are still evolving.

    Each type balances trade-offs between decentralization, stability, and scalability as the stablecoin ecosystem matures.

    USDs has combined the best of existing designs by featuring a 100% backed model like the CDP stablecoins but adds the scalability benefits of fiat backed stablecoins. An on-chain, redemption-based design makes USDs highly scalable, trustless and decentralized.

    USDs automates the process of earning yield on stablecoins. Yield is generated organically by sending collateral to audited decentralized finance protocols. 70% of yield generated on collateral is used to pay USDs holders auto-yield. Please refer to SIP-66 for more details.

    USDs auto-yield rate is adjustable and depends on the actual yield generated by the collaterals. Any yield generated beyond the max auto-yield rate will be stored in auto-yield reserves. This novel passive income strategy does not require any action from the user. Users can just hold USDs and see wallet balance grow, auto-compounding the yield in the process.

    SPA is the value accrual token of the Sperax ecosystem. SPA holders can stake SPA tokens to receive veSPA. veSPA holders are decision makers of the protocol. They will earn staking rewards and manage the protocol revenue via voting power in Sperax governance. Off-chain governance is live on Snapshot, and community (veSPA holders) can control parameters of USDs protocol, yield strategies, eligible collateral, new product features, etc. On-chain governance will be launched soon making veSPA holders the true owners of the Sperax ecosystem.

    How Sperax USD (USDs) Works
    stablecoin
    Step 1: SPA (Ethereum Mainnet) → wSPA (Ethereum Mainnet)
    1. 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.

    Step 2: wSPA (Ethereum Mainnet) → SPA (Arbitrum)

    1. 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.

    SPA token addresses

    Deployed contracts

    Name
    Explorer link

    USDs

    Vault

    CollateralManager

    FeeCalculator

    Staking 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 .

    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: (Arbitrum One).

    Distribution of Staking Rewards

    Staking 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

    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

    The 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.

    E721Farm

    Git Source

    Inherits: Farm, IERC721Receiver

    Author: Sperax Foundation.

    This contract contains the core logic for E721 farms.

    State Variables

    nftContract

    depositToTokenId

    Functions

    onERC721Received

    Function is called when user transfers the NFT to this farm.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    withdraw

    Function to withdraw a deposit from the farm.

    Parameters

    Name
    Type
    Description

    _getLiquidity

    Function to get the liquidity. Must be defined by the farm.

    This function should be overridden to add the respective logic.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    Errors

    UnauthorisedNFTContract

    NoData

    OperableDeposit

    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.

    Parameters

    Name
    Type
    Description

    _updateSubscriptionForDecrease

    Update subscription data of a deposit after decrease in liquidity.

    Parameters

    Name
    Type
    Description

    _increaseDeposit

    Common logic for increasing a deposit.

    Parameters

    Name
    Type
    Description

    _decreaseDeposit

    Common logic for decreasing a deposit.

    Parameters

    Name
    Type
    Description

    Events

    DepositIncreased

    DepositDecreased

    Errors

    DecreaseDepositNotPermitted

    InsufficientLiquidity

    FarmDeployer

    Git Source

    Inherits: Ownable, ReentrancyGuard

    Author: Sperax Foundation.

    Exposes base functionalities which will be useful in every deployer.

    State Variables

    FARM_REGISTRY

    farmImplementation

    farmId

    Functions

    constructor

    Constructor.

    Parameters

    Name
    Type
    Description

    updateFarmImplementation

    Update farm implementation's address.

    Only callable by the owner.

    Ensure that _newFarmId is correct for the new farm implementation.

    Parameters

    Name
    Type
    Description

    _collectFee

    Collect fee and transfer it to feeReceiver.

    Function fetches all the fee params from farmRegistry.

    _validateNonZeroAddr

    Validate address.

    Events

    FarmCreated

    FeeCollected

    FarmImplementationUpdated

    Errors

    InvalidAddress

    NewFarmImplementationSameAsOld

    RewarderFactory

    Git Source

    Inherits: IRewarderFactory, Ownable

    Author: Sperax Foundation.

    This contract deploys new rewarders and keeps track of them.

    State Variables

    oracle

    rewarderImplementation

    Functions

    constructor

    Constructor.

    Parameters

    Name
    Type
    Description

    deployRewarder

    Function to deploy new rewarder.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    updateRewarderImplementation

    Update rewarder implementation's address

    Parameters

    Name
    Type
    Description

    updateOracle

    Function to update the oracle's address.

    Parameters

    Name
    Type
    Description

    _validateNonZeroAddr

    Validate address.

    Parameters

    Name
    Type
    Description

    Minting and Redeeming

    Minting USDs

    To mint new USDs, minters only need eligible collateral. Currently, the protocol is accepting USDC.e, USDC and USDT as collateral and more tokens might be added to this list in the future. However, USDs tokens are fungible and redeemable in the same way, independent of their underlying minting collateral. It acts as an IOU on the pooled collateral.

    The protocol will mint 1 USDs by collecting 1 USD worth of collateral.

    Collateral tokens required = No. of USDs tokens minted/Min (Price of collateral in USD, 1 USD)

    When a collateral token is worth less than 1 dollar, it will be treated with its actual market price. However, when it is worth more than 1 dollar, it will be treated as 1 dollar. Hence, USDs will always be fully collateralized or over-collateralized.

    Governance

    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 . In order for a proposal to be accepted, it must go through the following phases:

    xSPA 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 () and redirecting veSPA emissions to bribes on Gauge (). 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.

    Buyback 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

    ExpirableFarm

    Inherits: Farm

    Author: Sperax Foundation.

    This contract helps in creating farms with expiry feature.

    State Variables

  • 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.

  • 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.

  • SIP-66
    0xA61a0719e9714c95345e89a2f1C83Fae6f5745ef

    https://arbiscan.io/address/0xd122840Fa5b48B2ddB723cCC5928f88dcb558AFC

    MasterPrice Oracle

    https://arbiscan.io/address/0x14D99412dAB1878dC01Fe7a1664cdE85896e8E50

    SPABuyback

    https://arbiscan.io/address/0xFbc0d3cA777722d234FE01dba94DeDeDb277AFe3

    YieldReserve

    https://arbiscan.io/address/0xfD14C8ef0993fd9409f7820BA8BA80370529d861

    Dripper

    https://arbiscan.io/address/0xd50193e8fFb00beA274bD2b11d0a7Ea08dA044c1

    RebaseManager

    https://arbiscan.io/address/0x297331A0155B1e30bBFA85CF3609eC0fF037BEEC

    AaveStrategy

    https://arbiscan.io/address/0x974993ee8df7f5c4f3f9aa4eb5b4534f359f3388

    StargateStrategy

    https://arbiscan.io/address/0xb9c9100720d8c6e35eb8dd0f9c1abef320daa136

    CompoundStrategy

    https://arbiscan.io/address/0xBCeb48625771E35420076f79Ec6921E783a82442

    https://arbiscan.io/address/0xD74f5255D557944cf7Dd0E45FF521520002D5748
    https://arbiscan.io/address/0x6Bbc476Ee35CBA9e9c3A59fc5b10d7a0BC6f74Ca
    https://arbiscan.io/address/0xdA423BFa1E196598190deEfbAFC28aDb36FaeDF0

    <none>

    address

    _from

    address

    The address of the owner.

    _tokenId

    uint256

    NFT Id generated by other protocol (e.g. Camelot or Uniswap).

    _data

    bytes

    The data should be the lockup flag (bool).

    <none>

    bytes4

    bytes4 The onERC721Received selector.

    _depositId

    uint256

    The id of the deposit to be withdrawn.

    _tokenId

    uint256

    The nft tokenId.

    <none>

    uint256

    The liquidity of the nft position.

    _depositId

    uint256

    Unique deposit id for the deposit.

    _amount

    uint256

    _amount to be increased.

    _depositId

    uint256

    Unique deposit id for the deposit

    _amount

    uint256

    _amount to be decreased.

    _depositId

    uint256

    Unique deposit id for the deposit

    _amount

    uint256

    _amount to be decreased.

    _depositId

    uint256

    Unique deposit id for the deposit

    _amount

    uint256

    _amount to be decreased.

    _farmRegistry

    address

    Address of the Farm Registry.

    _farmId

    string

    Id of the farm.

    _newFarmImplementation

    address

    New farm implementation's address.

    _newFarmId

    string

    ID of the new farm.

    _oracle

    address

    Address of the master price oracle of USDs.

    _rwdToken

    address

    Address of the reward token for which the rewarder is to be deployed.

    rewarder

    address

    Rewarder's address.

    _newRewarderImplementation

    address

    New Rewarder Implementation

    _newOracle

    address

    Address of the new oracle.

    _addr

    address

    Address to be validated.

    MIN_EXTENSION

    MAX_EXTENSION

    farmEndTime

    farmRegistry

    Functions

    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.

    Parameters

    Name
    Type
    Description

    _extensionDays

    uint256

    The number of days to 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.

    Parameters

    Name
    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.

    Returns

    Name
    Type
    Description

    <none>

    bool

    bool True if farm is open.

    _setupFarmExpiry

    Setup the farm data for farm expiry.

    Parameters

    Name
    Type
    Description

    _farmStartTime

    uint256

    Start time of the farm.

    _farmRegistry

    address

    Address of the farm registry.

    _collectExtensionFee

    Collects farm extension fee and transfers it to feeReceiver.

    Function fetches all the fee params from farmRegistry.

    Parameters

    Name
    Type
    Description

    _extensionDays

    uint256

    The number of days to extend the farm. Example: 150 means 150 days.

    Events

    FarmEndTimeUpdated

    ExtensionFeeCollected

    Errors

    InvalidExtension

    DurationExceeded

    FarmNotYetStarted

    Git Source
    address public nftContract;
    mapping(uint256 => uint256) public depositToTokenId;
    function onERC721Received(address, address _from, uint256 _tokenId, bytes calldata _data)
        external
        override
        returns (bytes4);
    function withdraw(uint256 _depositId) external override nonReentrant;
    function _getLiquidity(uint256 _tokenId) internal view virtual returns (uint256);
    error UnauthorisedNFTContract();
    error NoData();
    function _updateSubscriptionForIncrease(uint256 _depositId, uint256 _amount) internal;
    function _updateSubscriptionForDecrease(uint256 _depositId, uint256 _amount) internal;
    function _increaseDeposit(uint256 _depositId, uint256 _amount) internal;
    function _decreaseDeposit(uint256 _depositId, uint256 _amount) internal;
    event DepositIncreased(uint256 indexed depositId, uint256 liquidity);
    event DepositDecreased(uint256 indexed depositId, uint256 liquidity);
    error DecreaseDepositNotPermitted();
    error InsufficientLiquidity();
    address public immutable FARM_REGISTRY;
    address public farmImplementation;
    string public farmId;
    constructor(address _farmRegistry, string memory _farmId) Ownable(msg.sender);
    function updateFarmImplementation(address _newFarmImplementation, string calldata _newFarmId) external onlyOwner;
    function _collectFee() internal virtual;
    function _validateNonZeroAddr(address _addr) internal pure;
    event FarmCreated(address indexed farm, address indexed creator, address indexed admin);
    event FeeCollected(address indexed creator, address indexed token, uint256 amount);
    event FarmImplementationUpdated(address indexed newFarmImplementation, string newFarmId);
    error InvalidAddress();
    error NewFarmImplementationSameAsOld();
    address public oracle;
    address public rewarderImplementation;
    constructor(address _oracle) Ownable(msg.sender);
    function deployRewarder(address _rwdToken) external returns (address rewarder);
    function updateRewarderImplementation(address _newRewarderImplementation) external onlyOwner;
    function updateOracle(address _newOracle) public onlyOwner;
    function _validateNonZeroAddr(address _addr) private pure;
    uint256 public constant MIN_EXTENSION = 100;
    uint256 public constant MAX_EXTENSION = 300;
    uint256 public farmEndTime;
    address public farmRegistry;
    function extendFarmDuration(uint256 _extensionDays) external onlyOwner nonReentrant;
    function updateFarmStartTime(uint256 _newStartTime) public virtual override;
    function isFarmOpen() public view virtual override returns (bool);
    function _setupFarmExpiry(uint256 _farmStartTime, address _farmRegistry) internal;
    function _collectExtensionFee(uint256 _extensionDays) private;
    event FarmEndTimeUpdated(uint256 newEndTime);
    event ExtensionFeeCollected(address indexed token, uint256 extensionFee);
    error InvalidExtension();
    error DurationExceeded();
    error FarmNotYetStarted();
    Mint fees are subject to Sperax DAO approval.

    Redeeming USDs

    Redeeming 1 USDs at the protocol level gives the user back any one unit of collateral (maximum 1 unit of collateral) after deducting the redemption fee. Redeemers can choose from the list of eligible collaterals that they want to receive. A portion of USDs redeemed is collected as a redemption fee by the protocol.

    The protocol will redeem 1 USDs following this simple equation:

    Fee = x% of USDs amount redeemed. 'x' depends on the selected collateral.

    Collateral tokens redeemed = (1-x)% * (No. of USDs redeemed)

    When a collateral token is worth more than 1 dollar, it will be treated with its actual market price. However, when it is worth less than 1 dollar, it will be treated as 1 dollar. Hence, USDs will always be fully collateralized or over-collateralized.

    The aggregate collateral ratio (or CR = Total Value Locked/USDs Circulating Supply) should be close to 100% as USDs are backed by stablecoins and collateral is expected to hold their price even in situations of market volatility. However, in an extreme situation if the value of locked collateral changes and the collateral ratio drops by more than 10%, the protocol will be paused manually. Redemptions will begin based on governance or if the collateral ratio increases. To cover any gaps in collateralization SPA reserves from Treasury may be used by the protocol.

    Redemption fees are subject to Sperax DAO approval.

    Caps

    While minting USDs, if the price of the collateral token is more than 1 USD, then USDs will treat the price of the collateral as 1 USD and will only mint 1 USDs per collateral token deposited. But if the price of the collateral is less than 1 USD then more units of the collateral will be required to mint a unit of USDs. In that case minters would need to provide 1 USD worth of collateral to mint 1 USDs. If the price of the collateral has dropped below 0.97 cents then users would not be able to use that collateral to mint USDs. While redeeming users would always receive one unit of any collateral token of their choice for one unit of USDs.

    Fees

    Fees will be calculated and configurable based on the current state of the protocol’s collateral composition. Anyone would be able to call a function that updates the fee parameters. When a collateral would exceed the desired collateral composition the fee for that collateral would increase and its redemption fee would decrease. Similarly when the amount of collateral in the protocol is less than the desired amount, the mint fee for that collateral will decrease and the redemption fee would increase. Initially all collaterals except for USDC will be set to have a desired collateral composition of 20% and the base fee will be set to 0.05%. Any collateral having a composition of greater than the desired collateral composition cap will have a mint fee of greater than the base fee. The exact applicable fee on a collateral will vary and can be queried via a view function in the contract.

    x: Current collateral value in number of tokens

    Ca: (Desired collateral composition in % x Total Collateral in the Vault)

    Total collateral in vault = Total USDs supply

    Base mint and redeem fee (f base): Base fee for mint and redemption which will be discounted/increased dynamically based on the collaterals composition.

    Redeeming from Strategies

    Now users can redeem their choice of collateral from any yield strategy, instead of relying on a preset default mechanism. Strategies are a set of smart contracts that control the depositing and withdrawal of collaterals from various other DeFi protocols like Aave, Compound, Fluid etc. The slippage that occurs is still transferred back to the redeemers but since redeemers have a choice, they could calculate and manage their own slippage.

    Auto-yield is distributed after a user redeems and before a user mints. So, minters and redeemers should check if there’s an eligible auto-yield.

    Collateral=USDs−FeeCollateral = USDs - FeeCollateral=USDs−Fee
    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)

    If 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.

    Phase 2: Snapshot vote:

    Once 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.

    Phase 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

    Track the implementation of these proposals

    Forum
    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.

    All 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

    • 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.

    • 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

    • xSPA token is transferrable.

    • The staking and redemption criteria can be updated/modified through governance.

    How 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 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.

    Redeeming 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.

    SIP-32
    SIP-33

    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.

  • 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.

  • Buyback SPA - Any users or external wallet can view the USDs balance in the Buyback contract and sell their SPA tokens for USDs.

  • 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.

    1. 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

    1. getSPAReqdForUSDs - Input the target USDs amount to receive and get an estimate of how many SPA tokens are needed.

      1. Function: getSPAReqdForUSDs(uint256 _usdsAmount) external view returns (uint256)

      2. Input : _usdsAmount is the amount of USDs to purchase

      3. Output: Estimated amount of SPA required

      4. 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.

      1. Function: getUsdsOutForSpa(uint256 _spaIn) external view returns (uint256)

      2. Input : _spaIn is the amount of SPA to be sold

      3. Output: Estimated amount of USDs to be received

    User End Write Functions

    1. buyUSDs - Purchase USDs with SPA

      1. Function: buyUSDs(uint256 _spaIn, uint256 _minUSDsOut) external

      2. Input : _spaIn is the amount of SPA to be sold _minUSDsOut is the minimum amount of USDs to be received

      3. 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

      4. 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)

      1. Function: buyUSDs(address _receiver, uint256 _spaIn, uint256 _minUSDsOut) external

      2. 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

    Locking 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

    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.

    1. 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 outfor 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

    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 .

    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:

    Extension 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.

    CamelotV3FarmDeployer

    Git Source

    Inherits: FarmDeployer

    Author: Sperax Foundation.

    This contract allows anyone to calculate fees, pay fees and create farms.

    State Variables

    CAMELOT_V3_FACTORY

    NFPM

    CAMELOT_UTILS

    CAMELOT_NFPM_UTILS

    Functions

    constructor

    Constructor of the contract.

    Parameters

    Name
    Type
    Description

    createFarm

    Deploys a new CamelotV3 farm.

    The caller of this function should approve feeAmount to this contract before calling this function.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    Structs

    FarmData

    How 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)

    CamelotV3Farm

    Inherits: E721Farm, OperableDeposit, ClaimableFee

    Author: Sperax Foundation.

    This contract is the implementation of the Camelot V3 farm.

    State Variables

    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 will be able to increase their staked position if the lockup period is above 180 days.
    createRedemptionRequest
    Steps to Launch a Farm
    1. 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

    1. 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.

      • 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:

    1. 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.

    Farm 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:

    1. 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:

      1. For all liquidity providers.

      2. 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:

    1. 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.

    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

  • Extending the locking period

  • Locking more SPA

  • Claiming SPA staking rewards and restaking them into veSPA (compounding)

  • 4 year

    1

    4

    3 year

    1

    3

    2 year

    1

    2

    1 year

    1

    1

    6 months

    1

    0.5

    dapp
    Stay Staked at Residual Value
  • Example: Suppose getSPAReqdForUSDs(100*1e18) returns 1e18. It means the contract estimates that one will receive 1 USDs after spending 100 SPA.

  • 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
  • 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.

  • _camelotUtils

    address

    Address of CamelotUtils (Camelot helper) contract.

    _nfpmUtils

    address

    Address of Camelot INonfungiblePositionManagerUtils (NonfungiblePositionManager helper) contract.

    _farmRegistry

    address

    Address of the Farm Registry.

    _farmId

    string

    Id of the farm.

    _camelotV3Factory

    address

    Address of CamelotV3 factory.

    _nfpm

    address

    Address of Camelot NonfungiblePositionManager contract.

    _data

    FarmData

    Data for deployment.

    <none>

    address

    Address of the deployed farm.

    tickLowerAllowed

    tickUpperAllowed

    camelotPool

    camelotV3Factory

    camelotUtils

    nfpmUtils

    MIN_TICK

    MAX_TICK

    Functions

    initialize

    Initializer function of this farm.

    Parameters

    Name
    Type
    Description

    _input

    InitializeInput

    A struct having all the input params.

    increaseDeposit

    Allow user to increase liquidity for a deposit.

    Parameters

    Name
    Type
    Description

    _depositId

    uint256

    The id of the deposit to be increased.

    _amounts

    uint256[2]

    Desired amount of tokens to be increased.

    _minAmounts

    uint256[2]

    Minimum amount of tokens to be added as liquidity.

    decreaseDeposit

    Withdraw liquidity partially from an existing deposit.

    Parameters

    Name
    Type
    Description

    _depositId

    uint256

    Deposit index for the user.

    _liquidityToWithdraw

    uint128

    Amount to be withdrawn.

    _minAmounts

    uint256[2]

    Minimum amount of tokens to be received.

    getTokenAmounts

    Function to be called by Rewarder to get tokens and amounts associated with the farm's liquidity.

    Returns

    Name
    Type
    Description

    <none>

    address[]

    tokens An array of token addresses.

    <none>

    uint256[]

    amounts An array of token amounts.

    _claimPoolFee

    Claim pool fee implementation from ClaimableFee feature.

    Parameters

    Name
    Type
    Description

    _depositId

    uint256

    Deposit ID of the deposit in 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.

    Parameters

    Name
    Type
    Description

    _tokenId

    uint256

    The tokenId of the position.

    Returns

    Name
    Type
    Description

    <none>

    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.

    Check if the token adheres to the tick range.

    The ticks must be within the max range and must be multiple of tickSpacing.

    Parameters

    Name
    Type
    Description

    _tickLower

    int24

    The lower tick of the range.

    _tickUpper

    int24

    The upper tick of the range.

    Errors

    InvalidCamelotPoolConfig

    IncorrectPoolToken

    IncorrectTickRange

    InvalidTickRange

    InvalidAmount

    Git Source
    address public immutable CAMELOT_V3_FACTORY;
    address public immutable NFPM;
    address public immutable CAMELOT_UTILS;
    address public immutable CAMELOT_NFPM_UTILS;
    constructor(
        address _farmRegistry,
        string memory _farmId,
        address _camelotV3Factory,
        address _nfpm,
        address _camelotUtils,
        address _nfpmUtils
    ) FarmDeployer(_farmRegistry, _farmId);
    function createFarm(FarmData memory _data) external nonReentrant returns (address);
    struct FarmData {
        address farmAdmin;
        uint256 farmStartTime;
        uint256 cooldownPeriod;
        CamelotPoolData camelotPoolData;
        RewardTokenData[] rewardData;
    }
    int24 public tickLowerAllowed;
    int24 public tickUpperAllowed;
    address public camelotPool;
    address public camelotV3Factory;
    address public camelotUtils;
    address public nfpmUtils;
    int256 internal constant MIN_TICK = -887272;
    int256 internal constant MAX_TICK = 887272;
    function initialize(InitializeInput calldata _input) external;
    function increaseDeposit(uint256 _depositId, uint256[2] calldata _amounts, uint256[2] calldata _minAmounts)
        external
        nonReentrant;
    function decreaseDeposit(uint256 _depositId, uint128 _liquidityToWithdraw, uint256[2] calldata _minAmounts)
        external
        nonReentrant;
    function getTokenAmounts() external view override returns (address[] memory, uint256[] memory);
    function _claimPoolFee(uint256 _depositId)
        internal
        override
        returns (uint256 tokenId, uint256 amt0Recv, uint256 amt1Recv);
    function _getLiquidity(uint256 _tokenId) internal view override returns (uint256);
    function _validateTickRange(int24 _tickLower, int24 _tickUpper) private view;
    error InvalidCamelotPoolConfig();
    error IncorrectPoolToken();
    error IncorrectTickRange();
    error InvalidTickRange();
    error InvalidAmount();

    MasterPriceOracle

    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.

    Functions

    updateTokenPriceFeed

    Add/Update price feed for _token

    Have to be extra cautious while updating the price feed.

    Parameters

    Name
    Type
    Description

    removeTokenPriceFeed

    Remove an existing price feed for _token.

    Parameters

    Name
    Type
    Description

    getPrice

    Gets the price feed for _token.

    Function reverts if the price feed does not exists.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    priceFeedExists

    Validates if price feed exists for a _token

    Function reverts if price feed not set.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _getPriceFeed

    Gets the price feed for a _token given the feed data.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    Events

    PriceFeedUpdated

    PriceFeedRemoved

    Errors

    InvalidAddress

    UnableToFetchPriceFeed

    InvalidPriceFeed

    PriceFeedNotFound

    FarmRegistry

    Git Source

    Inherits: IFarmRegistry, OwnableUpgradeable

    Author: Sperax Foundation.

    This contract tracks fee details, privileged users, deployed farms and farm deployers.

    State Variables

    farms

    deployerList

    feeReceiver

    feeToken

    feeAmount

    extensionFeePerDay

    farmRegistered

    deployerRegistered

    isPrivilegedUser

    Functions

    constructor

    initialize

    constructor

    Parameters

    Name
    Type
    Description

    registerFarm

    Register a farm created by registered Deployer.

    Only registered deployer can register a farm.

    Parameters

    Name
    Type
    Description

    registerFarmDeployer

    Register a new farm deployer.

    Only owner can call this function.

    Parameters

    Name
    Type
    Description

    removeDeployer

    Remove an existing deployer from registry.

    Only owner can call this function.

    Parameters

    Name
    Type
    Description

    updatePrivilege

    Function to add/remove privileged User.

    Only callable by the owner.

    Parameters

    Name
    Type
    Description

    getFarmDeployerList

    Get list of registered deployer.

    Returns

    Name
    Type
    Description

    getFarmList

    Get list of farms created via registered deployer.

    Returns

    Name
    Type
    Description

    getFeeParams

    Get all the fee parameters for creating farm.

    It returns fee amount and extension fee as 0 if _user is privileged.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    updateFeeParams

    Update the fee params for registry.

    Parameters

    Name
    Type
    Description

    _validateNonZeroAddr

    Validate address.

    Fee Calculator

    Git Source

    Inherits: IFeeCalculator

    Author: Sperax Foundation

    A contract that calculates fees for minting and redeeming USDs.

    State Variables

    LOWER_THRESHOLD

    UPPER_THRESHOLD

    DISCOUNT_FACTOR

    PENALTY_MULTIPLIER

    CALIBRATION_GAP

    COLLATERAL_MANAGER

    collateralFee

    Functions

    constructor

    calibrateFee

    Calibrates fee for a particular collateral

    Parameters

    Name
    Type
    Description

    getMintFee

    Calculates fee to be collected for minting

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getRedeemFee

    Calculates fee to be collected for redeeming

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    calibrateFeeForAll

    Calibrates fee for all the collaterals registered

    _calibrateFee

    Helper function for calibrating fee for a collateral

    Parameters

    Name
    Type
    Description

    Events

    FeeCalibrated

    Errors

    InvalidCalibration

    Structs

    FeeData

    Dripper

    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

    Bug 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.

    _token

    address

    address of the desired token.

    _source

    address

    price feed source.

    _data

    bytes

    call data for fetching the price feed.

    _token

    address

    address of the token.

    _token

    address

    address of the desired token.

    PriceData

    (uint256 price, uint256 precision).

    _token

    address

    address of the desired token.

    bool

    bool if price feed exists.

    _token

    address

    address of the desired token.

    _source

    address

    price feed source.

    _msgData

    bytes

    call data for fetching feed.

    priceData

    PriceData

    (uint256 price, uint256 precision).

    _feeReceiver

    address

    Receiver of the fees.

    _feeToken

    address

    The fee token for farm creation.

    _feeAmount

    uint256

    The fee amount to be paid by the creator.

    _extensionFeePerDay

    uint256

    Extension fee per day.

    _farm

    address

    Address of the created farm contract

    _creator

    address

    Address of the farm creator.

    _deployer

    address

    Address of deployer to be registered.

    _id

    uint16

    ID of the deployer to be removed (0 index based).

    _user

    address

    User Address for which privilege is to be updated.

    _privilege

    bool

    Privilege(bool) whether true or false.

    <none>

    address[]

    Returns array of registered deployer addresses.

    <none>

    address[]

    Returns array of farm addresses.

    _user

    address

    The account creating the farm.

    <none>

    address

    Receiver of the fees.

    <none>

    address

    Token in which fee is to be paid.

    <none>

    uint256

    Amount of fees to be paid for creation of farm.

    <none>

    uint256

    Extension fee per day in case of extending a farm.

    _receiver

    address

    FeeReceiver address.

    _feeToken

    address

    Token address for fee.

    _amount

    uint256

    Amount of token to be collected.

    _extensionFeePerDay

    uint256

    Extension fee per day.

    _collateral

    address

    Address of the desired collateral

    _collateral

    address

    uint256

    (uint256) baseFeeIn

    _collateral

    address

    uint256

    (uint256) baseFeeOut

    _collateral

    address

    Address of the desired collateral

    vault

    dripRate

    dripDuration

    lastCollectTS

    Functions

    constructor

    Constructor to initialize the Dripper.

    Parameters

    Name
    Type
    Description

    _vault

    address

    Address of the contract that receives the dripped tokens.

    _dripDuration

    uint256

    The duration over which tokens are dripped.

    recoverTokens

    Emergency fund recovery function.

    Transfers the asset to the owner of the contract.

    Parameters

    Name
    Type
    Description

    _asset

    address

    Address of the asset to recover.

    addUSDs

    Function to be used to send USDs to dripper and update dripRate.

    Parameters

    Name
    Type
    Description

    _amount

    uint256

    Amount of USDs to be sent form caller to this contract.

    collect

    Transfers the dripped tokens to the vault.

    This function also updates the dripRate based on the fund state.

    Returns

    Name
    Type
    Description

    uint256

    The amount of tokens collected and transferred to the vault.

    updateVault

    Update the vault address.

    Parameters

    Name
    Type
    Description

    _vault

    address

    Address of the new vault contract.

    updateDripDuration

    Updates the dripDuration.

    Parameters

    Name
    Type
    Description

    _dripDuration

    uint256

    The desired drip duration to be set.

    getCollectableAmt

    Gets the collectible amount of tokens at the current time.

    Returns

    Name
    Type
    Description

    uint256

    The amount of tokens that can be collected.

    Events

    Collected

    Recovered

    VaultUpdated

    DripDurationUpdated

    USDsAdded

    Errors

    NothingToRecover

    Git Source
    mapping(address => PriceFeedData) public tokenPriceFeed;
    
    function updateTokenPriceFeed(address _token, address _source, bytes memory _data) external onlyOwner;
    
    function removeTokenPriceFeed(address _token) external onlyOwner;
    
    function getPrice(address _token) external view returns (PriceData memory);
    
    function priceFeedExists(address _token) external view returns (bool);
    
    function _getPriceFeed(address _token, address _source, bytes memory _msgData)
        private
        view
        returns (PriceData memory priceData);
    
    event PriceFeedUpdated(address indexed token, address indexed source, bytes msgData);
    
    event PriceFeedRemoved(address indexed token);
    
    error InvalidAddress();
    
    error UnableToFetchPriceFeed(address token);
    
    error InvalidPriceFeed(address token);
    
    error PriceFeedNotFound(address token);
    address[] internal farms;
    address[] internal deployerList;
    address public feeReceiver;
    address public feeToken;
    uint256 public feeAmount;
    uint256 public extensionFeePerDay;
    mapping(address => bool) public farmRegistered;
    mapping(address => bool) public deployerRegistered;
    mapping(address => bool) public isPrivilegedUser;
    constructor();
    function initialize(address _feeReceiver, address _feeToken, uint256 _feeAmount, uint256 _extensionFeePerDay)
        external
        initializer;
    function registerFarm(address _farm, address _creator) external;
    function registerFarmDeployer(address _deployer) external onlyOwner;
    function removeDeployer(uint16 _id) external onlyOwner;
    function updatePrivilege(address _user, bool _privilege) external onlyOwner;
    function getFarmDeployerList() external view returns (address[] memory);
    function getFarmList() external view returns (address[] memory);
    function getFeeParams(address _user) external view returns (address, address, uint256, uint256);
    function updateFeeParams(address _receiver, address _feeToken, uint256 _amount, uint256 _extensionFeePerDay)
        public
        onlyOwner;
    function _validateNonZeroAddr(address _addr) private pure;
    uint16 private constant LOWER_THRESHOLD = 5000;
    
    uint16 private constant UPPER_THRESHOLD = 15000;
    
    uint16 private constant DISCOUNT_FACTOR = 2;
    
    uint16 private constant PENALTY_MULTIPLIER = 2;
    
    uint32 private constant CALIBRATION_GAP = 1 days;
    
    ICollateralManager public immutable COLLATERAL_MANAGER;
    
    mapping(address => FeeData) public collateralFee;
    
    constructor(address _collateralManager);
    
    function calibrateFee(address _collateral) external;
    
    function getMintFee(address _collateral) external view returns (uint256);
    
    function getRedeemFee(address _collateral) external view returns (uint256);
    
    function calibrateFeeForAll() public;
    
    function _calibrateFee(address _collateral) private;
    
    event FeeCalibrated(address indexed collateral, uint16 mintFee, uint16 redeemFee);
    
    error InvalidCalibration();
    
    struct FeeData {
        uint32 nextUpdate;
        uint16 mintFee;
        uint16 redeemFee;
    }
    address public vault;
    
    uint256 public dripRate;
    
    uint256 public dripDuration;
    
    uint256 public lastCollectTS;
    
    constructor(address _vault, uint256 _dripDuration);
    
    function recoverTokens(address _asset) external onlyOwner;
    
    function addUSDs(uint256 _amount) external;
    
    function collect() public returns (uint256);
    
    function updateVault(address _vault) public onlyOwner;
    
    function updateDripDuration(uint256 _dripDuration) public onlyOwner;
    
    function getCollectableAmt() public view returns (uint256);
    
    event Collected(uint256 amount);
    
    event Recovered(address owner, uint256 amount);
    
    event VaultUpdated(address vault);
    
    event DripDurationUpdated(uint256 dripDuration);
    
    event USDsAdded(uint256 _amount);
    
    error NothingToRecover();
    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

    The 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:

    • 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:

    • 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:

    • 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.

    All 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/Severity
    Very Low
    Low
    Moderate
    High
    Critical

    Almost certain

    $1000

    $2500

    $5000

    $10000

    $15000

    Likely

    $500

    $1000

    Other terms

    The decisions made regarding rewards are final and binding.

    By 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.

    RebaseManager

    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

    vault

    dripper

    gap

    aprCap

    aprBottom

    lastRebaseTS

    Functions

    onlyVault

    constructor

    Constructor to initialize the Rebase Manager

    Parameters

    Name
    Type
    Description

    fetchRebaseAmt

    Get the current amount valid for rebase

    Function is called by the vault while rebasing

    Returns

    Name
    Type
    Description

    updateVault

    Updates the vault address

    Parameters

    Name
    Type
    Description

    updateDripper

    Updates the dripper contract for USDs vault

    Parameters

    Name
    Type
    Description

    updateGap

    Update the minimum time gap required between two rebases

    Parameters

    Name
    Type
    Description

    updateAPR

    Update the APR requirements for each rebase

    Parameters

    Name
    Type
    Description

    getAvailableRebaseAmt

    Gets the current available rebase fund

    Returns

    Name
    Type
    Description

    getMinAndMaxRebaseAmt

    Gets the minimum and maximum rebase USDs amount based on the APR config

    Returns

    Name
    Type
    Description

    Events

    VaultUpdated

    DripperUpdated

    GapUpdated

    APRUpdated

    Errors

    CallerNotVault

    InvalidAPRConfig

    SIP implementation – AsanaAsana
    Approved SIPsSperax DAO
    Tutorial for using Sperax Forum

    $2500

    $5000

    $10000

    Possible

    $100

    $500

    $1000

    $2500

    $5000

    Unlikely

    $100

    $100

    $500

    $1000

    $2500

    Almost Possible

    $100

    $100

    $100

    $500

    $1000

    uint256

    Maximum allowed APR for a rebase

    _aprBottom

    uint256

    Minimum allowed APR for a rebase

    _vault

    address

    Address of the vault contract

    _dripper

    address

    Address of the dripper contract for collecting USDs

    _gap

    uint256

    Minimum time gap required between two consecutive rebases

    uint256

    The available amount for rebasing USDs

    _newVault

    address

    Address of the new vault contract

    _dripper

    address

    Address of the new dripper contract

    _gap

    uint256

    Updated gap time

    _aprBottom

    uint256

    New minimum APR for a rebase

    _aprCap

    uint256

    New maximum APR for a rebase

    uint256

    Current balance in the vault plus collectable dripped USDs amount

    uint256

    Minimum and maximum rebase amounts

    uint256

    _aprCap

    uint256 private constant ONE_YEAR = 365 days;
    
    address public vault;
    
    address public dripper;
    
    uint256 public gap;
    
    uint256 public aprCap;
    
    uint256 public aprBottom;
    
    uint256 public lastRebaseTS;
    
    modifier onlyVault();
    
    constructor(address _vault, address _dripper, uint256 _gap, uint256 _aprCap, uint256 _aprBottom);
    
    function fetchRebaseAmt() external onlyVault returns (uint256);
    
    function updateVault(address _newVault) public onlyOwner;
    
    function updateDripper(address _dripper) public onlyOwner;
    
    function updateGap(uint256 _gap) public onlyOwner;
    
    function updateAPR(uint256 _aprBottom, uint256 _aprCap) public onlyOwner;
    
    function getAvailableRebaseAmt() public view returns (uint256);
    
    function getMinAndMaxRebaseAmt() public view returns (uint256, uint256);
    
    event VaultUpdated(address vault);
    
    event DripperUpdated(address dripper);
    
    event GapUpdated(uint256 gap);
    
    event APRUpdated(uint256 aprBottom, uint256 aprCap);
    
    error CallerNotVault(address caller);
    
    error InvalidAPRConfig(uint256 aprBottom, uint256 aprCap);
    Logo

    SPA 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

    Name
    Address
    Vesting & Purpose
    Initial Allocation
    % of Initial Supply

    Treasury

    This vests linearly over a 4-year time-lock, beginning from the launch date of governance protocol. The DAO will control this fund and utilize for future partnerships, marketing incentives, liquidity mining rewards, etc.

    1,250,000,000

    25%

    Foundation

    Foundation funds are being used to make initial markets and for protocol development. Since the foundation lends the tokens to third parties for market making activities, the actual token balance held in the foundation wallet will fluctuate. Foundation has burnt 375MM SPA in 2022 to further decentralise the protocol - 250MM SPA in May and 125MM SPA in September. is an operator wallet which is sometimes used to move tokens from Layer 1 to Layer 2.

    Staking Protocol Related

    Title
    Chain
    Contract / Wallet Address

    veSPA L1

    Ethereum

    0xbF82a3212e13b2d407D10f5107b5C8404dE7F403

    veSPA L2

    Arbitrum

    0x2e2071180682Ce6C247B1eF93d382D509F5F6A17

    RewardDistributor L1

    Ethereum

    0xa61DD4480BE2582283Afa54E461A1d3643b36040

    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.

    Title
    Chain
    Contract / Wallet Address

    Bootstrap Liquidity

    Ethereum

    0x8B65CE3b4Eaa8958346096C3a9303b73f2012aCc

    Bootstrap liquidity deployer

    Ethereum and Arbitrum

    0xc28c6970D8A345988e8335b1C229dEA3c802e0a6

    USDs/USDC Farm Rewarder (SPA)

    Arbitrum

    0x1733c5bc884090C73D89303467461693c54Ba58B

    SPA Buyback

    The 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.

    1,250,783,000

    25.02%

    Bootstrap Liquidity

    0x8B65CE3b4Eaa8958346096C3a9303b73f2012aCc 0xAF64e027D42bAc1C14277fd295De9Ae318eEF17E

    This will be used to provide rewards for liquidity mining during protocol genesis. Future liquidity mining rewards will be funded from treasury funds

    500,000,000

    10%

    Staking Rewards

    0xCD1B1ce6ce877a9315E73E2E4Ba3137228068A59

    0x3702E3e2DB2b5d037c1dbB23Ab7A51d0Cc90BD0e

    This will be used to reward users who stake $SPA in the staking protocol. Stakers will earn fees from the minting and redeeming of $USDs, as well as staking rewards from the allocated rewards budget.

    500,000,000

    10%

    Team & Advisor

    0xE10b88d70b01b956782Dc98d7D4f3a931FF59Fc7

    This vests linearly over 4 years with a 6 month cliff. Token unlock schedule will start from 4/1/2022 for existing team members. For new members, vesting would start at least three months from the day they join.

    499,217,000

    9.98%

    Private Sale

    0x2Fc8d8BCf4b2c0fc6594475E44c473AC3E844B6a

    All SPA tokens have been vested under a strict vesting schedule of 1 year starting from 9/18/2021.

    750,000,000

    15%

    RewardDistributor L2

    Arbitrum

    0x2c07bc934974BbF413a4a4CeDA98713DCb8d9e16

    Staking Reward

    Arbitrum

    0x3702E3e2DB2b5d037c1dbB23Ab7A51d0Cc90BD0e

    SPA/USDs Farm Rewarder 1 (SPA)

    Arbitrum

    0x136C218Ff8E87eD68f851433685960819F06b1fE

    USDs/USDC Farm Vesting (SPA)

    Arbitrum

    0x638d76763dE9492b609b0d8830D8F626C5933A4D

    SPA/USDs Farm Vesting (SPA)

    Arbitrum

    0x03b35477cFD400dEdfAc06f40422491500cbc663

    SPA/USDs Farm Rewarder 2 (SPA)

    Arbitrum

    0x36033594EC23E0f0B187f767889Eb4C539F4aE03

    SPA/USDs Farm Vesting 2 (SPA)

    Arbitrum

    0xC0F0484a216AfF20E0ead1a1513cE40fe0AFe0fe

    SPA-Reserve-L2 multi-sig

    Arbitrum

    0xb56e5620A79cfe59aF7c0FcaE95aADbEA8ac32A1

    SPA Farm

    Arbitrum

    0xc150cbdDC5932258fAc768bEB4d2352D127039fd

    SPA Farm rewarder

    Arbitrum

    0x852afF031bb282C054B26628A799D7F3a896873e

    Bootstrap liquidity Arbitrum

    Arbitrum

    0xAF64e027D42bAc1C14277fd295De9Ae318eEF17E

    0x4a692fD139259a5b94Cad7753E3C96350b7F2B9f
    0xBA6ca0B9e7333f5e667816b85704c024AB250C9D
    0x8898A38Eb8E3104f7c98622b55260E014B3a0217
    0xD95791bcab484C0552833cB558d18d4D3F198AF9
    0xb56e5620A79cfe59aF7c0FcaE95aADbEA8ac32A1
    0xC6e00e0E3544C93460cdFb53E85C4528EF348265
    0xC6e00e0E3544C93460cdFb53E85C4528EF348265

    SPA 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

    rewardPercentage

    oracle

    Functions

    constructor

    initialize

    Contract initializer

    Parameters

    Name
    Type
    Description

    withdraw

    Emergency withdrawal function for unexpected situations

    Can only be called by the owner

    Parameters

    Name
    Type
    Description

    updateRewardPercentage

    Changes the reward percentage

    Example value for _newRewardPercentage = 5000 for 50%

    Parameters

    Name
    Type
    Description

    updateVeSpaRewarder

    Update veSpaRewarder address

    Parameters

    Name
    Type
    Description

    updateOracle

    Update oracle address

    Parameters

    Name
    Type
    Description

    buyUSDs

    Function to buy USDs for SPA for frontend

    Parameters

    Name
    Type
    Description

    getSPAReqdForUSDs

    Calculates and returns SPA amount required for _usdsAmount

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    buyUSDs

    Buy USDs for SPA if you want a different receiver

    Parameters

    Name
    Type
    Description

    distributeAndBurnSPA

    Sends available SPA in this contract to rewarder based on rewardPercentage and burns the rest

    getUsdsOutForSpa

    Returns the amount of USDS for SPA amount in

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _getUsdsOutForSpa

    Returns the amount of USDS for SPA amount in

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _getOracleData

    Retrieves price data from the oracle contract for SPA and USDS tokens.

    Returns

    Name
    Type
    Description

    _isValidRewardPercentage

    Checks if the provided reward percentage is valid.

    The reward percentage must be a non-zero value and should not exceed the maximum percentage value.

    Parameters

    Name
    Type
    Description

    Events

    BoughtBack

    Withdrawn

    SPARewarded

    SPABurned

    RewardPercentageUpdated

    VeSpaRewarderUpdated

    OracleUpdated

    Errors

    CannotWithdrawSPA

    InsufficientUSDsBalance

    uint256

    _veSpaRewarder

    address

    Rewarder's address

    _rewardPercentage

    uint256

    Percentage of SPA to be rewarded

    _token

    address

    Address of the asset to be withdrawn

    _receiver

    address

    Address of the receiver of tokens

    _amount

    uint256

    Amount of tokens to be withdrawn

    _newRewardPercentage

    uint256

    New Reward Percentage

    _newVeSpaRewarder

    address

    is the address of desired veSpaRewarder

    _newOracle

    address

    is the address of desired oracle

    _spaIn

    uint256

    Amount of SPA tokens

    _minUSDsOut

    uint256

    Minimum amount out in USDs

    _usdsAmount

    uint256

    USDs amount the user wants

    uint256

    Amount of SPA required

    _receiver

    address

    Receiver of USDs

    _spaIn

    uint256

    Amount of SPA tokens

    _minUSDsOut

    uint256

    Minimum amount out in USDs

    _spaIn

    uint256

    Amount of SPA tokens

    uint256

    Amount of USDs user will get

    _spaIn

    uint256

    Amount of SPA tokens

    uint256

    Amount of USDs user will get

    uint256

    uint256

    The price of USDS in SPA, the price of SPA in USDS, and their respective precisions.

    uint256

    uint256

    _rewardPercentage

    uint256

    The reward percentage to validate.

    address public veSpaRewarder;
    
    uint256 public rewardPercentage;
    
    address public oracle;
    
    constructor();
    
    function initialize(address _veSpaRewarder, uint256 _rewardPercentage) external initializer;
    
    function withdraw(address _token, address _receiver, uint256 _amount) external onlyOwner;
    
    function updateRewardPercentage(uint256 _newRewardPercentage) external onlyOwner;
    
    function updateVeSpaRewarder(address _newVeSpaRewarder) external onlyOwner;
    
    function updateOracle(address _newOracle) external onlyOwner;
    
    function buyUSDs(uint256 _spaIn, uint256 _minUSDsOut) external;
    
    function getSPAReqdForUSDs(uint256 _usdsAmount) external view returns (uint256);
    
    function buyUSDs(address _receiver, uint256 _spaIn, uint256 _minUSDsOut) public nonReentrant;
    
    function distributeAndBurnSPA() public;
    
    function getUsdsOutForSpa(uint256 _spaIn) public view returns (uint256);
    
    function _getUsdsOutForSpa(uint256 _spaIn) private view returns (uint256, uint256);
    
    function _getOracleData() private view returns (uint256, uint256, uint256, uint256);
    
    function _isValidRewardPercentage(uint256 _rewardPercentage) private pure;
    
    event BoughtBack(
        address indexed receiverOfUSDs, address indexed senderOfSPA, uint256 spaPrice, uint256 spaAmount, uint256 usdsAmount
    );
    
    event Withdrawn(address indexed token, address indexed receiver, uint256 amount);
    
    event SPARewarded(uint256 spaAmount);
    
    event SPABurned(uint256 spaAmount);
    
    event RewardPercentageUpdated(uint256 newRewardPercentage);
    
    event VeSpaRewarderUpdated(address newVeSpaRewarder);
    
    event OracleUpdated(address newOracle);
    
    error CannotWithdrawSPA();
    
    error InsufficientUSDsBalance(uint256 toSend, uint256 bal);

    Yield 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

    oracle

    buyback

    dripper

    buybackPercentage

    tokenData

    Functions

    constructor

    Constructor of the YieldReserve contract.

    Parameters

    Name
    Type
    Description

    swap

    Swap function to be called by frontend users.

    Parameters

    Name
    Type
    Description

    toggleSrcTokenPermission

    Allow or disallow a specific token for use as a source/input token.

    Parameters

    Name
    Type
    Description

    toggleDstTokenPermission

    Allow or disallow a specific token for use as a destination/output token.

    Reverts if caller is not owner.

    Parameters

    Name
    Type
    Description

    withdraw

    Emergency withdrawal function for unexpected situations.

    Parameters

    Name
    Type
    Description

    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.

    Parameters

    Name
    Type
    Description

    updateBuyback

    Update the address of the Buyback contract.

    Reverts if caller is not owner.

    Parameters

    Name
    Type
    Description

    updateOracle

    Update the address of the Oracle contract.

    Reverts if caller is not owner.

    Parameters

    Name
    Type
    Description

    updateDripper

    Update the address of the Dripper contract.

    Reverts if caller is not owner.

    Parameters

    Name
    Type
    Description

    updateVault

    Update the address of the VaultCore contract.

    Reverts if caller is not owner.

    Parameters

    Name
    Type
    Description

    swap

    Swap allowed source token for allowed destination token.

    Parameters

    Name
    Type
    Description

    mintUSDs

    Mints USDs directly with the allowed collaterals for USDs.

    Only collaterals configured in USDs vault are allowed to be used for minting.

    Parameters

    Name
    Type
    Description

    getTokenBForTokenA

    Get an estimate of the output token amount for a given input token amount.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _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.

    Events

    Swapped

    USDsMintedViaSwapper

    Withdrawn

    BuybackPercentageUpdated

    BuybackUpdated

    OracleUpdated

    VaultUpdated

    DripperUpdated

    USDsSent

    SrcTokenPermissionUpdated

    DstTokenPermissionUpdated

    Errors

    InvalidSourceToken

    InvalidDestinationToken

    AlreadyInDesiredState

    TokenPriceFeedMissing

    Structs

    TokenData

    address

    Address of the Dripper contract.

    uint256

    Minimum output tokens expected.

    uint256

    Minimum output tokens expected.

    _receiver

    address

    Receiver of the tokens.

    _buyback

    address

    Address of the Buyback contract.

    _vault

    address

    Address of the Vault.

    _oracle

    address

    Address of the Oracle.

    _srcToken

    address

    Source/Input token.

    _dstToken

    address

    Destination/Output token.

    _amountIn

    uint256

    Input token amount.

    _token

    address

    Address of the token to be allowed or disallowed.

    _isAllowed

    bool

    If set to true, the token will be allowed as a source/input token; otherwise, it will be disallowed.

    _token

    address

    Address of the token to be allowed or disallowed.

    _isAllowed

    bool

    If set to true, the token will be allowed as a destination/output token; otherwise, it will be disallowed.

    _token

    address

    Address of the asset to be withdrawn.

    _receiver

    address

    Address of the receiver of tokens.

    _amount

    uint256

    Amount of tokens to be withdrawn.

    _toBuyback

    uint256

    The percentage of USDs sent to Buyback (e.g., 3000 for 30%).

    _newBuyBack

    address

    New address of the Buyback contract.

    _newOracle

    address

    New address of the Oracle contract.

    _newDripper

    address

    New address of the Dripper contract.

    _newVault

    address

    New address of the VaultCore contract.

    _srcToken

    address

    Source/Input token.

    _dstToken

    address

    Destination/Output token.

    _amountIn

    uint256

    Input token amount.

    _token

    address

    Address of token to mint USDs with

    _srcToken

    address

    Input token address.

    _dstToken

    address

    Output token address.

    _amountIn

    uint256

    Input amount of _srcToken.

    uint256

    Estimated output token amount.

    _dripper

    _minAmountOut

    _minAmountOut

    address public vault;
    
    address public oracle;
    
    address public buyback;
    
    address public dripper;
    
    uint256 public buybackPercentage;
    
    mapping(address => TokenData) public tokenData;
    
    constructor(address _buyback, address _vault, address _oracle, address _dripper);
    
    function swap(address _srcToken, address _dstToken, uint256 _amountIn, uint256 _minAmountOut) external;
    
    function toggleSrcTokenPermission(address _token, bool _isAllowed) external onlyOwner;
    
    function toggleDstTokenPermission(address _token, bool _isAllowed) external onlyOwner;
    
    function withdraw(address _token, address _receiver, uint256 _amount) external onlyOwner;
    
    function updateBuybackPercentage(uint256 _toBuyback) public onlyOwner;
    
    function updateBuyback(address _newBuyBack) public onlyOwner;
    
    function updateOracle(address _newOracle) public onlyOwner;
    
    function updateDripper(address _newDripper) public onlyOwner;
    
    function updateVault(address _newVault) public onlyOwner;
    
    function swap(address _srcToken, address _dstToken, uint256 _amountIn, uint256 _minAmountOut, address _receiver)
        public
        nonReentrant;
    
    function mintUSDs(address _token) public nonReentrant;
    
    function getTokenBForTokenA(address _srcToken, address _dstToken, uint256 _amountIn) public view returns (uint256);
    
    function _sendUSDs() private;
    
    event Swapped(
        address indexed srcToken, address indexed dstToken, address indexed dstReceiver, uint256 amountIn, uint256 amountOut
    );
    
    event USDsMintedViaSwapper(address indexed collateralAddr, uint256 usdsMinted);
    
    event Withdrawn(address indexed token, address indexed receiver, uint256 amount);
    
    event BuybackPercentageUpdated(uint256 toBuyback);
    
    event BuybackUpdated(address newBuyback);
    
    event OracleUpdated(address newOracle);
    
    event VaultUpdated(address newVault);
    
    event DripperUpdated(address newDripper);
    
    event USDsSent(uint256 toBuyback, uint256 toDripper);
    
    event SrcTokenPermissionUpdated(address indexed token, bool isAllowed);
    
    event DstTokenPermissionUpdated(address indexed token, bool isAllowed);
    
    error InvalidSourceToken();
    
    error InvalidDestinationToken();
    
    error AlreadyInDesiredState();
    
    error TokenPriceFeedMissing();
    
    struct TokenData {
        bool srcAllowed;
        bool dstAllowed;
        uint160 conversionFactor;
    }

    Rewarder

    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

    APR_PRECISION

    REWARD_PERIOD

    DENOMINATOR

    ONE_YEAR

    REWARD_TOKEN

    REWARD_TOKEN_DECIMALS

    totalRewardRate

    rewarderFactory

    calibrationRestricted

    farmRewardConfigs

    _decimals

    Functions

    constructor

    initialize

    Initializer function of this contract.

    Parameters

    Name
    Type
    Description

    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.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    updateTokenManagerOfFarm

    Function to update the token manager's address in the farm.

    Parameters

    Name
    Type
    Description

    recoverRewardFundsOfFarm

    Function to recover reward funds from the farm.

    Parameters

    Name
    Type
    Description

    updateAPR

    Function to update APR.

    Parameters

    Name
    Type
    Description

    toggleCalibrationRestriction

    Function to toggle calibration restriction.

    Parameters

    Name
    Type
    Description

    recoverERC20

    Function to recover ERC20 tokens from this contract.

    Parameters

    Name
    Type
    Description

    getTokenAmounts

    Function to get token amounts value of underlying pool of the farm.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getFarmRewardConfig

    Function to get reward config for a farm.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    rewardsEndTime

    Function to calculate the time till which rewards are there for an LP.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    updateRewardConfig

    Function to update the REWARD_TOKEN configuration. This function calibrates reward so token manager must be updated to address of this contract in the farm.

    Parameters

    Name
    Type
    Description

    _initialize

    Internal initialize function.

    Parameters

    Name
    Type
    Description

    _isConfigured

    Function to check if the farm's reward is configured.

    Parameters

    Name
    Type
    Description

    _getTokenAmounts

    An internal function to get token amounts for the farm.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _hasRewardToken

    Function to check if the reward token of this contract is one of farm's reward token.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _validateNonZeroAddr

    Validate address.

    Parameters

    Name
    Type
    Description

    _calibrateReward

    _setRewardRate

    Function to set reward rate in the farm.

    Parameters

    Name
    Type
    Description

    _adjustGlobalRewardRate

    Function to adjust global rewards per second emitted for a reward token.

    Parameters

    Name
    Type
    Description

    _isValidFarm

    Function to validate farm.

    It checks that the farm should implement getTokenAmounts and have REWARD_TOKEN. as one of the reward tokens.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _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.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _normalizeAmount

    Function to normalize asset amounts to be of precision REWARD_TOKEN_DECIMALS.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _getPrice

    Function to fetch and get the price of a token.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _validatePriceFeed

    Function to validate price feed.

    Parameters

    Name
    Type
    Description

    _validateRewardPer

    Function to validate the no lockup fund's reward percentage.

    Parameters

    Name
    Type
    Description

    _rwdToken

    address

    Address of the reward token.

    _oracle

    address

    Address of the USDs Master Price Oracle.

    _admin

    address

    Admin/ deployer of this contract.

    _farm

    address

    Address of the farm for which the rewards are to be calibrated.

    rewardsToSend

    uint256

    Rewards which are sent to the farm.

    _farm

    address

    Farm's address in which the token manager is to be updated.

    _newManager

    address

    Address of the new token manager.

    _farm

    address

    Farm's address from which reward funds is to be recovered.

    _amount

    uint256

    Amount which is to be recovered.

    _farm

    address

    Address of the farm.

    _apr

    uint256

    APR in 1e8 precision.

    _farm

    address

    Address of farm for which calibration restriction is to be toggled.

    _token

    address

    Address of the token.

    _amount

    uint256

    Amount of the tokens.

    _farm

    address

    Address of the farm.

    <none>

    address[]

    Array of token addresses.

    <none>

    uint256[]

    _farm

    address

    Address of the farm.

    <none>

    FarmRewardConfig

    FarmRewardConfig Farm reward config.

    _farm

    address

    Address of the farm for which the end time is to be calculated.

    rewardsEndingOn

    uint256

    Timestamp in seconds till which the rewards are there in farm and in rewarder.

    _farm

    address

    Address of the farm for which the config is to be updated.

    _rewardConfig

    FarmRewardConfigInput

    The config which is to be set.

    _rwdToken

    address

    Address of the reward token.

    _oracle

    address

    Address of the USDs Master Price Oracle.

    _admin

    address

    Admin/ deployer of this contract.

    _rewarderFactory

    address

    Address of Rewarder factory contract.

    _farm

    address

    Address of the farm.

    _farm

    address

    Address of the farm.

    <none>

    address[]

    Array of token addresses.

    <none>

    uint256[]

    Array of token amounts.

    _farm

    address

    Address of the farm.

    <none>

    bool

    If farm has one of the reward token as reward token of this contract.

    _addr

    address

    Address to be validated.

    _farm

    address

    Address of the farm.

    _rwdRate

    uint128

    Reward per second to be emitted.

    _nonLockupRewardPer

    uint256

    Reward percentage to be allocated to no lockup fund.

    _oldRewardRate

    uint256

    Old emission rate.

    _newRewardRate

    uint256

    New emission rate.

    _farm

    address

    Address of the farm to be validated.

    _baseTokens

    address[]

    Array of base tokens.

    <none>

    bool

    bool True if farm is valid.

    _farm

    address

    Address of the farm.

    _baseTokens

    address[]

    Array of base token addresses to be considered for value calculation.

    <none>

    bool

    hasBaseTokens True if baseTokens are non redundant and are a subset of assets.

    _token

    address

    Address of the asset token.

    _amount

    uint256

    Amount of the token.

    <none>

    uint256

    Normalized amount of the token in _desiredPrecision.

    _token

    address

    Token for which the the price is to be fetched.

    _oracle

    address

    Address of the oracle contract.

    priceData

    IOracle.PriceData

    Price data of the token.

    _token

    address

    Token to be validated.

    _oracle

    address

    Address of the oracle.

    _percentage

    uint256

    No lockup fund's reward percentage to be validated.

    uint256 public constant MAX_PERCENTAGE = 1e4;
    uint256 public constant APR_PRECISION = 1e8;
    uint256 public constant REWARD_PERIOD = 1 weeks;
    uint256 public constant DENOMINATOR = 100;
    uint256 public constant ONE_YEAR = 365 days;
    address public REWARD_TOKEN;
    uint8 public REWARD_TOKEN_DECIMALS;
    uint256 public totalRewardRate;
    address public rewarderFactory;
    mapping(address => bool) public calibrationRestricted;
    mapping(address => FarmRewardConfig) internal farmRewardConfigs;
    mapping(address => uint8) private _decimals;
    constructor();
    function initialize(address _rwdToken, address _oracle, address _admin) external initializer;
    function calibrateReward(address _farm) external nonReentrant returns (uint256 rewardsToSend);
    function updateTokenManagerOfFarm(address _farm, address _newManager) external onlyOwner;
    function recoverRewardFundsOfFarm(address _farm, uint256 _amount) external onlyOwner;
    function updateAPR(address _farm, uint256 _apr) external onlyOwner nonReentrant;
    function toggleCalibrationRestriction(address _farm) external onlyOwner;
    function recoverERC20(address _token, uint256 _amount) external onlyOwner;
    function getTokenAmounts(address _farm) external view returns (address[] memory, uint256[] memory);
    function getFarmRewardConfig(address _farm) external view returns (FarmRewardConfig memory);
    function rewardsEndTime(address _farm) external view returns (uint256 rewardsEndingOn);
    function updateRewardConfig(address _farm, FarmRewardConfigInput memory _rewardConfig) public onlyOwner nonReentrant;
    function _initialize(address _rwdToken, address _oracle, address _admin, address _rewarderFactory) internal;
    function _isConfigured(address _farm) internal view;
    function _getTokenAmounts(address _farm) internal view virtual returns (address[] memory, uint256[] memory);
    function _hasRewardToken(address _farm) internal view virtual returns (bool);
    function _validateNonZeroAddr(address _addr) internal pure;
    function _calibrateReward(address _farm) private returns (uint256 rewardsToSend);
    function _setRewardRate(address _farm, uint128 _rwdRate, uint256 _nonLockupRewardPer) private;
    function _adjustGlobalRewardRate(uint256 _oldRewardRate, uint256 _newRewardRate) private;
    function _isValidFarm(address _farm, address[] memory _baseTokens) private returns (bool);
    function _hasBaseTokens(address _farm, address[] memory _baseTokens) private returns (bool);
    function _normalizeAmount(address _token, uint256 _amount) private view returns (uint256);
    function _getPrice(address _token, address _oracle) private view returns (IOracle.PriceData memory priceData);
    function _validatePriceFeed(address _token, address _oracle) private view;
    function _validateRewardPer(uint256 _percentage) private pure;

    CollateralManager

    Git Source

    Inherits: ICollateralManager, Ownable

    Author: Sperax Foundation

    This contract manages the addition and removal of collateral, configuration of collateral strategies, and allocation percentages.

    Collateral Manager interacts with the Vault and various strategies for collateral management.

    State Variables

    collateralCompositionUsed

    VAULT

    collaterals

    collateralInfo

    collateralStrategyInfo

    collateralStrategies

    Functions

    constructor

    Constructor to initialize the Collateral Manager

    Parameters

    Name
    Type
    Description

    addCollateral

    Register a collateral for mint & redeem in USDs

    Parameters

    Name
    Type
    Description

    updateCollateralData

    Update existing collateral configuration

    Parameters

    Name
    Type
    Description

    removeCollateral

    Un-list a collateral

    Parameters

    Name
    Type
    Description

    addCollateralStrategy

    Add a new strategy to collateral

    Parameters

    Name
    Type
    Description

    updateCollateralStrategy

    Update existing strategy for collateral

    Parameters

    Name
    Type
    Description

    removeCollateralStrategy

    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

    Parameters

    Name
    Type
    Description

    updateCollateralDefaultStrategy

    validateAllocation

    Validate allocation for a collateral

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getFeeCalibrationData

    Get the required data for mint

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getMintParams

    Get the required data for mint

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getRedeemParams

    Get the required data for USDs redemption

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getAllCollaterals

    Gets a list of all listed collaterals

    Returns

    Name
    Type
    Description

    getCollateralStrategies

    Gets a list of all strategies linked to a collateral

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    isValidStrategy

    Verifies if a strategy is linked to a collateral

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getCollateralInStrategies

    Get the amount of collateral in all Strategies

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getCollateralInVault

    Get the amount of collateral in vault

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getCollateralInAStrategy

    Get the amount of collateral allocated in a strategy

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    Events

    CollateralAdded

    CollateralRemoved

    CollateralInfoUpdated

    CollateralStrategyAdded

    CollateralStrategyUpdated

    CollateralStrategyRemoved

    Errors

    CollateralExists

    CollateralDoesNotExist

    CollateralStrategyExists

    CollateralStrategyMapped

    CollateralStrategyNotMapped

    CollateralNotSupportedByStrategy

    CollateralAllocationPaused

    CollateralStrategyInUse

    AllocationPercentageLowerThanAllocatedAmt

    IsDefaultStrategy

    Structs

    CollateralData

    StrategyData

    Logo

    uint256

    _vault

    address

    Address of the Vault contract

    _collateral

    address

    Address of the collateral

    _data

    CollateralBaseData

    Collateral configuration data

    _collateral

    address

    Address of the collateral

    _updateData

    CollateralBaseData

    Updated configuration for the collateral

    _collateral

    address

    Address of the collateral

    _collateral

    address

    Address of the collateral

    _strategy

    address

    Address of the strategy

    _allocationCap

    uint16

    Allocation capacity

    _collateral

    address

    Address of the collateral

    _strategy

    address

    Address of the strategy

    _allocationCap

    uint16

    Allocation capacity

    _collateral

    address

    Address of the collateral

    _strategy

    address

    Address of the strategy

    _collateral

    address

    Address of the collateral

    _strategy

    address

    Address of the desired strategy

    _amount

    uint256

    Amount to be allocated.

    bool

    True for valid allocation request.

    _collateral

    address

    Address of the collateral

    uint16

    Base fee config for collateral (baseMintFee, baseRedeemFee, composition, totalCollateral)

    uint16

    uint16

    _collateral

    address

    Address of the collateral

    mintData

    CollateralMintData

    mintData

    _collateral

    address

    Address of the collateral

    redeemData

    CollateralRedeemData

    redeemData

    address[]

    List of addresses representing all listed collaterals

    _collateral

    address

    Address of the collateral

    address[]

    List of addresses representing available strategies for the collateral

    _collateral

    address

    Address of the collateral

    _strategy

    address

    Address of the strategy

    bool

    True if the strategy is linked to the collateral, otherwise False

    _collateral

    address

    Address of the collateral

    amountInStrategies

    uint256

    amountInStrategies

    _collateral

    address

    Address of the collateral

    amountInVault

    uint256

    amountInVault

    _collateral

    address

    Address of the collateral

    _strategy

    address

    Address of the strategy

    allocatedAmt

    uint256

    Allocated amount

    uint16 public collateralCompositionUsed;
    
    address public immutable VAULT;
    
    address[] private collaterals;
    
    mapping(address => CollateralData) public collateralInfo;
    
    mapping(address => mapping(address => StrategyData)) private collateralStrategyInfo;
    
    mapping(address => address[]) private collateralStrategies;
    
    constructor(address _vault);
    
    function addCollateral(address _collateral, CollateralBaseData memory _data) external onlyOwner;
    
    function updateCollateralData(address _collateral, CollateralBaseData memory _updateData) external onlyOwner;
    
    function removeCollateral(address _collateral) external onlyOwner;
    
    function addCollateralStrategy(address _collateral, address _strategy, uint16 _allocationCap) external onlyOwner;
    
    function updateCollateralStrategy(address _collateral, address _strategy, uint16 _allocationCap) external onlyOwner;
    
    function removeCollateralStrategy(address _collateral, address _strategy) external onlyOwner;
    
    function updateCollateralDefaultStrategy(address _collateral, address _strategy) external onlyOwner;
    
    function validateAllocation(address _collateral, address _strategy, uint256 _amount) external view returns (bool);
    
    function getFeeCalibrationData(address _collateral) external view returns (uint16, uint16, uint16, uint256);
    
    function getMintParams(address _collateral) external view returns (CollateralMintData memory mintData);
    
    function getRedeemParams(address _collateral) external view returns (CollateralRedeemData memory redeemData);
    
    function getAllCollaterals() external view returns (address[] memory);
    
    function getCollateralStrategies(address _collateral) external view returns (address[] memory);
    
    function isValidStrategy(address _collateral, address _strategy) external view returns (bool);
    
    function getCollateralInStrategies(address _collateral) public view returns (uint256 amountInStrategies);
    
    function getCollateralInVault(address _collateral) public view returns (uint256 amountInVault);
    
    function getCollateralInAStrategy(address _collateral, address _strategy) public view returns (uint256 allocatedAmt);
    
    event CollateralAdded(address collateral, CollateralBaseData data);
    
    event CollateralRemoved(address collateral);
    
    event CollateralInfoUpdated(address collateral, CollateralBaseData data);
    
    event CollateralStrategyAdded(address collateral, address strategy);
    
    event CollateralStrategyUpdated(address collateral, address strategy);
    
    event CollateralStrategyRemoved(address collateral, address strategy);
    
    error CollateralExists();
    
    error CollateralDoesNotExist();
    
    error CollateralStrategyExists();
    
    error CollateralStrategyMapped();
    
    error CollateralStrategyNotMapped();
    
    error CollateralNotSupportedByStrategy();
    
    error CollateralAllocationPaused();
    
    error CollateralStrategyInUse();
    
    error AllocationPercentageLowerThanAllocatedAmt();
    
    error IsDefaultStrategy();
    
    struct CollateralData {
        bool mintAllowed;
        bool redeemAllowed;
        bool allocationAllowed;
        bool exists;
        address defaultStrategy;
        uint16 baseMintFee;
        uint16 baseRedeemFee;
        uint16 downsidePeg;
        uint16 desiredCollateralComposition;
        uint16 collateralCapacityUsed;
        uint256 conversionFactor;
    }
    
    struct StrategyData {
        uint16 allocationCap;
        bool exists;
    }

    BaseStrategy

    Git Source

    Inherits: Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable

    Author: Sperax Foundation

    Contract acts as a single interface for implementing specific yield-earning strategies.

    State Variables

    vault

    withdrawSlippage

    depositSlippage

    harvestIncentiveRate

    assetsMapped

    rewardTokenAddress

    assetToPToken

    gap

    Functions

    onlyVault

    onlyVaultOrOwner

    constructor

    updateVault

    Update the linked vault contract.

    Parameters

    Name
    Type
    Description

    updateHarvestIncentiveRate

    Updates the HarvestIncentive rate for the user.

    Parameters

    Name
    Type
    Description

    recoverERC20

    A function to recover any erc20 token sent to this strategy mistakenly.

    Only callable by owner.

    reverts if amount > balance.

    Parameters

    Name
    Type
    Description

    deposit

    Deposit an amount of asset into the platform.

    Parameters

    Name
    Type
    Description

    withdraw

    Withdraw an amount of asset from the platform.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    withdrawToVault

    Withdraw an amount of asset from the platform to vault.

    Parameters

    Name
    Type
    Description

    collectInterest

    Withdraw the interest earned of asset from the platform.

    Parameters

    Name
    Type
    Description

    collectReward

    Collect accumulated reward token and send to Vault.

    checkBalance

    Get the amount of a specific asset held in the strategy, excluding the interest.

    Curve: assuming balanced withdrawal.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    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.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    checkInterestEarned

    AAVE: Get the interest earned on a specific asset. Curve: Get the total interest earned.

    Curve: to avoid double-counting, _asset has to be of index 'entryTokenIndex'.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    checkRewardEarned

    Get the amount of claimable reward.

    Returns

    Name
    Type
    Description

    checkLPTokenBalance

    Get the total LP token balance for a asset.

    Parameters

    Name
    Type
    Description

    supportsCollateral

    Check if an asset/collateral is supported.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    updateSlippage

    Change to a new depositSlippage & withdrawSlippage.

    Parameters

    Name
    Type
    Description

    _initialize

    Initialize the base properties of the strategy.

    Parameters

    Name
    Type
    Description

    _setPTokenAddress

    Provide support for asset by passing its pToken address. Add to internal mappings and execute the platform specific, abstract method _abstractSetPToken.

    Parameters

    Name
    Type
    Description

    _removePTokenAddress

    Remove a supported asset by passing its index. This method can only be called by the system owner.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _splitAndSendReward

    Splits 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.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _abstractSetPToken

    Call the necessary approvals for the underlying strategy.

    Parameters

    Name
    Type
    Description

    Events

    VaultUpdated

    YieldReceiverUpdated

    PTokenAdded

    PTokenRemoved

    Deposit

    Withdrawal

    SlippageUpdated

    HarvestIncentiveCollected

    HarvestIncentiveRateUpdated

    InterestCollected

    RewardTokenCollected

    Errors

    CallerNotVault

    CallerNotVaultOrOwner

    PTokenAlreadySet

    InvalidIndex

    CollateralNotSupported

    InvalidAssetLpPair

    CollateralAllocated

    Structs

    RewardData

    uint256

    to be split and sent.

    _newVault

    address

    Address of the new Vault.

    _newRate

    uint16

    new Desired rate.

    token

    address

    Address of the token.

    receiver

    address

    Receiver of the token.

    amount

    uint256

    Amount to be recovered.

    _asset

    address

    Address for the asset.

    _amount

    uint256

    Units of asset to deposit.

    _recipient

    address

    Address to which the asset should be sent.

    _asset

    address

    Address of the asset.

    _amount

    uint256

    Units of asset to withdraw.

    amountReceived

    uint256

    The actual amount received.

    _asset

    address

    Address of the asset.

    _amount

    uint256

    Units of asset to withdraw.

    _asset

    address

    Address of the asset.

    _asset

    address

    Address of the asset.

    uint256

    uint256 Balance of _asset in the strategy.

    _asset

    address

    Address of the asset.

    uint256

    uint256 Available balance inside the strategy for _asset.

    _asset

    address

    Address of the asset.

    uint256

    uint256 Amount of interest earned.

    RewardData[]

    struct array of type RewardData (address token, uint256 amount).

    _asset

    address

    Address of the asset.

    _asset

    address

    Address of the asset.

    bool

    bool Whether asset is supported.

    _depositSlippage

    uint16

    Slippage tolerance for allocation.

    _withdrawSlippage

    uint16

    Slippage tolerance for withdrawal.

    _vault

    address

    Address of the USDs Vault.

    _depositSlippage

    uint16

    Allowed max slippage for Deposit.

    _withdrawSlippage

    uint16

    Allowed max slippage for withdraw.

    _asset

    address

    Address for the asset.

    _pToken

    address

    Address for the corresponding platform token.

    _assetIndex

    uint256

    Index of the asset to be removed.

    asset

    address

    address which is removed.

    _token

    address

    Address of the reward token.

    _yieldReceiver

    address

    Address of the yield receiver.

    _harvestor

    address

    Address of the harvestor.

    uint256

    uint256 Harvested amount sent to yield receiver.

    _asset

    address

    Address of the asset.

    _pToken

    address

    Address of the corresponding receipt token.

    _amount

    address public vault;
    
    uint16 public withdrawSlippage;
    
    uint16 public depositSlippage;
    
    uint16 public harvestIncentiveRate;
    
    address[] internal assetsMapped;
    
    address[] public rewardTokenAddress;
    
    mapping(address => address) public assetToPToken;
    
    uint256[40] private __gap__;
    
    modifier onlyVault();
    
    modifier onlyVaultOrOwner();
    
    constructor();
    
    function updateVault(address _newVault) external onlyOwner;
    
    function updateHarvestIncentiveRate(uint16 _newRate) external onlyOwner;
    
    function recoverERC20(address token, address receiver, uint256 amount) external onlyOwner;
    
    function deposit(address _asset, uint256 _amount) external virtual;
    
    function withdraw(address _recipient, address _asset, uint256 _amount)
        external
        virtual
        returns (uint256 amountReceived);
    
    function withdrawToVault(address _asset, uint256 _amount) external virtual returns (uint256 amount);
    
    function collectInterest(address _asset) external virtual;
    
    function collectReward() external virtual;
    
    function checkBalance(address _asset) external view virtual returns (uint256);
    
    function checkAvailableBalance(address _asset) external view virtual returns (uint256);
    
    function checkInterestEarned(address _asset) external view virtual returns (uint256);
    
    function checkRewardEarned() external view virtual returns (RewardData[] memory);
    
    function checkLPTokenBalance(address _asset) external view virtual returns (uint256);
    
    function supportsCollateral(address _asset) external view virtual returns (bool);
    
    function updateSlippage(uint16 _depositSlippage, uint16 _withdrawSlippage) public onlyOwner;
    
    function _initialize(address _vault, uint16 _depositSlippage, uint16 _withdrawSlippage) internal;
    
    function _setPTokenAddress(address _asset, address _pToken) internal;
    
    function _removePTokenAddress(uint256 _assetIndex) internal returns (address asset);
    
    function _splitAndSendReward(address _token, address _yieldReceiver, address _harvestor, uint256 _amount)
        internal
        returns (uint256);
    
    function _abstractSetPToken(address _asset, address _pToken) internal virtual;
    
    event VaultUpdated(address newVaultAddr);
    
    event YieldReceiverUpdated(address newYieldReceiver);
    
    event PTokenAdded(address indexed asset, address pToken);
    
    event PTokenRemoved(address indexed asset, address pToken);
    
    event Deposit(address indexed asset, uint256 amount);
    
    event Withdrawal(address indexed asset, uint256 amount);
    
    event SlippageUpdated(uint16 depositSlippage, uint16 withdrawSlippage);
    
    event HarvestIncentiveCollected(address indexed token, address indexed harvestor, uint256 amount);
    
    event HarvestIncentiveRateUpdated(uint16 newRate);
    
    event InterestCollected(address indexed asset, address indexed recipient, uint256 amount);
    
    event RewardTokenCollected(address indexed rwdToken, address indexed recipient, uint256 amount);
    
    error CallerNotVault(address caller);
    
    error CallerNotVaultOrOwner(address caller);
    
    error PTokenAlreadySet(address collateral, address pToken);
    
    error InvalidIndex();
    
    error CollateralNotSupported(address asset);
    
    error InvalidAssetLpPair(address asset, address lpToken);
    
    error CollateralAllocated(address asset);
    
    struct RewardData {
        address token;
        uint256 amount;
    }

    Vault

    Vault

    • Responsible for following actions on USDs:

      • Mint USDs token

      • Redeem USDs token

      • Carry out rebase for USDs token

      • Allocate collateral to strategies

      • Withdraw collateral from strategies

    Contract documentation

    Inherits: Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable

    Author: Sperax Foundation

    This contract enables users to mint and redeem USDs with allowed collaterals.

    It also allocates collateral to strategies based on the Collateral Manager contract.

    State Variables

    feeVault

    yieldReceiver

    collateralManager

    feeCalculator

    oracle

    rebaseManager

    Functions

    constructor

    initialize

    updateFeeVault

    Updates the address receiving fee.

    Parameters

    Name
    Type
    Description

    updateYieldReceiver

    Updates the address receiving yields from strategies.

    Parameters

    Name
    Type
    Description

    updateCollateralManager

    Updates the address having the configuration for collaterals.

    Parameters

    Name
    Type
    Description

    updateRebaseManager

    Updates the address having the configuration for rebases.

    Parameters

    Name
    Type
    Description

    updateFeeCalculator

    Updates the fee calculator library.

    Parameters

    Name
    Type
    Description

    updateOracle

    Updates the price oracle address.

    Parameters

    Name
    Type
    Description

    allocate

    Allocates _amount of _collateral to _strategy.

    Parameters

    Name
    Type
    Description

    mint

    Mint USDs by depositing collateral.

    Parameters

    Name
    Type
    Description

    mintBySpecifyingCollateralAmt

    Mint USDs by depositing collateral (backward compatibility).

    This function is for backward compatibility.

    Parameters

    Name
    Type
    Description

    redeem

    Redeem USDs for _collateral.

    In case where there is not sufficient collateral available in the vault, the collateral is withdrawn from the default strategy configured for the collateral.

    Parameters

    Name
    Type
    Description

    redeem

    Redeem USDs for _collateral from a specific strategy.

    Parameters

    Name
    Type
    Description

    redeemView

    Get the expected redeem result.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    redeemView

    Get the expected redeem result from a specific strategy.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    rebase

    Rebase USDs to share earned yield with the USDs holders.

    If Rebase manager returns a non-zero value, it calls the rebase function on the USDs contract.

    mintView

    Get the expected mint result (USDs amount, fee).

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _mint

    Mint USDs by depositing collateral.

    Mints USDs by locking collateral based on user input, ensuring a minimum expected minted amount is met.

    If the minimum expected amount is not met, the transaction will revert.

    Fee is collected, and collateral is transferred accordingly.

    A rebase operation is triggered after minting.

    Parameters

    Name
    Type
    Description

    _redeem

    Redeem USDs for collateral.

    Redeems USDs for collateral, ensuring a minimum expected collateral amount is met.

    If the minimum expected collateral amount is not met, the transaction will revert.

    Fee is collected, collateral is transferred, and a rebase operation is triggered.

    Parameters

    Name
    Type
    Description

    _redeemView

    Get the expected redeem result.

    Calculates the expected results of a redemption, including collateral amount, fees, and strategy-specific details.

    Ensures that the redemption is allowed for the specified collateral.

    Calculates fees, burn amounts, and collateral amounts based on prices and conversion factors.

    Determines if collateral needs to be withdrawn from a strategy, and if so, checks the availability of collateral in the strategy.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    Events

    FeeVaultUpdated

    YieldReceiverUpdated

    CollateralManagerUpdated

    FeeCalculatorUpdated

    RebaseManagerUpdated

    OracleUpdated

    Minted

    Redeemed

    RebasedUSDs

    Allocated

    Errors

    AllocationNotAllowed

    RedemptionPausedForCollateral

    InsufficientCollateral

    InvalidStrategy

    MintFailed

    Farm

    Git Source

    Inherits: FarmStorage, OwnableUpgradeable, ReentrancyGuardUpgradeable, MulticallUpgradeable

    Author: Sperax Foundation.

    This contract contains the core logic for the Sperax farms.

    Functions

    constructor

    withdraw

    Function to be called to withdraw deposit.

    Parameters

    Name
    Type
    Description

    claimRewards

    A function to be called by the depositor to claim rewards.

    Parameters

    Name
    Type
    Description

    initiateCooldown

    Function to be called to initiate cooldown for a staked deposit.

    _depositId is corresponding to the user's deposit.

    Parameters

    Name
    Type
    Description

    addRewards

    Add rewards to the farm.

    Parameters

    Name
    Type
    Description

    updateCooldownPeriod

    Update the cooldown period.

    Parameters

    Name
    Type
    Description

    farmPauseSwitch

    Pause / UnPause the farm.

    Parameters

    Name
    Type
    Description

    closeFarm

    A function to explicitly close the farm.

    Recovers remaining non accrued rewards.

    recoverERC20

    Recover erc20 tokens other than the reward tokens.

    Parameters

    Name
    Type
    Description

    recoverRewardFunds

    Get the remaining reward balance out of the farm.

    Function recovers minOf(_amount, rewardsLeft).

    Parameters

    Name
    Type
    Description

    setRewardRate

    Function to update reward params for a fund.

    Parameters

    Name
    Type
    Description

    updateRewardData

    Transfer the tokenManagerRole to other user.

    Only the existing tokenManager for a reward can call this function.

    Parameters

    Name
    Type
    Description

    computeRewards

    Function to compute the total accrued rewards for a deposit for each subscription.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getRewardFunds

    Get the reward fund details.

    Returns

    Name
    Type
    Description

    getRewardData

    Get the reward details for specified reward token.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getDepositInfo

    Get deposit info for a deposit id.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getNumSubscriptions

    Get number of subscriptions for an account.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getSubscriptionInfo

    Get subscription stats for a deposit.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getRewardRates

    Get reward rates for a rewardToken.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getRewardFundInfo

    Get farm reward fund info.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    getRewardTokens

    Function to get the reward tokens added in the farm.

    Returns

    Name
    Type
    Description

    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.

    Returns

    Name
    Type
    Description

    updateFarmRewardData

    Function to update the FarmRewardData for all funds.

    claimRewardsTo

    Claim rewards and send it to another account.

    Only the depositor can call this function.

    Parameters

    Name
    Type
    Description

    updateFarmStartTime

    Update the farm start time.

    Can be updated only before the farm start. New start time should be in future.

    Parameters

    Name
    Type
    Description

    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.

    Returns

    Name
    Type
    Description

    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.

    Returns

    Name
    Type
    Description

    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.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _recoverERC20

    _deposit

    Common logic for deposit in the farm.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _initiateCooldown

    Common logic for initiating cooldown.

    Parameters

    Name
    Type
    Description

    _withdraw

    Common logic for withdraw.

    Parameters

    Name
    Type
    Description

    _updateAndClaimFarmRewards

    Claim rewards for the user.

    NOTE: any function calling this private function should be marked as non-reentrant.

    Parameters

    Name
    Type
    Description

    _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.

    Parameters

    Name
    Type
    Description

    _recoverRewardFunds

    Get the remaining reward balance out of the farm.

    Function recovers minOf(_amount, rewardsLeft).

    In case of partial withdraw of funds, the reward rate has to be set manually again.

    Parameters

    Name
    Type
    Description

    _setRewardRate

    Function to update reward params for a fund.

    Parameters

    Name
    Type
    Description

    _setupFarm

    Function to setup the reward funds and initialize the farm global params during construction.

    Parameters

    Name
    Type
    Description

    _addRewardData

    Adds new reward token to the farm.

    Parameters

    Name
    Type
    Description

    _updateLastRewardAccrualTime

    Update the last reward accrual time.

    _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.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _validateDeposit

    Validate the deposit for account.

    Parameters

    Name
    Type
    Description

    _validateNotRecentDeposit

    A function to validate deposit ts to prevent flash loan vulnerabilities

    Reverts when deposit made in the same transaction.

    Parameters

    Name
    Type
    Description

    _validateFarmOpen

    Validate if farm is open. Revert otherwise.

    This function can be overridden to add any new/additional logic.

    _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.

    _validateTokenManager

    Validate the caller is the token Manager. Revert otherwise.

    Parameters

    Name
    Type
    Description

    _validateRewardToken

    Validate the reward token is valid.

    Parameters

    Name
    Type
    Description

    _getRewardAccrualTimeElapsed

    Get the time elapsed since the last reward accrual.

    Returns

    Name
    Type
    Description

    _validateCooldownPeriod

    An internal function to validate cooldown period.

    Parameters

    Name
    Type
    Description

    _validateNonZeroAddr

    Validate address.

    Parameters

    Name
    Type
    Description

    _subscribeRewardFund

    Add subscription to the reward fund for a deposit.

    Parameters

    Name
    Type
    Description

    _unsubscribeRewardFund

    Unsubscribe a reward fund from a deposit.

    The rewards claimed from the reward fund is persisted in the event.

    Parameters

    Name
    Type
    Description

    uint256

    Expiry time of the transaction.

    uint256

    _deadline

    uint256

    Expiry time of the transaction.

    uint256

    Expiry time of the transaction.

    uint256

    Expiry time of the transaction.

    _strategy

    address

    Address of the strategy to withdraw excess collateral from.

    uint256

    Amount of collateral released from Vault.

    strategyAmt

    uint256

    Amount of collateral to withdraw from the strategy.

    uint256

    Amount of collateral released from Vault.

    strategyAmt

    uint256

    Amount of collateral to withdraw from the strategy.

    uint256

    Deadline timestamp for executing mint.

    uint256

    Deadline timestamp for executing the redemption.

    _strategyAddr

    address

    Address of the strategy to withdraw from.

    uint256

    Amount of collateral released from Vault.

    strategyAmt

    uint256

    Amount of collateral to withdraw from the strategy.

    strategy

    IStrategy

    Strategy contract to withdraw collateral from.

    _feeVault

    address

    New desired SPABuyback address.

    _yieldReceiver

    address

    New desired yield receiver address.

    _collateralManager

    address

    New desired collateral manager address.

    _rebaseManager

    address

    New desired rebase manager address.

    _feeCalculator

    address

    New desired fee calculator address.

    _oracle

    address

    New desired oracle address.

    _collateral

    address

    Address of the desired collateral.

    _strategy

    address

    Address of the desired strategy.

    _amount

    uint256

    Amount of collateral to be allocated.

    _collateral

    address

    Address of the collateral.

    _collateralAmt

    uint256

    Amount of collateral to mint USDs with.

    _minUSDSAmt

    uint256

    Minimum expected amount of USDs to be minted.

    _collateral

    address

    Address of the collateral.

    _collateralAmt

    uint256

    Amount of collateral to mint USDs with.

    _minUSDSAmt

    uint256

    Minimum expected amount of USDs to be minted.

    _collateral

    address

    Address of the collateral.

    _usdsAmt

    uint256

    Amount of USDs to be redeemed.

    _minCollAmt

    uint256

    Minimum expected amount of collateral to be received.

    _collateral

    address

    Address of the collateral.

    _usdsAmt

    uint256

    Amount of USDs to be redeemed.

    _minCollAmt

    uint256

    Minimum expected amount of collateral to be received.

    _collateral

    address

    Desired collateral address.

    _usdsAmt

    uint256

    Amount of USDs to be redeemed.

    calculatedCollateralAmt

    uint256

    Expected amount of collateral to be released based on the price calculation.

    usdsBurnAmt

    uint256

    Expected amount of USDs to be burnt in the process.

    feeAmt

    uint256

    Amount of USDs collected as fee for redemption.

    _collateral

    address

    Desired collateral address.

    _usdsAmt

    uint256

    Amount of USDs to be redeemed.

    _strategyAddr

    address

    Address of strategy to redeem from.

    calculatedCollateralAmt

    uint256

    Expected amount of collateral to be released based on the price calculation.

    usdsBurnAmt

    uint256

    Expected amount of USDs to be burnt in the process.

    feeAmt

    uint256

    Amount of USDs collected as fee for redemption.

    _collateral

    address

    Address of collateral.

    _collateralAmt

    uint256

    Amount of collateral.

    uint256

    Returns the expected USDs mint amount and fee for minting.

    uint256

    _collateral

    address

    Address of the collateral.

    _collateralAmt

    uint256

    Amount of collateral to deposit.

    _minUSDSAmt

    uint256

    Minimum expected amount of USDs to be minted.

    _collateral

    address

    Address of the collateral to receive.

    _usdsAmt

    uint256

    Amount of USDs to redeem.

    _minCollateralAmt

    uint256

    Minimum expected collateral amount to be received.

    _collateral

    address

    Desired collateral address.

    _usdsAmt

    uint256

    Amount of USDs to be redeemed.

    _strategyAddr

    address

    Address of the strategy to redeem from.

    calculatedCollateralAmt

    uint256

    Expected amount of collateral to be released based on the price calculation.

    usdsBurnAmt

    uint256

    Expected amount of USDs to be burnt in the process.

    feeAmt

    uint256

    Amount of USDs collected as a fee for redemption.

    Git Source

    _deadline

    _deadline

    _deadline

    vaultAmt

    vaultAmt

    _deadline

    _deadline

    vaultAmt

    _depositId

    uint256

    The id of the deposit.

    _depositId

    uint256

    The id of the deposit.

    _depositId

    uint256

    The id of the deposit to be locked.

    _rwdToken

    address

    The reward token's address.

    _amount

    uint256

    The amount of reward tokens to add.

    _newCooldownPeriod

    uint256

    The new cooldown period (in days). E.g: 7 means 7 days.

    _isPaused

    bool

    Desired state of the farm (true to pause the farm).

    _token

    address

    Address of token to be recovered.

    _rwdToken

    address

    The reward token's address.

    _amount

    uint256

    The amount of the reward tokens to be withdrawn.

    _rwdToken

    address

    The reward token's address.

    _newRewardRates

    uint128[]

    The new reward rate for the fund (includes the precision).

    _rwdToken

    address

    The reward token's address.

    _newTknManager

    address

    Address of the new token manager.

    _account

    address

    The user's address.

    _depositId

    uint256

    The id of the deposit.

    rewards

    uint256[][]

    The total accrued rewards for the deposit for each subscription (uint256[][]).

    <none>

    RewardFund[]

    The available reward funds' details for all the reward funds.

    _rwdToken

    address

    The address of the reward token.

    <none>

    RewardData

    The available reward details for the specified reward token.

    _depositId

    uint256

    The id of the deposit.

    <none>

    Deposit

    The deposit info (Deposit).

    _depositId

    uint256

    The deposit id.

    <none>

    uint256

    The number of subscriptions for the deposit.

    _depositId

    uint256

    The deposit id.

    _subscriptionId

    uint256

    The subscription's id.

    <none>

    Subscription

    The subscription info (Subscription).

    _rwdToken

    address

    The reward token's address.

    <none>

    uint256[]

    The reward rates for the reward token (uint256[]).

    _fundId

    uint8

    The fund's id.

    <none>

    RewardFund

    The reward fund info (RewardFund).

    <none>

    address[]

    The reward tokens added in the farm.

    <none>

    address[]

    Tokens associated with the farm's pool.

    <none>

    uint256[]

    Amounts associated with the farm's liquidity.

    _account

    address

    To receive the rewards.

    _depositId

    uint256

    The id of the deposit.

    _newStartTime

    uint256

    The new farm start time.

    <none>

    bool

    bool True if farm is open.

    <none>

    bool

    bool True if farm is active.

    _rwdToken

    address

    The address of the reward token.

    <none>

    uint256

    The available reward balance for the specified reward token.

    _account

    address

    Address of the depositor.

    _lockup

    bool

    Lockup option for the deposit.

    _liquidity

    uint256

    Liquidity amount to be added to the pool.

    <none>

    uint256

    The deposit id.

    _depositId

    uint256

    User's deposit Id.

    _depositId

    uint256

    User's deposit id.

    _depositId

    uint256

    The id of the deposit.

    _depositId

    uint256

    The id of the deposit.

    _receiver

    address

    The receiver of the rewards (Could be different from depositor)

    _rwdToken

    address

    The reward token's address.

    _amount

    uint256

    The amount of the reward token to be withdrawn.

    _rwdToken

    address

    The reward token's address.

    _newRewardRates

    uint128[]

    The new reward rate for the fund (includes the precision).

    _farmId

    string

    ID of the farm. E.g: Demeter_Camelot_V2.

    _farmStartTime

    uint256

    - Farm start time.

    _cooldownPeriod

    uint256

    - Cooldown period in days for locked deposits. E.g: 7 means 7 days.

    _rwdTokenData

    RewardTokenData[]

    - Reward data for each reward token.

    _token

    address

    Address of the reward token to be added.

    _tknManager

    address

    Address of the reward token Manager.

    _rwdId

    uint8

    Id of the reward token.

    _fundId

    uint8

    Id of the reward fund.

    _time

    uint256

    Time interval for the reward computation.

    _alreadyAccRewardBal

    uint256

    Already accrued reward balance.

    <none>

    uint256

    accRewards Accrued rewards for the given _rwdId, _fundId and _time.

    _account

    address

    Address of the caller to be checked against depositor.

    _depositId

    uint256

    Id of the deposit.

    _depositTs

    uint256

    depositTs of user's deposit. (It represents deposit ts or increaseDeposit ts)

    _rwdToken

    address

    Address of reward token.

    _rwdToken

    address

    Address of reward token.

    <none>

    uint256

    time The time elapsed since the last reward accrual.

    _cooldownPeriod

    uint256

    Period to be validated.

    _addr

    address

    Address to be validated.

    _fundId

    uint8

    The reward fund id.

    _depositId

    uint256

    The unique ID of the deposit.

    _liquidity

    uint256

    The liquidity of the deposit.

    _fundId

    uint8

    The reward fund id.

    _depositId

    uint256

    The deposit id corresponding to the user.

    address public feeVault;
    
    address public yieldReceiver;
    
    address public collateralManager;
    
    address public feeCalculator;
    
    address public oracle;
    
    address public rebaseManager;
    
    constructor();
    
    function initialize() external initializer;
    
    function updateFeeVault(address _feeVault) external onlyOwner;
    
    function updateYieldReceiver(address _yieldReceiver) external onlyOwner;
    
    function updateCollateralManager(address _collateralManager) external onlyOwner;
    
    function updateRebaseManager(address _rebaseManager) external onlyOwner;
    
    function updateFeeCalculator(address _feeCalculator) external onlyOwner;
    
    function updateOracle(address _oracle) external onlyOwner;
    
    function allocate(address _collateral, address _strategy, uint256 _amount) external nonReentrant;
    
    function mint(address _collateral, uint256 _collateralAmt, uint256 _minUSDSAmt, uint256 _deadline)
        external
        nonReentrant;
    
    function mintBySpecifyingCollateralAmt(
        address _collateral,
        uint256 _collateralAmt,
        uint256 _minUSDSAmt,
        uint256,
        uint256 _deadline
    ) external nonReentrant;
    
    function redeem(address _collateral, uint256 _usdsAmt, uint256 _minCollAmt, uint256 _deadline) external nonReentrant;
    
    function redeem(address _collateral, uint256 _usdsAmt, uint256 _minCollAmt, uint256 _deadline, address _strategy)
        external
        nonReentrant;
    
    function redeemView(address _collateral, uint256 _usdsAmt)
        external
        view
        returns (
            uint256 calculatedCollateralAmt,
            uint256 usdsBurnAmt,
            uint256 feeAmt,
            uint256 vaultAmt,
            uint256 strategyAmt
        );
    
    function redeemView(address _collateral, uint256 _usdsAmt, address _strategyAddr)
        external
        view
        returns (
            uint256 calculatedCollateralAmt,
            uint256 usdsBurnAmt,
            uint256 feeAmt,
            uint256 vaultAmt,
            uint256 strategyAmt
        );
    
    function rebase() public;
    
    function mintView(address _collateral, uint256 _collateralAmt) public view returns (uint256, uint256);
    
    function _mint(address _collateral, uint256 _collateralAmt, uint256 _minUSDSAmt, uint256 _deadline) private;
    
    function _redeem(
        address _collateral,
        uint256 _usdsAmt,
        uint256 _minCollateralAmt,
        uint256 _deadline,
        address _strategyAddr
    ) private;
    
    function _redeemView(address _collateral, uint256 _usdsAmt, address _strategyAddr)
        private
        view
        returns (
            uint256 calculatedCollateralAmt,
            uint256 usdsBurnAmt,
            uint256 feeAmt,
            uint256 vaultAmt,
            uint256 strategyAmt,
            IStrategy strategy
        );
    
    event FeeVaultUpdated(address newFeeVault);
    
    event YieldReceiverUpdated(address newYieldReceiver);
    
    event CollateralManagerUpdated(address newCollateralManager);
    
    event FeeCalculatorUpdated(address newFeeCalculator);
    
    event RebaseManagerUpdated(address newRebaseManager);
    
    event OracleUpdated(address newOracle);
    
    event Minted(
        address indexed wallet, address indexed collateralAddr, uint256 usdsAmt, uint256 collateralAmt, uint256 feeAmt
    );
    
    event Redeemed(
        address indexed wallet, address indexed collateralAddr, uint256 usdsAmt, uint256 collateralAmt, uint256 feeAmt
    );
    
    event RebasedUSDs(uint256 rebaseAmt);
    
    event Allocated(address indexed collateral, address indexed strategy, uint256 amount);
    
    error AllocationNotAllowed(address collateral, address strategy, uint256 amount);
    
    error RedemptionPausedForCollateral(address collateral);
    
    error InsufficientCollateral(address collateral, address strategy, uint256 amount, uint256 availableAmount);
    
    error InvalidStrategy(address _collateral, address _strategyAddr);
    
    error MintFailed();
    constructor();
    function withdraw(uint256 _depositId) external virtual;
    function claimRewards(uint256 _depositId) external;
    function initiateCooldown(uint256 _depositId) external nonReentrant;
    function addRewards(address _rwdToken, uint256 _amount) external nonReentrant;
    function updateCooldownPeriod(uint256 _newCooldownPeriod) external onlyOwner;
    function farmPauseSwitch(bool _isPaused) external onlyOwner;
    function closeFarm() external onlyOwner nonReentrant;
    function recoverERC20(address _token) external onlyOwner nonReentrant;
    function recoverRewardFunds(address _rwdToken, uint256 _amount) external nonReentrant;
    function setRewardRate(address _rwdToken, uint128[] memory _newRewardRates) external;
    function updateRewardData(address _rwdToken, address _newTknManager) external;
    function computeRewards(address _account, uint256 _depositId)
        external
        view
        virtual
        returns (uint256[][] memory rewards);
    function getRewardFunds() external view returns (RewardFund[] memory);
    function getRewardData(address _rwdToken) external view returns (RewardData memory);
    function getDepositInfo(uint256 _depositId) external view returns (Deposit memory);
    function getNumSubscriptions(uint256 _depositId) external view returns (uint256);
    function getSubscriptionInfo(uint256 _depositId, uint256 _subscriptionId) external view returns (Subscription memory);
    function getRewardRates(address _rwdToken) external view returns (uint256[] memory);
    function getRewardFundInfo(uint8 _fundId) external view returns (RewardFund memory);
    function getRewardTokens() external view returns (address[] memory);
    function getTokenAmounts() external view virtual returns (address[] memory, uint256[] memory);
    function updateFarmRewardData() public virtual;
    function claimRewardsTo(address _account, uint256 _depositId) public nonReentrant;
    function updateFarmStartTime(uint256 _newStartTime) public virtual onlyOwner;
    function isFarmOpen() public view virtual returns (bool);
    function isFarmActive() public view virtual returns (bool);
    function getRewardBalance(address _rwdToken) public view returns (uint256);
    function _recoverERC20(address _token) internal virtual;
    function _deposit(address _account, bool _lockup, uint256 _liquidity) internal returns (uint256);
    function _initiateCooldown(uint256 _depositId) internal;
    function _withdraw(uint256 _depositId) internal;
    function _updateAndClaimFarmRewards(uint256 _depositId) internal;
    function _updateAndClaimFarmRewardsTo(uint256 _depositId, address _receiver) internal;
    function _recoverRewardFunds(address _rwdToken, uint256 _amount) internal;
    function _setRewardRate(address _rwdToken, uint128[] memory _newRewardRates) internal;
    function _setupFarm(
        string calldata _farmId,
        uint256 _farmStartTime,
        uint256 _cooldownPeriod,
        RewardTokenData[] memory _rwdTokenData
    ) internal initializer;
    function _addRewardData(address _token, address _tknManager) internal;
    function _updateLastRewardAccrualTime() internal virtual;
    function _getAccRewards(uint8 _rwdId, uint8 _fundId, uint256 _time, uint256 _alreadyAccRewardBal)
        internal
        view
        returns (uint256);
    function _validateDeposit(address _account, uint256 _depositId) internal view;
    function _validateNotRecentDeposit(uint256 _depositTs) internal view;
    function _validateFarmOpen() internal view;
    function _validateFarmActive() internal view;
    function _validateTokenManager(address _rwdToken) internal view;
    function _validateRewardToken(address _rwdToken) internal view;
    function _getRewardAccrualTimeElapsed() internal view virtual returns (uint256);
    function _validateCooldownPeriod(uint256 _cooldownPeriod) internal pure;
    function _validateNonZeroAddr(address _addr) internal pure;
    function _subscribeRewardFund(uint8 _fundId, uint256 _depositId, uint256 _liquidity) private;
    function _unsubscribeRewardFund(uint8 _fundId, uint256 _depositId) private;

    USDs

    USDs

    • ERC20 contract

    • USDs is a stablecoin yield aggregator that provides auto-yield natively.

    • USDs is a rebasing token with two modes of accounting for users:

      • Rebasing wallets

        • Users holding tokens in their EOA are by default in this category and are eligible for auto-yield via a rebasing mechanism.

        • Any contract opted-in for rebase also comes in this category.

    • Rebasing Mechanism

      • Users mint USDs using approved list of collaterals

      • These collaterals accumulated in the USDs vault are then deployed to various yield on-chain earning opportunities.

      • Yield is harvested periodically and is used to buyback USDs from the market of which 70% goes for USDs auto yield, and rest is used for SPA buyback and burn.

    Contract Documentation

    Inherits: ERC20PermitUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable,

    Author: Sperax Foundation

    ERC20 compatible contract for USDs supporting the rebase feature. This ERC20 token represents USDs on the Arbitrum (L2) network. Note that the invariant holds that the sum of balanceOf(x) for all x is not greater than totalSupply(). This is a consequence of the rebasing design. Integrations with USDs should be aware of this feature.

    Inspired by OUSD:

    State Variables

    MAX_SUPPLY

    _totalSupply

    _deprecated_vars

    _allowances

    vault

    _creditBalances

    _deprecated_rebasingCredits

    rebasingCreditsPerToken

    nonRebasingSupply

    nonRebasingCreditsPerToken

    rebaseState

    _deprecated_gatewayAddr

    _deprecated_isUpgraded

    paused

    Functions

    onlyVault

    Verifies that the caller is the Savings Manager contract.

    constructor

    initialize

    Initializes the contract with the provided name, symbol, and vault address.

    Parameters

    Name
    Type
    Description

    mint

    Mints new USDs tokens, increasing totalSupply.

    Parameters

    Name
    Type
    Description

    burn

    Burns tokens, decreasing totalSupply.

    Parameters

    Name
    Type
    Description

    rebaseOptIn

    Voluntary opt-in for rebase.

    Useful for smart-contract wallets.

    rebaseOptOut

    Voluntary opt-out from rebase.

    rebaseOptIn

    Adds _account to the rebasing account list.

    Parameters

    Name
    Type
    Description

    rebaseOptOut

    Adds _account to the non-rebasing account list.

    Parameters

    Name
    Type
    Description

    rebase

    The rebase function. Modifies the supply without minting new tokens. This uses a change in the exchange rate between "credits" and USDs tokens to change balances.

    Parameters

    Name
    Type
    Description

    updateVault

    Change the vault address.

    Parameters

    Name
    Type
    Description

    pauseSwitch

    Called by the owner to pause or unpause the contract.

    Parameters

    Name
    Type
    Description

    transfer

    Transfer tokens to a specified address.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    transferFrom

    Transfer tokens from one address to another.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    approve

    Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. This method is included for ERC20 compatibility.

    increaseAllowance and decreaseAllowance should be used instead. Changing an allowance with this method brings the risk that someone may transfer both the old and the new allowance - if they are both greater than zero - if a transfer transaction is mined before the later approve() call is mined.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    increaseAllowance

    Increase the amount of tokens that an owner has allowed a _spender to spend. This method should be used instead of approve() to avoid the double approval vulnerability described above.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    decreaseAllowance

    Decrease the amount of tokens that an owner has allowed a _spender to spend.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    totalSupply

    Check the current total supply of USDs.

    Returns

    Name
    Type
    Description

    balanceOf

    Gets the USDs balance of the specified address.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    creditsBalanceOf

    Gets the credits balance of the specified address.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    allowance

    Function to check the amount of tokens that an owner has allowed a spender.

    Parameters

    Name
    Type
    Description

    Returns

    Name
    Type
    Description

    _mint

    Creates _amount tokens and assigns them to _account, increasing the total supply.

    Emits a {Transfer} event with from set to the zero address.

    Requirements - to cannot be the zero address.

    Parameters

    Name
    Type
    Description

    _burn

    Destroys _amount tokens from _account, reducing the total supply.

    Emits a {Transfer} event with to set to the zero address.

    • Requirements:

    • _account cannot be the zero address.

    • _account must have at least _amount tokens.*

    Parameters

    Name
    Type
    Description

    _executeTransfer

    For non-rebasing accounts credit amount = _amount

    Update the count of non-rebasing credits in response to a transfer

    Parameters

    Name
    Type
    Description

    _rebaseOptIn

    Add a contract address to the non-rebasing exception list. I.e., the address's balance will be part of rebases so the account will be exposed to upside and downside.

    Parameters

    Name
    Type
    Description

    _rebaseOptOut

    Remove a contract address from the non-rebasing exception list.

    _isNonRebasingAccount

    Is an account using rebasing accounting or non-rebasing accounting? Also, ensure contracts are non-rebasing if they have not opted in.

    Parameters

    Name
    Type
    Description

    _ensureNonRebasingMigration

    Ensures internal account for rebasing and non-rebasing credits and supply is updated following the deployment of frozen yield change.

    Parameters

    Name
    Type
    Description

    _balanceOf

    Calculates the balance of the account.

    Function assumes the _account is already upgraded.

    Parameters

    Name
    Type
    Description

    _creditsPerToken

    Get the credits per token for an account. Returns a fixed amount if the account is non-rebasing.

    Parameters

    Name
    Type
    Description

    _isNotPaused

    Validates if the contract is not paused.

    Events

    TotalSupplyUpdated

    Paused

    VaultUpdated

    RebaseOptIn

    RebaseOptOut

    Errors

    CallerNotVault

    ContractPaused

    IsAlreadyRebasingAccount

    IsAlreadyNonRebasingAccount

    CannotIncreaseZeroSupply

    InvalidRebase

    TransferToZeroAddr

    TransferGreaterThanBal

    MintToZeroAddr

    MaxSupplyReached

    Enums

    RebaseOptions

    Balance for this category is tracked via a credit system, as described below :

    • creditsPerToken is tracked at a global level and is updated when doing a rebase.

    • creditBalance is tracked at an account level and is updated via token transfer, mint, redemption of USDs

  • Non-Rebasing wallets

    • For Non-Rebasing wallets this token acts as a normal ERC20 token and tracks the balance of the wallet as usual.

  • While doing a rebase the x amount of USDs is partially burnt (without changing the overall total supply) and the creditsPerToken value is adjusted such that the burnt amount is proportionally distributed across all rebasing wallets.

  • _nameArg

    string

    The name of the USDs token.

    _symbolArg

    string

    The symbol of the USDs token.

    _vaultAddress

    address

    The address where collaterals of USDs protocol reside, and major actions like USDs minting are initiated.

    _account

    address

    The account address to which the newly minted USDs will be attributed.

    _amount

    uint256

    The amount of USDs to be minted.

    _amount

    uint256

    The amount to burn.

    _account

    address

    Address of the desired account.

    _account

    address

    Address of the desired account.

    _rebaseAmt

    uint256

    The amount of USDs to rebase with.

    _newVault

    address

    The new vault address.

    _pause

    bool

    The state of the pause switch.

    _to

    address

    The address to transfer to.

    _value

    uint256

    The amount to be transferred.

    bool

    True on success.

    _from

    address

    The address from which you want to send tokens.

    _to

    address

    The address to which the tokens will be transferred.

    _value

    uint256

    The amount of tokens to be transferred.

    bool

    true on success.

    _spender

    address

    The address that will spend the funds.

    _value

    uint256

    The amount of tokens to be spent.

    bool

    true on success.

    _spender

    address

    The address that will spend the funds.

    _addedValue

    uint256

    The amount of tokens to increase the allowance by.

    bool

    true on success.

    _spender

    address

    The address that will spend the funds.

    _subtractedValue

    uint256

    The amount of tokens to decrease the allowance by.

    bool

    true on success.

    uint256

    The total supply of USDs.

    _account

    address

    The address to query the balance of.

    uint256

    A uint256 representing the amount of base units owned by the specified address.

    _account

    address

    The address to query the balance of.

    uint256

    (uint256, uint256) Credit balance and credits per token of the address.

    uint256

    _owner

    address

    The address that owns the funds.

    _spender

    address

    The address that will spend the funds.

    uint256

    The number of tokens still available for the spender.

    _account

    address

    The account address to which the newly minted USDs will be attributed.

    _amount

    uint256

    The amount of USDs that will be minted.

    _account

    address

    The account address from which the USDs will be burnt.

    _amount

    uint256

    The amount of USDs that will be burnt.

    _from

    address

    The address from which you want to send tokens.

    _to

    address

    The address to which the tokens will be transferred.

    _value

    uint256

    Amount of USDs to transfer

    _account

    address

    address of the account opting in for rebase.

    _account

    address

    Address of the account.

    _account

    address

    Address of the account.

    _account

    address

    Address of the account.

    _account

    address

    Address of the account.

    IUSDs
    https://github.com/OriginProtocol/origindollar/blob/master/contracts/contracts/token/OUSD.sol
    creditsPerToken=CtcreditBalance=CbuserBalance=Cb/CtcreditsPerToken = C_t \\ creditBalance = C_b \\ userBalance = C_b/C_tcreditsPerToken=Ct​creditBalance=Cb​userBalance=Cb​/Ct​
    uint256 private constant MAX_SUPPLY = ~uint128(0);
    
    uint256 internal _totalSupply;
    
    uint256[4] private _deprecated_vars;
    
    mapping(address => mapping(address => uint256)) private _allowances;
    
    address public vault;
    
    mapping(address => uint256) private _creditBalances;
    
    uint256 private _deprecated_rebasingCredits;
    
    uint256 public rebasingCreditsPerToken;
    
    uint256 public nonRebasingSupply;
    
    mapping(address => uint256) public nonRebasingCreditsPerToken;
    
    mapping(address => RebaseOptions) public rebaseState;
    
    address[2] private _deprecated_gatewayAddr;
    
    mapping(address => bool) private _deprecated_isUpgraded;
    
    bool public paused;
    
    modifier onlyVault();
    
    constructor();
    
    function initialize(string memory _nameArg, string memory _symbolArg, address _vaultAddress) external initializer;
    
    function mint(address _account, uint256 _amount) external override onlyVault nonReentrant;
    
    function burn(uint256 _amount) external override nonReentrant;
    
    function rebaseOptIn() external;
    
    function rebaseOptOut() external;
    
    function rebaseOptIn(address _account) external onlyOwner;
    
    function rebaseOptOut(address _account) external onlyOwner;
    
    function rebase(uint256 _rebaseAmt) external override onlyVault nonReentrant;
    
    function updateVault(address _newVault) external onlyOwner;
    
    function pauseSwitch(bool _pause) external onlyOwner;
    
    function transfer(address _to, uint256 _value) public override returns (bool);
    
    function transferFrom(address _from, address _to, uint256 _value) public override returns (bool);
    
    function approve(address _spender, uint256 _value) public override returns (bool);
    
    function increaseAllowance(address _spender, uint256 _addedValue) public override returns (bool);
    
    function decreaseAllowance(address _spender, uint256 _subtractedValue) public override returns (bool);
    
    function totalSupply() public view override(ERC20Upgradeable, IUSDs) returns (uint256);
    
    function balanceOf(address _account) public view override returns (uint256);
    
    function creditsBalanceOf(address _account) public view returns (uint256, uint256);
    
    function allowance(address _owner, address _spender) public view override returns (uint256);
    
    function _mint(address _account, uint256 _amount) internal override;
    
    function _burn(address _account, uint256 _amount) internal override;
    
    function _executeTransfer(address _from, address _to, uint256 _value) private;
    
    function _rebaseOptIn(address _account) private;
    
    function _rebaseOptOut(address _account) private;
    
    function _isNonRebasingAccount(address _account) private returns (bool);
    
    function _ensureNonRebasingMigration(address _account) private;
    
    function _balanceOf(address _account) private view returns (uint256);
    
    function _creditsPerToken(address _account) private view returns (uint256);
    
    function _isNotPaused() private view;
    
    event TotalSupplyUpdated(uint256 totalSupply, uint256 rebasingCredits, uint256 rebasingCreditsPerToken);
    
    event Paused(bool isPaused);
    
    event VaultUpdated(address newVault);
    
    event RebaseOptIn(address indexed account);
    
    event RebaseOptOut(address indexed account);
    
    error CallerNotVault(address caller);
    
    error ContractPaused();
    
    error IsAlreadyRebasingAccount(address account);
    
    error IsAlreadyNonRebasingAccount(address account);
    
    error CannotIncreaseZeroSupply();
    
    error InvalidRebase();
    
    error TransferToZeroAddr();
    
    error TransferGreaterThanBal(uint256 val, uint256 bal);
    
    error MintToZeroAddr();
    
    error MaxSupplyReached(uint256 totalSupply);
    
    enum RebaseOptions {
        NotSet,
        OptOut,
        OptIn
    }