pool_u64 - [mainnet]
Simple module for tracking and calculating shares of a pool of coins. The shares are worth more as the total coins in the pool increases. New shareholder can buy more shares or redeem their existing shares.
Example flow:
- Pool start outs empty.
 - Shareholder A buys in with 1000 coins. A will receive 1000 shares in the pool. Pool now has 1000 total coins and 1000 total shares.
 - Pool appreciates in value from rewards and now has 2000 coins. A’s 1000 shares are now worth 2000 coins.
 - Shareholder B now buys in with 1000 coins. Since before the buy in, each existing share is worth 2 coins, B will receive 500 shares in exchange for 1000 coins. Pool now has 1500 shares and 3000 coins.
 - Pool appreciates in value from rewards and now has 6000 coins.
 - A redeems 500 shares. Each share is worth 6000 / 1500 = 4. A receives 2000 coins. Pool has 4000 coins and 1000 shares left.
 
use 0x1::error;use 0x1::simple_map;use 0x1::vector;Constants
const MAX_U64: u64 = 18446744073709551615;Cannot redeem more shares than the shareholder has in the pool.
const EINSUFFICIENT_SHARES: u64 = 4;Cannot destroy non-empty pool.
const EPOOL_IS_NOT_EMPTY: u64 = 3;Pool’s total coins cannot exceed u64.max.
const EPOOL_TOTAL_COINS_OVERFLOW: u64 = 6;Pool’s total shares cannot exceed u64.max.
const EPOOL_TOTAL_SHARES_OVERFLOW: u64 = 7;Shareholder not present in pool.
const ESHAREHOLDER_NOT_FOUND: u64 = 1;Shareholder cannot have more than u64.max shares.
const ESHAREHOLDER_SHARES_OVERFLOW: u64 = 5;There are too many shareholders in the pool.
const ETOO_MANY_SHAREHOLDERS: u64 = 2;Structs
Pool
struct Pool has storeFields
- 
shareholders_limit: u64 - 
total_coins: u64 - 
total_shares: u64 - 
shares: simple_map::SimpleMap<address, u64> - 
shareholders: vector<address> - 
scaling_factor: u64 
Functions
new
Create a new pool.
public fun new(shareholders_limit: u64): pool_u64::PoolImplementation
public fun new(shareholders_limit: u64): Pool {    // Default to a scaling factor of 1 (effectively no scaling).    create_with_scaling_factor(shareholders_limit, 1)}create
Deprecated. Use new instead.
Create a new pool.
#[deprecated]public fun create(shareholders_limit: u64): pool_u64::PoolImplementation
public fun create(shareholders_limit: u64): Pool {    new(shareholders_limit)}create_with_scaling_factor
Create a new pool with custom scaling_factor.
public fun create_with_scaling_factor(shareholders_limit: u64, scaling_factor: u64): pool_u64::PoolImplementation
public fun create_with_scaling_factor(shareholders_limit: u64, scaling_factor: u64): Pool {    Pool {        shareholders_limit,        total_coins: 0,        total_shares: 0,        shares: simple_map::create<address, u64>(),        shareholders: vector::empty<address>(),        scaling_factor,    }}destroy_empty
Destroy an empty pool. This will fail if the pool has any balance of coins.
public fun destroy_empty(self: pool_u64::Pool)Implementation
public fun destroy_empty(self: Pool) {    assert!(self.total_coins == 0, error::invalid_state(EPOOL_IS_NOT_EMPTY));    let Pool {        shareholders_limit: _,        total_coins: _,        total_shares: _,        shares: _,        shareholders: _,        scaling_factor: _,    } = self;}total_coins
Return self’s total balance of coins.
public fun total_coins(self: &pool_u64::Pool): u64Implementation
public fun total_coins(self: &Pool): u64 {    self.total_coins}total_shares
Return the total number of shares across all shareholders in self.
public fun total_shares(self: &pool_u64::Pool): u64Implementation
public fun total_shares(self: &Pool): u64 {    self.total_shares}contains
Return true if shareholder is in self.
public fun contains(self: &pool_u64::Pool, shareholder: address): boolImplementation
public fun contains(self: &Pool, shareholder: address): bool {    self.shares.contains_key(&shareholder)}shares
Return the number of shares of stakeholder in self.
public fun shares(self: &pool_u64::Pool, shareholder: address): u64Implementation
public fun shares(self: &Pool, shareholder: address): u64 {    if (self.contains(shareholder)) {        *self.shares.borrow(&shareholder)    } else {        0    }}balance
Return the balance in coins of shareholder in self.
public fun balance(self: &pool_u64::Pool, shareholder: address): u64Implementation
public fun balance(self: &Pool, shareholder: address): u64 {    let num_shares = self.shares(shareholder);    self.shares_to_amount(num_shares)}shareholders
Return the list of shareholders in self.
public fun shareholders(self: &pool_u64::Pool): vector<address>Implementation
public fun shareholders(self: &Pool): vector<address> {    self.shareholders}shareholders_count
Return the number of shareholders in self.
public fun shareholders_count(self: &pool_u64::Pool): u64Implementation
public fun shareholders_count(self: &Pool): u64 {    self.shareholders.length()}update_total_coins
Update self’s total balance of coins.
public fun update_total_coins(self: &mut pool_u64::Pool, new_total_coins: u64)Implementation
public fun update_total_coins(self: &mut Pool, new_total_coins: u64) {    self.total_coins = new_total_coins;}buy_in
Allow an existing or new shareholder to add their coins to the pool in exchange for new shares.
public fun buy_in(self: &mut pool_u64::Pool, shareholder: address, coins_amount: u64): u64Implementation
public fun buy_in(self: &mut Pool, shareholder: address, coins_amount: u64): u64 {    if (coins_amount == 0) return 0;
    let new_shares = self.amount_to_shares(coins_amount);    assert!(MAX_U64 - self.total_coins >= coins_amount, error::invalid_argument(EPOOL_TOTAL_COINS_OVERFLOW));    assert!(MAX_U64 - self.total_shares >= new_shares, error::invalid_argument(EPOOL_TOTAL_COINS_OVERFLOW));
    self.total_coins += coins_amount;    self.total_shares += new_shares;    self.add_shares(shareholder, new_shares);    new_shares}add_shares
Add the number of shares directly for shareholder in self.
This would dilute other shareholders if the pool’s balance of coins didn’t change.
fun add_shares(self: &mut pool_u64::Pool, shareholder: address, new_shares: u64): u64Implementation
fun add_shares(self: &mut Pool, shareholder: address, new_shares: u64): u64 {    if (self.contains(shareholder)) {        let existing_shares = self.shares.borrow_mut(&shareholder);        let current_shares = *existing_shares;        assert!(MAX_U64 - current_shares >= new_shares, error::invalid_argument(ESHAREHOLDER_SHARES_OVERFLOW));
        *existing_shares = current_shares + new_shares;        *existing_shares    } else if (new_shares > 0) {        assert!(            self.shareholders.length() < self.shareholders_limit,            error::invalid_state(ETOO_MANY_SHAREHOLDERS),        );
        self.shareholders.push_back(shareholder);        self.shares.add(shareholder, new_shares);        new_shares    } else {        new_shares    }}redeem_shares
Allow shareholder to redeem their shares in self for coins.
public fun redeem_shares(self: &mut pool_u64::Pool, shareholder: address, shares_to_redeem: u64): u64Implementation
public fun redeem_shares(self: &mut Pool, shareholder: address, shares_to_redeem: u64): u64 {    assert!(self.contains(shareholder), error::invalid_argument(ESHAREHOLDER_NOT_FOUND));    assert!(self.shares(shareholder) >= shares_to_redeem, error::invalid_argument(EINSUFFICIENT_SHARES));
    if (shares_to_redeem == 0) return 0;
    let redeemed_coins = self.shares_to_amount(shares_to_redeem);    self.total_coins -= redeemed_coins;    self.total_shares -= shares_to_redeem;    self.deduct_shares(shareholder, shares_to_redeem);
    redeemed_coins}transfer_shares
Transfer shares from shareholder_1 to shareholder_2.
public fun transfer_shares(self: &mut pool_u64::Pool, shareholder_1: address, shareholder_2: address, shares_to_transfer: u64)Implementation
public fun transfer_shares(    self: &mut Pool,    shareholder_1: address,    shareholder_2: address,    shares_to_transfer: u64,) {    assert!(self.contains(shareholder_1), error::invalid_argument(ESHAREHOLDER_NOT_FOUND));    assert!(self.shares(shareholder_1) >= shares_to_transfer, error::invalid_argument(EINSUFFICIENT_SHARES));    if (shares_to_transfer == 0) return;
    self.deduct_shares(shareholder_1, shares_to_transfer);    self.add_shares(shareholder_2, shares_to_transfer);}deduct_shares
Directly deduct shareholder’s number of shares in self and return the number of remaining shares.
fun deduct_shares(self: &mut pool_u64::Pool, shareholder: address, num_shares: u64): u64Implementation
fun deduct_shares(self: &mut Pool, shareholder: address, num_shares: u64): u64 {    assert!(self.contains(shareholder), error::invalid_argument(ESHAREHOLDER_NOT_FOUND));    assert!(self.shares(shareholder) >= num_shares, error::invalid_argument(EINSUFFICIENT_SHARES));
    let existing_shares = self.shares.borrow_mut(&shareholder);    *existing_shares -= num_shares;
    // Remove the shareholder completely if they have no shares left.    let remaining_shares = *existing_shares;    if (remaining_shares == 0) {        let (_, shareholder_index) = self.shareholders.index_of(&shareholder);        self.shareholders.remove(shareholder_index);        self.shares.remove(&shareholder);    };
    remaining_shares}amount_to_shares
Return the number of new shares coins_amount can buy in self.
amount needs to big enough to avoid rounding number.
public fun amount_to_shares(self: &pool_u64::Pool, coins_amount: u64): u64Implementation
public fun amount_to_shares(self: &Pool, coins_amount: u64): u64 {    self.amount_to_shares_with_total_coins(coins_amount, self.total_coins)}amount_to_shares_with_total_coins
Return the number of new shares coins_amount can buy in self with a custom total coins number.
amount needs to big enough to avoid rounding number.
public fun amount_to_shares_with_total_coins(self: &pool_u64::Pool, coins_amount: u64, total_coins: u64): u64Implementation
public fun amount_to_shares_with_total_coins(self: &Pool, coins_amount: u64, total_coins: u64): u64 {    // No shares yet so amount is worth the same number of shares.    if (self.total_coins == 0 || self.total_shares == 0) {        // Multiply by scaling factor to minimize rounding errors during internal calculations for buy ins/redeems.        // This can overflow but scaling factor is expected to be chosen carefully so this would not overflow.        coins_amount * self.scaling_factor    } else {        // Shares price = total_coins / total existing shares.        // New number of shares = new_amount / shares_price = new_amount * existing_shares / total_amount.        // We rearrange the calc and do multiplication first to avoid rounding errors.        self.multiply_then_divide(coins_amount, self.total_shares, total_coins)    }}shares_to_amount
Return the number of coins shares are worth in self.
shares needs to big enough to avoid rounding number.
public fun shares_to_amount(self: &pool_u64::Pool, shares: u64): u64Implementation
public fun shares_to_amount(self: &Pool, shares: u64): u64 {    self.shares_to_amount_with_total_coins(shares, self.total_coins)}shares_to_amount_with_total_coins
Return the number of coins shares are worth in self with a custom total coins number.
shares needs to big enough to avoid rounding number.
public fun shares_to_amount_with_total_coins(self: &pool_u64::Pool, shares: u64, total_coins: u64): u64Implementation
public fun shares_to_amount_with_total_coins(self: &Pool, shares: u64, total_coins: u64): u64 {    // No shares or coins yet so shares are worthless.    if (self.total_coins == 0 || self.total_shares == 0) {        0    } else {        // Shares price = total_coins / total existing shares.        // Shares worth = shares * shares price = shares * total_coins / total existing shares.        // We rearrange the calc and do multiplication first to avoid rounding errors.        self.multiply_then_divide(shares, total_coins, self.total_shares)    }}multiply_then_divide
public fun multiply_then_divide(self: &pool_u64::Pool, x: u64, y: u64, z: u64): u64Implementation
public fun multiply_then_divide(self: &Pool, x: u64, y: u64, z: u64): u64 {    math64::mul_div(x, y, z)}Specification
pragma verify = false;Pool
struct Pool has store- 
shareholders_limit: u64 - 
total_coins: u64 - 
total_shares: u64 - 
shares: simple_map::SimpleMap<address, u64> - 
shareholders: vector<address> - 
scaling_factor: u64 
invariant forall addr: address:    (simple_map::spec_contains_key(shares, addr) == vector::spec_contains(shareholders, addr));invariant forall i in 0..len(shareholders), j in 0..len(shareholders):    shareholders[i] == shareholders[j] ==> i == j;fun spec_contains(pool: Pool, shareholder: address): bool {   simple_map::spec_contains_key(pool.shares, shareholder)}contains
public fun contains(self: &pool_u64::Pool, shareholder: address): boolaborts_if false;ensures result == spec_contains(self, shareholder);fun spec_shares(pool: Pool, shareholder: address): u64 {   if (simple_map::spec_contains_key(pool.shares, shareholder)) {       simple_map::spec_get(pool.shares, shareholder)   }   else {       0   }}shares
public fun shares(self: &pool_u64::Pool, shareholder: address): u64aborts_if false;ensures result == spec_shares(self, shareholder);balance
public fun balance(self: &pool_u64::Pool, shareholder: address): u64let shares = spec_shares(self, shareholder);let total_coins = self.total_coins;aborts_if self.total_coins > 0 && self.total_shares > 0 && (shares * total_coins) / self.total_shares > MAX_U64;ensures result == spec_shares_to_amount_with_total_coins(self, shares, total_coins);buy_in
public fun buy_in(self: &mut pool_u64::Pool, shareholder: address, coins_amount: u64): u64let new_shares = spec_amount_to_shares_with_total_coins(self, coins_amount, self.total_coins);aborts_if self.total_coins + coins_amount > MAX_U64;aborts_if self.total_shares + new_shares > MAX_U64;include coins_amount > 0 ==> AddSharesAbortsIf { new_shares };include coins_amount > 0 ==> AddSharesEnsures { new_shares };ensures self.total_coins == old(self.total_coins) + coins_amount;ensures self.total_shares == old(self.total_shares) + new_shares;ensures result == new_shares;add_shares
fun add_shares(self: &mut pool_u64::Pool, shareholder: address, new_shares: u64): u64include AddSharesAbortsIf;include AddSharesEnsures;let key_exists = simple_map::spec_contains_key(self.shares, shareholder);ensures result == if (key_exists) { simple_map::spec_get(self.shares, shareholder) }else { new_shares };schema AddSharesAbortsIf {    self: Pool;    shareholder: address;    new_shares: u64;    let key_exists = simple_map::spec_contains_key(self.shares, shareholder);    let current_shares = simple_map::spec_get(self.shares, shareholder);    aborts_if key_exists && current_shares + new_shares > MAX_U64;    aborts_if !key_exists && new_shares > 0 && len(self.shareholders) >= self.shareholders_limit;}schema AddSharesEnsures {    self: Pool;    shareholder: address;    new_shares: u64;    let key_exists = simple_map::spec_contains_key(self.shares, shareholder);    let current_shares = simple_map::spec_get(self.shares, shareholder);    ensures key_exists ==>        self.shares == simple_map::spec_set(old(self.shares), shareholder, current_shares + new_shares);    ensures (!key_exists && new_shares > 0) ==>        self.shares == simple_map::spec_set(old(self.shares), shareholder, new_shares);    ensures (!key_exists && new_shares > 0) ==>        vector::eq_push_back(self.shareholders, old(self.shareholders), shareholder);}fun spec_amount_to_shares_with_total_coins(pool: Pool, coins_amount: u64, total_coins: u64): u64 {   if (pool.total_coins == 0 || pool.total_shares == 0) {       coins_amount * pool.scaling_factor   }   else {       (coins_amount * pool.total_shares) / total_coins   }}redeem_shares
public fun redeem_shares(self: &mut pool_u64::Pool, shareholder: address, shares_to_redeem: u64): u64let redeemed_coins = spec_shares_to_amount_with_total_coins(self, shares_to_redeem, self.total_coins);aborts_if !spec_contains(self, shareholder);aborts_if spec_shares(self, shareholder) < shares_to_redeem;aborts_if self.total_coins < redeemed_coins;aborts_if self.total_shares < shares_to_redeem;ensures self.total_coins == old(self.total_coins) - redeemed_coins;ensures self.total_shares == old(self.total_shares) - shares_to_redeem;include shares_to_redeem > 0 ==> DeductSharesEnsures {    num_shares: shares_to_redeem};ensures result == redeemed_coins;transfer_shares
public fun transfer_shares(self: &mut pool_u64::Pool, shareholder_1: address, shareholder_2: address, shares_to_transfer: u64)pragma aborts_if_is_partial;aborts_if !spec_contains(self, shareholder_1);aborts_if spec_shares(self, shareholder_1) < shares_to_transfer;deduct_shares
fun deduct_shares(self: &mut pool_u64::Pool, shareholder: address, num_shares: u64): u64aborts_if !spec_contains(self, shareholder);aborts_if spec_shares(self, shareholder) < num_shares;include DeductSharesEnsures;let remaining_shares = simple_map::spec_get(self.shares, shareholder) - num_shares;ensures remaining_shares > 0 ==> result == simple_map::spec_get(self.shares, shareholder);ensures remaining_shares == 0 ==> result == 0;schema DeductSharesEnsures {    self: Pool;    shareholder: address;    num_shares: u64;    let remaining_shares = simple_map::spec_get(self.shares, shareholder) - num_shares;    ensures remaining_shares > 0 ==> simple_map::spec_get(self.shares, shareholder) == remaining_shares;    ensures remaining_shares == 0 ==> !simple_map::spec_contains_key(self.shares, shareholder);    ensures remaining_shares == 0 ==> !vector::spec_contains(self.shareholders, shareholder);}amount_to_shares_with_total_coins
public fun amount_to_shares_with_total_coins(self: &pool_u64::Pool, coins_amount: u64, total_coins: u64): u64aborts_if self.total_coins > 0 && self.total_shares > 0    && (coins_amount * self.total_shares) / total_coins > MAX_U64;aborts_if (self.total_coins == 0 || self.total_shares == 0)    && coins_amount * self.scaling_factor > MAX_U64;aborts_if self.total_coins > 0 && self.total_shares > 0 && total_coins == 0;ensures result == spec_amount_to_shares_with_total_coins(self, coins_amount, total_coins);shares_to_amount_with_total_coins
public fun shares_to_amount_with_total_coins(self: &pool_u64::Pool, shares: u64, total_coins: u64): u64aborts_if self.total_coins > 0 && self.total_shares > 0    && (shares * total_coins) / self.total_shares > MAX_U64;ensures result == spec_shares_to_amount_with_total_coins(self, shares, total_coins);fun spec_shares_to_amount_with_total_coins(pool: Pool, shares: u64, total_coins: u64): u64 {   if (pool.total_coins == 0 || pool.total_shares == 0) {       0   }   else {       (shares * total_coins) / pool.total_shares   }}multiply_then_divide
public fun multiply_then_divide(self: &pool_u64::Pool, x: u64, y: u64, z: u64): u64aborts_if z == 0;aborts_if (x * y) / z > MAX_U64;ensures result == (x * y) / z;