Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f003a26
chore: update SDK dependency to @hiero-ledger/sdk (#1375)
themariofrancia Jan 19, 2026
03efcf4
SC tests executed in hardhat local network and solidity-coverage plug…
mamoralesiob Feb 2, 2026
5844330
feat: [BBND-1305] Add Docusaurus to SCS and improve documentation (#1…
rubenbermejo3 Feb 9, 2026
c10cdf0
Increase contract coverage to 100% (#1389)
mamoralesiob Feb 16, 2026
57d9745
chore: update doc and remove blog posts (#1391)
themariofrancia Feb 16, 2026
bf4b076
Pin third party node dependencies (#1392)
mamoralesiob Feb 16, 2026
9613f1d
feat: Implementation of HWC 2.0 (#1398)
jaime-iobermudez Feb 20, 2026
2e2762a
Executing coverage in hardhat and tests in hiero ledger solo (#1400)
mamoralesiob Feb 24, 2026
bf86f8d
Token expiration time not updateable through the web (#1402)
mamoralesiob Feb 24, 2026
73bd640
fix: metamask chainid error (#1405)
jaime-iobermudez Feb 25, 2026
cf1cff3
feat: [BBND-1379] Accessing transaction id for stablecoin operations …
ruben-martinez-iob Feb 25, 2026
b8ccfcc
chore: upgrade version
jaime-iobermudez Feb 27, 2026
9e7e4f3
fix: remove multisig metamask (#1417)
jaime-iobermudez Mar 3, 2026
e475c39
Granting the unlimited cash in role through the web is fixed (#1410)
mamoralesiob Mar 3, 2026
000134f
Merge remote-tracking branch 'origin/main' into develop
themariofrancia Mar 3, 2026
db23ebb
docs: remove outdated SDK documentation
themariofrancia Mar 3, 2026
8915eee
Merge remote-tracking branch 'origin/develop' into develop
themariofrancia Mar 3, 2026
c2bc563
chore: update workflows and .gitignore
themariofrancia Mar 3, 2026
60dd363
Adapt monorepo scaffolding to ATS's (#1420)
mamoralesiob Mar 5, 2026
acf8861
feat: [BBND-1376] Support external wallets in the SDK (#1415)
rubenbermejo3 Mar 10, 2026
699ecdb
Fix critical and high vulnerabilities (#1427)
mamoralesiob Mar 13, 2026
c01263d
fix: [BBND-1394] multisig transaction decode description (#1425)
ruben-martinez-iob Mar 16, 2026
1ef8e3f
Change scripts to create multisig account and to associate a token to…
mamoralesiob Mar 17, 2026
170f2b6
docs(config): update references path and exclusions (#1431)
themariofrancia Mar 17, 2026
04572b5
docs: update SDK usage and wallet docs
themariofrancia Mar 17, 2026
19ae009
chore: bump version to 4.2.0
themariofrancia Mar 17, 2026
00e2e0d
docs(config): update references path and exclusions (#1431)
themariofrancia Mar 17, 2026
1d02923
docs: update SDK usage and wallet docs
themariofrancia Mar 17, 2026
34d15c4
chore: bump version to 4.2.0
themariofrancia Mar 17, 2026
a0c7257
Merge remote-tracking branch 'origin/develop' into develop
themariofrancia Mar 17, 2026
9f4b86a
merge: integrate main into develop
themariofrancia Mar 18, 2026
d6f28a3
docs: adjust references paths and sidebar
themariofrancia Mar 18, 2026
7be427a
fix: align React versions across workspaces (#1437)
PepeSainzIob Mar 18, 2026
bfbcbee
feat: add local custom networks multisig (#1435)
jaime-iobermudez Mar 20, 2026
e4b809a
New github action changeset workflow
mamoralesiob Mar 19, 2026
a1d2223
chore: release to V4.3.0
mamoralesiob Apr 5, 2026
4353ca9
Merge remote-tracking branch 'origin/main' into chore/v4.3.0
mamoralesiob Apr 6, 2026
cee0964
Update package-lock.json
mamoralesiob Apr 6, 2026
ec65173
Update apps/web/package.json
mamoralesiob Apr 6, 2026
e132fd7
refactor: adding validation that API responses are valid JSON before …
mamoralesiob Apr 13, 2026
91638db
fix: try to find out why fetching api responses is failing
mamoralesiob Apr 13, 2026
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
8 changes: 8 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)

We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
19 changes: 19 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [
[
"@hashgraph/stablecoin-npm-contracts",
"@hashgraph/stablecoin-npm-backend",
"@hashgraph/stablecoin-npm-sdk",
"@hashgraph/stablecoin-npm-cli",
"@hashgraph/stablecoin-dapp"
]
],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ Please describe the tests that you ran to verify your changes. Provide instructi
- **Effective Tests** (I have added tests that prove my fix is effective or that my feature works) ✔️
- **Local Test Pass** (New and existing unit tests pass locally with my changes) ✅
- **Dependency Updates** (Any dependent changes have been merged and published in downstream modules) 🔄
- **Changeset** (I have added a changeset file via `npm run changeset`, or applied a bypass label if not applicable: `no-changeset`, `docs-only`, `chore`, `hotfix`) 📦
- **Spellcheck** (I have checked my code and corrected any misspellings) 📝
57 changes: 41 additions & 16 deletions .github/actions/create-env-file/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,19 @@ runs:
API_RESPONSE_1=$(curl -s -H "Authorization: Bearer ${{ inputs.api-access-token-1 }}" -H "User-Agent: AppName/1.2.3" https://portal.hedera.com/api/account)
API_RESPONSE_2=$(curl -s -H "Authorization: Bearer ${{ inputs.api-access-token-2 }}" -H "User-Agent: AppName/1.2.3" https://portal.hedera.com/api/account)

# Validate API responses are valid JSON before parsing
if ! echo "$API_RESPONSE_1" | jq . > /dev/null 2>&1; then
echo "Error: API call 1 did not return valid JSON. Check that api-access-token-1 is set and valid."
exit 1
fi
if ! echo "$API_RESPONSE_2" | jq . > /dev/null 2>&1; then
echo "Error: API call 2 did not return valid JSON. Check that api-access-token-2 is set and valid."
exit 1
fi

# Extract private keys in DER
TESTNET_PRIVATE_KEY_0_DER=$(echo $API_RESPONSE_1 | jq -r '.accounts[1].privateKey')
TESTNET_PRIVATE_KEY_1_DER=$(echo $API_RESPONSE_2 | jq -r '.accounts[1].privateKey')
TESTNET_PRIVATE_KEY_0_DER=$(echo "$API_RESPONSE_1" | jq -r '.accounts[1].privateKey')
TESTNET_PRIVATE_KEY_1_DER=$(echo "$API_RESPONSE_2" | jq -r '.accounts[1].privateKey')

# Remove DER header and add 0x prefix
TESTNET_PRIVATE_KEY_0="0x$(echo $TESTNET_PRIVATE_KEY_0_DER | tail -c 65)" # Strip first 26 bytes (52 hex characters)
Expand All @@ -41,24 +51,39 @@ runs:
shell: sh
run: |
# Making API call to fetch secrets
API_RESPONSE_1=$(curl -s -H "Authorization: Bearer ${{ inputs.api-access-token-1 }}" -H "User-Agent: AppName/1.2.3" https://portal.hedera.com/api/account)
API_RESPONSE_2=$(curl -s -H "Authorization: Bearer ${{ inputs.api-access-token-2 }}" -H "User-Agent: AppName/1.2.3" https://portal.hedera.com/api/account)
API_RESPONSE_1=$(curl -s -w "\n%{http_code}" -H "Authorization: Bearer ${{ inputs.api-access-token-1 }}" -H "User-Agent: AppName/1.2.3" https://portal.hedera.com/api/account)
HTTP_STATUS_1=$(echo "$API_RESPONSE_1" | tail -n1)
API_RESPONSE_1=$(echo "$API_RESPONSE_1" | sed '$d')

API_RESPONSE_2=$(curl -s -w "\n%{http_code}" -H "Authorization: Bearer ${{ inputs.api-access-token-2 }}" -H "User-Agent: AppName/1.2.3" https://portal.hedera.com/api/account)
HTTP_STATUS_2=$(echo "$API_RESPONSE_2" | tail -n1)
API_RESPONSE_2=$(echo "$API_RESPONSE_2" | sed '$d')

# Validate API responses are valid JSON before parsing
if ! echo "$API_RESPONSE_1" | jq . > /dev/null 2>&1; then
echo "Error: API call 1 failed (HTTP $HTTP_STATUS_1). Check that api-access-token-1 is set and valid."
exit 1
fi
if ! echo "$API_RESPONSE_2" | jq . > /dev/null 2>&1; then
echo "Error: API call 2 failed (HTTP $HTTP_STATUS_2). Check that api-access-token-2 is set and valid."
exit 1
fi

# Extract account details from API_RESPONSE_1
CLIENT_PRIVATE_KEY_ECDSA=$(echo $API_RESPONSE_1 | jq -r '.accounts[1].privateKey')
CLIENT_PUBLIC_KEY_ECDSA=$(echo $API_RESPONSE_1 | jq -r '.accounts[1].publicKey')
CLIENT_ACCOUNT_ID_ECDSA=$(echo $API_RESPONSE_1 | jq -r '.accounts[1].accountNum')
CLIENT_PRIVATE_KEY_ED25519=$(echo $API_RESPONSE_1 | jq -r '.accounts[0].privateKey')
CLIENT_PUBLIC_KEY_ED25519=$(echo $API_RESPONSE_1 | jq -r '.accounts[0].publicKey')
CLIENT_ACCOUNT_ID_ED25519=$(echo $API_RESPONSE_1 | jq -r '.accounts[0].accountNum')
CLIENT_PRIVATE_KEY_ECDSA=$(echo "$API_RESPONSE_1" | jq -r '.accounts[1].privateKey')
CLIENT_PUBLIC_KEY_ECDSA=$(echo "$API_RESPONSE_1" | jq -r '.accounts[1].publicKey')
CLIENT_ACCOUNT_ID_ECDSA=$(echo "$API_RESPONSE_1" | jq -r '.accounts[1].accountNum')
CLIENT_PRIVATE_KEY_ED25519=$(echo "$API_RESPONSE_1" | jq -r '.accounts[0].privateKey')
CLIENT_PUBLIC_KEY_ED25519=$(echo "$API_RESPONSE_1" | jq -r '.accounts[0].publicKey')
CLIENT_ACCOUNT_ID_ED25519=$(echo "$API_RESPONSE_1" | jq -r '.accounts[0].accountNum')

# Extract account details from API_RESPONSE_2
CLIENT_PRIVATE_KEY_ECDSA_2=$(echo $API_RESPONSE_2 | jq -r '.accounts[1].privateKey')
CLIENT_PUBLIC_KEY_ECDSA_2=$(echo $API_RESPONSE_2 | jq -r '.accounts[1].publicKey')
CLIENT_ACCOUNT_ID_ECDSA_2=$(echo $API_RESPONSE_2 | jq -r '.accounts[1].accountNum')
CLIENT_PRIVATE_KEY_ED25519_2=$(echo $API_RESPONSE_2 | jq -r '.accounts[0].privateKey')
CLIENT_PUBLIC_KEY_ED25519_2=$(echo $API_RESPONSE_2 | jq -r '.accounts[0].publicKey')
CLIENT_ACCOUNT_ID_ED25519_2=$(echo $API_RESPONSE_2 | jq -r '.accounts[0].accountNum')
CLIENT_PRIVATE_KEY_ECDSA_2=$(echo "$API_RESPONSE_2" | jq -r '.accounts[1].privateKey')
CLIENT_PUBLIC_KEY_ECDSA_2=$(echo "$API_RESPONSE_2" | jq -r '.accounts[1].publicKey')
CLIENT_ACCOUNT_ID_ECDSA_2=$(echo "$API_RESPONSE_2" | jq -r '.accounts[1].accountNum')
CLIENT_PRIVATE_KEY_ED25519_2=$(echo "$API_RESPONSE_2" | jq -r '.accounts[0].privateKey')
CLIENT_PUBLIC_KEY_ED25519_2=$(echo "$API_RESPONSE_2" | jq -r '.accounts[0].publicKey')
CLIENT_ACCOUNT_ID_ED25519_2=$(echo "$API_RESPONSE_2" | jq -r '.accounts[0].accountNum')

# Fetch EVM addresses from Mirror Node API
CLIENT_EVM_ADDRESS_ECDSA=$(curl -s https://testnet.mirrornode.hedera.com/api/v1/accounts/0.0.$CLIENT_ACCOUNT_ID_ECDSA | jq -r '.evm_address')
Expand Down
124 changes: 124 additions & 0 deletions .github/workflows/changeset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
name: "000: [FLOW] Changeset Check"

on:
pull_request:
branches:
- develop
types:
- opened
- synchronize
- reopened
- labeled # checks for bypass labels (no-changeset, docs-only, hotfix, chore)
- unlabeled

defaults:
run:
shell: bash

permissions:
contents: read
pull-requests: read

jobs:
check-changeset:
name: Validate Changeset Required
runs-on: token-studio-linux-medium
timeout-minutes: 5

steps:
- name: Harden Runner
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit

- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
# Ensure we have the head branch for comparison
ref: ${{ github.head_ref }}

- name: Fetch base branch
run: |
echo "Base branch: ${{ github.base_ref }}"
git fetch origin ${{ github.base_ref }}:${{ github.base_ref }}
git branch -a

- name: Setup NodeJS Environment
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22.20.0

- name: Check for bypass labels
id: bypass
env:
GH_TOKEN: ${{ github.token }}
run: |
LABELS=$(gh pr view ${{ github.event.number }} --json labels --jq '.labels[].name' || echo "")
if echo "${LABELS}" | grep -E "(no-changeset|docs-only|hotfix|chore)" > /dev/null; then
echo "bypass=true" >> "${GITHUB_OUTPUT}"
echo "✅ Found bypass label. Skipping changeset check."
else
echo "bypass=false" >> "${GITHUB_OUTPUT}"
echo "🔍 No bypass labels found. Changeset check required."
fi

- name: Install dependencies
if: ${{ steps.bypass.outputs.bypass == 'false' }}
run: npm ci

- name: Check changeset status
if: ${{ steps.bypass.outputs.bypass == 'false' }}
env:
BASE_BRANCH: ${{ github.base_ref }}
run: |
echo "🔍 Checking for NEW changesets in this PR..."
echo "Comparing HEAD against base branch: ${BASE_BRANCH}"

NEW_CHANGESETS=$(git diff "${BASE_BRANCH}...HEAD" --name-only --diff-filter=A | grep "^\.changeset/.*\.md$" | grep -v "README.md" || true)
NEW_CHANGESET_COUNT=$(echo "${NEW_CHANGESETS}" | grep -c "\.md$" || true)

echo "Files changed in PR:"
git diff "${BASE_BRANCH}...HEAD" --name-only --diff-filter=A | head -10
echo ""
echo "NEW changeset files in this PR: ${NEW_CHANGESET_COUNT}"
if [[ -n "${NEW_CHANGESETS}" ]]; then
echo "Found NEW changesets:"
echo "${NEW_CHANGESETS}"
fi

if [[ "${NEW_CHANGESET_COUNT}" -gt 0 ]]; then
echo "✅ Changeset validation passed - found NEW changeset files in PR"
else
echo ""
echo "❌ NEW CHANGESET REQUIRED"
echo ""
echo "This PR requires a NEW changeset to document the changes."
echo "We found no new .changeset/*.md files introduced by this PR."
echo ""
echo "To create a changeset:"
echo "1. Run: npm run changeset"
echo "2. Select the packages that changed"
echo "3. Choose the change type (patch/minor/major)"
echo "4. Write a description of your changes"
echo "5. Commit the generated .changeset/*.md file"
echo ""
echo "To bypass this check (for docs/chore changes only):"
echo "Add one of these labels to the PR:"
echo "- no-changeset: For pure documentation or config changes"
echo "- docs-only: For documentation-only changes"
echo "- chore: For build system or dependency updates"
echo "- hotfix: For emergency fixes"
echo ""
echo "More info: https://github.com/changesets/changesets/blob/main/docs/intro-to-using-changesets.md"
exit 1
fi

- name: Success summary
if: ${{ always() }}
run: |
if [[ "${{ steps.bypass.outputs.bypass }}" == "true" ]]; then
echo "✅ Changeset check bypassed due to label"
else
echo "✅ Changeset validation completed successfully"
fi
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,21 @@ npm run prettier # Format all modules
npm run prettier:check # Check formatting without changes
```

### Changesets

PRs targeting the `develop` branch must include a changeset file documenting the change. To create one:

```bash
npm run changeset # Interactive prompt: select packages and bump type
```

This generates a `.changeset/*.md` file that must be committed with your changes. If your PR does not require a changeset (documentation, chores, hotfixes), add one of the following labels to bypass the check: `no-changeset`, `docs-only`, `chore`, `hotfix`.

## Continuous Integration

The project uses separate GitHub Actions workflows for each module:

- **Changeset Check** (`.github/workflows/changeset.yaml`): Enforces that PRs to `develop` include a changeset file; can be bypassed with labels (`no-changeset`, `docs-only`, `hotfix`, `chore`)
- **Contracts Tests** (`.github/workflows/test-contracts.yaml`): Runs when contract files change
- **SDK Tests** (`.github/workflows/test-sdk.yaml`): Runs when SDK files change
- **Backend Tests** (`.github/workflows/test-backend.yaml`): Runs when backend files change
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hashgraph/stablecoin-npm-backend",
"version": "4.2.0",
"version": "4.3.0",
"description": "",
"author": "",
"license": "Apache-2.0",
Expand All @@ -19,7 +19,7 @@
"test:cov": "npx jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "npx jest --config ./test/jest-e2e.json",
"test:ci": "npx jest --ci --runInBand",
"test:ci": "npx jest --ci --runInBand --forceExit",
"clear-cache": "npx jest --clearCache",
"clean:modules": "rimraf node_modules",
"deleteAllTransactions": "ts-node ./src/scripts/deleteAllDBTransactions.ts",
Expand Down
5 changes: 3 additions & 2 deletions apps/backend/src/jobs/autoSubmit.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ import TransactionService from '../transaction/transaction.service';
import { TransactionStatus } from '../transaction/status.enum';
import {
Transaction,
Client,
PublicKey,
TransactionResponse,
TransactionReceipt,
Status,
Client,
} from '@hiero-ledger/sdk';
import { buildHederaClient } from '../utils/clientFactory';
import { GetTransactionsResponseDto } from '../transaction/dto/get-transactions-response.dto';
import { hexToUint8Array } from '../utils/utils';
import { LoggerService } from '../logger/logger.service.js';
Expand Down Expand Up @@ -142,7 +143,7 @@ export default class AutoSubmitService {

async submit(transaction: GetTransactionsResponseDto): Promise<boolean> {
try {
const client: Client = Client.forName(transaction.network);
const client = buildHederaClient(transaction.network, transaction.consensus_nodes);

let deserializedTransaction = Transaction.fromBytes(
hexToUint8Array(transaction.transaction_message),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,28 @@ import {
IsIn,
IsInt,
IsNotEmpty,
IsOptional,
IsString,
Matches,
Min,
ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';
import { hederaIdRegex, hexRegex } from '../../common/regexp';
import { RemoveHexPrefix } from '../../common/decorators/transform-hexPrefix.decorator';
import { Network } from '../network.enum';
import { Transform } from 'class-transformer';

export class ConsensusNodeDto {
@IsString()
@IsNotEmpty()
url: string;

@IsString()
@IsNotEmpty()
nodeId: string;
}

export class CreateTransactionRequestDto {
@ApiProperty({
description: 'The message to be signed by the keys',
Expand Down Expand Up @@ -112,6 +125,17 @@ export class CreateTransactionRequestDto {
)
network: Network;

@ApiProperty({
description:
'Consensus nodes for custom networks (required when network is "custom")',
required: false,
})
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => ConsensusNodeDto)
consensus_nodes?: ConsensusNodeDto[];

@ApiProperty({
description: 'The start date of the transaction in ISO 8601 format',
example: '2023-08-01T12:00:00Z',
Expand All @@ -129,6 +153,7 @@ export class CreateTransactionRequestDto {
threshold: number,
network: Network,
start_date: string,
consensus_nodes?: ConsensusNodeDto[],
) {
this.transaction_message = transaction_message;
this.description = description;
Expand All @@ -137,5 +162,6 @@ export class CreateTransactionRequestDto {
this.threshold = threshold;
this.network = network;
this.start_date = start_date;
this.consensus_nodes = consensus_nodes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class GetTransactionsResponseDto {
network: string;
hedera_account_id: string;
start_date: string;
consensus_nodes: { url: string; nodeId: string }[] | null;

constructor(
id: string,
Expand All @@ -43,6 +44,7 @@ export class GetTransactionsResponseDto {
network: string,
hedera_account_id: string,
start_date: string,
consensus_nodes: { url: string; nodeId: string }[] | null,
) {
this.id = id;
this.transaction_message = transaction_message;
Expand All @@ -55,5 +57,6 @@ export class GetTransactionsResponseDto {
this.network = network;
this.hedera_account_id = hedera_account_id;
this.start_date = start_date;
this.consensus_nodes = consensus_nodes;
}
}
1 change: 1 addition & 0 deletions apps/backend/src/transaction/network.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum Network {
MAINNET = 'mainnet',
TESTNET = 'testnet',
PREVIEWNET = 'previewnet',
CUSTOM = 'custom',
}
Loading
Loading