diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d216841 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +.PHONY: clean +clean: + rm -rf build + +.PHONY: build +build: + mkdir -p build + cd build && cmake ../ && make + mv build/dgoods/dgoods* . + rm -rf build/* + mkdir -p build/dgoods + mv dgoods.abi dgoods.wasm build/dgoods/ diff --git a/build/dgoods/dgoods.abi b/build/dgoods/dgoods.abi index 8089f84..59f7758 100644 --- a/build/dgoods/dgoods.abi +++ b/build/dgoods/dgoods.abi @@ -1,6 +1,7 @@ { - "____comment": "This file was generated with eosio-abigen. DO NOT EDIT Mon May 6 16:40:44 2019", + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", "version": "eosio::abi/1.1", + "types": [], "structs": [ { "name": "accounts", @@ -201,7 +202,7 @@ "type": "name" }, { - "name": "metadata_uri", + "name": "relative_uri", "type": "string?" } ] @@ -273,7 +274,7 @@ "type": "string" }, { - "name": "metadata_uri", + "name": "relative_uri", "type": "string" }, { @@ -324,20 +325,6 @@ } ] }, - { - "name": "setrampayer", - "base": "", - "fields": [ - { - "name": "payer", - "type": "name" - }, - { - "name": "id", - "type": "uint64" - } - ] - }, { "name": "tokenconfigs", "base": "", @@ -413,7 +400,6 @@ ] } ], - "types": [], "actions": [ { "name": "burnft", @@ -460,11 +446,6 @@ "type": "setconfig", "ricardian_contract": "" }, - { - "name": "setrampayer", - "type": "setrampayer", - "ricardian_contract": "" - }, { "name": "transferft", "type": "transferft", @@ -521,6 +502,5 @@ } ], "ricardian_clauses": [], - "variants": [], - "abi_extensions": [] + "variants": [] } \ No newline at end of file diff --git a/build/dgoods/dgoods.wasm b/build/dgoods/dgoods.wasm index 1d732d0..3a67753 100755 Binary files a/build/dgoods/dgoods.wasm and b/build/dgoods/dgoods.wasm differ diff --git a/include/dasset.hpp b/include/dasset.hpp index 3664d9a..482b0f0 100644 --- a/include/dasset.hpp +++ b/include/dasset.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include "utility.hpp" @@ -23,25 +22,25 @@ namespace dgoods_asset { void _check_precision(const uint8_t& p) { static constexpr uint8_t max_precision = 18; - eosio_assert( p <= max_precision, "precision must be less than 19"); + check( p <= max_precision, "precision must be less than 19"); } void _check_max(const uint64_t& a) { static constexpr uint64_t max_amount = ( 1LL << 62 ) - 1; - eosio_assert( a <= max_amount, "max supply must be less than 2^62 - 1"); - eosio_assert( a > 0, "max supply must be greater than 0"); + check( a < max_amount, "max supply must be less than 2^62 - 1"); } dasset() {} void from_string(const string& s) { string string_amount = trim(s); + check( ( string_amount[0] != '-' ), "Amount can not be negative" ); auto dot_pos = string_amount.find('.'); uint64_t uint_part; uint64_t frac_part; uint64_t p10; if ( dot_pos != string::npos ) { - eosio_assert( ( dot_pos != string_amount.size() - 1 ), "missing decimal fraction after decimal point"); + check( ( dot_pos != string_amount.size() - 1 ), "missing decimal fraction after decimal point"); } if ( dot_pos == string::npos ) { uint_part = stoull(string_amount); @@ -57,6 +56,7 @@ namespace dgoods_asset { frac_part = stoull(string_amount.substr( dot_pos + 1 )); } amount = uint_part * p10 + frac_part; + check( amount > 0, "max supply must be greater than 0"); } @@ -66,6 +66,7 @@ namespace dgoods_asset { _check_precision(p); string string_amount = trim(s); + check( ( string_amount[0] != '-' ), "Amount can not be negative" ); // 1.0 1. 1 uint64_t uint_part; uint64_t frac_part; @@ -82,6 +83,7 @@ namespace dgoods_asset { _check_max(uint_part); uint64_t p10 = _get_precision(p); amount = uint_part * p10 + frac_part; + check( amount > 0, "max supply must be greater than 0"); } diff --git a/include/dgoods.hpp b/include/dgoods.hpp index e83dfaa..72ed6a0 100644 --- a/include/dgoods.hpp +++ b/include/dgoods.hpp @@ -1,10 +1,10 @@ #pragma once -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include @@ -52,15 +52,12 @@ CONTRACT dgoods: public contract { ACTION buynft(name from, name to, asset quantity, string memo); - ACTION setrampayer(name payer, - uint64_t id); - ACTION transfernft(name from, name to, vector dgood_ids, string memo); - ACTION transferft(name from, + ACTION transferft(name from, name to, name category, name token_name, @@ -160,9 +157,9 @@ CONTRACT dgoods: public contract { private: - void mint( name to, name issuer, name category, name token_name, - uint64_t issued_supply, string relative_uri ); - void add_balance( name owner, name issuer, name category, name token_name, - uint64_t category_name_id, dasset quantity ); - void sub_balance( name owner, uint64_t category_name_id, dasset quantity ); + void mint(name to, name issuer, name category, name token_name, + uint64_t issued_supply, string relative_uri); + void add_balance(name owner, name issuer, name category, name token_name, + uint64_t category_name_id, dasset quantity); + void sub_balance(name owner, uint64_t category_name_id, dasset quantity); }; diff --git a/include/utility.hpp b/include/utility.hpp index c4291ad..60dfa26 100644 --- a/include/utility.hpp +++ b/include/utility.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include using namespace std; using namespace eosio; @@ -34,10 +34,12 @@ namespace utility { tuple parsememo(const string& memo) { auto dot_pos = memo.find(','); + string errormsg = "malformed memo: must have dgood_id,to_account"; + check ( dot_pos != string::npos, errormsg.c_str() ); if ( dot_pos != string::npos ) { - eosio_assert( ( dot_pos != memo.size() - 1 ), "malformed memo, must have dgood_id,to_account"); + check( ( dot_pos != memo.size() - 1 ), errormsg.c_str() ); } - // need to trim substring + // will abort if stoull throws error since wasm no error checking uint64_t dgood_id = stoull( trim( memo.substr( 0, dot_pos ) ) ); name to_account = name( trim ( memo.substr( dot_pos + 1 ) ) ); diff --git a/src/dgoods.cpp b/src/dgoods.cpp index 0f60b11..e546985 100644 --- a/src/dgoods.cpp +++ b/src/dgoods.cpp @@ -4,7 +4,7 @@ ACTION dgoods::setconfig(symbol_code sym, string version) { require_auth( get_self() ); // valid symbol - eosio_assert( sym.is_valid(), "not valid symbol" ); + check( sym.is_valid(), "not valid symbol" ); // can only have one symbol per contract config_index config_table(get_self(), get_self().value); @@ -16,30 +16,30 @@ ACTION dgoods::setconfig(symbol_code sym, string version) { } ACTION dgoods::create(name issuer, - name category, - name token_name, - bool fungible, - bool burnable, - bool transferable, - string base_uri, - string max_supply) { + name category, + name token_name, + bool fungible, + bool burnable, + bool transferable, + string base_uri, + string max_supply) { require_auth( get_self() ); // check if issuer account exists - eosio_assert( is_account( issuer ), "issuer account does not exist" ); + check( is_account( issuer ), "issuer account does not exist" ); dasset m_supply; if ( fungible == true ) { m_supply.from_string(max_supply); } else { m_supply.from_string(max_supply, 0); - eosio_assert(m_supply.amount >= 1, "max_supply for nft must be at least 1"); + check(m_supply.amount >= 1, "max_supply for nft must be at least 1"); } // get category_name_id config_index config_table( get_self(), get_self().value ); - eosio_assert(config_table.exists(), "Symbol table does not exist, setconfig first"); + check(config_table.exists(), "Symbol table does not exist, setconfig first"); auto config_singleton = config_table.get(); auto category_name_id = config_singleton.category_name_id; @@ -56,7 +56,7 @@ ACTION dgoods::create(name issuer, stats_index stats_table( get_self(), category.value ); auto existing_token = stats_table.find( token_name.value ); - eosio_assert( existing_token == stats_table.end(), "Token with category and token_name exists" ); + check( existing_token == stats_table.end(), "Token with category and token_name exists" ); // token type hasn't been created, create it stats_table.emplace( get_self(), [&]( auto& stats ) { stats.category_name_id = category_name_id; @@ -78,14 +78,14 @@ ACTION dgoods::create(name issuer, ACTION dgoods::issue(name to, - name category, - name token_name, - string quantity, - string relative_uri, - string memo) { + name category, + name token_name, + string quantity, + string relative_uri, + string memo) { - eosio_assert( is_account( to ), "to account does not exist"); - eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" ); + check( is_account( to ), "to account does not exist"); + check( memo.size() <= 256, "memo has more than 256 bytes" ); // dgoodstats table stats_index stats_table( get_self(), category.value ); @@ -100,7 +100,7 @@ ACTION dgoods::issue(name to, // mint nft q.from_string("1", 0); // check cannot issue more than max supply, careful of overflow of uint - eosio_assert( q.amount <= (dgood_stats.max_supply.amount - dgood_stats.current_supply), "Cannot issue more than max supply" ); + check( q.amount <= (dgood_stats.max_supply.amount - dgood_stats.current_supply), "Cannot issue more than max supply" ); mint(to, dgood_stats.issuer, category, token_name, dgood_stats.issued_supply, relative_uri); @@ -109,9 +109,9 @@ ACTION dgoods::issue(name to, // issue fungible q.from_string(quantity); // check cannot issue more than max supply, careful of overflow of uint - eosio_assert( q.amount <= (dgood_stats.max_supply.amount - dgood_stats.current_supply), "Cannot issue more than max supply" ); + check( q.amount <= (dgood_stats.max_supply.amount - dgood_stats.current_supply), "Cannot issue more than max supply" ); string string_precision = "precision of quantity must be " + to_string(dgood_stats.max_supply.precision); - eosio_assert( q.precision == dgood_stats.max_supply.precision, string_precision.c_str() ); + check( q.precision == dgood_stats.max_supply.precision, string_precision.c_str() ); add_balance(to, dgood_stats.issuer, category, token_name, dgood_stats.category_name_id, q); } @@ -125,7 +125,7 @@ ACTION dgoods::issue(name to, } ACTION dgoods::burnnft(name owner, - vector dgood_ids) { + vector dgood_ids) { require_auth(owner); @@ -133,13 +133,13 @@ ACTION dgoods::burnnft(name owner, dgood_index dgood_table( get_self(), get_self().value ); for ( auto const& dgood_id: dgood_ids ) { const auto& token = dgood_table.get( dgood_id, "token does not exist" ); - eosio_assert( token.owner == owner, "must be token owner" ); + check( token.owner == owner, "must be token owner" ); stats_index stats_table( get_self(), token.category.value ); const auto& dgood_stats = stats_table.get( token.token_name.value, "dgood stats not found" ); - eosio_assert( dgood_stats.burnable == true, "Not burnable"); - eosio_assert( dgood_stats.fungible == false, "Cannot call burnnft on fungible token, call burn instead"); + check( dgood_stats.burnable == true, "Not burnable"); + check( dgood_stats.fungible == false, "Cannot call burnnft on fungible token, call burn instead"); dasset quantity; quantity.from_string("1", 0); @@ -158,8 +158,8 @@ ACTION dgoods::burnnft(name owner, } ACTION dgoods::burnft(name owner, - uint64_t category_name_id, - string quantity) { + uint64_t category_name_id, + string quantity) { require_auth(owner); @@ -172,7 +172,7 @@ ACTION dgoods::burnft(name owner, dasset q; q.from_string(quantity); string string_precision = "precision of quantity must be " + to_string(dgood_stats.max_supply.precision); - eosio_assert( q.precision == dgood_stats.max_supply.precision, string_precision.c_str() ); + check( q.precision == dgood_stats.max_supply.precision, string_precision.c_str() ); // lower balance from owner sub_balance(owner, category_name_id, q); @@ -186,29 +186,29 @@ ACTION dgoods::burnft(name owner, ACTION dgoods::transfernft(name from, - name to, - vector dgood_ids, - string memo ) { + name to, + vector dgood_ids, + string memo ) { // ensure authorized to send from account - eosio_assert( from != to, "cannot transfer to self" ); + check( from != to, "cannot transfer to self" ); require_auth( from ); // ensure 'to' account exists - eosio_assert( is_account( to ), "to account does not exist"); + check( is_account( to ), "to account does not exist"); // check memo size - eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" ); + check( memo.size() <= 256, "memo has more than 256 bytes" ); // loop through vector of dgood_ids, check token exists dgood_index dgood_table( get_self(), get_self().value ); for ( auto const& dgood_id: dgood_ids ) { const auto& token = dgood_table.get( dgood_id, "token does not exist" ); - eosio_assert( token.owner == from, "must be token owner" ); + check( token.owner == from, "must be token owner" ); stats_index stats_table( get_self(), token.category.value ); const auto& dgood_stats = stats_table.get( token.token_name.value, "dgood stats not found" ); - eosio_assert( dgood_stats.transferable == true, "not transferable"); + check( dgood_stats.transferable == true, "not transferable"); // notifiy both parties require_recipient( from ); @@ -227,52 +227,52 @@ ACTION dgoods::transfernft(name from, } ACTION dgoods::transferft(name from, - name to, - name category, - name token_name, - string quantity, - string memo ) { + name to, + name category, + name token_name, + string quantity, + string memo ) { // ensure authorized to send from account - eosio_assert( from != to, "cannot transfer to self" ); + check( from != to, "cannot transfer to self" ); require_auth( from ); // ensure 'to' account exists - eosio_assert( is_account( to ), "to account does not exist"); + check( is_account( to ), "to account does not exist"); // check memo size - eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" ); + check( memo.size() <= 256, "memo has more than 256 bytes" ); require_recipient( from ); require_recipient( to ); stats_index stats_table( get_self(), category.value ); const auto& dgood_stats = stats_table.get( token_name.value, "dgood stats not found" ); - eosio_assert( dgood_stats.transferable == true, "not transferable"); - eosio_assert( dgood_stats.fungible == true, "Must be fungible token"); + check( dgood_stats.transferable == true, "not transferable"); + check( dgood_stats.fungible == true, "Must be fungible token"); dasset q; q.from_string(quantity); string string_precision = "precision of quantity must be " + to_string(dgood_stats.max_supply.precision); - eosio_assert( q.precision == dgood_stats.max_supply.precision, string_precision.c_str() ); + check( q.precision == dgood_stats.max_supply.precision, string_precision.c_str() ); sub_balance(from, dgood_stats.category_name_id, q); add_balance(to, from, category, token_name, dgood_stats.category_name_id, q); } ACTION dgoods::listsalenft(name seller, - uint64_t dgood_id, - asset net_sale_amount) { + uint64_t dgood_id, + asset net_sale_amount) { require_auth( seller ); vector dgood_ids = {dgood_id}; - eosio_assert( net_sale_amount.amount > .02, "minimum price of at least 0.02 EOS"); - eosio_assert( net_sale_amount.symbol == symbol( symbol_code("EOS"), 4), "only accept EOS for sale" ); + check( net_sale_amount.amount > .02, "minimum price of at least 0.02 EOS"); + check( net_sale_amount.symbol == symbol( symbol_code("EOS"), 4), "only accept EOS for sale" ); ask_index ask_table( get_self(), get_self().value ); auto ask = ask_table.find( dgood_id); - eosio_assert ( ask == ask_table.end(), "already listed for sale" ); + check ( ask == ask_table.end(), "already listed for sale" ); // inline action checks ownership, permissions, and transferable prop SEND_INLINE_ACTION( *this, transfernft, { seller, name("active")}, @@ -283,16 +283,16 @@ ACTION dgoods::listsalenft(name seller, ask.dgood_id = dgood_id; ask.seller = seller; ask.amount = net_sale_amount; - ask.expiration = time_point_sec(now()) + WEEK_SEC; + ask.expiration = time_point_sec(current_time_point()) + WEEK_SEC; }); } ACTION dgoods::closesalenft(name seller, - uint64_t dgood_id) { + uint64_t dgood_id) { ask_index ask_table( get_self(), get_self().value ); const auto& ask = ask_table.get( dgood_id, "cannot cancel sale that doesn't exist" ); // if sale has expired anyone can call this and ask removed, token sent back to orig seller - if ( time_point_sec(now()) > ask.expiration ) { + if ( time_point_sec(current_time_point()) > ask.expiration ) { ask_table.erase( ask ); vector dgood_ids = {dgood_id}; @@ -301,7 +301,7 @@ ACTION dgoods::closesalenft(name seller, {get_self(), ask.seller, dgood_ids, "clear sale returning to: " + ask.seller.to_string()} ); } else { require_auth( seller ); - eosio_assert( ask.seller == seller, "only the seller can cancel a sale in progress"); + check( ask.seller == seller, "only the seller can cancel a sale in progress"); vector dgood_ids = {dgood_id}; // inline action checks ownership, permissions, and transferable prop @@ -312,15 +312,18 @@ ACTION dgoods::closesalenft(name seller, } ACTION dgoods::buynft(name from, - name to, - asset quantity, - string memo) { + name to, + asset quantity, + string memo) { + // allow EOS to be sent by sending with empty string memo + if ( memo == "deposit" ) return; + // don't allow spoofs if ( to != get_self() ) return; if ( from == name("eosio.stake") ) return; if ( quantity.symbol != symbol( symbol_code("EOS"), 4) ) return; if ( memo.length() > 32 ) return; - // memo format comma separated - // dgood_id,to_account + //memo format comma separated + //dgood_id,to_account uint64_t dgood_id; name to_account; @@ -328,8 +331,8 @@ ACTION dgoods::buynft(name from, ask_index ask_table( get_self(), get_self().value ); const auto& ask = ask_table.get( dgood_id, "token not listed for sale" ); - eosio_assert ( ask.amount.amount == quantity.amount, "send the correct amount"); - eosio_assert (ask.expiration > time_point_sec(now()), "sale has expired"); + check ( ask.amount.amount == quantity.amount, "send the correct amount"); + check (ask.expiration > time_point_sec(current_time_point()), "sale has expired"); action( permission_level{ get_self(), name("active") }, name("eosio.token"), name("transfer"), make_tuple( get_self(), ask.seller, quantity, "sold token: " + to_string(dgood_id))).send(); @@ -351,11 +354,11 @@ ACTION dgoods::logcall(uint64_t dgood_id) { // Private void dgoods::mint(name to, - name issuer, - name category, - name token_name, - uint64_t issued_supply, - string relative_uri) { + name issuer, + name category, + name token_name, + uint64_t issued_supply, + string relative_uri) { dgood_index dgood_table( get_self(), get_self().value); auto dgood_id = dgood_table.available_primary_key(); @@ -382,7 +385,7 @@ void dgoods::mint(name to, } // Private -void dgoods::add_balance( name owner, name ram_payer, name category, name token_name, +void dgoods::add_balance(name owner, name ram_payer, name category, name token_name, uint64_t category_name_id, dasset quantity) { account_index to_account( get_self(), owner.value ); auto acct = to_account.find( category_name_id ); @@ -401,11 +404,11 @@ void dgoods::add_balance( name owner, name ram_payer, name category, name token_ } // Private -void dgoods::sub_balance( name owner, uint64_t category_name_id, dasset quantity ) { +void dgoods::sub_balance(name owner, uint64_t category_name_id, dasset quantity) { account_index from_account( get_self(), owner.value ); const auto& acct = from_account.get( category_name_id, "token does not exist in account" ); - eosio_assert( acct.amount.amount >= quantity.amount, "quantity is more than account balance"); + check( acct.amount.amount >= quantity.amount, "quantity is more than account balance"); if ( acct.amount.amount == quantity.amount ) { from_account.erase( acct );