From 8fb0247e1ac3d61d20802eaf2f81c2511039174e Mon Sep 17 00:00:00 2001 From: Jozef Henzl Date: Wed, 7 Nov 2018 17:28:00 +0100 Subject: [PATCH] make ERC20 more user friendly - proper handling of token amounts - code cleanup --- helpers/math.c | 26 ++++++ helpers/math.h | 18 ++++ helpers/uint256.c | 11 ++- helpers/uint256.h | 1 + tests/test_helpers.cpp | 10 +++ zephyr/erc20_shell.c | 196 ++++++++++++++++++++++++++++++----------- zephyr/utils.c | 28 ++++++ zephyr/utils.h | 3 + 8 files changed, 240 insertions(+), 53 deletions(-) create mode 100644 helpers/math.c create mode 100644 helpers/math.h diff --git a/helpers/math.c b/helpers/math.c new file mode 100644 index 0000000..fbfc4da --- /dev/null +++ b/helpers/math.c @@ -0,0 +1,26 @@ +/** +* @brief +* @file math.c +* @author J.H. +* @date 2018-11-07 +*/ + +/* system includes */ + +/* local includes */ +#include "math.h" + + +uint64_t ipow(uint64_t base, uint8_t exp) +{ + uint64_t result = 1; + for (;;) + { + if (exp & 1) { result *= base; } + exp >>= 1; + if (!exp) { break; } + base *= base; + } + + return result; +} diff --git a/helpers/math.h b/helpers/math.h new file mode 100644 index 0000000..b96f02e --- /dev/null +++ b/helpers/math.h @@ -0,0 +1,18 @@ +#ifndef _MATH_H_ +#define _MATH_H_ +/* system includes */ +#include +/* local includes */ + + +#ifdef __cplusplus +extern "C" { +#endif +uint64_t ipow(uint64_t base, uint8_t exp); + +#ifdef __cplusplus +} +#endif + +#endif /* _MATH_H_ */ + diff --git a/helpers/uint256.c b/helpers/uint256.c index 4954e2b..23f633f 100644 --- a/helpers/uint256.c +++ b/helpers/uint256.c @@ -23,6 +23,7 @@ #include #include "hextobin.h" +#include "endian.h" static const char HEXDIGITS[] = "0123456789abcdef"; @@ -56,8 +57,8 @@ void readu256BE(const uint8_t *buffer, uint256_t *target) { void writeu128BE(const uint128_t *number, uint8_t *buffer) { - *(uint64_t*)buffer = ENDIAN_SWAP_U64(LOWER_P(number)); - *(uint64_t*)(buffer + sizeof(uint64_t)) = ENDIAN_SWAP_U64(UPPER_P(number)); + *(uint64_t*)buffer = ENDIAN_SWAP_U64(UPPER_P(number)); + *(uint64_t*)(buffer + sizeof(uint64_t)) = ENDIAN_SWAP_U64(LOWER_P(number)); } void writeu256BE(const uint256_t *number, uint8_t *buffer) @@ -587,6 +588,12 @@ void set256_uint64(uint256_t *target, uint64_t val) LOWER(LOWER_P(target)) = val; } +void set256_uint64BE(uint256_t *target, uint64_t val) +{ + clear256(target); + LOWER(LOWER_P(target)) = htobe64(val); +} + // connect hexencoded ASCII to an uint256 int fromstring256(const char *hexencoded, uint256_t *out) { diff --git a/helpers/uint256.h b/helpers/uint256.h index 7ab5205..cf53c3b 100644 --- a/helpers/uint256.h +++ b/helpers/uint256.h @@ -56,6 +56,7 @@ void writeu256BE(const uint256_t *number, uint8_t *buffer); void writeu128BE(const uint128_t *number, uint8_t *buffer); void set256_uint64(uint256_t *target, uint64_t val); +void set256_uint64BE(uint256_t *target, uint64_t val); bool zero128(const uint128_t *number); bool zero256(const uint256_t *number); diff --git a/tests/test_helpers.cpp b/tests/test_helpers.cpp index af56be0..e9b0b6f 100644 --- a/tests/test_helpers.cpp +++ b/tests/test_helpers.cpp @@ -16,6 +16,7 @@ #include "helpers/hextobin.h" #include "helpers/fp2str.h" #include "helpers/uint256.h" +#include "helpers/math.h" #define TEST_BUF_LEN 64 @@ -133,3 +134,12 @@ TEST(TEST_HELPERS, TEST_STR2UINT256) } } + +TEST(TEST_HELPERS, TEST_IPOW) +{ + uint64_t res; + + res = ipow(10, 18); + + ASSERT_EQ(res, 1000000000000000000); +} diff --git a/zephyr/erc20_shell.c b/zephyr/erc20_shell.c index 5df6cfd..cf616cd 100644 --- a/zephyr/erc20_shell.c +++ b/zephyr/erc20_shell.c @@ -13,10 +13,10 @@ #include /* local includes */ -#include "erc20_shell.h" +#include "endian.h" +#include "helpers/math.h" #include "helpers/uint256.h" #include "helpers/hextobin.h" -#include "wallet.h" #include "eth/transaction.h" #include "eth/address.h" #include "eth/data.h" @@ -25,6 +25,56 @@ #include "erc20_abi.h" #include "zephyr/utils.h" #include "zephyr/web3_rpc.h" +#include "zephyr/erc20_shell.h" +#include "zephyr/wallet.h" + +#define TOKEN_SYMBOL_MAX 3 +#define TOKENS_MAX 5 + +typedef struct { + address_t address; + uint8_t decimals; + char symbol[TOKEN_SYMBOL_MAX+1]; + uint8_t used; +} token_nfo_t; + +static token_nfo_t _tokens[TOKENS_MAX]; +uint8_t _token_idx = 0; + +static int _token_add(const address_t *addr, uint8_t decimals, char *symbol) +{ + assert(addr != NULL); + assert(symbol != NULL); + uint8_t tkn_idx = 0; + for(tkn_idx = 0; tkn_idx < TOKENS_MAX; tkn_idx++) { + if(_tokens[tkn_idx].used == 1) { continue; } + _tokens[tkn_idx].decimals = decimals; + memcpy(&_tokens[tkn_idx].address, addr, sizeof(address_t)); + strncpy(_tokens[tkn_idx].symbol, symbol, TOKEN_SYMBOL_MAX); + _tokens[tkn_idx].symbol[TOKEN_SYMBOL_MAX] = '\0'; + _tokens[tkn_idx].used = 1; + return 0; + } + return -1; +} + +static int _get_token_by_index(const struct shell *shell, const char *arg, token_nfo_t **out) +{ + errno = 0; + char *endptr = NULL; + uint8_t token_idx = strtoul(arg, &endptr, 10); + if((errno != 0) || ((endptr != NULL) && (*endptr != 0)) || (token_idx >= TOKENS_MAX)) { + shell_fprintf(shell, SHELL_WARNING, "invalid token index\n"); + return -1; + } + + *out = &_tokens[token_idx]; + if((*out)->used == 0) { + shell_fprintf(shell, SHELL_WARNING, "invalid token index\n"); + return -1; + } + return 0; +} static int erc20_balance(const struct shell *shell, size_t argc, char *argv[]) { @@ -32,17 +82,17 @@ static int erc20_balance(const struct shell *shell, size_t argc, char *argv[]) memset(&tx, 0, sizeof(tx)); account_t *acc = wallet_get_account(); assert(acc != NULL); - // first parameter is recipient address - if(argc < 2) { - printk("missing argument: address\n"); + if(argc != 2) { + shell_fprintf(shell, SHELL_WARNING, "use: erc20 balance \n"); return -1; } - if(hextobin(argv[1], (uint8_t*)&tx.to, sizeof(tx.to)) < 0) { - printk("invalid argument: address\n"); + // first parameter is a token index + token_nfo_t *tkn_p = NULL; + if(_get_token_by_index(shell, argv[1], &tkn_p) != 0) { return -1; } - // build the transaction + // build the transaction data_block_t tx_data; uint8_t datablk[256+4]; tx_data.data = datablk; @@ -54,13 +104,13 @@ static int erc20_balance(const struct shell *shell, size_t argc, char *argv[]) } tx.data = datablk; tx.data_len = encoded_data_len; + tx.to = tkn_p->address; uint256_t out; if(web3_eth_call(&acc->address, &tx, &out, TX_NO_FROM | TX_NO_GAS | TX_NO_GASPRICE | TX_NO_VALUE) < 0) { return -1; } - - printk_uint256(shell, &out); + shell_print_decimal_u256(shell, &out, tkn_p->decimals); return 0; } @@ -70,40 +120,53 @@ static int erc20_transfer(const struct shell *shell, size_t argc, char *argv[]) transaction_t tx; account_t *acc = wallet_get_account(); assert(acc != NULL); - // first parameter is recipient address - if(argc < 2) { - printk("missing argument: address\n"); + if(argc != 4) { + shell_fprintf(shell, SHELL_WARNING, "use: erc20 transfer \n"); return -1; } - if(hextobin(argv[1], (uint8_t*)&tx.to, sizeof(tx.to)) < 0) { - printk("invalid argument: address\n"); + // first parameter is an index + token_nfo_t *tkn_p = NULL; + if(_get_token_by_index(shell, argv[1], &tkn_p) != 0) { return -1; } - // amount to send - if(argc < 3) { - printk("missing argument: transfer amount\n"); + + // second parameter is recipient address + address_t address_to; + if(hextobin(argv[2], (uint8_t*)&address_to, sizeof(address_to)) < 0) { + shell_fprintf(shell, SHELL_NORMAL, "invalid argument: address\n"); return -1; } - char *endptr = NULL; - uint64_t tx_value = strtoul(argv[2], &endptr, 10); - if((errno != 0) || ((endptr != NULL) && (*endptr != 0))) { - printk("invalid argument: amount\n"); + // third parameter is amount to transfer + uint64_t tx_value = 0; + if(shell_get_uint64(argv[3], &tx_value) != 0) { + shell_fprintf(shell, SHELL_WARNING, "invalid argument: amount\n"); return -1; } + // tokens to transfer + uint256_t decimals_u256; + clear256(&decimals_u256); + set256_uint64(&decimals_u256, ipow(10, tkn_p->decimals)); + uint256_t tx_value_u256; + clear256(&tx_value_u256); + set256_uint64(&tx_value_u256, tx_value); + uint256_t tx_result_u256; + // mul256 requires LE values + mul256(&tx_value_u256, &decimals_u256, &tx_result_u256); + // convert to BE + writeu256BE(&tx_result_u256, (uint8_t*)&tx_value_u256); // build the transaction + assert(tkn_p != NULL); tx.nonce = acc->nonce; - tx.gas_price = 1 * 1000000000; - tx.gas_limit = 31843; + tx.gas_price = acc->gas_price; + tx.gas_limit = 35427; + tx.to = tkn_p->address; clear256(&tx.value); - uint256_t tx_value_256; - clear256(&tx_value_256); - LOWER(LOWER(tx_value_256)) = tx_value; data_block_t tx_data; uint8_t datablk[256+4]; tx_data.data = datablk; tx_data.data_len = sizeof(datablk); - int encoded_data_len = Token_transfer(&acc->address, &tx_value_256, &tx_data); + int encoded_data_len = Token_transfer(&address_to, &tx_value_u256, &tx_data); if(encoded_data_len < 0) { shell_fprintf(shell, SHELL_WARNING, "Can't encode Token::transfer()\n"); return -1; @@ -117,7 +180,7 @@ static int erc20_transfer(const struct shell *shell, size_t argc, char *argv[]) uint256_t tx_hash; if(web3_eth_sendRawTransaction(buf, txlen, &tx_hash) < 0) { - printk("Error: eth_sendRawTransaction()\n"); + shell_fprintf(shell, SHELL_WARNING, "Error: eth_sendRawTransaction()\n"); return -1; } printk_uint256(shell, &tx_hash); @@ -125,22 +188,12 @@ static int erc20_transfer(const struct shell *shell, size_t argc, char *argv[]) return 0; } -static int erc20_decimals(const struct shell *shell, size_t argc, char *argv[]) +static int _token_decimals(const address_t *addr, uint8_t *out) { transaction_t tx; memset(&tx, 0, sizeof(tx)); account_t *acc = wallet_get_account(); assert(acc != NULL); - // first parameter is recipient address - if(argc < 2) { - printk("missing argument: address\n"); - return -1; - } - if(hextobin(argv[1], (uint8_t*)&tx.to, sizeof(tx.to)) < 0) { - printk("invalid argument: address\n"); - return -1; - } - // build the transaction data_block_t tx_data; uint8_t datablk[256+4]; @@ -148,19 +201,18 @@ static int erc20_decimals(const struct shell *shell, size_t argc, char *argv[]) tx_data.data_len = sizeof(datablk); int encoded_data_len = Token_decimals(&tx_data); if(encoded_data_len < 0) { - shell_fprintf(shell, SHELL_WARNING, "Can't encode Token::decimals()\n"); return -1; } tx.data = datablk; tx.data_len = encoded_data_len; + tx.to = *addr; - uint256_t out; - if(web3_eth_call(&acc->address, &tx, &out, TX_NO_FROM | TX_NO_GAS | TX_NO_GASPRICE | TX_NO_VALUE) < 0) { + uint256_t out_u256; + if(web3_eth_call(&acc->address, &tx, &out_u256, TX_NO_FROM | TX_NO_GAS | TX_NO_GASPRICE | TX_NO_VALUE) < 0) { return -1; } - printk_uint256(shell, &out); - + *out = LOWER(LOWER(out_u256)); return 0; } @@ -171,12 +223,12 @@ static int erc20_totalSupply(const struct shell *shell, size_t argc, char *argv[ account_t *acc = wallet_get_account(); assert(acc != NULL); // first parameter is recipient address - if(argc < 2) { - printk("missing argument: address\n"); + if(argc != 2) { + printk("missing argument: token index\n"); return -1; } - if(hextobin(argv[1], (uint8_t*)&tx.to, sizeof(tx.to)) < 0) { - printk("invalid argument: address\n"); + token_nfo_t *tkn_p = NULL; + if(_get_token_by_index(shell, argv[1], &tkn_p) != 0) { return -1; } // build the transaction @@ -192,20 +244,62 @@ static int erc20_totalSupply(const struct shell *shell, size_t argc, char *argv[ } tx.data = datablk; tx.data_len = encoded_data_len; + tx.to = tkn_p->address; uint256_t out; if(web3_eth_call(&acc->address, &tx, &out, TX_NO_FROM | TX_NO_GAS | TX_NO_GASPRICE | TX_NO_VALUE) < 0) { return -1; } - printk_uint256(shell, &out); + shell_print_decimal_u256(shell, &out, tkn_p->decimals); + + return 0; +} +static int erc20_add(const struct shell *shell, size_t argc, char *argv[]) +{ + address_t addr; + // first parameter is recipient address + if(argc < 2) { + printk("missing argument: address\n"); + return -1; + } + if(hextobin(argv[1], (uint8_t*)&addr, sizeof(addr)) < 0) { + printk("invalid argument: address\n"); + return -1; + } + uint8_t decimals = 0; + if(_token_decimals(&addr, &decimals) < 0) { + shell_fprintf(shell, SHELL_WARNING, "couldn't get token decimals\n"); + return -1; + } + int ret = _token_add(&addr, decimals, "TKN"); + if(ret < 0) { + shell_fprintf(shell, SHELL_WARNING, "can't add token\n"); + return -1; + } + return 0; +} + +static int erc20_list(const struct shell *shell, size_t argc, char *argv[]) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + // first parameter is recipient address + for(int i = 0; i < TOKENS_MAX; i++) { + token_nfo_t *nfo = &_tokens[i]; + if(nfo->used == 0) { continue; } + shell_fprintf(shell, SHELL_NORMAL, "%d\t%s\t%u\t", i, nfo->symbol, nfo->decimals); + printk_hex(shell, (uint8_t*)&nfo->address, sizeof(address_t)); + shell_fprintf(shell, SHELL_NORMAL, "\n"); + } return 0; } SHELL_CREATE_STATIC_SUBCMD_SET(sub_erc20) { + SHELL_CMD(add, NULL, "add", erc20_add), SHELL_CMD(balance, NULL, "balance", erc20_balance), - SHELL_CMD(decimals, NULL, "decimals", erc20_decimals), + SHELL_CMD(list, NULL, "list", erc20_list), SHELL_CMD(transfer, NULL, "transfer", erc20_transfer), SHELL_CMD(totalSupply, NULL, "total supply", erc20_totalSupply), SHELL_SUBCMD_SET_END diff --git a/zephyr/utils.c b/zephyr/utils.c index ef4c6f7..4f67e33 100644 --- a/zephyr/utils.c +++ b/zephyr/utils.c @@ -9,9 +9,11 @@ #include #include #include +#include /* local includes */ #include "utils.h" +#include "helpers/math.h" void printk_hex(const struct shell *shell, const uint8_t *data, size_t data_len) { @@ -52,3 +54,29 @@ void printk_uint256_int(const struct shell *shell, const uint256_t *v, uint8_t d } shell_fprintf(shell, SHELL_NORMAL, "%s", buf); } + +int shell_get_uint64(const char *argval, uint64_t *out) +{ + char *endptr = NULL; + errno = 0; + *out = strtoul(argval, &endptr, 10); + if((errno != 0) || ((endptr != NULL) && (*endptr != 0))) { + return -1; + } + return 0; +} + +void shell_print_decimal_u256(const struct shell *shell, const uint256_t *val, uint8_t decimals) +{ + uint256_t decimals_u256, div_u256, mod_u256; + clear256(&decimals_u256); + clear256(&div_u256); + clear256(&mod_u256); + LOWER(LOWER(decimals_u256)) = ipow(10, decimals); + divmod256(val, &decimals_u256, &div_u256, &mod_u256); + + printk_uint256_int(shell, &div_u256, 0, FMT_EMPTY); + printk("."); + printk_uint256_int(shell, &mod_u256, decimals, FMT_FIXED | FMT_NO_TRAIL_ZERO); + printk("\n"); +} diff --git a/zephyr/utils.h b/zephyr/utils.h index a83b919..633a619 100644 --- a/zephyr/utils.h +++ b/zephyr/utils.h @@ -21,6 +21,9 @@ void printk_hex(const struct shell *shell, const uint8_t *data, size_t data_len) #define printk_hex_nl(shell, data, len) printk_hex(shell, data, len); shell_fprintf(shell, SHELL_NORMAL, "\n"); void printk_uint256(const struct shell *shell, const uint256_t *v); void printk_uint256_int(const struct shell *shell, const uint256_t *v, uint8_t decimals, uint8_t fmt); + +int shell_get_uint64(const char *argval, uint64_t *out); +void shell_print_decimal_u256(const struct shell *shell, const uint256_t *val, uint8_t decimals); #ifdef __cplusplus } #endif