From 8055d2e8ca43d6fffeef637076a934ad2d6e6c1e Mon Sep 17 00:00:00 2001 From: prajeeta pal Date: Fri, 28 Nov 2025 13:22:57 +0530 Subject: [PATCH 1/4] fix: workflow check Signed-off-by: prajeeta pal --- .github/workflows/pr-checks.yml | 83 ++++++++++++++++++++++++++++++--- CHANGELOG.md | 4 +- 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 4aa042ae2..515b20c33 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -73,7 +73,9 @@ jobs: # List files in the PR and check for CHANGELOG.md (root or any path ending with /CHANGELOG.md) FILES_JSON="$(gh api repos/${{ github.repository }}/pulls/${PR_NUMBER}/files --paginate)" if ! echo "${FILES_JSON}" | jq -e '.[] | select(.filename | test("(^|/)CHANGELOG\\.md$"; "i"))' >/dev/null; then - echo "FAIL: CHANGELOG.md was not changed in this PR." + echo "❌ FAIL: CHANGELOG.md was not changed in this PR." + echo "" + echo "Please add an entry to CHANGELOG.md under the [Unreleased] section." exit 1 fi @@ -83,15 +85,84 @@ jobs: # If no patch is returned (very large file or API omission), treat presence as sufficient if [[ -z "${PATCH_CONTENT}" ]]; then - echo "CHANGELOG.md modified (no patch provided by API). Passing." + echo "✅ PASS: CHANGELOG.md modified (no patch provided by API)." exit 0 fi - # Look for actual line changes (exclude diff headers +++/---) - if echo "${PATCH_CONTENT}" | awk '{print $0}' | grep -E '^[+-]' | grep -vE '^\+\+\+|^\-\-\-' >/dev/null; then - echo "PASS: CHANGELOG.md contains line-level changes." + # Extract only the added lines from the patch (lines starting with +, excluding +++ header) + ADDED_LINES="$(echo "${PATCH_CONTENT}" | grep -E '^\+' | grep -v '^\+\+\+' || true)" + + # Check if there are any actual additions + if [[ -z "${ADDED_LINES}" ]]; then + echo "❌ FAIL: No line-level additions detected in CHANGELOG.md." + echo "" + echo "Please add an entry to CHANGELOG.md under the [Unreleased] section." + exit 1 + fi + + # Check if changes are under [Unreleased] + # Parse the patch to track which section we're in and validate additions + IN_UNRELEASED=false + IN_RELEASED_VERSION=false + FOUND_VALID_ADDITION=false + RELEASED_VERSION_FOUND="" + + while IFS= read -r line; do + # Check for [Unreleased] header (context line or added line) + if [[ "$line" =~ \[Unreleased\] ]]; then + IN_UNRELEASED=true + IN_RELEASED_VERSION=false + RELEASED_VERSION_FOUND="" + fi + + # Check for released version header pattern: ## [X.Y.Z] - DATE + # Match both context and added lines + if [[ "$line" =~ \[([0-9]+\.[0-9]+\.[0-9]+)\].*-.*[0-9]{4} ]]; then + # We found a released version header + RELEASED_VERSION_FOUND="${BASH_REMATCH[1]}" + IN_UNRELEASED=false + IN_RELEASED_VERSION=true + fi + + # Check for added lines (starting with +, not +++) + if [[ "$line" =~ ^\+[^+] ]]; then + # Extract the actual content (remove the leading +) + ADDED_CONTENT="${line:1}" + + # Skip empty lines and section headers + if [[ -n "$ADDED_CONTENT" && ! "$ADDED_CONTENT" =~ ^###\ ]]; then + if [[ "$IN_UNRELEASED" == true ]]; then + FOUND_VALID_ADDITION=true + elif [[ "$IN_RELEASED_VERSION" == true && -n "$RELEASED_VERSION_FOUND" ]]; then + # Found an addition under a released version - this is wrong + echo "❌ FAIL: Changelog entry found under a released version, not under [Unreleased]." + echo "" + echo "You added content under version [$RELEASED_VERSION_FOUND] instead of [Unreleased]:" + echo " $ADDED_CONTENT" + echo "" + echo "📝 Please move your changes to the [Unreleased] section at the top of CHANGELOG.md" + echo "" + echo "Example structure:" + echo " ## [Unreleased]" + echo " ### Added" + echo " - Your new feature or fix here" + exit 1 + fi + fi + fi + done <<< "$PATCH_CONTENT" + + if [[ "$FOUND_VALID_ADDITION" == true ]]; then + echo "✅ PASS: CHANGELOG.md contains valid additions under [Unreleased]." exit 0 else - echo "FAIL: No line-level changes detected in CHANGELOG.md." + echo "❌ FAIL: No valid changelog entries found under [Unreleased]." + echo "" + echo "Please ensure your changes are added under the [Unreleased] section of CHANGELOG.md" + echo "" + echo "Example:" + echo " ## [Unreleased]" + echo " ### Added" + echo " - Your feature/fix description" exit 1 fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 6668e7cf0..41c5fdc2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,11 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ## [Unreleased] ### Added + - Add example demonstrating usage of `CustomFeeLimit` in `examples/transaction/custom_fee_limit.py` - Added `.github/workflows/merge-conflict-bot.yml` to automatically detect and notify users of merge conflicts in Pull Requests. - +- Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new chnagelog entries are added under [Unreleased] + ### Changed - Removed duplicate import of transaction_pb2 in transaction.py From ca729cbc1306c771f8587c40a7889b6da2d28d3d Mon Sep 17 00:00:00 2001 From: prajeeta pal Date: Thu, 4 Dec 2025 13:01:44 +0530 Subject: [PATCH 2/4] fix: hbar transfer Signed-off-by: prajeeta pal --- CHANGELOG.md | 4 ++ .../abstract_token_transfer_transaction.py | 2 +- .../transfer_transaction_e2e_test.py | 45 +++++++++++++++++++ tests/unit/test_transfer_transaction.py | 6 +-- 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32fc574dc..8cfe21992 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - fixed workflow: changelog check with improved sensitivity to deletions, additions, new releases +### Breaking Changes + +- Changed error message in `TransferTransaction._add_hbar_transfer()` and `AbstractTokenTransferTransaction._add_token_transfer()` when amount is zero from "Amount must be a non-zero integer" to "Amount must be a non-zero value." for clarity and consistency. + ## [0.1.9] - 2025-11-26 ### Added diff --git a/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py b/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py index cd3910217..790d3a0bd 100644 --- a/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py +++ b/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py @@ -149,7 +149,7 @@ def _add_token_transfer( if not isinstance(account_id, AccountId): raise TypeError("account_id must be an AccountId instance.") if not isinstance(amount, int) or amount == 0: - raise ValueError("Amount must be a non-zero integer.") + raise ValueError("Amount must be a non-zero value.") if expected_decimals is not None and not isinstance(expected_decimals, int): raise TypeError("expected_decimals must be an integer.") if not isinstance(is_approved, bool): diff --git a/tests/integration/transfer_transaction_e2e_test.py b/tests/integration/transfer_transaction_e2e_test.py index 1e037031b..2aa284251 100644 --- a/tests/integration/transfer_transaction_e2e_test.py +++ b/tests/integration/transfer_transaction_e2e_test.py @@ -7,6 +7,7 @@ from hiero_sdk_python.crypto.private_key import PrivateKey from hiero_sdk_python.exceptions import PrecheckError from hiero_sdk_python.hbar import Hbar +from hiero_sdk_python.hbar_unit import HbarUnit from hiero_sdk_python.query.account_balance_query import CryptoGetAccountBalanceQuery from hiero_sdk_python.response_code import ResponseCode from hiero_sdk_python.tokens.nft_id import NftId @@ -448,3 +449,47 @@ def test_integration_transfer_transaction_approved_token_transfer(): finally: env.close() + + +@pytest.mark.integration +def test_integration_transfer_transaction_with_hbar_units(): + env = IntegrationTestEnv() + + try: + new_account_private_key = PrivateKey.generate() + new_account_public_key = new_account_private_key.public_key() + + initial_balance = Hbar(1) + + account_transaction = AccountCreateTransaction( + key=new_account_public_key, initial_balance=initial_balance, memo="Recipient Account" + ) + + receipt = account_transaction.execute(env.client) + + assert ( + receipt.status == ResponseCode.SUCCESS + ), f"Account creation failed with status: {ResponseCode(receipt.status).name}" + + account_id = receipt.account_id + assert account_id is not None + + transfer_transaction = TransferTransaction() + transfer_transaction.add_hbar_transfer(env.operator_id, Hbar(-1.5, HbarUnit.HBAR)) + transfer_transaction.add_hbar_transfer(account_id, Hbar(1.5, HbarUnit.HBAR)) + + receipt = transfer_transaction.execute(env.client) + + assert ( + receipt.status == ResponseCode.SUCCESS + ), f"Transfer failed with status: {ResponseCode(receipt.status).name}" + + query_transaction = CryptoGetAccountBalanceQuery(account_id) + balance = query_transaction.execute(env.client) + + expected_balance_tinybars = Hbar(1).to_tinybars() + Hbar(1.5, HbarUnit.HBAR).to_tinybars() + assert ( + balance and balance.hbars.to_tinybars() == expected_balance_tinybars + ), f"Expected balance: {expected_balance_tinybars}, actual balance: {balance.hbars.to_tinybars()}" + finally: + env.close() diff --git a/tests/unit/test_transfer_transaction.py b/tests/unit/test_transfer_transaction.py index cdf45556a..aa4db98e9 100644 --- a/tests/unit/test_transfer_transaction.py +++ b/tests/unit/test_transfer_transaction.py @@ -286,11 +286,11 @@ def test_zero_amount_validation(mock_account_ids): transfer_tx = TransferTransaction() # Test zero HBAR amount should raise ValueError - with pytest.raises(ValueError, match="Amount must be a non-zero integer"): + with pytest.raises(ValueError, match="Amount must be a non-zero value"): transfer_tx.add_hbar_transfer(account_id_1, 0) # Test zero token amount should raise ValueError - with pytest.raises(ValueError, match="Amount must be a non-zero integer"): + with pytest.raises(ValueError, match="Amount must be a non-zero value"): transfer_tx.add_token_transfer(token_id_1, account_id_1, 0) @@ -647,7 +647,7 @@ def test_hbar_transfer_conversion_accuracy(mock_account_ids): transfer_tx = TransferTransaction() hbar_value = Hbar(1.5, HbarUnit.HBAR) - expected_tinybars = 1_500_000_000 + expected_tinybars = 150_000_000 transfer_tx.add_hbar_transfer(account_id_1, hbar_value) From 58333dbd0e6cd83befe7c7af314ea433d2976422 Mon Sep 17 00:00:00 2001 From: prajeeta pal Date: Thu, 4 Dec 2025 23:33:30 +0530 Subject: [PATCH 3/4] fix: hbar support Signed-off-by: prajeeta pal --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cfe21992..4b0ef0d16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - Added `.github/workflows/merge-conflict-bot.yml` to automatically detect and notify users of merge conflicts in Pull Requests. - Added `.github/workflows/bot-office-hours.yml` to automate the Weekly Office Hour Reminder. - feat: Implement account creation with EVM-style alias transaction example. -- Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new chnagelog entries are added under [Unreleased] +- Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new changelog entries are added under [Unreleased] - feat: Allow `add_hbar_transfer`, `add_approved_hbar_transfer`, and internal `_add_hbar_transfer` to accept `Hbar` objects in addition to raw tinybar integers, with internal normalization to tinybars. Added tests validating the new behavior. ### Changed From ed3a26f58d9a0066901ac754ca9347d3c1617e6e Mon Sep 17 00:00:00 2001 From: prajeeta <96904203+prajeeta15@users.noreply.github.com> Date: Fri, 5 Dec 2025 15:18:08 +0530 Subject: [PATCH 4/4] Delete .github/workflows/pr-checks.yml Signed-off-by: prajeeta <96904203+prajeeta15@users.noreply.github.com> --- .github/workflows/pr-checks.yml | 168 -------------------------------- 1 file changed, 168 deletions(-) delete mode 100644 .github/workflows/pr-checks.yml diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml deleted file mode 100644 index 515b20c33..000000000 --- a/.github/workflows/pr-checks.yml +++ /dev/null @@ -1,168 +0,0 @@ -name: 'PR Formatting' -on: - workflow_dispatch: - pull_request_target: - types: - - opened - - reopened - - edited - - synchronize - -defaults: - run: - shell: bash - -permissions: - contents: read - checks: write - statuses: write - -concurrency: - group: pr-checks-${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - title-check: - name: Title Check - runs-on: ubuntu-latest - if: ${{ !github.event.pull_request.base.repo.fork }} - permissions: - checks: write - statuses: write - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 - with: - egress-policy: audit - - - name: Check PR Title - uses: step-security/conventional-pr-title-action@cb1c5657ccf4c42f5c0a6c0708cb8251b960d902 # v3.2.5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - changelog-check: - name: Changelog Check - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 - with: - egress-policy: audit - - - name: Validate CHANGELOG.md has changes - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -euo pipefail - - if [[ "${{ github.event_name }}" != "pull_request_target" && "${{ github.event_name }}" != "pull_request" ]]; then - echo "Not a pull request event. Skipping." - exit 0 - fi - - PR_NUMBER="${{ github.event.pull_request.number }}" - if [[ -z "${PR_NUMBER}" ]]; then - echo "Unable to determine PR number." - exit 1 - fi - - echo "Checking files changed in PR #${PR_NUMBER}..." - - # List files in the PR and check for CHANGELOG.md (root or any path ending with /CHANGELOG.md) - FILES_JSON="$(gh api repos/${{ github.repository }}/pulls/${PR_NUMBER}/files --paginate)" - if ! echo "${FILES_JSON}" | jq -e '.[] | select(.filename | test("(^|/)CHANGELOG\\.md$"; "i"))' >/dev/null; then - echo "❌ FAIL: CHANGELOG.md was not changed in this PR." - echo "" - echo "Please add an entry to CHANGELOG.md under the [Unreleased] section." - exit 1 - fi - - # Ensure there is at least one line-level change (+ or -) in the patch for CHANGELOG.md - PATCH_CONTENT="$(echo "${FILES_JSON}" \ - | jq -r '.[] | select(.filename | test("(^|/)CHANGELOG\\.md$"; "i")) | .patch // empty')" - - # If no patch is returned (very large file or API omission), treat presence as sufficient - if [[ -z "${PATCH_CONTENT}" ]]; then - echo "✅ PASS: CHANGELOG.md modified (no patch provided by API)." - exit 0 - fi - - # Extract only the added lines from the patch (lines starting with +, excluding +++ header) - ADDED_LINES="$(echo "${PATCH_CONTENT}" | grep -E '^\+' | grep -v '^\+\+\+' || true)" - - # Check if there are any actual additions - if [[ -z "${ADDED_LINES}" ]]; then - echo "❌ FAIL: No line-level additions detected in CHANGELOG.md." - echo "" - echo "Please add an entry to CHANGELOG.md under the [Unreleased] section." - exit 1 - fi - - # Check if changes are under [Unreleased] - # Parse the patch to track which section we're in and validate additions - IN_UNRELEASED=false - IN_RELEASED_VERSION=false - FOUND_VALID_ADDITION=false - RELEASED_VERSION_FOUND="" - - while IFS= read -r line; do - # Check for [Unreleased] header (context line or added line) - if [[ "$line" =~ \[Unreleased\] ]]; then - IN_UNRELEASED=true - IN_RELEASED_VERSION=false - RELEASED_VERSION_FOUND="" - fi - - # Check for released version header pattern: ## [X.Y.Z] - DATE - # Match both context and added lines - if [[ "$line" =~ \[([0-9]+\.[0-9]+\.[0-9]+)\].*-.*[0-9]{4} ]]; then - # We found a released version header - RELEASED_VERSION_FOUND="${BASH_REMATCH[1]}" - IN_UNRELEASED=false - IN_RELEASED_VERSION=true - fi - - # Check for added lines (starting with +, not +++) - if [[ "$line" =~ ^\+[^+] ]]; then - # Extract the actual content (remove the leading +) - ADDED_CONTENT="${line:1}" - - # Skip empty lines and section headers - if [[ -n "$ADDED_CONTENT" && ! "$ADDED_CONTENT" =~ ^###\ ]]; then - if [[ "$IN_UNRELEASED" == true ]]; then - FOUND_VALID_ADDITION=true - elif [[ "$IN_RELEASED_VERSION" == true && -n "$RELEASED_VERSION_FOUND" ]]; then - # Found an addition under a released version - this is wrong - echo "❌ FAIL: Changelog entry found under a released version, not under [Unreleased]." - echo "" - echo "You added content under version [$RELEASED_VERSION_FOUND] instead of [Unreleased]:" - echo " $ADDED_CONTENT" - echo "" - echo "📝 Please move your changes to the [Unreleased] section at the top of CHANGELOG.md" - echo "" - echo "Example structure:" - echo " ## [Unreleased]" - echo " ### Added" - echo " - Your new feature or fix here" - exit 1 - fi - fi - fi - done <<< "$PATCH_CONTENT" - - if [[ "$FOUND_VALID_ADDITION" == true ]]; then - echo "✅ PASS: CHANGELOG.md contains valid additions under [Unreleased]." - exit 0 - else - echo "❌ FAIL: No valid changelog entries found under [Unreleased]." - echo "" - echo "Please ensure your changes are added under the [Unreleased] section of CHANGELOG.md" - echo "" - echo "Example:" - echo " ## [Unreleased]" - echo " ### Added" - echo " - Your feature/fix description" - exit 1 - fi