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

feat: solution to query the mirror node for the account balance, account info, and contract info data #2289

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
cd7779a
update: queries
svetoslav-nikol0v May 10, 2024
de0edbd
update: queries
svetoslav-nikol0v May 15, 2024
cb91a79
fix: adjustments and formatting
svetoslav-nikol0v May 15, 2024
aba6d43
update: increase timeout
svetoslav-nikol0v May 15, 2024
4e08ee9
update: integration tests
svetoslav-nikol0v May 21, 2024
a1bfa7e
remove logs
svetoslav-nikol0v May 27, 2024
7ea713f
update: test
svetoslav-nikol0v May 28, 2024
716c200
fix: reorder tests in the suite
svetoslav-nikol0v May 28, 2024
a360bbf
fix: reliability issue
svetoslav-nikol0v May 28, 2024
cea4dc6
fix: reliability issue
svetoslav-nikol0v May 28, 2024
c602b44
fix: integration test
svetoslav-nikol0v May 28, 2024
b4a2a49
update: reorder steps in the pipeline
svetoslav-nikol0v May 28, 2024
f70337f
update: update build workflow
svetoslav-nikol0v May 28, 2024
db6ee8c
update: reorder steps in build workflow
svetoslav-nikol0v May 28, 2024
8d56428
chore: remove log
svetoslav-nikol0v May 29, 2024
2be6ead
chore: adjustment
svetoslav-nikol0v May 29, 2024
987953d
fix
svetoslav-nikol0v May 29, 2024
3da90e4
update: unit test
svetoslav-nikol0v May 29, 2024
bfacf9c
fix: unit test
svetoslav-nikol0v May 29, 2024
6c9fb6c
update: unit tests
svetoslav-nikol0v May 29, 2024
3b08ef0
update: revert workflow
svetoslav-nikol0v May 29, 2024
7602f57
update workflow
svetoslav-nikol0v May 29, 2024
a967f1e
update: workflow
svetoslav-nikol0v May 29, 2024
c785296
update workflow
svetoslav-nikol0v May 29, 2024
970b7ac
update: workflow
svetoslav-nikol0v May 29, 2024
2205131
update: workflow
svetoslav-nikol0v May 29, 2024
0b7bcfd
add logs
svetoslav-nikol0v May 29, 2024
404bac0
update
svetoslav-nikol0v May 29, 2024
91289cf
..
svetoslav-nikol0v May 29, 2024
abef8df
workflow
svetoslav-nikol0v May 29, 2024
439cbc1
log
svetoslav-nikol0v May 29, 2024
6650019
update
svetoslav-nikol0v May 30, 2024
e119fa9
update: workflow
svetoslav-nikol0v May 30, 2024
bae37ba
update
svetoslav-nikol0v May 30, 2024
ccb63b2
update: workflow
svetoslav-nikol0v May 30, 2024
b09ce78
chore: formatting
svetoslav-nikol0v May 30, 2024
93f25f2
update
svetoslav-nikol0v May 30, 2024
43a71fa
update: workflow
svetoslav-nikol0v May 30, 2024
cdc252a
add: tests
svetoslav-nikol0v May 30, 2024
f6eb0d1
chore: formatting
svetoslav-nikol0v May 30, 2024
34ffbea
deprecate symbol
svetoslav-nikol0v May 31, 2024
1d59900
update: review changes
svetoslav-nikol0v Jun 4, 2024
bbb9299
update: added missing statuses
svetoslav-nikol0v Jun 4, 2024
7c3d1a2
chore: renaming
svetoslav-nikol0v Jun 4, 2024
e68c0c0
chore: naming
svetoslav-nikol0v Jun 4, 2024
afca2d4
chore: formatting
svetoslav-nikol0v Jun 4, 2024
6881780
fix: typo
svetoslav-nikol0v Jun 6, 2024
20fd9f0
update: add new response code
svetoslav-nikol0v Jun 6, 2024
495d2ed
update: build workflow
svetoslav-nikol0v Jun 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,30 +164,30 @@ jobs:
if: ${{ steps.build-sdk.conclusion == 'success' && steps.start-local-node.conclusion == 'success' && !cancelled() && always() }}
run: ${{ env.CG_EXEC }} task test:integration:codecov

- name: Stop the local node
id: stop-local-node
if: ${{ steps.start-local-node.conclusion == 'success' && !cancelled() && always() }}
run: ${{ env.CG_EXEC }} npx @hashgraph/hedera-local stop

- name: Build @hashgraph/cryptography
working-directory: packages/cryptography
if: ${{ steps.build-sdk.conclusion == 'success' && steps.stop-local-node.conclusion == 'success' && !cancelled() && always() }}
if: ${{ steps.build-sdk.conclusion == 'success' && steps.start-local-node.conclusion == 'success' && !cancelled() && always() }}
run: ${{ env.CG_EXEC }} task build

- name: Unit Test @hashgraph/cryptography
working-directory: packages/cryptography
if: ${{ steps.build-sdk.conclusion == 'success' && steps.stop-local-node.conclusion == 'success' && !cancelled() && always() }}
if: ${{ steps.build-sdk.conclusion == 'success' && steps.start-local-node.conclusion == 'success' && !cancelled() && always() }}
run: ${{ env.CG_EXEC }} task test:unit

- name: Codecov @hashgraph/cryptography
working-directory: packages/cryptography
if: ${{ steps.build-sdk.conclusion == 'success' && steps.stop-local-node.conclusion == 'success' && !cancelled() && always() }}
if: ${{ steps.build-sdk.conclusion == 'success' && steps.start-local-node.conclusion == 'success' && !cancelled() && always() }}
run: ${{ env.CG_EXEC }} task test:unit:codecov

- name: Unit Test @hashgraph/sdk
if: ${{ steps.build-sdk.conclusion == 'success' && steps.stop-local-node.conclusion == 'success' && steps.playwright-deps.conclusion == 'success' && !cancelled() && always() }}
if: ${{ steps.build-sdk.conclusion == 'success' && steps.start-local-node.conclusion == 'success' && steps.playwright-deps.conclusion == 'success' && !cancelled() && always() }}
run: ${{ env.CG_EXEC }} task test:unit

- name: Codecov @hashgraph/sdk
if: ${{ steps.build-sdk.conclusion == 'success' && steps.stop-local-node.conclusion == 'success' && !cancelled() && always() }}
if: ${{ steps.build-sdk.conclusion == 'success' && steps.start-local-node.conclusion == 'success' && !cancelled() && always() }}
run: ${{ env.CG_EXEC }} task test:unit:codecov

- name: Stop the local node
id: stop-local-node
if: ${{ steps.start-local-node.conclusion == 'success' && !cancelled() && always() }}
run: ${{ env.CG_EXEC }} npx @hashgraph/hedera-local stop
23 changes: 23 additions & 0 deletions src/Executable.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import HttpError from "./http/HttpError.js";
* @typedef {import("./Signer.js").Signer} Signer
* @typedef {import("./PublicKey.js").default} PublicKey
* @typedef {import("./logger/Logger.js").default} Logger
* @typedef {import("./LedgerId.js").default} LedgerId
*/

/**
Expand Down Expand Up @@ -134,6 +135,20 @@ export default class Executable {
* @type {Logger | null}
*/
this._logger = null;

/**
* List of mirror network nodes with which execution will be attempted.
* @protected
* @type {string[]}
*/
this._mirrorNetworkNodes = [];

/**
* Current LedgerId of the network with which execution will be attempted.
* @protected
* @type {LedgerId | null}
*/
this._ledgerId = null;
}

/**
Expand Down Expand Up @@ -511,6 +526,14 @@ export default class Executable {
* @returns {Promise<OutputT>}
*/
async execute(client, requestTimeout) {
// Set list of mirror network nodes with
// which execution will be attempted
this._mirrorNetworkNodes = client.mirrorNetwork;

// Set current LedgerId of the network with
// which execution will be attempted
this._ledgerId = client.ledgerId;

// If the logger on the request is not set, use the logger in client
// (if set, otherwise do not use logger)
this._logger =
Expand Down
54 changes: 54 additions & 0 deletions src/Status.js
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,18 @@ export default class Status {
return "INVALID_GRPC_CERTIFICATE";
case Status.InvalidMaxAutoAssociations:
return "INVALID_MAX_AUTO_ASSOCIATIONS";
case Status.MaxNodesCreated:
return "MAX_NODES_CREATED";
case Status.IpFqdnCannotBeSetForSameEndpoint:
return "IP_FQDN_CANNOT_BE_SET_FOR_SAME_ENDPOINT";
case Status.GossipEndpointCannotHaveFqdn:
return "GOSSIP_ENDPOINT_CANNOT_HAVE_FQDN";
case Status.FqdnSizeTooLarge:
return "FQDN_SIZE_TOO_LARGE";
case Status.InvalidEndpoint:
return "INVALID_ENDPOINT";
case Status.GossipEndpointsExceededLimit:
return "GOSSIP_ENDPOINTS_EXCEEDED_LIMIT";
default:
return `UNKNOWN (${this._code})`;
}
Expand Down Expand Up @@ -1273,6 +1285,18 @@ export default class Status {
return Status.InvalidGrpcCertificate;
case 346:
return Status.InvalidMaxAutoAssociations;
case 347:
return Status.MaxNodesCreated;
case 348:
return Status.IpFqdnCannotBeSetForSameEndpoint;
case 349:
return Status.GossipEndpointCannotHaveFqdn;
case 350:
return Status.FqdnSizeTooLarge;
case 351:
return Status.InvalidEndpoint;
case 352:
return Status.GossipEndpointsExceededLimit;
default:
throw new Error(
`(BUG) Status.fromCode() does not handle code: ${code}`,
Expand Down Expand Up @@ -2855,3 +2879,33 @@ Status.InvalidGrpcCertificate = new Status(345);
* The most common cause for this error is a value less than `-1`.
*/
Status.InvalidMaxAutoAssociations = new Status(346);

/**
* The maximum number of nodes allowed in the address book have been created.
*/
Status.MaxNodesCreated = new Status(347);

/**
* In ServiceEndpoint, domain_name and ipAddressV4 are mutually exclusive
*/
Status.IpFqdnCannotBeSetForSameEndpoint = new Status(348);

/**
* Fully qualified domain name is not allowed in gossip_endpoint
*/
Status.GossipEndpointCannotHaveFqdn = new Status(349);

/**
* In ServiceEndpoint, domain_name size too large
*/
Status.FqdnSizeTooLarge = new Status(350);

/**
* ServiceEndpoint is invalid
*/
Status.InvalidEndpoint = new Status(351);

/**
* The number of gossip endpoints exceeds the limit
*/
Status.GossipEndpointsExceededLimit = new Status(352);
10 changes: 0 additions & 10 deletions src/account/AccountBalance.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,8 @@ export default class AccountBalance {
*/
this.hbars = props.hbars;

/**
* @deprecated - Use the mirror node API https://docs.hedera.com/guides/docs/mirror-node-api/rest-api#api-v1-accounts instead
* @readonly
*/
// eslint-disable-next-line deprecation/deprecation
this.tokens = props.tokens;

/**
* @deprecated - Use the mirror node API https://docs.hedera.com/guides/docs/mirror-node-api/rest-api#api-v1-accounts instead
* @readonly
*/
// eslint-disable-next-line deprecation/deprecation
this.tokenDecimals = props.tokenDecimals;

Object.freeze(this);
Expand Down
75 changes: 69 additions & 6 deletions src/account/AccountBalanceQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import Query, { QUERY_REGISTRY } from "../query/Query.js";
import AccountId from "./AccountId.js";
import ContractId from "../contract/ContractId.js";
import AccountBalance from "./AccountBalance.js";
import MirrorNodeService from "../network/MirrorNodeService.js";
import MirrorNodeGateway from "../network/MirrorNodeGateway.js";

/**
* @namespace proto
Expand All @@ -31,6 +33,7 @@ import AccountBalance from "./AccountBalance.js";
* @typedef {import("@hashgraph/proto").proto.IResponseHeader} HashgraphProto.proto.IResponseHeader
* @typedef {import("@hashgraph/proto").proto.ICryptoGetAccountBalanceQuery} HashgraphProto.proto.ICryptoGetAccountBalanceQuery
* @typedef {import("@hashgraph/proto").proto.ICryptoGetAccountBalanceResponse} HashgraphProto.proto.ICryptoGetAccountBalanceResponse
* @typedef {import("@hashgraph/proto").proto.ITokenBalance} HashgraphProto.proto.ITokenBalance
*/

/**
Expand Down Expand Up @@ -69,6 +72,13 @@ export default class AccountBalanceQuery extends Query {
*/
this._contractId = null;

/**
* @private
* @description Delay in ms if is necessary to wait for the mirror node to update the account balance
* @type {number}
*/
this._timeout = 0;

if (props.accountId != null) {
this.setAccountId(props.accountId);
}
Expand Down Expand Up @@ -149,6 +159,16 @@ export default class AccountBalanceQuery extends Query {
return this;
}

/**
*
* @param {number} timeout
* @returns {this}
*/
setTimeout(timeout) {
this._timeout = timeout;
return this;
}

/**
* @protected
* @override
Expand Down Expand Up @@ -210,13 +230,56 @@ export default class AccountBalanceQuery extends Query {
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_mapResponse(response, nodeAccountId, request) {
const cryptogetAccountBalance =
/** @type {HashgraphProto.proto.ICryptoGetAccountBalanceResponse} */ (
response.cryptogetAccountBalance
return new Promise((resolve, reject) => {
const mirrorNodeGateway = MirrorNodeGateway.forNetwork(
this._mirrorNetworkNodes,
this._ledgerId,
);
return Promise.resolve(
AccountBalance._fromProtobuf(cryptogetAccountBalance),
);
const mirrorNodeService = new MirrorNodeService(mirrorNodeGateway);

const cryptogetAccountBalanceFromConsensusNode =
/** @type {HashgraphProto.proto.ICryptoGetAccountBalanceResponse} */ (
response.cryptogetAccountBalance
);

if (cryptogetAccountBalanceFromConsensusNode.accountID) {
const accountIdFromConsensusNode = AccountId._fromProtobuf(
cryptogetAccountBalanceFromConsensusNode.accountID,
);

mirrorNodeService
.setTimeout(this._timeout)
.getTokenBalancesForAccount(
accountIdFromConsensusNode.num.toString(),
)
.then(
(
/** @type {HashgraphProto.proto.ITokenBalance[]} */ tokenBalances,
) => {
if (
cryptogetAccountBalanceFromConsensusNode?.tokenBalances &&
tokenBalances
) {
// Reset the array to avoid duplicates
cryptogetAccountBalanceFromConsensusNode.tokenBalances.length = 0;
// Add the token balances from the mirror node to the response from the consensus node
cryptogetAccountBalanceFromConsensusNode.tokenBalances.push(
...tokenBalances,
);

resolve(
AccountBalance._fromProtobuf(
cryptogetAccountBalanceFromConsensusNode,
),
);
}
},
)
.catch((error) => {
reject(error);
});
}
});
}

/**
Expand Down
Loading