Skip to content

Instantly share code, notes, and snippets.

@redjade
Created May 25, 2018 14:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save redjade/ca88d90e3f838c57ea7a1a287b23e30a to your computer and use it in GitHub Desktop.
Save redjade/ca88d90e3f838c57ea7a1a287b23e30a to your computer and use it in GitHub Desktop.
bancor_test.cpp
#include <iostream>
#include <cmath>
#include <iomanip>
#include <limits>
using namespace std;
class Connector {
public:
long double balance;
long double weight;
string symbol;
};
class Supply {
public:
long double amount;
string symbol;
};
class Asset {
public:
long double amount;
string symbol;
};
class Bancor {
public:
Supply supply;
Connector base;
Connector quote;
string mode;
int debug;
Bancor() {
supply.amount = 100000000000000ll;
//supply.amount = 100000;
supply.symbol = "RAMCORE";
base.balance = 1024.0 * 1024.0 * 1024.0 * 64.0;
//base.balance = 10000;
base.weight = 0.5;
base.symbol = "RAM";
quote.balance = 1000000000ll / 1000.0;
//quote.balance = 5000;
quote.weight = 0.5;
quote.symbol = "CORE";
}
void report() {
cout << supply.symbol << " " << supply.amount << endl;
cout << base.symbol << " " << base.balance << endl;
cout << quote.symbol << " " << quote.balance << endl;
}
Asset convert_to_exchange(Connector& c, Asset in) {
Asset asset;
long double R = supply.amount;
long double C;
if (mode == "hy") {
C = c.balance;
} else if (mode == "cur") {
C = c.balance + in.amount;
} else {
cout << "mode not defined" << endl;
}
if (debug) {
cout << "conn bal " << c.balance << endl;
cout << "conn bal + in.amount" << c.balance + in.amount << endl;
cout << "C in mode " << mode << " " << C << endl;
}
long double F = c.weight / 1000.0;
//long double F = c.weight;
long double T = in.amount;
long double E;
E = R * ( std::pow( 1.0 + T / C, F) - 1.0 );
cout << "E " << E << endl;
supply.amount += E;
c.balance += T;
asset.amount = E;
asset.symbol = supply.symbol;
return asset;
}
Asset convert_from_exchange(Connector& c, Asset in) {
Asset asset;
long double R;
if (mode == "hy") {
R = supply.amount;
} else if (mode == "cur") {
R = supply.amount - in.amount;
} else {
cout << "mode not defined" << endl;
}
if (debug) {
cout << "sup bal " << supply.amount << endl;
cout << "sup bal - in.amount " << supply.amount - in.amount << endl;
cout << "R in mode " << mode << " " << R << endl;
}
long double C = c.balance;
long double F = 1000.0 / c.weight;
//long double F = 1.0 / c.weight;
long double E = in.amount;
long double T;
if (mode == "hy") {
T = -C * ( std::pow( 1.0 - E / R, F ) - 1.0 );
//cout << "mode hy" << endl;
} else if (mode == "cur") {
T = C * ( std::pow( 1.0 + E / R, F ) - 1.0 );
//cout << "mode cur" << endl;
} else {
cout << "mode not defined" << endl;
}
cout << "T " << T << endl;
supply.amount -= E;
c.balance -= T;
asset.amount = T;
asset.symbol = c.symbol;
return asset;
}
Asset convert(Asset from, string symbol) {
string sell_symbol = from.symbol;
string ex_symbol = supply.symbol;
string base_symbol = base.symbol;
string quote_symbol = quote.symbol;
if( sell_symbol != ex_symbol ) {
if( sell_symbol == base_symbol ) {
from = convert_to_exchange( base, from );
} else if( sell_symbol == quote_symbol ) {
from = convert_to_exchange( quote, from );
} else {
cout << "invalid sell" << endl;
}
} else {
if( symbol == base_symbol ) {
from = convert_from_exchange( base, from );
} else if( symbol == quote_symbol ) {
from = convert_from_exchange( quote, from );
} else {
cout << "invalid conversion" << endl;
}
}
if( symbol != from.symbol )
return convert( from, symbol );
return from;
}
};
void test(string mode, string to_symbol) {
Bancor bc;
Asset asset, r_asset, r_asset_2;
Asset core_asset;
Asset acc_asset;
string mode_desc;
cout << std::setprecision(std::numeric_limits<long double>::digits10 + 1);
acc_asset.amount = 0.0;
acc_asset.symbol = to_symbol;
core_asset.amount = 0.0;
core_asset.symbol = "CORE";
bc.mode = mode;
if (mode == "hy") {
mode_desc = "with fixes by @hyunwoongJi";
} else if (mode == "cur") {
mode_desc = "with current EOSIO implementation";
}
cout << "Test mode: " << mode_desc << ", converting between CORE <-> " << to_symbol << endl;
bc.report();
asset.amount = 1247.49294959;
asset.symbol = "CORE";
core_asset.amount += asset.amount;
cout << "converting CORE " << asset.amount << " to " << to_symbol << endl;
r_asset = bc.convert(asset, to_symbol);
acc_asset.amount += r_asset.amount;
cout << r_asset.amount << " " << r_asset.symbol << endl;
cout << "eff price ( CORE / " << to_symbol << " ): " << asset.amount / r_asset.amount << endl;
bc.report();
asset.amount = 332.49294959;
asset.symbol = "CORE";
core_asset.amount += asset.amount;
cout << "converting CORE " << asset.amount << " to " << to_symbol << endl;
r_asset = bc.convert(asset, to_symbol);
acc_asset.amount += r_asset.amount;
cout << r_asset.amount << " " << r_asset.symbol << endl;
cout << "eff price ( CORE / " << to_symbol << " ): " << asset.amount / r_asset.amount << endl;
bc.report();
asset.amount = 672.49294959;
asset.symbol = "CORE";
core_asset.amount += asset.amount;
cout << "converting CORE " << asset.amount << " to " << to_symbol << endl;
r_asset = bc.convert(asset, to_symbol);
acc_asset.amount += r_asset.amount;
cout << r_asset.amount << " " << r_asset.symbol << endl;
cout << "eff price ( CORE / " << to_symbol << " ): " << asset.amount / r_asset.amount << endl;
bc.report();
cout << "converting " << to_symbol << " " << acc_asset.amount << " to CORE" << endl;
r_asset_2 = bc.convert(acc_asset, "CORE");
bc.report();
cout << "CORE total paid so far: " << core_asset.amount << " " << core_asset.symbol<< endl;
cout << "CORE total regained: " << r_asset_2.amount << " " << r_asset_2.symbol << endl;
cout << "CORE regained - paid: " << r_asset_2.amount - core_asset.amount << " CORE" << endl;
cout << "Test done: " << mode_desc << ", converting between CORE <-> " << to_symbol << endl << endl;
return;
}
int main() {
test("hy", "RAMCORE");
test("cur", "RAMCORE");
test("hy", "RAM");
test("cur", "RAM");
}
@redjade
Copy link
Author

redjade commented May 25, 2018

Results under macos.

Result summary and notes:

  • correct bancor implementation with fixes suggested by @hyunwoongJi
    • smart token scenario (CORE <-> RAMCORE) : works as expected.
    • relay token scenario (CORE <-> RAMCORE <-> RAM) : works as expected.
  • current bancor implementation of EOSIO
    • smart token scenario (CORE <-> RAMCORE) : shows broken result.
    • relay token scenario (CORE <-> RAMCORE <-> RAM) : fortunately works because two-way conversion holds invariant.
      • invariant holds in current bancor implementation of EOSIO, verified by @sergioyuhjtman
      • invariant holds when S = S_0, m = 2, F1 = F2 in multiple reserve currencies, also verified by @sergioyuhjtman
Test mode: with fixes by @hyunwoongJi, converting between CORE <-> RAMCORE
RAMCORE 100000000000000
RAM 68719476736
CORE 1000000.000000000139
CORE total paid so far: 2252.47884877000007 CORE
CORE total regained: 2252.478848769861115 CORE
CORE regained - paid: -1.389548476282698175e-10 CORE

Test mode: with current EOSIO implementation, converting between CORE <-> RAMCORE
RAMCORE 99999999999999.99999
RAM 68719476736
CORE 999997.0486492722129
CORE total paid so far: 2252.47884877000007 CORE
CORE total regained: 2255.430199497787153 CORE
CORE regained - paid: 2.951350727787082517 CORE

Test mode: with fixes by @hyunwoongJi, converting between CORE <-> RAM
RAMCORE 100000000000000
RAM 68719476736
CORE 1000000.000000000139
CORE total paid so far: 2252.47884877000007 CORE
CORE total regained: 2252.478848769861115 CORE
CORE regained - paid: -1.389548476282698175e-10 CORE

Test mode: with current EOSIO implementation, converting between CORE <-> RAM
RAMCORE 100000000000000
RAM 68719476736
CORE 1000000.000000000122
CORE total paid so far: 2252.47884877000007 CORE
CORE total regained: 2252.47884876987823 CORE
CORE regained - paid: -1.218400935698582543e-10 CORE

Full results below:

Test mode: with fixes by @hyunwoongJi, converting between CORE <-> RAMCORE
RAMCORE 100000000000000
RAM 68719476736
CORE 1000000
converting CORE 1247.49294959000008 to RAMCORE
E 62335793.26813754387
62335793.26813754387 RAMCORE
eff price ( CORE / RAMCORE ): 2.001246609991640875e-05
RAMCORE 100000062335793.2681
RAM 68719476736
CORE 1001247.49294959
converting CORE 332.4929495900000234 to RAMCORE
E 16601189.61901551068
16601189.61901551068 RAMCORE
eff price ( CORE / RAMCORE ): 2.002826045725978713e-05
RAMCORE 100000078936982.8872
RAM 68719476736
CORE 1001579.98589918
converting CORE 672.4929495899999665 to RAMCORE
E 33560371.45616803816
33560371.45616803816 RAMCORE
eff price ( CORE / RAMCORE ): 2.003830471508094522e-05
RAMCORE 100000112497354.3433
RAM 68719476736
CORE 1002252.47884877
converting RAMCORE 112497354.3433210927 to CORE
T 2252.478848769861115
RAMCORE 100000000000000
RAM 68719476736
CORE 1000000.000000000139
CORE total paid so far: 2252.47884877000007 CORE
CORE total regained: 2252.478848769861115 CORE
CORE regained - paid: -1.389548476282698175e-10 CORE
Test done: with fixes by @hyunwoongJi, converting between CORE <-> RAMCORE

Test mode: with current EOSIO implementation, converting between CORE <-> RAMCORE
RAMCORE 100000000000000
RAM 68719476736
CORE 1000000
converting CORE 1247.49294959000008 to RAMCORE
E 62258175.00356582365
62258175.00356582365 RAMCORE
eff price ( CORE / RAMCORE ): 2.003741596213750545e-05
RAMCORE 100000062258175.0036
RAM 68719476736
CORE 1001247.49294959
converting CORE 332.4929495900000234 to RAMCORE
E 16595679.4490921798
16595679.4490921798 RAMCORE
eff price ( CORE / RAMCORE ): 2.003491032771111518e-05
RAMCORE 100000078853854.4527
RAM 68719476736
CORE 1001579.98589918
converting CORE 672.4929495899999665 to RAMCORE
E 33537860.58401557203
33537860.58401557203 RAMCORE
eff price ( CORE / RAMCORE ): 2.005175458062807361e-05
RAMCORE 100000112391715.0367
RAM 68719476736
CORE 1002252.47884877
converting RAMCORE 112391715.0366735755 to CORE
T 2255.430199497787153
RAMCORE 99999999999999.99999
RAM 68719476736
CORE 999997.0486492722129
CORE total paid so far: 2252.47884877000007 CORE
CORE total regained: 2255.430199497787153 CORE
CORE regained - paid: 2.951350727787082517 CORE
Test done: with current EOSIO implementation, converting between CORE <-> RAMCORE

Test mode: with fixes by @hyunwoongJi, converting between CORE <-> RAM
RAMCORE 100000000000000
RAM 68719476736
CORE 1000000
converting CORE 1247.49294959000008 to RAM
E 62335793.26813754387
T 85620252.06688208878
85620252.06688208878 RAM
eff price ( CORE / RAM ): 1.457006864001665699e-05
RAMCORE 100000000000000
RAM 68633856483.93311791
CORE 1001247.49294959
converting CORE 332.4929495900000234 to RAM
E 16601179.27053872053
T 22784274.55155515243
22784274.55155515243 RAM
eff price ( CORE / RAM ): 1.459308914302498805e-05
RAMCORE 100000000000000
RAM 68611072209.38156276
CORE 1001579.98589918
converting CORE 672.4929495899999665 to RAM
E 33560344.96464427655
T 46036765.48409762318
46036765.48409762318 RAM
eff price ( CORE / RAM ): 1.460773671908591624e-05
RAMCORE 100000000000000
RAM 68565035443.89746514
CORE 1002252.47884877
converting RAM 154441292.1025348644 to CORE
E 112497354.3433177396
T 2252.478848769861115
RAMCORE 100000000000000
RAM 68719476736
CORE 1000000.000000000139
CORE total paid so far: 2252.47884877000007 CORE
CORE total regained: 2252.478848769861115 CORE
CORE regained - paid: -1.389548476282698175e-10 CORE
Test done: with fixes by @hyunwoongJi, converting between CORE <-> RAM

Test mode: with current EOSIO implementation, converting between CORE <-> RAM
RAMCORE 100000000000000
RAM 68719476736
CORE 1000000
converting CORE 1247.49294959000008 to RAM
E 62258175.00356582365
T 85620252.06687392294
85620252.06687392294 RAM
eff price ( CORE / RAM ): 1.457006864001804658e-05
RAMCORE 100000000000000
RAM 68633856483.93312608
CORE 1001247.49294959
converting CORE 332.4929495900000234 to RAM
E 16595669.11693145796
T 22784274.55157152227
22784274.55157152227 RAM
eff price ( CORE / RAM ): 1.459308914301450334e-05
RAMCORE 100000000000000
RAM 68611072209.38155456
CORE 1001579.98589918
converting CORE 672.4929495899999665 to RAM
E 33537834.13814065417
T 46036765.48409458263
46036765.48409458263 RAM
eff price ( CORE / RAM ): 1.460773671908688102e-05
RAMCORE 100000000000000
RAM 68565035443.89745997
CORE 1002252.47884877
converting RAM 154441292.1025400278 to CORE
E 112244809.3551949022
T 2252.47884876987823
RAMCORE 100000000000000
RAM 68719476736
CORE 1000000.000000000122
CORE total paid so far: 2252.47884877000007 CORE
CORE total regained: 2252.47884876987823 CORE
CORE regained - paid: -1.218400935698582543e-10 CORE
Test done: with current EOSIO implementation, converting between CORE <-> RAM

@sergioyuhjtman
Copy link

Thanks @redjade.

Indeed, in current implementation the conversion between EOS and RAM verifies that the product of the connectors' reserves (EOS times RAM) is constant. This means exact agreement with Rosenfeld's paper (https://goo.gl/HXQBUr) section "multiple reserve currencies" taking S=S_0, m=2 and F_1=F_2.

This simple feature looks rather obscured in the code, leading to the following question: https://eosio.stackexchange.com/questions/408/whats-the-purpose-of-rameos-relay

See also: https://eosio.stackexchange.com/questions/317/is-there-a-math-error-in-bancor-paper

@hyunwoongJi
Copy link

Add two new functions into class Bancor,

        long double buyRAM(long double dC) {
            long double R = base.balance; // RAM
            long double C = quote.balance; // EOS
            long double dR; // dRAM

            dR = R * (dC / (C + dC));

            base.balance -= dR;
            quote.balance += dC;

            return dR;
        }

        long double sellRAM(long double dR) {
            long double R = base.balance; // RAM
            long double C = quote.balance; // EOS
            long double dC;

            dC = C * (dR / (R + dR));

            quote.balance -= dC;
            base.balance += dR;

            return dC;
        }

And add test_relay function() into main()

void test_relay() {
    Bancor bc;
    long double c_amount, r_amount;
    long double c_amount_acc = 0.0;
    long double  r_amount_acc = 0.0;

    c_amount = 1247.49294959;
    r_amount = bc.buyRAM(c_amount);
    c_amount_acc += c_amount;
    r_amount_acc += r_amount;
    cout << "converting CORE " << c_amount << " to RAM" << endl;
    cout << "RAM: " << r_amount << endl;

    c_amount = 332.49294959;
    r_amount = bc.buyRAM(c_amount);
    c_amount_acc += c_amount;
    r_amount_acc += r_amount;
    cout << "converting CORE " << c_amount << " to RAM" << endl;
    cout << "RAM: " << r_amount << endl;

    c_amount = 672.49294959;
    r_amount = bc.buyRAM(c_amount);
    c_amount_acc += c_amount;
    r_amount_acc += r_amount;
    cout << "converting CORE " << c_amount << " to RAM" << endl;
    cout << "RAM: " << r_amount << endl;

    c_amount = bc.sellRAM(r_amount_acc);
    cout << "converting RAM " << r_amount_acc << " to CORE" << endl;
    cout << "CORE total paid so far: " << c_amount_acc << " CORE" << endl;
    cout << "CORE total regained: " << c_amount << " CORE" << endl;
    cout << "CORE regained - paid: " << c_amount - c_amount_acc << " CORE" <<  endl;
}

int main() {
    test_relay();
}

And the result is as follows:

converting CORE 1247.49294959000008 to RAM
RAM: 85620252.06687848171
converting CORE 332.4929495900000234 to RAM
RAM: 22784274.55156515296
converting CORE 672.4929495899999665 to RAM
RAM: 46036765.48410075686
converting RAM 154441292.1025443915 to CORE
CORE total paid so far: 2252.47884877000007 CORE
CORE total regained: 2252.47884877000007 CORE
CORE regained - paid: -2.220446049250313081e-16 CORE

The error is much less than the previous one.

  • error of recommended implementation above : -2.220446049250313081e-16
  • error of correct implementation with convert_to_exchange and convert_from_exchange : -1.389548476282698175e-10
  • error of current EOSIO implementation with convert_to_exchange and convert_from_exchange : -1.218400935698582543e-10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment