Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

841 add account balanceinfo contract info mirror node queries #853

34 changes: 32 additions & 2 deletions account_balance_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ func (q *AccountBalanceQuery) Execute(client *Client) (AccountBalance, error) {
if client == nil {
return AccountBalance{}, errNoClientProvided
}

var err error

err = q.validateNetworkOnIDs(client)
Expand All @@ -113,7 +112,38 @@ func (q *AccountBalanceQuery) Execute(client *Client) (AccountBalance, error) {
return AccountBalance{}, err
}

return _AccountBalanceFromProtobuf(resp.(*services.Response).GetCryptogetAccountBalance()), nil
result := _AccountBalanceFromProtobuf(resp.(*services.Response).GetCryptogetAccountBalance())

network := obtainUrlForMirrorNode(client)
if q.accountID != nil {
err = queryBalanceFromMirrorNode(network, q.accountID.String(), &result)
} else {
err = queryBalanceFromMirrorNode(network, q.contractID.String(), &result)
}
if err != nil {
return AccountBalance{}, nil
}
return result, nil
}

// Helper function, which query the mirror node and if the balance has tokens, it iterate over the tokens and assign them
// inside `AccountBalance` tokens field
func queryBalanceFromMirrorNode(network string, id string, result *AccountBalance) error {
response, err := accountBalanceQuery(network, id)
if err != nil {
return err
}
// If user has tokens
result.Tokens.balances = make(map[string]uint64)
if tokens, ok := response["tokens"].([]map[string]interface{}); ok {
for _, token := range tokens {
for key, value := range token {
result.Tokens.balances[key] = value.(uint64)
}
}
}

return nil
}

// SetMaxQueryPayment sets the maximum payment allowed for this query.
Expand Down
3 changes: 3 additions & 0 deletions account_balance_query_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package hedera
*/

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -194,6 +195,8 @@ func TestIntegrationAccountBalanceQueryNoAccountIDError(t *testing.T) {
_, err := NewAccountBalanceQuery().
SetNodeAccountIDs(env.NodeAccountIDs).
Execute(env.Client)

fmt.Println(env.Client.network.ledgerID)
assert.Error(t, err)
assert.True(t, err.Error() == "exceptional precheck status INVALID_ACCOUNT_ID")

Expand Down
33 changes: 32 additions & 1 deletion account_info_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package hedera
*/

import (
"errors"
"time"

"github.com/hashgraph/hedera-protobufs-go/services"
Expand Down Expand Up @@ -56,7 +57,37 @@ func (q *AccountInfoQuery) Execute(client *Client) (AccountInfo, error) {
return AccountInfo{}, err
}

return _AccountInfoFromProtobuf(resp.GetCryptoGetInfo().AccountInfo)
result, err := _AccountInfoFromProtobuf(resp.GetCryptoGetInfo().AccountInfo)
if err != nil {
return AccountInfo{}, err
}

network := obtainUrlForMirrorNode(client)
_, err = accountInfoqueryTokensRelationshipFromMirrorNode(network, q.accountID.String(), &result)

if err != nil {
return AccountInfo{}, err
}
return result, nil
}

// Helper function, which query the mirror node about tokenRelationship of for all tokens that the account is
// being associated with
func accountInfoqueryTokensRelationshipFromMirrorNode(network string, id string, result *AccountInfo) (*AccountInfo, error) {
response, err := tokenReleationshipQuery(network, id)
if err != nil {
return result, err
}
tokens, ok := response["tokens"].([]interface{})
if !ok {
return result, errors.New("Ivalid tokens format")
}
mappedTokens, err := mapTokenRelationship(tokens)
if err != nil {
return &AccountInfo{}, err
}
result.TokenRelationships = mappedTokens
return result, nil
}

// SetGrpcDeadline When execution is attempted, a single attempt will timeout when this deadline is reached. (The SDK may subsequently retry the execution.)
Expand Down
12 changes: 7 additions & 5 deletions account_info_query_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ package hedera
*/

import (
"testing"

"github.com/stretchr/testify/assert"
"testing"
"time"

"github.com/stretchr/testify/require"
)
Expand All @@ -52,6 +52,7 @@ func TestIntegrationAccountInfoQueryCanExecute(t *testing.T) {

accountID := *receipt.AccountID
require.NoError(t, err)
time.Sleep(3 * time.Second)

info, err := NewAccountInfoQuery().
SetAccountID(accountID).
Expand Down Expand Up @@ -82,8 +83,8 @@ func TestIntegrationAccountInfoQueryCanExecute(t *testing.T) {
_, err = resp.SetValidateStatus(true).GetReceipt(env.Client)
require.NoError(t, err)

//err = CloseIntegrationTestEnv(env, nil)
//require.NoError(t, err)
err = CloseIntegrationTestEnv(env, nil)
require.NoError(t, err)
}

func TestIntegrationAccountInfoQueryGetCost(t *testing.T) {
Expand Down Expand Up @@ -116,7 +117,7 @@ func TestIntegrationAccountInfoQueryGetCost(t *testing.T) {

cost, err := accountInfo.GetCost(env.Client)
require.NoError(t, err)

time.Sleep(3 * time.Second)
info, err := accountInfo.SetQueryPayment(cost).Execute(env.Client)
require.NoError(t, err)

Expand Down Expand Up @@ -230,6 +231,7 @@ func TestIntegrationAccountInfoQuerySetBigMaxPayment(t *testing.T) {
_, err = accountInfo.GetCost(env.Client)
require.NoError(t, err)

time.Sleep(3 * time.Second)
info, err := accountInfo.SetQueryPayment(NewHbar(1)).Execute(env.Client)
require.NoError(t, err)

Expand Down
20 changes: 11 additions & 9 deletions contract_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ import (

// Current information on the smart contract instance, including its balance.
type ContractInfo struct {
AccountID AccountID
ContractID ContractID
ContractAccountID string
AdminKey Key
ExpirationTime time.Time
AutoRenewPeriod time.Duration
Storage uint64
ContractMemo string
Balance uint64
AccountID AccountID
ContractID ContractID
ContractAccountID string
AdminKey Key
ExpirationTime time.Time
AutoRenewPeriod time.Duration
Storage uint64
ContractMemo string
Balance uint64
// Deprecated
TokenRelationships []*TokenRelationship
LedgerID LedgerID
AutoRenewAccountID *AccountID
MaxAutomaticTokenAssociations int32
Expand Down
25 changes: 25 additions & 0 deletions contract_info_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package hedera
*/

import (
"errors"
"time"

"github.com/hashgraph/hedera-protobufs-go/services"
Expand Down Expand Up @@ -80,10 +81,34 @@ func (q *ContractInfoQuery) Execute(client *Client) (ContractInfo, error) {
if err != nil {
return ContractInfo{}, err
}
network := obtainUrlForMirrorNode(client)
_, err = contractInfoqueryTokensRelationshipFromMirrorNode(network, q.contractID.String(), &info)
if err != nil {
return ContractInfo{}, err
}

return info, nil
}

// Helper function, which query the mirror node about tokenRelationship of for all tokens that the account is
// being associated with
func contractInfoqueryTokensRelationshipFromMirrorNode(network string, id string, result *ContractInfo) (*ContractInfo, error) {
response, err := tokenReleationshipQuery(network, id)
if err != nil {
return result, err
}
tokens, ok := response["tokens"].([]interface{})
if !ok {
return result, errors.New("Ivalid tokens format")
}
mappedTokens, err := mapTokenRelationship(tokens)
if err != nil {
return &ContractInfo{}, err
}
result.TokenRelationships = mappedTokens
return result, nil
}

// SetMaxQueryPayment sets the maximum payment allowed for this Query.
func (q *ContractInfoQuery) SetMaxQueryPayment(maxPayment Hbar) *ContractInfoQuery {
q.Query.SetMaxQueryPayment(maxPayment)
Expand Down
98 changes: 98 additions & 0 deletions mirror_node_gateway.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package hedera

import (
"encoding/json"
"fmt"
"net/http"
)

/*-
*
* Hedera Go SDK
*
* Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
const httpsPrefix = "https://"
const apiPathVersion = "/api/v1"

var queryTypes = map[string]string{
"account": "accounts",
"contract": "contracts",
"token": "tokens"}

// Function to obtain balance of tokens for given account ID. Return the pure JSON response as mapping
func accountBalanceQuery(network string, accountId string) (map[string]interface{}, error) {
info, err := accountInfoQuery(network, accountId)
// Cast balance body to map
return info["balance"].(map[string]interface{}), err
}

// Function to obtain account info for given account ID. Return the pure JSON response as mapping
func accountInfoQuery(network string, accountId string) (map[string]interface{}, error) {
accountInfoUrl := buildUrl(network, queryTypes["account"], accountId)
return makeGetRequest(accountInfoUrl)
}

// Function to obtain balance of tokens for given contract ID. Return the pure JSON response as mapping
func contractInfoQuery(network string, contractId string) (map[string]interface{}, error) { // nolint
contractInfoUrl := buildUrl(network, queryTypes["contract"], contractId)
return makeGetRequest(contractInfoUrl)
}

func tokenReleationshipQuery(network string, id string) (map[string]interface{}, error) {
tokenRelationshipUrl := buildUrl(network, queryTypes["account"], id, queryTypes["token"])
return makeGetRequest(tokenRelationshipUrl)
}

// Make a GET HTTP request to provided URL and map it's json response to a generic `interface` map and return it
func makeGetRequest(url string) (response map[string]interface{}, e error) {
// Make an HTTP request
resp, err := http.Get(url) //nolint

if err != nil {
return nil, err
}
if resp.StatusCode >= 400 {
return nil, fmt.Errorf("HTTP request failed with status code: %d", resp.StatusCode)
}
defer resp.Body.Close()

// Decode the JSON response into a map
var resultMap map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&resultMap)
if err != nil {
return nil, err
}

return resultMap, nil
}

func obtainUrlForMirrorNode(client *Client) string {
const localNetwork = "127.0.0.1"
if client.GetMirrorNetwork()[0] == localNetwork+":5600" || client.GetMirrorNetwork()[0] == localNetwork+":443" {
return localNetwork + "5551"
} else {
return client.GetMirrorNetwork()[0]
}
}

func buildUrl(network string, args ...string) string {
url := httpsPrefix + network + apiPathVersion
for _, arg := range args {
url += "/" + arg
}
return url
}
Loading
Loading