From 926171bb11e2ded30d1f616f72a2a4c023a7a1ca Mon Sep 17 00:00:00 2001 From: Chuck Harrison Date: Wed, 10 Nov 2021 09:53:44 -0800 Subject: [PATCH 1/7] add reject_and_clear flag to 'approve' action --- RainbowC/include/rainbow.hpp | 8 +++++--- RainbowC/ricardian/rainbow.contracts.md | 3 ++- RainbowC/src/rainbow.cpp | 26 ++++++++++++++++++------- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/RainbowC/include/rainbow.hpp b/RainbowC/include/rainbow.hpp index 391cf36..0f0bd73 100644 --- a/RainbowC/include/rainbow.hpp +++ b/RainbowC/include/rainbow.hpp @@ -78,15 +78,17 @@ namespace eosio { /** - * By this action the contract owner approves the creation of the token. Until - * this approval, no tokens may be issued. + * By this action the contract owner approves or rejects the creation of the token. Until + * this approval, no tokens may be issued. If rejected, and no issued tokens are outstanding, + * the table entries for this token are deleted. * * @param symbolcode - the symbol_code of the token to execute the close action for. + * @param reject_and_clear - if this flag is true, delete token; if false, approve creation * * @pre The symbol must have been created. */ [[eosio::action]] - void approve( const symbol_code& symbolcode ); + void approve( const symbol_code& symbolcode, const bool& reject_and_clear ); /** diff --git a/RainbowC/ricardian/rainbow.contracts.md b/RainbowC/ricardian/rainbow.contracts.md index f72d0ca..93315f0 100644 --- a/RainbowC/ricardian/rainbow.contracts.md +++ b/RainbowC/ricardian/rainbow.contracts.md @@ -3,12 +3,13 @@ --- spec_version: "0.2.0" title: Approve Created Token -summary: 'Approve characteristics of a newly created token' +summary: 'Approve characteristics of a newly created token, or reject it' icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ --- The contract owner allows the issuer to begin issuing tokens under a newly created {{symbol_to_symbol_code symbol}}. Once approved, the issuer may modify the token configuration without any further approval action required. +If {{reject_and_clear}} is true, and there are no outstanding issued tokens, the token is deleted. <

close

diff --git a/RainbowC/src/rainbow.cpp b/RainbowC/src/rainbow.cpp index c5ff5f8..f948c35 100644 --- a/RainbowC/src/rainbow.cpp +++ b/RainbowC/src/rainbow.cpp @@ -88,16 +88,28 @@ void token::create( const name& issuer, }); } -void token::approve( const symbol_code& symbolcode ) +void token::approve( const symbol_code& symbolcode, const bool& reject_and_clear ) { require_auth( get_self() ); - stats statstable( get_self(), symbolcode.raw() ); - const auto& st = statstable.get( symbolcode.raw(), "token with symbol does not exist" ); - configs configtable( get_self(), symbolcode.raw() ); + auto sym_code_raw = symbolcode.raw(); + stats statstable( get_self(), sym_code_raw ); + const auto& st = statstable.get( sym_code_raw, "token with symbol does not exist" ); + configs configtable( get_self(), sym_code_raw ); const auto& cf = *configtable.begin(); - configtable.modify (cf, st.issuer, [&]( auto& s ) { - s.approved = true; - }); + if( reject_and_clear ) { + check( st.supply.amount == 0, "cannot clear with outstanding tokens" ); + stakes stakestable( get_self(), sym_code_raw ); + for( auto itr = stakestable.begin(); itr != stakestable.end(); ) { + itr = stakestable.erase(itr); + } + configtable.erase( configtable.begin() ); + statstable.erase( statstable.iterator_to(st) ); + } else { + configtable.modify (cf, st.issuer, [&]( auto& s ) { + s.approved = true; + }); + } + } void token::setstake( const name& issuer, From aa6489d90864c3d3a22cbe9517d1139a3830d919 Mon Sep 17 00:00:00 2001 From: Chuck Harrison Date: Wed, 10 Nov 2021 10:24:49 -0800 Subject: [PATCH 2/7] Support deferred staking --- RainbowC/include/rainbow.hpp | 6 +++++- RainbowC/ricardian/rainbow.contracts.md | 2 +- RainbowC/src/rainbow.cpp | 13 +++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/RainbowC/include/rainbow.hpp b/RainbowC/include/rainbow.hpp index 0f0bd73..d1c79f5 100644 --- a/RainbowC/include/rainbow.hpp +++ b/RainbowC/include/rainbow.hpp @@ -104,6 +104,7 @@ namespace eosio { * @param stake_token_contract - the staked token contract account (e.g. token.seeds), * @param stake_to - the escrow account where stake is held, or `deletestake` * to remove a row from the stakes table + * @param deferred - staking relationship does not transfer stake to escrow. * @param memo - the memo string to accompany the transaction. * * @pre Token symbol must have already been created by this issuer @@ -119,9 +120,11 @@ namespace eosio { const asset& stake_per_bucket, const name& stake_token_contract, const name& stake_to, + const bool& deferred, const string& memo); /** - * This action issues a `quantity` of tokens to the issuer account. + * This action issues a `quantity` of tokens to the issuer account, and transfers + * a proportional amount of stake to escrow if staking is configured. * * @param to - the account to issue tokens to, it must be the same as the issuer, * @param quantity - the amount of tokens to be issued, @@ -278,6 +281,7 @@ namespace eosio { asset stake_per_bucket; name stake_token_contract; name stake_to; + bool deferred; uint64_t primary_key()const { return index; }; uint128_t by_secondary() const { diff --git a/RainbowC/ricardian/rainbow.contracts.md b/RainbowC/ricardian/rainbow.contracts.md index 93315f0..a9230e8 100644 --- a/RainbowC/ricardian/rainbow.contracts.md +++ b/RainbowC/ricardian/rainbow.contracts.md @@ -79,7 +79,7 @@ RAM will be deducted from the token manager (issuer) resources to create the nec This action does not allow the total quantity to exceed the max allowed supply of the token. -A proportionate number of staking tokens are transferred from issuer's account to the stake_to escrow account for each stake listed in the stake stats table. +A proportionate number of staking tokens are transferred from issuer's account to the stake_to escrow account for each non-deferred stake listed in the stake stats table.

open

diff --git a/RainbowC/src/rainbow.cpp b/RainbowC/src/rainbow.cpp index f948c35..092e57f 100644 --- a/RainbowC/src/rainbow.cpp +++ b/RainbowC/src/rainbow.cpp @@ -117,6 +117,7 @@ void token::setstake( const name& issuer, const asset& stake_per_bucket, const name& stake_token_contract, const name& stake_to, + const bool& deferred, const string& memo) { require_auth( issuer ); @@ -151,11 +152,12 @@ void token::setstake( const name& issuer, const auto& sk = *existing; bool restaking = token_bucket != sk.token_bucket || stake_per_bucket != sk.stake_per_bucket || - stake_to != sk.stake_to; + stake_to != sk.stake_to || + deferred != sk.deferred; bool destaking = stake_to == sk.stake_to && stake_per_bucket.amount == 0; if( st.supply.amount != 0 ) { - if( destaking ) { + if( destaking && !deferred) { unstake_one( st, sk, st.issuer, st.supply.amount ); } else if ( restaking ) { check( sk.stake_per_bucket.amount == 0, "must destake before restaking"); @@ -169,8 +171,9 @@ void token::setstake( const name& issuer, s.stake_per_bucket = stake_per_bucket; s.stake_token_contract = stake_token_contract; s.stake_to = stake_to; + s.deferred = deferred; }); - if( restaking ) { + if( restaking && !deferred ) { stake_one( st, sk, st.supply.amount ); } return; @@ -239,7 +242,9 @@ void token::stake_one( const currency_stats st, const stake_stats sk, const uint void token::stake_all( const currency_stats st, const uint64_t amount ) { stakes stakestable( get_self(), st.supply.symbol.code().raw() ); for( auto itr = stakestable.begin(); itr != stakestable.end(); itr++ ) { - stake_one( st, *itr, amount ); + if( !itr->deferred ) { + stake_one( st, *itr, amount ); + } } } From 8d07b5a28394937379d1cb8c7c1a818af60acf82 Mon Sep 17 00:00:00 2001 From: Chuck Harrison Date: Wed, 10 Nov 2021 10:57:05 -0800 Subject: [PATCH 3/7] refactor stake_one() etc --- RainbowC/include/rainbow.hpp | 8 ++++---- RainbowC/src/rainbow.cpp | 36 ++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/RainbowC/include/rainbow.hpp b/RainbowC/include/rainbow.hpp index d1c79f5..5f3670f 100644 --- a/RainbowC/include/rainbow.hpp +++ b/RainbowC/include/rainbow.hpp @@ -301,10 +301,10 @@ namespace eosio { void sub_balance( const name& owner, const asset& value ); void add_balance( const name& owner, const asset& value, const name& ram_payer ); - void stake_all( const currency_stats st, const uint64_t amount ); - void unstake_all( const currency_stats st, const name& owner, const uint64_t amount ); - void stake_one( const currency_stats st, const stake_stats sk, const uint64_t amount ); - void unstake_one( const currency_stats st, const stake_stats sk, const name& owner, const uint64_t amount ); + void stake_all( const name& owner, const asset& quantity ); + void unstake_all( const name& owner, const asset& quantity ); + void stake_one( const stake_stats& sk, const name& owner, const asset& quantity ); + void unstake_one( const stake_stats& sk, const name& owner, const asset& quantity ); }; diff --git a/RainbowC/src/rainbow.cpp b/RainbowC/src/rainbow.cpp index 092e57f..d210602 100644 --- a/RainbowC/src/rainbow.cpp +++ b/RainbowC/src/rainbow.cpp @@ -158,7 +158,7 @@ void token::setstake( const name& issuer, stake_per_bucket.amount == 0; if( st.supply.amount != 0 ) { if( destaking && !deferred) { - unstake_one( st, sk, st.issuer, st.supply.amount ); + unstake_one( sk, st.issuer, st.supply ); } else if ( restaking ) { check( sk.stake_per_bucket.amount == 0, "must destake before restaking"); if( stake_to == deletestakeacct ) { @@ -174,7 +174,7 @@ void token::setstake( const name& issuer, s.deferred = deferred; }); if( restaking && !deferred ) { - stake_one( st, sk, st.supply.amount ); + stake_one( sk, st.issuer, st.supply ); } return; } @@ -188,9 +188,10 @@ void token::setstake( const name& issuer, s.stake_per_bucket = stake_per_bucket; s.stake_token_contract = stake_token_contract; s.stake_to = stake_to; + s.deferred = deferred; }); if( st.supply.amount != 0 ) { - stake_one( st, sk, st.supply.amount ); + stake_one( sk, st.issuer, st.supply ); } } @@ -218,20 +219,19 @@ void token::issue( const asset& quantity, const string& memo ) s.supply += quantity; }); - stake_all( st, quantity.amount ); + stake_all( st.issuer, quantity ); add_balance( st.issuer, quantity, st.issuer ); } -void token::stake_one( const currency_stats st, const stake_stats sk, const uint64_t amount ) { +void token::stake_one( const stake_stats& sk, const name& owner, const asset& quantity ) { if( sk.stake_per_bucket.amount > 0 ) { asset stake_quantity = sk.stake_per_bucket; - stake_quantity.amount = (int64_t)((int128_t)amount*sk.stake_per_bucket.amount/sk.token_bucket.amount); - // TBD: are there potential exploits based on rounding inaccuracy? + stake_quantity.amount = (int64_t)((int128_t)quantity.amount*sk.stake_per_bucket.amount/sk.token_bucket.amount); action( - permission_level{st.issuer,"active"_n}, + permission_level{owner, "active"_n}, sk.stake_token_contract, "transfer"_n, - std::make_tuple(st.issuer, + std::make_tuple(owner, sk.stake_to, stake_quantity, std::string("rainbow stake")) @@ -239,19 +239,19 @@ void token::stake_one( const currency_stats st, const stake_stats sk, const uint } } -void token::stake_all( const currency_stats st, const uint64_t amount ) { - stakes stakestable( get_self(), st.supply.symbol.code().raw() ); +void token::stake_all( const name& owner, const asset& quantity ) { + stakes stakestable( get_self(), quantity.symbol.code().raw() ); for( auto itr = stakestable.begin(); itr != stakestable.end(); itr++ ) { if( !itr->deferred ) { - stake_one( st, *itr, amount ); + stake_one( *itr, owner, quantity ); } } } -void token::unstake_one( const currency_stats st, const stake_stats sk, const name& owner, const uint64_t amount ) { +void token::unstake_one( const stake_stats& sk, const name& owner, const asset& quantity ) { if( sk.stake_per_bucket.amount > 0 ) { asset stake_quantity = sk.stake_per_bucket; - stake_quantity.amount = (int64_t)((int128_t)amount*sk.stake_per_bucket.amount/sk.token_bucket.amount); + stake_quantity.amount = (int64_t)((int128_t)quantity.amount*sk.stake_per_bucket.amount/sk.token_bucket.amount); // TBD: are there potential exploits based on rounding inaccuracy? action( permission_level{sk.stake_to,"active"_n}, @@ -264,10 +264,10 @@ void token::unstake_one( const currency_stats st, const stake_stats sk, const na ).send(); } } -void token::unstake_all( const currency_stats st, const name& owner, const uint64_t amount ) { - stakes stakestable( get_self(), st.supply.symbol.code().raw() ); +void token::unstake_all( const name& owner, const asset& quantity ) { + stakes stakestable( get_self(), quantity.symbol.code().raw() ); for( auto itr = stakestable.begin(); itr != stakestable.end(); itr++ ) { - unstake_one( st, *itr, owner, amount ); + unstake_one( *itr, owner, quantity ); } } @@ -297,7 +297,7 @@ void token::retire( const name& owner, const asset& quantity, const string& memo }); sub_balance( owner, quantity ); - unstake_all( st, owner, quantity.amount ); + unstake_all( owner, quantity ); } void token::transfer( const name& from, From 92f079f7336f3bf112991f78399fd70febdbecfc Mon Sep 17 00:00:00 2001 From: Chuck Harrison Date: Sun, 14 Nov 2021 20:23:57 -0800 Subject: [PATCH 4/7] added proportional redeem option (but not yet implemented in `retire`) --- RainbowC/include/rainbow.hpp | 3 +++ RainbowC/src/rainbow.cpp | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/RainbowC/include/rainbow.hpp b/RainbowC/include/rainbow.hpp index 5f3670f..4bf4866 100644 --- a/RainbowC/include/rainbow.hpp +++ b/RainbowC/include/rainbow.hpp @@ -105,6 +105,7 @@ namespace eosio { * @param stake_to - the escrow account where stake is held, or `deletestake` * to remove a row from the stakes table * @param deferred - staking relationship does not transfer stake to escrow. + * @param proportional - redeem by proportion of escrow rather than by staking ratio. * @param memo - the memo string to accompany the transaction. * * @pre Token symbol must have already been created by this issuer @@ -121,6 +122,7 @@ namespace eosio { const name& stake_token_contract, const name& stake_to, const bool& deferred, + const bool& proportional, const string& memo); /** * This action issues a `quantity` of tokens to the issuer account, and transfers @@ -282,6 +284,7 @@ namespace eosio { name stake_token_contract; name stake_to; bool deferred; + bool proportional; uint64_t primary_key()const { return index; }; uint128_t by_secondary() const { diff --git a/RainbowC/src/rainbow.cpp b/RainbowC/src/rainbow.cpp index d210602..c7d9461 100644 --- a/RainbowC/src/rainbow.cpp +++ b/RainbowC/src/rainbow.cpp @@ -118,6 +118,7 @@ void token::setstake( const name& issuer, const name& stake_token_contract, const name& stake_to, const bool& deferred, + const bool& proportional, const string& memo) { require_auth( issuer ); @@ -153,7 +154,8 @@ void token::setstake( const name& issuer, bool restaking = token_bucket != sk.token_bucket || stake_per_bucket != sk.stake_per_bucket || stake_to != sk.stake_to || - deferred != sk.deferred; + deferred != sk.deferred || + proportional != sk.proportional; bool destaking = stake_to == sk.stake_to && stake_per_bucket.amount == 0; if( st.supply.amount != 0 ) { @@ -172,6 +174,7 @@ void token::setstake( const name& issuer, s.stake_token_contract = stake_token_contract; s.stake_to = stake_to; s.deferred = deferred; + s.proportional = proportional; }); if( restaking && !deferred ) { stake_one( sk, st.issuer, st.supply ); @@ -189,6 +192,7 @@ void token::setstake( const name& issuer, s.stake_token_contract = stake_token_contract; s.stake_to = stake_to; s.deferred = deferred; + s.proportional = proportional; }); if( st.supply.amount != 0 ) { stake_one( sk, st.issuer, st.supply ); @@ -252,7 +256,8 @@ void token::unstake_one( const stake_stats& sk, const name& owner, const asset& if( sk.stake_per_bucket.amount > 0 ) { asset stake_quantity = sk.stake_per_bucket; stake_quantity.amount = (int64_t)((int128_t)quantity.amount*sk.stake_per_bucket.amount/sk.token_bucket.amount); - // TBD: are there potential exploits based on rounding inaccuracy? + // TODO (a) if proportional, compute unstake amount based on current escrow balance and note in memo string + // (b) if not proportional, check that escrow is fully funded action( permission_level{sk.stake_to,"active"_n}, sk.stake_token_contract, From 4538bafa5e14c795a74b5c381b463a1a68f364a3 Mon Sep 17 00:00:00 2001 From: Chuck Harrison Date: Tue, 16 Nov 2021 13:53:10 -0800 Subject: [PATCH 5/7] convert config table to singleton --- RainbowC/include/rainbow.hpp | 7 ++-- RainbowC/src/rainbow.cpp | 70 ++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/RainbowC/include/rainbow.hpp b/RainbowC/include/rainbow.hpp index 4bf4866..b48b731 100644 --- a/RainbowC/include/rainbow.hpp +++ b/RainbowC/include/rainbow.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -273,10 +274,10 @@ namespace eosio { time_point config_locked_until; bool transfers_frozen; bool approved; - - uint64_t primary_key()const { return 0; } // single row per scope }; + // TODO add another symbol_code-scoped singleton for logo info + struct [[eosio::table]] stake_stats { uint64_t index; asset token_bucket; @@ -294,7 +295,7 @@ namespace eosio { typedef eosio::multi_index< "accounts"_n, account > accounts; typedef eosio::multi_index< "stat"_n, currency_stats > stats; - typedef eosio::multi_index< "configs"_n, currency_config > configs; + typedef eosio::singleton< "configs"_n, currency_config > configs; typedef eosio::multi_index < "stakes"_n, stake_stats, indexed_by < "staketoken"_n, diff --git a/RainbowC/src/rainbow.cpp b/RainbowC/src/rainbow.cpp index c7d9461..88514e8 100644 --- a/RainbowC/src/rainbow.cpp +++ b/RainbowC/src/rainbow.cpp @@ -42,7 +42,7 @@ void token::create( const name& issuer, // token exists const auto& st = *existing; configs configtable( get_self(), sym.code().raw() ); - const auto& cf = *configtable.begin(); + auto cf = configtable.get(); check( cf.config_locked_until.time_since_epoch() < current_time_point().time_since_epoch(), "token reconfiguration is locked" ); check( st.issuer == issuer, "mismatched issuer account" ); @@ -58,14 +58,13 @@ void token::create( const name& issuer, s.max_supply = maximum_supply; s.issuer = issuer; }); - configtable.modify (cf, issuer, [&]( auto& s ) { - s.membership_mgr = membership_mgr; - s.withdrawal_mgr = withdrawal_mgr; - s.withdraw_to = withdraw_to; - s.freeze_mgr = freeze_mgr; - s.redeem_locked_until = redeem_locked_until; - s.config_locked_until = config_locked_until; - }); + cf.membership_mgr = membership_mgr; + cf.withdrawal_mgr = withdrawal_mgr; + cf.withdraw_to = withdraw_to; + cf.freeze_mgr = freeze_mgr; + cf.redeem_locked_until = redeem_locked_until; + cf.config_locked_until = config_locked_until; + configtable.set( cf, issuer ); return; } // new token @@ -75,17 +74,17 @@ void token::create( const name& issuer, s.issuer = issuer; }); configs configtable( get_self(), sym.code().raw() ); - configtable.emplace( issuer, [&]( auto& s ) { - s.membership_mgr = membership_mgr; - s.withdrawal_mgr = withdrawal_mgr; - s.withdraw_to = withdraw_to; - s.freeze_mgr = freeze_mgr; - s.redeem_locked_until = redeem_locked_until; - s.config_locked_until = config_locked_until; - s.transfers_frozen = false; - s.approved = false; - - }); + currency_config new_config{ + .membership_mgr = membership_mgr, + .withdrawal_mgr = withdrawal_mgr, + .withdraw_to = withdraw_to, + .freeze_mgr = freeze_mgr, + .redeem_locked_until = redeem_locked_until, + .config_locked_until = config_locked_until, + .transfers_frozen = false, + .approved = false + }; + configtable.set( new_config, issuer ); } void token::approve( const symbol_code& symbolcode, const bool& reject_and_clear ) @@ -95,19 +94,18 @@ void token::approve( const symbol_code& symbolcode, const bool& reject_and_clear stats statstable( get_self(), sym_code_raw ); const auto& st = statstable.get( sym_code_raw, "token with symbol does not exist" ); configs configtable( get_self(), sym_code_raw ); - const auto& cf = *configtable.begin(); + auto cf = configtable.get(); if( reject_and_clear ) { check( st.supply.amount == 0, "cannot clear with outstanding tokens" ); stakes stakestable( get_self(), sym_code_raw ); for( auto itr = stakestable.begin(); itr != stakestable.end(); ) { itr = stakestable.erase(itr); } - configtable.erase( configtable.begin() ); + configtable.remove( ); statstable.erase( statstable.iterator_to(st) ); } else { - configtable.modify (cf, st.issuer, [&]( auto& s ) { - s.approved = true; - }); + cf.approved = true; + configtable.set (cf, st.issuer ); } } @@ -141,7 +139,7 @@ void token::setstake( const name& issuer, stats statstable( get_self(), sym_code_raw ); const auto& st = statstable.get( sym_code_raw, "token with symbol does not exist" ); configs configtable( get_self(), sym_code_raw ); - const auto& cf = *configtable.begin(); + const auto& cf = configtable.get(); check( cf.config_locked_until.time_since_epoch() < current_time_point().time_since_epoch(), "token reconfiguration is locked" ); check( st.issuer == issuer, "mismatched issuer account" ); @@ -210,7 +208,7 @@ void token::issue( const asset& quantity, const string& memo ) stats statstable( get_self(), sym.code().raw() ); const auto& st = statstable.get( sym.code().raw(), "token with symbol does not exist, create token before issue" ); configs configtable( get_self(), sym.code().raw() ); - const auto& cf = *configtable.begin(); + const auto& cf = configtable.get(); check( cf.approved, "cannot issue until token is approved" ); require_auth( st.issuer ); check( quantity.is_valid(), "invalid quantity" ); @@ -285,7 +283,7 @@ void token::retire( const name& owner, const asset& quantity, const string& memo stats statstable( get_self(), sym.code().raw() ); const auto& st = statstable.get( sym.code().raw(), "token with symbol does not exist" ); configs configtable( get_self(), sym.code().raw() ); - const auto& cf = *configtable.begin(); + const auto& cf = configtable.get(); if( cf.redeem_locked_until.time_since_epoch() < current_time_point().time_since_epoch() ) { check( !cf.transfers_frozen, "transfers are frozen"); } else { @@ -317,7 +315,7 @@ void token::transfer( const name& from, stats statstable( get_self(), sym_code_raw ); const auto& st = statstable.get( sym_code_raw ); configs configtable( get_self(), sym_code_raw ); - const auto& cf = *configtable.begin(); + const auto& cf = configtable.get(); if( cf.membership_mgr != allowallacct ) { accounts to_acnts( get_self(), to.value ); @@ -384,7 +382,7 @@ void token::open( const name& owner, const symbol_code& symbolcode, const name& stats statstable( get_self(), sym_code_raw ); const auto& st = statstable.get( sym_code_raw, "symbol does not exist" ); configs configtable( get_self(), sym_code_raw ); - const auto& cf = *configtable.begin(); + const auto& cf = configtable.get(); if( cf.membership_mgr != allowallacct) { require_auth( cf.membership_mgr ); } @@ -403,7 +401,7 @@ void token::close( const name& owner, const symbol_code& symbolcode ) stats statstable( get_self(), sym_code_raw ); const auto& st = statstable.get( sym_code_raw, "symbol does not exist" ); configs configtable( get_self(), sym_code_raw ); - const auto& cf = *configtable.begin(); + const auto& cf = configtable.get(); if( cf.membership_mgr == allowallacct || !has_auth( cf.membership_mgr ) ) { require_auth( owner ); } @@ -420,12 +418,11 @@ void token::freeze( const symbol_code& symbolcode, const bool& freeze, const str stats statstable( get_self(), sym_code_raw ); const auto& st = statstable.get( sym_code_raw, "symbol does not exist" ); configs configtable( get_self(), sym_code_raw ); - const auto& cf = *configtable.begin(); + auto cf = configtable.get(); check( memo.size() <= 256, "memo has more than 256 bytes" ); require_auth( cf.freeze_mgr ); - configtable.modify (cf, same_payer, [&]( auto& s ) { - s.transfers_frozen = freeze; - }); + cf.transfers_frozen = freeze; + configtable.set (cf, st.issuer ); } void token::resetram( const name& table, const string& scope, const uint32_t& limit ) @@ -447,6 +444,9 @@ void token::resetram( const name& table, const string& scope, const uint32_t& li for( auto itr = stakestable.begin(); itr != stakestable.end() && counter Date: Wed, 17 Nov 2021 11:06:22 -0800 Subject: [PATCH 6/7] add display data table (logos, etc) --- RainbowC/include/rainbow.hpp | 44 ++++++++++++++++++++++--- RainbowC/ricardian/rainbow.contracts.md | 13 ++++++++ RainbowC/src/rainbow.cpp | 30 +++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/RainbowC/include/rainbow.hpp b/RainbowC/include/rainbow.hpp index b48b731..99fdc8f 100644 --- a/RainbowC/include/rainbow.hpp +++ b/RainbowC/include/rainbow.hpp @@ -125,6 +125,31 @@ namespace eosio { const bool& deferred, const bool& proportional, const string& memo); + + /** + * Allows `issuer` account to create or update display metadata for a token. All fields + * except `name` and `json_meta` are expected to be urls. Issuer pays for RAM. + * The currency_display table is intended for apps to access (e.g. via nodeos chain API). + * + * @param issuer - the account that created the token, + * @param symbol_code - the token, + * @param memo - the memo string to accompany the transaction. + * + * @pre Token symbol must have already been created by this issuer + * @pre String parameters must be within length limits + * name < 32 char, json_meta < 512 char, all others < 128 char + */ + [[eosio::action]] + void setdisplay( const name& issuer, + const symbol_code& symbolcode, + const string& name, + const string& logo, + const string& logo_lg, + const string& web_link, + const string& background, + const string& json_meta + ); + /** * This action issues a `quantity` of tokens to the issuer account, and transfers * a proportional amount of stake to escrow if staking is configured. @@ -240,6 +265,7 @@ namespace eosio { using create_action = eosio::action_wrapper<"create"_n, &token::create>; using approve_action = eosio::action_wrapper<"approve"_n, &token::approve>; using setstake_action = eosio::action_wrapper<"setstake"_n, &token::setstake>; + using setdisplay_action = eosio::action_wrapper<"setdisplay"_n, &token::setdisplay>; using issue_action = eosio::action_wrapper<"issue"_n, &token::issue>; using retire_action = eosio::action_wrapper<"retire"_n, &token::retire>; using transfer_action = eosio::action_wrapper<"transfer"_n, &token::transfer>; @@ -251,13 +277,14 @@ namespace eosio { const name allowallacct = "allowallacct"_n; const name deletestakeacct = "deletestake"_n; const int max_stake_count = 8; // don't use too much cpu time to complete transaction - struct [[eosio::table]] account { + + struct [[eosio::table]] account { // scoped on account name asset balance; uint64_t primary_key()const { return balance.symbol.code().raw(); } }; - struct [[eosio::table]] currency_stats { + struct [[eosio::table]] currency_stats { // scoped on token symbol code asset supply; asset max_supply; name issuer; @@ -265,7 +292,7 @@ namespace eosio { uint64_t primary_key()const { return supply.symbol.code().raw(); } }; - struct [[eosio::table]] currency_config { + struct [[eosio::table]] currency_config { // scoped on token symbol code name membership_mgr; name withdrawal_mgr; name withdraw_to; @@ -276,7 +303,15 @@ namespace eosio { bool approved; }; - // TODO add another symbol_code-scoped singleton for logo info + struct [[eosio::table]] currency_display { // scoped on token symbol code + string name; + string logo; + string logo_lg; + string web_link; + string background; + string json_meta; + }; + struct [[eosio::table]] stake_stats { uint64_t index; @@ -296,6 +331,7 @@ namespace eosio { typedef eosio::multi_index< "accounts"_n, account > accounts; typedef eosio::multi_index< "stat"_n, currency_stats > stats; typedef eosio::singleton< "configs"_n, currency_config > configs; + typedef eosio::singleton< "displays"_n, currency_display > displays; typedef eosio::multi_index < "stakes"_n, stake_stats, indexed_by < "staketoken"_n, diff --git a/RainbowC/ricardian/rainbow.contracts.md b/RainbowC/ricardian/rainbow.contracts.md index a9230e8..9590388 100644 --- a/RainbowC/ricardian/rainbow.contracts.md +++ b/RainbowC/ricardian/rainbow.contracts.md @@ -125,6 +125,19 @@ A proportionate number of staking tokens are transferred from the stake_to escro {{memo}} {{/if}} +

setdisplay

+ +--- +spec_version: "0.2.0" +title: Create or Update Display Metadata for a Token +summary: 'Set display metadata' +icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ +--- + +{{issuer}} agrees to associate a set of display metadata with an existing token {{symbol_code}}. + +RAM will deducted from {{issuer}}’s resources to update the necessary records. +

setstake

--- diff --git a/RainbowC/src/rainbow.cpp b/RainbowC/src/rainbow.cpp index 88514e8..13db0b5 100644 --- a/RainbowC/src/rainbow.cpp +++ b/RainbowC/src/rainbow.cpp @@ -85,6 +85,9 @@ void token::create( const name& issuer, .approved = false }; configtable.set( new_config, issuer ); + displays displaytable( get_self(), sym.code().raw() ); + currency_display new_display{ "", "", "", "", "", "" }; + displaytable.set( new_display, issuer ); } void token::approve( const symbol_code& symbolcode, const bool& reject_and_clear ) @@ -198,6 +201,33 @@ void token::setstake( const name& issuer, } +void token::setdisplay( const name& issuer, + const symbol_code& symbolcode, + const string& token_name, + const string& logo, + const string& logo_lg, + const string& web_link, + const string& background, + const string& json_meta ) +{ + require_auth( issuer ); + auto sym_code_raw = symbolcode.raw(); + displays displaytable( get_self(), sym_code_raw ); + auto dt = displaytable.get(); + check( token_name.size() <= 32, "name has more than 32 bytes" ); + const string *url_list[] = { &logo, &logo_lg, &web_link, &background }; + for( const string* s : url_list ) { + check( s->size() <= 128, "url string has more than 128 bytes" ); + } + check( json_meta.size() <= 512, "json metadata has more than 512 bytes" ); + dt.name = token_name; + dt.logo = logo; + dt.logo_lg = logo_lg; + dt.web_link = web_link; + dt.background = background; + dt.json_meta = json_meta; + displaytable.set( dt, issuer ); +} void token::issue( const asset& quantity, const string& memo ) { From de8a997a1e49d309a8261a901b6acb2283838566 Mon Sep 17 00:00:00 2001 From: Chuck Harrison Date: Wed, 17 Nov 2021 12:18:11 -0800 Subject: [PATCH 7/7] clean up singleton contracts --- RainbowC/include/rainbow.hpp | 4 +++- RainbowC/src/rainbow.cpp | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/RainbowC/include/rainbow.hpp b/RainbowC/include/rainbow.hpp index 99fdc8f..a140d59 100644 --- a/RainbowC/include/rainbow.hpp +++ b/RainbowC/include/rainbow.hpp @@ -137,7 +137,7 @@ namespace eosio { * * @pre Token symbol must have already been created by this issuer * @pre String parameters must be within length limits - * name < 32 char, json_meta < 512 char, all others < 128 char + * name < 32 char, json_meta < 1024 char, all others < 256 char */ [[eosio::action]] void setdisplay( const name& issuer, @@ -331,7 +331,9 @@ namespace eosio { typedef eosio::multi_index< "accounts"_n, account > accounts; typedef eosio::multi_index< "stat"_n, currency_stats > stats; typedef eosio::singleton< "configs"_n, currency_config > configs; + typedef eosio::multi_index< "configs"_n, currency_config > dump_for_config; typedef eosio::singleton< "displays"_n, currency_display > displays; + typedef eosio::multi_index< "displays"_n, currency_display > dump_for_display; typedef eosio::multi_index < "stakes"_n, stake_stats, indexed_by < "staketoken"_n, diff --git a/RainbowC/src/rainbow.cpp b/RainbowC/src/rainbow.cpp index 13db0b5..f2e70a6 100644 --- a/RainbowC/src/rainbow.cpp +++ b/RainbowC/src/rainbow.cpp @@ -98,6 +98,7 @@ void token::approve( const symbol_code& symbolcode, const bool& reject_and_clear const auto& st = statstable.get( sym_code_raw, "token with symbol does not exist" ); configs configtable( get_self(), sym_code_raw ); auto cf = configtable.get(); + displays displaytable( get_self(), sym_code_raw ); if( reject_and_clear ) { check( st.supply.amount == 0, "cannot clear with outstanding tokens" ); stakes stakestable( get_self(), sym_code_raw ); @@ -105,6 +106,7 @@ void token::approve( const symbol_code& symbolcode, const bool& reject_and_clear itr = stakestable.erase(itr); } configtable.remove( ); + displaytable.remove( ); statstable.erase( statstable.iterator_to(st) ); } else { cf.approved = true; @@ -217,9 +219,9 @@ void token::setdisplay( const name& issuer, check( token_name.size() <= 32, "name has more than 32 bytes" ); const string *url_list[] = { &logo, &logo_lg, &web_link, &background }; for( const string* s : url_list ) { - check( s->size() <= 128, "url string has more than 128 bytes" ); + check( s->size() <= 256, "url string has more than 256 bytes" ); } - check( json_meta.size() <= 512, "json metadata has more than 512 bytes" ); + check( json_meta.size() <= 1024, "json metadata has more than 1024 bytes" ); dt.name = token_name; dt.logo = logo; dt.logo_lg = logo_lg; @@ -460,7 +462,7 @@ void token::resetram( const name& table, const string& scope, const uint32_t& li require_auth2( get_self().value, "active"_n.value ); uint64_t scope_raw; check( !scope.empty(), "scope string is empty" ); - if( scope.length() <= 7 ) { + if( isupper(scope.c_str()[0]) ) { symbol_code code(scope); check( code.is_valid(), "invalid symbol code" ); scope_raw = code.raw();