diff --git a/integration_tests/contracts/contracts/ICA.sol b/integration_tests/contracts/contracts/ICA.sol new file mode 100644 index 0000000000..f81df77dee --- /dev/null +++ b/integration_tests/contracts/contracts/ICA.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +interface IICAModule { + event SubmitMsgsResult(string seq); + function registerAccount(string calldata connectionID, string calldata version) external payable returns (bool); + function queryAccount(string calldata connectionID, address addr) external view returns (string memory); + function submitMsgs(string calldata connectionID, string calldata data, uint256 timeout) external payable returns (uint64); +} diff --git a/integration_tests/contracts/contracts/TestICA.sol b/integration_tests/contracts/contracts/TestICA.sol index 0ab724b300..159dcb02db 100644 --- a/integration_tests/contracts/contracts/TestICA.sol +++ b/integration_tests/contracts/contracts/TestICA.sol @@ -1,8 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity >0.6.6; +pragma solidity ^0.8.4; + +import {IICAModule} from "./ICA.sol"; contract TestICA { address constant icaContract = 0x0000000000000000000000000000000000000066; + IICAModule ica = IICAModule(icaContract); function encodeRegister(string memory connectionID) internal view returns (bytes memory) { return abi.encodeWithSignature( @@ -12,9 +15,7 @@ contract TestICA { } function callRegister(string memory connectionID) public returns (bool) { - (bool result, ) = icaContract.call(encodeRegister(connectionID)); - require(result, "call failed"); - return true; + return ica.registerAccount(connectionID, ""); } function delegateRegister(string memory connectionID) public returns (bool) { @@ -37,9 +38,7 @@ contract TestICA { } function callQueryAccount(string memory connectionID, address addr) public returns (string memory) { - (bool result, bytes memory data) = icaContract.call(encodeQueryAccount(connectionID, addr)); - require(result, "call failed"); - return abi.decode(data, (string)); + return ica.queryAccount(connectionID, addr); } function delegateQueryAccount(string memory connectionID, address addr) public returns (string memory) { @@ -62,9 +61,7 @@ contract TestICA { } function callSubmitMsgs(string memory connectionID, string memory data) public returns (uint64) { - (bool result, bytes memory data) = icaContract.call(encodeSubmitMsgs(connectionID, data)); - require(result, "call failed"); - return abi.decode(data, (uint64)); + return ica.submitMsgs(connectionID, data, 300000000000); } function delegateSubmitMsgs(string memory connectionID, string memory data) public returns (uint64) { diff --git a/integration_tests/test_ica_precompile.py b/integration_tests/test_ica_precompile.py index a2dc397749..bde97dab99 100644 --- a/integration_tests/test_ica_precompile.py +++ b/integration_tests/test_ica_precompile.py @@ -123,21 +123,81 @@ def submit_msgs(msgs, seq): balance -= amt1 assert cli_host.balance(ica_address, denom=denom) == balance + +def test_sc_call(ibc): + connid = "connection-0" + cli_host = ibc.chainmain.cosmos_cli() + cli_controller = ibc.cronos.cosmos_cli() + w3 = ibc.cronos.w3 + contract = w3.eth.contract(address=CONTRACT, abi=contract_info) tcontract = deploy_contract(w3, CONTRACTS["TestICA"]) + addr = tcontract.address + signer2 = ADDRS["signer2"] + keys = KEYS["signer2"] + owner = eth_to_bech32(addr) + data = {"from": signer2, "gas": 200000} + tx = tcontract.functions.callRegister(connid).build_transaction(data) + assert send_transaction(w3, tx, keys).status == 1 + channel_id = "channel-1" + wait_for_check_channel_ready(cli_controller, connid, channel_id) + res = cli_controller.ica_query_account(connid, owner) + ica_address = res["interchain_account_address"] + print("query ica account", ica_address) + balance = funds_ica(cli_host, ica_address) + res = contract.functions.queryAccount(connid, addr).call() + assert ica_address == res, res assert tcontract.functions.callQueryAccount(connid, addr).call() == ica_address assert tcontract.functions.delegateQueryAccount(connid, addr).call() == ica_address assert tcontract.functions.staticQueryAccount(connid, addr).call() == ica_address - - tx = tcontract.functions.callRegister(connid).build_transaction(data) - assert send_transaction(w3, tx, keys).status == 0 + # readonly call should fail tx = tcontract.functions.delegateRegister(connid).build_transaction(data) assert send_transaction(w3, tx, keys).status == 0 tx = tcontract.functions.staticRegister(connid).build_transaction(data) assert send_transaction(w3, tx, keys).status == 0 - tx = tcontract.functions.callSubmitMsgs(connid, "").build_transaction(data) - assert send_transaction(w3, tx, keys).status == 0 - tx = tcontract.functions.delegateSubmitMsgs(connid, "").build_transaction(data) - assert send_transaction(w3, tx, keys).status == 0 - tx = tcontract.functions.staticSubmitMsgs(connid, "").build_transaction(data) - assert send_transaction(w3, tx, keys).status == 0 + name = "validator" + denom = "basecro" + amt = 1000 + + def submit_msgs(msgs, seq): + generated_packet = cli_controller.ica_generate_packet_data(msgs) + num_txs = len(cli_host.query_all_txs(ica_address)["txs"]) + start = w3.eth.get_block_number() + str = json.dumps(generated_packet) + # submit transaction on host chain on behalf of interchain account + tx = tcontract.functions.callSubmitMsgs(connid, str).build_transaction(data) + assert send_transaction(w3, tx, keys).status == 1 + logs = get_logs_since(w3, CONTRACT, start) + expected = [{"seq": seq}] + for i, log in enumerate(logs): + method_name, args = get_topic_data(w3, method_map, contract_info, log) + assert args == AttributeDict(expected[i]), [i, method_name] + wait_for_check_tx(cli_host, ica_address, num_txs) + # readonly call should fail + tx = tcontract.functions.delegateSubmitMsgs(connid, str).build_transaction(data) + assert send_transaction(w3, tx, keys).status == 0 + tx = tcontract.functions.staticSubmitMsgs(connid, str).build_transaction(data) + assert send_transaction(w3, tx, keys).status == 0 + + # generate msg send tx to host chain + msg_send = generate_msg_send( + ica_address, + cli_host.address(name), + denom, + amt, + ) + submit_msgs(json.dumps(msg_send), "1") + balance -= amt + assert cli_host.balance(ica_address, denom=denom) == balance + # generate multi msgs to host chain + amt1 = 100 + msg_delegate = generate_msg_delegate( + ica_address, + cli_host.address(name, bech="val"), + denom, + amt1, + ) + submit_msgs(json.dumps([msg_send, msg_delegate]), "2") + balance -= amt + balance -= amt1 + assert cli_host.balance(ica_address, denom=denom) == balance diff --git a/x/cronos/keeper/precompiles/ica.go b/x/cronos/keeper/precompiles/ica.go index 7518ab6f8c..c6f339fed4 100644 --- a/x/cronos/keeper/precompiles/ica.go +++ b/x/cronos/keeper/precompiles/ica.go @@ -35,7 +35,7 @@ func init() { uint64Type, _ := abi.NewType("uint64", "", nil) boolType, _ := abi.NewType("bool", "", nil) RegisterAccountMethod = abi.NewMethod( - "registerAccount", "registerAccount", abi.Function, "", false, false, abi.Arguments{abi.Argument{ + "registerAccount", "registerAccount", abi.Function, "", false, true, abi.Arguments{abi.Argument{ Name: "connectionID", Type: stringType, }, abi.Argument{ @@ -61,7 +61,7 @@ func init() { }}, ) SubmitMsgsMethod = abi.NewMethod( - "submitMsgs", "submitMsgs", abi.Function, "", false, false, abi.Arguments{abi.Argument{ + "submitMsgs", "submitMsgs", abi.Function, "", false, true, abi.Arguments{abi.Argument{ Name: "connectionID", Type: stringType, }, abi.Argument{