From 38f35fd362b6cc57ea27a0ca20915202b2b74cb7 Mon Sep 17 00:00:00 2001 From: kayan Date: Mon, 7 Oct 2024 15:22:51 +0800 Subject: [PATCH] add erc20(usdt) test with btc as based token --- antelope_contracts/tests/erc20/CMakeLists.txt | 1 + .../tests/erc20/different_gas_token_tests.cpp | 614 ++++++++++++++++++ .../tests/erc20/erc20_tester.cpp | 33 +- .../tests/erc20/erc20_tester.hpp | 8 +- 4 files changed, 640 insertions(+), 16 deletions(-) create mode 100644 antelope_contracts/tests/erc20/different_gas_token_tests.cpp diff --git a/antelope_contracts/tests/erc20/CMakeLists.txt b/antelope_contracts/tests/erc20/CMakeLists.txt index 42f5a0b..1573198 100644 --- a/antelope_contracts/tests/erc20/CMakeLists.txt +++ b/antelope_contracts/tests/erc20/CMakeLists.txt @@ -20,6 +20,7 @@ add_eosio_test_executable( unit_test ${CMAKE_CURRENT_SOURCE_DIR}/erc20_tester.cpp ${CMAKE_CURRENT_SOURCE_DIR}/transfer_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/integrated_tests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/different_gas_token_tests.cpp ${EXTERNAL_DIR}/silkworm/silkworm/core/rlp/encode.cpp ${EXTERNAL_DIR}/silkworm/silkworm/core/rlp/decode.cpp ${EXTERNAL_DIR}/silkworm/silkworm/core/types/transaction.cpp diff --git a/antelope_contracts/tests/erc20/different_gas_token_tests.cpp b/antelope_contracts/tests/erc20/different_gas_token_tests.cpp new file mode 100644 index 0000000..20f6f02 --- /dev/null +++ b/antelope_contracts/tests/erc20/different_gas_token_tests.cpp @@ -0,0 +1,614 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "erc20_tester.hpp" + +using namespace eosio; +using namespace eosio::chain; +using namespace erc20_test; +using namespace eosio::testing; +using mvo = fc::mutable_variant_object; + +using intx::operator""_u256; +constexpr size_t kAddressLength{20}; + +constexpr eosio::chain::name btc_evm_account{"btc.evm"_n}; +constexpr eosio::chain::name btc_token_account {"btcbtcbtcbtc"_n}; +const eosio::chain::symbol btc_symbol{8, "BTC"}; + +struct diff_gas_token_tester : erc20_tester { + std::string address_str32(const evmc::address& x) { + std::stringstream hex_ss; + for (uint8_t c : x.bytes) { + hex_ss << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); + } + int hex_length = hex_ss.str().length(); + + std::stringstream ss; + ss << std::setfill('0') << std::setw(64 - hex_length) << 0; + for (uint8_t c : x.bytes) { + ss << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); + } + return ss.str(); + } + + std::string uint256_str32(intx::uint256 x) { + uint8_t buffer[32] = {}; + intx::be::store(buffer, x); + + std::stringstream ss; + + for (uint8_t c : buffer) { + ss << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); + } + return ss.str(); + } + + std::string int_str32(uint32_t x) { + std::stringstream hex_ss; + hex_ss << std::hex << x; + int hex_length = hex_ss.str().length(); + + std::stringstream ss; + ss << std::setfill('0') << std::setw(64 - hex_length) << 0 << std::hex << std::uppercase << x; + return ss.str(); + } + + std::string str_to_hex(const std::string& str) { + std::stringstream ss; + for (char c : str) { + ss << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); + } + return ss.str(); + } + + std::string data_str32(const std::string& str) { + std::stringstream ss; + ss << str; + int ps = 64 - (str.length() % 64); + if (ps == 64) { + ps = 0; + } + ss << std::setw(ps) << std::setfill('0') << ""; + return ss.str(); + } + + std::string evm_address; + diff_gas_token_tester() : erc20_tester(true, btc_evm_account, btc_symbol.to_string(), btc_token_account) { + create_accounts({"alice"_n}); + transfer_token(btc_token_account, faucet_account_name, "alice"_n, make_asset(100'0000'0000, btc_symbol)); + produce_block(); + transfer_token(token_account, faucet_account_name, "alice"_n, make_asset(10000'0000, token_symbol)); + produce_block(); + create_accounts({"bob"_n}); + transfer_token(btc_token_account, faucet_account_name, "bob"_n, make_asset(100'0000'0000, btc_symbol)); + produce_block(); + transfer_token(token_account, faucet_account_name, "bob"_n, make_asset(10000'0000, token_symbol)); + + produce_block(); + + evm_address = getSolidityContractAddress(); + BOOST_REQUIRE_MESSAGE(evm_address.size() == 42, std::string("address wrong: ") + evm_address); + + // init(); + } + + std::string getSolidityContractAddress() { + auto r = getRegistedTokenInfo(); + return vec_to_hex(r.address, true); + } + + token_t getRegistedTokenInfo() { + auto& db = const_cast(control->db()); + + const auto* existing_tid = db.find( + boost::make_tuple(erc20_account, erc20_account, "tokens"_n)); + if (!existing_tid) { + return {}; + } + const auto* kv_obj = db.find( + boost::make_tuple(existing_tid->id, 0)); + + auto r = fc::raw::unpack( + kv_obj->value.data(), + kv_obj->value.size()); + return r; + } + + intx::uint256 egressFee(std::optional callback = {}, std::optional context = {}) { + exec_input input; + input.context = context; + input.to = *erc20_test::from_hex(evm_address.c_str()); + BOOST_REQUIRE_MESSAGE(input.to.size() == 20, std::string("address wrong: ") + evm_address); + + bytes calldata; + uint8_t func[4] = {0x6a, 0x03, 0x66, 0xbf}; // sha3(egressFee())[:4] = 6a0366bf + + calldata.insert(calldata.end(), func, func + 4); + + input.data = calldata; + + auto res = exec(input, callback); + + BOOST_REQUIRE(res); + BOOST_REQUIRE(res->action_traces.size() == 1); + + // Since callback information was not provided the result of the + // execution is returned in the action return_value + auto out = fc::raw::unpack(res->action_traces[0].return_value); + BOOST_REQUIRE(out.status == 0); + BOOST_REQUIRE(out.data.size() == 32); + + auto result = intx::be::unsafe::load(reinterpret_cast(out.data.data())); + return result; + } + + intx::uint256 balanceOf(const char* owner, std::optional callback = {}, std::optional context = {}) { + exec_input input; + input.context = context; + input.to = *erc20_test::from_hex(evm_address.c_str()); + + bytes calldata; + uint8_t func[4] = {0x70, 0xa0, 0x82, 0x31}; // sha3(balanceOf(address))[:4] = 70a08231 + + calldata.insert(calldata.end(), func, func + 4); + auto dest_buffer = erc20_test::from_hex(owner); + uint8_t value_buffer[32] = {}; + memcpy(value_buffer + 32 - kAddressLength, dest_buffer->data(), kAddressLength); + + calldata.insert(calldata.end(), value_buffer, value_buffer + 32); + + input.data = calldata; + + auto res = exec(input, callback); + + BOOST_REQUIRE(res); + BOOST_REQUIRE(res->action_traces.size() == 1); + + // Since callback information was not provided the result of the + // execution is returned in the action return_value + auto out = fc::raw::unpack(res->action_traces[0].return_value); + BOOST_REQUIRE(out.status == 0); + BOOST_REQUIRE(out.data.size() == 32); + + auto result = intx::be::unsafe::load(reinterpret_cast(out.data.data())); + return result; + } + + void bridgeTransferERC20(evm_eoa& from, evmc::address& to, intx::uint256 amount, std::string memo, intx::uint256 egressfee) { + auto target = evmc::from_hex(evm_address); + auto txn = generate_tx(*target, egressfee, 500'000); + // bridgeTransfer(address,uint256,string) = 73761828 + txn.data = evmc::from_hex("0x73761828").value(); + txn.data += evmc::from_hex(address_str32(to)).value(); // param1 (to: address) + txn.data += evmc::from_hex(uint256_str32(amount)).value(); // param2 (amount: uint256) + txn.data += evmc::from_hex(int_str32(96)).value(); // offset memo (data: bytes) + txn.data += evmc::from_hex(int_str32(memo.size())).value(); // memo length + if (!memo.empty()) { + txn.data += evmc::from_hex(data_str32(str_to_hex(memo))).value(); // memo + } + + auto old_nonce = from.next_nonce; + from.sign(txn); + + try { + auto r = pushtx(txn); + // dlog("action trace: ${a}", ("a", r)); + } catch (...) { + from.next_nonce = old_nonce; + throw; + } + } + + void transferERC20(evm_eoa& from, evmc::address& to, intx::uint256 amount) { + auto target = evmc::from_hex(evm_address); + + auto txn = generate_tx(*target, 0, 500'000); + // transfer(address,uint256) = a9059cbb + txn.data = evmc::from_hex("0xa9059cbb").value(); + txn.data += evmc::from_hex(address_str32(to)).value(); // param1 (to: address) + txn.data += evmc::from_hex(uint256_str32(amount)).value(); // param2 (amount: uint256) + + auto old_nonce = from.next_nonce; + from.sign(txn); + + try { + auto r = pushtx(txn); + // dlog("action trace: ${a}", ("a", r)); + } catch (...) { + from.next_nonce = old_nonce; + throw; + } + } +}; + +BOOST_AUTO_TEST_SUITE(different_gas_token_tests) + +BOOST_FIXTURE_TEST_CASE(basic_transfer_test, diff_gas_token_tester) +try { + evm_eoa evm1; + auto addr_alice = silkworm::make_reserved_address("alice"_n.to_uint64_t()); + + // Give evm1 some BTC + transfer_token(btc_token_account, "alice"_n, btc_evm_account, make_asset(1'00000000, btc_symbol), evm1.address_0x().c_str()); + produce_block(); + + + // USDT balance should be zero + auto bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 0); + + produce_block(); + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000, token_symbol), evm1.address_0x().c_str()); + + bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 990000); + BOOST_REQUIRE(99990000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + auto tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(9900, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(100, token_symbol)); + + produce_block(); + + auto fee = egressFee(); + // received = 1000/1e6*1e4 = 10 + bridgeTransferERC20(evm1, addr_alice, 1000, "aaa", fee); + produce_block(); + + bal = balanceOf(evm1.address_0x().c_str()); + + BOOST_REQUIRE(bal == 989000); + bal = get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount(); + + BOOST_REQUIRE(99990010 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); +} +FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(unregtoken_test, diff_gas_token_tester) +try { + evm_eoa evm1; + auto addr_alice = silkworm::make_reserved_address("alice"_n.to_uint64_t()); + + // Give evm1 some BTC + transfer_token(btc_token_account, "alice"_n, btc_evm_account, make_asset(10'00000000, btc_symbol), evm1.address_0x().c_str()); + produce_block(); + + // USDT balance should be zero + auto bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 0); + + produce_block(); + + // alice send 1.0000 USDT to evm1 + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000, token_symbol), evm1.address_0x().c_str()); + + // evm1 has 0.990000 USDT + BOOST_REQUIRE(balanceOf(evm1.address_0x().c_str()) == 990000); + + // alice has 9999.0000 USDT + BOOST_REQUIRE(9999'0000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + + // unregtoken + push_action( + erc20_account, "unregtoken"_n, erc20_account, mvo()("eos_contract_name", token_account)("token_symbol_code", (std::string)(token_symbol.name()))); + + // EOS->EVM not allowed after unregtoken + BOOST_REQUIRE_EXCEPTION( + transfer_token(token_account, "alice"_n, erc20_account, make_asset(20000, token_symbol), evm1.address_0x().c_str()), + eosio_assert_message_exception, + eosio_assert_message_is("received unregistered token")); + + // EVM->EOS not allowed after unregtoken + auto fee = egressFee(); + BOOST_REQUIRE_EXCEPTION( + bridgeTransferERC20(evm1, addr_alice, 10000, "aaa", fee), + eosio_assert_message_exception, + eosio_assert_message_is("ERC-20 token not registerred")); + + // register token again (imply a different ERC-EVM address) + push_action(erc20_account, "regtoken"_n, erc20_account, mvo()("eos_contract_name",token_account.to_string())("evm_token_name","EVM USDT V2")("evm_token_symbol","WUSDT")("ingress_fee","0.0100 USDT")("egress_fee", make_asset(100))("erc20_precision",6)); + + // EOS->EVM: alice transfer 2 USDT to evm1 in EVM (new ERC-EVM address) + transfer_token(token_account, "alice"_n, erc20_account, make_asset(20000, token_symbol), evm1.address_0x().c_str()); + + // alice has 9997.0000 USDT + BOOST_REQUIRE(9997'0000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + + // evm1 has 0.990000 USDT under the original ERC-20 address + BOOST_REQUIRE(balanceOf(evm1.address_0x().c_str()) == 990000); + + // refresh evm token address + evm_address = getSolidityContractAddress(); + + // evm1 has 1.990000 USDT under the new ERC-20 address + BOOST_REQUIRE(balanceOf(evm1.address_0x().c_str()) == 1990000); + + // EVM->EOS: evm1 tranfer 0.010000 USDT to alice + bridgeTransferERC20(evm1, addr_alice, 10000, "aaa", fee); + + // evm1 has 1.980000 USDT under the new ERC-20 address + BOOST_REQUIRE(balanceOf(evm1.address_0x().c_str()) == 1980000); + + // alice has 9997.0000 USDT + BOOST_REQUIRE(9997'0100 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); +} +FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(eos_to_evm_test, diff_gas_token_tester) +try { + evm_eoa evm1; + auto addr_alice = silkworm::make_reserved_address("alice"_n.to_uint64_t()); + // Give evm1 some BTC + transfer_token(btc_token_account, "alice"_n, btc_evm_account, make_asset(1'00000000, btc_symbol), evm1.address_0x().c_str()); + produce_block(); + + + // USDT balance should be zero + auto bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 0); + produce_block(); + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000, token_symbol), evm1.address_0x().c_str()); + bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 990000); // +1000000 - 10000 + BOOST_REQUIRE(99990000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // -10000 + auto tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(9900, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(100, token_symbol)); + produce_block(); + + BOOST_REQUIRE_EXCEPTION(transfer_token(token_account, "alice"_n, erc20_account, make_asset(0, token_symbol), evm1.address_0x().c_str()), + eosio_assert_message_exception, eosio_assert_message_is("must transfer positive quantity")); + bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 990000); + BOOST_REQUIRE(99990000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(9900, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(100, token_symbol)); + produce_block(); + + + BOOST_REQUIRE_EXCEPTION(transfer_token(token_account, "alice"_n, erc20_account, make_asset(10, token_symbol), evm1.address_0x().c_str()), + eosio_assert_message_exception, eosio_assert_message_is("deposit amount must be greater than ingress fee")); + bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 990000); + BOOST_REQUIRE(99990000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(9900, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(100, token_symbol)); + produce_block(); + + BOOST_REQUIRE_EXCEPTION(transfer_token(token_account, "alice"_n, erc20_account, make_asset(100, token_symbol), evm1.address_0x().c_str()), + eosio_assert_message_exception, eosio_assert_message_is("deposit amount must be greater than ingress fee")); + bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 990000); + BOOST_REQUIRE(99990000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(9900, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(100, token_symbol)); + produce_block(); + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(101, token_symbol), evm1.address_0x().c_str()); + bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 990100); // 9900000 + (10100 - 10000) + BOOST_REQUIRE(99989899 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // 99990000 - 101 + tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(9901, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(200, token_symbol)); + produce_block(); + + // setting a lower gas limit, USDT(EOS)-> USDT(EVM) will fails + push_action(erc20_account, "setgaslimit"_n, erc20_account, mvo("gaslimit", 21001)("init_gaslimit", 10000000)); + + BOOST_REQUIRE_EXCEPTION( + transfer_token(token_account, "alice"_n, erc20_account, make_asset(102, token_symbol), evm1.address_0x().c_str()), + eosio_assert_message_exception, + eosio_assert_message_is("pre_validate_transaction error: 22 Intrinsic gas too low") + ); + + // set it back + push_action(erc20_account, "setgaslimit"_n, erc20_account, mvo("gaslimit", 500000)("init_gaslimit", 10000000)); + transfer_token(token_account, "alice"_n, erc20_account, make_asset(103, token_symbol), evm1.address_0x().c_str()); + +} +FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(evm_to_eos_test, diff_gas_token_tester) +try { + evm_eoa evm1; + auto addr_alice = silkworm::make_reserved_address("alice"_n.to_uint64_t()); + // Give evm1 some BTC + transfer_token(btc_token_account, "alice"_n, btc_evm_account, make_asset(1'00000000, btc_symbol), evm1.address_0x().c_str()); + produce_block(); + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000100, token_symbol), evm1.address_0x().c_str()); + auto bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 1000000000); // +1000010000 - 10000, 1000 USDT + BOOST_REQUIRE(89999900 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // + produce_block(); + + auto fee = egressFee(); + // received = 1000/1e6*1e4 = 10 + bridgeTransferERC20(evm1, addr_alice, 1000, "aaa", fee); + BOOST_REQUIRE(999999000 == balanceOf(evm1.address_0x().c_str())); + BOOST_REQUIRE(89999910 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // + produce_block(); + + BOOST_REQUIRE_EXCEPTION(bridgeTransferERC20(evm1, addr_alice, 0, "aaa", fee), + eosio_assert_message_exception, eosio_assert_message_is("bridge amount must be positive")); + BOOST_REQUIRE(999999000 == balanceOf(evm1.address_0x().c_str())); + BOOST_REQUIRE(89999910 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // + produce_block(); + + BOOST_REQUIRE_EXCEPTION(bridgeTransferERC20(evm1, addr_alice, 1, "aaa", fee), + eosio_assert_message_exception, eosio_assert_message_is("bridge amount can not have dust")); + BOOST_REQUIRE(999999000 == balanceOf(evm1.address_0x().c_str())); + BOOST_REQUIRE(89999910 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // + produce_block(); + + bridgeTransferERC20(evm1, addr_alice, 100, "aaa", fee); + BOOST_REQUIRE(999998900 == balanceOf(evm1.address_0x().c_str())); + BOOST_REQUIRE(89999911 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // + produce_block(); + + bridgeTransferERC20(evm1, addr_alice, 100, "aaa", fee+1); // revert + BOOST_REQUIRE(999998900 == balanceOf(evm1.address_0x().c_str())); + BOOST_REQUIRE(89999911 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // + produce_block(); + + bridgeTransferERC20(evm1, addr_alice, 100, "aaa", fee-1); // revert + BOOST_REQUIRE(999998900 == balanceOf(evm1.address_0x().c_str())); + BOOST_REQUIRE(89999911 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // + produce_block(); + + +} +FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(set_ingress_fee_test, diff_gas_token_tester) +try { + evm_eoa evm1; + auto addr_alice = silkworm::make_reserved_address("alice"_n.to_uint64_t()); + // Give evm1 some BTC + transfer_token(btc_token_account, "alice"_n, btc_evm_account, make_asset(1'00000000, btc_symbol), evm1.address_0x().c_str()); + produce_block(); + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000100, token_symbol), evm1.address_0x().c_str()); + auto bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 1000000000); // +1000010000 - 10000, 1000 USDT + BOOST_REQUIRE(89999900 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + BOOST_REQUIRE(10000100 == get_balance(erc20_account, token_account, symbol::from_string("4,USDT")).get_amount()); + auto tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(10000000, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(100, token_symbol)); + produce_block(); + + push_action(erc20_account, "setingressfee"_n, erc20_account, + mvo()("token_contract", token_account)("ingress_fee", make_asset(200, token_symbol))); + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000200, token_symbol), evm1.address_0x().c_str()); + bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 2000000000); // 1000000000 +1000020000 - 20000, 2000 USDT + BOOST_REQUIRE(79999700 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // + BOOST_REQUIRE(20000300 == get_balance(erc20_account, token_account, symbol::from_string("4,USDT")).get_amount()); + tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(20000000, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(300, token_symbol)); + produce_block(); + + // Change fee and try transfer again. + push_action(erc20_account, "setingressfee"_n, erc20_account, + mvo()("token_contract", token_account)("ingress_fee", make_asset(0, token_symbol))); + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000000, token_symbol), evm1.address_0x().c_str()); + bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 3000000000); // +1000000000 + BOOST_REQUIRE(69999700 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // + BOOST_REQUIRE(30000300 == get_balance(erc20_account, token_account, symbol::from_string("4,USDT")).get_amount()); + tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(30000000, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(300, token_symbol)); + produce_block(); + + BOOST_REQUIRE_EXCEPTION(push_action(erc20_account, "setingressfee"_n, erc20_account, + mvo()("token_contract", token_account)("ingress_fee", make_asset(0, symbol::from_string("4,USDC"))));, + eosio_assert_message_exception, eosio_assert_message_is("token not registered")); + + BOOST_REQUIRE_EXCEPTION(push_action(erc20_account, "setingressfee"_n, erc20_account, + mvo()("token_contract", token_account)("ingress_fee", make_asset(0, symbol::from_string("2,USDT"))));, + eosio_assert_message_exception, eosio_assert_message_is("incorrect precision for registered token")); + +} +FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(set_egress_fee_test, diff_gas_token_tester) +try { + constexpr intx::uint256 minimum_natively_representable = intx::exp(10_u256, intx::uint256(18 - 8)); + + BOOST_REQUIRE_EXCEPTION(push_action(erc20_account, "setegressfee"_n, erc20_account, + mvo()("token_contract", token_account)("token_symbol_code", "USDT")("egress_fee", make_asset(50))), + eosio_assert_message_exception, eosio_assert_message_is("egress fee must be at least as large as the receiver's minimum fee")); + + produce_block(); + + BOOST_REQUIRE(100 * minimum_natively_representable == egressFee()); // was 0.01 + + produce_block(); + // set to 0.5 + push_action(erc20_account, "setegressfee"_n, erc20_account, + mvo()("token_contract", token_account)("token_symbol_code", "USDT")("egress_fee", make_asset(5000))); + + BOOST_REQUIRE(5000 * minimum_natively_representable == egressFee()); + + produce_block(); + + BOOST_REQUIRE_EXCEPTION(push_action(erc20_account, "setegressfee"_n, btc_evm_account, + mvo()("token_contract", token_account)("token_symbol_code", "USDT")("egress_fee", make_asset(1000))), + missing_auth_exception, eosio::testing::fc_exception_message_starts_with("missing authority of eosio.erc2o")); + + + produce_block(); +} +FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(withdraw_fees_test, diff_gas_token_tester) +try { + evm_eoa evm1; + auto addr_alice = silkworm::make_reserved_address("alice"_n.to_uint64_t()); + // Give evm1 some BTC + transfer_token(btc_token_account, "alice"_n, btc_evm_account, make_asset(1'00000000, btc_symbol), evm1.address_0x().c_str()); + produce_block(); + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000100, token_symbol), evm1.address_0x().c_str()); + auto bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 1000000000); // +1000010000 - 10000, 1000 USDT + BOOST_REQUIRE(89999900 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); // + BOOST_REQUIRE(10000100 == get_balance(erc20_account, token_account, symbol::from_string("4,USDT")).get_amount()); + auto tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(10000000, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(100, token_symbol)); + produce_block(); + + BOOST_REQUIRE_EXCEPTION(push_action(erc20_account, "withdrawfee"_n, erc20_account, + mvo()("token_contract", token_account)("quantity", make_asset(10000, token_symbol))("to", "alice"_n)("memo", "asd")), + eosio_assert_message_exception, eosio_assert_message_is("overdrawn balance")); + + BOOST_REQUIRE_EXCEPTION(push_action(erc20_account, "withdrawfee"_n, erc20_account, + mvo()("token_contract", token_account)("quantity", make_asset(0, token_symbol))("to", "alice"_n)("memo", "asd")), + eosio_assert_message_exception, eosio_assert_message_is("quantity must be positive")); + + BOOST_REQUIRE_EXCEPTION(push_action(erc20_account, "withdrawfee"_n, erc20_account, + mvo()("token_contract", token_account)("quantity", make_asset(100, symbol::from_string("4,USDC")))("to", "alice"_n)("memo", "asd")), + eosio_assert_message_exception, eosio_assert_message_is("token not registered")); + + BOOST_REQUIRE_EXCEPTION(push_action(erc20_account, "withdrawfee"_n, erc20_account, + mvo()("token_contract", token_account)("quantity", make_asset(100, symbol::from_string("2,USDT")))("to", "alice"_n)("memo", "asd")), + eosio_assert_message_exception, eosio_assert_message_is("incorrect precision for registered token")); + + push_action(erc20_account, "withdrawfee"_n, erc20_account, + mvo()("token_contract", token_account)("quantity", make_asset(100, token_symbol))("to", "alice"_n)("memo", "asd")); + + BOOST_REQUIRE(bal == 1000000000); // +1000010000 - 10000, 1000 USDT + BOOST_REQUIRE(90000000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + BOOST_REQUIRE(10000000 == get_balance(erc20_account, token_account, symbol::from_string("4,USDT")).get_amount()); + tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(10000000, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(0, token_symbol)); +} +FC_LOG_AND_RETHROW() + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/antelope_contracts/tests/erc20/erc20_tester.cpp b/antelope_contracts/tests/erc20/erc20_tester.cpp index 9e0c0ea..485919b 100644 --- a/antelope_contracts/tests/erc20/erc20_tester.cpp +++ b/antelope_contracts/tests/erc20/erc20_tester.cpp @@ -114,7 +114,7 @@ evm_eoa::~evm_eoa() { secp256k1_context_destroy(ctx); } -erc20_tester::erc20_tester(bool use_real_evm, eosio::chain::name evm_account_, std::string native_symbol_str) : evm_account(evm_account_), native_symbol(symbol::from_string(native_symbol_str)) { +erc20_tester::erc20_tester(bool use_real_evm, eosio::chain::name evm_account_, std::string native_symbol_str, eosio::chain::name eos_token_account_) : evm_account(evm_account_), native_symbol(symbol::from_string(native_symbol_str)), eos_token_account(eos_token_account_) { auto def_conf = default_config(tempdir, 4096); cfg = def_conf.first; @@ -193,8 +193,9 @@ erc20_tester::erc20_tester(bool use_real_evm, eosio::chain::name evm_account_, s open(erc20_account); - transfer_token(eos_token_account, faucet_account_name, evm_account, make_asset(10000'0000), "eosio.erc2o"); - bridgereg(erc20_account, erc20_account, asset(100, symbol::from_string("4,EOS"))); + transfer_token(eos_token_account, faucet_account_name, evm_account, make_asset(10'0000'0000), "eosio.erc2o"); // 100K EOS or 10 BTC + + bridgereg(erc20_account, erc20_account, make_asset(100)); } else { set_code(evm_account, testing::contracts::evm_stub_wasm()); set_abi(evm_account, testing::contracts::evm_stub_abi().data()); @@ -214,20 +215,24 @@ erc20_tester::erc20_tester(bool use_real_evm, eosio::chain::name evm_account_, s evmc::address impl_addr = silkworm::create_address(deployer.address, deployer.next_nonce); if (use_real_evm) { - transfer_token(eos_token_account, faucet_account_name, evmin_account, make_asset(1000000, eos_token_symbol), deployer.address_0x().c_str()); + try { - auto txn = prepare_deploy_contract_tx(solidity::erc20::bytecode, sizeof(solidity::erc20::bytecode), 10'000'000); + transfer_token(eos_token_account, faucet_account_name, evm_account, make_asset(10'0000'0000), deployer.address_0x().c_str()); - deployer.sign(txn); - pushtx(txn); - produce_block(); + auto txn = prepare_deploy_contract_tx(solidity::erc20::bytecode, sizeof(solidity::erc20::bytecode), 10'000'000); + + deployer.sign(txn); + pushtx(txn); + produce_block(); + + } FC_CAPTURE_AND_RETHROW() } push_action(erc20_account, "upgradeto"_n, erc20_account, mvo()("impl_address",fc::variant(impl_addr).as_string())); produce_block(); - push_action(erc20_account, "regtoken"_n, erc20_account, mvo()("eos_contract_name",token_account.to_string())("evm_token_name","EVM USDT V1")("evm_token_symbol","WUSDT")("ingress_fee","0.0100 USDT")("egress_fee","0.0100 EOS")("erc20_precision",6)); + push_action(erc20_account, "regtoken"_n, erc20_account, mvo()("eos_contract_name",token_account.to_string())("evm_token_name","EVM USDT V1")("evm_token_symbol","WUSDT")("ingress_fee","0.0100 USDT")("egress_fee", make_asset(100))("erc20_precision",6)); produce_block(); @@ -251,10 +256,10 @@ void erc20_tester::init_evm(const uint64_t chainid, if (ingress_bridge_fee.has_value()) { fee_params("ingress_bridge_fee", *ingress_bridge_fee); } else { - fee_params("ingress_bridge_fee", "0.0000 EOS"); + fee_params("ingress_bridge_fee", make_asset(0)); } - push_action(evm_account, "init"_n, evm_account, mvo()("chainid", chainid)("fee_params", fee_params)); + push_action(evm_account, "init"_n, evm_account, mvo()("chainid", chainid)("fee_params", fee_params)("token_contract", eos_token_account)); if (also_prepare_self_balance) { prepare_self_balance(); @@ -264,10 +269,14 @@ void erc20_tester::init_evm(const uint64_t chainid, void erc20_tester::prepare_self_balance(uint64_t fund_amount) { // Ensure internal balance for evm_account_name has at least 1 EOS to cover max bridge gas fee with even high gas // price. - transfer_token(eos_token_account, faucet_account_name, evm_account, make_asset(1'0000), evm_account.to_string()); + transfer_token(eos_token_account, faucet_account_name, evm_account, make_asset(1000'0000), evm_account.to_string()); // 100K EOS or 0.1 BTC } transaction_trace_ptr erc20_tester::bridgereg(eosio::chain::name receiver, eosio::chain::name handler, eosio::chain::asset min_fee, vector extra_signers) { + + if (extra_signers.size() == 1 && extra_signers[0] == ""_n) { + extra_signers[0] = evm_account; + } extra_signers.push_back(receiver); if (receiver != handler) extra_signers.push_back(handler); diff --git a/antelope_contracts/tests/erc20/erc20_tester.hpp b/antelope_contracts/tests/erc20/erc20_tester.hpp index f9cc812..84df8c9 100644 --- a/antelope_contracts/tests/erc20/erc20_tester.hpp +++ b/antelope_contracts/tests/erc20/erc20_tester.hpp @@ -129,11 +129,11 @@ class erc20_tester : public eosio::testing::base_tester { static constexpr eosio::chain::name faucet_account_name = "eosio.faucet"_n; static constexpr eosio::chain::name erc20_account = "eosio.erc2o"_n; static constexpr eosio::chain::name eos_system_account = "eosio"_n; - static constexpr eosio::chain::name eos_token_account = "eosio.token"_n; + const eosio::chain::name eos_token_account; - const eosio::chain::name evm_account = "eosio.evm"_n; + const eosio::chain::name evm_account; const eosio::chain::symbol native_symbol; - explicit erc20_tester(bool use_real_evm = false, eosio::chain::name evm_account_ = "eosio.evm"_n, std::string native_symbol_str = "4,EOS"); + explicit erc20_tester(bool use_real_evm = false, eosio::chain::name evm_account_ = "eosio.evm"_n, std::string native_symbol_str = "4,EOS", eosio::chain::name eos_token_account_ = "eosio.token"_n); unsigned int exec_count = 0; // ensure uniqueness in exec @@ -141,7 +141,7 @@ class erc20_tester : public eosio::testing::base_tester { eosio::chain::asset make_asset(int64_t amount, const eosio::chain::symbol& target_symbol) const { return eosio::chain::asset(amount, target_symbol); } eosio::chain::transaction_trace_ptr transfer_token(eosio::chain::name token_account_name, eosio::chain::name from, eosio::chain::name to, eosio::chain::asset quantity, std::string memo = ""); void prepare_self_balance(uint64_t fund_amount = 100'0000); - transaction_trace_ptr bridgereg(eosio::chain::name receiver, eosio::chain::name handler, eosio::chain::asset min_fee, vector extra_signers={"eosio.evm"_n}); + transaction_trace_ptr bridgereg(eosio::chain::name receiver, eosio::chain::name handler, eosio::chain::asset min_fee, vector extra_signers={ ""_n /* default value replaced with evm_account*/}); void open(name owner); transaction_trace_ptr exec(const exec_input& input, const std::optional& callback); eosio::chain::action get_action( account_name code, action_name acttype, std::vector auths,