Intercoin / ReserveTokenContract

Used to power the Intercoin Reserve Token, this token is optimized for stability and growth.
GNU Affero General Public License v3.0
0 stars 1 forks source link

Implement base features and math of smart contract #1

Open EGreg opened 2 years ago

EGreg commented 2 years ago

Please read this article on bonding curves completely before beginning work.

  1. Make a regular ERC777 contract as a base, with init parameters like string name, string ticker, uint256 initialIncrease, uint256 fractionToReserve, uint256 fractionToLiquidity, array disbursementList, with defaults baked into the contract: "Intercoin", "INTER", 0.8, 0.05, 0.00000001, [[dividendsGroup, 100%]] (converted to fixed-point math)
  2. Have a linear bonding curve that starts at a given price in terms of ETH, that will allow users to send ETH (or BNB) to the smart contract and the price will go up linearly relative to that, with slope equal to initialIncrease per INTER sold. Or they can send to the smart contract its own tokens and receive back ETH/BNB, with the price going down linearly as described in the section #5.
  3. From every sale fractionToReserve is stored on reserve in ETH while fractionToLiquidity will be added to the ETH-INTER pairs on UniSwap or PancakeSwap (the most-used exchanges, respectively). These pairs can be calculated in advance, if we know what the contract's address will be (using vanity.eth) so we don't even need the owner to set them and then renounceOwnership. We will just put them into initparameters when deploying on the blockchain.
  4. The rest of the ETH from each sale will be sent to the disbursementList, and then you are supposed to call disburse() on each contract in that list. Make a setDividendsList(dividendsList) onlyOwner method, so owner can change list until they renounce ownership, and it will check whether the keccak of disburse() is callable on each contract in the list.
  5. The formula for buying and selling is described below. You will need to go through the entire reasoning to arrive at the formulas for how many coins and tokens the contract will release.

Reasoning

Explanation #1: Why do we use ETH/BNB? Because the assumption is that if the blockchain is operational, then the most decentralized asset on it is its own coin (instead of USDT, or USDC, which relies on off-chain guarantees by centralized entities that may not be true in 20-30 years). If you have questions about the math, contact me.

Explanation #2: Since fractionToReserves is kept on reserve for buybacks, you can imagine a trapezoid which starts at 0 and increases by currentIncrease * fractionToReserves for every 1 INTER sold. If everyone wanted to sell back to the contract at the same price, the price would then have to be: (allTimeHigh / 2) * fractionToReserves. So to give a real example, that could be ~ (allTimeHigh / 2 * 80%) = allTimeHigh * 40%. Thus everyone would get 40% of their money back. But we don't want to give everyone the same amount. Rather, we want to start giving 80% of the allTimeHigh and move down to 0% for the very last INTER bought back ( 40 is in the middle between 80 and 0 ). Thus the actual buyback price would be a line with a midpoint at at allTimeHigh / 2 and negative slope. Note that even though the smart contract reduces the buyback price as its reserves reduce, it doesn't reduce the sell price! New Intercoins can only be bought at the all-time high. If people think that price is too high, they will start to buy from other people. The smart contract only dispenses new Intercoins when the market demands them (i.e. more communities, and larger local economies) and is willing to pay above the all-time-high price. Until then, the Intercoins can list in secondary markets and have wide availability trading on many exchanges against many currencies (see https://intercoin.org/tokenomics)

Explanation #3: How do we calculate the price curve and let the buybacks be sustainable?

First of all let's look at redeeming tokens, when the contract has a certain reserve fund build up. If all the tokens were to be redeemed at the all-time-high price, the contract would need to have totalRefund = totalSupplyOfTokens * allTimeHighPrice coins, and its reserves are actually a lot lower. Over time, the area under a diagonal line (triangle) approaches 50% of the area under the rectangle (even if the initial price wouldn't have been zero, it becomes insignificant). So the smart contract has about half of the 80% it put on reserve, in other words around 40%. It could give out coins at a constant price of 40% of ATH, but we want the earliest people to get it at a better price, and latest people get it at a lower price near zero, so we tilt the horizontal line. In our example, start at 80% of all-time-high price for the first tokens, and end with 10% of all-time-high price for the last tokens (40 is in the midpoint between 70 and 10).

However, the reserve ratio would fall after every buyback. The smart contract has to use its reserves to buy back tokens whenever someone wants to redeem them. But since the contract doesn't reduce its sales price, the very next person who comes to buy would be buying at the all-time-high price, causing the reserve ratio to fall even lower (relative to having to buy back ALL tokens at the all-time-high price). To mitigate this, we must make the increasePrice speed slow down proportionally to the decline in the reserve ratio. So the initial reserve ratio starts out at 40% = initialReserveRatio = fractionToReserve / 2. But at every moment when we buy, just calculate currentReserveRatio = currentReserve / totalRefund and just set:

currentIncrease = initialIncrease * currentReserveRatio / initialReserveRatio

Thus, the rate at which the price grows would slow down every time the smart contract buys back some tokens. It might eventually get so slow as to become almost a stablecoin, until the reserves build back up as people buy more coins, and then the growth picks up again.

The Mathematics

We are inspired by Bancor's Reserve Ratio with 50% kept on reserve, but rescaled by fractionToReserve (i.e. 80% of 50%). There, we use the calculatePurchaseReturn and calculateSaleReturn from that article, but here we will modify the logic, so let's reason through it together!

For buying back tokens it's easy, because the horizontal axis is in "tokens being redeemed", and users are redeeming tokens with smart contract. So to know the number of coins to give out, first consider if all the coins were given at a constant price of remainingBalanceOfCoins / totalSupplyOfTokens (40% in the example above where everyone only bought, and then everyone only sold). But we want the earliest people to get it at a better price, and latest people get it at a lower price near zero (in our example, start at 80% of all-time-high price for the first tokens, and end with 0% of all-time-high price for the last tokens). We can just "tilt the line" around the midpoint, and take the integral under the line, which is simply calculated as a * t + b * t^2/2. To keep things simple, let's make the first tokens sell for twice the reserve ratio (80% of All Time High price) and the last tokens sell for 0. This can always be possible regardless of the current reserve ratio.

a = 2 * reserveRatio * allTimeHigh;
b = - a / totalSupplyOfTokens; // b is negative
coinsToSend = a * t + b * t^2 / 2; 

For selling tokens, it's more complex. because the horizontal axis is in "tokens being bought", but users are sending coins, so we must do some algebra to solve for the number of tokens to return, using the number of coins (like ETH or BNB) the user is sending to the contract. So we do the integral math and find the indefinite integral of the function to be Integral(price)(t) = currentPrice * t + currentIncrease * t^2 / 2. Basically, we're talking about getting the area of the rectangle on the bottom, plus a triangle sitting on top of it:

  /| <-- triangle with slope = currentIncrease
 /=|  
.==| <-- height: allTimeHigh
.==|
.==|
.==|
---------------> horizontal axis

Thus, given the current price of allTimeHigh, and current totalSupply as T (a known constant during the transaction) we want to solve for and solve for t = tokensToSend in the following formula:

coinsToSpend = allTimeHigh * t + (currentIncrease / 2) * T^2 = A * t^2 + B * t

a quadratic expression where A = currentIncrease / 2 and B = allTimeHigh

solving with quadratic formula gives:

tokensToSend = [ -B + sqrt(B^2 - 4A) ] / 2A

Just try "-B +" or "-B -" and maybe flip the sign, to send tokens. Test and you know how many tokens to send. This formula would be the same all the time once you get it, you only have to experiment with flipping signs in math, not in the code.

So that is how you calculate how many tokens to send to the user.

EGreg commented 2 years ago

Note: we may have a feature to reduce the sales price over time, if no one is buying at the all-time-high price. (This could happen because Intercoin is trading on exchanges somewhere else. But it also means that ITR holders are missing out on dividends they could get because the smart contract is not competing with the tighter spreads on those other exchanges.)

Most of the above stuff, including the currentIncrease calculation would remain the same, but replace allTimeHigh everywhere with currentPrice.