Technical Appendix — Alethena Share Dispenser

You can find the deployed and verified smart contract here

Smart Contract Code Extract

State Variables

The contract uses the following variables:

  • address public XCHFContractAddress is the contract address where the Crypto Franc is deployed
  • address public ALEQContractAddress is the contract address where the Alethena Shares Token is deployed
  • address pubic usageFeeAddress is a (non-contract) address where the usage fees are collected

The three addresses above have to be supplied to the constructor function (allows for easy cascaded deployment).

  • uint256 public usageFeeBSP This is the usage fee expressed in basis points (10000BSP = 100%). When shares are bought or sold, this fraction of the XCHF payment is transferred to the usageFeeAddress. A value of zero corresponds to no usage fee, a value of 500 corresponds to 5% usage fee.

Pricing Model

  • The price is adjusted according to the available supply.
  • Initially a total of uint256 public initialNumberOfShares shares are allocated (i.e. sent to) the Share Dispenser contract.
  • The first share is to be sold at price uint256 public minPriceInXCHF and the last at price uint256 public maxPriceInXCHF. In-between a linear interpolation is used.
  • The pricing functions are implemented in the getCumulatedPrice and getCumulatedBuyBackPrice functions which both take two arguments, namely the number of shares to be bought/sold and the number of shares currently available.
  • There is a relation between buy and sell prices in the sense that buying shares and subsequently selling them straight away should have no effect apart from transactions fees spent.
  • If the company additionally supplies XCHF, a situation can occur where the number of shares held by the contract exceeds initialNumberOfShares. In this case, the share surplus will be sold at price minPriceInXCHF.
  • The buy and sell sides can be separately enabled/disabled through the variables bool public buyEnabled and bool public sellEnabled.

The Buy Process

To buy shares the user first grants a sufficient XCHF allowance to the dispenser smart contract. Then the user calls the function buyShares with one argument, the number of shares to buy.

  1. It is checked that buying is enabled.
  2. The number of shares available is fetched and the totalPrice is computed using getCumulatedPrice.
  3. It is checked that the dispenser has enough shares and the buyer has sufficient XCHF balance and allowance set.
  4. The usage fee is computed and subtracted from the totalPrice.
  5. Usage fee is transferred to the corresponding address.
  6. The remaining XCHF is transferred to the dispenser contract.
  7. The shares are transferred to the buyer.
  8. An event is emitted and the function returns true.

If any conditions is not met or an ERC20 transaction does not go through, the function reverts. Any user can call buyShares.

The Sell Process

To buy shares the user first grants a sufficient ALEQ allowance to the dispenser smart contract. Then the user calls the function sellShares with two arguments, the number of shares to sell and the lowest price the seller will accept (see below “Race Conditions”).

  1. It is checked that selling is enabled.
  2. The number of XCHF available is fetched and the totalPrice is computed using getCumulatedBuyBackPrice.
  3. It is checked that the totalPrice is not lower than the price limit supplied as the second argument.
  4. It is checked that the dispenser has enough XCHF and the seller has sufficient ALEQ balance and allowance set.
  5. The usage fee is computed and subtracted from the totalPrice.
  6. The shares are transferred from the seller to the dispenser contract.
  7. Usage fee is transferred to the corresponding address.
  8. The remaining XCHF is transferred to the dispenser seller.
  9. An event is emitted and the function returns true.

If any conditions is not met or an ERC20 transaction does not go through, the function reverts. Any user can call sellShares.

Decimals and Arithmetic

XCHF has 18 decimals, ALEQ has zero decimals. Share prices are entered by the user in Rappen (0.01 XCHF). Hence we need a factor of 10**16 in-between.

All arithmetic operations are handled using the safeMath open-zeppelin library. Given transaction costs (as well as usage fee) rounding errors in integer division will not lead to an arbitrage possibility through repeated buying and selling.

Additional Functions

  • The XCHF or ALEQ balance held by the dispenser contract can be retrieved through getERC20Balance which takes the corresponding contract address as an argument.
  • To check that a share purchase can happen, the user needs to hold a sufficient amount of XCHF (or ALEQ) but they also need to give a sufficient allowance to the dispenser contract. This is checked using the getERC20Available function which takes two arguments, the contract address (ALEQ or XCHF) as well as the potential buyer/seller.
  • In order for the company to retrieve XCHF or ALEQ from the dispenser contract the retrieveERC20 function is implemented. It expects three arguments, the contract address (ALEQ or XCHF), the address to send the tokens to as well as the amount. This function can only be called by the contract owner.

Permissions

  • The contract has an owner. This is handled through the corresponding open-zeppelin contract.
  • The contract is pausible by the owner (using the corresponding open-zeppelin contract).
  • The user can call the following functions buyShares, sellShares, getERC20Balance, getERC20Available, getCumulatedPrice, getCumulatedBuyBackPrice.
  • All other functions are restricted to internal use or can only be called by the contract owner.

A Note on Versions

The deployed version of the ALEQ contract uses Solidity 0.4.24, the deployed version of the XCHF contract uses Solidity 0.4.25. The Share Dispenser uses Solidity version 0.5.0 (because of Truffle compatibility). Only the core ERC20 functionality of these two tokens is used, which is not affected by the compiler version.

Race Conditions

Consider a situation where two users (Alice and Bob) both check the cumulated price for buying 10 shares roughly at the same time. Based on this Alice and Bob both give the corresponding XCHF allowance to the Share Dispenser contract. Let’s assume Alice and Bob now both call the buyShares function roughly at the same time. If the transaction of Alice goes through first, the transaction of Bob will revert because the price for the same number of shares has increased in the meantime.

A similar case can occur when selling shares. To protect the seller, along with the number of shares to sell a limit needs to be supplied. If the price moves below the limit after a transaction is broadcasted, it will revert.

Both of these cases can be handled in the front-end so that the user understands what has happened.


Visit www.alethena.com for more information.