-
Notifications
You must be signed in to change notification settings - Fork 35
Home
Version 1.0.1 - Last updated: Apr 5th 2020
The goal of this wiki is to provide a reference specification for the smart-contract(s) implementing the continuous financing model described in the continuous organizations whitepaper. The continuous financing model enables organizations to finance themselves in a permission-less and non-dilutive way by continuously issuing tokens called FAIR while aligning stakeholders to their financial success.
On a high-level, the contract acts as a state machine with 3 states:
-
init
state. The default state when the c-org contract is instantiated. In this state, the c-org is initializing and needs to sell a minimum amount of FAIR (init_goal
) to switch to therun
state. Onlybeneficiary
is allowed to transfer tokens during this state. All other participants can onlybuy()
orsell()
. -
run
state. The c-org is running and accepting investments using the bonding curve contract model described in the whitepaper. -
close
state. The c-org closing, paying out every FAIR holder and not accepting investments anymore. To close the c-org, thebeneficiary
needs to escrow theexit_fee
and call theclose()
function. -
cancel
state. The c-org got cancelled (by callingclose()
) while still ininit
state.
-
init_investors
. A map with all investors ininit
state usingaddress
as a key andamount
(in FAIR) as value. This structure's purpose is to make sure that only investors can withdraw their money ifinit_goal
is not reached.
These constants are preset in the contract.
-
currency
. The address of the token used as reserve in the bonding curve (i.e. the DAI contract). UseETH
if0
. -
init_reserve
. The initial number of FAIR created at initialization for thebeneficiary
. Technically however, this variable is not a constant as we must always haveinit_reserve>=total_supply+burnt_supply
which means thatinit_reserve
will be automatically decreased to equaltotal_supply+burnt_supply
in caseinit_reserve>total_supply+burnt_supply
after an investor sells his FAIRs. -
init_goal
. The initial fundraising goal (expressed in FAIR) to start the c-org.0
means that there is no initial fundraising and the c-org immediately moves torun
state. -
buy_slope
. The buy slope of the bonding curve. Does not affect the financial model, only the granularity of FAIR. -
investment_reserve
. The investment reserve of the c-org. Defines the percentage of the value invested that is automatically funneled and held into thebuyback_reserve
. -
setup_fee
. The success fee (expressed incurrency
) that will be earned bysetup_fee_recipient
as soon asinit_goal
is reached. We must havesetup_fee <= buy_slope*init_goal^(2)/2
. -
setup_fee_recipient
. The recipient of thesetup_fee
onceinit_goal
is reached.
These variables are preset in the contract but can be altered under certain conditions once the contract is deployed. The variables can be updated using the control
address. We highly recommend that this control
address be a multisig to prevent unilateral modification of these variables.
-
control
. The address from which the updatable variables (see below) can be updated. Defaults to the contract deployer. -
beneficiary
. The address of the beneficiary organization which receives the investments. Points to the wallet of the organization. Defaults to the contract deployer. -
auto_burn
. Set if the FAIRs minted by the organization when it commits its revenues are automatically burnt (1
) or not (0
). Defaults to0
meaning that there is no automatic burn. -
fee
. The fee collected each time new FAIR are issued. Defaults to0%
. -
fee_collector
. The address where fees are sent. Defaults to the contract deployer. -
min_investment
. The minimum investment accepted. Defaults to100
. -
locked_until
. The minimum date before which the c-org contract cannot be closed once the contract has reached therun
state. When updated, the new value oflocked_until
cannot be earlier than the previouslocked_until
and must be later thannow
. -
whitelist
refers to the contracts in charge of enforcing the legal restrictions related to the c-org (lock-up periods, no flow-back, transfer restrictions etc...). The contract referred bywhitelist
is modeled after the ERC-1404 (with one addition). Set whitelist to0
if you don't want to enforce on-chain legal restrictions, we advise you to seek for legal advise before doing so. Just make sure you know what you're doing. -
revenue_commitment
. The "on-chain" revenue commitment of the organization. If the organization has its revenues off-chain,revenue_commitment
is set to0
. To protect investors, it is important to note that the revenue commitment can be increased but can never be decreased. -
override_pay_to
. The address to which FAIRs minted by thepay()
function should be sent in case the caller ofpay()
used theto
parameter. Ifoverride_pay_to=0
then FAIRs will be sent to theto
address, otherwise FAIRs will be sent tooverride_pay_to
address.
These variables are the result of a calculation based on other variables. A key variable in the c-org model is the sell_slope
, the sell slope of the bonding curve. It is actually not coded as a variable in this contract but is being calculated on demand (with sell_slope=(2*buyback_reserve)/((total_supply+burnt_supply)^2)
) to avoid rounding issues. Other key variables non-coded are issuance_price
, the current issuance price for FAIR which equals (total_supply+burnt_supply-init_reserve)*buy_slope
and buyback_price
, the current minimum buy-back price which equals (total_supply+burnt_supply)*sell_slope + (sell_slope*burnt_supply^2)/(2*total_supply)
.
-
state
. The current state of the contract:0
=init
,1
=run
,2
=close
. Unlessinit_goal
equals0
, default isinit
otherwise default isrun
. Onceinit_goal
is reached,state
changes torun
. Finallystate
switches toclose
whenexit_fee
is paid. -
buyback_reserve
. The total amount of value currently locked in the buyback reserve. Thebuyback_reserve
should be implemented as the current contract balance of thecurrency
(instead of a separate variable). -
total_supply
. The total outstanding supply of FAIR issued, including the pre-minted FAIRs (seeinit_reserve
) but excluding burnt FAIRs (seeburnt_supply
). -
burnt_supply
. The total number of FAIR burnt.
Method called to buy FAIR. When buy()
is called:
When in init
state, every investor receives tokens for the same price until init_goal
is reached. beneficiary
is the only one allowed to transfer()
FAIRs from init_reserve
if any (ideally using vesting schedules). The investor is the caller or to
if specified. Note that minimum
is discarded in init
state.
- If investor is not allowed to buy FAIR (see compliance below), then the function exits.
- If
amount < min_investment
, then the function exits. - If
amount > (buy_slope*init_goal)*(init_goal-total_supply+init_reserve)/2
thennext_amount=amount - (buy_slope*init_goal)*(init_goal-total_supply+init_reserve)/2
amount=amount - next_amount
- if
next_amount>0
thenadditional_tokens=((2*next_amount/buy_slope)+init_goal^2)^(1/2)-init_goal
elseadditional_tokens=0
- Add
x
to the investor's balance withx=2*amount/(buy_slope*init_goal)+additional_tokens
. - Increase
total_supply
withx
new FAIRs. - Add
amount
to thebuyback_reserve
. - Save investor's total investment in
init_investors[address]+=x
. - If
total_supply - init_reserve >= init_goal
, then:-
state=run
. - calculate the amount
y
invested by the beneficiary during init withy=init_investors[beneficiary]*buy_slope*init_goal/2
- if
setup_fee>0
then- send
setup_fee
frombuyback_reserve
tosetup_fee_recipient
- update
buyback_reserve=buyback_reserve-setup_fee
- if
y>0 && y>setup_fee
theny=y-setup_fee
- else if
y>0
theny=0
- send
- send
(buyback_reserve-y)*(1-investment_reserve)*(1-fee)
to thebeneficiary
- send
(buyback_reserve-y)*(1-investment_reserve)*fee
to thefee_collector
- update
buyback_reserve = investment_reserve * (buyback_reserve-y) + y
-
- If the investor is not allowed to buy FAIR (see compliance), then the function exits.
- If
amount < min_investment
, then the function exits. - Calculate the number of FAIR
x
that the investor should receive for his investment withx=sqrt((2*amount/buy_slope)+(total_supply-init_reserve+burnt_supply)^2)-(total_supply-init_reserve+burnt_supply)
. - If
x < minimum
then the call fails. This is a protection against large price movements and front-running attacks. - Add
x
FAIRs to the investor's balance. - Increase
total_supply
withx
new FAIRs. - If the investor is the
beneficiary
, then:- if
auto_burn==1
thenburn(x)
is called. - the full
amount
is added to thebuyback_reserve
.
- if
- If the investor is not the
beneficiary
, then:-
investment_reserve*amount
is being added to thebuyback_reserve
-
(1-investment_reserve)*amount*(1-fee)
is being transfered tobeneficiary
. -
(1-investment_reserve)*amount*fee
is being sent tofee_collector
-
The buy()
functions fails in close
state.
Method called to sell FAIR. It is important to note that beneficiary
is only allowed to sell in close
state or cancel
state, beneficiary
cannot sell in any other state. When sell()
is called:
In init
state, the minimum
parameter is ignored.
- If
address == beneficiary
, then the function exits. - If
init_investors[address]
does not exists, then the function exits. Prevents the receivers of free FAIR frominit_reserve
to sell them at this time. - If
init_investors[address]<amount
then the call fails. Prevents the receivers of free FAIR frominit_reserve
to sell them at this time. -
amount
is being substracted from the investor's balance. - The investor receives
x
collateral value from thebuyback_reserve
withx=amount*buyback_reserve/(total_supply-init_reserve)
. - Save investor's total withdrawal in
init_investors[address]-=amount
. - The
total_supply
is decreased ofamount
FAIRs.
- If
address == beneficiary
, then the function exits. - If
init_goal=0 && buyback_reserve=0
, then the function exits. - The collateral value
x
that the investor should receive from the buyback reserve is calculated withx=(total_supply+burnt_supply)*amount*sell_slope-((sell_slope*amount^2)/2)+(sell_slope*amount*burnt_supply^2)/(2*(total_supply))
withsell_slope=(2*buyback_reserve)/((total_supply+burnt_supply)^2)
. - If
x < minimum
then the call fails. -
amount
is being substracted from the investor's balance if it is superior or equal toamount
. Otherwise the call fails. - The investor receives
x
collateral value from the buyback reserve - Substract
amount
FAIRs fromtotal_supply
to remove the sold FAIRs from the outstanding supply. - If
init_reserve>total_supply+burnt_supply
then setinit_reserve=total_supply+burnt_supply
In close
state, the minimum
parameter is ignored.
-
amount
is being substracted from the investor's balance if their balance is superior or equal toamount
. Otherwise the call fails. - The investor receives
x
collateral value from the buyback reserve withx=buyback_reserve*amount/total_supply
. - Substract
amount
FAIRs fromtotal_supply
to remove the sold FAIRs from the outstanding supply.
In cancel
state, the minimum
parameter is ignored.
- If
init_investors[address]
does not exists, then the function exits. Prevents the receivers of free FAIR frominit_reserve
to sell them at this time. - If
init_investors[address]<amount
then the call fails. Prevents the receivers of free FAIR frominit_reserve
to sell them at this time. -
amount
is being substracted from the investor's balance. - The investor receives
x
collateral value from thebuyback_reserve
withx=amount*buyback_reserve/(total_supply-init_reserve)
. - Save investor's total withdrawal in
init_investors[address]-=amount
. - The
total_supply
is decreased ofamount
FAIRs.
Method called to burn FAIR. When burn()
is called:
- If
state != 'run'
then the function exits. - Burn
amount
FAIRs by addingamount
toburnt_supply
. - Substract
amount
fromtotal_supply
The burn()
method fails during init
, close
and cancel
states.
Method called to pay the organization on-chain. This is the payable
method call when a transaction is sent to the c-org contract. If to
is specified and to
is allowed to receive FAIRs, then to
should receive the newly minted FAIRs. In the case where to
is not specified, the new FAIRs go to beneficiary
. When pay()
is called:
- If
state != 'run'
then the function exits. -
revenue_commitment*amount
is being added to thebuyback_reserve
and(1-revenue_commitment)*amount
is being transfered to thebeneficiary
. - Calculate
x
the number of newly issued FAIRs withx=sqrt((2*revenue_commitment*amount/buy_slope)+(total_supply+burnt_supply)^2)-(total_supply+burnt_supply)
. - If
to
is specified and ifto
is allowed to receive FAIRs and ifoverride_pay_to=0
, thenx
FAIRs are added to theto
address specified, otherwise, ifoverride_pay_to!=0
thenx
FAIRs are added to theoverride_pay_to
address, otherwisex
FAIRs are added to thebeneficiary
address. Ifto
is not specified,x
FAIRs are added to thebeneficiary
's balance. - The
total_supply
is increased withx
new FAIRs. - If
(auto_burn==1 && to==beneficiary)
thenburn(x)
is called.
The pay()
method fails during init
, close
and cancel
states.
Method called to close the c-org contract. To close the c-org contract, the beneficiary
needs to pay an exit_fee = total_supply*(total_supply+burnt_supply)*buy_slope - buyback_reserve
.
- If
address != beneficiary
then the function exits. - If
state == 'close' OR state == 'cancel'
then the function exits. - If
state == 'init'
thenstate = 'cancel'
- If
state == 'run'
andnow > locked_until
then- if
balanceOf(beneficiary) < (total_supply^2 * buy_slope)/2 + burnt_supply*buy_slope*total_supply - buyback_reserve
then the function exits. -
state = 'close'
. - substract
(total_supply^2 * buy_slope)/2 + burnt_supply*buy_slope*total_supply - buyback_reserve
from the balance ofbeneficiary
. -
buyback_reserve = (total_supply^2 * buy_slope)/2 + burnt_supply*buy_slope*total_supply
.
- if
The c-org contract is highly sensitive due to the funds it holds in its reserve and due to the potential number of investors it coordinates. Fairmint is committed to provide the community with c-org contracts of the highest quality and providing the highest security. It is expected that other contracts respecting the below specification will be developed by third-parties. If you are a developer developing a c-org contract on any blockchain, please get in touch with us to make sure we DO NOT release buggy or deceptive contracts on the market. Likewise, if you are a user of a contract not developed or not audited techically, economically and legally by Fairmint, please be very cautious as you might put your money and the money of your investors at risk. Obviously, Fairmint offers no warranty and is not responsible nor liable for any of the contracts implementing this specification. Use them at your own risk.