2 | Start a reverse Dutch auction for the BANK/SYS token, in exchange for a given currency.
3 |
buyshares
4 | Buy the BANK/SYS token from a given auction using currency.
5 |
updateask
6 | Update the ask price for a given auction.
7 |
--------------------------------------------------------------------------------
/tests/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "pay2key.sign.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "bs58": "^4.0.1",
14 | "eosjs-ecc": "^4.0.4"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/bank.cdp/perm.json:
--------------------------------------------------------------------------------
1 | {
2 | "threshold": 1,
3 | "keys": [
4 | {
5 | "key": "EOS6TnW2MQbZwXHWDHAYQazmdc3Sc1KGv4M9TSgsKZJSo43Uxs2Bx",
6 | "weight": 1
7 | }
8 | ],
9 | "accounts": [
10 | {
11 | "permission": {
12 | "actor": "bank.cdp",
13 | "permission": "eosio.code"
14 | },
15 | "weight": 1
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/bank.pay2key/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "EOSIO UTXO Frontend",
3 | "name": "EOSIO UTXO Frontend",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/bank.pay2key/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/bank.pay2key/frontend/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | background-color: #282c34;
4 | min-height: 100vh;
5 | display: flex;
6 | flex-direction: column;
7 | align-items: center;
8 | justify-content: center;
9 | font-size: calc(10px + 2vmin);
10 | color: white;
11 | }
12 |
13 | .label {
14 | display: block;
15 | text-align: right;
16 | width: 430px;
17 | }
18 |
19 | .input {
20 | margin-left: 15px;
21 | }
22 |
--------------------------------------------------------------------------------
/bank.pay2key/js_test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js_test",
3 | "version": "1.0.0",
4 | "description": "Test suite",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "bs58": "^4.0.1",
13 | "eosjs-ecc": "^4.0.4"
14 | },
15 | "devDependencies": {
16 | "shelljs": "^0.8.3"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/bank.pay2key/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/bank.pay2key/relay-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "relay-server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "node relayer.js"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "@hapi/joi": "^15.0.0",
15 | "body-parser": "^1.19.0",
16 | "dotenv": "^7.0.0",
17 | "eosjs": "^20.0.0",
18 | "express": "^4.16.4",
19 | "morgan": "^1.9.1",
20 | "node-fetch": "^2.4.1"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/bank.pay2key/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eosio_utxo_frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "bs58": "^4.0.1",
7 | "eosjs-ecc": "^4.0.4",
8 | "react": "^16.7.0",
9 | "react-dom": "^16.7.0",
10 | "react-scripts": "2.1.3"
11 | },
12 | "scripts": {
13 | "start": "react-scripts start",
14 | "build": "react-scripts build",
15 | "test": "react-scripts test",
16 | "eject": "react-scripts eject"
17 | },
18 | "eslintConfig": {
19 | "extends": "react-app"
20 | },
21 | "browserslist": [
22 | ">0.2%",
23 | "not dead",
24 | "not ie <= 11",
25 | "not op_mini all"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/UnitTestsExternalProject.txt:
--------------------------------------------------------------------------------
1 | include(ExternalProject)
2 | find_package(Git REQUIRED)
3 | include(GNUInstallDirs)
4 |
5 | string(REPLACE ";" "|" TEST_FRAMEWORK_PATH "${CMAKE_FRAMEWORK_PATH}")
6 | string(REPLACE ";" "|" TEST_MODULE_PATH "${CMAKE_MODULE_PATH}")
7 |
8 | ExternalProject_Add(
9 | contracts_unit_tests
10 | LIST_SEPARATOR | # Use the alternate list separator
11 | CMAKE_ARGS -DCMAKE_BUILD_TYPE=${TEST_BUILD_TYPE} -DCMAKE_FRAMEWORK_PATH=${TEST_FRAMEWORK_PATH} -DCMAKE_MODULE_PATH=${TEST_MODULE_PATH} -DEOSIO_ROOT=${EOSIO_ROOT} -DLLVM_DIR=${LLVM_DIR}
12 | SOURCE_DIR ${CMAKE_SOURCE_DIR}/tests
13 | BINARY_DIR ${CMAKE_BINARY_DIR}/tests
14 | BUILD_ALWAYS 1
15 | TEST_COMMAND ""
16 | INSTALL_COMMAND ""
17 | )
18 |
--------------------------------------------------------------------------------
/bank.pay2key/relay-server/README.md:
--------------------------------------------------------------------------------
1 | # UTXO Relay Server
2 |
3 | This HTTP server exposes a single endpoint:
4 |
5 | `POST /relay`: Relay a UTXO tranasction with the parameters specified in the `.env` file. Seem `SAMPLE.env` for how to configure the environment.
6 |
7 | Example:
8 |
9 | ```
10 | POST /relay
11 | Content-Type: application/json
12 |
13 | {
14 | "from": "EOS7PoGq46ssqeGh8ZNScWQxqbwg5RNvLAwVw3i5dQcZ3a1h9nRyr",
15 | "to": "EOS7VZbHpaEog944Wdw6XuddGBCYVGcy7W8QA5khtQP3dAgBVgB7T",
16 | "amount": "9.000 IQUTXO",
17 | "fee": "1.000 IQUTXO",
18 | "nonce": 6,
19 | "memo": "testing",
20 | "sig": "SIG_K1_K17GrShyMD6A84D9YkZn2CNioY6rDrKGTz7uCduHZ663cNfLq7ecvZqY9vJsPMExetVMwKTQeNMceKtopMj4iSo8nyXLEW"
21 | }
22 | ```
23 |
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Decentral Bank: Turing-Complete Currency and Banking Blockchain Network
2 |
3 | Decentral Bank is a new project headed by Everipedia execs Sam Kazemian and Kedar Iyer which seeks to build a new general-compute blockchain specially customized for central bank digital currencies, stablecoins, DeFi applications, stablecoins issuance, and DLT monetary policy. Join the public telegram group here: https://t.me/decentralbank
4 |
5 | The blockchain's base smart contract system is based on the EOSIO protocol with many customizations, consensus method changes, and new libraries for issuing stablecoins, building defi apps, and true privacy transactions.
6 |
7 | This repository houses the contracts for banking and monetary policy usage of the chain. Current modules in progress are: bond tokens and instruments, UTXO and MimbleWimble tokens, a debt/collateralization framework (CDP), and the network's native staking/consensus token.
8 |
9 | More information coming soon
10 |
--------------------------------------------------------------------------------
/tests/contracts.hpp.in:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | namespace eosio { namespace testing {
5 |
6 | struct contracts {
7 | static std::vector fraxloans_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../frax.loans/frax.loans.wasm"); }
8 | static std::vector fraxloans_abi() { return read_abi("${CMAKE_BINARY_DIR}/../frax.loans/frax.loans.abi"); }
9 |
10 | static std::vector fraxreserve_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../frax.reserve/frax.reserve.wasm"); }
11 | static std::vector fraxreserve_abi() { return read_abi("${CMAKE_BINARY_DIR}/../frax.reserve/frax.reserve.abi"); }
12 |
13 | static std::vector token_wasm() { return read_wasm("${CMAKE_SOURCE_DIR}/test_contracts/eosio.token/eosio.token.wasm"); }
14 | static std::vector token_abi() { return read_abi("${CMAKE_SOURCE_DIR}/test_contracts/eosio.token/eosio.token.abi"); }
15 | };
16 | }} //ns eosio::testing
17 |
--------------------------------------------------------------------------------
/bank.safesnd/bank.safesnd.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | using namespace eosio;
5 |
6 | struct transfer_args {
7 | name from;
8 | name to;
9 | asset quantity;
10 | std::string memo;
11 | };
12 |
13 | void safetransfer (const transfer_args& transfer) {
14 | if (transfer.to != "bank.safesnd"_n)
15 | eosio_exit(0);
16 |
17 | name to = name(transfer.memo);
18 | action(
19 | permission_level{ "bank.safesnd"_n, "active"_n },
20 | "bank.token", "transfer"_n,
21 | std::make_tuple( "bank.safesnd"_n, to, transfer.quantity, std::string("bank.safesnd") )
22 | ).send();
23 | }
24 |
25 | extern "C" {
26 | [[noreturn]] void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
27 | eosio_assert( code == "bank.token"_n.value && action == "transfer"_n.value, "only bank.token transfers permitted");
28 | safetransfer( unpack_action_data() );
29 | eosio_exit(0);
30 | }
31 | }
--------------------------------------------------------------------------------
/bank.pay2key/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Chris Atanasian and Mason Raasch
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/tests/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #define BOOST_TEST_STATIC_LINK
14 |
15 | void translate_fc_exception(const fc::exception &e) {
16 | std::cerr << "\033[33m" << e.to_detail_string() << "\033[0m" << std::endl;
17 | BOOST_TEST_FAIL("Caught Unexpected Exception");
18 | }
19 |
20 | boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
21 |
22 | bool is_verbose = false;
23 | std::string verbose_arg = "--verbose";
24 | for (int i = 0; i < argc; i++) {
25 | if (verbose_arg == argv[i]) {
26 | is_verbose = true;
27 | break;
28 | }
29 | }
30 | if(!is_verbose) fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::off);
31 |
32 | boost::unit_test::unit_test_monitor.template register_exception_translator(&translate_fc_exception);
33 |
34 | return nullptr;
35 | }
36 |
--------------------------------------------------------------------------------
/tests/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | set(EOSIO_VERSION_MIN "1.6")
4 | set(EOSIO_VERSION_SOFT_MAX "1.8")
5 | #set(EOSIO_VERSION_HARD_MAX "")
6 |
7 | find_package(eosio)
8 |
9 | ### Check the version of eosio
10 | set(VERSION_MATCH_ERROR_MSG "")
11 | EOSIO_CHECK_VERSION(VERSION_OUTPUT "${EOSIO_VERSION}"
12 | "${EOSIO_VERSION_MIN}"
13 | "${EOSIO_VERSION_SOFT_MAX}"
14 | "${EOSIO_VERSION_HARD_MAX}"
15 | VERSION_MATCH_ERROR_MSG)
16 | if(VERSION_OUTPUT STREQUAL "MATCH")
17 | message(STATUS "Using eosio version ${EOSIO_VERSION}")
18 | elseif(VERSION_OUTPUT STREQUAL "WARN")
19 | message(WARNING "Using eosio version ${EOSIO_VERSION} even though it exceeds the maximum supported version of ${EOSIO_VERSION_SOFT_MAX}; continuing with configuration, however build may fail.\nIt is recommended to use eosio version ${EOSIO_VERSION_SOFT_MAX}.x")
20 | else() # INVALID OR MISMATCH
21 | message(FATAL_ERROR "Found eosio version ${EOSIO_VERSION} but it does not satisfy version requirements: ${VERSION_MATCH_ERROR_MSG}\nPlease use eosio version ${EOSIO_VERSION_SOFT_MAX}.x")
22 | endif(VERSION_OUTPUT STREQUAL "MATCH")
23 |
24 |
25 | enable_testing()
26 |
27 | configure_file(${CMAKE_SOURCE_DIR}/contracts.hpp.in ${CMAKE_BINARY_DIR}/contracts.hpp)
28 |
29 | include_directories(${CMAKE_BINARY_DIR})
30 |
31 | file(GLOB UNIT_TESTS "*.cpp" "*.hpp")
32 |
33 | add_eosio_test( unit_test ${UNIT_TESTS} )
34 |
--------------------------------------------------------------------------------
/bank.pay2key/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
15 |
16 |
25 | EOSIO UTXO Frontend
26 |
27 |
28 |
29 |
30 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 | project(frax_contracts VERSION 1.0.0)
3 |
4 | set(EOSIO_CDT_VERSION_MIN "1.6")
5 | set(EOSIO_CDT_VERSION_SOFT_MAX "1.6")
6 | #set(EOSIO_CDT_VERSION_HARD_MAX "")
7 |
8 | find_package(eosio.cdt)
9 |
10 | ### Check the version of eosio.cdt
11 | set(VERSION_MATCH_ERROR_MSG "")
12 | EOSIO_CHECK_VERSION(VERSION_OUTPUT "${EOSIO_CDT_VERSION}"
13 | "${EOSIO_CDT_VERSION_MIN}"
14 | "${EOSIO_CDT_VERSION_SOFT_MAX}"
15 | "${EOSIO_CDT_VERSION_HARD_MAX}"
16 | VERSION_MATCH_ERROR_MSG)
17 | if(VERSION_OUTPUT STREQUAL "MATCH")
18 | message(STATUS "Using eosio.cdt version ${EOSIO_CDT_VERSION}")
19 | elseif(VERSION_OUTPUT STREQUAL "WARN")
20 | message(WARNING "Using eosio.cdt version ${EOSIO_CDT_VERSION} even though it exceeds the maximum supported version of ${EOSIO_CDT_VERSION_SOFT_MAX}; continuing with configuration, however build may fail.\nIt is recommended to use eosio.cdt version ${EOSIO_CDT_VERSION_SOFT_MAX}.x")
21 | else() # INVALID OR MISMATCH
22 | message(FATAL_ERROR "Found eosio.cdt version ${EOSIO_CDT_VERSION} but it does not satisfy version requirements: ${VERSION_MATCH_ERROR_MSG}\nPlease use eosio.cdt version ${EOSIO_CDT_VERSION_SOFT_MAX}.x")
23 | endif(VERSION_OUTPUT STREQUAL "MATCH")
24 |
25 | if(CMAKE_BUILD_TYPE STREQUAL "Debug")
26 | set(TEST_BUILD_TYPE "Debug")
27 | set(CMAKE_BUILD_TYPE "Release")
28 | else()
29 | set(TEST_BUILD_TYPE ${CMAKE_BUILD_TYPE})
30 | endif()
31 |
32 | add_subdirectory(frax.loans)
33 | add_subdirectory(frax.reserve)
34 |
35 | if (APPLE)
36 | set(OPENSSL_ROOT "/usr/local/opt/openssl")
37 | elseif (UNIX)
38 | set(OPENSSL_ROOT "/usr/include/openssl")
39 | endif()
40 | set(SECP256K1_ROOT "/usr/local")
41 |
42 | if ("$ENV{NOTEST}" STREQUAL "")
43 | include(UnitTestsExternalProject.txt)
44 | endif()
45 |
--------------------------------------------------------------------------------
/tests/token_test_api.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | using namespace eosio::testing;
4 | using namespace eosio;
5 | using namespace eosio::chain;
6 |
7 | using mvo = fc::mutable_variant_object;
8 | using action_result = base_tester::action_result;
9 |
10 | class token_test_api {
11 | public:
12 | account_name contract;
13 |
14 | token_test_api(account_name acnt, tester* tester) {
15 | contract = acnt;
16 | _tester = tester;
17 |
18 | _tester->create_accounts({contract});
19 | _tester->set_code(contract, contracts::token_wasm());
20 | _tester->set_abi(contract, contracts::token_abi().data());
21 |
22 | const auto &accnt = _tester->control->db().get(contract);
23 |
24 | abi_def abi;
25 | BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true);
26 | abi_ser.set_abi(abi, abi_serializer_max_time);
27 | }
28 |
29 | action_result transfer(const name& from, const name& to, const asset& amount, const string memo = "")
30 | {
31 | return push_action(
32 | from, contract, N(transfer),
33 | mvo()("from", from)("to", to)("quantity", amount)("memo", memo));
34 | }
35 |
36 | fc::variant get_account(const account_name& acc, const string& symbolname)
37 | {
38 | auto symb = eosio::chain::symbol::from_string(symbolname);
39 | auto symbol_code = symb.to_symbol_code().value;
40 | vector data = _tester->get_row_by_account( contract, acc, N(accounts), symbol_code );
41 | return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "account", data, abi_serializer_max_time );
42 | }
43 |
44 | action_result push_action(const account_name &signer,
45 | const account_name &cnt,
46 | const action_name &name,
47 | const variant_object &data) {
48 | string action_type_name = abi_ser.get_action_type(name);
49 | action act;
50 | act.account = cnt;
51 | act.name = name;
52 | act.data = abi_ser.variant_to_binary(action_type_name, data, abi_serializer_max_time);
53 |
54 | return _tester->push_action(std::move(act), uint64_t(signer));
55 | }
56 |
57 | private:
58 | abi_serializer abi_ser;
59 | tester* _tester;
60 | fc::microseconds abi_serializer_max_time{1000*1000};
61 | };
62 |
--------------------------------------------------------------------------------
/bank.price/bank.price.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | using namespace eosio;
6 | using namespace std;
7 |
8 | class [[eosio::contract("bank.price")]] bankprice : public contract {
9 |
10 | private:
11 |
12 | struct [[eosio::table]] price_t {
13 | asset price;
14 | uint64_t primary_key() const { return price.symbol.code().raw(); }
15 | };
16 |
17 | typedef multi_index<"price"_n, price_t> prices_table;
18 |
19 | public:
20 |
21 | using contract::contract;
22 |
23 | ACTION create(asset initprice);
24 |
25 | ACTION update(name updater, asset price);
26 |
27 | };
28 |
29 | // Table info from eosio.system contract
30 | // Needed to determine top 21 producers
31 | namespace eosiosystem {
32 | struct [[eosio::table, eosio::contract("eosio.system")]] producer_info {
33 | name owner;
34 | double total_votes = 0;
35 | eosio::public_key producer_key; /// a packed public key object
36 | bool is_active = true;
37 | std::string url;
38 | uint32_t unpaid_blocks = 0;
39 | time_point last_claim_time;
40 | uint16_t location = 0;
41 |
42 | uint64_t primary_key()const { return owner.value; }
43 | double by_votes()const { return is_active ? -total_votes : total_votes; }
44 | bool active()const { return is_active; }
45 | void deactivate() { producer_key = public_key(); is_active = false; }
46 |
47 | // explicit serialization macro is not necessary, used here only to improve compilation time
48 | EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(producer_key)(is_active)(url)
49 | (unpaid_blocks)(last_claim_time)(location) )
50 | };
51 |
52 | typedef eosio::multi_index< "producers"_n, producer_info,
53 | indexed_by<"prototalvote"_n, const_mem_fun >
54 | > producers_table;
55 | }
56 |
--------------------------------------------------------------------------------
/bank.cdp/bank.cdp.contracts.md:
--------------------------------------------------------------------------------
1 |
bail
2 | Reclaims collateral from an overcollateralized cdp, without taking the cdp below its liquidation ratio.
3 |
close
4 | Close balance of given symbol for account.
5 |
deposit
6 | Description of the deposit function
7 |
draw
8 | Issue fresh stablecoin from this cdp.
9 |
liquify
10 | If the collateral value in a CDP drops below 150% of the outstanding stablecoin, the contract automatically sells enough of your collateral to buy back as many stablecoin as you issued. The issued stablecoin is thus taken out of circulation. Similar to a margin call.
11 |
lock
12 | Unless CDP is in liquidation, its owner can use lock to lock more collateral.
13 |
open
14 | Open cdp or initiate balances for account.
15 |
propose
16 | Publish a proposal that either motions for the enactment of a new cdp type, or modification of an existing one.
17 |
referended
18 | Called either automatically after VOTE_PERIOD from the moment of proposal via "propose", via deferred action with proposer's auth, or (if deferred action fails) may be called directly by proposer after VOTE_PERIOD has passed. Proposal may be enacted if there are more votes in favor than against. Cleanup must be done regardless on relevant multi_index tables.
19 |
settle
20 | The system uses the market price of collateral at the time of settlement to compute how much collateral each user gets.
21 |
shut
22 | Owner can close this CDP, if the price feed is up to date and the CDP is not in liquidation. Reclaims all collateral and cancels all issuance plus fee.
23 |
transfer
24 | Transfer stablecoin
25 |
upfeed
26 | Price feed data update action.
27 |
vote
28 | Take a position (for/against) on a cdp proposal
29 |
wipe
30 | Owner can send back stablecoin and reduce the cdp's issued stablecoin balance.
31 |
withdraw
32 | Description of the withdraw function
33 |
--------------------------------------------------------------------------------
/tests/pay2key.sign.js:
--------------------------------------------------------------------------------
1 | const ecc = require('eosjs-ecc');
2 | const base58 = require('bs58');
3 |
4 | // due to JS limitaitons, this only has 48-bit precision,
5 | // but that's good enough for what we need
6 | function uint64_to_little_endian(num) {
7 | const buf = Buffer.alloc(8);
8 | buf.writeUIntLE(num, 0, 6);
9 | return buf;
10 | }
11 |
12 | function uint32_to_little_endian(num) {
13 | const buf = Buffer.alloc(4);
14 | buf.writeUIntLE(num, 0, 4);
15 | return buf;
16 | }
17 |
18 | function sign(chain_id, from, to, amount, fee, nonce, memo, privkey) {
19 | const version = 2;
20 | const length = 96 + memo.length;
21 |
22 | const chainidBuf = uint32_to_little_endian(chain_id);
23 | const pkeyFrom = base58.decode(from.substring(3));
24 | const pkeyTo = base58.decode(to.substring(3));
25 | const amountBuf = uint64_to_little_endian(amount);
26 | const feeBuf = uint64_to_little_endian(fee);
27 | const nonceBuf = uint64_to_little_endian(nonce);
28 | const memoBuf = Buffer.from(memo);
29 |
30 | // create raw tx
31 | const buffer = Buffer.alloc(length);
32 | buffer[0] = version;
33 | buffer[1] = length;
34 | chainidBuf.copy(buffer, 2, 0, 4);
35 | pkeyFrom.copy(buffer, 6, 0, 33);
36 | pkeyTo.copy(buffer, 39, 0, 33);
37 | amountBuf.copy(buffer, 72, 0, 8);
38 | feeBuf.copy(buffer, 80, 0, 8);
39 | nonceBuf.copy(buffer, 88, 0, 8);
40 | memoBuf.copy(buffer, 96, 0, memoBuf.length);
41 | //console.log(buffer.toString('hex'));
42 |
43 | // hash raw tx
44 | const hashed = ecc.sha256(buffer, 'hex');
45 | //console.log(hashed);
46 |
47 | // sign transaction
48 | const sig = ecc.signHash(hashed, privkey);
49 |
50 | return sig;
51 | }
52 |
53 | ///////////////////////////////
54 | // Main Execution Thread
55 | if (process.argv.length != 10) {
56 | console.log(`
57 | USAGE: node pay2key.sign.js [chain_id] [from] [to] [amount] [fee] [nonce] [memo] [privkey]
58 | Example: node pay2key.sign.js 0 EOS7PoGq46ssqeGh8ZNScWQxqbwg5RNvLAwVw3i5dQcZ3a1h9nRyr EOS6KnJPV1mDuS8pYuLucaWzkwbWjGPeJsfQDpqc7NZ4F7zTQh4Wt 10000 10 1 "token transfer" 5KQRA6BBHEHSbmvio3S9oFfVERvv79XXppmYExMouSBqPkZTD79
59 | `);
60 | }
61 | else {
62 | const args = process.argv.slice(2);
63 | console.log(sign(...args));
64 | }
65 |
--------------------------------------------------------------------------------
/bank.price/bank.price.cpp:
--------------------------------------------------------------------------------
1 | #include "bank.price.hpp"
2 |
3 | // Create a new price to track
4 | [[eosio::action]]
5 | void bankprice::create(asset initprice) {
6 | // Auth check
7 | require_auth( _self );
8 |
9 | // Validate the initprice asset
10 | auto sym = initprice.symbol;
11 | eosio_assert( sym.is_valid(), "invalid symbol name" );
12 | eosio_assert( initprice.is_valid(), "invalid initprice");
13 | eosio_assert( initprice.amount > 0, "initprice must be positive");
14 |
15 | // Make sure the symbol is not already being tracked
16 | prices_table pricetable( _self, _self.value );
17 | auto existing = pricetable.find( sym.code().raw() );
18 | eosio_assert( existing == pricetable.end(), "price with symbol already exists" );
19 |
20 | // Create an entry for the symbol
21 | pricetable.emplace( _self, [&]( auto& p ) {
22 | p.price = initprice;
23 | });
24 | }
25 |
26 | // Update the price for a symbol
27 | [[eosio::action]]
28 | void bankprice::update(name updater, asset price) {
29 | // Auth check
30 | require_auth(updater);
31 |
32 | // Validate inputs
33 | eosio_assert( price.is_valid(), "invalid price" );
34 | eosio_assert( price.amount > 0, "must update with positive price" );
35 |
36 | // Get producer vote info
37 | eosiosystem::producers_table producers(name("eosio"), name("eosio").value);
38 | auto votes_idx = producers.get_index();
39 | auto vote_it = votes_idx.end();
40 |
41 | // Make sure producer is in top 21
42 | uint8_t loop = 0;
43 | bool isTop21 = false;
44 | while(vote_it != votes_idx.begin() && loop < 21) {
45 | if (vote_it->owner == updater) {
46 | isTop21 = true;
47 | break;
48 | }
49 | }
50 | eosio_assert(isTop21, "Producer must be in top 21");
51 |
52 | // Get price info
53 | prices_table pricetable( _self, _self.value );
54 | auto current_price = pricetable.get(price.symbol.code().raw(), "Symbol does not exist. Create it first");
55 | eosio_assert( current_price.price.symbol == price.symbol, "Symbol precision mismatch" );
56 |
57 | // Calculate updated price info
58 | uint64_t new_price = current_price.price.amount * 9 / 10 + price.amount / 10;
59 |
60 | // Update table
61 | pricetable.modify( current_price, _self, [&]( auto& p ) {
62 | p.price.amount = new_price;
63 | });
64 |
65 | }
66 | EOSIO_DISPATCH( bankprice, (create)(update))
67 |
--------------------------------------------------------------------------------
/frax.reserve/frax.reserve.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | using namespace eosio;
7 | using namespace std;
8 |
9 | const symbol FRAX_SYMBOL = symbol(symbol_code("FRAX"), 4);
10 | const symbol FXS_SYMBOL = symbol(symbol_code("FXS"), 4);
11 | const symbol USDT_SYMBOL = symbol(symbol_code("USDT"), 4);
12 |
13 | class [[eosio::contract("frax.reserve")]] fraxreserve : public contract {
14 |
15 | public:
16 | using contract::contract;
17 |
18 | [[eosio::action]]
19 | void addtoken(name contract, symbol ticker);
20 |
21 | [[eosio::action]]
22 | void buyfrax(name buyer, asset frax);
23 |
24 | [[eosio::action]]
25 | void settarget(asset reserve_usdt, asset reserve_fxs, uint64_t fxs_price);
26 |
27 | // Public but not a directly callable action
28 | // Called indirectly by sending EOS to this contract
29 | void deposit( name from, name to, asset quantity, string memo );
30 |
31 | // Deposits table
32 | // Scoped by user
33 | struct [[eosio::table]] account {
34 | asset balance;
35 |
36 | uint64_t primary_key() const { return balance.symbol.raw(); }
37 | };
38 | typedef eosio::multi_index<"accounts"_n, account> accounts;
39 |
40 | // Token stats
41 | // Contract scope
42 | struct [[eosio::table]] stats_t {
43 | asset supply;
44 | name contract;
45 |
46 | uint64_t primary_key() const { return supply.symbol.raw(); }
47 | uint64_t by_contract() const { return contract.value; }
48 | uint128_t by_contract_symbol() const { return merge_contract_symbol(contract, supply.symbol); }
49 | };
50 | typedef eosio::multi_index<"stats"_n, stats_t,
51 | indexed_by<"bycontract"_n, const_mem_fun>,
52 | indexed_by<"byctrsym"_n, const_mem_fun>
53 | > stats;
54 |
55 | // System parameters
56 | // Singleton - Contract scope
57 | struct [[eosio::table]] params_t {
58 | asset target_usdt;
59 | asset target_fxs;
60 | uint64_t fxs_price; // (FXS / USDT) * 1e4. Ex: $20 FXS. Set to 20e4
61 |
62 | uint64_t primary_key() const { return 1; } // constant primary key forces singleton
63 | };
64 | typedef eosio::multi_index<"sysparams"_n, params_t > sysparams;
65 |
66 | private:
67 |
68 | static uint128_t merge_contract_symbol( name contract, symbol sym ) {
69 | uint128_t merged;
70 | uint64_t raw_sym = sym.raw();
71 | memcpy((uint8_t *)&merged, (uint8_t *)&contract.value, 8);
72 | memcpy((uint8_t *)&merged + 8, (uint8_t *)&raw_sym, 8);
73 | return merged;
74 | }
75 |
76 | };
77 |
--------------------------------------------------------------------------------
/tests/test_contracts/eosio.token/eosio.token.abi:
--------------------------------------------------------------------------------
1 | {
2 | "version": "eosio::abi/1.0",
3 | "types": [{
4 | "new_type_name": "account_name",
5 | "type": "name"
6 | }],
7 | "structs": [{
8 | "name": "transfer",
9 | "base": "",
10 | "fields": [
11 | {"name":"from", "type":"account_name"},
12 | {"name":"to", "type":"account_name"},
13 | {"name":"quantity", "type":"asset"},
14 | {"name":"memo", "type":"string"}
15 | ]
16 | },{
17 | "name": "create",
18 | "base": "",
19 | "fields": [
20 | {"name":"issuer", "type":"account_name"},
21 | {"name":"maximum_supply", "type":"asset"}
22 | ]
23 | },{
24 | "name": "issue",
25 | "base": "",
26 | "fields": [
27 | {"name":"to", "type":"account_name"},
28 | {"name":"quantity", "type":"asset"},
29 | {"name":"memo", "type":"string"}
30 | ]
31 | },{
32 | "name": "retire",
33 | "base": "",
34 | "fields": [
35 | {"name":"quantity", "type":"asset"},
36 | {"name":"memo", "type":"string"}
37 | ]
38 | },{
39 | "name": "close",
40 | "base": "",
41 | "fields": [
42 | {"name":"owner", "type":"account_name"},
43 | {"name":"symbol", "type":"symbol"}
44 | ]
45 | },{
46 | "name": "account",
47 | "base": "",
48 | "fields": [
49 | {"name":"balance", "type":"asset"}
50 | ]
51 | },{
52 | "name": "currency_stats",
53 | "base": "",
54 | "fields": [
55 | {"name":"supply", "type":"asset"},
56 | {"name":"max_supply", "type":"asset"},
57 | {"name":"issuer", "type":"account_name"}
58 | ]
59 | }
60 | ],
61 | "actions": [{
62 | "name": "transfer",
63 | "type": "transfer",
64 | "ricardian_contract": ""
65 | },{
66 | "name": "issue",
67 | "type": "issue",
68 | "ricardian_contract": ""
69 | },{
70 | "name": "retire",
71 | "type": "retire",
72 | "ricardian_contract": ""
73 | }, {
74 | "name": "create",
75 | "type": "create",
76 | "ricardian_contract": ""
77 | }, {
78 | "name": "close",
79 | "type": "close",
80 | "ricardian_contract": ""
81 | }
82 |
83 | ],
84 | "tables": [{
85 | "name": "accounts",
86 | "type": "account",
87 | "index_type": "i64",
88 | "key_names" : ["currency"],
89 | "key_types" : ["uint64"]
90 | },{
91 | "name": "stat",
92 | "type": "currency_stats",
93 | "index_type": "i64",
94 | "key_names" : ["currency"],
95 | "key_types" : ["uint64"]
96 | }
97 | ],
98 | "ricardian_clauses": [],
99 | "abi_extensions": []
100 | }
101 |
--------------------------------------------------------------------------------
/tests/fraxloans_api.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | using namespace eosio::testing;
4 | using namespace eosio;
5 | using namespace eosio::chain;
6 |
7 | using mvo = fc::mutable_variant_object;
8 | using action_result = base_tester::action_result;
9 |
10 | class fraxloans_api {
11 | public:
12 | account_name contract;
13 |
14 | fraxloans_api(account_name acnt, tester* tester) {
15 | contract = acnt;
16 | _tester = tester;
17 |
18 | _tester->create_accounts({contract});
19 | _tester->set_code(contract, contracts::fraxloans_wasm());
20 | _tester->set_abi(contract, contracts::fraxloans_abi().data());
21 |
22 | const auto &accnt = _tester->control->db().get(contract);
23 |
24 | abi_def abi;
25 | BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true);
26 | abi_ser.set_abi(abi, abi_serializer_max_time);
27 | }
28 |
29 | action_result addtoken(const name& token_contract, string symb)
30 | {
31 | auto ticker = eosio::chain::symbol::from_string(symb);
32 | return push_action(
33 | contract, contract, N(addtoken),
34 | mvo()("contract", token_contract)("ticker", ticker));
35 | }
36 |
37 | action_result borrow(const name& borrower, asset quantity)
38 | {
39 | return push_action(
40 | borrower, contract, N(borrow),
41 | mvo()("borrower", borrower)("quantity", quantity));
42 | }
43 |
44 | action_result setprice(asset price)
45 | {
46 | return push_action(
47 | contract, contract, N(setprice),
48 | mvo()("price", price));
49 | }
50 |
51 | fc::variant get_account(const account_name& acc, const string& symbolname)
52 | {
53 | auto symb = eosio::chain::symbol::from_string(symbolname);
54 | auto symbol_code = symb.to_symbol_code().value;
55 | vector data = _tester->get_row_by_account( contract, acc, N(accounts), symbol_code );
56 | return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "account", data, abi_serializer_max_time );
57 | }
58 |
59 | fc::variant get_tokenstats(const string& symbolname)
60 | {
61 | auto symb = eosio::chain::symbol::from_string(symbolname);
62 | auto symbol_code = symb.to_symbol_code().value;
63 | vector data = _tester->get_row_by_account( contract, contract, N(tokenstats), symbol_code );
64 |
65 | return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "stats_t", data, abi_serializer_max_time );
66 | }
67 |
68 | action_result push_action(const account_name &signer,
69 | const account_name &cnt,
70 | const action_name &name,
71 | const variant_object &data) {
72 | string action_type_name = abi_ser.get_action_type(name);
73 | action act;
74 | act.account = cnt;
75 | act.name = name;
76 | act.data = abi_ser.variant_to_binary(action_type_name, data, abi_serializer_max_time);
77 |
78 | return _tester->push_action(std::move(act), uint64_t(signer));
79 | }
80 |
81 | private:
82 | abi_serializer abi_ser;
83 | tester* _tester;
84 | fc::microseconds abi_serializer_max_time{1000*1000};
85 | };
86 |
--------------------------------------------------------------------------------
/bank.pay2key/frontend/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `npm run build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/frax.loans/frax.loans.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | using namespace eosio;
7 | using namespace std;
8 |
9 | class [[eosio::contract("frax.loans")]] fraxloans : public contract {
10 |
11 | public:
12 | using contract::contract;
13 |
14 | // Constants
15 | const symbol FRAX_SYMBOL = symbol(symbol_code("FRAX"), 4);
16 | const symbol FXS_SYMBOL = symbol(symbol_code("FXS"), 4);
17 | const symbol USDT_SYMBOL = symbol(symbol_code("USDT"), 4);
18 | const double COLLATERAL_RATIO = 0.75; // Can only borrow upto 75% of collateral
19 |
20 | [[eosio::action]]
21 | void addtoken(name contract, symbol ticker);
22 |
23 | [[eosio::action]]
24 | void borrow(name borrower, asset quantity);
25 |
26 | [[eosio::action]]
27 | void repay(name borrower, asset quantity);
28 |
29 | [[eosio::action]]
30 | void setprice(asset price);
31 |
32 | //[[eosio::action]]
33 | //void liquidate(name user, name executor);
34 |
35 | [[eosio::on_notify("tmp::tmp")]] void tmp() { } // temp hack. required to make on_notify work until bug is patched
36 |
37 | [[eosio::on_notify("*::transfer")]]
38 | void deposit( name from, name to, asset quantity, string memo );
39 |
40 | // Deposits table
41 | // Scoped by user
42 | struct [[eosio::table]] account {
43 | asset balance;
44 | asset borrowing;
45 | uint64_t last_updated;
46 |
47 | uint64_t primary_key() const { return balance.symbol.code().raw(); }
48 | };
49 | typedef eosio::multi_index<"accounts"_n, account> accounts;
50 |
51 | // Token stats
52 | // Contract scope
53 | struct [[eosio::table]] stats_t {
54 | asset available;
55 | asset loaned;
56 | name contract;
57 | asset price; // 4 decimal places, prices in USDT
58 | bool allowed_as_collateral;
59 | bool can_deposit;
60 | uint64_t interest_counter;
61 |
62 | uint64_t primary_key() const { return available.symbol.code().raw(); }
63 | uint64_t by_contract() const { return contract.value; }
64 | uint128_t by_contract_symbol() const { return merge_contract_symbol(contract, available.symbol); }
65 | };
66 | typedef eosio::multi_index<"tokenstats"_n, stats_t,
67 | indexed_by<"bycontract"_n, const_mem_fun>,
68 | indexed_by<"byctrsym"_n, const_mem_fun>
69 | > stats;
70 |
71 | // System parameters
72 | // Singleton - Contract scope
73 | struct [[eosio::table]] params_t {
74 |
75 | uint64_t primary_key() const { return 1; } // constant primary key forces singleton
76 | };
77 | typedef eosio::multi_index<"sysparams"_n, params_t > sysparams;
78 |
79 | private:
80 |
81 | static uint128_t merge_contract_symbol( name contract, symbol sym ) {
82 | uint128_t merged;
83 | uint64_t raw_sym = sym.raw();
84 | memcpy((uint8_t *)&merged, (uint8_t *)&contract.value, 8);
85 | memcpy((uint8_t *)&merged + 8, (uint8_t *)&raw_sym, 8);
86 | return merged;
87 | }
88 |
89 | };
90 |
--------------------------------------------------------------------------------
/bank.pay2key/bank.pay2key.hpp:
--------------------------------------------------------------------------------
1 | ///////////////////////////////////////
2 | // COMPILED WTIH EOSIO.CDT 1.6.1
3 | ///////////////////////////////////////
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | using namespace eosio;
11 | using namespace std;
12 |
13 | const std::string WITHDRAW_ADDRESS = "EOS1111111111111111111111111111111114T1Anm";
14 | uint8_t WITHDRAW_KEY_BYTES[37] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 231, 181, 34 };
15 |
16 | class [[eosio::contract("bank.pay2key")]] pay2key : public contract {
17 |
18 | public:
19 | using contract::contract;
20 |
21 | [[eosio::action]]
22 | void create(name token_contract, symbol ticker);
23 |
24 | [[eosio::action]]
25 | void transfer(
26 | uint64_t chain_id,
27 | name relayer_account,
28 | public_key relayer,
29 | public_key from,
30 | public_key to,
31 | asset amount,
32 | asset fee,
33 | uint64_t nonce,
34 | string memo,
35 | signature sig);
36 |
37 | // Public but not a directly callable action
38 | // Called indirectly by sending EOS to this contract
39 | void issue( name from, name to, asset quantity, string memo );
40 |
41 | struct [[eosio::table]] account {
42 | uint64_t key;
43 | public_key publickey;
44 | asset balance;
45 | uint64_t last_nonce;
46 |
47 | uint64_t primary_key() const { return key; }
48 | fixed_bytes<32> bypk() const {
49 | return public_key_to_fixed_bytes(publickey);
50 | };
51 | };
52 |
53 | struct [[eosio::table]] currstats {
54 | uint64_t chain_id;
55 | asset supply;
56 | name token_contract;
57 | eosio::symbol symbol;
58 |
59 | uint64_t primary_key() const { return chain_id; }
60 | uint64_t by_token_contract() const { return token_contract.value; }
61 | uint128_t by_contract_symbol() const { return merge_contract_symbol(token_contract, symbol); }
62 | };
63 |
64 | typedef eosio::multi_index<"accounts"_n,
65 | account,
66 | indexed_by<"bypk"_n, const_mem_fun, &account::bypk>>
67 | > accounts;
68 |
69 | typedef eosio::multi_index<"stats"_n, currstats,
70 | indexed_by<"bycontract"_n, const_mem_fun>,
71 | indexed_by<"byctrsym"_n, const_mem_fun>
72 | > stats;
73 |
74 | private:
75 |
76 | static fixed_bytes<32> public_key_to_fixed_bytes(const public_key publickey) {
77 | return sha256(publickey.data.begin(), 33);
78 | }
79 |
80 | void sub_balance(uint64_t chain_id, public_key sender, asset value);
81 |
82 | void add_balance(uint64_t chain_id, public_key recipient, asset value, name ram_payer);
83 |
84 | static uint128_t merge_contract_symbol( name contract, symbol sym ) {
85 | uint128_t merged;
86 | uint64_t raw_sym = sym.raw();
87 | memcpy((uint8_t *)&merged, (uint8_t *)&contract.value, 8);
88 | memcpy((uint8_t *)&merged + 8, (uint8_t *)&raw_sym, 8);
89 | return merged;
90 | }
91 |
92 | };
93 |
--------------------------------------------------------------------------------
/bank.shares/bank.shares.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | using namespace eosio;
6 | using namespace std;
7 |
8 | const name SYS_TOKEN_NAME = "BANK"_n;
9 |
10 | class [[eosio::contract("bank.shares")]] bankshares : public contract {
11 | public:
12 | using contract::contract;
13 |
14 | // Return type for the updater function
15 | struct AuctionPack {
16 | auto _it;
17 | auctions _tbl;
18 | }
19 |
20 | // -------------STRUCTS-------------
21 | // List of auctions
22 | struct [[eosio::table]] auction {
23 | uint64_t id;
24 | asset issue; // Amount of SYS_TOKEN_NAME being issued
25 | asset iss_remain; // SYS_TOKEN_NAME left to be sold
26 | asset current_ask; // Current asking price for 1 SYS_TOKEN_NAME, in CURR
27 | asset ask_floor; // Minimum asking price for 1 SYS_TOKEN_NAME that you are willing to accept, in CURR
28 | time_point start_time; // Starting time of the auction
29 | time_point end_time; // Ending time of the auction
30 |
31 | uint64_t primary_key() const { return id; }
32 | symbol get_symbol () const { return ask_floor.symbol; }
33 | };
34 |
35 | // -------------ACTIONS-------------
36 | [[eosio::action]]
37 | startauction(
38 | asset quantity, // Amount of SYS_TOKEN_NAME being issued
39 | asset askstart, // Starting asking price for 1 SYS_TOKEN_NAME, in CURR
40 | asset askfloor, // Minimum asking price for 1 SYS_TOKEN_NAME that you are willing to accept, in CURR
41 | string memo, // A memo
42 | time_point end_time // The ending time, in epoch milliseconds
43 | );
44 |
45 | [[eosio::action]]
46 | updateask(
47 | uint64_t auctionid // The id of the auction
48 | );
49 |
50 | [[eosio::action]]
51 | buyshares(
52 | uint64_t auctionid, // ID of the auction you want to participate in
53 | name buyer, // The name of you, the buyer
54 | asset buyamount, // The amount of SYS_TOKEN_NAME that you want to buy
55 | asset maxbid // The maximum price per 1 SYS_TOKEN_NAME that you are willing to pay, in CURR
56 | );
57 |
58 | // --------TABLE DEFINITIONS--------
59 | typedef eosio::multi_index<"auctions"_n, auction,
60 | indexed_by< "bypk", const_mem_fun>,
61 | indexed_by< "bysymbol", const_mem_fun>
62 | > auctions;
63 |
64 | private:
65 | // Update the bid internally
66 | AuctionPack update_ask_local(uint64_t auctionid);
67 | };
68 |
69 | // Table info from bank.token contract. Needed for the auction and various other things.
70 | namespace banktoken {
71 |
72 | struct [[eosio::table, eosio::contract("bank.token")]] account {
73 | asset balance;
74 |
75 | uint64_t primary_key()const { return balance.symbol.code().raw(); }
76 | };
77 |
78 | struct [[eosio::table, eosio::contract("bank.token")]] currency_stats {
79 | asset supply;
80 | asset max_supply;
81 | name issuer;
82 |
83 | uint64_t primary_key()const { return supply.symbol.code().raw(); }
84 | };
85 | typedef eosio::multi_index<"accounts"_n, account> accounts;
86 | typedef eosio::multi_index<"stats"_n, currency_stats> stats;
87 | }
--------------------------------------------------------------------------------
/bank.cdp/README.md:
--------------------------------------------------------------------------------
1 |
2 | ###### Future note: some of the information herein should be apt for re-organization into the relevant Ricardian clauses of the bank.cdp contract ABI
3 |
4 | # Design Decisions
5 |
6 | * If you'd like your CDP to dip below the agreed upon liqidation ratio of your CDP's type, via one of the relevant actions (draw or bail), then TOO BAD because you won't be permitted (you'll hit an assertion).
7 | * If you'd like to liquidate your own CDP in the event of significant price slippage, then TOO BAD because that would let you print free money and we won't have that kind of behavior here (unless you collude with another account).
8 |
9 | # Differences between cdpEOS (aka bank.cdp) and cdpETH (aka MakerDAO)
10 |
11 | bank.cdp successfully implements multi-collateral CDPs by accepting any eosio.token, and the entire protocol is implemented via only 15 distinct functions, and just over 800 SLOC.
12 |
13 | The major discrepancy between the two protocols is in how
14 | liquidation auctions, stability fees, and governance are handled. Nonetheless, there are many concepts in common, such as the fact that liquidation may (and should) operate effectively in the absence of price feeds.
15 |
16 | To provide for better liquidity, Maker uses simultaneous (parallel) auctions of four types:
17 |
18 | * #1 Stablecoin (stable token) bids for collateral
19 | * #2 Reverse dutch of the above (collateral bids for stablecoin) in the event of some collateral remaining to be refunded to a CDP owner.
20 | * #3 Stablecoin bids for MKR (voting token) in the event of balance shortage in #1 (not enough stablecoin was raised)
21 | * #4 MKR bids for stablecoin in the event of balance surplus in #1 (too much stablecoin was raised)
22 |
23 | Auction #2 is omitted entirely from our protocol: we do not endorse it because CDP owners should be playing it safe and overcollateralizing even more. The rest of the phases are executed in sequence rather than in parallel.
24 |
25 | Unlike in Ethereum where the primary constraint is speed and gas, with faster block times the main constraint in EOS is RAM so our approach was taken from the angle of a more space efficient implementation, and a simpler view of the computation overall.
26 |
27 | On that last note, we also omit the calculation of stability fees on a global basis as in multi-collateral stablecoin (whenever debt is being taken on or repaid, the fee is minted into the contract's balance and incremented onto a CDP's debt), in favor of an APR-based deduction in VTO upon every wipe action executed on a CDP (closer to single-collateral stablecoin). Moreover, our voting and referendum procedure has a very common sense nature:
28 |
29 | Any account may create a proposal, and as many varying proposals as desired, but we do constrain proposals per CDP type. Only one proposal may be active (in voting) per CDP type (new or modification of existing).
30 |
31 | Voters may vote with their tokens as much as they please (a la Maker) and even post two postions simultaneously (both for and against).
32 |
33 | All voting tokens are refunded to voters upon the expiration of the voting period for a proposal, and if there is a tie between total for and against positions, another voting period is scheduled to take place immediately.
34 |
35 | Global settlement is subjected to popular vote like any CDP change or creation proposal, rather than designated to a select group of Keepers as in MakerDAO.
36 |
37 | Global constants are the designated accounts that provide price feeds, the voting period of 2 weeks for proposals, and the minimum age of 5 minutes (how recent) for price data that is considered acceptable.
38 |
39 | ###### TODO
40 | medium.com/makerdao/dai-reward-rate-earn-a-reward-from-holding-dai-10a07f52f3cf
--------------------------------------------------------------------------------
/frax.reserve/frax.reserve.abi:
--------------------------------------------------------------------------------
1 | {
2 | "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ",
3 | "version": "eosio::abi/1.1",
4 | "types": [],
5 | "structs": [
6 | {
7 | "name": "account",
8 | "base": "",
9 | "fields": [
10 | {
11 | "name": "balance",
12 | "type": "asset"
13 | }
14 | ]
15 | },
16 | {
17 | "name": "addtoken",
18 | "base": "",
19 | "fields": [
20 | {
21 | "name": "contract",
22 | "type": "name"
23 | },
24 | {
25 | "name": "ticker",
26 | "type": "symbol"
27 | }
28 | ]
29 | },
30 | {
31 | "name": "buyfrax",
32 | "base": "",
33 | "fields": [
34 | {
35 | "name": "buyer",
36 | "type": "name"
37 | },
38 | {
39 | "name": "frax",
40 | "type": "asset"
41 | }
42 | ]
43 | },
44 | {
45 | "name": "params_t",
46 | "base": "",
47 | "fields": [
48 | {
49 | "name": "target_usdt",
50 | "type": "asset"
51 | },
52 | {
53 | "name": "target_fxs",
54 | "type": "asset"
55 | },
56 | {
57 | "name": "fxs_price",
58 | "type": "uint64"
59 | }
60 | ]
61 | },
62 | {
63 | "name": "settarget",
64 | "base": "",
65 | "fields": [
66 | {
67 | "name": "reserve_usdt",
68 | "type": "asset"
69 | },
70 | {
71 | "name": "reserve_fxs",
72 | "type": "asset"
73 | },
74 | {
75 | "name": "fxs_price",
76 | "type": "uint64"
77 | }
78 | ]
79 | },
80 | {
81 | "name": "stats_t",
82 | "base": "",
83 | "fields": [
84 | {
85 | "name": "supply",
86 | "type": "asset"
87 | },
88 | {
89 | "name": "contract",
90 | "type": "name"
91 | }
92 | ]
93 | }
94 | ],
95 | "actions": [
96 | {
97 | "name": "addtoken",
98 | "type": "addtoken",
99 | "ricardian_contract": ""
100 | },
101 | {
102 | "name": "buyfrax",
103 | "type": "buyfrax",
104 | "ricardian_contract": ""
105 | },
106 | {
107 | "name": "settarget",
108 | "type": "settarget",
109 | "ricardian_contract": ""
110 | }
111 | ],
112 | "tables": [
113 | {
114 | "name": "accounts",
115 | "type": "account",
116 | "index_type": "i64",
117 | "key_names": [],
118 | "key_types": []
119 | },
120 | {
121 | "name": "stats",
122 | "type": "stats_t",
123 | "index_type": "i64",
124 | "key_names": [],
125 | "key_types": []
126 | },
127 | {
128 | "name": "sysparams",
129 | "type": "params_t",
130 | "index_type": "i64",
131 | "key_names": [],
132 | "key_types": []
133 | }
134 | ],
135 | "ricardian_clauses": [],
136 | "variants": []
137 | }
--------------------------------------------------------------------------------
/bank.pay2key/bank.pay2key.abi:
--------------------------------------------------------------------------------
1 | {
2 | "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ",
3 | "version": "eosio::abi/1.1",
4 | "types": [],
5 | "structs": [
6 | {
7 | "name": "account",
8 | "base": "",
9 | "fields": [
10 | {
11 | "name": "key",
12 | "type": "uint64"
13 | },
14 | {
15 | "name": "publickey",
16 | "type": "public_key"
17 | },
18 | {
19 | "name": "balance",
20 | "type": "asset"
21 | },
22 | {
23 | "name": "last_nonce",
24 | "type": "uint64"
25 | }
26 | ]
27 | },
28 | {
29 | "name": "create",
30 | "base": "",
31 | "fields": [
32 | {
33 | "name": "token_contract",
34 | "type": "name"
35 | },
36 | {
37 | "name": "ticker",
38 | "type": "symbol"
39 | }
40 | ]
41 | },
42 | {
43 | "name": "currstats",
44 | "base": "",
45 | "fields": [
46 | {
47 | "name": "chain_id",
48 | "type": "uint64"
49 | },
50 | {
51 | "name": "supply",
52 | "type": "asset"
53 | },
54 | {
55 | "name": "token_contract",
56 | "type": "name"
57 | },
58 | {
59 | "name": "symbol",
60 | "type": "symbol"
61 | }
62 | ]
63 | },
64 | {
65 | "name": "transfer",
66 | "base": "",
67 | "fields": [
68 | {
69 | "name": "chain_id",
70 | "type": "uint64"
71 | },
72 | {
73 | "name": "relayer_account",
74 | "type": "name"
75 | },
76 | {
77 | "name": "relayer",
78 | "type": "public_key"
79 | },
80 | {
81 | "name": "from",
82 | "type": "public_key"
83 | },
84 | {
85 | "name": "to",
86 | "type": "public_key"
87 | },
88 | {
89 | "name": "amount",
90 | "type": "asset"
91 | },
92 | {
93 | "name": "fee",
94 | "type": "asset"
95 | },
96 | {
97 | "name": "nonce",
98 | "type": "uint64"
99 | },
100 | {
101 | "name": "memo",
102 | "type": "string"
103 | },
104 | {
105 | "name": "sig",
106 | "type": "signature"
107 | }
108 | ]
109 | }
110 | ],
111 | "actions": [
112 | {
113 | "name": "create",
114 | "type": "create",
115 | "ricardian_contract": ""
116 | },
117 | {
118 | "name": "transfer",
119 | "type": "transfer",
120 | "ricardian_contract": ""
121 | }
122 | ],
123 | "tables": [
124 | {
125 | "name": "accounts",
126 | "type": "account",
127 | "index_type": "i64",
128 | "key_names": [],
129 | "key_types": []
130 | },
131 | {
132 | "name": "stats",
133 | "type": "currstats",
134 | "index_type": "i64",
135 | "key_names": [],
136 | "key_types": []
137 | }
138 | ],
139 | "ricardian_clauses": [],
140 | "variants": []
141 | }
--------------------------------------------------------------------------------
/bank.pay2key/README.md:
--------------------------------------------------------------------------------
1 | # eosio.pay2key
2 |
3 | The UTXO token contract (also called Pay 2 Key contract) is a standardized EOSIO smart contract, which allows token transacting using only public-private key pairs without the need for an EOSIO account or system action. It is similar in feature to the Bitcoin transaction system. To move tokens in a UTXO based contract, an owner only needs the corresponding private key to the public key. A transaction fee is also paid to move the token. A "relayer" with an EOS account (can be a block producer or any individual account) accepts the fee then relays the signed UTXO message to the contract and updates the balance of the appropriate public key. A relayer is similar to a Bitcoin miner in that they update the state of the UTXO contract by appending the signed UTXO transaction to the contract database.
4 |
5 | There are 4 main actions in the contract: 1. Creating the UTXO token specs when the contract is first deployed 2. Sending tokens from EOS accounts to a public key in the Pay2Key contract 3. Transacting UTXO/Pay2Key tokens between public keys 4. Withdrawing your UTXOs from a public key back into an EOS account
6 |
7 | ## 1. Creating a token with a maximum supply
8 |
9 | Before you are able to issue a token to a public key, you must create the spendable output and give the token maximum supply to set up the contract. In this documentation, the ticker of the example token in this documentation is IQ with the Pay2Key ticker as IQUTXO.
10 |
11 | ```
12 | cleos push action {UTXO_contract_account} create \'["IQUTXO", "100.0000 IQUTXO"]\' -p {UTXO_contract_account}
13 | ```
14 |
15 | ## 2. Transferring tokens to issue the UTXO
16 |
17 | Anyone can send the designated token to the UTXO contract, triggering an issuance to the public key of their choosing.
18 | ```
19 | cleos push action everipediaiq transfer '["{EOS_account}", "{UTXO_contract_account}", "100.000 IQUTXO", "{PUBLIC_KEY}"]' -p {EOS_account}@active
20 | ```
21 | Now that public key will have 100 IQUTXO to spend
22 |
23 | ## 3. Transferring from one public key to another
24 |
25 | To transfer from one public key to another, the transaction must be signed using your private key. For details of how to generate the signature, check out the code in frontend section. The frontend client makes generating this signature easy. There are a number of hosted solutions for UTXO front ends coming.
26 |
27 | ```
28 | cleos push action utxo transfer '["RELAYER_PUBLIC_KEY", "PUBLIC_KEY1", "PUBLIC_KEY2", "3.0000 IQUTXO", "1.0000 IQUTXO", {nonce}, "memo here", "{SIGNATURE}"]' -p utxo
29 | ```
30 |
31 | ### Generating Signatures
32 | To create a signed transaction to spend UTXOs, a signature must be created by the private key of a public key with spendable outputs. We've included a front end interface to easily do that in the repository for local usage. There are also hosted solutions of the front end at:
33 |
34 |
35 | The Signature is generated with the following fields:
36 |
37 | Public key of the spender
38 |
39 | Private key of the spender
40 |
41 | Public key of the recipient
42 |
43 | Amount of UTXO to spend (denominated as uint64)
44 |
45 | Amount of UTXO to pay as fee (denominated as uint64)
46 |
47 | A nonce (we highly recommend incrementing the nonce by 1 starting from 0 for each public key)
48 |
49 | A memo/string field
50 |
51 | In the future, we will add a token ID field to add replay protection to generated signatures.
52 |
53 | ## 4. Withdrawing UTXOs out of the contract back into an EOS account
54 |
55 | To withdraw tokens from an account, issue a transfer as normal, except make the receiving public key be the NULL address, EOS1111111111111111111111111111111114T1Anm, and the memo the receiving EOS account name.
56 |
57 | The contract detects this, modifies the circulating supply, and triggers an inline action to send IQ to the EOS account specified in the memo field.
58 |
59 | ```
60 | cleos push action utxo transfer '["{RELAYER_PUBLIC_KEY}", "{PUBLIC_KEY1}", "EOS1111111111111111111111111111111114T1Anm", "3.0000 IQUTXO", "1.0000 IQUTXO", {nonce}, "chrisaccount", "{SIGNATURE}"]' -p utxo
61 | ```
62 |
63 |
64 |
65 | ## Running tests
66 | ```
67 | cd js_test
68 | npm install
69 | EOSIO_CONTRACTS_ROOT=_path_to_eosio_contracts_ node test.js
70 | ```
71 |
72 | ## Running frontend
73 | ```
74 | cd frontend
75 | yarn install
76 | yarn start
77 | ```
78 |
--------------------------------------------------------------------------------
/bank.pay2key/relay-server/relayer.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const bodyParser = require('body-parser');
3 | const Joi = require('@hapi/joi');
4 | const fetch = require('node-fetch');
5 | const dotenv = require('dotenv');
6 | const eosjs = require('eosjs');
7 | const jssig = require('eosjs/dist/eosjs-jssig');
8 | const morgan = require('morgan');
9 |
10 | const Api = eosjs.Api;
11 | const JsonRpc = eosjs.JsonRpc;
12 | const JsSignatureProvider = jssig.JsSignatureProvider;
13 |
14 | require('dotenv').config()
15 |
16 | const app = express();
17 | app.use(morgan('combined'));
18 |
19 | // CORS
20 | app.use(function(req, res, next) {
21 | res.header("Access-Control-Allow-Origin", "*");
22 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
23 | next();
24 | });
25 |
26 | const rpc = new JsonRpc(process.env.EOS_API_BASE, { fetch });
27 | const signatureProvider = new JsSignatureProvider([process.env.RELAYER_PRIVATE_KEY]);
28 | const eosapi = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() });
29 |
30 | const eosSchema = Joi.object().keys({
31 | chain_id: Joi.number().min(0).max(1000).required(),
32 | from: Joi.string().length(53).regex(/EOS.+/).required(),
33 | to: Joi.string().length(53).regex(/EOS.+/).required(),
34 | amount: Joi.string(),
35 | fee: Joi.string(),
36 | nonce: Joi.number().integer().min(0).required(),
37 | memo: Joi.string().min(0).max(163).required(),
38 | sig: Joi.string()
39 | });
40 |
41 | const btcSchema = Joi.object().keys({
42 | chain_id: Joi.number().min(0).max(1000).required(),
43 | from: Joi.string().min(25).max(35).required(),
44 | to: Joi.string().min(25).max(35).required(),
45 | amount: Joi.string(),
46 | fee: Joi.string(),
47 | nonce: Joi.number().integer().min(0).required(),
48 | memo: Joi.string().min(0).max(163).required(),
49 | sig: Joi.string()
50 | });
51 |
52 | app.use(bodyParser.json());
53 |
54 | app.post('/relay/eos', function (req, res) {
55 | const msg = req.body;
56 | const validates = Joi.validate(msg, eosSchema);
57 | if (validates.error) {
58 | const error = validates.error.details[0].message;
59 | res.status(400).send({ error: error });
60 | console.error(error);
61 | return;
62 | }
63 |
64 | msg.relayer = process.env.RELAYER_PUBLIC_KEY;
65 |
66 | const result = eosapi.transact({
67 | actions: [{
68 | account: process.env.EOS_UTXO_ACCOUNT,
69 | name: 'transfer',
70 | authorization: [{
71 | actor: process.env.RELAYER_ACCOUNT,
72 | permission: 'active',
73 | }],
74 | data: msg
75 | }]
76 | }, {
77 | blocksBehind: 3,
78 | expireSeconds: 30,
79 | }).then(response => {
80 | res.status(200).send(response);
81 | }).catch(err => {
82 | console.error(err);
83 | res.status(500).send({ error: err.message });
84 | });
85 |
86 | })
87 |
88 | app.post('/relay/btc', function (req, res) {
89 | const msg = req.body;
90 | const validates = Joi.validate(msg, btcSchema);
91 | if (validates.error) {
92 | const error = validates.error.details[0].message;
93 | res.status(400).send({ error: error });
94 | console.error(error);
95 | return;
96 | }
97 |
98 | msg.relayer = process.env.RELAYER_PUBLIC_KEY;
99 |
100 | const result = eosapi.transact({
101 | actions: [{
102 | account: process.env.BTC_UTXO_ACCOUNT,
103 | name: 'transfer',
104 | authorization: [{
105 | actor: process.env.RELAYER_ACCOUNT,
106 | permission: 'active',
107 | }],
108 | data: msg
109 | }]
110 | }, {
111 | blocksBehind: 3,
112 | expireSeconds: 30,
113 | }).then(response => {
114 | res.status(200).send(response);
115 | }).catch(err => {
116 | console.error(err);
117 | res.status(500).send({ error: err.message });
118 | });
119 |
120 | })
121 |
122 | app.listen(6400, (e) => {
123 | if (e) console.error(e);
124 | else console.log("Relay server listening on port 6400...");
125 | });
126 |
--------------------------------------------------------------------------------
/tests/pay2key.test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | CYAN='\033[1;36m'
4 | NC='\033[0m'
5 |
6 | assert ()
7 | {
8 |
9 | if [ $1 -eq 0 ]; then
10 | FAIL_LINE=$( caller | awk '{print $1}')
11 | echo -e "Assertion failed. Line $FAIL_LINE:"
12 | head -n $FAIL_LINE $BASH_SOURCE | tail -n 1
13 | exit 99
14 | fi
15 | }
16 |
17 | PRIVKEY1="5JW2ZpYaHA96yEpDXW1arVmEezV3XPF6aD2b7BB5EyGUEapdUTL"
18 | PUBKEY1="EOS6u25uWXi52QLp1FurM8KPDJydbvxbrPM2XNrYPz7vPCQuGFCer"
19 | PRIVKEY2="5KRKuMUFswLXmeA9wzWvn6f9a9fNH36oGkVG29X2VGpbMPCJ3eC"
20 | PUBKEY2="EOS6wh7kToXbq6rt92bFVFz2mj9AcJf8NEFK3sFfYk78QTnEGAiBp"
21 | WITHDRAW_PUBKEY="EOS1111111111111111111111111111111114T1Anm"
22 |
23 | echo -e "${CYAN}-----------------------DEPLOY CONTRACT-----------------------${NC}"
24 | cleos set contract bank.pay2key ../bank.pay2key/
25 |
26 | echo -e "${CYAN}-----------------------CREATE TOKENS-----------------------${NC}"
27 | cleos push action bank.pay2key create '["eosio.token", "4,EOS"]' -p bank.pay2key
28 | cleos push action bank.pay2key create '["everipediaiq", "3,IQ"]' -p bank.pay2key
29 |
30 | echo -e "${CYAN}-----------------------DEPOSIT TOKENS-----------------------${NC}"
31 | cleos transfer dcbtestusera bank.pay2key "100 EOS" "$PUBKEY1"
32 | assert $(bc <<< "$? == 0")
33 | cleos push action everipediaiq transfer "[\"dcbtestuserb\", \"bank.pay2key\", \"1000.000 IQ\", \"$PUBKEY2\"]" -p dcbtestuserb
34 | assert $(bc <<< "$? == 0")
35 |
36 | echo -e "${CYAN}-----------------------TRANSFER TOKENS-----------------------${NC}"
37 | NONCE1=$(cleos get table bank.pay2key 0 accounts | jq ".rows[0].last_nonce")
38 | NONCE1=$(echo "$NONCE1 + 1" | bc)
39 | MEMO1="EOS transfer"
40 | CHAIN1=0
41 | SIG1=$(node pay2key.sign.js $CHAIN1 $PUBKEY1 $PUBKEY2 10000 10 $NONCE1 "$MEMO1" $PRIVKEY1)
42 | cleos push action bank.pay2key transfer "[$CHAIN1, \"dcbtestusera\", \"$PUBKEY1\", \"$PUBKEY1\", \"$PUBKEY2\", \"1.0000 EOS\", \"0.0010 EOS\", $NONCE1, \"$MEMO1\", \"$SIG1\"]" -p dcbtestusera
43 | assert $(bc <<< "$? == 0")
44 |
45 | NONCE2=$(cleos get table bank.pay2key 1 accounts | jq ".rows[0].last_nonce")
46 | NONCE2=$(echo "$NONCE2 + 1" | bc)
47 | MEMO2="IQ transfer"
48 | CHAIN2=1
49 | SIG2=$(node pay2key.sign.js $CHAIN2 $PUBKEY2 $PUBKEY1 20300 15 $NONCE2 "$MEMO2" $PRIVKEY2)
50 | cleos push action bank.pay2key transfer "[$CHAIN2, \"dcbtestuserb\", \"$PUBKEY2\", \"$PUBKEY2\", \"$PUBKEY1\", \"20.300 IQ\", \"0.015 IQ\", $NONCE2, \"$MEMO2\", \"$SIG2\"]" -p dcbtestuserb
51 | assert $(bc <<< "$? == 0")
52 |
53 | echo -e "${CYAN}-----------------REPLAY PROTECTION (TRANSFER SHOULD FAIL)------------------${NC}"
54 | # Amount and fee are multiplied by 10 because they IQ has 3 decimal places and EOS has 4
55 | cleos push action bank.pay2key transfer "[$CHAIN2, \"dcbtestusera\", \"$PUBKEY1\", \"$PUBKEY1\", \"$PUBKEY2\", \"10.000 IQ\", \"0.010 IQ\", $NONCE1, \"$MEMO1\", \"$SIG1\"]" -p dcbtestusera
56 | assert $(bc <<< "$? == 1")
57 |
58 | echo -e "${CYAN}-----------------------WITHDRAW------------------------${NC}"
59 | EOS_BALANCE_BEFORE=$(cleos get table eosio.token dcbtestusera accounts | jq ".rows[0].balance" | tr -d '"' | awk '{print $1}')
60 | IQ_BALANCE_BEFORE=$(cleos get table everipediaiq dcbtestuserb accounts | jq ".rows[0].balance" | tr -d '"' | awk '{print $1}')
61 |
62 | NONCE3=$(echo "$NONCE1 + 1" | bc)
63 | MEMO3="dcbtestusera"
64 | SIG3=$(node pay2key.sign.js $CHAIN1 $PUBKEY1 $WITHDRAW_PUBKEY 20000 10 $NONCE3 "$MEMO3" $PRIVKEY1)
65 | cleos push action bank.pay2key transfer "[$CHAIN1, \"dcbtestusera\", \"$PUBKEY1\", \"$PUBKEY1\", \"$WITHDRAW_PUBKEY\", \"2.0000 EOS\", \"0.0010 EOS\", $NONCE3, \"$MEMO3\", \"$SIG3\"]" -p dcbtestusera
66 | assert $(bc <<< "$? == 0")
67 |
68 | NONCE4=$(echo "$NONCE2 + 1" | bc)
69 | MEMO4="dcbtestuserb:test memo"
70 | SIG4=$(node pay2key.sign.js $CHAIN2 $PUBKEY2 $WITHDRAW_PUBKEY 30000 15 $NONCE4 "$MEMO4" $PRIVKEY2)
71 | cleos push action bank.pay2key transfer "[$CHAIN2, \"dcbtestuserb\", \"$PUBKEY2\", \"$PUBKEY2\", \"$WITHDRAW_PUBKEY\", \"30.000 IQ\", \"0.015 IQ\", $NONCE4, \"$MEMO4\", \"$SIG4\"]" -p dcbtestuserb
72 | assert $(bc <<< "$? == 0")
73 |
74 | # edge case with just a colon
75 | NONCE5=$(echo "$NONCE4 + 1" | bc)
76 | MEMO5="dcbtestuserb:"
77 | SIG5=$(node pay2key.sign.js $CHAIN2 $PUBKEY2 $WITHDRAW_PUBKEY 30000 15 $NONCE5 "$MEMO5" $PRIVKEY2)
78 | cleos push action bank.pay2key transfer "[$CHAIN2, \"dcbtestuserb\", \"$PUBKEY2\", \"$PUBKEY2\", \"$WITHDRAW_PUBKEY\", \"30.000 IQ\", \"0.015 IQ\", $NONCE5, \"$MEMO5\", \"$SIG5\"]" -p dcbtestuserb
79 | assert $(bc <<< "$? == 0")
80 |
81 | EOS_BALANCE_AFTER=$(cleos get table eosio.token dcbtestusera accounts | jq ".rows[0].balance" | tr -d '"' | awk '{print $1}')
82 | IQ_BALANCE_AFTER=$(cleos get table everipediaiq dcbtestuserb accounts | jq ".rows[0].balance" | tr -d '"' | awk '{print $1}')
83 |
84 | assert $(bc <<< "$EOS_BALANCE_AFTER - $EOS_BALANCE_BEFORE == 2.0")
85 | assert $(bc <<< "$IQ_BALANCE_AFTER - $IQ_BALANCE_BEFORE == 60.0")
86 |
--------------------------------------------------------------------------------
/bank.pay2key/js_test/test.js:
--------------------------------------------------------------------------------
1 | const ecc = require('eosjs-ecc');
2 | const base58 = require('bs58');
3 | const shell = require('shelljs');
4 |
5 | const EOSIO_CONTRACTS_ROOT = '~/eosio.contracts/build/contracts';
6 |
7 | function getKeys() {
8 | return Promise.all([ecc.randomKey(), ecc.randomKey(), ecc.randomKey()]);
9 | }
10 |
11 | function createKeyPair(privateKey) {
12 | return {
13 | public: ecc.privateToPublic(privateKey),
14 | private: privateKey,
15 | };
16 | }
17 |
18 | function transferCommand(relayer, fromKeys, to, amount, fee, nonce, memo) {
19 | const version = 1;
20 | const length = 92 + memo.length;
21 |
22 | const pkeyFrom = base58.decode(fromKeys.public.substring(3));
23 | const pkeyTo = base58.decode(to.substring(3));
24 | const amountBuf = uint64_to_little_endian(amount * 10000);
25 | const feeBuf = uint64_to_little_endian(fee * 10000);
26 | const nonceBuf = uint64_to_little_endian(nonce);
27 | const memoBuf = Buffer.from(memo);
28 |
29 | // create raw tx
30 | const buffer = Buffer.alloc(length);
31 | buffer[0] = version;
32 | buffer[1] = length;
33 | pkeyFrom.copy(buffer, 2, 0, 33);
34 | pkeyTo.copy(buffer, 35, 0, 33);
35 | amountBuf.copy(buffer, 68, 0, 8);
36 | feeBuf.copy(buffer, 76, 0, 8);
37 | nonceBuf.copy(buffer, 84, 0, 8);
38 | memoBuf.copy(buffer, 92, 0, memoBuf.length);
39 |
40 | // hash raw tx
41 | const hashed = ecc.sha256(buffer, 'hex');
42 |
43 | // sign transaction
44 | const sig = ecc.signHash(hashed, fromKeys.private);
45 |
46 | return `cleos push action bank.pay2key transfer '["${relayer}", "${fromKeys.public}", "${to}", "${amount}.0000 UTXO", "${fee}.0000 UTXO", ${nonce}, "${memo}", "${sig}"]' -p bank.pay2key`;
47 | }
48 |
49 | // due to JS limitaitons, this only has 48-bit precision,
50 | // but that's good enough for what we need
51 | function uint64_to_little_endian(num) {
52 | const buf = Buffer.alloc(8);
53 | buf.writeUIntLE(num, 0, 6);
54 | return buf;
55 | }
56 |
57 | async function test() {
58 | const privateKeys = await getKeys();
59 | const utxoKeys = createKeyPair(privateKeys[0]);
60 | const firstAccountKeys = createKeyPair(privateKeys[1]);
61 | const secondAccountKeys = createKeyPair(privateKeys[2]);
62 |
63 | // wallet setup
64 | shell.exec('rm ~/eosio-wallet/default.wallet');
65 | shell.exec('cleos wallet create --to-console');
66 | shell.exec('cleos wallet import --private-key 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3'); // EOS key
67 | shell.exec(`cleos wallet import --private-key ${utxoKeys.private}`);
68 |
69 | // account setup
70 | shell.exec(`cleos create account eosio bank.pay2key ${utxoKeys.public}`);
71 | shell.exec(`cleos create account eosio everipediaiq ${utxoKeys.public}`);
72 | shell.exec(`cleos create account eosio eosio.token ${utxoKeys.public}`);
73 |
74 | // bootstrap system contracts
75 | shell.exec(`cleos set contract eosio.token ${EOSIO_CONTRACTS_ROOT}/eosio.token/`)
76 | shell.exec(`cleos push action eosio.token create '["eosio", "1000.0000 EOS"]' -p eosio.token`)
77 | shell.exec(`cleos push action eosio.token issue '["eosio", "1000.0000 EOS", "memo"]' -p eosio`)
78 |
79 | // compilation and setting
80 | shell.exec('eosio-cpp -w -o ../bank.pay2key.wasm ../bank.pay2key.cpp');
81 | shell.exec('cleos set contract bank.pay2key ..');
82 |
83 | // create
84 | shell.exec('cleos push action bank.pay2key create \'["eosio.token", "EOS", 1]\' -p bank.pay2key');
85 | shell.exec('cleos push action bank.pay2key create \'["everipediaiq", "IQ", 2]\' -p bank.pay2key');
86 | shell.exec('cleos get table bank.pay2key bank.pay2key stats');
87 |
88 | // issue
89 | shell.exec(`cleos transfer eosio bank.pay2key "1.0000 EOS" ${utxoKeys.public}`);
90 | shell.exec(`cleos transfer eosio bank.pay2key "5.0000 EOS" ${firstAccountKeys.public}`);
91 | shell.exec(`cleos transfer eosio bank.pay2key "5.0000 EOS" ${secondAccountKeys.public}`);
92 | shell.exec('cleos get table bank.pay2key bank.pay2key accounts');
93 |
94 | // transfer, expected balances is 2, 7, 2
95 | shell.exec(transferCommand(utxoKeys.public, secondAccountKeys, firstAccountKeys.public, 3, 1, 1, "transfer from second account to first"));
96 | shell.exec(transferCommand(utxoKeys.public, firstAccountKeys, secondAccountKeys.public, 1, 0, 1, "transfer from second account to first"));
97 | shell.exec('cleos get table bank.pay2key bank.pay2key accounts');
98 |
99 | // withdrawal, expected balances is 2, 2, 2
100 | shell.exec(`cleos set account permission bank.pay2key active '{ "threshold": 1, "keys": [{ "key": "${utxoKeys.public}", "weight": 1 }], "accounts": [{ "permission": { "actor":"bank.pay2key","permission":"eosio.code" }, "weight":1 } ] }' owner -p bank.pay2key`);
101 | shell.exec(transferCommand(utxoKeys.public, firstAccountKeys, 'EOS1111111111111111111111111111111114T1Anm', 5, 0, 2, 'eosio.token', 'bank.pay2key'));
102 | shell.exec('cleos get table bank.pay2key bank.pay2key accounts');
103 |
104 | shell.exec('cleos get table bank.pay2key 340784338180 stats');
105 | }
106 |
107 | test();
108 |
--------------------------------------------------------------------------------
/bank.pay2key/base58.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2014 Luke Dashjr
3 | *
4 | * This program is free software; you can redistribute it and/or modify it
5 | * under the terms of the standard MIT license. See COPYING for more details.
6 | */
7 |
8 | const char b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
9 |
10 | const int8_t b58digits_map[] = {
11 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
12 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
13 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
14 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,-1,-1,-1,-1,-1,-1,
15 | -1, 9,10,11,12,13,14,15, 16,-1,17,18,19,20,21,-1,
16 | 22,23,24,25,26,27,28,29, 30,31,32,-1,-1,-1,-1,-1,
17 | -1,33,34,35,36,37,38,39, 40,41,42,43,-1,44,45,46,
18 | 47,48,49,50,51,52,53,54, 55,56,57,-1,-1,-1,-1,-1,
19 | };
20 |
21 | void memzero(void *const pnt, const size_t len)
22 | {
23 | // I'm pretty sure none of the commented out stuff below
24 | // is needed for WASM but I've left it there in case
25 | // - Kedar Iyer
26 | //#ifdef _WIN32
27 | // SecureZeroMemory(pnt, len);
28 | //#elif defined(HAVE_MEMSET_S)
29 | // memset_s(pnt, (rsize_t) len, 0, (rsize_t) len);
30 | //#elif defined(HAVE_EXPLICIT_BZERO)
31 | // explicit_bzero(pnt, len);
32 | //#elif defined(HAVE_EXPLICIT_MEMSET)
33 | // explicit_memset(pnt, 0, len);
34 | //#else
35 | volatile unsigned char *volatile pnt_ =
36 | (volatile unsigned char *volatile) pnt;
37 | size_t i = (size_t) 0U;
38 |
39 | while (i < len) {
40 | pnt_[i++] = 0U;
41 | }
42 | //#endif
43 | }
44 |
45 |
46 | bool b58enc(char *b58, size_t *b58sz, const uint8_t *bin, size_t binsz)
47 | {
48 | int carry;
49 | ssize_t i, j, high, zcount = 0;
50 | size_t size;
51 |
52 | while (zcount < (ssize_t)binsz && !bin[zcount])
53 | ++zcount;
54 |
55 | size = (binsz - zcount) * 138 / 100 + 1;
56 | uint8_t buf[size];
57 | memzero(buf, size);
58 |
59 | for (i = zcount, high = size - 1; i < (ssize_t)binsz; ++i, high = j)
60 | {
61 | for (carry = bin[i], j = size - 1; (j > high) || carry; --j)
62 | {
63 | carry += 256 * buf[j];
64 | buf[j] = carry % 58;
65 | carry /= 58;
66 | }
67 | }
68 |
69 | for (j = 0; j < (ssize_t)size && !buf[j]; ++j);
70 |
71 | if (*b58sz <= zcount + size - j)
72 | {
73 | *b58sz = zcount + size - j + 1;
74 | return false;
75 | }
76 |
77 | if (zcount)
78 | memset(b58, '1', zcount);
79 | for (i = zcount; j < (ssize_t)size; ++i, ++j)
80 | b58[i] = b58digits_ordered[buf[j]];
81 | b58[i] = '\0';
82 | *b58sz = i + 1;
83 |
84 | return true;
85 | }
86 |
87 | bool b58tobin(void *bin, size_t *binszp, const char *b58)
88 | {
89 | size_t binsz = *binszp;
90 |
91 | if (binsz == 0) {
92 | return false;
93 | }
94 |
95 | const unsigned char *b58u = (const unsigned char*)b58;
96 | unsigned char *binu = (unsigned char *)bin;
97 | size_t outisz = (binsz + 3) / 4;
98 | uint32_t outi[outisz];
99 | uint64_t t;
100 | uint32_t c;
101 | size_t i, j;
102 | uint8_t bytesleft = binsz % 4;
103 | uint32_t zeromask = bytesleft ? (0xffffffff << (bytesleft * 8)) : 0;
104 | unsigned zerocount = 0;
105 | size_t b58sz;
106 |
107 | b58sz = strlen(b58);
108 |
109 | memzero(outi, sizeof(outi));
110 |
111 | // Leading zeros, just count
112 | for (i = 0; i < b58sz && b58u[i] == '1'; ++i)
113 | ++zerocount;
114 |
115 | for ( ; i < b58sz; ++i)
116 | {
117 | if (b58u[i] & 0x80)
118 | // High-bit set on invalid digit
119 | return false;
120 | if (b58digits_map[b58u[i]] == -1)
121 | // Invalid base58 digit
122 | return false;
123 | c = (unsigned)b58digits_map[b58u[i]];
124 | for (j = outisz; j--; )
125 | {
126 | t = ((uint64_t)outi[j]) * 58 + c;
127 | c = (t & 0x3f00000000) >> 32;
128 | outi[j] = t & 0xffffffff;
129 | }
130 | if (c)
131 | // Output number too big (carry to the next int32)
132 | return false;
133 | if (outi[0] & zeromask)
134 | // Output number too big (last int32 filled too far)
135 | return false;
136 | }
137 |
138 | j = 0;
139 | switch (bytesleft) {
140 | case 3:
141 | *(binu++) = (outi[0] & 0xff0000) >> 16;
142 | //-fallthrough
143 | case 2:
144 | *(binu++) = (outi[0] & 0xff00) >> 8;
145 | //-fallthrough
146 | case 1:
147 | *(binu++) = (outi[0] & 0xff);
148 | ++j;
149 | //-fallthrough
150 | default:
151 | break;
152 | }
153 |
154 | for (; j < outisz; ++j)
155 | {
156 | *(binu++) = (outi[j] >> 0x18) & 0xff;
157 | *(binu++) = (outi[j] >> 0x10) & 0xff;
158 | *(binu++) = (outi[j] >> 8) & 0xff;
159 | *(binu++) = (outi[j] >> 0) & 0xff;
160 | }
161 |
162 | // Count canonical base58 byte count
163 | binu = (unsigned char *)bin;
164 | for (i = 0; i < binsz; ++i)
165 | {
166 | if (binu[i]) {
167 | if (zerocount > i) {
168 | /* result too large */
169 | return false;
170 | }
171 | break;
172 | }
173 | --*binszp;
174 | }
175 | *binszp += zerocount;
176 |
177 | return true;
178 | }
179 |
--------------------------------------------------------------------------------
/frax.reserve/frax.reserve.cpp:
--------------------------------------------------------------------------------
1 | #include "frax.reserve.hpp"
2 |
3 | [[eosio::action]]
4 | void fraxreserve::addtoken(name contract, symbol ticker) {
5 | require_auth( _self );
6 |
7 | check(ticker.is_valid(), "invalid symbol name");
8 |
9 | stats statstable(_self, _self.value);
10 | auto contract_sym_index = statstable.get_index();
11 | uint128_t merged = merge_contract_symbol(contract, ticker);
12 | auto existing = contract_sym_index.find(merged);
13 | check(existing == contract_sym_index.end(), "Token is already registered");
14 | check(statstable.find(ticker.raw()) == statstable.end(), "Another token with that ticker already exists");
15 |
16 | statstable.emplace(_self, [&](auto& s) {
17 | s.contract = contract;
18 | s.supply = asset(0, ticker);
19 | });
20 | }
21 |
22 | void fraxreserve::deposit( name from, name to, asset quantity, string memo ) {
23 | if (from == _self) return; // sending tokens, ignore
24 |
25 | auto symbol = quantity.symbol;
26 | check(symbol.is_valid(), "invalid symbol name");
27 | check(to == _self, "stop trying to hack the contract");
28 |
29 | // make sure contract and symbol are accepted by contract
30 | stats statstable(_self, _self.value);
31 | auto contract_sym_index = statstable.get_index();
32 | uint128_t merged = merge_contract_symbol(get_first_receiver(), symbol);
33 | auto existing = contract_sym_index.find(merged);
34 | check(existing != contract_sym_index.end(), "Token is not yet supported");
35 |
36 | // validate arguments
37 | check(quantity.is_valid(), "invalid quantity");
38 | check(quantity.amount < 1e10, "quantity is suspiciously high. send a smaller amount");
39 | check(quantity.amount > 0, "must deposit positive quantity");
40 | check(memo.size() < 256, "memo must be less than 256 bytes");
41 |
42 | // create/update entry in table
43 | accounts deptbl( _self, from.value );
44 | auto account_it = deptbl.find(symbol.raw());
45 | if (account_it == deptbl.end()) {
46 | deptbl.emplace( _self, [&](auto& a) {
47 | a.balance = quantity;
48 | });
49 | }
50 | else {
51 | deptbl.modify( account_it, _self, [&](auto& a) {
52 | a.balance += quantity;
53 | });
54 | }
55 |
56 | }
57 |
58 | [[eosio::action]]
59 | void fraxreserve::buyfrax(name buyer, asset frax) {
60 | require_auth(buyer);
61 |
62 | check(frax.amount > 0, "Must buy a positive amount");
63 | check(frax.symbol == FRAX_SYMBOL, "Can only buy FRAX");
64 |
65 | sysparams paramstbl( _self, _self.value);
66 | check(paramstbl.begin() != paramstbl.end(), "Reserve params must be initialized first");
67 | auto param_it = paramstbl.begin();
68 |
69 | stats statstable(_self, _self.value);
70 | auto fxs_stats = statstable.find(FXS_SYMBOL.raw());
71 | auto usdt_stats = statstable.find(USDT_SYMBOL.raw());
72 | check(fxs_stats != statstable.end(), "Must addtoken FXS first");
73 | check(usdt_stats != statstable.end(), "Must addtoken USDT first");
74 |
75 | asset needed_usdt = param_it->target_usdt - usdt_stats->supply;
76 | asset needed_fxs = param_it->target_fxs - fxs_stats->supply;
77 | uint64_t needed_fxs_value = needed_fxs.amount * param_it->fxs_price / 1e4;
78 | uint64_t total_needed_value = needed_usdt.amount + needed_fxs_value;
79 |
80 | asset sell_usdt = asset(frax.amount * needed_usdt.amount / total_needed_value, USDT_SYMBOL);
81 | asset sell_fxs = asset(frax.amount * needed_fxs_value / total_needed_value, FXS_SYMBOL);
82 | check(sell_usdt <= needed_usdt, "Attempting to buy too much FRAX");
83 | check(sell_fxs <= needed_fxs, "Attempting to buy too much FRAX");
84 |
85 | // Update accounts and stats
86 | accounts deptbl( _self, buyer.value );
87 | if (sell_usdt.amount > 0) {
88 | auto usdt_account = deptbl.find(USDT_SYMBOL.raw());
89 | check(usdt_account != deptbl.end(), "No USDT balance for this user");
90 | check(usdt_account->balance >= sell_usdt, "Not enough USDT balance to buy FRAX");
91 | deptbl.modify( usdt_account, _self, [&](auto& a) {
92 | a.balance -= sell_usdt;
93 | });
94 | statstable.modify( usdt_stats, same_payer, [&](auto& usdt) {
95 | usdt.supply += sell_usdt;
96 | });
97 | }
98 | if (sell_fxs.amount > 0) {
99 | auto fxs_account = deptbl.find(FXS_SYMBOL.raw());
100 | check(fxs_account != deptbl.end(), "No FXS balance for this user");
101 | check(fxs_account->balance >= sell_fxs, "Not enough FXS balance to buy FRAX");
102 | deptbl.modify( fxs_account, _self, [&](auto& a) {
103 | a.balance -= sell_fxs;
104 | });
105 | statstable.modify( fxs_stats, same_payer, [&](auto& fxs) {
106 | fxs.supply += sell_fxs;
107 | });
108 | }
109 |
110 | // Issue FRAX
111 | action(
112 | permission_level { _self, name("active") },
113 | name("fraxtokenfxs"), name("issue"),
114 | std::make_tuple( buyer, frax, std::string("issue FRAX") )
115 | ).send();
116 |
117 | }
118 |
119 | [[eosio::action]]
120 | void fraxreserve::settarget(asset target_usdt, asset target_fxs, uint64_t fxs_price) {
121 | require_auth( _self );
122 |
123 | check(target_usdt.symbol == USDT_SYMBOL, "Symbol mismatch for target_usdt");
124 | check(target_fxs.symbol == FXS_SYMBOL, "Symbol mismatch for target_usdt");
125 | check(fxs_price > 0, "fxs_price must be postive");
126 |
127 | sysparams paramstbl( _self, _self.value);
128 | if (paramstbl.begin() == paramstbl.end()) {
129 | paramstbl.emplace( _self, [&](auto& p) {
130 | p.target_usdt = target_usdt;
131 | p.target_fxs = target_fxs;
132 | p.fxs_price = fxs_price;
133 | });
134 | }
135 | else {
136 | paramstbl.modify( paramstbl.begin(), _self, [&](auto& p) {
137 | p.target_usdt = target_usdt;
138 | p.target_fxs = target_fxs;
139 | });
140 | }
141 |
142 | }
143 |
144 | extern "C" void apply(uint64_t receiver, uint64_t code, uint64_t action)
145 | {
146 | auto _self = receiver;
147 | if (code != _self && action == name("transfer").value)
148 | {
149 | eosio::execute_action(
150 | eosio::name(receiver), eosio::name(code), &fraxreserve::deposit
151 | );
152 | }
153 | else if (code == _self)
154 | {
155 | switch (action) {
156 | EOSIO_DISPATCH_HELPER( fraxreserve, (addtoken)(buyfrax)(settarget) )
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/tests/fraxloans_tests.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "contracts.hpp"
4 | #include "token_test_api.hpp"
5 | #include "fraxloans_api.hpp"
6 |
7 | using namespace eosio::testing;
8 | using namespace eosio;
9 | using namespace eosio::chain;
10 | using namespace fc;
11 | using namespace std;
12 |
13 | using mvo = fc::mutable_variant_object;
14 |
15 | #define SUCCESS(call) BOOST_REQUIRE_EQUAL(success(), call)
16 | #define ERROR(msg, call) BOOST_REQUIRE_EQUAL(wasm_assert_msg(msg), call)
17 |
18 | class fraxloans_tester: public tester
19 | {
20 | protected:
21 | token_test_api fraxtokens;
22 | token_test_api tether;
23 | token_test_api eos;
24 | fraxloans_api fraxloans;
25 | public:
26 | fraxloans_tester():
27 | fraxtokens(N(frax.token), this),
28 | tether(N(tethertether), this),
29 | eos(N(eosio.token), this),
30 | fraxloans(N(frax.loans), this)
31 | {
32 | create_accounts({ N(alice), N(bob), N(carol) });
33 | produce_blocks(2);
34 | init_tokens();
35 | }
36 |
37 | private:
38 | void init_tokens()
39 | {
40 | SUCCESS(eos.push_action(eos.contract, eos.contract, N(create), mvo()
41 | ("issuer", eos.contract)
42 | ("maximum_supply", asset::from_string("10000000000.0000 EOS"))
43 | ));
44 |
45 | SUCCESS(eos.push_action(eos.contract, eos.contract, N(issue), mvo()
46 | ("to", eos.contract)
47 | ("quantity", asset::from_string("100000.0000 EOS"))
48 | ("memo", "")
49 | ));
50 |
51 | SUCCESS(tether.push_action(tether.contract, tether.contract, N(create), mvo()
52 | ("issuer", tether.contract)
53 | ("maximum_supply", asset::from_string("10000000000.0000 USDT"))
54 | ));
55 |
56 | SUCCESS(tether.push_action(tether.contract, tether.contract, N(issue), mvo()
57 | ("to", tether.contract)
58 | ("quantity", asset::from_string("100000.0000 USDT"))
59 | ("memo", "")
60 | ));
61 |
62 | SUCCESS(fraxtokens.push_action(fraxtokens.contract, fraxtokens.contract, N(create), mvo()
63 | ("issuer", fraxtokens.contract)
64 | ("maximum_supply", asset::from_string("100000.0000 FRAX"))
65 | ));
66 |
67 | SUCCESS(fraxtokens.push_action(fraxtokens.contract, fraxtokens.contract, N(create), mvo()
68 | ("issuer", fraxtokens.contract)
69 | ("maximum_supply", asset::from_string("100000.0000 FXS"))
70 | ));
71 |
72 | SUCCESS(fraxtokens.push_action(fraxtokens.contract, fraxtokens.contract, N(issue), mvo()
73 | ("to", fraxtokens.contract)
74 | ("quantity", asset::from_string("100000.0000 FRAX"))
75 | ("memo", "")
76 | ));
77 |
78 | SUCCESS(fraxtokens.push_action(fraxtokens.contract, fraxtokens.contract, N(issue), mvo()
79 | ("to", fraxtokens.contract)
80 | ("quantity", asset::from_string("100000.0000 FXS"))
81 | ("memo", "")
82 | ));
83 |
84 | SUCCESS(fraxtokens.transfer(fraxtokens.contract, N(alice), asset::from_string("1000.0000 FRAX")));
85 | SUCCESS(fraxtokens.transfer(fraxtokens.contract, N(bob), asset::from_string("1000.0000 FRAX")));
86 | SUCCESS(fraxtokens.transfer(fraxtokens.contract, N(carol), asset::from_string("1000.0000 FRAX")));
87 | SUCCESS(fraxtokens.transfer(fraxtokens.contract, N(alice), asset::from_string("1000.0000 FXS")));
88 | SUCCESS(fraxtokens.transfer(fraxtokens.contract, N(bob), asset::from_string("1000.0000 FXS")));
89 | SUCCESS(fraxtokens.transfer(fraxtokens.contract, N(carol), asset::from_string("1000.0000 FXS")));
90 | SUCCESS(eos.transfer(eos.contract, N(alice), asset::from_string("1000.0000 EOS")));
91 | SUCCESS(eos.transfer(eos.contract, N(bob), asset::from_string("1000.0000 EOS")));
92 | SUCCESS(eos.transfer(eos.contract, N(carol), asset::from_string("1000.0000 EOS")));
93 | SUCCESS(tether.transfer(tether.contract, N(alice), asset::from_string("10000.0000 USDT")));
94 | SUCCESS(tether.transfer(tether.contract, N(bob), asset::from_string("10000.0000 USDT")));
95 | SUCCESS(tether.transfer(tether.contract, N(carol), asset::from_string("10000.0000 USDT")));
96 |
97 | // enable tokens in frax loans module
98 | SUCCESS(fraxloans.addtoken(fraxtokens.contract, "4,FRAX"));
99 | SUCCESS(fraxloans.addtoken(fraxtokens.contract, "4,FXS"));
100 | SUCCESS(fraxloans.addtoken(tether.contract, "4,USDT"));
101 | SUCCESS(fraxloans.addtoken(eos.contract, "4,EOS"));
102 |
103 | }
104 | };
105 |
106 | BOOST_AUTO_TEST_SUITE(fraxloans_tests)
107 |
108 | BOOST_FIXTURE_TEST_CASE(fraxloans_full, fraxloans_tester) try {
109 | // deposit for supply
110 | SUCCESS(tether.transfer(N(carol), fraxloans.contract, asset::from_string("1000.0000 USDT")));
111 | SUCCESS(eos.transfer(N(carol), fraxloans.contract, asset::from_string("1000.0000 EOS")));
112 | SUCCESS(fraxtokens.transfer(N(alice), fraxloans.contract, asset::from_string("1000.0000 FRAX")));
113 | SUCCESS(fraxtokens.transfer(N(bob), fraxloans.contract, asset::from_string("1000.0000 FXS")));
114 |
115 | auto tether_stats = fraxloans.get_tokenstats("4,USDT");
116 | auto eos_stats = fraxloans.get_tokenstats("4,EOS");
117 | auto frax_stats = fraxloans.get_tokenstats("4,FRAX");
118 | auto fxs_stats = fraxloans.get_tokenstats("4,FXS");
119 | auto alice_frax = fraxloans.get_account(N(alice), "4,FRAX");
120 | auto carol_tether = fraxloans.get_account(N(carol), "4,USDT");
121 | auto carol_eos = fraxloans.get_account(N(carol), "4,EOS");
122 | auto bob_fxs = fraxloans.get_account(N(bob), "4,FXS");
123 | BOOST_REQUIRE_EQUAL(tether_stats["available"], "1000.0000 USDT");
124 | BOOST_REQUIRE_EQUAL(eos_stats["available"], "1000.0000 EOS");
125 | BOOST_REQUIRE_EQUAL(fxs_stats["available"], "1000.0000 FXS");
126 | BOOST_REQUIRE_EQUAL(frax_stats["available"], "1000.0000 FRAX");
127 | BOOST_REQUIRE_EQUAL(alice_frax["balance"], "1000.0000 FRAX");
128 | BOOST_REQUIRE_EQUAL(carol_tether["balance"], "1000.0000 USDT");
129 | BOOST_REQUIRE_EQUAL(carol_eos["balance"], "1000.0000 EOS");
130 | BOOST_REQUIRE_EQUAL(bob_fxs["balance"], "1000.0000 FXS");
131 |
132 | // deposit for collateral
133 | SUCCESS(eos.transfer(N(alice), fraxloans.contract, asset::from_string("1000.0000 EOS")));
134 | SUCCESS(tether.transfer(N(bob), fraxloans.contract, asset::from_string("2000.0000 USDT")));
135 |
136 | // set token prices
137 | SUCCESS(fraxloans.setprice(asset::from_string("1.000 FRAX")));
138 | SUCCESS(fraxloans.setprice(asset::from_string("3.500 EOS")));
139 | SUCCESS(fraxloans.setprice(asset::from_string("1.000 FXS")));
140 | SUCCESS(fraxloans.setprice(asset::from_string("1.000 USDT")));
141 |
142 | // borrow
143 | SUCCESS(fraxloans.borrow(N(alice), asset::from_string("1000.0000 USDT")));
144 | SUCCESS(fraxloans.borrow(N(bob), asset::from_string("300.0000 EOS")));
145 |
146 | auto alice_usdt = fraxloans.get_account(N(alice), "4,USDT");
147 | auto bob_eos = fraxloans.get_account(N(bob), "4,EOS");
148 | BOOST_REQUIRE_EQUAL(alice_usdt["balance"], "1000.0000 USDT");
149 | BOOST_REQUIRE_EQUAL(alice_usdt["borrowing"], "1000.0000 USDT");
150 | BOOST_REQUIRE_EQUAL(bob_eos["balance"], "300.0000 EOS");
151 | BOOST_REQUIRE_EQUAL(bob_eos["borrowing"], "300.0000 EOS");
152 |
153 | // accrue interest
154 | produce_blocks(1000);
155 | }
156 | FC_LOG_AND_RETHROW()
157 |
158 | BOOST_AUTO_TEST_SUITE_END()
159 |
--------------------------------------------------------------------------------
/bank.pay2key/frontend/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './App.css';
3 |
4 | const ecc = require('eosjs-ecc');
5 | const base58 = require('bs58');
6 |
7 | class App extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | from: '',
12 | fromPrivateKey: '',
13 | to: '',
14 | amount: 0,
15 | fee: 1,
16 | nonce: 1,
17 | memo: '',
18 | sig: '',
19 | };
20 | }
21 |
22 | targetValue = (target) => {
23 | switch(target.type) {
24 | case "number":
25 | return target.valueAsNumber;
26 | case "text":
27 | return target.value;
28 | default:
29 | throw new Error("Unexpected target type");
30 | }
31 | }
32 |
33 | handleChange = (event) => {
34 | this.setState({ [event.target.name]: this.targetValue(event.target) });
35 | }
36 |
37 | handleSubmit = (event) => {
38 | event.preventDefault();
39 | const sig = this.transactionSignature(this.state.from,
40 | this.state.fromPrivateKey,
41 | this.state.to,
42 | this.state.amount,
43 | this.state.fee,
44 | this.state.nonce,
45 | this.state.memo);
46 | this.setState({ sig: sig });
47 | console.log(sig);
48 | this.apiRequest(sig);
49 | }
50 |
51 | // due to JS limitaitons, this only has 48-bit precision,
52 | // but that's good enough for what we need
53 | uint64_to_little_endian(num) {
54 | const buf = Buffer.alloc(8);
55 | buf.writeUIntLE(num, 0, 6);
56 | return buf;
57 | }
58 |
59 | transactionSignature(fromPublicKey, fromPrivateKey, toPublicKey, amount, fee, nonce, memo) {
60 | const version = 1;
61 | const length = 92 + memo.length;
62 |
63 | const pkeyFrom = base58.decode(fromPublicKey.substring(3));
64 | const pkeyTo = base58.decode(toPublicKey.substring(3));
65 | const amountBuf = this.uint64_to_little_endian(amount);
66 | const feeBuf = this.uint64_to_little_endian(fee);
67 | const nonceBuf = this.uint64_to_little_endian(nonce);
68 | const memoBuf = Buffer.from(memo);
69 |
70 | // create raw tx
71 | const buffer = Buffer.alloc(length);
72 | buffer[0] = version;
73 | buffer[1] = length;
74 | pkeyFrom.copy(buffer, 2, 0, 33);
75 | pkeyTo.copy(buffer, 35, 0, 33);
76 | amountBuf.copy(buffer, 68, 0, 8);
77 | feeBuf.copy(buffer, 76, 0, 8);
78 | nonceBuf.copy(buffer, 84, 0, 8);
79 | memoBuf.copy(buffer, 92, 0, memoBuf.length);
80 |
81 | // hash raw tx
82 | const hashed = ecc.sha256(buffer, 'hex');
83 |
84 | return ecc.signHash(hashed, fromPrivateKey);
85 | }
86 |
87 | async apiRequest(signature) {
88 | this.setState({ error: null });
89 | const RELAY_ENDPOINT = "https://relayer.utxo.libertyblock.io/relay";
90 | const relay_response = await fetch(RELAY_ENDPOINT, {
91 | method: 'POST',
92 | headers: {
93 | 'Accept': 'application/json',
94 | 'Content-Type': 'application/json',
95 | },
96 | body: JSON.stringify({
97 | from: this.state.from,
98 | to: this.state.to,
99 | amount: (this.state.amount / 1000).toFixed(3) + " IQUTXO",
100 | fee: (this.state.fee / 1000).toFixed(3) + " IQUTXO",
101 | nonce: this.state.nonce,
102 | memo: this.state.memo,
103 | sig: signature
104 | })
105 | })
106 | .then(response => response.json())
107 |
108 | console.log(relay_response);
109 | if (relay_response.error)
110 | this.setState({ error: relay_response.error });
111 | else
112 | this.setState({ txid: relay_response.transaction_id });
113 | }
114 |
115 | isSubmitDisabled() {
116 | return this.state.from === '' ||
117 | this.state.fromPrivateKey === '' ||
118 | this.state.to === '' ||
119 | isNaN(this.state.amount) ||
120 | isNaN(this.state.fee) ||
121 | isNaN(this.state.nonce);
122 | }
123 |
124 | render() {
125 | const { txid, error } = this.state;
126 |
127 | return (
128 |
129 |
IQ Pay-to-key
130 |
192 |
193 | {error ?
{error}
: null }
194 | {txid ?
195 |
196 | You transfer was successful. View it here
197 |