Skip to content

Commit

Permalink
feat(903): associate account with hedera token
Browse files Browse the repository at this point in the history
  • Loading branch information
asenslime committed Aug 11, 2023
1 parent 663b30f commit 0af328b
Show file tree
Hide file tree
Showing 6 changed files with 129 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
35 changes: 35 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,41 @@ 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"`
Alias string `json:"alias"`
AutoRenewPeriod interface{} `json:"auto_renew_period"`
Balance struct {
Timestamp string `json:"timestamp"`
Balance int `json:"balance"`
Tokens []struct {
TokenId string `json:"token_id"`
Balance int `json:"balance"`
} `json:"tokens"`
} `json:"balance"`
CreatedTimestamp string `json:"created_timestamp"`
DeclineReward bool `json:"decline_reward"`
Deleted bool `json:"deleted"`
EthereumNonce int `json:"ethereum_nonce"`
EvmAddress string `json:"evm_address"`
ExpiryTimestamp interface{} `json:"expiry_timestamp"`
Key interface{} `json:"key"`
MaxAutomaticTokenAssociations int `json:"max_automatic_token_associations"`
Memo string `json:"memo"`
PendingReward int `json:"pending_reward"`
ReceiverSigRequired bool `json:"receiver_sig_required"`
StakedAccountId interface{} `json:"staked_account_id"`
StakedNodeId int `json:"staked_node_id"`
StakePeriodStart string `json:"stake_period_start"`
} `json:"accounts"`
Links struct {
Next string `json:"next"`
} `json:"links"`
}

// 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 0af328b

Please sign in to comment.