As most of us are now aware; Spartan Protocol deployed a new v2 SPARTA token on BSC mainnet following the drain on the v1 pools. Whilst the exploit in the v1 pools was not related to this base contract; the event raised the opportunity to:
- Remove some functions that are no longer required from the burnForSparta distribution period
- Allow some of the remaining distribution supply to be claimed by those who were affected by the recent exploit
- Add in long-term deflationary aspects to counter the short-term expedited distributed supply
Removal of ‘BurnForSparta’ Functions
Spartan Protocol’s initial fair distribution involved bringing along BNB and selected BEP20 tokens and burning them in exchange for SPARTA.
This distribution program is now over and has left behind some functions which are no longer required. Those have been left out of the new V2 token contract to leave us with something less complex.
Upgrade & Claim SPARTA v2
The deployment of the V2 token contract enabled users to upgrade their V1 tokens to V2. With it an allocated fund for affected users to claim from, please see more in this article:
Deflationary — feeBurn on Transfer
With the proposal to use some of the SPARTA remaining from the bond distribution to make LPers partial again, came concerns including expedited short-term inflation and/or dumping on market.
Most of the raised concerns are relevant even without the claim of SPARTA being allocated to those affected. Out-of-the-box thinking will be required ongoing to move forward with bringing more utility to the token within the V2 protocol ecosystem.
On the base level; we were able to implement a simple deflationary mechanism within the existing internal _transfer() function. This article is a bit of an explainer on how that works and how it will affect users and the overall tokenomics.
feeBurn in _transfer()
With the SPARTAv2 token, any wallet, user or contract performing a standard transfer of SPARTAv2 tokens will have a small portion of that transfer burnt out of the supply along the way.
The feeBurn has been in action since the first SPARTAv2 token transfer. It starts at 0% and grows/shrinks with the token’s total supply. The feeBurn is set to be 1% *if* the token’s total supply is at the max (300M) however the max supply is programmatically impossible to reach.
When any standard transfer event takes place, the feeBurn will apply, which essentially means whenever SPARTA tokens move from one wallet/contract to another. The only transfer events excluded are when SPARTA is minted.
This feeBurn adds deflationary pressure to counteract the emissions curve. What we will likely see is a push/pull at some point in the supply where the feeBurn is having a tug-of-war with the daily emissions and holding the supply in a fairly consistent band.
As the supply increases, the feeBurn gets more aggressive and the daily emissions get less aggressive. This will mean that SPARTA gets more deflationary as the supply increases and time goes on.
Note: If v1 had this deflationary function in place, we could estimate a rough ~2,600,000 SPARTA would have been burnt out of the total supply by now based on the transfer count * avg transfer amount plugged into the calculation of the feeBurn using half of the supply as the totalSupply figure
Function Broken Down
The following is a more detailed breakdown of each line of the functions involved in the feeBurn. For the sake of examples; let’s assume the user is transferring 100 SPARTA and the totalSupply is currently 100m.
Checks that the sender of the transfer is not 0x0000000000000000000000000000000000000000. This will ensure that the feeBurn does not apply when SPARTA is being minted into the supply.
Note: The BASE._mint() function will still emit a ‘Transfer’ event however it handles the transfer inside itself and does not use the BASE._transfer() function.
Checks that the recipient is not the SPARTA token contract. Numerous Spartans accidentally sent their precious SPARTA to the base contract in V1. This new user-safety check will prevent users from accidentally sending SPARTAv2 tokens to the SPARTAv2 token contract.
Gets the sender’s total balance of SPARTA, and checks that their balance is greater than the amount they are attempting to transfer. Self-explanatory.
Calls UTILS.calcPart() to calculate the feeBurn amount.
Before we go there, however, we need to explore the value of the BASE.feeOnTransfer storage which is handed to UTILS.calcPart(feeOnTransfer, amount)
BASE.feeOnTransfer is calculated using UTILS.getFeeOnTransfer() which is called via BASE._checkEmission() after every BASE._transfer()
Let’s start with the relevant section of BASE._checkEmission() where the BASE.feeOnTransfer storage is updated.
Here we perform a simple calculation for BASE.feeOnTransfer using the BASE.totalSupply & BASE.maxSupply
We will use 100M total supply as the example for the remainder of this breakdown resulting in a BASE.feeOnTransfer value of 33.333333333
share = (amount * part) / total;feeOnTransfer = (100 * totalSupply) / maxSupply
maxSupply = 300m
totalSupply = 100m (as an example)feeOnTransfer = 33.333333333
Note: UTILS.getFeeOnTransfer() can be swapped out by the DAO in the future by swapping out the UTILS contract; so the 100 basis points could theoretically be increased or decreased (or changed to ‘0’ to disable feeBurn)
However as highlighted below; BASE.feeOnTransfer is hard coded in the base token contract to be capped at 500 basis points (5%) to prevent a flawed or rogue UTILS contract performing an undesired burn amount in the future.
Now that we understand the BASE.feeOnTransfer value; we can continue with step 4a where we call UTILS.calcPart(feeOnTransfer, amount)
UTILS.calcPart(feeOnTransfer, amount) ensures the basis point value is valid and then hands it to UTILS.calcShare(feeOnTransfer, 10000, amount)
Using our example values will result in this output:
share = (amount * part) / total;feeBurn = (transferAmount * feeOnTransfer) / 10000transferAmount = 100 SPARTA
feeOnTransfer = 33.33333333 (from previous example)feeBurn (_fee) = 0.333333 SPARTA
Back to the BASE._transfer() function. Here we check if the feeBurn value (_fee) is valid and perform the burn.
The BASE._burn(sender, _fee) function does its checks and then removes the feeBurn (_fee) amount from the users' balance and also from BASE.totalSupply and emits a ‘Transfer’ event.
The transfer function then adjusts the two users’ balances & emits the ‘Transfer’ event.
Run BASE.checkEmission() to update the BASE.feeOnTransfer storage value ready for the next Transfer.