diff --git a/.github/workflows/publish_release.yaml b/.github/workflows/publish_release.yaml index 335c5e0d1d..6006ba91a5 100644 --- a/.github/workflows/publish_release.yaml +++ b/.github/workflows/publish_release.yaml @@ -177,6 +177,32 @@ jobs: - name: Install Playwright Dependencies run: sudo npx playwright install-deps + - name: Check Proto Subpackage Publish Status + id: proto + working-directory: packages/proto + run: | + PACKAGE_VERSION="$(node -p "require('./package.json').version")" + PUBLISH_REQUIRED="false" + if ! curl -sSLf "https://registry.npmjs.org/@hashgraph/proto/${PACKAGE_VERSION}" >/dev/null 2>&1; then + PUBLISH_REQUIRED="true" + fi + + echo "version=${PACKAGE_VERSION}" >>"${GITHUB_OUTPUT}" + echo "publish-required=${PUBLISH_REQUIRED}" >>"${GITHUB_OUTPUT}" + + - name: Check Cryptography Subpackage Publish Status + id: cryptography + working-directory: packages/cryptography + run: | + PACKAGE_VERSION="$(node -p "require('./package.json').version")" + PUBLISH_REQUIRED="false" + if ! curl -sSLf "https://registry.npmjs.org/@hashgraph/cryptography/${PACKAGE_VERSION}" >/dev/null 2>&1; then + PUBLISH_REQUIRED="true" + fi + + echo "version=${PACKAGE_VERSION}" >>"${GITHUB_OUTPUT}" + echo "publish-required=${PUBLISH_REQUIRED}" >>"${GITHUB_OUTPUT}" + - name: Calculate Publish Arguments id: publish run: | @@ -188,14 +214,28 @@ jobs: # Add the registry authentication stanza with variable substitution to the .npmrc configuration file. echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' >>".npmrc" - - name: Publish Release + - name: Publish Proto Release + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + working-directory: packages/proto + if: ${{ steps.proto.outputs.publish-required == 'true' && !cancelled() && !failure() }} + run: task publish -- ${{ steps.publish.outputs.args }} + + - name: Publish Cryptography Release + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + working-directory: packages/cryptography + if: ${{ steps.cryptography.outputs.publish-required == 'true' && !cancelled() && !failure() }} + run: task publish -- ${{ steps.publish.outputs.args }} + + - name: Publish SDK Release env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: task publish -- ${{ steps.publish.outputs.args }} - name: Generate Github Release uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 - if: ${{ github.event.inputs.dry-run-enabled != 'true' }} + if: ${{ github.event.inputs.dry-run-enabled != 'true' && !cancelled() && !failure() }} with: tag: ${{ steps.validate-release.outputs.tag }} prerelease: ${{ needs.validate-release.outputs.prerelease == 'true' }} diff --git a/CHANGELOG.md b/CHANGELOG.md index dbf4e109ad..368ef3b151 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v2.44.0 + +## What's Changed + +* fix: set correct autoRenrewAccountId by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2217 +* update: add a new getter to the TransferTransaction class by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2214 +* fix: integer overflow isuue for defaultMaxQueryPayment field by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2213 +* chore(ci): update the publish workflow to release the cryptography and proto artifacts if needed by @nathanklick in https://github.com/hashgraph/hedera-sdk-js/pull/2198 + ## v2.43.0 ## What's Changed diff --git a/package.json b/package.json index c4abf4eea9..e26d854b55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hashgraph/sdk", - "version": "2.43.0", + "version": "2.44.0", "description": "Hederaâ„¢ Hashgraph SDK", "types": "./lib/index.d.ts", "main": "./lib/index.cjs", diff --git a/src/account/TransferTransaction.js b/src/account/TransferTransaction.js index 008268408b..7df47038ff 100644 --- a/src/account/TransferTransaction.js +++ b/src/account/TransferTransaction.js @@ -368,6 +368,13 @@ export default class TransferTransaction extends Transaction { return map; } + /** + * @returns {Transfer[]} + */ + get hbarTransfersList() { + return this._hbarTransfers; + } + /** * @internal * @param {AccountId | string} accountId diff --git a/src/client/Client.js b/src/client/Client.js index e2d9b61054..63a0ed651f 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -29,6 +29,7 @@ import LedgerId from "../LedgerId.js"; import FileId from "../file/FileId.js"; import CACHE from "../Cache.js"; import Logger from "../logger/Logger.js"; // eslint-disable-line +import { convertToNumber } from "../util.js"; /** * @typedef {import("../channel/Channel.js").default} Channel @@ -449,7 +450,9 @@ export default class Client { * @returns {Client} */ setDefaultMaxQueryPayment(defaultMaxQueryPayment) { - if (defaultMaxQueryPayment.toTinybars().toInt() < 0) { + const isMaxQueryPaymentNegative = + convertToNumber(defaultMaxQueryPayment.toTinybars()) < 0; + if (isMaxQueryPaymentNegative) { throw new Error("defaultMaxQueryPayment must be non-negative"); } this._defaultMaxQueryPayment = defaultMaxQueryPayment; diff --git a/src/token/TokenCreateTransaction.js b/src/token/TokenCreateTransaction.js index 1c335166fb..0ea8b030ac 100644 --- a/src/token/TokenCreateTransaction.js +++ b/src/token/TokenCreateTransaction.js @@ -172,7 +172,12 @@ export default class TokenCreateTransaction extends Transaction { * @private * @type {?Timestamp} */ - this._expirationTime = null; + this._expirationTime = new Timestamp( + Math.floor( + Date.now() / 1000 + DEFAULT_AUTO_RENEW_PERIOD.toNumber(), + ), + 0, + ); /** * @private @@ -657,7 +662,6 @@ export default class TokenCreateTransaction extends Transaction { */ setExpirationTime(time) { this._requireNotFrozen(); - this._autoRenewPeriod = null; this._expirationTime = time instanceof Timestamp ? time : Timestamp.fromDate(time); @@ -791,30 +795,6 @@ export default class TokenCreateTransaction extends Transaction { return this; } - /** - * @override - * @param {AccountId} accountId - */ - _freezeWithAccountId(accountId) { - super._freezeWithAccountId(accountId); - - if (this._autoRenewPeriod != null && accountId != null) { - this._autoRenewAccountId = accountId; - } - } - - /** - * @param {?import("../client/Client.js").default} client - * @returns {this} - */ - freezeWith(client) { - if (client != null && client.operatorAccountId != null) { - this._freezeWithAccountId(client.operatorAccountId); - } - - return super.freezeWith(client); - } - /** * @param {Client} client */ diff --git a/test/integration/ClientIntegrationTest.js b/test/integration/ClientIntegrationTest.js index 117278c792..204faaa380 100644 --- a/test/integration/ClientIntegrationTest.js +++ b/test/integration/ClientIntegrationTest.js @@ -172,6 +172,24 @@ describe("ClientIntegration", function () { expect(clientTestnet.isTransportSecurity()).to.be.an("boolean"); }); + it("should return the following error message `defaultMaxQueryPayment must be non-negative` when the user tries to set a negative value to the defaultMaxQueryPayment field", async function () { + this.timeout(120000); + try { + env.client.setDefaultMaxQueryPayment(new Hbar(1).negated()); + } catch (error) { + expect(error.message).to.be.equal( + "defaultMaxQueryPayment must be non-negative", + ); + } + }); + + it("should set defaultMaxQueryPayment field", async function () { + this.timeout(120000); + const value = new Hbar(100); + env.client.setDefaultMaxQueryPayment(value); + expect(env.client.defaultMaxQueryPayment).to.be.equal(value); + }); + after(async function () { await env.close(); clientTestnet.close(); diff --git a/test/integration/TokenCreateIntegrationTest.js b/test/integration/TokenCreateIntegrationTest.js index b4da60200a..fd50af46b7 100644 --- a/test/integration/TokenCreateIntegrationTest.js +++ b/test/integration/TokenCreateIntegrationTest.js @@ -1,6 +1,7 @@ import { PrivateKey, Status, + Timestamp, TokenCreateTransaction, TokenDeleteTransaction, TokenInfoQuery, @@ -59,12 +60,8 @@ describe("TokenCreate", function () { expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; }); @@ -101,12 +98,8 @@ describe("TokenCreate", function () { expect(info.defaultFreezeStatus).to.be.null; expect(info.defaultKycStatus).to.be.null; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; let err = false; @@ -126,6 +119,95 @@ describe("TokenCreate", function () { } }); + it("when autoRenewAccountId is set", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + + const response = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(operatorId) + .setAutoRenewAccountId(operatorId) + .execute(env.client); + + const tokenId = (await response.getReceipt(env.client)).tokenId; + + const info = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(info.autoRenewAccountId).to.be.not.null; + expect(info.autoRenewAccountId.toString()).to.be.eql( + operatorId.toString(), + ); + }); + + it("when expirationTime is set", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const DAYS_45_IN_SECONDS = 3888000; + const expirationTime = new Timestamp( + Math.floor(Date.now() / 1000 + DAYS_45_IN_SECONDS), + 0, + ); + + const response = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(operatorId) + .setExpirationTime(expirationTime) + .execute(env.client); + + const tokenId = (await response.getReceipt(env.client)).tokenId; + + const info = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(info.expirationTime).to.be.not.null; + expect(info.expirationTime.toString()).to.be.eql( + expirationTime.toString(), + ); + }); + + it("when autoRenewAccountId and expirationTime are set", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const DAYS_90_IN_SECONDS = 7776000; + const expirationTime = new Timestamp( + Math.floor(Date.now() / 1000 + DAYS_90_IN_SECONDS), + 0, + ); + + const response = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(operatorId) + .setExpirationTime(expirationTime) + .setAutoRenewAccountId(operatorId) + .execute(env.client); + + const tokenId = (await response.getReceipt(env.client)).tokenId; + + const info = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(info.autoRenewAccountId).to.be.not.null; + expect(info.autoRenewAccountId.toString()).to.be.eql( + operatorId.toString(), + ); + expect(info.autoRenewPeriod).to.be.not.null; + expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.expirationTime).to.be.not.null; + expect(info.expirationTime.toString()).to.be.eql( + expirationTime.toString(), + ); + }); + it("should error when token name is not set", async function () { this.timeout(120000); diff --git a/test/integration/TokenInfoIntegrationTest.js b/test/integration/TokenInfoIntegrationTest.js index ec40015576..389994a2c4 100644 --- a/test/integration/TokenInfoIntegrationTest.js +++ b/test/integration/TokenInfoIntegrationTest.js @@ -59,12 +59,8 @@ describe("TokenInfo", function () { expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; }); @@ -101,12 +97,8 @@ describe("TokenInfo", function () { expect(info.defaultFreezeStatus).to.be.null; expect(info.defaultKycStatus).to.be.null; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; }); diff --git a/test/integration/TokenUpdateIntegrationTest.js b/test/integration/TokenUpdateIntegrationTest.js index 28a9de2cd5..64ddc03594 100644 --- a/test/integration/TokenUpdateIntegrationTest.js +++ b/test/integration/TokenUpdateIntegrationTest.js @@ -70,12 +70,8 @@ describe("TokenUpdate", function () { expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; await ( @@ -104,12 +100,8 @@ describe("TokenUpdate", function () { expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; }); @@ -171,12 +163,8 @@ describe("TokenUpdate", function () { expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; await ( @@ -219,12 +207,8 @@ describe("TokenUpdate", function () { expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; }); diff --git a/test/unit/TokenCreateTransaction.js b/test/unit/TokenCreateTransaction.js index 7fc9425035..90e8150ca6 100644 --- a/test/unit/TokenCreateTransaction.js +++ b/test/unit/TokenCreateTransaction.js @@ -7,6 +7,7 @@ import { Timestamp, } from "../../src/index.js"; import Long from "long"; +import { DEFAULT_AUTO_RENEW_PERIOD } from "../../src/transaction/Transaction.js"; describe("TokenCreateTransaction", function () { it("encodes to correct protobuf", function () { @@ -34,6 +35,13 @@ describe("TokenCreateTransaction", function () { const autoRenewAccountId = new AccountId(10); const treasuryAccountId = new AccountId(11); + const expirationTime = new Timestamp( + Math.floor( + Date.now() / 1000 + DEFAULT_AUTO_RENEW_PERIOD.toNumber(), + ), + 0, + ); + const transaction = new TokenCreateTransaction() .setMaxTransactionFee(new Hbar(30)) .setTransactionId( @@ -48,6 +56,7 @@ describe("TokenCreateTransaction", function () { .setDecimals(7) .setTreasuryAccountId(treasuryAccountId) .setAutoRenewAccountId(autoRenewAccountId) + .setExpirationTime(expirationTime) .setAdminKey(key1) .setKycKey(key2) .setFreezeKey(key3) @@ -77,7 +86,7 @@ describe("TokenCreateTransaction", function () { autoRenewPeriod: { seconds: Long.fromValue(7776000), }, - expiry: null, + expiry: expirationTime._toProtobuf(), treasury: treasuryAccountId._toProtobuf(), adminKey: { ed25519: key1.publicKey.toBytesRaw(), diff --git a/test/unit/TransferTransaction.js b/test/unit/TransferTransaction.js index 770ce963cc..1c7a2fa432 100644 --- a/test/unit/TransferTransaction.js +++ b/test/unit/TransferTransaction.js @@ -374,4 +374,27 @@ describe("TransferTransaction", function () { ], }); }); + + it("should return hbarTransfer list", function () { + const accountId1 = AccountId.fromString("0.0.0"); + const accountId2 = AccountId.fromString("0.0.1"); + const amount = new Hbar(1); + + const tx = new TransferTransaction() + .addHbarTransfer(accountId1, amount.negated()) + .addHbarTransfer(accountId2, amount); + + expect(tx.hbarTransfersList).to.deep.equal([ + { + accountId: accountId1, + amount: amount.negated(), + isApproved: false, + }, + { + accountId: accountId2, + amount: amount, + isApproved: false, + }, + ]); + }); });