Spartan Protocol is a liquidity pool protocol that allows token-agnostic provision of liquidity. Traders can swap between tokens at arbitrarily low fees, but a liquidity-sensitive fee maximises revenue for liquidity providers during periods of high demand.
The Spartan Protocol can also facilitate the following features:
- Synthetic Token Generation using liquidity pool shares
- Lending markets using a flexible peg-out of low-use pool capital
- Derivatives by winding up synthetic token generation in multiple runs
The following contracts manage the protocol:
BASEContract (Sparta Token Contract)DAOContract (Manages Governance)UTILSContract (Stateless contract that manages core math and helper functions)ROUTERContract (Manages how liquidity is moved around the system)POOLContract (Holds funds and state for each pool)SYNTHContract (Holds Lps and state for each synthetic)SYNTHFACTORYContract (Creates a synthetic token from curated Pools)POOLFACTORYContract (Creates a pool)SYNTHVAULTContract (Holds funds and state for members)DAOVAULTContract (Holds funds and state for members)RESERVEContract (Holds emissions from base, grants funds to grantors)BONDVAULTContract (Holds LP funds and state for bond members)
BASE is the source-of-truth for the location of the DAO, as well as minting and distributing incentives.
DAO is the source-of-truth for the location of the ROUTER, UTILS,DAOVAULT,POOLFACTORY,SYNTHFACTORY, RESERVE as well as distributing rewards and managing how the system upgrades itself. It has goverance features that use a member's claim on BASE in each pool to attribute voting weight. The DAO can upgrade itself, as well as amending some features in the BASE contract.
UTILS contains utility and math functions, and can be upgraded by the DAO.
ROUTER contains state and business logic for moving funds, and can be upgraded by the DAO. Users interact with the ROUTER.
POOL holds the funds for each pool, as well as state. It asks the DAO for the location of UTILS which has core math relating to how swaps and liquidity is provisioned, such as calculating fees.
WBNB - 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
The contracts are to be deployed and then connected together. The DEPLOYER (EOA) has initial DAO privileges in order to manage the process. DEPLOYER should be purged when the system is stable.
- Deploy
SPARTA - Deploy
SYNTHVAULT(SPARTA.address, Dao.address) - Deploy
UTILS(SPARTA.address, Dao.address) - Deploy
ROUTER(SPARTA.address, wbnb.address, Dao.address) - Deploy
DAOVAULT(SPARTA.address, Dao.address) - Deploy
BONDVAULT(SPARTA.address, Dao.address) - Deploy
POOLFACTORY(SPARTA.address, wbnb.address, Dao.address) - Deploy
SYNTHFACTORY(SPARTA.address, wbnb.address, Dao.address) - Deploy
RESERVE(SPARTA.address) - Set
(router.address, utils.address, daoVault.address, poolFactory.address, synthFactory.address, SPReserve.address)inDAO - Set
dao.addressinSPARTA - Set
(router.address, utils.address, synthV.address, Dao.address)inRESERVE - Call
start()inRESERVE
- SPARTA is the
BASEcurrency. UTILSneeds to know SPARTA (to ask forDAO)DAOneeds to know SPARTA (to manage), along withROUTERandUTILSROUTERneeds to know SPARTA to ask forDAO, to ask forUTILSPOOLneeds to knowDAOto ask forUTILS
Goverance should pass a proposal electing a new address.
Once passed, the DAO will know the new UTILS contract, and return it when queried (by the ROUTER).
Critical - the new UTILS contract must be inspected for malicious code before allowing an upgrade.
Once passed, the DAO will know the new ROUTER contract, and return it when queried.
Since the ROUTER holds state, the new ROUTER may or may not need state migrated in from the old ROUTER. The state includes:
- array of tokens listed (registry - critical)
- metrics for the protocol (read-only - not critical)
The new Router may instead want to query the old router for the registry, in addition to managing its own.
Once passed, the DAO will tell the BASE contract of the new DAO. POOL will now know, because it asks BASE for the location.
This address receives emissions from BASE. The DAO can set a RESERVE address.
The BONDVAULT holds state and LP tokens for all members who perform a bond() via the DAO. Contains logic for calculating member weights and claimable LP tokens.
Members firstly lock Spartan Protocol liquidity tokens in the DAOVault, which allow a claim on BASE in each pool to be detected and summed. Importantly, goverance is on-market and liquid - whilst locking another member can purchase BASE off existing members and lock. This reduces existing member weight.
Proposals are a 3-step process:
- Create a proposal with parameters.
- Vote for that proposal, if passing quorum, then proceed into a cool-off period in "finalising" state.
- Once finalising, and past cool-off, anyone can call and finalise in order to automate the actions of the proposal on the system.
- Majority: 66.6%
- Quorum: 50%
- Minority: 16.6%
Proposals that upgrade critical infrastructure require Majority:
- Upgrade
DAOcontract - Upgrade
UTILScontract - Upgrade
RESERVEcontract - Upgrade
ROUTERcontract - Propose
GET_SPARTAbond allocation - Propose
GRANTissue grant - Propose
LIST_BONDlist bondable asset - Propose
ADD_CURATED_POOLenable curated asset
All other proposals require Quorum:
- Propose
FLIP_EMISSIONSSPARTA emissions fromBASE - Propose
COOL_OFFproposals cooloff onDAO - Propose
ERAS_TO_EARNeras to earn onDAO - Propose
DELIST_BONDdelist bondable asset - Propose
REMOVE_CURATED_POOLdisable curated asset
During the Cool-Off period, a competing proposal that has Minority vote-weight, can call in and veto a finalising Quorum proposal. A scenario is as follows:
- A questionable Proposal to grant a large holder some funds, gets past 50% vote and enters cool-off.
- Minority (16.6%) members are concerned it is not in the best interest of the system, so thus have the cool-off period to vote for a competing proposal that can be used to neutralise (1).
- The competing proposal is not finalised/completed, as it also needs to achieve Quorum first.
- Change
DAO - Change
ROUTER - Change
UTILS - Change
RESERVE - Enable/disable emission on
BASE - Change Cool-off period length
- Change erasToEarn (how fast the incentives pay out)
- Give out a SPARTA grant to an address
- Enable an allocation of SPARTA for
Bond(Held inDAO) - List a new
BONDenabled asset - Delist a
BONDenabled asset - Add an existing pooled-asset to the 'curated' list
- Remove an existing pooled-asset from the 'curated' list
The DAOVAULT holds state and LP tokens for all members who perform a deposit() via the DAO. Contains logic for calculating member weights.
Each POOL contains logic and holds funds and state. The only way to get funds out of the POOL is to send it funds first (assets or liquidity tokens).
- Liquidity is added by sending the
POOLfunds, then callingadd(). It will find all spare funds on its address and attribute that to the liquidity provider, callingmint(). - Liquidity is removed by sending liquidity tokens to the
POOLthen callingremove(). It will find all spare tokens on its address, burn them, then send the liquidity provider their fair share of funds. - A Swap is executed by sending the
POOLfunds, then callingswap(token). It finds all spare funds on its address, and calculates the swap output in the other token, then sending that out. - A
sync()function is added that re-syncs the recorded token amounts on thePOOLaddress.
The pool asks the UTILS contract for logic relating to adding/removing liquidity, as well as swapping, so in the future, logic can be changed. However an upgrade to a malicous UTILS contract could compromise funds in the system.
The POOLFACTORY manages the creation and status of all Spartan pools.
- Create a pool
- Add a pool to 'curated'
- Remove a pool from 'curated'
- Helper functions relating to pool counts / lists
The BASE contract mints a certain number of coins every era and sends them to the RESERVE.
The mint amount is set by (300m-totalSupply)/emissionCurve which will mint a slowly decreasing amount each day. The feeBurn in BASE enforces a deflationary burn to counter the emissions that gradually increases as the supply increases to counter the emissions.
Users lock LP tokens in the DAO and can call harvest() as often as they want, although since the reserve in the DAO depletes, it favours those who call it more frequently. If the erasToEarn is set to 30 days, then after the final drop, it will take 30 days for all rewards to be consumed. However, since new rewards are sent there every day, the velocity of emissions should be fairly constant.
The ROUTER facilitates movement of funds from users into pools, containing business logic for adding/removing liquidity, swapping and managing synths.
The Router does not hold funds.
- The
ROUTERalso tracks metrics, such as fee-based revenue. - The
ROUTERhandles BNB by converting it into WrappedBNB first, and unwrapping when returning to the user.
Each SYNTH contains logic and holds LP tokens and state. Minting synths requires the relevant POOL to send LP units to the SYNTH and call mintSynth().
- Synths are created by sending the
SYNTHLP tokens, then callingmintSynth(). It will mint the relevant requested amount of synths and attribute that to the user, viamint(). - Synths are swapped back to layer 1 assets via
POOLfunction:burnSynth()by sending synth tokens to theSYNTHthen callingburnSynth(). It will find all spare synth tokens on its address, burn them, then send the LP tokens back to the pool to also be burnt and attribute the user their fair share of the requested BEP20 asset. - A
realise()function burns excess LP tokens to ensure the revenue is going to the liquidity providers in the underlying pools instead of the un-owned LP tokens held on atSYNTH
The SYNTHFACTORY manages the creation and status of all Spartan synth assets.
- Create a synth
- Helper functions relating to synth counts / lists
UTILS works as both a web3 aggregrator (one call that makes several EVM calls, returning objects), as well as the core arithmetic of the system.
It is also used to retrieve state from the router, tokens and pools. DApps can read from UTILS, write to ROUTER.
The UTILS contract has the following:
- Getting all pools and their details
- Arithmetic relating to pools and members
- Core math relating to calculating swaps, synths and liquidity provisioning
The UTILS contract can be upgraded, but upgrading it to a malicious contract can compromise funds in the system by changing logic to favour an attacker.
- Lending - not implemented yet
The test suite uses Buidler as the preferred testing suite, since it compiles and tests faster. The test suite implements 7 routines that can be tested individually.
npx buidler compile
Execute all at once:
npx builder test
Or execute individually:
npx builder test/1_base.js