Skip to content

Commit

Permalink
feat(903): associate token with account (#907)
Browse files Browse the repository at this point in the history
* feat(903): associate account with hedera token

Signed-off-by: Asen Prodanov <[email protected]>

* feat(903): add test

Signed-off-by: Asen Prodanov <[email protected]>

* feat(903): inc test cov

Signed-off-by: Asen Prodanov <[email protected]>

---------

Signed-off-by: Asen Prodanov <[email protected]>
  • Loading branch information
asenslime authored Aug 15, 2023
1 parent 44c5e7e commit 3a6873d
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 2 deletions.
31 changes: 30 additions & 1 deletion app/clients/hedera/mirror-node/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,36 @@ func (c Client) AccountExists(accountID hedera.AccountID) bool {
return c.query(accountQuery, accountID.String())
}

// GetAccount retrieves an account entity by its id
// GetAccount retrieves an account entity by its id or public key
func (c Client) GetAccountByPublicKey(publicKey string) (*account.AccountsQueryResponse, error) {
mirrorNodeApiTransactionAddress := fmt.Sprintf("%s%s", c.mirrorAPIAddress, "accounts")

query := fmt.Sprintf("%s?account.publickey=%s",
mirrorNodeApiTransactionAddress,
publicKey)

httpResponse, e := c.get(query)
if e != nil {
return nil, e
}
if httpResponse.StatusCode >= 400 {
return nil, fmt.Errorf(`Failed to execute query: [%s]. Error: [%s]`, query, query)
}

bodyBytes, e := readResponseBody(httpResponse)
if e != nil {
return nil, e
}

var response *account.AccountsQueryResponse
e = json.Unmarshal(bodyBytes, &response)
if e != nil {
return nil, e
}

return response, nil
}

func (c Client) GetAccount(accountID string) (*account.AccountsResponse, error) {
mirrorNodeApiTransactionAddress := fmt.Sprintf("%s%s", c.mirrorAPIAddress, "accounts")
query := fmt.Sprintf("%s/%s",
Expand Down
52 changes: 52 additions & 0 deletions app/clients/hedera/mirror-node/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,58 @@ func Test_GetAccount(t *testing.T) {
assert.Equal(t, expected.Balance.Balance, response.Balance.Balance)
}

func Test_GetAccountByPublicKey(t *testing.T) {
setup()

expected := account.AccountsQueryResponse{
Accounts: []struct {
Account string `json:"account"`
}{
{
Account: "0.0.1",
},
},
}

encodedContent, err := httpHelper.EncodeBodyContent(expected)
if err != nil {
t.Fatal(err)
}

mocks.MHTTPClient.On("Get", mock.Anything).Return(&http.Response{StatusCode: 200, Body: encodedContent}, nil)
response, err := c.GetAccountByPublicKey("1234")
assert.Nil(t, err)
assert.Equal(t, expected.Accounts[0].Account, response.Accounts[0].Account)
}

func Test_GetAccountByPublicKey_UnmarshalErr(t *testing.T) {
setup()

encodedContent, err := httpHelper.EncodeBodyContent("123")
if err != nil {
t.Fatal(err)
}

mocks.MHTTPClient.On("Get", mock.Anything).Return(&http.Response{StatusCode: 200, Body: encodedContent}, nil)
response, err := c.GetAccountByPublicKey("1234")
assert.Nil(t, response)
assert.Contains(t, err.Error(), "json: cannot unmarshal string into Go value of type")
}

func Test_GetAccountByPublicKey_Err(t *testing.T) {
setup()

encodedContent, err := httpHelper.EncodeBodyContent(nil)
if err != nil {
t.Fatal(err)
}

mocks.MHTTPClient.On("Get", mock.Anything).Return(&http.Response{StatusCode: 400, Body: encodedContent}, nil)
response, err := c.GetAccountByPublicKey("1234")
assert.Nil(t, response)
assert.Contains(t, err.Error(), "Failed to execute query")
}

func Test_GetToken_HttpErr(t *testing.T) {
setup()

Expand Down
8 changes: 8 additions & 0 deletions app/clients/hedera/mirror-node/model/account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ type (
Account string `json:"account"`
Balance Balance `json:"balance"`
}

// AccountsQueryResponse struct used by the Hedera Mirror node REST API to return information regarding a given Account
AccountsQueryResponse struct {
Accounts []struct {
Account string `json:"account"`
} `json:"accounts"`
}

// Balance struct used by the Hedera Mirror node REST API to return information
// regarding a given Account
Balance struct {
Expand Down
2 changes: 2 additions & 0 deletions app/domain/client/mirror-node.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ type MirrorNode interface {
AccountExists(accountID hedera.AccountID) bool
// GetAccount gets the account data by ID.
GetAccount(accountID string) (*account.AccountsResponse, error)
// GetAccountByPublicKey gets the account data by public key
GetAccountByPublicKey(publicKey string) (*account.AccountsQueryResponse, error)
// GetToken gets the token data by ID.
GetToken(tokenID string) (*token.TokenResponse, error)
// TopicExists sends a query to check whether a specific topic exists. If the query returns a status != 200, the function returns a false value
Expand Down
2 changes: 1 addition & 1 deletion e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"database/sql"
"encoding/base64"
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"math/big"
"strconv"
"testing"
Expand All @@ -43,7 +44,6 @@ import (
"github.com/limechain/hedera-eth-bridge-validator/e2e/helper/verify"
"github.com/limechain/hedera-eth-bridge-validator/e2e/setup"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/hashgraph/hedera-sdk-go/v2"
)
Expand Down
52 changes: 52 additions & 0 deletions scripts/token/native/create/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ import (
"flag"
"fmt"
"github.com/hashgraph/hedera-sdk-go/v2"
mirrorNode "github.com/limechain/hedera-eth-bridge-validator/app/clients/hedera/mirror-node"
"github.com/limechain/hedera-eth-bridge-validator/config"
"github.com/limechain/hedera-eth-bridge-validator/scripts/client"
"github.com/limechain/hedera-eth-bridge-validator/scripts/token/associate"
"github.com/limechain/hedera-eth-bridge-validator/scripts/token/native/create"
"strings"
)

const (
HederaMainnetNetworkId = 295
HederaTestnetNetworkId = 296
)

func main() {
privateKey := flag.String("privateKey", "0x0", "Hedera Private Key")
accountID := flag.String("accountID", "0.0", "Hedera Account ID")
Expand All @@ -51,6 +58,18 @@ func main() {
fmt.Println("-----------Start-----------")
client := client.Init(*privateKey, *accountID, *network)

mirrorNodeConfigByNetwork := map[uint64]config.MirrorNode{
HederaMainnetNetworkId: {
ClientAddress: "mainnet-public.mirrornode.hedera.com/:443",
ApiAddress: "https://mainnet-public.mirrornode.hedera.com/api/v1/",
},
HederaTestnetNetworkId: {
ClientAddress: "hcs.testnet.mirrornode.hedera.com:5600",
ApiAddress: "https://testnet.mirrornode.hedera.com/api/v1/",
},
}

var hederaNetworkId uint64
if *network != "testnet" && *setSupplyKey {
var confirmation string
fmt.Printf("Network is set to [%s] and setSupplyKey is set to [%v]. Are you sure you what to proceed?\n", *network, *setSupplyKey)
Expand All @@ -59,16 +78,23 @@ func main() {
if confirmation != "Y" {
panic("Exiting")
}
hederaNetworkId = HederaMainnetNetworkId
} else {
hederaNetworkId = HederaTestnetNetworkId
}

mirrorNodeClient := mirrorNode.NewClient(mirrorNodeConfigByNetwork[hederaNetworkId])
membersSlice := strings.Split(*memberPrKeys, ",")

var custodianKey []hedera.PrivateKey
var membersPublicKey []hedera.PublicKey
for i := 0; i < len(membersSlice); i++ {
privateKeyFromStr, err := hedera.PrivateKeyFromString(membersSlice[i])
if err != nil {
panic(err)
}

membersPublicKey = append(membersPublicKey, privateKeyFromStr.PublicKey())
custodianKey = append(custodianKey, privateKeyFromStr)
}

Expand Down Expand Up @@ -97,4 +123,30 @@ func main() {
}
fmt.Println("Token ID:", tokenId)
fmt.Println("Associate transaction status:", receipt.Status)

// associate token with members
for _, memberPrKey := range membersPublicKey {
accounts, err := mirrorNodeClient.GetAccountByPublicKey(memberPrKey.String())
if err != nil {
panic(fmt.Errorf("cannot obtain account by public key: %w", err))
}

if len(accounts.Accounts) == 0 {
panic("cannot find account by public key")
} else if len(accounts.Accounts) != 1 {
panic("multiple accounts found for public key - " + memberPrKey.String())
}

hAccount, err := hedera.AccountIDFromString(accounts.Accounts[0].Account)
if err != nil {
panic(fmt.Errorf("cannot convert string to hedera account: %w", err))
}

receipt, err := associate.TokenToAccount(client, *tokenId, hAccount)
if err != nil {
panic(fmt.Errorf("failed to associate token to account: %w", err))
}
fmt.Printf("Account[%s] associated with token[%s], tx status: %s\n",
hAccount.String(), tokenId.String(), receipt.Status)
}
}
9 changes: 9 additions & 0 deletions test/mocks/client/hedera_mirror_client_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ func (m *MockHederaMirror) AccountExists(accountID hedera.AccountID) bool {
return args.Get(0).(bool)
}

func (m *MockHederaMirror) GetAccountByPublicKey(publicKey string) (*account.AccountsQueryResponse, error) {
args := m.Called(publicKey)

if args.Get(1) == nil {
return args.Get(0).(*account.AccountsQueryResponse), nil
}
return args.Get(0).(*account.AccountsQueryResponse), args.Get(1).(error)
}

func (m *MockHederaMirror) GetAccount(accountID string) (*account.AccountsResponse, error) {
args := m.Called(accountID)

Expand Down

0 comments on commit 3a6873d

Please sign in to comment.