diff --git a/ethcoder/abi.go b/ethcoder/abi.go index f0cd2653..2253e18d 100644 --- a/ethcoder/abi.go +++ b/ethcoder/abi.go @@ -108,6 +108,16 @@ func AbiDecodeExprAndStringify(expr string, input []byte) ([]string, error) { return AbiMarshalStringValues(argTypes, input) } +func AbiDecodeExprToInterfaces(expr string, input []byte) ([]interface{}, error) { + argsList := parseArgumentExpr(expr) + argTypes := []string{} + for _, v := range argsList { + argTypes = append(argTypes, v.Type) + } + + return AbiDecoderWithReturnedValues(argTypes, input) +} + func AbiMarshalStringValues(argTypes []string, input []byte) ([]string, error) { values, err := AbiDecoderWithReturnedValues(argTypes, input) if err != nil { diff --git a/ethrpc/ens.go b/ethrpc/ens.go index 9f7da4b4..b5b7c548 100644 --- a/ethrpc/ens.go +++ b/ethrpc/ens.go @@ -50,25 +50,36 @@ func ResolveEnsAddress(ctx context.Context, ens string, provider *Provider) (com return common.Address{}, false, fmt.Errorf("ethrpc: failed to generate namehash: %w", err) } - resolverAddress, err := provider.contractQuery(ctx, ENSContractAddress, "resolver(bytes32)", "address", []interface{}{namehash}) + res, err := provider.contractQuery(ctx, ENSContractAddress, "resolver(bytes32)", "address", []interface{}{namehash}) if err != nil { return common.Address{}, false, fmt.Errorf("ethrpc: failed to query resolver address: %w", err) } - if len(resolverAddress) < 1 || (resolverAddress[0] == common.Address{}.Hex()) { + if len(res) < 1 { return common.Address{}, false, nil } - contractAddress, err := provider.contractQuery(ctx, resolverAddress[0], "addr(bytes32)", "address", []interface{}{namehash}) + resolverAddress, ok := res[0].(common.Address) + if !ok || resolverAddress.Hex() == (common.Address{}).Hex() { + return common.Address{}, false, nil + } + + res, err = provider.contractQuery(ctx, resolverAddress.Hex(), "addr(bytes32)", "address", []interface{}{namehash}) if err != nil { return common.Address{}, false, fmt.Errorf("ethrpc: failed to query resolver address: %w", err) } - if len(contractAddress) < 1 { + if len(res) < 1 { + return common.Address{}, false, nil + } + + contractAddress, ok := res[0].(common.Address) + + if !ok || contractAddress.Hex() == (common.Address{}).Hex() { return common.Address{}, false, nil } - return common.HexToAddress(contractAddress[0]), true, nil + return contractAddress, true, nil } // NameHash generates a hash from a name that can be used to diff --git a/ethrpc/ethrpc.go b/ethrpc/ethrpc.go index 7c5c54ba..9068a412 100644 --- a/ethrpc/ethrpc.go +++ b/ethrpc/ethrpc.go @@ -390,7 +390,7 @@ func (p *Provider) SubscribeFilterLogs(ctx context.Context, query ethereum.Filte // ie, ContractQuery(context.Background(), "0xabcdef..", "balanceOf(uint256)", "uint256", []string{"1"}) // TODO: add common methods in helpers util, and also use generics to convert the return for us -func (p *Provider) ContractQuery(ctx context.Context, contractAddress string, inputAbiExpr, outputAbiExpr string, args interface{}) ([]string, error) { +func (p *Provider) ContractQuery(ctx context.Context, contractAddress string, inputAbiExpr, outputAbiExpr string, args interface{}) ([]interface{}, error) { if !common.IsHexAddress(contractAddress) { // Check for ens ensAddress, ok, err := ResolveEnsAddress(ctx, contractAddress, p) @@ -405,7 +405,7 @@ func (p *Provider) ContractQuery(ctx context.Context, contractAddress string, in return p.contractQuery(ctx, contractAddress, inputAbiExpr, outputAbiExpr, args) } -func (p *Provider) contractQuery(ctx context.Context, contractAddress string, inputAbiExpr, outputAbiExpr string, args interface{}) ([]string, error) { +func (p *Provider) contractQuery(ctx context.Context, contractAddress string, inputAbiExpr, outputAbiExpr string, args interface{}) ([]interface{}, error) { contract := common.HexToAddress(contractAddress) var ( @@ -441,7 +441,7 @@ func (p *Provider) contractQuery(ctx context.Context, contractAddress string, in if err != nil { return nil, fmt.Errorf("contract call failed: %w", err) } - resp, err := ethcoder.AbiDecodeExprAndStringify(outputAbiExpr, output) + resp, err := ethcoder.AbiDecodeExprToInterfaces(outputAbiExpr, output) if err != nil { return nil, fmt.Errorf("abi decode of response failed: %w", err) } diff --git a/ethrpc/ethrpc_test.go b/ethrpc/ethrpc_test.go index 71a1afb2..0f55b94c 100644 --- a/ethrpc/ethrpc_test.go +++ b/ethrpc/ethrpc_test.go @@ -59,10 +59,12 @@ func TestERC20MintAndTransfer(t *testing.T) { require.NotNil(t, receipt) // Query erc20Mock balance to confirm - ret, err := provider.ContractQuery(ctx, erc20Mock.Address.Hex(), "balanceOf(address)", "uint256", []string{wallet.Address().Hex()}) + res, err := provider.ContractQuery(ctx, erc20Mock.Address.Hex(), "balanceOf(address)", "uint256", []string{wallet.Address().Hex()}) require.NoError(t, err) - require.Equal(t, 1, len(ret)) - require.Equal(t, "2000", ret[0]) + require.Equal(t, 1, len(res)) + ret, ok := res[0].(*big.Int) + require.True(t, ok) + require.Equal(t, "2000", ret.String()) // Transfer token to another wallet wallet2, _ := testchain.DummyWallet(600) @@ -74,11 +76,12 @@ func TestERC20MintAndTransfer(t *testing.T) { txn, receipt = ethtest.SendTransactionAndWaitForReceipt(t, wallet, erc20Mock.Address, calldata, nil) require.NotNil(t, txn) require.NotNil(t, receipt) - - ret, err = provider.ContractQuery(ctx, erc20Mock.Address.Hex(), "balanceOf(address)", "uint256", []string{wallet2.Address().Hex()}) + res, err = provider.ContractQuery(ctx, erc20Mock.Address.Hex(), "balanceOf(address)", "uint256", []string{wallet2.Address().Hex()}) require.NoError(t, err) - require.Equal(t, 1, len(ret)) - require.Equal(t, "42", ret[0]) + require.Equal(t, 1, len(res)) + ret, ok = res[0].(*big.Int) + require.True(t, ok) + require.Equal(t, "42", ret.String()) } func TestBlockByNumber(t *testing.T) { diff --git a/ethtest/contracts.go b/ethtest/contracts.go index 2019c62f..2992c314 100644 --- a/ethtest/contracts.go +++ b/ethtest/contracts.go @@ -56,7 +56,7 @@ func ContractCall(provider *ethrpc.Provider, contractAddress common.Address, con return output, nil } -func ContractQuery(provider *ethrpc.Provider, contractAddress common.Address, inputExpr, outputExpr string, args []string) ([]string, error) { +func ContractQuery(provider *ethrpc.Provider, contractAddress common.Address, inputExpr, outputExpr string, args []string) ([]interface{}, error) { return provider.ContractQuery(context.Background(), contractAddress.Hex(), inputExpr, outputExpr, args) } diff --git a/ethtest/erc20.go b/ethtest/erc20.go index 9389c338..62abfcf9 100644 --- a/ethtest/erc20.go +++ b/ethtest/erc20.go @@ -56,8 +56,11 @@ func (c *ERC20Mock) Transfer(t *testing.T, owner *ethwallet.Wallet, to ethkit.Ad func (c *ERC20Mock) GetBalance(t *testing.T, account ethkit.Address, expectedAmount int64) { provider := c.testchain.Provider - ret, err := provider.ContractQuery(context.Background(), c.Contract.Address.Hex(), "balanceOf(address)", "uint256", []string{account.Hex()}) + res, err := provider.ContractQuery(context.Background(), c.Contract.Address.Hex(), "balanceOf(address)", "uint256", []string{account.Hex()}) require.NoError(t, err) - require.Equal(t, 1, len(ret)) - require.Equal(t, fmt.Sprintf("%d", expectedAmount), ret[0]) + require.NotEmpty(t, res) + ret, ok := res[0].(*big.Int) + require.True(t, ok) + require.NotEqual(t, big.NewInt(0), ret) + require.Equal(t, fmt.Sprintf("%d", expectedAmount), ret.String()) } diff --git a/ethtest/testchain_test.go b/ethtest/testchain_test.go index 88673fd8..3085c7b2 100644 --- a/ethtest/testchain_test.go +++ b/ethtest/testchain_test.go @@ -44,9 +44,12 @@ func TestContractHelpers(t *testing.T) { assert.Equal(t, types.ReceiptStatusSuccessful, receipt.Status) // Query the value ensuring its been updated on-chain - ret, err := ethtest.ContractQuery(testchain.Provider, callmockContract.Address, "lastValA()", "uint256", nil) + res, err := ethtest.ContractQuery(testchain.Provider, callmockContract.Address, "lastValA()", "uint256", nil) assert.NoError(t, err) - assert.Equal(t, []string{"143"}, ret) + assert.Equal(t, 1, len(res)) + ret, ok := res[0].(*big.Int) + assert.True(t, ok) + assert.Equal(t, "143", ret.String()) // Query the value using different method, where we unpack the value var result *big.Int