diff --git a/.cicd/platforms/ubuntu22.Dockerfile b/.cicd/platforms/ubuntu22.Dockerfile index bab8c4b..22321c2 100644 --- a/.cicd/platforms/ubuntu22.Dockerfile +++ b/.cicd/platforms/ubuntu22.Dockerfile @@ -9,7 +9,8 @@ RUN apt-get update && apt-get upgrade -y && \ git \ jq \ wget \ - xxd + xxd \ + libcurl4-gnutls-dev #Install Node.js #https://github.com/nodesource/distributions/blob/master/README.md#installation-instructions diff --git a/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp b/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp index 776e506..4bf0f5c 100644 --- a/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp +++ b/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp @@ -55,6 +55,8 @@ class [[eosio::contract]] erc20 : public contract { [[eosio::action]] void setingressfee(eosio::name token_contract, eosio::asset ingress_fee); [[eosio::action]] void withdrawfee(eosio::name token_contract, eosio::asset quantity, eosio::name to, std::string memo); + [[eosio::action]] void unregtoken(eosio::name eos_contract_name, eosio::symbol_code token_symbol_code); + struct [[eosio::table("implcontract")]] impl_contract_t { uint64_t id = 0; bytes address; diff --git a/antelope_contracts/contracts/erc20/src/erc20.cpp b/antelope_contracts/contracts/erc20/src/erc20.cpp index 897cf61..6cc1eaa 100644 --- a/antelope_contracts/contracts/erc20/src/erc20.cpp +++ b/antelope_contracts/contracts/erc20/src/erc20.cpp @@ -424,6 +424,17 @@ void erc20::setegressfee(eosio::name token_contract, eosio::symbol_code token_sy call_act.send(receiver_account(), token_table_iter->address, value_zero, call_data, evm_gaslimit); } +void erc20::unregtoken(eosio::name token_contract, eosio::symbol_code token_symbol_code) { + require_auth(get_self()); + + token_table_t token_table(_self, _self.value); + auto index_symbol = token_table.get_index<"by.symbol"_n>(); + auto token_table_iter = index_symbol.find(token_symbol_key(token_contract, token_symbol_code)); + eosio::check(token_table_iter != index_symbol.end(), "token not registered"); + + index_symbol.erase(token_table_iter); +} + inline eosio::name erc20::receiver_account()const { return get_self(); } diff --git a/antelope_contracts/tests/erc20/erc20_tester.cpp b/antelope_contracts/tests/erc20/erc20_tester.cpp index 3cb5955..85f7892 100644 --- a/antelope_contracts/tests/erc20/erc20_tester.cpp +++ b/antelope_contracts/tests/erc20/erc20_tester.cpp @@ -270,7 +270,7 @@ void erc20_tester::open(name owner) { push_action(evm_account, "open"_n, owner, transaction_trace_ptr erc20_tester::exec(const exec_input& input, const std::optional& callback) { auto binary_data = fc::raw::pack>(input, callback); - return erc20_tester::push_action(evm_account, "exec"_n, evm_account, bytes{binary_data.begin(), binary_data.end()}); + return erc20_tester::push_action(evm_account, "exec"_n, evm_account, bytes{binary_data.begin(), binary_data.end()}, DEFAULT_EXPIRATION_DELTA + (exec_count++) % 3500); } eosio::chain::action erc20_tester::get_action(account_name code, action_name acttype, std::vector auths, diff --git a/antelope_contracts/tests/erc20/erc20_tester.hpp b/antelope_contracts/tests/erc20/erc20_tester.hpp index c3572e1..5905ac6 100644 --- a/antelope_contracts/tests/erc20/erc20_tester.hpp +++ b/antelope_contracts/tests/erc20/erc20_tester.hpp @@ -135,6 +135,8 @@ class erc20_tester : public eosio::testing::base_tester { const eosio::chain::symbol native_symbol; explicit erc20_tester(bool use_real_evm = false, std::string native_symbol_str = "4,EOS"); + unsigned int exec_count = 0; // ensure uniqueness in exec + eosio::chain::asset make_asset(int64_t amount) const { return eosio::chain::asset(amount, native_symbol); } 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 = ""); @@ -187,7 +189,6 @@ class erc20_tester : public eosio::testing::base_tester { const std::optional ingress_bridge_fee = std::nullopt, const bool also_prepare_self_balance = true); - }; diff --git a/antelope_contracts/tests/erc20/integrated_tests.cpp b/antelope_contracts/tests/erc20/integrated_tests.cpp index f2fc06d..5cafe01 100644 --- a/antelope_contracts/tests/erc20/integrated_tests.cpp +++ b/antelope_contracts/tests/erc20/integrated_tests.cpp @@ -195,9 +195,17 @@ struct it_tester : erc20_tester { 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); - auto r = pushtx(txn); - // dlog("action trace: ${a}", ("a", r)); + + 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) { @@ -209,9 +217,16 @@ struct it_tester : erc20_tester { 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); - auto r = pushtx(txn); - // dlog("action trace: ${a}", ("a", r)); + + try { + auto r = pushtx(txn); + // dlog("action trace: ${a}", ("a", r)); + } catch (...) { + from.next_nonce = old_nonce; + throw; + } } }; @@ -258,7 +273,76 @@ try { } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE(it_unregtoken, it_tester) +try { + evm_eoa evm1; + auto addr_alice = silkworm::make_reserved_address("alice"_n.to_uint64_t()); + + // Give evm1 some EOS + transfer_token(eos_token_account, "alice"_n, evm_account, make_asset(1000000, eos_token_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","0.0100 EOS")("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(it_eos_to_evm, it_tester) try { @@ -351,14 +435,12 @@ try { 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()); // - evm1.next_nonce --; 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()); // - evm1.next_nonce --; produce_block(); bridgeTransferERC20(evm1, addr_alice, 100, "aaa", fee);