├── .gitignore ├── Bancor based utility-enhanced token protocol.md ├── Bancor based utility-enhanced token protocol.pdf ├── CMakeLists.txt ├── UnitTestsExternalProject.txt ├── example ├── CMakeLists.txt ├── example.cpp └── example.hpp ├── logo ├── EOSpark.png ├── eosflare-logo-512.png ├── tokendapppub-rectangle.jpg ├── tokendapppub.jpg └── tpdex-small.png ├── readme.md ├── tests ├── CMakeLists.txt ├── main.cpp ├── tokendapppub_test.cpp └── tokendapppub_test.hpp.in ├── tokendapppub.buy_rc.md ├── tokendapppub.claim_rc.md ├── tokendapppub.consume_rc.md ├── tokendapppub.cpp ├── tokendapppub.destroy_rc.md ├── tokendapppub.hellodapppub_rc.md ├── tokendapppub.hpp ├── tokendapppub.newtoken_rc.md ├── tokendapppub.receipt_rc.md ├── tokendapppub.reg_rc.md ├── tokendapppub.sell_rc.md ├── tokendapppub.transfer_rc.md ├── tokendapppub_rc.md ├── 基于班柯的实用增强型通证协议.md └── 基于班柯的实用增强型通证协议.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | build 4 | tests/eosio.contracts -------------------------------------------------------------------------------- /Bancor based utility-enhanced token protocol.md: -------------------------------------------------------------------------------- 1 | # **Bancor based utility-enhanced token protocol** 2 | 3 | As a series of mainnets for high TPS blockchain infrastructure are being implemented in 2018, DApp development is becoming more and more popular recently. However, there is no corresponding industry standard for these DApp utility tokens so far. In this whitepaper, we purpose a kind of new utility token that helps DApp developers design and issue their own utility tokens. 4 | 5 | On the Ethereum network, quite much efforts are taken to provide strong liquidity for these traditional ERC20 tokens. However, some DApps do not necessarily require strong liquidity. When DApps are on the different economic scale or in different development stages, different token demands need to be 6 | met. Free capital flow, fixed exchange rate, independent token policy -- these three requirements need to be balanced constantly for a DApp utility token. And the performance of the entire crypto currency market starting from 2017 has also proven that the designed strong liquidity does not help DApp developers or users much. It's only good for the market speculators and gamblers for the moment. 7 | 8 | In view of the various problems caused by the strong token liquidity, we believe that utility tokens should focus on the token usage scenario and dividends for long-term investors. Therefore, based on the "Bancor Protocol", in this whitepaper, we introduce the token consumption protocol and dividend protocol along with the option protocol and a new trading fee algorithm to make this utility token in line with the current DApp development trend. 9 | 10 | ### 1. Token consumption protocol 11 | 12 | The core of the Bancor-based token protocol is to consume the tokens held by users and return them to the current token pool. Meanwhile, since the benchmark currency at the other end of the Bancor protocol remains the same, it will increase the value of this utility token for those token holders. The corresponding recursion formulas are as follows: 13 | $$ 14 | \begin{align} 15 | baseCurrencyAmount'&=\frac{(baseCurrencyAmount-baseCurrencyReserve)\times utilityTokenTotalSupply}{utilityTokenTotalSupply-utiltyTokenRemainings-utilityTokenConsumption}\\ 16 | baseCurrencyReserve'&=\frac{(baseCurrencyAmount-baseCurrencyReserve)\times (utilityTokenRemainings+UtilityTokenConsumption)}{UtilityTokenTotalSupply-utilityTokenRemainings-utilityTokenConsumption}\\ 17 | utilituTokenRemainings'&=utilityTokenRemainings+utilityTokenConsumption 18 | \end{align} 19 | $$ 20 | 21 | ### 2. Token dividend protocol 22 | 23 | The core of the Bancor-based token dividend protocol is to increase the amount of the base currency, while keeping the remaining amount and total supply of the utility token the same in the meantime, which increase the value of this utility token for those token holders. The corresponding recursion formulas are as follows: 24 | 25 | 26 | $$ 27 | baseCurrencyAmount'=\frac{(baseCurrencyAmount-baseCurrencyReserve+baseCurrencyDividends)\times utilityTokenTotalSupply}{utilityTokenTotalSupply-utilityTokenRemainings}\\ 28 | baseCurrencyReserve'=\frac{(baseCurrencyAmount-baseCurrencyReserve+baseCUrrencyDividends)\times utilityTokenRemainings}{utilityTokenTotalSupply-utilityTokenRemainings} 29 | $$ 30 | 31 | ### 3. Token option protocol 32 | 33 | All most all so-called team lock-up plan for the ERC-20 tokens on the Ethereum network are performed manually by the project team. These ERC-20 tokens are kept in a public account so all investors could monitor together. 34 | 35 | In consideration of the need for the team lock-up plan purpose, we purpose a lock-up release protocol In this whitepaper. The locked tokens will be released gradually during the lock-up period, and the corresponding formulas are as follows: 36 | 37 | 38 | $$ 39 | utilityTokenReleasedOptionAmount'=\frac{(currentTime-utilityTokenIssuedTime)\times utilityTokenOptionTotalSupply}{utilityTokenOptionLockupPeriod} 40 | $$ 41 | 42 | The option release process is equivalent to the new token distribution process, and the newly issued utility tokens are authorized to the DApp project team in the mean time. It is necessary to keep the Bancor trading protocol smooth running all the time especially when the new tokens are released. In the EOS RAM extension model, there is a problem that large amount of EOS will be locked in the smart contract eventually. Even RAM holder sell all their RAM, they can not get their locked EOS back from the contract. In this new model, this problem could be avoided so as to protect the token holders' interests. The corresponding formulas are as follows: 43 | 44 | $$ 45 | \begin{align} 46 | baseCurrencyAmount'&=\frac{(baseCurrencyAmount-baseCurrencyReserve)\times (utilityTokenReleasedOptionAmount+utilityTokenTotalSupply)}{utilityTokenTotalSupply-utilityTokenRemainings+utilityTokenReleasedOptionAmount}\\ 47 | baseCurrencyReserve'&=\frac{(baseCurrencyAmount-baseCurrencyReserve)\times utilityTokenRemainings}{utilityTokenTotalSupply-utilityTokenRemainings+utilityTokenReleasedOptionAmount}\\ 48 | utilityTokenTotalSupply'&=utilityTokenTotalSupply+utilityTokenReleasedOptionAmount 49 | \end{align} 50 | $$ 51 | 52 | ### 4. Token trading fee 53 | 54 | In order to encourage users to purchase and consume the utility tokens issued by the DApp project team, token purchase fee and consumption fee is waived for users. However, there will be a selling fee charged when users sell tokens. The main reason is that when a DApp is in its early stage, it's economy is rather small. Market speculation behaviours could lead to sharp fluncuations in token price for Bancor protocol based tokens, which has a negative impact on the DApp development process. That is why a relative high selling fee is introduced at the beginning, and it will decrease gradually when the option is released. The recursion formulas for the selling fee are as follows: 55 | 56 | 57 | $$ 58 | utilityTokenSellingFee'=\frac{2 \times (initFeeRatio-baseFeeRatio) \times utilityTokenOptionLockupPeriod}{(curentTime-utilityTokenIssuedTime)+utilityTokenOptionLockupPeriod}+(2 \times baseFeeRatio - initFeeRatio) 59 | $$ 60 | -------------------------------------------------------------------------------- /Bancor based utility-enhanced token protocol.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeBankDeFi/tokendapppub/1c4ab3d9047710e9757d66d03e0baa6f969c6d9c/Bancor based utility-enhanced token protocol.pdf -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | project(tokendapppub) 3 | 4 | set(EOSIO_DEPENDENCY "1.1") 5 | set(EOSIO_WASMSDK_DEPENDENCY "1.1") 6 | 7 | set(CMAKE_CXX_STANDARD 14) 8 | 9 | if(EOSIO_WASMSDK_ROOT STREQUAL "" OR NOT EOSIO_WASMSDK_ROOT) 10 | set(EOSIO_WASMSDK_ROOT "/usr/local/eosio.wasmsdk") 11 | endif() 12 | 13 | list(APPEND CMAKE_MODULE_PATH ${EOSIO_WASMSDK_ROOT}/lib/cmake) 14 | include(EosioWasmToolchain) 15 | 16 | add_executable(tokendapppub.wasm tokendapppub.hpp tokendapppub.cpp) 17 | 18 | if(EOSIO_ROOT STREQUAL "" OR NOT EOSIO_ROOT) 19 | set(EOSIO_ROOT "/usr/local/eosio") 20 | endif() 21 | set(EOSIOCPP ${EOSIO_ROOT}/bin/eosiocpp) 22 | set(CONTRACT_ABI_FILE ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.abi) 23 | set(CONTRACT_HPP_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_PROJECT_NAME}.hpp) 24 | add_custom_target(tokendapppub.abi ALL) 25 | add_custom_command( 26 | TARGET tokendapppub.abi 27 | COMMAND ${EOSIOCPP} -g ${CONTRACT_ABI_FILE} ${CONTRACT_HPP_FILE} 28 | ) 29 | 30 | # include(UnitTestsExternalProject.txt) -------------------------------------------------------------------------------- /UnitTestsExternalProject.txt: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | find_package(Git REQUIRED) 3 | include(GNUInstallDirs) 4 | 5 | ExternalProject_Add( 6 | tokendapppub_unit_tests 7 | CMAKE_ARGS -DCMAKE_BUILD_TYPE=${TEST_BUILD_TYPE} -DEOSIO_ROOT=${EOSIO_ROOT} -DEOSIO_DEPENDENCY=${EOSIO_DEPENDENCY} 8 | 9 | SOURCE_DIR ${CMAKE_SOURCE_DIR}/tests 10 | BINARY_DIR ${CMAKE_BINARY_DIR}/tests 11 | BUILD_ALWAYS 0 12 | TEST_COMMAND "" 13 | INSTALL_COMMAND "" 14 | ) -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | project(example) 3 | 4 | set(EOSIO_DEPENDENCY "1.1") 5 | set(EOSIO_WASMSDK_DEPENDENCY "1.1") 6 | 7 | set(CMAKE_CXX_STANDARD 14) 8 | 9 | if(EOSIO_WASMSDK_ROOT STREQUAL "" OR NOT EOSIO_WASMSDK_ROOT) 10 | set(EOSIO_WASMSDK_ROOT "/usr/local/eosio.wasmsdk") 11 | endif() 12 | 13 | list(APPEND CMAKE_MODULE_PATH ${EOSIO_WASMSDK_ROOT}/lib/cmake) 14 | include(EosioWasmToolchain) 15 | 16 | add_executable(example.wasm example.hpp example.cpp) 17 | 18 | if(EOSIO_ROOT STREQUAL "" OR NOT EOSIO_ROOT) 19 | set(EOSIO_ROOT "/usr/local/eosio") 20 | endif() 21 | set(EOSIOCPP ${EOSIO_ROOT}/bin/eosiocpp) 22 | set(CONTRACT_ABI_FILE ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.abi) 23 | set(CONTRACT_HPP_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_PROJECT_NAME}.hpp) 24 | add_custom_target(example.abi ALL) 25 | add_custom_command( 26 | TARGET example.abi 27 | COMMAND ${EOSIOCPP} -g ${CONTRACT_ABI_FILE} ${CONTRACT_HPP_FILE} 28 | ) -------------------------------------------------------------------------------- /example/example.cpp: -------------------------------------------------------------------------------- 1 | #include "example.hpp" 2 | 3 | void example::consume(account_name from, asset quantity, string memo) { 4 | require_auth(from); 5 | eosio_assert(quantity.is_valid(), "invalid quantity"); 6 | eosio_assert(quantity.symbol == EXAMPLE_SYMBOL, "symbol mismatch"); 7 | 8 | // TODO: your business logic 9 | 10 | action( 11 | permission_level{_self, N(active)}, 12 | _self, 13 | N(receipt), 14 | make_tuple(from, quantity) 15 | ).send(); 16 | } 17 | 18 | void example::receipt(account_name from, asset quantity) { 19 | require_auth(_self); 20 | } 21 | 22 | extern "C" { 23 | void apply( uint64_t receiver, uint64_t code, uint64_t action ) { 24 | example thiscontract(receiver); 25 | 26 | if((code == N(tokendapppub)) && (action == N(consume))) { 27 | execute_action(&thiscontract, &example::consume); 28 | return; 29 | } 30 | 31 | if (code != receiver) return; 32 | 33 | switch (action) { 34 | EOSIO_API(example, (receipt)) 35 | }; 36 | eosio_exit(0); 37 | } 38 | } -------------------------------------------------------------------------------- /example/example.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | using namespace eosio; 8 | using namespace std; 9 | 10 | const symbol_type EXAMPLE_SYMBOL = S(4, EXAMPLE); 11 | 12 | class example: public contract { 13 | public: 14 | example(account_name self): 15 | contract(self) 16 | {}; 17 | void consume(account_name from, asset quantity, string memo); 18 | void receipt(account_name from, asset quantity); 19 | }; 20 | 21 | #ifdef ABIGEN 22 | EOSIO_ABI(example, (receipt)) 23 | #endif -------------------------------------------------------------------------------- /logo/EOSpark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeBankDeFi/tokendapppub/1c4ab3d9047710e9757d66d03e0baa6f969c6d9c/logo/EOSpark.png -------------------------------------------------------------------------------- /logo/eosflare-logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeBankDeFi/tokendapppub/1c4ab3d9047710e9757d66d03e0baa6f969c6d9c/logo/eosflare-logo-512.png -------------------------------------------------------------------------------- /logo/tokendapppub-rectangle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeBankDeFi/tokendapppub/1c4ab3d9047710e9757d66d03e0baa6f969c6d9c/logo/tokendapppub-rectangle.jpg -------------------------------------------------------------------------------- /logo/tokendapppub.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeBankDeFi/tokendapppub/1c4ab3d9047710e9757d66d03e0baa6f969c6d9c/logo/tokendapppub.jpg -------------------------------------------------------------------------------- /logo/tpdex-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeBankDeFi/tokendapppub/1c4ab3d9047710e9757d66d03e0baa6f969c6d9c/logo/tpdex-small.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # tokendapppub: a bancor based utility-enhanced token protocol 2 | 3 | * Read the Whitepaper at first: [English](https://github.com/Dappub/tokendapppub/blob/master/Bancor%20based%20utility-enhanced%20token%20protocol.pdf)/[Chinese](https://github.com/Dappub/tokendapppub/blob/master/%E5%9F%BA%E4%BA%8E%E7%8F%AD%E6%9F%AF%E7%9A%84%E5%AE%9E%E7%94%A8%E5%A2%9E%E5%BC%BA%E5%9E%8B%E9%80%9A%E8%AF%81%E5%8D%8F%E8%AE%AE.pdf) 4 | * Introduction about this DApp on Medium: [tokendapppub](https://medium.com/@DappPub/tokendapppub-1-b6143c6039e3) 5 | 6 | ### Usage (buy,sell,transfer,publish) 7 | 8 | publish your DApp token by yourself. And then, you can buy/sell and transfer your token on the website. 9 | 10 | ### Buy Example 11 | 12 | ```js 13 | contract: eosio.token 14 | action: transfer 15 | data: { 16 | "to": "tokendapppub", 17 | "from": "yourself", 18 | "quantity": "10.0000 EOS", 19 | "memo": "TPT-referrer:godofdapppub" 20 | } 21 | ``` 22 | 23 | ### Sell Example 24 | 25 | ```js 26 | contract: tokendapppub 27 | action: sell 28 | data: { 29 | "from": "yourself", 30 | "quantity": "100.0000 PUB" 31 | } 32 | ``` 33 | 34 | 35 | 36 | ### Consume Example 37 | 38 | If you are a developer and you want to pushlish a utility token for consuming on your DApp. after you publish your token, you should wirte a contract to do this. after you set your code on EOS mainnet, when user consume your token, we will notify your contract by `require_receiption` in tokendapppub contract. The consume Richard Contract is [here](https://github.com/Dappub/tokendapppub/blob/master/tokendapppub.consume_rc.md). 39 | 40 | * example.hpp 41 | 42 | ```c++ 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | 49 | using namespace eosio; 50 | using namespace std; 51 | 52 | const symbol_type EXAMPLE_SYMBOL = S(4, EXAMPLE); 53 | 54 | class example: public contract { 55 | public: 56 | example(account_name self): 57 | contract(self) 58 | {}; 59 | void consume(account_name from, asset quantity, string memo); 60 | void receipt(account_name from, asset quantity); 61 | }; 62 | 63 | #ifdef ABIGEN 64 | EOSIO_ABI(example, (receipt)) 65 | #endif 66 | ``` 67 | 68 | * example.cpp 69 | 70 | ```c++ 71 | #include "example.hpp" 72 | 73 | void example::consume(account_name from, asset quantity, string memo) { 74 | require_auth(from); 75 | eosio_assert(quantity.is_valid(), "invalid quantity"); 76 | eosio_assert(quantity.symbol == EXAMPLE_SYMBOL, "symbol mismatch"); 77 | 78 | // TODO: your business logic 79 | 80 | action( 81 | permission_level{_self, N(active)}, 82 | _self, 83 | N(receipt), 84 | make_tuple(from, quantity) 85 | ).send(); 86 | } 87 | 88 | void example::receipt(account_name from, asset quantity) { 89 | require_auth(_self); 90 | } 91 | 92 | extern "C" { 93 | void apply( uint64_t receiver, uint64_t code, uint64_t action ) { 94 | example thiscontract(receiver); 95 | 96 | if((code == N(tokendapppub)) && (action == N(consume))) { 97 | execute_action(&thiscontract, &example::consume); 98 | return; 99 | } 100 | 101 | if (code != receiver) return; 102 | 103 | switch (action) { 104 | EOSIO_API(example, (receipt)) 105 | }; 106 | eosio_exit(0); 107 | } 108 | } 109 | ``` 110 | 111 | ### Token Exchange 112 | 113 | [![tokenpocket](https://raw.githubusercontent.com/Dappub/logo/master/partners/tpdex-small.png)](https://tpdex.io/#/) 114 | 115 | ![bitportal](https://raw.githubusercontent.com/Dappub/logo/master/partners/bitportal.png) 116 | 117 | ![51token](https://raw.githubusercontent.com/Dappub/logo/master/partners/51token.png) 118 | 119 | ### Block Explorer 120 | 121 | [![eospark](https://raw.githubusercontent.com/Dappub/tokendapppub/master/logo/EOSpark.png)](https://eospark.com/MainNet/account/tokendapppub) 122 | 123 | [![eosflare](https://raw.githubusercontent.com/Dappub/tokendapppub/master/logo/eosflare-logo-512.png)](https://eosflare.io/account/tokendapppub) 124 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required( VERSION 3.5 ) 2 | 3 | include(ExternalProject) 4 | find_package(Git REQUIRED) 5 | include(GNUInstallDirs) 6 | 7 | ExternalProject_Add( 8 | eosio.contracts 9 | CMAKE_ARGS -DCMAKE_BUILD_TYPE=${TEST_BUILD_TYPE} -DEOSIO_ROOT=${EOSIO_ROOT} -DEOSIO_DEPENDENCY=${EOSIO_DEPENDENCY} 10 | 11 | SOURCE_DIR ${CMAKE_SOURCE_DIR}/eosio.contracts 12 | BINARY_DIR ${CMAKE_BINARY_DIR}/eosio.contracts 13 | BUILD_ALWAYS 0 14 | 15 | EXCLUDE_FROM_ALL TRUE 16 | 17 | GIT_REPOSITORY git@github.com:eosio/eosio.contracts.git 18 | GIT_TAG v1.2.0 19 | 20 | BUILD_COMMAND make eosio.sudo.wasm eosio.system.wasm eosio.token.wasm eosio.msig.wasm contracts_unit_tests 21 | TEST_COMMAND "" 22 | INSTALL_COMMAND ln -sf ${CMAKE_BINARY_DIR}/eosio.contracts/tests/contracts.hpp ${CMAKE_BINARY_DIR}/ 23 | ) 24 | 25 | list(APPEND CMAKE_MODULE_PATH ${EOSIO_ROOT}/lib/cmake) 26 | include(EosioTester) 27 | 28 | ### check the version of EOSIO 29 | string(FIND "${EOSIO_VERSION}" "${EOSIO_DEPENDENCY}" output) 30 | if (NOT "${output}" EQUAL 0) 31 | message(FATAL_ERROR "Incorrect EOSIO version, please use version ${EOSIO_DEPENDENCY}.x") 32 | endif() 33 | 34 | enable_testing() 35 | 36 | configure_file(${CMAKE_SOURCE_DIR}/tokendapppub_test.hpp.in ${CMAKE_BINARY_DIR}/tokendapppub_test.hpp) 37 | 38 | include_directories(${CMAKE_BINARY_DIR}) 39 | 40 | file(GLOB UNIT_TESTS "*.cpp" "*.hpp" ) 41 | 42 | add_eosio_test(unit_test ${UNIT_TESTS}) 43 | 44 | add_dependencies(unit_test eosio.contracts) -------------------------------------------------------------------------------- /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 | #include "eosio.contracts/tests/eosio.system_tester.hpp" 14 | 15 | using namespace eosio_system; 16 | #define BOOST_TEST_STATIC_LINK 17 | 18 | void translate_fc_exception(const fc::exception &e) { 19 | std::cerr << "\033[33m" << e.to_detail_string() << "\033[0m" << std::endl; 20 | BOOST_TEST_FAIL("Caught Unexpected Exception"); 21 | } 22 | 23 | boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { 24 | // Turn off blockchain logging if no --verbose parameter is not added 25 | // To have verbose enabled, call "tests/chain_test -- --verbose" 26 | bool is_verbose = false; 27 | std::string verbose_arg = "--verbose"; 28 | for (int i = 0; i < argc; i++) { 29 | if (verbose_arg == argv[i]) { 30 | is_verbose = true; 31 | break; 32 | } 33 | } 34 | if(!is_verbose) fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::off); 35 | 36 | // Register fc::exception translator 37 | boost::unit_test::unit_test_monitor.template register_exception_translator(&translate_fc_exception); 38 | 39 | std::srand(time(NULL)); 40 | std::cout << "Random number generator seeded to " << time(NULL) << std::endl; 41 | return nullptr; 42 | } -------------------------------------------------------------------------------- /tests/tokendapppub_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Runtime/Runtime.h" 5 | #include "eosio.contracts/tests/eosio.system_tester.hpp" 6 | 7 | #include 8 | #include "tokendapppub_test.hpp" 9 | 10 | using namespace eosio::testing; 11 | using namespace eosio; 12 | using namespace eosio::chain; 13 | using namespace eosio::testing; 14 | using namespace fc; 15 | using namespace std; 16 | 17 | using mvo = fc::mutable_variant_object; 18 | 19 | #define SN(X) (string_to_symbol(0, #X) >> 8) 20 | 21 | const account_name GOD_ACCOUNT = N(godofdapppub); 22 | const symbol_name PUB_SYMBOL_NAME = SN(PUB); 23 | const symbol_type PUB_SYMBOL = S(4, PUB); 24 | const asset NEW_GAME_CONSOME = asset(1000000, PUB_SYMBOL); 25 | 26 | class tokendapppub_test : public eosio_system::eosio_system_tester { 27 | public: 28 | 29 | tokendapppub_test() { 30 | produce_blocks(2); 31 | 32 | const asset large_asset = core_from_string("80.0000"); 33 | create_account_with_resources( N(tokendapppub), config::system_account_name, core_from_string("10.0000"), false, large_asset, large_asset ); 34 | 35 | produce_blocks(2); 36 | 37 | set_code( N(tokendapppub), tokendapppub_wasm() ); 38 | set_abi( N(tokendapppub), tokendapppub_abi().data() ); 39 | 40 | produce_blocks(); 41 | 42 | set_authority( 43 | N(tokendapppub), 44 | "active", 45 | authority( 46 | 1, 47 | vector{{get_private_key("eosio", "active").get_public_key(), 1}}, 48 | vector{{{N(tokendapppub), config::eosio_code_name}, 1}} 49 | ), 50 | "owner", 51 | { { N(tokendapppub), "active" } }, { get_private_key( N(tokendapppub), "active" ) } 52 | ); 53 | 54 | const auto& accnt = control->db().get( N(tokendapppub) ); 55 | abi_def abi; 56 | BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); 57 | abi_ser.set_abi(abi, abi_serializer_max_time); 58 | } 59 | 60 | void transfer( name from, name to, const asset& amount, string memo, name manager = config::system_account_name ) { 61 | base_tester::push_action(N(eosio.token), N(transfer), manager, mvo() 62 | ("from", from) 63 | ("to", to) 64 | ("quantity", amount) 65 | ("memo", memo) 66 | ); 67 | } 68 | 69 | action_result hellodapppub(asset base_eos_quantity, asset maximum_stake, asset option_quantity, uint32_t lock_up_period, uint8_t base_fee_percent, uint8_t init_fee_percent) { 70 | return base_tester::push_action(N(tokendapppub), N(hellodapppub), 71 | vector{{N(godofdapppub),config::active_name}}, 72 | mvo() 73 | ("base_eos_quantity", base_eos_quantity) 74 | ("maximum_stake", maximum_stake) 75 | ("option_quantity", option_quantity) 76 | ("lock_up_period", lock_up_period) 77 | ("base_fee_percent", base_fee_percent) 78 | ("init_fee_percent", init_fee_percent) 79 | ) 80 | } 81 | 82 | abi_serializer abi_ser; 83 | }; 84 | 85 | BOOST_AUTO_TEST_SUITE(tokendapppub_tests) 86 | 87 | BOOST_FIXTURE_TEST_CASE( newtoken_tests, tokendapppub_test ) try { 88 | const asset large_asset = core_from_string("80.0000"); 89 | create_account_with_resources( N(godofdapppub), config::system_account_name, core_from_string("10.0000"), false, large_asset, large_asset ); 90 | produce_blocks(); 91 | BOOST_REQUIRE_EQUAL(success(), hellodapppub(core_from_string("10000.0000"), asset(100000000, PUB_SYMBOL), asset(0, PUB_SYMBOL), 0, 0, 0)); 92 | produce_blocks(); 93 | } FC_LOG_AND_RETHROW() 94 | 95 | BOOST_AUTO_TEST_SUITE_END() -------------------------------------------------------------------------------- /tests/tokendapppub_test.hpp.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | static std::vector tokendapppub_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../tokendapppub.wasm"); } 5 | static std::vector tokendapppub_abi() { return read_abi("${CMAKE_BINARY_DIR}/../tokendapppub.abi"); } -------------------------------------------------------------------------------- /tokendapppub.buy_rc.md: -------------------------------------------------------------------------------- 1 | # CONTRACT FOR tokendapppub::buy 2 | ## ACTION NAME: buy 3 | 4 | ### Parameters 5 | Input parameters: 6 | 7 | * `from` (user who want to buy some token) 8 | * `to` (must be your contract name) 9 | * `quantity` (CORE token tranfered amount) 10 | * `memo` (to specify the token name) 11 | 12 | ### Intent 13 | INTENT. The main purpose of this contract is to help users buy token. 14 | 15 | ### Term 16 | TERM. This Contract expires at the conclusion of code execution. -------------------------------------------------------------------------------- /tokendapppub.claim_rc.md: -------------------------------------------------------------------------------- 1 | CONTRACT FOR tokendapppub::claim 2 | 3 | ## ACTION NAME: claim 4 | 5 | ### Parameters 6 | Input parameters: 7 | 8 | * `name` (token name) 9 | * `sell` (whether to sell) 10 | 11 | ### Intent 12 | The primary purpose of this contract is to help DApp project owners claim their expired options. After the project owners claim their options, they could choose to sell the tokens as how the ordinary users do. 13 | 14 | ### Term 15 | TERM. This Contract expires at the conclusion of code execution. 16 | -------------------------------------------------------------------------------- /tokendapppub.consume_rc.md: -------------------------------------------------------------------------------- 1 | # CONTRACT FOR tokendapppub::consume 2 | 3 | ## ACTION NAME: consume 4 | 5 | ### Parameters 6 | Input parameters: 7 | 8 | * `from` (token consumer) 9 | * `quantity` (token consumption amount) 10 | * `memo` (DApp project owner will be notified of the "memo" info and used it as parameters for convenience) 11 | 12 | ### Intent 13 | INTENT. The main purpose of this contract is to help users consume the token. During the exection process, DApp project owner will be notified of the consumed token amount and "memo" info. When users comsume this specific token, the total supply of this token still remains the same, but the current circulating supply will be reduced. Others users will benefit from this token consumption behaviour as their holding tokens will be appreciated. 14 | 15 | ### Term 16 | TERM. This Contract expires at the conclusion of code execution. 17 | -------------------------------------------------------------------------------- /tokendapppub.cpp: -------------------------------------------------------------------------------- 1 | #include "tokendapppub.hpp" 2 | 3 | void tokendapppub::detail(string tokenname, string dappname, string logo, string website, 4 | string social, string community, string medium, string github, account_name contract, string intro) { 5 | symbol_name name = _string_to_symbol_name(tokenname.c_str()); 6 | tb_games game_sgt(_self, name); 7 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 8 | st_game game = game_sgt.get(); 9 | require_auth(game.owner); 10 | 11 | eosio_assert(intro.size() <= 1024, "memo has more than 1024 bytes"); 12 | } 13 | 14 | void tokendapppub::create(account_name issuer, asset maximum_supply) { 15 | require_auth(issuer); 16 | 17 | auto sym = maximum_supply.symbol; 18 | eosio_assert( sym.is_valid(), "invalid symbol name" ); 19 | eosio_assert( maximum_supply.is_valid(), "invalid supply"); 20 | eosio_assert( maximum_supply.amount > 0, "max-supply must be positive"); 21 | 22 | tb_games game_sgt(_self, sym.name()); 23 | eosio_assert(game_sgt.exists(), "game not found by this symbol name"); 24 | st_game game = game_sgt.get(); 25 | eosio_assert(game.symbol == sym, "symbol precision mismatch"); 26 | eosio_assert(game.owner == issuer, "issuer is not the owner of this token"); 27 | eosio_assert(game.base_stake - game.deserved_option + game.base_option == maximum_supply.amount, "invalid maximum supply"); 28 | 29 | stats statstable(_self, sym.name()); 30 | auto existing = statstable.find(sym.name()); 31 | eosio_assert(existing == statstable.end(), "token with symbol already exists" ); 32 | 33 | statstable.emplace(issuer, [&]( auto& s) { 34 | s.supply.symbol = maximum_supply.symbol; 35 | s.max_supply = maximum_supply; 36 | s.issuer = issuer; 37 | }); 38 | } 39 | 40 | void tokendapppub::issue(account_name to, asset quantity, string memo) { 41 | auto sym = quantity.symbol; 42 | eosio_assert( sym.is_valid(), "invalid symbol name" ); 43 | eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" ); 44 | 45 | auto sym_name = sym.name(); 46 | stats statstable( _self, sym_name ); 47 | auto existing = statstable.find( sym_name ); 48 | eosio_assert( existing != statstable.end(), "token with symbol does not exist, create token before issue" ); 49 | const auto& st = *existing; 50 | 51 | require_auth( st.issuer ); 52 | eosio_assert( quantity.is_valid(), "invalid quantity" ); 53 | eosio_assert( quantity.amount > 0, "must issue positive quantity" ); 54 | 55 | eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); 56 | eosio_assert( quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply"); 57 | 58 | statstable.modify( st, 0, [&]( auto& s ) { 59 | s.supply += quantity; 60 | }); 61 | } 62 | 63 | void tokendapppub::reg(account_name from, string memo) { 64 | require_auth(from); 65 | eosio_assert(memo.length() <= 7, "invalid memo format"); 66 | symbol_name name = string_to_symbol(0, memo.c_str()) >> 8; 67 | 68 | eosio_assert(!check_action_locked(name, N(reg)) || check_bypass_lock(name, N(reg), from), "reg action locked by owner"); 69 | 70 | tb_games game_sgt(_self, name); 71 | eosio_assert(game_sgt.exists(), "token not found by this symbol name"); 72 | 73 | accounts from_player(_self, from); 74 | auto player_itr = from_player.find(name); 75 | if (player_itr == from_player.end()) { 76 | from_player.emplace(from, [&](auto& rt){ 77 | rt.balance = asset(0, game_sgt.get().symbol); 78 | }); 79 | } 80 | } 81 | 82 | void tokendapppub::buy(account_name from, account_name to, asset quantity, string memo) { 83 | if ((from == _self) || (to != _self) || (from == BANK_RESERVES) || (from == IBANK_ACCOUNT)) { 84 | return; 85 | } 86 | eosio_assert(quantity.symbol == CORE_SYMBOL, "must pay with CORE token"); 87 | 88 | if (memo.find("-profit") != string::npos) { 89 | auto first_separator_pos = memo.find("-profit"); 90 | eosio_assert(first_separator_pos != string::npos, "invalid memo format for profit"); 91 | 92 | string name_str = memo.substr(0, first_separator_pos); 93 | eosio_assert(name_str.length() <= 7, "invalid symbol name"); 94 | symbol_name name = string_to_symbol(0, name_str.c_str()) >> 8; 95 | 96 | game_profit(name, quantity.amount); 97 | 98 | return; 99 | } 100 | 101 | string referrer_account_str = ""; 102 | symbol_name name; 103 | string referrer_str = "-referrer:"; 104 | if (memo.find(referrer_str) != string::npos) { 105 | auto first_separator_pos = memo.find(referrer_str); 106 | string name_str = memo.substr(0, first_separator_pos); 107 | eosio_assert(name_str.length() <= 7, "invalid symbol name"); 108 | name = string_to_symbol(0, name_str.c_str()) >> 8; 109 | 110 | referrer_account_str = memo.substr(first_separator_pos+referrer_str.length()); 111 | eosio_assert(referrer_account_str.length() <= 12, "invalid referrer account name"); 112 | } else { 113 | eosio_assert(memo.length() <= 7, "invalid memo format"); 114 | name = string_to_symbol(0, memo.c_str()) >> 8; 115 | } 116 | 117 | eosio_assert(!check_action_locked(name, N(buy)) || check_bypass_lock(name, N(buy), from), "buy action locked by owner"); 118 | 119 | int32_t fee = 0; 120 | tb_refer refer_sgt(_self, name); 121 | if (refer_sgt.exists()) { 122 | fee = refer_sgt.get().fee(quantity.amount); 123 | eosio_assert(quantity.amount - fee > 0, "refer fee must be less than amount of eos"); 124 | 125 | if (fee > 0) { 126 | tb_games game_sgt(_self, name); 127 | eosio_assert(game_sgt.exists(), "game not found by this symbol name"); 128 | account_name referrer_account; 129 | if (referrer_account_str == "") { 130 | referrer_account = game_sgt.get().owner; 131 | } else { 132 | referrer_account = string_to_name(referrer_account_str.c_str()); 133 | if (referrer_account == from) { 134 | referrer_account = game_sgt.get().owner; 135 | } 136 | if (referrer_account != game_sgt.get().owner) { 137 | tb_ref ref_sgt(_self, name); 138 | if (ref_sgt.exists() && !ref_sgt.get().opened()) { 139 | if (!check_referrer_in_whitelist(name, referrer_account)) { 140 | referrer_account = game_sgt.get().owner; 141 | } 142 | } 143 | } 144 | } 145 | action( 146 | permission_level{_self, N(active)}, 147 | N(eosio.token), 148 | N(transfer), 149 | make_tuple(_self, referrer_account, asset(fee), string("tokendapppub refer fee https://dapp.pub")) 150 | ).send(); 151 | } 152 | } 153 | 154 | asset stake_quantity = game_buy(name, quantity.amount - fee); 155 | 156 | accounts from_player(_self, from); 157 | auto player_itr = from_player.find(name); 158 | if (player_itr == from_player.end()) { 159 | from_player.emplace(from, [&](auto& rt){ 160 | rt.balance = stake_quantity; 161 | }); 162 | } else { 163 | from_player.modify(player_itr, 0, [&](auto& rt){ 164 | rt.balance += stake_quantity; 165 | }); 166 | } 167 | 168 | action( 169 | permission_level{_self, N(active)}, 170 | _self, 171 | N(receipt), 172 | make_tuple(from, string("buy"), quantity, stake_quantity, asset(fee)) 173 | ).send(); 174 | } 175 | 176 | void tokendapppub::sell(account_name from, asset quantity) { 177 | require_auth(from); 178 | accounts from_player(_self, from); 179 | auto player_itr = from_player.find(quantity.symbol.name()); 180 | eosio_assert(player_itr != from_player.end(), "account not found"); 181 | eosio_assert(quantity.symbol == player_itr->balance.symbol, "symbol precision mismatch"); 182 | eosio_assert((quantity.amount > 0) && (quantity.amount <= player_itr->balance.amount), "invalid amount"); 183 | 184 | eosio_assert(!check_action_locked(quantity.symbol.name(), N(sell)) || check_bypass_lock(quantity.symbol.name(), N(sell), from), "sell action locked by owner"); 185 | 186 | asset eos_quantity, all_quantity; 187 | tie(eos_quantity, all_quantity) = game_sell(quantity.symbol.name(), quantity.amount); 188 | eosio_assert(eos_quantity.amount > 0, "selled eos amount should be greater than 0"); 189 | 190 | action( 191 | permission_level{_self, N(active)}, 192 | N(eosio.token), 193 | N(transfer), 194 | make_tuple(_self, from, eos_quantity, string("tokendapppub withdraw https://dapp.pub")) 195 | ).send(); 196 | 197 | from_player.modify(player_itr, 0, [&](auto& rt){ 198 | rt.balance -= quantity; 199 | }); 200 | 201 | if (player_itr->balance.amount == 0) { 202 | from_player.erase(player_itr); 203 | } 204 | 205 | action( 206 | permission_level{_self, N(active)}, 207 | _self, 208 | N(receipt), 209 | make_tuple(from, string("sell"), quantity, all_quantity, all_quantity-eos_quantity) 210 | ).send(); 211 | } 212 | 213 | void tokendapppub::consume(account_name from, asset quantity, string memo) { 214 | require_auth(from); 215 | accounts from_player(_self, from); 216 | auto player_itr = from_player.find(quantity.symbol.name()); 217 | eosio_assert(player_itr != from_player.end(), "player not found"); 218 | eosio_assert((quantity.amount > 0) && (quantity.amount <= player_itr->balance.amount), "not enough balance to consume"); 219 | eosio_assert(quantity.symbol == player_itr->balance.symbol, "symbol precision mismatch"); 220 | 221 | eosio_assert(!check_action_locked(quantity.symbol.name(), N(consume)) || check_bypass_lock(quantity.symbol.name(), N(consume), from), "consume action locked by owner"); 222 | 223 | game_consume(quantity.symbol.name(), quantity.amount); 224 | 225 | from_player.modify(player_itr, 0, [&](auto& rt){ 226 | rt.balance -= quantity; 227 | }); 228 | 229 | if (player_itr->balance.amount == 0) { 230 | from_player.erase(player_itr); 231 | } 232 | } 233 | 234 | void tokendapppub::claim(string name_str, bool sell) { 235 | symbol_name name = _string_to_symbol_name(name_str.c_str()); 236 | tb_games game_sgt(_self, name); 237 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 238 | st_game game = game_sgt.get(); 239 | require_auth(game.owner); 240 | 241 | asset stake_quantity = game_claim(name); 242 | 243 | accounts from_player(_self, game.owner); 244 | auto player_itr = from_player.find(name); 245 | if (player_itr == from_player.end()) { 246 | from_player.emplace(game.owner, [&](auto& rt){ 247 | rt.balance = stake_quantity; 248 | }); 249 | } else { 250 | from_player.modify(player_itr, 0, [&](auto& rt){ 251 | rt.balance += stake_quantity; 252 | }); 253 | } 254 | 255 | if (sell) { 256 | player_itr = from_player.find(name); 257 | eosio_assert(player_itr != from_player.end(), "WTF!"); 258 | this->sell(game.owner, player_itr->balance); 259 | } 260 | } 261 | 262 | void tokendapppub::transfer(account_name from, account_name to, asset quantity, string memo) { 263 | eosio_assert(from != to, "cannot transfer to self"); 264 | require_auth(from); 265 | eosio_assert(is_account(to), "to account does not exist"); 266 | auto sym = quantity.symbol.name(); 267 | 268 | tb_games game_sgt(_self, sym); 269 | eosio_assert(game_sgt.exists(), "game not found by this symbol name"); 270 | st_game game = game_sgt.get(); 271 | eosio_assert(quantity.is_valid(), "invalid quantity"); 272 | eosio_assert(quantity.amount > 0, "must transfer positive quantity"); 273 | eosio_assert(quantity.symbol == game.symbol, "symbol precision mismatch"); 274 | eosio_assert(memo.size() <= 256, "memo has more than 256 bytes"); 275 | 276 | eosio_assert(!check_action_locked(sym, N(transfer)) || check_bypass_lock(sym, N(transfer), from, to), "transfer action locked by owner"); 277 | 278 | tb_trans trans_sgt(_self, sym); 279 | if (trans_sgt.exists() && !trans_sgt.get().transactable()) { 280 | eosio_assert(check_in_whitelist(sym, from) || check_in_whitelist(sym, to), "should only transfer token with account in the whitelist"); 281 | } 282 | 283 | require_recipient( from ); 284 | require_recipient( to ); 285 | 286 | accounts from_player(_self, from); 287 | auto player_itr = from_player.find(quantity.symbol.name()); 288 | eosio_assert(player_itr != from_player.end(), "no balance object found by from account"); 289 | eosio_assert(player_itr->balance.amount >= quantity.amount, "overdrawn balance" ); 290 | from_player.modify(player_itr, 0, [&](auto& rt){ 291 | rt.balance -= quantity; 292 | }); 293 | 294 | if (player_itr->balance.amount == 0) { 295 | from_player.erase(player_itr); 296 | } 297 | 298 | accounts to_player(_self, to); 299 | auto to_player_itr = to_player.find(quantity.symbol.name()); 300 | if (to_player_itr == to_player.end()) { 301 | to_player.emplace(from, [&](auto& rt){ 302 | rt.balance = quantity; 303 | }); 304 | } else { 305 | to_player.modify(to_player_itr, 0, [&](auto& rt){ 306 | rt.balance += quantity; 307 | }); 308 | } 309 | } 310 | 311 | void tokendapppub::destroy(string name_str) { 312 | symbol_name name = _string_to_symbol_name(name_str.c_str()); 313 | tb_games game_sgt(_self, name); 314 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 315 | st_game game = game_sgt.get(); 316 | require_auth(game.owner); 317 | 318 | eosio_assert(game.base_stake == game.stake, "all stake should be retrieved before erasing game"); 319 | game_sgt.remove(); 320 | 321 | stats statstable(_self, name); 322 | auto existing = statstable.find(name); 323 | // some token issued before this contract compatible with eosio.token 324 | if (existing != statstable.end()) { 325 | statstable.erase(existing); 326 | } 327 | 328 | tb_refer refer_sgt(_self, name); 329 | if (refer_sgt.exists()) { 330 | refer_sgt.remove(); 331 | } 332 | } 333 | 334 | void tokendapppub::hellodapppub(asset base_eos_quantity, asset maximum_stake, asset option_quantity, 335 | uint32_t lock_up_period, 336 | uint8_t base_fee_percent, uint8_t init_fee_percent, uint64_t refer_fee, uint32_t start_time) { 337 | require_auth(GOD_ACCOUNT); 338 | new_game(GOD_ACCOUNT, base_eos_quantity, maximum_stake, option_quantity, lock_up_period, base_fee_percent, init_fee_percent, start_time); 339 | set_refer_fee(maximum_stake.symbol.name(), refer_fee, GOD_ACCOUNT); 340 | 341 | SEND_INLINE_ACTION(*this, create, {GOD_ACCOUNT, N(active)}, {GOD_ACCOUNT, maximum_stake}); 342 | SEND_INLINE_ACTION(*this, issue, {GOD_ACCOUNT, N(active)}, {GOD_ACCOUNT, maximum_stake, string("")}); 343 | } 344 | 345 | void tokendapppub::newtoken(account_name from, asset base_eos_quantity, asset maximum_stake, asset option_quantity, 346 | uint32_t lock_up_period, 347 | uint8_t base_fee_percent, uint8_t init_fee_percent, uint64_t refer_fee, uint32_t start_time) { 348 | require_auth(from); 349 | asset fee = NEW_GAME_CONSOME; 350 | if (maximum_stake.symbol.name_length() <= 3) { 351 | fee = NEW_GAME_CONSOME * pow(10, 4 - maximum_stake.symbol.name_length()); 352 | } 353 | this->consume(from, fee, "consume for new token"); 354 | new_game(from, base_eos_quantity, maximum_stake, option_quantity, lock_up_period, base_fee_percent, init_fee_percent, start_time); 355 | set_refer_fee(maximum_stake.symbol.name(), refer_fee, from); 356 | 357 | SEND_INLINE_ACTION(*this, create, {from, N(active)}, {from, maximum_stake}); 358 | SEND_INLINE_ACTION(*this, issue, {from, N(active)}, {from, maximum_stake, string("")}); 359 | } 360 | 361 | void tokendapppub::setreferfee(string name_str, uint64_t refer_fee) { 362 | symbol_name name = _string_to_symbol_name(name_str.c_str()); 363 | tb_games game_sgt(_self, name); 364 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 365 | st_game game = game_sgt.get(); 366 | require_auth(game.owner); 367 | 368 | set_refer_fee(name, refer_fee, game.owner); 369 | } 370 | 371 | void tokendapppub::settrans(string name_str, uint64_t trans) { 372 | symbol_name name = _string_to_symbol_name(name_str.c_str()); 373 | tb_games game_sgt(_self, name); 374 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 375 | st_game game = game_sgt.get(); 376 | require_auth(game.owner); 377 | 378 | set_trans(name, trans, game.owner); 379 | } 380 | 381 | void tokendapppub::setsellfee(string name_str, uint64_t base_fee_percent, uint64_t init_fee_percent) { 382 | symbol_name name = _string_to_symbol_name(name_str.c_str()); 383 | tb_games game_sgt(_self, name); 384 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 385 | st_game game = game_sgt.get(); 386 | require_auth(game.owner); 387 | eosio_assert((base_fee_percent >= 0) && (base_fee_percent <= 99), "invalid fee percent"); 388 | eosio_assert((init_fee_percent >= base_fee_percent) && (init_fee_percent <=99), "invalid init fee percent"); 389 | game.base_fee_percent = base_fee_percent; 390 | game.init_fee_percent = init_fee_percent; 391 | game_sgt.set(game, game.owner); 392 | } 393 | 394 | void tokendapppub::addtowl(string name_str, account_name agent) { 395 | symbol_name name = _string_to_symbol_name(name_str.c_str()); 396 | tb_games game_sgt(_self, name); 397 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 398 | st_game game = game_sgt.get(); 399 | require_auth(game.owner); 400 | 401 | add_agent_to_whitelist(name, agent, game.owner); 402 | } 403 | 404 | void tokendapppub::setref(string name_str, uint64_t ref) { 405 | symbol_name name = _string_to_symbol_name(name_str.c_str()); 406 | tb_games game_sgt(_self, name); 407 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 408 | st_game game = game_sgt.get(); 409 | require_auth(game.owner); 410 | 411 | set_ref(name, ref, game.owner); 412 | } 413 | 414 | void tokendapppub::addreftowl(string name_str, account_name referrer) { 415 | symbol_name name = _string_to_symbol_name(name_str.c_str()); 416 | tb_games game_sgt(_self, name); 417 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 418 | st_game game = game_sgt.get(); 419 | require_auth(game.owner); 420 | 421 | add_referrer_to_whitelist(name, referrer, game.owner); 422 | } 423 | 424 | void tokendapppub::lock(string name_str, vector actions) { 425 | symbol_name name = _string_to_symbol_name(name_str.c_str()); 426 | tb_games game_sgt(_self, name); 427 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 428 | st_game game = game_sgt.get(); 429 | require_auth(game.owner); 430 | 431 | set_lock_actions(name, actions, game.owner); 432 | } 433 | 434 | void tokendapppub::setactionwl(string name_str, action_name action, vector from, vector to) { 435 | symbol_name name = _string_to_symbol_name(name_str.c_str()); 436 | tb_games game_sgt(_self, name); 437 | eosio_assert(game_sgt.exists(), "token not found by this symbol_name"); 438 | st_game game = game_sgt.get(); 439 | require_auth(game.owner); 440 | 441 | set_action_whitelist(name, action, from, to, game.owner); 442 | } 443 | 444 | void tokendapppub::receipt(account_name from, string type, asset in, asset out, asset fee) { 445 | require_auth(_self); 446 | } 447 | 448 | extern "C" { 449 | void apply( uint64_t receiver, uint64_t code, uint64_t action ) { 450 | tokendapppub thiscontract(receiver); 451 | 452 | if((code == N(eosio.token)) && (action == N(transfer))) { 453 | execute_action(&thiscontract, &tokendapppub::buy); 454 | return; 455 | } 456 | 457 | if (code != receiver) return; 458 | 459 | switch (action) { 460 | EOSIO_API(tokendapppub, (setactionwl)(lock)(setref)(addreftowl)(addtowl)(settrans)(setreferfee)(detail)(issue)(create)(reg)(receipt)(transfer)(sell)(consume)(destroy)(claim)(newtoken)(hellodapppub)) 461 | }; 462 | eosio_exit(0); 463 | } 464 | } -------------------------------------------------------------------------------- /tokendapppub.destroy_rc.md: -------------------------------------------------------------------------------- 1 | # CONTRACT FOR tokendapppub::destroy 2 | 3 | ## ACTION NAME: destroy 4 | 5 | ### Parameters 6 | Input parameters: 7 | 8 | * `name` (token name) 9 | 10 | ### Intent 11 | Intent. The main purpose of this contract is to help the DApp project owner destroy the token. Before the project owner destroys, it is necessary to confirm that all users have sold their token, otherwise it will be impossible to destroy if there are still users holding the token. 12 | 13 | ### Term 14 | TERM. This Contract expires at the conclusion of code execution. -------------------------------------------------------------------------------- /tokendapppub.hellodapppub_rc.md: -------------------------------------------------------------------------------- 1 | # CONTRACT FOR tokendapppub::hellodapppub 2 | 3 | ## ACTION NAME: hellodapppub 4 | 5 | ### Parameters 6 | Input parameters: 7 | 8 | * `base_eos_quantity` (basic EOS fund pool) 9 | * `maximum_stake` (maximum token supply) 10 | * `option_quantity` (option amount for the token project owner) 11 | * `lock_up_period` (option lock period: seconds) 12 | * `base_fee_percent` (lowest token selling fee) 13 | * `init_fee_percent` (initial token selling fee) 14 | * `refer_fee` (referral fee when user buy the token) 15 | * `start_time` (time when the issuance of tokens) 16 | 17 | ### Intent 18 | INTENT. The main purpose of this contract is to create the platform token for the contract owner. 19 | 20 | ### Term 21 | TERM. This Contract expires at the conclusion of code execution. 22 | -------------------------------------------------------------------------------- /tokendapppub.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using namespace eosio; 13 | using namespace std; 14 | 15 | #define SN(X) (string_to_symbol(0, #X) >> 8) 16 | 17 | const account_name BANK_RESERVES = N(bankreserves); 18 | const account_name IBANK_ACCOUNT = N(bankcoinfund); 19 | const account_name GOD_ACCOUNT = N(godofdapppub); 20 | const symbol_name PUB_SYMBOL_NAME = SN(PUB); 21 | const symbol_type PUB_SYMBOL = S(4, PUB); 22 | const asset NEW_GAME_CONSOME = asset(1000000, PUB_SYMBOL); 23 | const uint32_t MAX_PERIOD = 100ul * 365 * 24 * 60 * 60; 24 | 25 | class tokendapppub: public contract { 26 | public: 27 | tokendapppub(account_name self): 28 | contract(self) 29 | {}; 30 | // for user 31 | void reg(account_name from, string memo); 32 | void buy(account_name from, account_name to, asset quantity, string memo); 33 | void sell(account_name from, asset quantity); 34 | void consume(account_name from, asset quantity, string memo); 35 | void transfer(account_name from, account_name to, asset quantity, string memo); 36 | void receipt(account_name from, string type, asset in, asset out, asset fee); 37 | // for god 38 | void hellodapppub(asset base_eos_quantity, asset maximum_stake, asset option_quantity, uint32_t lock_up_period, 39 | uint8_t base_fee_percent, uint8_t init_fee_percent, uint64_t refer_fee, uint32_t start_time); 40 | // for owner 41 | void newtoken(account_name from, asset base_eos_quantity, asset maximum_stake, asset option_quantity, 42 | uint32_t lock_up_period, uint8_t base_fee_percent, uint8_t init_fee_percent, 43 | uint64_t refer_fee, uint32_t start_time); 44 | void destroy(string name); 45 | void claim(string name, bool sell); 46 | void detail(string tokenname, string dappname, string logo, string website, string social, 47 | string community, string medium, string github, account_name contract, string memo); 48 | void setreferfee(string name_str, uint64_t refer_fee); 49 | void settrans(string name_str, uint64_t trans); 50 | void addtowl(string name_str, account_name agent); 51 | void setref(string name_str, uint64_t trans); 52 | void addreftowl(string name_str, account_name agent); 53 | void lock(string name_str, vector actions); 54 | void setactionwl(string name_str, action_name action, vector from, vector to); 55 | void setsellfee(string name_str, uint64_t base_fee_percent, uint64_t init_fee_percent); 56 | // for eosio.token 57 | void create(account_name issuer, asset maximum_supply); 58 | void issue(account_name to, asset quantity, string memo); 59 | private: 60 | // @abi table whitelist i64 61 | struct whitelist { 62 | account_name agent; 63 | uint64_t primary_key() const {return agent;} 64 | }; 65 | typedef eosio::multi_index tb_whitelist; 66 | 67 | void add_agent_to_whitelist(symbol_name name, account_name agent, account_name payer) { 68 | eosio_assert(is_account(agent), "agent account does not exist"); 69 | tb_whitelist whitelist(_self, name); 70 | whitelist.emplace(payer, [&](auto& rt){ 71 | rt.agent = agent; 72 | }); 73 | } 74 | 75 | bool check_in_whitelist(symbol_name name, account_name agent) { 76 | tb_whitelist whitelist(_self, name); 77 | auto itr = whitelist.find(agent); 78 | return itr != whitelist.end(); 79 | } 80 | 81 | // @abi table refwl i64 82 | struct refwh { 83 | account_name referrer; 84 | uint64_t primary_key() const {return referrer;} 85 | }; 86 | typedef eosio::multi_index tb_refwl; 87 | 88 | void add_referrer_to_whitelist(symbol_name name, account_name referrer, account_name payer) { 89 | eosio_assert(is_account(referrer), "referrer account does not exist"); 90 | tb_refwl refwh(_self, name); 91 | refwh.emplace(payer, [&](auto& rt){ 92 | rt.referrer = referrer; 93 | }); 94 | } 95 | 96 | bool check_referrer_in_whitelist(symbol_name name, account_name referrer) { 97 | tb_refwl refwh(_self, name); 98 | auto itr = refwh.find(referrer); 99 | return itr != refwh.end(); 100 | } 101 | 102 | // @abi table openref i64 103 | struct st_ref { 104 | uint64_t ref; 105 | bool opened() { 106 | return ref == 1; 107 | } 108 | }; 109 | typedef singleton tb_ref; 110 | 111 | void set_ref(symbol_name name, uint64_t ref, account_name payer) { 112 | eosio_assert(ref == 0 || ref == 1, "ref should be bool"); 113 | tb_ref ref_sgt(_self, name); 114 | ref_sgt.set(st_ref{ 115 | .ref = ref, 116 | }, payer); 117 | } 118 | 119 | // @abi table stat i64 120 | struct cur_stats { 121 | asset supply; 122 | asset max_supply; 123 | account_name issuer; 124 | 125 | uint64_t primary_key()const { return supply.symbol.name(); } 126 | }; 127 | typedef eosio::multi_index stats; 128 | 129 | symbol_name _string_to_symbol_name(const char* str) { 130 | return string_to_symbol(0, str) >> 8; 131 | } 132 | // @abi table games i64 133 | struct st_game { 134 | symbol_type symbol; 135 | account_name owner; 136 | int128_t base_eos; 137 | int64_t base_stake; 138 | int64_t base_option; 139 | int64_t deserved_option; 140 | int64_t claimed_option; 141 | int128_t eos; 142 | int64_t stake; 143 | uint32_t lock_up_period; 144 | uint32_t start_time; 145 | uint8_t base_fee_percent; 146 | uint8_t init_fee_percent; 147 | 148 | uint8_t _fee_percent() { 149 | if ((init_fee_percent == base_fee_percent) || (now() >= start_time + lock_up_period)) { 150 | return base_fee_percent; 151 | } 152 | const double INIT = init_fee_percent; 153 | const double BASE = base_fee_percent; 154 | const double NOW = now(); 155 | const double START = start_time; 156 | const double PERIOD = lock_up_period; 157 | 158 | return uint8_t(ceil(2 * PERIOD * (INIT - BASE) / ((NOW-START) + PERIOD) + 2 * BASE - INIT)); 159 | } 160 | 161 | void _profit_eos(int64_t eos_amount) { 162 | eosio_assert(eos_amount > 0, "amount of EOS profit should be bigger than 0"); 163 | eosio_assert(stake < base_stake, "cannot profit when no one holds stake"); 164 | const double BASE_STAKE = base_stake; 165 | const double BASE_EOS = base_eos; 166 | const double STAKE = stake; 167 | const double EOS = eos; 168 | const double PROFIT = eos_amount; 169 | eos = int128_t((EOS - BASE_EOS + PROFIT) * BASE_STAKE / (BASE_STAKE - STAKE)); 170 | base_eos = int128_t((EOS - BASE_EOS + PROFIT) * STAKE / (BASE_STAKE - STAKE)); 171 | } 172 | 173 | void _issue_stake(int64_t stake_amount) { 174 | eosio_assert(stake_amount > 0, "amount of stake issuance should be bigger than zero"); 175 | 176 | if (stake == base_stake) { 177 | base_stake += stake_amount; 178 | stake += stake_amount; 179 | claimed_option += stake_amount; 180 | return; 181 | } 182 | 183 | eosio_assert(stake < base_stake, "stake should be less than base_stake"); 184 | const double BASE_STAKE = base_stake; 185 | const double BASE_EOS = base_eos; 186 | const double STAKE = stake; 187 | const double EOS = eos; 188 | const double NEW_STAKE = stake_amount; 189 | eos = int128_t((BASE_STAKE + NEW_STAKE) * (EOS - BASE_EOS) / (BASE_STAKE + NEW_STAKE - STAKE)); 190 | base_eos = int128_t((STAKE) * (EOS - BASE_EOS) / (BASE_STAKE + NEW_STAKE - STAKE)); 191 | base_stake += stake_amount; 192 | } 193 | 194 | void _consume_stake(int64_t stake_amount) { 195 | eosio_assert(stake_amount > 0, "amount of comsumed stake should be bigger than zero"); 196 | eosio_assert(stake + stake_amount < base_stake, "cannot comsume all remaining stake"); 197 | const double BASE_STAKE = base_stake; 198 | const double BASE_EOS = base_eos; 199 | const double STAKE = stake; 200 | const double EOS = eos; 201 | const double CONSUME = stake_amount; 202 | eos = int128_t((EOS - BASE_EOS) * BASE_STAKE / (BASE_STAKE - STAKE - CONSUME)); 203 | base_eos = int128_t((EOS - BASE_EOS) * (STAKE + CONSUME) / (BASE_STAKE - STAKE - CONSUME)); 204 | stake += stake_amount; 205 | } 206 | 207 | void _update_option() { 208 | // no option or all options has been deserved. 209 | if ((base_option == 0) || (deserved_option == base_option)) { 210 | return; 211 | } 212 | 213 | int64_t last_deserved_option = deserved_option; 214 | 215 | const double NOW = now(); 216 | const double START = start_time; 217 | const double PERIOD = lock_up_period; 218 | const double BASE = base_option; 219 | if ((NOW - START) >= PERIOD) { 220 | deserved_option = base_option; 221 | } else { 222 | deserved_option = int64_t(BASE * (NOW - START) / PERIOD); 223 | } 224 | 225 | if (deserved_option == last_deserved_option) { 226 | return; 227 | } 228 | 229 | int64_t new_deserved_option = deserved_option - last_deserved_option; 230 | _issue_stake(new_deserved_option); 231 | } 232 | 233 | void _check() { 234 | eosio_assert(base_eos > 0, "failed to check base_eos should be bigger than zero"); 235 | eosio_assert(stake > 0, "failed to check stake should be bigger than zero"); 236 | eosio_assert(eos >= base_eos, "failed to check eos is bigger than base_eos"); 237 | eosio_assert(base_stake >= stake, "failed to check base_stake is bigger than stake"); 238 | } 239 | 240 | void consume(int64_t stake_amount) { 241 | _update_option(); 242 | _consume_stake(stake_amount); 243 | _check(); 244 | } 245 | 246 | void profit(int64_t eos_amount) { 247 | _update_option(); 248 | _profit_eos(eos_amount); 249 | _check(); 250 | } 251 | 252 | int64_t buy(int64_t eos_amount) { 253 | _update_option(); 254 | 255 | const double STAKE = double(stake); 256 | const double EOS = double(eos); 257 | const double IN = double(eos_amount); 258 | 259 | int64_t stake_amount = int64_t((IN*STAKE) / (IN+EOS)); 260 | 261 | eos += eos_amount; 262 | stake -= stake_amount; 263 | _check(); 264 | return stake_amount; 265 | } 266 | 267 | int64_t sell(int64_t stake_amount) { 268 | _update_option(); 269 | 270 | const double STAKE = double(stake); 271 | const double EOS = double(eos); 272 | const double IN = double(stake_amount); 273 | 274 | int64_t eos_amount = int64_t((IN*EOS)/(STAKE+IN)); 275 | 276 | eos -= eos_amount; 277 | stake += stake_amount; 278 | _check(); 279 | return eos_amount; 280 | } 281 | 282 | int64_t fee(int64_t eos_amount) { 283 | int64_t fee = 0; 284 | int64_t fee_percent = _fee_percent(); 285 | if (fee_percent > 0 && stake < base_stake) { 286 | fee = (eos_amount * fee_percent + 99) / 100; 287 | } 288 | return fee; 289 | } 290 | 291 | int64_t claim() { 292 | _update_option(); 293 | int64_t new_claimed = deserved_option - claimed_option; 294 | claimed_option = deserved_option; 295 | _check(); 296 | return new_claimed; 297 | } 298 | }; 299 | typedef singleton tb_games; 300 | 301 | void new_game(account_name owner, asset base_eos_quantity, asset maximum_stake, asset option_quantity, 302 | uint32_t lock_up_period, uint8_t base_fee_percent, uint8_t init_fee_percent, uint32_t start_time) { 303 | symbol_type symbol = maximum_stake.symbol; 304 | eosio_assert(symbol.is_valid(), "invalid symbol name"); 305 | eosio_assert(symbol == option_quantity.symbol, "maximum stake and option quantity should be the same symbol type"); 306 | 307 | tb_games game_sgt(_self, symbol.name()); 308 | eosio_assert(!game_sgt.exists(), "game has started before"); 309 | eosio_assert(base_eos_quantity.symbol == CORE_SYMBOL, "base_eos must be core token"); 310 | eosio_assert((base_eos_quantity.amount > 0) && (base_eos_quantity.amount <= 1000000000ll*10000), "invalid amount of base EOS pool"); 311 | eosio_assert(maximum_stake.is_valid(), "invalid maximum stake"); 312 | eosio_assert((maximum_stake.amount > 0) && (maximum_stake.amount <= 10000000000000ll*10000), "invalid amount of maximum supply"); 313 | eosio_assert((option_quantity.amount >= 0) && (option_quantity.amount <= maximum_stake.amount), "invalid amount of option"); 314 | eosio_assert(lock_up_period <= MAX_PERIOD, "invalid lock up period"); 315 | eosio_assert((base_fee_percent >= 0) && (base_fee_percent <= 99), "invalid fee percent"); 316 | eosio_assert((init_fee_percent >= base_fee_percent) && (init_fee_percent <=99), "invalid init fee percent"); 317 | eosio_assert(start_time <= now() + 180 * 24 * 60 * 60, "the token issuance must be within six months"); 318 | 319 | game_sgt.set(st_game{ 320 | .symbol = symbol, 321 | .owner = owner, 322 | .base_stake = maximum_stake.amount - option_quantity.amount, 323 | .base_eos = base_eos_quantity.amount, 324 | .base_option = option_quantity.amount, 325 | .deserved_option = 0, 326 | .claimed_option = 0, 327 | .lock_up_period = lock_up_period, 328 | .eos = base_eos_quantity.amount, 329 | .stake = maximum_stake.amount - option_quantity.amount, 330 | .base_fee_percent = base_fee_percent, 331 | .init_fee_percent = init_fee_percent, 332 | .start_time = max(start_time, now()), 333 | }, owner); 334 | } 335 | 336 | asset game_buy(symbol_name name, int64_t eos_amount) { 337 | eosio_assert(eos_amount > 0, "eos amount should be bigger than 0"); 338 | 339 | tb_games game_sgt(_self, name); 340 | eosio_assert(game_sgt.exists(), "game not found by this symbol name"); 341 | 342 | st_game game = game_sgt.get(); 343 | eosio_assert(now() > game.start_time, "the token issuance has not yet begun"); 344 | 345 | int64_t stake_amount = game.buy(eos_amount); 346 | 347 | eosio_assert(stake_amount > 0, "stake amount should be bigger than 0"); 348 | eosio_assert(stake_amount < game.base_stake, "stake amount overflow"); 349 | 350 | game_sgt.set(game, game.owner); 351 | 352 | return asset(stake_amount, game.symbol); 353 | } 354 | 355 | tuple game_sell(symbol_name name, int64_t stake_amount) { 356 | eosio_assert(stake_amount > 0, "stake amount should be bigger than 0"); 357 | 358 | tb_games game_sgt(_self, name); 359 | eosio_assert(game_sgt.exists(), "game not found by this symbol_name"); 360 | 361 | st_game game = game_sgt.get(); 362 | eosio_assert(now() > game.start_time, "the token issuance has not yet begun"); 363 | 364 | int64_t eos_amount = game.sell(stake_amount); 365 | eosio_assert(eos_amount > 0, "must reserve a positive amount"); 366 | 367 | int64_t eos_fee = game.fee(eos_amount); 368 | eosio_assert(eos_fee >= 0, "fee amount must be a Non-negative"); 369 | if (eos_fee > 0) { 370 | game.profit(eos_fee); 371 | } 372 | 373 | int64_t reserve_amount = eos_amount - eos_fee; 374 | eosio_assert(reserve_amount > 0, "must reserve a positive amount"); 375 | 376 | game_sgt.set(game, game.owner); 377 | 378 | return make_tuple(asset(reserve_amount, CORE_SYMBOL), asset(eos_amount, CORE_SYMBOL)); 379 | } 380 | 381 | asset game_claim(symbol_name name) { 382 | tb_games game_sgt(_self, name); 383 | eosio_assert(game_sgt.exists(), "game not found by this symbol_name"); 384 | st_game game = game_sgt.get(); 385 | eosio_assert(now() > game.start_time, "the token issuance has not yet begun"); 386 | 387 | int64_t claimed = game.claim(); 388 | eosio_assert(claimed > 0, "claimed stake should be bigger than zero"); 389 | game_sgt.set(game, game.owner); 390 | return asset(claimed, game.symbol); 391 | } 392 | 393 | void game_consume(symbol_name name, int64_t stake_amount) { 394 | eosio_assert(stake_amount > 0, "consume stake amount should be bigger than 0"); 395 | 396 | tb_games game_sgt(_self, name); 397 | eosio_assert(game_sgt.exists(), "game not found by this symbol_name"); 398 | 399 | st_game game = game_sgt.get(); 400 | eosio_assert(now() > game.start_time, "the token issuance has not yet begun"); 401 | eosio_assert(stake_amount < game.base_stake - game.stake, "consume too much stake"); 402 | 403 | game.consume(stake_amount); 404 | 405 | game_sgt.set(game, game.owner); 406 | 407 | require_recipient(game.owner); 408 | } 409 | 410 | void game_profit(symbol_name name, int64_t eos_amount) { 411 | eosio_assert(eos_amount > 0, "profit eos amount should be bigger than 0"); 412 | 413 | tb_games game_sgt(_self, name); 414 | eosio_assert(game_sgt.exists(), "game not found by this symbol_name"); 415 | 416 | st_game game = game_sgt.get(); 417 | eosio_assert(now() > game.start_time, "the token issuance has not yet begun"); 418 | eosio_assert(game.stake < game.base_stake, "cannot profit when no one holds stake"); 419 | 420 | game.profit(eos_amount); 421 | 422 | game_sgt.set(game, game.owner); 423 | } 424 | 425 | // @abi table refer i64 426 | struct st_refer { 427 | uint64_t fee_percent; 428 | 429 | int64_t fee(int64_t eos_amount) { 430 | return int64_t((eos_amount * fee_percent + 9999) / 10000); 431 | } 432 | }; 433 | typedef singleton tb_refer; 434 | 435 | void set_refer_fee(symbol_name name, uint64_t refer_fee, account_name payer) { 436 | eosio_assert(refer_fee < 10000, "invalid refer fee"); 437 | tb_refer refer_sgt(_self, name); 438 | eosio_assert(!refer_sgt.exists(), "cannot update exist refer fee"); 439 | refer_sgt.set(st_refer{ 440 | .fee_percent = refer_fee, 441 | }, payer); 442 | } 443 | 444 | // @abi table trans i64 445 | struct st_trans { 446 | uint64_t trans; 447 | bool transactable() { 448 | return trans == 1; 449 | } 450 | }; 451 | typedef singleton tb_trans; 452 | 453 | void set_trans(symbol_name name, uint64_t trans, account_name payer) { 454 | eosio_assert(trans == 0 || trans == 1, "trans should be bool"); 455 | tb_trans trans_sgt(_self, name); 456 | trans_sgt.set(st_trans{ 457 | .trans = trans, 458 | }, payer); 459 | } 460 | 461 | // @abi table accounts i64 462 | struct account { 463 | asset balance; 464 | uint64_t primary_key() const {return balance.symbol.name();} 465 | }; 466 | typedef multi_index accounts; 467 | 468 | // @abi table lockactions i64 469 | struct st_lock { 470 | symbol_name name; 471 | vector actions; 472 | 473 | bool has_action(const action_name action) { 474 | return find(actions.begin(), actions.end(), action) != actions.end(); 475 | } 476 | }; 477 | typedef singleton tb_lock; 478 | 479 | void set_lock_actions(symbol_name name, vector actions, account_name payer) { 480 | account_name lock_action_names[5] = {N(buy), N(sell), N(transfer), N(reg), N(consume)}; 481 | for (unsigned int i = 0; i < actions.size(); i++) { 482 | eosio_assert(find(begin(lock_action_names), end(lock_action_names), actions[i]) != end(lock_action_names), "unknwon action name."); 483 | } 484 | tb_lock lock_sgt(_self, name); 485 | lock_sgt.set(st_lock{ 486 | .name = name, 487 | .actions = actions, 488 | }, payer); 489 | } 490 | 491 | bool check_action_locked(symbol_name name, action_name action) { 492 | tb_lock lock_sgt(_self, name); 493 | if (!lock_sgt.exists()) { 494 | return false; 495 | } 496 | return lock_sgt.get().has_action(action); 497 | } 498 | 499 | // @abi table actionwl i64 500 | struct st_actionwl { 501 | action_name action; 502 | vector from; 503 | vector to; 504 | uint64_t primary_key() const { 505 | return action; 506 | } 507 | 508 | bool in_from(const account_name account) { 509 | return find(from.begin(), from.end(), account) != from.end(); 510 | } 511 | bool in_to(const account_name account) { 512 | return find(to.begin(), to.end(), account) != to.end(); 513 | } 514 | }; 515 | typedef multi_index tb_actionwls; 516 | 517 | void set_action_whitelist(symbol_name name, action_name action, vector from, vector to, account_name payer) { 518 | account_name lock_action_names[5] = {N(buy), N(sell), N(transfer), N(reg), N(consume)}; 519 | eosio_assert(find(begin(lock_action_names), end(lock_action_names), action) != end(lock_action_names), "unknwon action name."); 520 | 521 | tb_actionwls action_whitelists(_self, name); 522 | auto whitelist_itr = action_whitelists.find(action); 523 | if (whitelist_itr == action_whitelists.end()) { 524 | action_whitelists.emplace(payer, [&](auto& rt){ 525 | rt.action = action; 526 | rt.from = from; 527 | rt.to = to; 528 | }); 529 | } else { 530 | action_whitelists.modify(whitelist_itr, 0, [&](auto& rt){ 531 | rt.from = from; 532 | rt.to = to; 533 | }); 534 | } 535 | } 536 | 537 | bool check_bypass_lock(symbol_name name, action_name action, account_name from, account_name to) { 538 | tb_actionwls action_whitelists(_self, name); 539 | auto whitelist_itr = action_whitelists.find(action); 540 | if (whitelist_itr == action_whitelists.end()) { 541 | return false; 542 | } 543 | st_actionwl whitelist = *whitelist_itr; 544 | return whitelist.in_from(from) || whitelist.in_to(to); 545 | } 546 | 547 | bool check_bypass_lock(symbol_name name, action_name action, account_name from) { 548 | tb_actionwls action_whitelists(_self, name); 549 | auto whitelist_itr = action_whitelists.find(action); 550 | if (whitelist_itr == action_whitelists.end()) { 551 | return false; 552 | } 553 | st_actionwl whitelist = *whitelist_itr; 554 | return whitelist.in_from(from); 555 | } 556 | }; 557 | 558 | #ifdef ABIGEN 559 | EOSIO_ABI(tokendapppub, (setactionwl)(lock)(setref)(addreftowl)(addtowl)(settrans)(setreferfee)(detail)(issue)(create)(reg)(receipt)(transfer)(sell)(consume)(destroy)(claim)(newtoken)(hellodapppub)(setsellfee)) 560 | #endif -------------------------------------------------------------------------------- /tokendapppub.newtoken_rc.md: -------------------------------------------------------------------------------- 1 | #CONTRACT FOR tokendapppub::newtoken 2 | 3 | ## ACTION NAME: newtoken 4 | 5 | ### Parameters 6 | Input parameters: 7 | 8 | * `from` (token issuer) 9 | * `base_eos_quantity` (basic EOS fund pool) 10 | * `maximum_stake` (maxmiun token supply) 11 | * `option_quantity` (option amount for the token project owner) 12 | * `lock_up_period` (option lock period: seconds) 13 | * `base_fee_percent` (minimum token selling fee) 14 | * `init_fee_percent` (initial token selling fee) 15 | * `refer_fee` (referral fee when user buy the token) 16 | * `start_time` (time when the issuance of tokens) 17 | 18 | ### Intent 19 | INTENT. The main purpose of this contract is for the DApp prject teams to issue their own tokens, and it will cost the platform tokens in the meantime. 20 | 21 | ### Term 22 | TERM. This Contract expires at the conclusion of code execution. 23 | -------------------------------------------------------------------------------- /tokendapppub.receipt_rc.md: -------------------------------------------------------------------------------- 1 | # CONTRACT FOR tokendapppub::receipt 2 | 3 | ## ACTION NAME: receipt 4 | 5 | ### Parameters 6 | Input parameters: 7 | 8 | * `from` (user account name) 9 | * `type` (buy or sell type) 10 | * `in` (quantity of token transfer in) 11 | * `out` (quantity of token transfer out) 12 | 13 | ### Intent 14 | Intent. The main purpose of this contract is to help the user to get receipt feedback on action history. Only for buy and sell action now. 15 | 16 | ### Term 17 | TERM. This Contract expires at the conclusion of code execution. -------------------------------------------------------------------------------- /tokendapppub.reg_rc.md: -------------------------------------------------------------------------------- 1 | # CONTRACT FOR tokendapppub::reg 2 | ## ACTION NAME: reg 3 | 4 | ### Parameters 5 | Input parameters: 6 | 7 | * `from` (user who want to register to this Dapp) 8 | * `memo` (to specify the token name) 9 | 10 | ### Intent 11 | INTENT. The main purpose of this contract is to help users register the token. it will cost user 128 bytes RAM. 12 | 13 | ### Term 14 | TERM. This Contract expires at the conclusion of code execution. -------------------------------------------------------------------------------- /tokendapppub.sell_rc.md: -------------------------------------------------------------------------------- 1 | # CONTRACT FOR tokendapppub::sell 2 | 3 | ## ACTION NAME: sell 4 | 5 | ### Parameters 6 | * `from` (token seller) 7 | * `quantity` (token selling amount) 8 | 9 | ### Intent 10 | INTENT. The main purpose of this contract is to help users sell the token. The selling fee defined by the DApp project owner will be deducted from the token selling amount and will be distributed to the current token to increase its price. 11 | 12 | ### Term 13 | TERM. This Contract expires at the conclusion of code execution. 14 | -------------------------------------------------------------------------------- /tokendapppub.transfer_rc.md: -------------------------------------------------------------------------------- 1 | # CONTRACT FOR tokendapppub::transfer 2 | 3 | ## ACTION NAME: transfer 4 | 5 | ### Parameters 6 | Input parameters: 7 | 8 | * `from` (token payer) 9 | * `to` (token receiver) 10 | * `quantity` (token tranfer amount) 11 | * `memo` (DApp project owner will be notified of the "memo" info and used it as parameters for convenience) 12 | 13 | ### Intent 14 | INTENT. The main purpose of this contract is to help users transfer the token. 15 | 16 | ### Term 17 | TERM. This Contract expires at the conclusion of code execution. -------------------------------------------------------------------------------- /tokendapppub_rc.md: -------------------------------------------------------------------------------- 1 | ### CLAUSE NAME: Warranty 2 | WARRANTY. The invoker of the contract action shall uphold its Obligations under this Contract in a timely and workmanlike manner, using knowledge and recommendations for performing the services which meet generally acceptable standards set forth by EOS.IO Blockchain Block Producers. 3 | 4 | ### CLAUSE NAME: Default 5 | DEFAULT. The occurrence of any of the following shall constitute a material default under this Contract: 6 | 7 | ### CLAUSE NAME: Remedies 8 | REMEDIES. In addition to any and all other rights a party may have available according to law, if a party defaults by failing to substantially perform any provision, term or condition of this Contract, the other party may terminate the Contract by providing written notice to the defaulting party. This notice shall describe with sufficient detail the nature of the default. The party receiving such notice shall promptly be removed from being a Block Producer and this Contract shall be automatically terminated. 9 | 10 | ### CLAUSE NAME: Force Majeure 11 | FORCE MAJEURE. If performance of this Contract or any obligation under this Contract is prevented, restricted, or interfered with by causes beyond either party's reasonable control ("Force Majeure"), and if the party unable to carry out its obligations gives the other party prompt written notice of such event, then the obligations of the party invoking this provision shall be suspended to the extent necessary by such event. The term Force Majeure shall include, without limitation, acts of God, fire, explosion, vandalism, storm or other similar occurrence, orders or acts of military or civil authority, or by national emergencies, insurrections, riots, or wars, or strikes, lock-outs, work stoppages, or supplier failures. The excused party shall use reasonable efforts under the circumstances to avoid or remove such causes of non-performance and shall proceed to perform with reasonable dispatch whenever such causes are removed or ceased. An act or omission shall be deemed within the reasonable control of a party if committed, omitted, or caused by such party, or its employees, officers, agents, or affiliates. 12 | 13 | ### CLAUSE NAME: Dispute Resolution 14 | DISPUTE RESOLUTION. Any controversies or disputes arising out of or relating to this Contract will be resolved by binding arbitration under the default rules set forth by the EOS.IO Blockchain. The arbitrator's award will be final, and judgment may be entered upon it by any court having proper jurisdiction. 15 | 16 | ### CLAUSE NAME: Entire Agreement 17 | ENTIRE AGREEMENT. This Contract contains the entire agreement of the parties, and there are no other promises or conditions in any other agreement whether oral or written concerning the subject matter of this Contract. This Contract supersedes any prior written or oral agreements between the parties. 18 | 19 | ### CLAUSE NAME: Severability 20 | SEVERABILITY. If any provision of this Contract will be held to be invalid or unenforceable for any reason, the remaining provisions will continue to be valid and enforceable. If a court finds that any provision of this Contract is invalid or unenforceable, but that by limiting such provision it would become valid and enforceable, then such provision will be deemed to be written, construed, and enforced as so limited. 21 | 22 | ### CLAUSE NAME: Amendment 23 | AMENDMENT. This Contract may be modified or amended in writing by mutual agreement between the parties, if the writing is signed by the party obligated under the amendment. 24 | 25 | ### CLAUSE NAME: Governing Law 26 | GOVERNING LAW. This Contract shall be construed in accordance with the Maxims of Equity. 27 | 28 | ### CLAUSE NAME: Notice 29 | NOTICE. Any notice or communication required or permitted under this Contract shall be sufficiently given if delivered to a verifiable email address or to such other email address as one party may have publicly furnished in writing, or published on a broadcast contract provided by this blockchain for purposes of providing notices of this type. 30 | ### CLAUSE NAME: Waiver of Contractual Right 31 | WAIVER OF CONTRACTUAL RIGHT. The failure of either party to enforce any provision of this Contract shall not be construed as a waiver or limitation of that party's right to subsequently enforce and compel strict compliance with every provision of this Contract. 32 | 33 | ### CLAUSE NAME: Arbitrator's Fees to Prevailing Party 34 | ARBITRATOR'S FEES TO PREVAILING PARTY. In any action arising hereunder or any separate action pertaining to the validity of this Agreement, both sides shall pay half the initial cost of arbitration, and the prevailing party shall be awarded reasonable arbitrator's fees and costs. 35 | 36 | ### CLAUSE NAME: Construction and Interpretation 37 | CONSTRUCTION AND INTERPRETATION. The rule requiring construction or interpretation against the drafter is waived. The document shall be deemed as if it were drafted by both parties in a mutual effort. 38 | 39 | ### CLAUSE NAME: In Witness Whereof 40 | IN WITNESS WHEREOF, the parties hereto have caused this Agreement to be executed by themselves or their duly authorized representatives as of the date of execution, and authorized as proven by the cryptographic signature on the transaction that invokes this contract. -------------------------------------------------------------------------------- /基于班柯的实用增强型通证协议.md: -------------------------------------------------------------------------------- 1 | # 基于班柯的实用增强型通证协议 2 | 3 | 随着2018年高性能公链落地,DApp开发愈发火热,而针对使用DApp功能所需的应用型通证行业内还没有一套相应的规范。本白皮书尝试设计一套成熟的应用型通证,方便DApp开发者设计并发行自己的应用型通证。 4 | 5 | 传统以太坊上的ERC20代币在设计上刻意强化了通证的流通特性,但是对于一个DApp来讲,通证的流动性并不是越强越好的,根据DApp的经济规模和发展阶段会对通证有不同的诉求。资本自由流动、固定汇率、独立的通证政策这三个诉求对DApp来说是需要不断进行平衡的。而从2017年以来整个虚拟通证市场的表现也证明了刻意增强的流动性主要服务的对象不是DApp的开发者及其用户,而是市场的投机者和赌徒。 6 | 7 | 针对市场上因流动性而造成的种种问题,我们认为应用型通证应该着重于用户本身的通证使用诉求以及通证长期投资者的分红收益诉求,所以本白皮书将在Bancor协议的基础上增加通证的使用协议和通证的分红协议,并引入期权协议和交易手续费算法使得此应用型通证能够更加符合当前DApp发展所需。 8 | 9 | ### 通证使用协议 10 | 11 | 基于Bancor的通证使用协议核心在于消耗用户手中的通证,将其返还到当前通证池中。同时因为Bancor协议另一端的基准货币量不变,所以会使得当前持有此应用型通证的投资人手中的通证价值提升。对应的公式如下: 12 | $$ 13 | \begin{align} 14 | 基准货币资金量'&=\frac{(基准货币资金量-基准货币基储量)\times 应用通证发行量}{应用通证发行量-应用通证剩余量-应用通证消耗量}\\ 15 | 基准货币基储量'&=\frac{(基准货币资金量-基准货币基储量)\times (应用通证剩余量+应用通证消耗量)}{应用通证发行量-应用通证剩余量-应用通证消耗量}\\ 16 | 应用通证剩余量'&=应用通证剩余量+应用通证消耗量 17 | \end{align} 18 | $$ 19 | 20 | ### 通证分红协议 21 | 22 | 基于Bancor的通证使用协议核心在于增加基准货币资金量,同时保持另一端的应用通证余量和发行量不变,所以使得当前持有此应用型通证的投资人手中的通证价值提升。对应的公式如下: 23 | $$ 24 | \begin{align} 25 | 基准货币资金量'&=\frac{(基准货币资金量-基准货币基储量+基准货币分红量)\times 应用通证发行量}{应用通证发行量-应用通证剩余量}\\ 26 | 基准货币基储量'&=\frac{(基准货币资金量-基准货币基储量+基准货币分红量)\times 应用通证剩余量}{应用通证发行量-应用通证剩余量} 27 | \end{align} 28 | $$ 29 | 30 | ### 通证期权协议 31 | 32 | 以太坊上大部分ERC20通证的项目方发起的所谓团队锁仓行为均为“人肉”锁仓,将其ERC20通证转入某个公示账户中让投资人来监督。在本白皮书的应用通证设计中,考虑到团队锁仓的需要,设计了锁仓释放协议。通证将在锁定期逐渐释放,对应的公式如下: 33 | $$ 34 | 应用通证期权释放量'=\frac{(当前时间-应用通证发行起始时间)\times 应用通证期权总量}{应用通证期权锁定期}-应用通证期权释放量 35 | $$ 36 | 期权释放的过程等同于发行新的应用通证,同时将新发行的应用型通证授权给DApp的项目方。这个过程中需要保证新的通证释放出来后,整个Bancor交易协议依然可以正常运作。而不至于像EOS RAM的增发模型那样,最后会有大额的EOS锁定在合约中,RAM持有人们即使将所有的RAM都卖出也无法拿到锁进合约中的EOS。从而保护了通证持有者的利益,其对应公式如下: 37 | $$ 38 | \begin{align} 39 | 基准货币资金量'&=\frac{(基准货币资金量-基准货币基储量)\times (应用通证期权释放量+应用通证发行量)}{应用通证发行量-应用通证剩余量+应用通证期权释放量}\\ 40 | 基准货币基储量'&=\frac{(基准货币资金量-基准货币基储量)\times 应用通证剩余量}{应用通证发行量-应用通证剩余量+应用通证期权释放量}\\ 41 | 应用通证发行量'&=应用通证发行量+应用通证期权释放量 42 | \end{align} 43 | $$ 44 | 45 | ### 通证交易手续费 46 | 47 | 为了鼓励用户购买并使用DApp项目方所发行的应用型通证,对于通证的购买和使用行为,均免收交易手续费。但是对于出售应用通证的行为将收取一定的手续费,因为基于Bancor协议下的交易市场,在早期DApp的经济规模还比较小的时候极易发生剧烈的波动,为了防止早期投机者操纵应用型通证价格对DApp的健康发展造成冲击,所以手续费会在早期有一个相对较高的比例,而随着期权的逐渐释放而降低。而在期权释放完毕后,则恒等于DApp设置的基础手续费。期权释放期间的卖出手续费公式如下: 48 | $$ 49 | 应用通证出售手续费比例=\frac{2 \times (初始手续费比例-基础手续费比例) \times 应用通证期权锁定期}{(当前时间-应用通证发行起始时间)+应用通证期权锁定期}+(2 \times 基础手续费比例 - 初始手续费比例) 50 | $$ 51 | 52 | ### 通证流动性激励 53 | 54 | 对于DApp通证而言,其流动性不在于交易所内的市场深度大小,而更多的依赖于其使用场景。其通证的实际使用和流通能力是实用型通证流动性的基础,所以对于各个接入了通证购买以及使用场景的流量入口都需要给予激励。具体使用场景下的激励,开发者可以基于合约中提供的通知接口自行实现。为了方便DApp通证发行方激励帮助其销售通证的组织或个人,通证协议层面提供了购买渠道分红激励。其激励比例可由DApp通证发行方自行设定。 -------------------------------------------------------------------------------- /基于班柯的实用增强型通证协议.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeBankDeFi/tokendapppub/1c4ab3d9047710e9757d66d03e0baa6f969c6d9c/基于班柯的实用增强型通证协议.pdf --------------------------------------------------------------------------------