EOSIO / eosio.token

Reference contract for an EOSIO based token
MIT License
4 stars 7 forks source link

[feature/standartization] Transfer with fee #11

Open Avm07 opened 4 years ago

Avm07 commented 4 years ago

Sometimes business model for token smart contract want have fee from token transfer. We need standard interface how to do transfer with fee, count fees, inform receiver/sender from transaction payload, which will be used in blockchain history solution such as Hyperion, Chronicle, dfuse etc.

One of possible signature and realization for fee = 1% * quantity:

  • Allows from account to transfer to to account the quantity - fee tokens.
  • One account is debited, smart contract account is credited with fee tokens and the to account is credited with quantity - fee tokens.
  • @param from - the account to transfer from,
  • @param to - the account to be transferred to,
  • @param quantity - the quantity of tokens to be transferred,
  • @param fee - the quantity of tokens to be paid as commission,
  • @param memo - the memo string to accompany the transaction.
      [[eosio::action]] void transfer(const name& from, const name& to, const asset& quantity, const asset& fee, const string&  memo);
void token::transfer( const name& from,
                      const name& to,
                      const asset& quantity,
                      const asset& fee,
                      const string&  memo )
{
    check( from != to, "cannot transfer to self" );
    require_auth( from );
    check( is_account( to ), "to account does not exist");
    auto sym = quantity.symbol.code();
    stats statstable( get_self(), sym.raw() );
    const auto& st = statstable.get( sym.raw() );

    require_recipient( from );
    require_recipient( to );

    check( quantity.is_valid(), "invalid quantity" );
    check( fee.is_valid(), "invalid fee" );
    check( quantity.amount > 0, "must transfer positive quantity" );
    check( quantity.amount > 1, "can not transfer minimum amount");
    check( quantity.symbol == st.supply.symbol, "symbol precision mismatch" );
    check( fee.symbol == quantity.symbol, "fee symbol precision mismatch" );
    check( is_valid_fee(fee), "invalid fee quantity" );
    check( memo.size() <= 256, "memo has more than 256 bytes" );

    auto payer = has_auth( to ) ? to : from;

    sub_balance( from, quantity );
    add_balance( get_self(), fee, payer );
    add_balance( to, quantity - fee, payer );
}

bool token::is_valid_fee( const asset& quantity, const asset& fee )
{
      return fee == count_fee(quantity) ? true : false;
}

asset token::count_fee( const asset& quantity )
{
      return (quantity.amount > 100 ? asset(quantity.amount / 100, quantity.symbol) : asset(1, quantity.symbol));
}

So from transaction history we can calculate how much tokens received(quantity - fee). Total fees received by summing all transfer transactions fee field.

deckb commented 3 years ago

Moving this to the new reference token contract repo. I am not 100% sure we want to have this feature in this particular implementation of a token contract but will review.

abhi3700 commented 3 years ago

I think this type of extra parameter requirement in the standard token contract is not that necessary.

As one can fire an event via creating a blank action like this:

ACTION void feecharged(asset fee) {

}

Then, from the transaction history, one can add the total amount received as (transferred_qty - fee_qty) by summing up all the values with this contract & function name.

dougbutner commented 3 years ago

Looks like the inclusion of the extra parameter means a non-standard argument is required, meaning this would make the token incompatible with most dapps.

I'll modify it for my use case to include this as a constant, or make it settable when setting up the token.

Also, it looks like this sends the fee to the token contract itself, something to consider for every use case, as we may want it to be a different account.

Thanks for your help uploading this, it will make it much easier for me.

Avm07 commented 3 years ago

Looks like the inclusion of the extra parameter means a non-standard argument is required, meaning this would make the token incompatible with most dapps.

I'll modify it for my use case to include this as a constant, or make it settable when setting up the token.

Also, it looks like this sends the fee to the token contract itself, something to consider for every use case, as we may want it to be a different account.

Thanks for your help uploading this, it will make it much easier for me.

Yes, main problem that dapps and exchanges looks on notification via require_recipient( to ); it means that they need to do calculation of real income amount = quantity - fee. In different solutions the standart notification will be wrong, because real income amount is less then in notification.