diff --git a/.github/actions/create-env-file/action.yaml b/.github/actions/create-env-file/action.yaml index 61d8029e2..77c87300a 100644 --- a/.github/actions/create-env-file/action.yaml +++ b/.github/actions/create-env-file/action.yaml @@ -34,7 +34,7 @@ runs: echo "TESTNET_PRIVATE_KEY_0=$TESTNET_PRIVATE_KEY_0" >> $GITHUB_ENV echo "TESTNET_PRIVATE_KEY_1=$TESTNET_PRIVATE_KEY_1" >> $GITHUB_ENV - working-directory: contracts + working-directory: packages/contracts - name: Create .env file for SDK if: ${{ inputs.module == 'sdk' }} @@ -98,4 +98,4 @@ runs: FACTORY_ADDRESS=0.0.7353542 RESOLVER_ADDRESS=0.0.7353500 EOF - working-directory: sdk + working-directory: packages/sdk diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 893ccfbcc..0fa87ba8b 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -57,7 +57,7 @@ jobs: - name: Install Contracts run: npm ci - working-directory: contracts + working-directory: packages/contracts # * Build contracts - name: Build Contracts diff --git a/apps/cli/src/app/service/stablecoin/HoldStableCoinService.ts b/apps/cli/src/app/service/stablecoin/HoldStableCoinService.ts index aea062124..aec19979b 100644 --- a/apps/cli/src/app/service/stablecoin/HoldStableCoinService.ts +++ b/apps/cli/src/app/service/stablecoin/HoldStableCoinService.ts @@ -47,7 +47,9 @@ export default class HoldStableCoinService extends Service { public async createHold(req: CreateHoldRequest): Promise { let holdId: number; await utilsService.showSpinner( - StableCoin.createHold(req).then((response) => (holdId = response.holdId)), + StableCoin.createHold(req).then( + (response) => (holdId = 'holdId' in response ? response.holdId : 0), + ), { text: language.getText('state.loading'), successText: language.getText('state.loadCompleted') + '\n', @@ -68,7 +70,7 @@ export default class HoldStableCoinService extends Service { let holdId: number; await utilsService.showSpinner( StableCoin.createHoldByController(req).then( - (response) => (holdId = response.holdId), + (response) => (holdId = 'holdId' in response ? response.holdId : 0), ), { text: language.getText('state.loading'), diff --git a/apps/web/src/services/SDKService.ts b/apps/web/src/services/SDKService.ts index 7a5e3a5fd..63f102590 100644 --- a/apps/web/src/services/SDKService.ts +++ b/apps/web/src/services/SDKService.ts @@ -37,6 +37,7 @@ import type { ReserveViewModel, ResetSupplierAllowanceRequest, RevokeMultiRolesRequest, + SerializedTransactionData, StableCoinCapabilities, StableCoinListViewModel, StableCoinViewModel, @@ -477,7 +478,11 @@ export class SDKService { public static async createStableCoin( createRequest: CreateRequest, - ): Promise<{ coin: StableCoinViewModel; reserve: ReserveViewModel } | null> { + ): Promise< + | { coin: StableCoinViewModel; reserve: ReserveViewModel } + | SerializedTransactionData + | null + > { return await StableCoin.create(createRequest); } diff --git a/package-lock.json b/package-lock.json index 8bcbb9022..f3ffd3733 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14470,17 +14470,6 @@ } } }, - "node_modules/@hiero-ledger/cryptography/node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "csstype": "^3.2.2" - } - }, "node_modules/@hiero-ledger/cryptography/node_modules/bignumber.js": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", @@ -14761,17 +14750,6 @@ } } }, - "node_modules/@hiero-ledger/sdk/node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "csstype": "^3.2.2" - } - }, "node_modules/@hiero-ledger/sdk/node_modules/bignumber.js": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", diff --git a/packages/sdk/__tests__/port/in/CustomFees.test.ts b/packages/sdk/__tests__/port/in/CustomFees.test.ts index 61ee9abbe..2493a6cac 100644 --- a/packages/sdk/__tests__/port/in/CustomFees.test.ts +++ b/packages/sdk/__tests__/port/in/CustomFees.test.ts @@ -53,6 +53,7 @@ import { MirrorNode } from '../../../src/domain/context/network/MirrorNode.js'; import { JsonRpcRelay } from '../../../src/domain/context/network/JsonRpcRelay.js'; import { CommandBus } from '../../../src/core/command/CommandBus.js'; import { ConnectCommand } from '../../../src/app/usecase/command/network/connect/ConnectCommand.js'; +import { TransactionResult } from '../../../src/domain/context/transaction/TransactionResult.js'; const mirrorNode: MirrorNode = { name: MIRROR_NODE.name, @@ -103,7 +104,8 @@ describe('πŸ§ͺ [ADAPTER] ClientTransactionAdapter with ECDSA accounts', () => { decimals: stableCoinCapabilitiesHTS.coin.decimals, amount: amount.toString(), }); - const result = await Fees.addFixedFee(fixedFee); + const result = await Fees.addFixedFee(fixedFee) as TransactionResult; + expect(result).toBeTruthy(); expect(result.success).toBeTruthy(); expect(result.transactionId).toBeTruthy(); @@ -137,7 +139,7 @@ describe('πŸ§ͺ [ADAPTER] ClientTransactionAdapter with ECDSA accounts', () => { net: net, }); - const result = await Fees.addFractionalFee(FractionalFee); + const result = await Fees.addFractionalFee(FractionalFee) as TransactionResult; expect(result).toBeTruthy(); expect(result.success).toBeTruthy(); expect(result.transactionId).toBeTruthy(); @@ -172,7 +174,7 @@ describe('πŸ§ͺ [ADAPTER] ClientTransactionAdapter with ECDSA accounts', () => { net: net, }); - const result = await Fees.addFractionalFee(FractionalFee); + const result = await Fees.addFractionalFee(FractionalFee) as TransactionResult; expect(result).toBeTruthy(); expect(result.success).toBeTruthy(); expect(result.transactionId).toBeTruthy(); @@ -241,7 +243,7 @@ describe('πŸ§ͺ [ADAPTER] ClientTransactionAdapter with ECDSA accounts', () => { tokenId: stableCoinCapabilitiesHTS.coin.tokenId!.toString(), }); - const result = await Fees.updateCustomFees(newFees); + const result = await Fees.updateCustomFees(newFees) as TransactionResult; expect(result).toBeTruthy(); expect(result.success).toBeTruthy(); expect(result.transactionId).toBeTruthy(); diff --git a/packages/sdk/__tests__/port/in/Management.test.ts b/packages/sdk/__tests__/port/in/Management.test.ts index dc3ef0206..40fe6030b 100644 --- a/packages/sdk/__tests__/port/in/Management.test.ts +++ b/packages/sdk/__tests__/port/in/Management.test.ts @@ -46,6 +46,7 @@ import { JsonRpcRelay } from '../../../src/domain/context/network/JsonRpcRelay.j import { CommandBus } from '../../../src/core/command/CommandBus.js'; import { ConnectCommand } from '../../../src/app/usecase/command/network/connect/ConnectCommand.js'; import ConfigInfoViewModel from '../../../src/port/in/response/ConfigInfoViewModel.js'; +import { TransactionResult } from '../../../src/domain/context/transaction/TransactionResult.js'; const mirrorNode: MirrorNode = { name: MIRROR_NODE.name, @@ -95,7 +96,7 @@ describe('πŸ§ͺ Management test', () => { tokenId, resolver, }); - const res = await Management.updateResolver(request); + const res = await Management.updateResolver(request) as TransactionResult; const configInfo = await Management.getConfigInfo( new GetConfigInfoRequest({ tokenId, @@ -113,7 +114,7 @@ describe('πŸ§ͺ Management test', () => { configVersion: newConfigVersion, tokenId, }); - const res = await Management.updateConfigVersion(request); + const res = await Management.updateConfigVersion(request) as TransactionResult; const configInfo = await Management.getConfigInfo( new GetConfigInfoRequest({ tokenId, @@ -134,7 +135,7 @@ describe('πŸ§ͺ Management test', () => { configVersion: configVersion, tokenId, }); - const res = await Management.updateConfig(request); + const res = await Management.updateConfig(request) as TransactionResult; const configInfo = await Management.getConfigInfo( new GetConfigInfoRequest({ tokenId, diff --git a/packages/sdk/__tests__/port/in/Reserve.test.ts b/packages/sdk/__tests__/port/in/Reserve.test.ts index a95a64b1c..406043907 100644 --- a/packages/sdk/__tests__/port/in/Reserve.test.ts +++ b/packages/sdk/__tests__/port/in/Reserve.test.ts @@ -40,6 +40,7 @@ import { RESOLVER_ADDRESS, } from '../../config.js'; import Injectable from '../../../src/core/Injectable.js'; +import { TransactionResult } from '../../../src/domain/context/transaction/TransactionResult.js'; describe('πŸ§ͺ Reserve test', () => { const stableCoinSC = { @@ -95,7 +96,7 @@ describe('πŸ§ͺ Reserve test', () => { reserveAddress: reserveAddress, reserveAmount: '0', }), - ); + ) as TransactionResult; expect(result).toBeTruthy(); expect(result.transactionId).toBeTruthy(); diff --git a/packages/sdk/__tests__/port/in/Roles.test.ts b/packages/sdk/__tests__/port/in/Roles.test.ts index 2b0199213..61f91e47f 100644 --- a/packages/sdk/__tests__/port/in/Roles.test.ts +++ b/packages/sdk/__tests__/port/in/Roles.test.ts @@ -55,6 +55,7 @@ import GetAccountsWithRolesRequest from '../../../src/port/in/request/GetAccount import { MirrorNode } from '../../../src/domain/context/network/MirrorNode.js'; import { JsonRpcRelay } from '../../../src/domain/context/network/JsonRpcRelay.js'; import Injectable from '../../../src/core/Injectable.js'; +import { TransactionResult } from '../../../src/domain/context/transaction/TransactionResult.js'; describe('πŸ§ͺ Role test', () => { const stableCoinSC = { @@ -113,7 +114,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.WIPE_ROLE, }), - ); + ) as TransactionResult; const hasRole = await Role.hasRole( new HasRoleRequest({ @@ -128,7 +129,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.WIPE_ROLE, }), - ); + ) as TransactionResult; const noRoleAgain = await Role.hasRole( new HasRoleRequest({ @@ -169,7 +170,7 @@ describe('πŸ§ͺ Role test', () => { roles: [StableCoinRole.WIPE_ROLE, StableCoinRole.CASHIN_ROLE], amounts: ['0'], }), - ); + ) as TransactionResult; const hasRole_1 = await Role.hasRole( new HasRoleRequest({ @@ -197,7 +198,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', roles: [StableCoinRole.WIPE_ROLE, StableCoinRole.CASHIN_ROLE], }), - ); + ) as TransactionResult; const noRole_again_1 = await Role.hasRole( new HasRoleRequest({ targetId: CLIENT_ACCOUNT_ED25519.id.toString(), @@ -240,7 +241,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; const hasRole = await Role.hasRole( new HasRoleRequest({ targetId: CLIENT_ACCOUNT_ED25519.id.toString(), @@ -266,7 +267,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; expect(revokeRes).toBeTruthy(); expect(revokeRes.transactionId).toBeTruthy(); @@ -298,7 +299,7 @@ describe('πŸ§ͺ Role test', () => { supplierType: 'limited', amount: AMOUNT, }), - ); + ) as TransactionResult; const hasRole = await Role.hasRole( new HasRoleRequest({ @@ -325,7 +326,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; expect(revokeRes).toBeTruthy(); expect(revokeRes.transactionId).toBeTruthy(); @@ -346,7 +347,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; const hasRole = await Role.hasRole( new HasRoleRequest({ @@ -361,7 +362,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; const noRole = await Role.hasRole( new HasRoleRequest({ @@ -408,7 +409,7 @@ describe('πŸ§ͺ Role test', () => { supplierType: 'limited', amount: '10', }), - ); + ) as TransactionResult; const allowanceBefore = await Role.getAllowance( new GetSupplierAllowanceRequest({ @@ -421,7 +422,7 @@ describe('πŸ§ͺ Role test', () => { targetId: CLIENT_ACCOUNT_ED25519.id.toString(), tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; const allowanceAfter = await Role.getAllowance( new GetSupplierAllowanceRequest({ @@ -435,7 +436,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; expect(revokeRes).toBeTruthy(); expect(revokeRes.transactionId).toBeTruthy(); @@ -461,7 +462,7 @@ describe('πŸ§ͺ Role test', () => { supplierType: 'limited', amount: '10', }), - ); + ) as TransactionResult; const allowanceBefore = await Role.getAllowance( new GetSupplierAllowanceRequest({ @@ -475,7 +476,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', amount: '10', }), - ); + ) as TransactionResult; const allowanceAfter = await Role.getAllowance( new GetSupplierAllowanceRequest({ @@ -489,7 +490,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; expect(revokeRes).toBeTruthy(); expect(revokeRes.transactionId).toBeTruthy(); @@ -514,7 +515,7 @@ describe('πŸ§ͺ Role test', () => { supplierType: 'limited', amount: '10', }), - ); + ) as TransactionResult; const allowanceBefore = await Role.getAllowance( new GetSupplierAllowanceRequest({ @@ -528,7 +529,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', amount: '5', }), - ); + ) as TransactionResult; const allowanceAfter = await Role.getAllowance( new GetSupplierAllowanceRequest({ @@ -542,7 +543,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; expect(revokeRes).toBeTruthy(); expect(revokeRes.success).toBeTruthy(); @@ -570,7 +571,7 @@ describe('πŸ§ͺ Role test', () => { supplierType: 'limited', amount: '10', }), - ); + ) as TransactionResult; const isLimited = await Role.isLimited( new CheckSupplierLimitRequest({ @@ -590,7 +591,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; expect(revokeRes).toBeTruthy(); expect(revokeRes.success).toBeTruthy(); @@ -617,7 +618,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; const isLimited = await Role.isLimited( new CheckSupplierLimitRequest({ @@ -637,7 +638,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', role: StableCoinRole.CASHIN_ROLE, }), - ); + ) as TransactionResult; expect(revokeRes).toBeTruthy(); expect(revokeRes.success).toBeTruthy(); @@ -684,7 +685,7 @@ describe('πŸ§ͺ Role test', () => { tokenId: stableCoinSC?.tokenId?.toString() ?? '0.0.0', roles: [StableCoinRole.PAUSE_ROLE], }), - ); + ) as TransactionResult; expect(revokeRes).toBeTruthy(); expect(revokeRes.success).toBeTruthy(); diff --git a/packages/sdk/__tests__/port/in/StableCoin.test.ts b/packages/sdk/__tests__/port/in/StableCoin.test.ts index 5b4f5145a..787b5ae6e 100644 --- a/packages/sdk/__tests__/port/in/StableCoin.test.ts +++ b/packages/sdk/__tests__/port/in/StableCoin.test.ts @@ -27,7 +27,6 @@ import { Account, Balance, BigDecimal, - CreateHoldTransactionResult, HBAR_DECIMALS, HederaId, LoggerTransports, @@ -658,7 +657,7 @@ describe('πŸ§ͺ Stablecoin test', () => { sourceId, targetId, }), - ); + ) as { holdId: number } & TransactionResult; const holderBalance = await StableCoin.getBalanceOf( new GetAccountBalanceRequest({ @@ -726,7 +725,7 @@ describe('πŸ§ͺ Stablecoin test', () => { amount, sourceId: CLIENT_ACCOUNT_ED25519.id.toString(), }), - ); + ) as TransactionResult; const targetBalance = await StableCoin.getBalanceOf( new GetAccountBalanceRequest({ @@ -780,7 +779,7 @@ describe('πŸ§ͺ Stablecoin test', () => { amount, sourceId: CLIENT_ACCOUNT_ED25519.id.toString(), }), - ); + ) as TransactionResult; const holderBalance = await StableCoin.getBalanceOf( new GetAccountBalanceRequest({ @@ -835,7 +834,7 @@ describe('πŸ§ͺ Stablecoin test', () => { tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', sourceId: CLIENT_ACCOUNT_ED25519.id.toString(), }), - ); + ) as TransactionResult; const holderBalance = await StableCoin.getBalanceOf( new GetAccountBalanceRequest({ @@ -885,7 +884,7 @@ describe('πŸ§ͺ Stablecoin test', () => { amount: burnAmount.toString(), tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; const finalAmount = await StableCoin.getBalanceOf( new GetAccountBalanceRequest({ @@ -924,7 +923,7 @@ describe('πŸ§ͺ Stablecoin test', () => { tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', targetId: CLIENT_ACCOUNT_ED25519.id.toString(), }), - ); + ) as TransactionResult; const finalAmount = await StableCoin.getBalanceOf( new GetAccountBalanceRequest({ @@ -962,7 +961,7 @@ describe('πŸ§ͺ Stablecoin test', () => { amount: rescueAmount.toString(), tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; const finalAmount = await StableCoin.getBalanceOf( new GetAccountBalanceRequest({ @@ -998,7 +997,7 @@ describe('πŸ§ͺ Stablecoin test', () => { amount: rescueAmount.toString(), tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; const finalAmount = await StableCoin.getBalanceOfHBAR( new GetAccountBalanceHBARRequest({ @@ -1034,7 +1033,7 @@ describe('πŸ§ͺ Stablecoin test', () => { tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', targetId: CLIENT_ACCOUNT_ED25519.id.toString(), }), - ); + ) as TransactionResult; const finalAmount = await StableCoin.getBalanceOf( new GetAccountBalanceRequest({ @@ -1086,7 +1085,7 @@ describe('πŸ§ͺ Stablecoin test', () => { targetId: CLIENT_ACCOUNT_ED25519.id.toString(), tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; const Frozen = await StableCoin.isAccountFrozen( new FreezeAccountRequest({ @@ -1100,7 +1099,7 @@ describe('πŸ§ͺ Stablecoin test', () => { targetId: CLIENT_ACCOUNT_ED25519.id.toString(), tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; const notFrozen_2 = await StableCoin.isAccountFrozen( new FreezeAccountRequest({ @@ -1135,7 +1134,7 @@ describe('πŸ§ͺ Stablecoin test', () => { targetId: CLIENT_ACCOUNT_ED25519.id.toString(), tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; const kycNOK = await StableCoin.isAccountKYCGranted( new KYCRequest({ @@ -1149,7 +1148,7 @@ describe('πŸ§ͺ Stablecoin test', () => { targetId: CLIENT_ACCOUNT_ED25519.id.toString(), tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; const kycOK_2 = await StableCoin.isAccountKYCGranted( new KYCRequest({ @@ -1176,13 +1175,13 @@ describe('πŸ§ͺ Stablecoin test', () => { new PauseRequest({ tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; const result_unpasue = await StableCoin.unPause( new PauseRequest({ tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; expect(result_pause).toBeTruthy(); expect(result_pause.success).toBeTruthy(); @@ -1211,7 +1210,7 @@ describe('πŸ§ͺ Stablecoin test', () => { tokenId: stableCoin?.tokenId?.toString() ?? '0.0.0', reserveAddress: newReserveAddress, }), - ); + ) as TransactionResult; expect(result).toBeTruthy(); expect(result.success).toBeTruthy(); expect(result.transactionId).toBeTruthy(); @@ -1263,7 +1262,7 @@ describe('πŸ§ͺ Stablecoin test', () => { feeScheduleKey: stableCoin.feeScheduleKey, metadata: metadata, }), - ); + ) as TransactionResult; expect(result).toBeTruthy(); expect(result.success).toBeTruthy(); @@ -1332,7 +1331,7 @@ describe('πŸ§ͺ Stablecoin test', () => { escrow: string, expirationDate: string, targetId: string, - ): Promise { + ): Promise<{ holdId: number } & TransactionResult> { await StableCoin.create(requestSC); await StableCoin.cashIn( new CashInRequest({ @@ -1349,7 +1348,7 @@ describe('πŸ§ͺ Stablecoin test', () => { expirationDate, targetId, }), - ); + ) as { holdId: number } & TransactionResult; } async function removeHold( diff --git a/packages/sdk/__tests__/port/out/AWSKMSTransactionAdapter.test.ts b/packages/sdk/__tests__/port/out/AWSKMSTransactionAdapter.test.ts index cf7139a93..9d2ad3149 100644 --- a/packages/sdk/__tests__/port/out/AWSKMSTransactionAdapter.test.ts +++ b/packages/sdk/__tests__/port/out/AWSKMSTransactionAdapter.test.ts @@ -49,6 +49,7 @@ import { import Injectable from '../../../src/core/Injectable'; import { Time } from '../../../src/core/Time'; import { CONFIG_SC, DEFAULT_VERSION } from '../../../src/core/Constants'; +import { TransactionResult } from '../../../src/domain/context/transaction/TransactionResult.js'; const initialSupply = 1000; const configId = CONFIG_SC; @@ -125,7 +126,8 @@ describe('πŸ§ͺ AWSKMSTransactionAdapter test', () => { configId: configId, configVersion: configVersion, }); - stableCoinHTS = (await StableCoin.create(requestCreateStableCoin)).coin; + const createResult = await StableCoin.create(requestCreateStableCoin); + stableCoinHTS = (createResult as { coin: StableCoinViewModel }).coin; await Time.delay(10, 'seconds'); }, 60_000); @@ -139,7 +141,7 @@ describe('πŸ§ͺ AWSKMSTransactionAdapter test', () => { targetId: AWS_KMS_SETTINGS.hederaAccountId, tokenId: stableCoinHTS?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; expect(result).toBeTruthy(); expect(result.success).toBeTruthy(); expect(result.transactionId).toBeTruthy(); diff --git a/packages/sdk/__tests__/port/out/DFNSTransactionAdapter.test.ts b/packages/sdk/__tests__/port/out/DFNSTransactionAdapter.test.ts index 7d005f173..33c0341fd 100644 --- a/packages/sdk/__tests__/port/out/DFNSTransactionAdapter.test.ts +++ b/packages/sdk/__tests__/port/out/DFNSTransactionAdapter.test.ts @@ -50,6 +50,7 @@ import { import Injectable from '../../../src/core/Injectable'; import { Time } from '../../../src/core/Time'; import { CONFIG_SC, DEFAULT_VERSION } from '../../../src/core/Constants'; +import { TransactionResult } from '../../../src/domain/context/transaction/TransactionResult.js'; const initialSupply = 1000; const configId = CONFIG_SC; @@ -131,7 +132,8 @@ describe('πŸ§ͺ DFNSTransactionAdapter test', () => { configVersion: configVersion, }); - stableCoinHTS = (await StableCoin.create(requestCreateStableCoin)).coin; + const createResult = await StableCoin.create(requestCreateStableCoin); + stableCoinHTS = (createResult as { coin: StableCoinViewModel }).coin; await Time.delay(5, 'seconds'); }, 60_000); @@ -146,7 +148,7 @@ describe('πŸ§ͺ DFNSTransactionAdapter test', () => { targetId: DFNS_SETTINGS.hederaAccountId, tokenId: stableCoinHTS?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; expect(result).toBeTruthy(); expect(result.success).toBeTruthy(); expect(result.transactionId).toBeTruthy(); diff --git a/packages/sdk/__tests__/port/out/FireblocksTransactionAdapter.test.ts b/packages/sdk/__tests__/port/out/FireblocksTransactionAdapter.test.ts index 094894723..b6a6566b9 100644 --- a/packages/sdk/__tests__/port/out/FireblocksTransactionAdapter.test.ts +++ b/packages/sdk/__tests__/port/out/FireblocksTransactionAdapter.test.ts @@ -50,6 +50,7 @@ import { import Injectable from '../../../src/core/Injectable'; import { Time } from '../../../src/core/Time'; import { CONFIG_SC, DEFAULT_VERSION } from '../../../src/core/Constants'; +import { TransactionResult } from '../../../src/domain/context/transaction/TransactionResult.js'; const initialSupply = 1000; const apiSecretKey = FIREBLOCKS_SETTINGS.apiSecretKeyPath; @@ -129,7 +130,8 @@ describe('πŸ§ͺ FireblocksTransactionAdapter test', () => { configVersion: configVersion, }); - stableCoinHTS = (await StableCoin.create(requesCreateStableCoin)).coin; + const createResult = await StableCoin.create(requesCreateStableCoin); + stableCoinHTS = (createResult as { coin: StableCoinViewModel }).coin; await Time.delay(5, 'seconds'); }, 80_000); @@ -143,7 +145,7 @@ describe('πŸ§ͺ FireblocksTransactionAdapter test', () => { targetId: FIREBLOCKS_SETTINGS.hederaAccountId, tokenId: stableCoinHTS?.tokenId?.toString() ?? '0.0.0', }), - ); + ) as TransactionResult; expect(result).toBeTruthy(); expect(result.success).toBeTruthy(); expect(result.transactionId).toBeTruthy(); diff --git a/packages/sdk/example/.env.sample b/packages/sdk/example/.env.sample index 0cb88c59c..e89869a1a 100644 --- a/packages/sdk/example/.env.sample +++ b/packages/sdk/example/.env.sample @@ -1,4 +1,9 @@ MY_ACCOUNT_ID='0.0.XXXX' MY_PRIVATE_KEY='3032...' +MY_PRIVATE_KEY_ECDSA='hex_ecdsa_key_without_0x' RESOLVER_ADDRESS='0.0.XXXX' -FACTORY_ADDRESS='0.0.XXXX' \ No newline at end of file +FACTORY_ADDRESS='0.0.XXXX' +# Optional: provide an existing token ID to skip creation in testExternalEVM +TOKEN_ID='' +# Optional: provide an existing token ID to skip creation in testExternalHedera +TOKEN_ID_HEDERA='' \ No newline at end of file diff --git a/packages/sdk/example/ts/burn.ts b/packages/sdk/example/ts/burn.ts index 68d325eed..19dda032b 100644 --- a/packages/sdk/example/ts/burn.ts +++ b/packages/sdk/example/ts/burn.ts @@ -114,7 +114,7 @@ const main = async () => { }); // Create the stablecoin and log the result - const stableCoin = await StableCoin.create(request); + const stableCoin = (await StableCoin.create(request)) as { coin: any; reserve: any }; console.log('StableCoin created:', stableCoin); // Associate the stablecoin with the account diff --git a/packages/sdk/example/ts/mint.ts b/packages/sdk/example/ts/mint.ts index 39537cef1..13155b170 100644 --- a/packages/sdk/example/ts/mint.ts +++ b/packages/sdk/example/ts/mint.ts @@ -115,7 +115,7 @@ const main = async () => { }); // Create the stablecoin and log the result - const stableCoin = await StableCoin.create(request); + const stableCoin = (await StableCoin.create(request)) as { coin: any; reserve: any }; console.log('StableCoin created:', stableCoin); // Associate the stablecoin with the account diff --git a/packages/sdk/example/ts/package.json b/packages/sdk/example/ts/package.json index b9a346369..d45434adb 100644 --- a/packages/sdk/example/ts/package.json +++ b/packages/sdk/example/ts/package.json @@ -13,6 +13,8 @@ "mint": "npm run build && node build/mint.js", "wipe": "npm run build && node build/wipe.js", "roles": "npm run build && node build/role.js", + "test-external-evm": "npm run build && node build/testExternalEVM.js", + "test-external-hedera": "npm run build && node build/testExternalHedera.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], diff --git a/packages/sdk/example/ts/role.ts b/packages/sdk/example/ts/role.ts index 701fbe2b3..b199cdf5b 100644 --- a/packages/sdk/example/ts/role.ts +++ b/packages/sdk/example/ts/role.ts @@ -115,7 +115,7 @@ const main = async () => { }); // Create the stablecoin and log the result - const stableCoin = await StableCoin.create(request); + const stableCoin = (await StableCoin.create(request)) as { coin: any; reserve: any }; console.log('StableCoin created:', stableCoin); // Revoke the Wipe role from the account diff --git a/packages/sdk/example/ts/testExternalCommon.ts b/packages/sdk/example/ts/testExternalCommon.ts new file mode 100644 index 000000000..1fad83d8f --- /dev/null +++ b/packages/sdk/example/ts/testExternalCommon.ts @@ -0,0 +1,930 @@ +/* + * + * Hedera Stablecoin SDK + * + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * Shared utilities, SDK setup helpers, and the full test suite for the + * External Wallet adapter integration tests (EVM and Hedera). + * + * Each adapter-specific entry point (testExternalEVM.ts / testExternalHedera.ts) + * implements the `ExternalSigner` interface and calls `runTestSuite()`. + */ + +import { + Network, + InitializationRequest, + CreateRequest, + CashInRequest, + BurnRequest, + WipeRequest, + FreezeAccountRequest, + PauseRequest, + DeleteRequest, + KYCRequest, + RescueRequest, + RescueHBARRequest, + UpdateRequest, + GrantRoleRequest, + RevokeRoleRequest, + GrantMultiRolesRequest, + RevokeMultiRolesRequest, + IncreaseSupplierAllowanceRequest, + DecreaseSupplierAllowanceRequest, + ResetSupplierAllowanceRequest, + AddFixedFeeRequest, + AddFractionalFeeRequest, + UpdateCustomFeesRequest, + UpdateConfigRequest, + UpdateConfigVersionRequest, + UpdateResolverRequest, + UpdateReserveAddressRequest, + UpdateReserveAmountRequest, + CreateHoldRequest, + CreateHoldByControllerRequest, + ExecuteHoldRequest, + ReleaseHoldRequest, + ReclaimHoldRequest, + GetHoldsIdForRequest, + ConnectRequest, + SupportedWallets, + TokenSupplyType, + StableCoin, + Role, + Management, + Fees, + ReserveDataFeed, + AssociateTokenRequest, + StableCoinRole, +} from '@hashgraph/stablecoin-npm-sdk'; + +// Re-export so adapter-specific files can import from one place instead of +// directly depending on @hashgraph/stablecoin-npm-sdk (whose build may lag). +export { SupportedWallets }; + +require('dotenv').config({ path: __dirname + '/../../.env' }); + +// ─── Config ─────────────────────────────────────────────────────────────────── + +export const TESTNET_MIRROR_URL = + 'https://testnet.mirrornode.hedera.com/api/v1/'; +export const TESTNET_RPC_URL = 'https://testnet.hashio.io/api'; + +export const CONFIG_ID = + '0x0000000000000000000000000000000000000000000000000000000000000002'; +export const RESERVE_CONFIG_ID = + '0x0000000000000000000000000000000000000000000000000000000000000003'; + +export const mirrorNodeConfig = { + name: 'Testnet Mirror Node', + network: 'testnet', + baseUrl: TESTNET_MIRROR_URL, + apiKey: '', + headerName: '', + selected: true, +}; + +export const rpcNodeConfig = { + name: 'HashIO', + network: 'testnet', + baseUrl: TESTNET_RPC_URL, + apiKey: '', + headerName: '', + selected: true, +}; + +// ─── Helpers ────────────────────────────────────────────────────────────────── + +export function requireEnv(name: string): string { + const val = process.env[name]; + if (!val) throw new Error(`Missing required env var: ${name}`); + return val; +} + +export function detectKeyType(key: string): 'ED25519' | 'ECDSA' { + const hex = key.startsWith('0x') ? key.slice(2) : key; + return hex.length === 64 ? 'ECDSA' : 'ED25519'; +} + +export function waitMs(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +// ─── Test result tracking ───────────────────────────────────────────────────── + +export interface TestResult { + name: string; + status: 'PASS' | 'FAIL' | 'SKIP'; + /** txHash (EVM) or txId (Hedera) on PASS; error message on FAIL */ + detail?: string; +} + +export const testResults: TestResult[] = []; + +/** + * Interface each adapter-specific file must implement. + * `run()` invokes `fn()`, verifies it returns SerializedTransactionData, + * signs + submits the transaction, and records PASS or FAIL. + */ +export interface ExternalSigner { + /** The external wallet type – used to reconnect after CLIENT operations. */ + readonly wallet: SupportedWallets; + run(name: string, fn: () => Promise): Promise; +} + +// ─── Summary ───────────────────────────────────────────────────────────────── + +export function printSummary(title: string): void { + const pass = testResults.filter((r) => r.status === 'PASS').length; + const fail = testResults.filter((r) => r.status === 'FAIL').length; + const skip = testResults.filter((r) => r.status === 'SKIP').length; + + console.log('\n' + '═'.repeat(80)); + console.log(` ${title}`); + console.log('═'.repeat(80)); + console.log(` ${'TEST'.padEnd(45)} ${'STATUS'.padEnd(8)} DETAILS`); + console.log('─'.repeat(80)); + + for (const r of testResults) { + const icon = + r.status === 'PASS' ? 'βœ“' : r.status === 'SKIP' ? 'β—‹' : 'βœ—'; + const details = + r.status === 'PASS' + ? (r.detail ?? '').substring(0, 30) + '...' + : r.status === 'SKIP' + ? 'Skipped' + : (r.detail ?? '').substring(0, 30); + console.log( + ` ${icon} ${r.name.padEnd(43)} ${r.status.padEnd(8)} ${details}`, + ); + } + + console.log('─'.repeat(80)); + console.log( + ` Total: ${testResults.length} βœ“ PASS: ${pass} βœ— FAIL: ${fail} β—‹ SKIP: ${skip}`, + ); + console.log('═'.repeat(80)); +} + +// ─── SDK setup helpers ──────────────────────────────────────────────────────── + +export async function initSdk( + factoryAddress: string, + resolverAddress: string, +): Promise { + console.log('[0] Initializing SDK on testnet...'); + await Network.init( + new InitializationRequest({ + network: 'testnet', + mirrorNode: mirrorNodeConfig, + rpcNode: rpcNodeConfig, + configuration: { factoryAddress, resolverAddress }, + }), + ); +} + +export async function connectClient( + accountId: string, + privateKeyStr: string, +): Promise { + await Network.connect( + new ConnectRequest({ + account: { + accountId, + privateKey: { + key: privateKeyStr, + type: detectKeyType(privateKeyStr), + }, + }, + network: 'testnet', + mirrorNode: mirrorNodeConfig, + rpcNode: rpcNodeConfig, + wallet: SupportedWallets.CLIENT, + }), + ); +} + +export async function connectExternal( + accountId: string, + wallet: SupportedWallets, +): Promise { + await Network.connect( + new ConnectRequest({ + account: { accountId }, + network: 'testnet', + mirrorNode: mirrorNodeConfig, + rpcNode: rpcNodeConfig, + wallet, + }), + ); +} + +// ─── Token setup ────────────────────────────────────────────────────────────── + +export interface StablecoinSetup { + tokenId: string; + reserveAddress: string; + proxyAddress: string; +} + +export async function createStablecoin( + accountId: string, + name: string, + symbol: string, +): Promise { + console.log('\n[Setup] Creating stablecoin...'); + const createResult = (await StableCoin.create( + new CreateRequest({ + name, + symbol, + decimals: 2, + initialSupply: '1000', + freezeKey: { key: 'null', type: 'null' }, + kycKey: { key: 'null', type: 'null' }, + wipeKey: { key: 'null', type: 'null' }, + pauseKey: { key: 'null', type: 'null' }, + feeScheduleKey: { key: 'null', type: 'null' }, + supplyType: TokenSupplyType.INFINITE, + createReserve: true, + reserveInitialAmount: '10000', + reserveConfigId: RESERVE_CONFIG_ID, + reserveConfigVersion: 1, + updatedAtThreshold: '0', + grantKYCToOriginalSender: true, + burnRoleAccount: accountId, + wipeRoleAccount: accountId, + rescueRoleAccount: accountId, + pauseRoleAccount: accountId, + freezeRoleAccount: accountId, + deleteRoleAccount: accountId, + kycRoleAccount: accountId, + cashInRoleAccount: accountId, + feeRoleAccount: accountId, + cashInRoleAllowance: '0', + proxyOwnerAccount: accountId, + configId: CONFIG_ID, + configVersion: 1, + }), + )) as { coin: any; reserve: any }; + + const tokenId = + (createResult.coin as { tokenId?: string }).tokenId ?? ''; + const reserveAddress = + (createResult.reserve as { proxyAddress?: string }).proxyAddress ?? ''; + const proxyAddress = + (createResult.coin as { proxyAddress?: any }).proxyAddress?.toString() ?? + ''; + + if (!tokenId) throw new Error('Token creation failed – tokenId missing'); + console.log(` βœ“ Token created: ${tokenId}`); + if (reserveAddress) console.log(` βœ“ Reserve created: ${reserveAddress}`); + if (proxyAddress) console.log(` βœ“ Proxy address: ${proxyAddress}`); + return { tokenId, reserveAddress, proxyAddress }; +} + +export async function setupToken( + tokenId: string, + accountId: string, +): Promise { + console.log('\n[Setup] Associating token + granting KYC...'); + await StableCoin.associate( + new AssociateTokenRequest({ targetId: accountId, tokenId }), + ); + console.log(' βœ“ Associated'); + + await StableCoin.grantKyc(new KYCRequest({ targetId: accountId, tokenId })); + console.log(' βœ“ KYC granted (waiting 5s for mirror node indexing...)'); + await waitMs(5000); + + console.log('\n[Setup] Minting tokens for test account...'); + await StableCoin.cashIn( + new CashInRequest({ tokenId, targetId: accountId, amount: '100' }), + ); + console.log(' βœ“ 100 tokens minted to account'); + + console.log('\n[Setup] Granting HOLD_CREATOR_ROLE...'); + await Role.grantRole( + new GrantRoleRequest({ + tokenId, + targetId: accountId, + role: StableCoinRole.HOLD_CREATOR_ROLE, + }), + ); + console.log(' βœ“ HOLD_CREATOR_ROLE granted'); + + console.log('\n[Setup] Waiting 5s for mirror node indexing...'); + await waitMs(5000); +} + +// ─── Test context ───────────────────────────────────────────────────────────── + +export interface TestContext { + accountId: string; + privateKeyStr: string; + tokenId: string; + reserveAddress: string; + proxyAddress: string; + resolverAddress: string; +} + +// ─── Full test suite ────────────────────────────────────────────────────────── + +export async function runTestSuite( + signer: ExternalSigner, + ctx: TestContext, +): Promise { + const { tokenId, accountId, reserveAddress, proxyAddress, resolverAddress } = + ctx; + + // ── Category 1: Basic token operations ──────────────────────────────── + + await signer.run( + 'cashIn (mint 10 to account)', + () => + StableCoin.buildCashIn( + new CashInRequest({ tokenId, targetId: accountId, amount: '10' }), + ), + ); + + await signer.run( + 'burn (5 from treasury supply)', + () => StableCoin.buildBurn(new BurnRequest({ tokenId, amount: '5' })), + ); + + await signer.run( + 'wipe (3 from account balance)', + () => + StableCoin.buildWipe( + new WipeRequest({ tokenId, targetId: accountId, amount: '3' }), + ), + ); + + // ── Associate test ───────────────────────────────────────────────────── + // The main token is already associated. Create a temp token via CLIENT + // wallet so we can test the buildAssociate() flow on a fresh token. + console.log( + '\n[Associate Test] Creating temporary token for association test...', + ); + let tempTokenId = ''; + try { + await connectClient(accountId, ctx.privateKeyStr); + const tempCreateResult = (await StableCoin.create( + new CreateRequest({ + name: 'Temp Associate Test', + symbol: 'TEMPASSOC', + decimals: 2, + initialSupply: '10', + freezeKey: { key: 'null', type: 'null' }, + kycKey: { key: 'null', type: 'null' }, + wipeKey: { key: 'null', type: 'null' }, + pauseKey: { key: 'null', type: 'null' }, + feeScheduleKey: { key: 'null', type: 'null' }, + supplyType: TokenSupplyType.INFINITE, + createReserve: false, + updatedAtThreshold: '0', + grantKYCToOriginalSender: false, + proxyOwnerAccount: accountId, + configId: CONFIG_ID, + configVersion: 1, + }), + )) as { coin: any }; + tempTokenId = + ( + tempCreateResult.coin as { + tokenId?: { toString(): string } | string; + } + ).tokenId?.toString() ?? ''; + console.log(` βœ“ Temp token created: ${tempTokenId}`); + await waitMs(5000); + } catch (error: any) { + console.log( + ` βœ— Failed to create temp token: ${error?.message ?? error}`, + ); + } finally { + // Always reconnect to the external wallet regardless of whether the + // temp token creation succeeded or failed, so subsequent tests run + // with the correct adapter. + await connectExternal(accountId, signer.wallet); + } + + if (tempTokenId) { + await signer.run( + 'associate', + () => + StableCoin.buildAssociate( + new AssociateTokenRequest({ + targetId: accountId, + tokenId: tempTokenId, + }), + ), + ); + } else { + testResults.push({ name: 'associate', status: 'SKIP' }); + console.log('\n β—‹ associate β†’ SKIP (temp token creation failed)'); + } + + // transfers() is not implemented in any adapter + testResults.push({ + name: 'transfers (not implemented in adapters)', + status: 'SKIP', + }); + console.log('\n β—‹ transfers (not implemented in any adapter) β†’ SKIP'); + + // ── Category 2: Compliance ──────────────────────────────────────────── + + await signer.run( + 'freeze (freeze account)', + () => + StableCoin.buildFreeze( + new FreezeAccountRequest({ tokenId, targetId: accountId }), + ), + ); + + await signer.run( + 'unFreeze (unfreeze account)', + () => + StableCoin.buildUnFreeze( + new FreezeAccountRequest({ tokenId, targetId: accountId }), + ), + ); + + await signer.run( + 'revokeKyc (revoke KYC from account)', + () => + StableCoin.buildRevokeKyc(new KYCRequest({ tokenId, targetId: accountId })), + ); + + // Wait for mirror node to index revokeKyc before granting again. + // GrantKycCommandHandler checks KYC status on the mirror node. + console.log('\n ⏳ Waiting 5s for mirror node to index revokeKyc...'); + await waitMs(5000); + + await signer.run( + 'grantKyc (re-grant KYC to account)', + () => + StableCoin.buildGrantKyc(new KYCRequest({ tokenId, targetId: accountId })), + ); + + await signer.run( + 'pause (pause token)', + () => StableCoin.buildPause(new PauseRequest({ tokenId })), + ); + + await signer.run( + 'unPause (unpause token)', + () => StableCoin.buildUnPause(new PauseRequest({ tokenId })), + ); + + // ── Category 3: Token update ─────────────────────────────────────────── + + await signer.run( + 'update (rename token)', + () => + StableCoin.buildUpdate( + new UpdateRequest({ + tokenId, + name: 'External Updated', + symbol: 'EXTTEST2', + }), + ), + ); + + // ── Category 4: Roles ───────────────────────────────────────────────── + + await signer.run( + 'revokeRole (revoke BURN_ROLE)', + () => + Role.buildRevokeRole( + new RevokeRoleRequest({ + tokenId, + targetId: accountId, + role: StableCoinRole.BURN_ROLE, + }), + ), + ); + + await signer.run( + 'grantRole (grant BURN_ROLE)', + () => + Role.buildGrantRole( + new GrantRoleRequest({ + tokenId, + targetId: accountId, + role: StableCoinRole.BURN_ROLE, + }), + ), + ); + + await signer.run( + 'revokeMultiRoles (revoke FREEZE_ROLE)', + () => + Role.buildRevokeMultiRoles( + new RevokeMultiRolesRequest({ + tokenId, + targetsId: [accountId], + roles: [StableCoinRole.FREEZE_ROLE], + }), + ), + ); + + await signer.run( + 'grantMultiRoles (grant FREEZE_ROLE)', + () => + Role.buildGrantMultiRoles( + new GrantMultiRolesRequest({ + tokenId, + targetsId: [accountId], + roles: [StableCoinRole.FREEZE_ROLE], + }), + ), + ); + + // ── Category 5: Supplier allowance ──────────────────────────────────── + // Must revoke unlimited role first; contract rejects unlimitedβ†’limited directly. + + await signer.run( + 'revokeRole CASHIN_ROLE (prep: remove unlimited)', + () => + Role.buildRevokeRole( + new RevokeRoleRequest({ + tokenId, + targetId: accountId, + role: StableCoinRole.CASHIN_ROLE, + }), + ), + ); + + await signer.run( + 'grantRole CASHIN_ROLE limited (100)', + () => + Role.buildGrantRole( + new GrantRoleRequest({ + tokenId, + targetId: accountId, + role: StableCoinRole.CASHIN_ROLE, + supplierType: 'limited', + amount: '100', + }), + ), + ); + + await signer.run( + 'increaseAllowance (+50)', + () => + Role.buildIncreaseAllowance( + new IncreaseSupplierAllowanceRequest({ + tokenId, + targetId: accountId, + amount: '50', + }), + ), + ); + + await signer.run( + 'decreaseAllowance (-25)', + () => + Role.buildDecreaseAllowance( + new DecreaseSupplierAllowanceRequest({ + tokenId, + targetId: accountId, + amount: '25', + }), + ), + ); + + await signer.run( + 'resetAllowance', + () => + Role.buildResetAllowance( + new ResetSupplierAllowanceRequest({ tokenId, targetId: accountId }), + ), + ); + + await signer.run( + 'grantRole CASHIN_ROLE unlimited (restore)', + () => + Role.buildGrantRole( + new GrantRoleRequest({ + tokenId, + targetId: accountId, + role: StableCoinRole.CASHIN_ROLE, + supplierType: 'unlimited', + }), + ), + ); + + // ── Category 6: Custom fees ─────────────────────────────────────────── + + await signer.run( + 'addFixedFee (HBAR-denominated, 0.01 HBAR)', + // tokenIdCollected '0.0.0' β†’ HederaId.NULL β†’ isNull()=true β†’ HBAR fee + () => + Fees.buildAddFixedFee( + new AddFixedFeeRequest({ + tokenId, + collectorId: accountId, + collectorsExempt: true, + decimals: 2, + tokenIdCollected: '0.0.0', + amount: '1', + }), + ), + ); + + await signer.run( + 'addFractionalFee (1% fee)', + () => + Fees.buildAddFractionalFee( + new AddFractionalFeeRequest({ + tokenId, + collectorId: accountId, + collectorsExempt: true, + decimals: 2, + percentage: '1', + min: '0', + max: '10', + net: false, + }), + ), + ); + + await signer.run( + 'updateCustomFees (clear all fees)', + () => + Fees.buildUpdateCustomFees( + new UpdateCustomFeesRequest({ tokenId, customFees: [] }), + ), + ); + + // ── Category 7: Rescue ──────────────────────────────────────────────── + + await signer.run( + 'rescue (attempt rescue HTS tokens)', + () => StableCoin.buildRescue(new RescueRequest({ tokenId, amount: '1' })), + ); + + await signer.run( + 'rescueHBAR (rescue 0.1 HBAR from proxy)', + () => + proxyAddress + ? StableCoin.buildRescueHBAR( + new RescueHBARRequest({ tokenId, amount: '0.1' }), + ) + : Promise.reject( + new Error('No proxy address available for rescueHBAR'), + ), + ); + + // ── Category 8: Reserve operations ─────────────────────────────────── + // buildUpdateReserveAddress('0.0.0') clears the reserve without a mirror node query. + + await signer.run( + 'updateReserveAddress (set to 0.0.0)', + () => + StableCoin.buildUpdateReserveAddress( + new UpdateReserveAddressRequest({ + tokenId, + reserveAddress: '0.0.0', + }), + ), + ); + + await signer.run( + 'updateReserveAmount (set to 1000)', + () => + reserveAddress + ? ReserveDataFeed.buildUpdateReserveAmount( + new UpdateReserveAmountRequest({ + reserveAddress, + reserveAmount: '1000', + }), + ) + : Promise.reject( + new Error( + 'No reserve address available for updateReserveAmount', + ), + ), + ); + + // ── Category 9: Hold operations ─────────────────────────────────────── + + const expirationDate = Math.floor(Date.now() / 1000 + 3600).toString(); // 1h + + // createHold β†’ releaseHold + let holdId1 = -1; + await signer.run( + 'createHold (hold 5 tokens, escrow=self)', + () => + StableCoin.buildCreateHold( + new CreateHoldRequest({ + tokenId, + amount: '5', + escrow: accountId, + expirationDate, + targetId: accountId, + }), + ), + ); + await waitMs(4000); + try { + const ids = await StableCoin.getHoldsIdFor( + new GetHoldsIdForRequest({ + tokenId, + sourceId: accountId, + start: 0, + end: 100, + }), + ); + holdId1 = ids.length > 0 ? (ids[ids.length - 1] as number) : -1; + console.log(`\n ↳ holdId1=${holdId1}`); + } catch (_) { + /* ignore */ + } + + await signer.run( + `releaseHold (holdId=${holdId1})`, + () => + holdId1 >= 0 + ? StableCoin.buildReleaseHold( + new ReleaseHoldRequest({ + tokenId, + sourceId: accountId, + holdId: holdId1, + amount: '5', + }), + ) + : Promise.reject( + new Error('No holdId – createHold may not be indexed yet'), + ), + ); + + // createHold β†’ executeHold + let holdId2 = -1; + await signer.run( + 'createHold (hold 5 tokens for executeHold)', + () => + StableCoin.buildCreateHold( + new CreateHoldRequest({ + tokenId, + amount: '5', + escrow: accountId, + expirationDate, + targetId: accountId, + }), + ), + ); + await waitMs(4000); + try { + const ids = await StableCoin.getHoldsIdFor( + new GetHoldsIdForRequest({ + tokenId, + sourceId: accountId, + start: 0, + end: 100, + }), + ); + holdId2 = ids.length > 0 ? (ids[ids.length - 1] as number) : -1; + console.log(`\n ↳ holdId2=${holdId2}`); + } catch (_) { + /* ignore */ + } + + await signer.run( + `executeHold (holdId=${holdId2})`, + () => + holdId2 >= 0 + ? StableCoin.buildExecuteHold( + new ExecuteHoldRequest({ + tokenId, + sourceId: accountId, + holdId: holdId2, + amount: '5', + targetId: accountId, + }), + ) + : Promise.reject(new Error('No holdId available')), + ); + + // createHoldByController + await signer.run( + 'createHoldByController (controller creates hold)', + () => + StableCoin.buildCreateHoldByController( + new CreateHoldByControllerRequest({ + tokenId, + amount: '5', + escrow: accountId, + expirationDate, + sourceId: accountId, + targetId: accountId, + }), + ), + ); + + // reclaimHold (short expiration +10s) + const shortExpirationDate = Math.floor(Date.now() / 1000 + 10).toString(); + let reclaimHoldId = -1; + + await signer.run( + 'createHold (short expiration for reclaim test)', + () => + StableCoin.buildCreateHold( + new CreateHoldRequest({ + tokenId, + amount: '5', + escrow: accountId, + expirationDate: shortExpirationDate, + targetId: accountId, + }), + ), + ); + await waitMs(4000); + try { + const ids = await StableCoin.getHoldsIdFor( + new GetHoldsIdForRequest({ + tokenId, + sourceId: accountId, + start: 0, + end: 100, + }), + ); + reclaimHoldId = ids.length > 0 ? (ids[ids.length - 1] as number) : -1; + console.log(`\n ↳ reclaimHoldId=${reclaimHoldId}`); + } catch (_) { + /* ignore */ + } + + console.log(`\n ⏳ Waiting 15s for hold ${reclaimHoldId} to expire...`); + await waitMs(15000); + + await signer.run( + `reclaimHold (expired hold, holdId=${reclaimHoldId})`, + () => + reclaimHoldId >= 0 + ? StableCoin.buildReclaimHold( + new ReclaimHoldRequest({ + tokenId, + sourceId: accountId, + holdId: reclaimHoldId, + }), + ) + : Promise.reject(new Error('No holdId available for reclaim')), + ); + + // ── Category 10: Management ─────────────────────────────────────────── + + await signer.run( + 'updateConfig (same configId+version)', + () => + Management.buildUpdateConfig( + new UpdateConfigRequest({ + tokenId, + configId: CONFIG_ID, + configVersion: 1, + }), + ), + ); + + await signer.run( + 'updateConfigVersion (version=1)', + () => + Management.buildUpdateConfigVersion( + new UpdateConfigVersionRequest({ tokenId, configVersion: 1 }), + ), + ); + + await signer.run( + 'updateResolver (same resolver)', + () => + Management.buildUpdateResolver( + new UpdateResolverRequest({ + tokenId, + configId: CONFIG_ID, + configVersion: 1, + resolver: resolverAddress, + }), + ), + ); + + // ── Category 11: Dangerous – delete last ───────────────────────────── + + await signer.run( + 'delete (permanent – runs last)', + () => StableCoin.buildDelete(new DeleteRequest({ tokenId })), + ); +} diff --git a/packages/sdk/example/ts/testExternalEVM.ts b/packages/sdk/example/ts/testExternalEVM.ts new file mode 100644 index 000000000..bea7501db --- /dev/null +++ b/packages/sdk/example/ts/testExternalEVM.ts @@ -0,0 +1,286 @@ +/* + * + * Hedera Stablecoin SDK + * + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * ExternalEVMTransactionAdapter integration test. + * + * Implements EVMSigner (gets unsigned EVM bytes β†’ sign with ethers.Wallet β†’ + * broadcast β†’ verify receipt) and delegates the full test suite to runTestSuite(). + * + * Required env vars (sdk/.env): + * MY_ACCOUNT_ID – Hedera account ID (e.g. 0.0.7625517) + * MY_PRIVATE_KEY – ECDSA (0x-prefixed) or ED25519 hex private key + * MY_PRIVATE_KEY_ECDSA – ECDSA hex private key used for EVM signing + * FACTORY_ADDRESS – Hedera factory contract ID + * RESOLVER_ADDRESS – Hedera resolver contract ID + * Optional: + * TOKEN_ID – Reuse an existing token (skips creation) + */ + +import { ethers } from 'ethers'; +import { + requireEnv, + waitMs, + testResults, + ExternalSigner, + printSummary, + initSdk, + connectClient, + connectExternal, + createStablecoin, + setupToken, + runTestSuite, + TESTNET_RPC_URL, + SupportedWallets, +} from './testExternalCommon'; + +// ─── Suppress transient RPC 502 errors from ethers.js polling ──────────────── +process.on('unhandledRejection', (reason: unknown) => { + const msg = String(reason); + if ( + msg.includes('502') || + msg.includes('Bad Gateway') || + msg.includes('SERVER_ERROR') + ) { + console.warn('[suppressed transient RPC error]', msg.substring(0, 200)); + return; + } + console.error('Unhandled rejection:', msg.substring(0, 200)); +}); + +// ─── EVM-specific helpers ───────────────────────────────────────────────────── + +function toHexKey(key: string): string { + return key.startsWith('0x') ? key : '0x' + key; +} + +async function withRetry( + fn: () => Promise, + retries = 3, + delayMs = 5000, +): Promise { + for (let i = 0; i < retries; i++) { + try { + return await fn(); + } catch (e: any) { + const is502 = + e?.info?.responseStatus?.includes('502') || + e?.shortMessage?.includes('502') || + String(e).includes('502'); + if (is502 && i < retries - 1) { + await waitMs(delayMs); + continue; + } + throw e; + } + } + throw new Error('unreachable'); +} + +// ─── EVMSigner ──────────────────────────────────────────────────────────────── + +class EVMSigner implements ExternalSigner { + readonly wallet = SupportedWallets.EXTERNAL_EVM; + + constructor( + private readonly provider: ethers.JsonRpcProvider, + private readonly ecdsaPrivateKey: string, + ) {} + + async run(name: string, fn: () => Promise): Promise { + console.log(`\n β–Ά ${name}...`); + try { + const result = await fn(); + + if (!result || !('serializedTransaction' in result)) { + testResults.push({ + name, + status: 'FAIL', + detail: 'Did not return SerializedTransactionData', + }); + console.log(` βœ— FAIL: Did not return SerializedTransactionData`); + return; + } + + const data = result as { serializedTransaction: string }; + const ethWallet = new ethers.Wallet( + toHexKey(this.ecdsaPrivateKey), + this.provider, + ); + const unsignedTx = ethers.Transaction.from(data.serializedTransaction); + + unsignedTx.nonce = await withRetry(() => + this.provider.getTransactionCount(ethWallet.address), + ); + const feeData = await withRetry(() => this.provider.getFeeData()); + if (unsignedTx.type === 2) { + unsignedTx.maxFeePerGas = feeData.maxFeePerGas; + unsignedTx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas; + } else { + unsignedTx.gasPrice = feeData.gasPrice; + } + + const signedTx = await ethWallet.signTransaction(unsignedTx); + const txResponse = await withRetry(() => + this.provider.broadcastTransaction(signedTx), + ); + const receipt = await txResponse.wait(1, 60_000); + + if (!receipt) { + testResults.push({ + name, + status: 'FAIL', + detail: `Receipt is null txHash=${txResponse.hash}`, + }); + console.log( + ` βœ— FAIL Receipt is null txHash=${txResponse.hash}`, + ); + return; + } + + if (receipt.status !== 1) { + let revertReason = 'Transaction reverted'; + try { + const tx = await this.provider.getTransaction(txResponse.hash); + if (tx) await this.provider.call(tx); + } catch (e: any) { + if (e.reason) revertReason = e.reason; + else if (e.message) revertReason = e.message.substring(0, 100); + } + testResults.push({ + name, + status: 'FAIL', + detail: `status=${receipt.status} – ${revertReason}`, + }); + console.log( + ` βœ— FAIL status=${receipt.status} gas=${receipt.gasUsed} txHash=${txResponse.hash}`, + ); + console.log(` └─ Reason: ${revertReason}`); + return; + } + + testResults.push({ name, status: 'PASS', detail: txResponse.hash }); + console.log( + ` βœ“ PASS gas=${receipt.gasUsed} txHash=${txResponse.hash}`, + ); + } catch (error: any) { + const errMsg = (error?.message ?? String(error)).substring(0, 300); + testResults.push({ name, status: 'FAIL', detail: errMsg }); + console.log(` βœ— FAIL: ${errMsg.substring(0, 150)}`); + } + } +} + +// ─── Fund proxy contract with HBAR (EVM path) ──────────────────────────────── + +async function fundProxyWithHBAR( + proxyAddress: string, + provider: ethers.JsonRpcProvider, + ecdsaPrivateKey: string, + amountInHbar = '0.5', +): Promise { + console.log( + '\n[Setup] Funding proxy contract with HBAR for rescueHBAR test...', + ); + // Convert Hedera contract ID (0.0.X) to padded EVM address + const parts = proxyAddress.split('.'); + const evmAddress = '0x' + parseInt(parts[2]).toString(16).padStart(40, '0'); + console.log(` β†’ Proxy ${proxyAddress} β†’ EVM address ${evmAddress}`); + + const ethWallet = new ethers.Wallet(toHexKey(ecdsaPrivateKey), provider); + const tx = await ethWallet.sendTransaction({ + to: evmAddress, + value: ethers.parseEther(amountInHbar), + }); + const receipt = await tx.wait(); + console.log( + ` βœ“ Sent ${amountInHbar} HBAR to proxy txHash=${receipt?.hash}`, + ); +} + +// ─── Main ───────────────────────────────────────────────────────────────────── + +const main = async () => { + const accountId = requireEnv('MY_ACCOUNT_ID'); + const privateKey = requireEnv('MY_PRIVATE_KEY'); + const ecdsaPrivateKey = requireEnv('MY_PRIVATE_KEY_ECDSA'); + const factoryAddress = requireEnv('FACTORY_ADDRESS'); + const resolverAddress = requireEnv('RESOLVER_ADDRESS'); + + await initSdk(factoryAddress, resolverAddress); + + // ── Step 1: Connect CLIENT + create/setup token ──────────────────────── + let tokenId = process.env.TOKEN_ID ?? ''; + let reserveAddress = ''; + let proxyAddress = ''; + + if (!tokenId) { + await connectClient(accountId, privateKey); + console.log('[1] Connected with CLIENT wallet'); + const setup = await createStablecoin( + accountId, + 'ExternalEVM Test Token', + 'EVMTEST', + ); + tokenId = setup.tokenId; + reserveAddress = setup.reserveAddress; + proxyAddress = setup.proxyAddress; + await setupToken(tokenId, accountId); + + if (proxyAddress) { + const tempProvider = new ethers.JsonRpcProvider(TESTNET_RPC_URL); + await fundProxyWithHBAR(proxyAddress, tempProvider, ecdsaPrivateKey); + } + } else { + console.log(`\n[1] Using existing token: ${tokenId}`); + } + + // ── Step 2: Switch to EXTERNAL_EVM ──────────────────────────────────── + console.log('\n[2] Connecting with EXTERNAL_EVM wallet...'); + await connectExternal(accountId, SupportedWallets.EXTERNAL_EVM); + console.log(' βœ“ Connected (no private key held by SDK)'); + + // ── Step 3: Run the full test suite ─────────────────────────────────── + const provider = new ethers.JsonRpcProvider(TESTNET_RPC_URL); + const signer = new EVMSigner(provider, ecdsaPrivateKey); + const ethWallet = new ethers.Wallet(toHexKey(ecdsaPrivateKey), provider); + + console.log(`\n[3] Running tests as EVM address: ${ethWallet.address}`); + console.log(` Token: ${tokenId}`); + console.log(` Account: ${accountId}\n`); + + await runTestSuite(signer, { + accountId, + privateKeyStr: privateKey, + tokenId, + reserveAddress, + proxyAddress, + resolverAddress, + }); + + printSummary('EXTERNAL EVM TEST SUMMARY'); + process.exit(0); +}; + +main().catch((error) => { + console.error('\nβœ— Fatal error:', error); + printSummary('EXTERNAL EVM TEST SUMMARY'); + process.exit(1); +}); diff --git a/packages/sdk/example/ts/testExternalHedera.ts b/packages/sdk/example/ts/testExternalHedera.ts new file mode 100644 index 000000000..f85360f64 --- /dev/null +++ b/packages/sdk/example/ts/testExternalHedera.ts @@ -0,0 +1,220 @@ +/* + * + * Hedera Stablecoin SDK + * + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * ExternalHederaTransactionAdapter integration test. + * + * Implements HederaSigner (gets serialized Hedera tx bytes β†’ sign with + * Hedera SDK PrivateKey β†’ submit via Client β†’ verify receipt SUCCESS) + * and delegates the full test suite to runTestSuite(). + * + * Required env vars (sdk/.env): + * MY_ACCOUNT_ID – Hedera account ID (e.g. 0.0.7625517) + * MY_PRIVATE_KEY – ECDSA (0x-prefixed) or ED25519 hex private key + * FACTORY_ADDRESS – Hedera factory contract ID + * RESOLVER_ADDRESS – Hedera resolver contract ID + * Optional: + * TOKEN_ID_HEDERA – Reuse an existing token (skips creation) + */ + +import { + Transaction, + TransferTransaction, + PrivateKey, + Client, + AccountId, + Hbar, +} from '@hiero-ledger/sdk'; +import { + requireEnv, + detectKeyType, + testResults, + ExternalSigner, + printSummary, + initSdk, + connectClient, + connectExternal, + createStablecoin, + setupToken, + runTestSuite, + SupportedWallets, +} from './testExternalCommon'; + +// ─── HederaSigner ───────────────────────────────────────────────────────────── + +class HederaSigner implements ExternalSigner { + readonly wallet = SupportedWallets.EXTERNAL_HEDERA; + + constructor( + private readonly hederaClient: Client, + private readonly privateKey: PrivateKey, + ) {} + + async run(name: string, fn: () => Promise): Promise { + console.log(`\n β–Ά ${name}...`); + try { + const result = await fn(); + + if (!result || !('serializedTransaction' in result)) { + testResults.push({ + name, + status: 'FAIL', + detail: 'Did not return SerializedTransactionData', + }); + console.log(` βœ— FAIL: Did not return SerializedTransactionData`); + return; + } + + const data = result as { serializedTransaction: string }; + const bytes = Buffer.from(data.serializedTransaction, 'hex'); + const tx = Transaction.fromBytes(bytes); + const signedTx = await tx.sign(this.privateKey); + const txResponse = await signedTx.execute(this.hederaClient); + const receipt = await txResponse.getReceipt(this.hederaClient); + + const statusStr = receipt.status.toString(); + if (statusStr !== 'SUCCESS') { + testResults.push({ + name, + status: 'FAIL', + detail: `Receipt status: ${statusStr} txId=${txResponse.transactionId}`, + }); + console.log( + ` βœ— FAIL status=${statusStr} txId=${txResponse.transactionId}`, + ); + return; + } + + testResults.push({ + name, + status: 'PASS', + detail: txResponse.transactionId.toString(), + }); + console.log(` βœ“ PASS txId=${txResponse.transactionId}`); + } catch (error: any) { + const errMsg = (error?.message ?? String(error)).substring(0, 300); + testResults.push({ name, status: 'FAIL', detail: errMsg }); + console.log(` βœ— FAIL: ${errMsg.substring(0, 150)}`); + } + } +} + +// ─── Fund proxy contract with HBAR (Hedera native path) ────────────────────── + +async function fundProxyWithHBAR( + senderAccountId: string, + proxyAddress: string, + hederaClient: Client, + privateKey: PrivateKey, + amountHbar = 0.5, +): Promise { + console.log( + '\n[Setup] Funding proxy contract with HBAR for rescueHBAR test...', + ); + const transfer = new TransferTransaction() + .addHbarTransfer(AccountId.fromString(senderAccountId), new Hbar(-amountHbar)) + .addHbarTransfer(AccountId.fromString(proxyAddress), new Hbar(amountHbar)); + const frozenTx = await transfer.freezeWith(hederaClient); + const signedTx = await frozenTx.sign(privateKey); + const response = await signedTx.execute(hederaClient); + const receipt = await response.getReceipt(hederaClient); + console.log( + ` βœ“ Sent ${amountHbar} HBAR to proxy ${proxyAddress} – status: ${receipt.status}`, + ); +} + +// ─── Main ───────────────────────────────────────────────────────────────────── + +const main = async () => { + const accountId = requireEnv('MY_ACCOUNT_ID'); + const privateKeyStr = requireEnv('MY_PRIVATE_KEY'); + const factoryAddress = requireEnv('FACTORY_ADDRESS'); + const resolverAddress = requireEnv('RESOLVER_ADDRESS'); + + await initSdk(factoryAddress, resolverAddress); + + // ── Build Hedera client for signing + submission ──────────────────────── + const keyHex = privateKeyStr.startsWith('0x') + ? privateKeyStr.slice(2) + : privateKeyStr; + const pk = + detectKeyType(privateKeyStr) === 'ECDSA' + ? PrivateKey.fromStringECDSA(keyHex) + : PrivateKey.fromStringED25519(keyHex); + const hederaClient = Client.forTestnet(); + hederaClient.setOperator(AccountId.fromString(accountId), pk); + hederaClient.setDefaultMaxTransactionFee(new Hbar(20)); + + // ── Step 1: Connect CLIENT + create/setup token ──────────────────────── + let tokenId = process.env.TOKEN_ID_HEDERA ?? ''; + let reserveAddress = ''; + let proxyAddress = ''; + + if (!tokenId) { + await connectClient(accountId, privateKeyStr); + console.log('[1] Connected with CLIENT wallet'); + const setup = await createStablecoin( + accountId, + 'ExternalHedera Test Token', + 'HERTEST', + ); + tokenId = setup.tokenId; + reserveAddress = setup.reserveAddress; + proxyAddress = setup.proxyAddress; + await setupToken(tokenId, accountId); + + if (proxyAddress) { + await fundProxyWithHBAR(accountId, proxyAddress, hederaClient, pk); + } + } else { + console.log(`\n[1] Using existing token: ${tokenId}`); + } + + // ── Step 2: Switch to EXTERNAL_HEDERA ───────────────────────────────── + console.log('\n[2] Connecting with EXTERNAL_HEDERA wallet...'); + await connectExternal(accountId, SupportedWallets.EXTERNAL_HEDERA); + console.log(' βœ“ Connected (no private key held by SDK)'); + + // ── Step 3: Run the full test suite ─────────────────────────────────── + const signer = new HederaSigner(hederaClient, pk); + + console.log(`\n[3] Running tests as Hedera account: ${accountId}`); + console.log(` Token: ${tokenId}`); + console.log(` Key type: ${detectKeyType(privateKeyStr)}\n`); + + await runTestSuite(signer, { + accountId, + privateKeyStr, + tokenId, + reserveAddress, + proxyAddress, + resolverAddress, + }); + + hederaClient.close(); + printSummary('EXTERNAL HEDERA TEST SUMMARY'); + process.exit(0); +}; + +main().catch((error) => { + console.error('\nβœ— Fatal error:', error); + printSummary('EXTERNAL HEDERA TEST SUMMARY'); + process.exit(1); +}); diff --git a/packages/sdk/example/ts/tsconfig.json b/packages/sdk/example/ts/tsconfig.json index e8dd07772..d76040544 100644 --- a/packages/sdk/example/ts/tsconfig.json +++ b/packages/sdk/example/ts/tsconfig.json @@ -6,6 +6,7 @@ "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, - "skipLibCheck": true + "skipLibCheck": true, + "typeRoots": ["./node_modules/@types"] } } diff --git a/packages/sdk/example/ts/wipe.ts b/packages/sdk/example/ts/wipe.ts index a31171db2..9ce08c258 100644 --- a/packages/sdk/example/ts/wipe.ts +++ b/packages/sdk/example/ts/wipe.ts @@ -116,7 +116,7 @@ const main = async () => { }); // Create the stablecoin and log the result - const stableCoin = await StableCoin.create(request); + const stableCoin = (await StableCoin.create(request)) as { coin: any; reserve: any }; console.log('StableCoin created:', stableCoin); // Associate the stablecoin with the account diff --git a/packages/sdk/package.json b/packages/sdk/package.json index e349ea50b..c17ee6867 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -67,7 +67,9 @@ "format:imports": "npx format-imports ./src", "pre-commit": "npm run lint && npm run prettier", "prettierCheck": "prettier --config .prettierrc --check", - "prepack": "npm run build" + "prepack": "npm run build", + "test-external-evm": "ts-node --project example/ts/tsconfig.json example/ts/testExternalEVM.ts", + "test-external-hedera": "ts-node --project example/ts/tsconfig.json example/ts/testExternalHedera.ts" }, "license": "Apache-2.0", "repository": "https://github.com/hashgraph/stablecoin-studio", diff --git a/packages/sdk/src/app/service/TransactionService.ts b/packages/sdk/src/app/service/TransactionService.ts index c92742a7f..8014574c3 100644 --- a/packages/sdk/src/app/service/TransactionService.ts +++ b/packages/sdk/src/app/service/TransactionService.ts @@ -60,6 +60,8 @@ import { Response } from '../../domain/context/transaction/Response'; import { EmptyResponse } from './error/EmptyResponse.js'; import { InvalidResponse } from '../../port/out/mirror/error/InvalidResponse.js'; import { ClientTransactionAdapter } from '../../port/out/hs/client/ClientTransactionAdapter.js'; +import { ExternalHederaTransactionAdapter } from '../../port/out/hs/external/ExternalHederaTransactionAdapter.js'; +import { ExternalEVMTransactionAdapter } from '../../port/out/hs/external/ExternalEVMTransactionAdapter.js'; export const EVM_ADDRESS_REGEX = /0x[a-fA-F0-9]{40}$/; @@ -107,11 +109,23 @@ export default class TransactionService extends Service { ); case SupportedWallets.AWSKMS: return Injectable.resolve(AWSKMSTransactionAdapter); + case SupportedWallets.EXTERNAL_HEDERA: + return Injectable.resolve(ExternalHederaTransactionAdapter); + case SupportedWallets.EXTERNAL_EVM: + return Injectable.resolve(ExternalEVMTransactionAdapter); default: return Injectable.resolve(ClientTransactionAdapter); } } + isExternalWallet(): boolean { + const handler = this.getHandler(); + return ( + handler instanceof ExternalHederaTransactionAdapter || + handler instanceof ExternalEVMTransactionAdapter + ); + } + static async getDescription( t: Transaction, mirrorNodeAdapter: MirrorNodeAdapter, diff --git a/packages/sdk/src/app/usecase/command/account/associate/AssociateCommand.ts b/packages/sdk/src/app/usecase/command/account/associate/AssociateCommand.ts index 0e865d9f4..3927d4054 100644 --- a/packages/sdk/src/app/usecase/command/account/associate/AssociateCommand.ts +++ b/packages/sdk/src/app/usecase/command/account/associate/AssociateCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../domain/context/transaction/TransactionResponse.js'; export class AssociateCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/account/associate/AssociateCommandHandler.ts b/packages/sdk/src/app/usecase/command/account/associate/AssociateCommandHandler.ts index 139bd685c..30a7d19fb 100644 --- a/packages/sdk/src/app/usecase/command/account/associate/AssociateCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/account/associate/AssociateCommandHandler.ts @@ -62,7 +62,7 @@ export class AssociateCommandHandler const res = await handler.associateToken(tokenId, targetId); return Promise.resolve( - new AssociateCommandResponse(res.error === undefined, res.id), + new AssociateCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/network/connect/ConnectCommand.ts b/packages/sdk/src/app/usecase/command/network/connect/ConnectCommand.ts index 7c5f2dfa5..0e0f220e1 100644 --- a/packages/sdk/src/app/usecase/command/network/connect/ConnectCommand.ts +++ b/packages/sdk/src/app/usecase/command/network/connect/ConnectCommand.ts @@ -8,6 +8,7 @@ import DfnsSettings from 'domain/context/custodialwalletsettings/DfnsSettings.js import FireblocksSettings from 'domain/context/custodialwalletsettings/FireblocksSettings.js'; import AWSKMSSettings from '../../../../../domain/context/custodialwalletsettings/AWSKMSSettings'; import HWCSettings from '../../../../../domain/context/hwalletconnectsettings/HWCSettings.js'; +import { ExternalWalletSettings } from '../../../../../port/in/request/ConnectRequest.js'; export class ConnectCommandResponse implements CommandResponse { constructor( @@ -26,6 +27,7 @@ export class ConnectCommand extends Command { | FireblocksSettings | AWSKMSSettings, public readonly hWCSettings?: HWCSettings, + public readonly externalWalletSettings?: ExternalWalletSettings, ) { super(); } diff --git a/packages/sdk/src/app/usecase/command/network/connect/ConnectCommandHandler.ts b/packages/sdk/src/app/usecase/command/network/connect/ConnectCommandHandler.ts index bc93f63d8..4c4ffe3b6 100644 --- a/packages/sdk/src/app/usecase/command/network/connect/ConnectCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/network/connect/ConnectCommandHandler.ts @@ -20,23 +20,48 @@ import { ICommandHandler } from '../../../../../core/command/CommandHandler.js'; import { CommandHandler } from '../../../../../core/decorator/CommandHandlerDecorator.js'; +import { SupportedWallets } from '../../../../../domain/context/network/Wallet.js'; import TransactionService from '../../../../service/TransactionService.js'; +import { ExternalHederaTransactionAdapter } from '../../../../../port/out/hs/external/ExternalHederaTransactionAdapter.js'; +import { ExternalEVMTransactionAdapter } from '../../../../../port/out/hs/external/ExternalEVMTransactionAdapter.js'; import { ConnectCommand, ConnectCommandResponse } from './ConnectCommand.js'; +import LogService from '../../../../service/LogService.js'; @CommandHandler(ConnectCommand) export class ConnectCommandHandler implements ICommandHandler { async execute(command: ConnectCommand): Promise { - console.log('ConnectCommand Handler' + command.wallet); + LogService.logTrace('ConnectCommandHandler: wallet=', command.wallet); const handler = TransactionService.getHandlerClass(command.wallet); const input = command.custodialSettings === undefined ? command.hWCSettings === undefined - ? command.account + ? command.account! : command.hWCSettings : command.custodialSettings; - const registration = await handler.register(input); + if ( + command.wallet === SupportedWallets.EXTERNAL_HEDERA && + handler instanceof ExternalHederaTransactionAdapter + ) { + handler.setExternalWalletSettings( + command.externalWalletSettings?.validStartOffsetMinutes, + ); + } else if ( + command.wallet === SupportedWallets.EXTERNAL_EVM && + handler instanceof ExternalEVMTransactionAdapter + ) { + handler.setExternalWalletSettings( + command.externalWalletSettings?.validStartOffsetMinutes, + ); + } + + // TypeScript resolves handler.register() to the narrowest override + // (e.g. ExternalHederaTransactionAdapter accepts only Account), so a + // full-union cast triggers a type error. Runtime behaviour is correct: + // each adapter validates its own input internally. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const registration = await handler.register(input as any); return Promise.resolve( new ConnectCommandResponse(registration, command.wallet), diff --git a/packages/sdk/src/app/usecase/command/reserve/operations/updateReserveAmount/UpdateReserveAmountCommand.ts b/packages/sdk/src/app/usecase/command/reserve/operations/updateReserveAmount/UpdateReserveAmountCommand.ts index 49b262c39..007618237 100644 --- a/packages/sdk/src/app/usecase/command/reserve/operations/updateReserveAmount/UpdateReserveAmountCommand.ts +++ b/packages/sdk/src/app/usecase/command/reserve/operations/updateReserveAmount/UpdateReserveAmountCommand.ts @@ -22,11 +22,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import ContractId from '../../../../../../domain/context/contract/ContractId.js'; import BigDecimal from '../../../../../../domain/context/shared/BigDecimal.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class UpdateReserveAmountCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/reserve/operations/updateReserveAmount/UpdateReserveAmountCommandHandler.ts b/packages/sdk/src/app/usecase/command/reserve/operations/updateReserveAmount/UpdateReserveAmountCommandHandler.ts index da475b126..c16846751 100644 --- a/packages/sdk/src/app/usecase/command/reserve/operations/updateReserveAmount/UpdateReserveAmountCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/reserve/operations/updateReserveAmount/UpdateReserveAmountCommandHandler.ts @@ -47,10 +47,7 @@ export class UpdateReserveAmountCommandHandler reserveAmount, ); return Promise.resolve( - new UpdateReserveAmountCommandResponse( - res.error === undefined, - res.id, - ), + new UpdateReserveAmountCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/create/CreateCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/create/CreateCommand.ts index d5a5eb70c..04cca66ff 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/create/CreateCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/create/CreateCommand.ts @@ -24,20 +24,24 @@ import ContractId from '../../../../../domain/context/contract/ContractId.js'; import { StableCoinProps } from '../../../../../domain/context/stablecoin/StableCoin.js'; import BigDecimal from '../../../../../domain/context/shared/BigDecimal.js'; import { HederaId } from '../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../domain/context/transaction/TransactionResponse.js'; export class CreateCommandResponse implements CommandResponse { public readonly tokenId: ContractId; public readonly stableCoinProxy: ContractId; public readonly reserveProxy: ContractId; + public readonly serializedTransactionData?: SerializedTransactionData; constructor( tokenId: ContractId, stableCoinProxy: ContractId, reserveProxy: ContractId, + serializedTransactionData?: SerializedTransactionData, ) { this.tokenId = tokenId; this.reserveProxy = reserveProxy; this.stableCoinProxy = stableCoinProxy; + this.serializedTransactionData = serializedTransactionData; } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/create/CreateCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/create/CreateCommandHandler.ts index 2899bb334..d3f3382e0 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/create/CreateCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/create/CreateCommandHandler.ts @@ -175,6 +175,15 @@ export class CreateCommandHandler implements ICommandHandler { reserveConfigVersion, ); + if (this.transactionService.isExternalWallet()) { + return new CreateCommandResponse( + new ContractId('0.0.0'), + new ContractId('0.0.0'), + new ContractId('0.0.0'), + res.serializedTransactionData, + ); + } + if (!res.id) throw new Error('Create Command Handler response id empty'); diff --git a/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFixedFeesCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFixedFeesCommand.ts index c3879312c..994bc82d2 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFixedFeesCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFixedFeesCommand.ts @@ -2,11 +2,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import BigDecimal from '../../../../../../domain/context/shared/BigDecimal.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class addFixedFeesCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFixedFeesCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFixedFeesCommandHandler.ts index d31d5de38..be0b61670 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFixedFeesCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFixedFeesCommandHandler.ts @@ -113,7 +113,7 @@ export class addFixedFeesCommandHandler const res = await handler.updateCustomFees(capabilities, HcustomFee); return Promise.resolve( - new addFixedFeesCommandResponse(res.error === undefined, res.id), + new addFixedFeesCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFractionalFeesCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFractionalFeesCommand.ts index 7478d46a3..7c6980dcf 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFractionalFeesCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFractionalFeesCommand.ts @@ -2,11 +2,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import BigDecimal from '../../../../../../domain/context/shared/BigDecimal.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class addFractionalFeesCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFractionalFeesCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFractionalFeesCommandHandler.ts index 56291abed..2743f2192 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFractionalFeesCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/fees/addCustomFees/addFractionalFeesCommandHandler.ts @@ -116,10 +116,7 @@ export class addFractionalFeesCommandHandler const res = await handler.updateCustomFees(capabilities, HcustomFee); return Promise.resolve( - new addFractionalFeesCommandResponse( - res.error === undefined, - res.id, - ), + new addFractionalFeesCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/fees/updateCustomFees/UpdateCustomFeesCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/fees/updateCustomFees/UpdateCustomFeesCommand.ts index 0dfb2e260..7c589888f 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/fees/updateCustomFees/UpdateCustomFeesCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/fees/updateCustomFees/UpdateCustomFeesCommand.ts @@ -2,11 +2,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { CustomFee } from '../../../../../../domain/context/fee/CustomFee.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class UpdateCustomFeesCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/fees/updateCustomFees/UpdateCustomFeesCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/fees/updateCustomFees/UpdateCustomFeesCommandHandler.ts index b48c5091a..70a86f670 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/fees/updateCustomFees/UpdateCustomFeesCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/fees/updateCustomFees/UpdateCustomFeesCommandHandler.ts @@ -99,10 +99,7 @@ export class UpdateCustomFeesCommandHandler const res = await handler.updateCustomFees(capabilities, HcustomFee); return Promise.resolve( - new UpdateCustomFeesCommandResponse( - res.error === undefined, - res.id, - ), + new UpdateCustomFeesCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfig/updateConfigCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfig/updateConfigCommand.ts index dfa205c8f..7fb267cb8 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfig/updateConfigCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfig/updateConfigCommand.ts @@ -21,11 +21,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class UpdateConfigCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfig/updateConfigCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfig/updateConfigCommandHandler.ts index 974530c88..b77772dd5 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfig/updateConfigCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfig/updateConfigCommandHandler.ts @@ -60,7 +60,7 @@ export class UpdateConfigCommandHandler ); return Promise.resolve( - new UpdateConfigCommandResponse(res.error === undefined, res.id), + new UpdateConfigCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfigVersion/updateConfigVersionCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfigVersion/updateConfigVersionCommand.ts index 46233fe0a..1ed7f0885 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfigVersion/updateConfigVersionCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfigVersion/updateConfigVersionCommand.ts @@ -21,11 +21,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class UpdateConfigVersionCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfigVersion/updateConfigVersionCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfigVersion/updateConfigVersionCommandHandler.ts index 1cf9369e0..ea716879e 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfigVersion/updateConfigVersionCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/management/updateConfigVersion/updateConfigVersionCommandHandler.ts @@ -59,10 +59,7 @@ export class UpdateConfigVersionCommandHandler ); return Promise.resolve( - new UpdateConfigVersionCommandResponse( - res.error === undefined, - res.id, - ), + new UpdateConfigVersionCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/management/updateResolver/updateResolverCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/management/updateResolver/updateResolverCommand.ts index 58926503d..73007540f 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/management/updateResolver/updateResolverCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/management/updateResolver/updateResolverCommand.ts @@ -22,11 +22,13 @@ import ContractId from '../../../../../../domain/context/contract/ContractId.js' import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class UpdateResolverCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/management/updateResolver/updateResolverCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/management/updateResolver/updateResolverCommandHandler.ts index 8c51efd47..652a94525 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/management/updateResolver/updateResolverCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/management/updateResolver/updateResolverCommandHandler.ts @@ -61,7 +61,7 @@ export class UpdateResolverCommandHandler ); return Promise.resolve( - new UpdateResolverCommandResponse(res.error === undefined, res.id), + new UpdateResolverCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/burn/BurnCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/burn/BurnCommand.ts index f3586081e..29f76b197 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/burn/BurnCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/burn/BurnCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class BurnCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/burn/BurnCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/burn/BurnCommandHandler.ts index 7885feb0a..eedeae6d4 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/burn/BurnCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/burn/BurnCommandHandler.ts @@ -72,7 +72,7 @@ export class BurnCommandHandler implements ICommandHandler { const res = await handler.burn(capabilities, amountBd, startDate); return Promise.resolve( - new BurnCommandResponse(res.error === undefined, res.id), + new BurnCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/cashin/CashInCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/cashin/CashInCommand.ts index 0f89e49e0..56aecf532 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/cashin/CashInCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/cashin/CashInCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class CashInCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/cashin/CashInCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/cashin/CashInCommandHandler.ts index 68fb6b5f9..e6a2b07b2 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/cashin/CashInCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/cashin/CashInCommandHandler.ts @@ -194,7 +194,7 @@ export class CashInCommandHandler implements ICommandHandler { startDate, ); return Promise.resolve( - new CashInCommandResponse(res.error === undefined, res.id), + new CashInCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/delete/DeleteCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/delete/DeleteCommand.ts index 6f3e24289..86f1ef379 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/delete/DeleteCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/delete/DeleteCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class DeleteCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/delete/DeleteCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/delete/DeleteCommandHandler.ts index a3b50b788..8b15e3e29 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/delete/DeleteCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/delete/DeleteCommandHandler.ts @@ -47,7 +47,7 @@ export class DeleteCommandHandler implements ICommandHandler { ); const res = await handler.delete(capabilities, startDate); return Promise.resolve( - new DeleteCommandResponse(res.error === undefined, res.id), + new DeleteCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/freeze/FreezeCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/freeze/FreezeCommand.ts index 71d6c6a0f..7e32bafba 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/freeze/FreezeCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/freeze/FreezeCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class FreezeCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/freeze/FreezeCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/freeze/FreezeCommandHandler.ts index 61efaffc9..8442b5848 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/freeze/FreezeCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/freeze/FreezeCommandHandler.ts @@ -63,7 +63,7 @@ export class FreezeCommandHandler implements ICommandHandler { ); const res = await handler.freeze(capabilities, targetId, startDate); return Promise.resolve( - new FreezeCommandResponse(res.error === undefined, res.id), + new FreezeCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/grantKyc/GrantKycCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/grantKyc/GrantKycCommand.ts index 5b1cc0877..d295e376f 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/grantKyc/GrantKycCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/grantKyc/GrantKycCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class GrantKycCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/grantKyc/GrantKycCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/grantKyc/GrantKycCommandHandler.ts index 6afb5f38a..475732fe6 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/grantKyc/GrantKycCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/grantKyc/GrantKycCommandHandler.ts @@ -102,7 +102,7 @@ export class GrantKycCommandHandler const res = await handler.grantKyc(capabilities, targetId); return Promise.resolve( - new GrantKycCommandResponse(res.error === undefined, res.id), + new GrantKycCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHold/CreateHoldCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHold/CreateHoldCommand.ts index 736e2a578..4f6f4f498 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHold/CreateHoldCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHold/CreateHoldCommand.ts @@ -1,12 +1,14 @@ import { Command } from '../../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../../domain/context/transaction/TransactionResponse.js'; export class CreateHoldCommandResponse implements CommandResponse { constructor( public readonly holdId: number, public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHold/CreateHoldCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHold/CreateHoldCommandHandler.ts index cb5ab009e..260ef924b 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHold/CreateHoldCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHold/CreateHoldCommandHandler.ts @@ -123,6 +123,15 @@ export class CreateHoldCommandHandler targetId, ); + if (this.transactionService.isExternalWallet()) { + return new CreateHoldCommandResponse( + 0, + false, + res.id, + res.serializedTransactionData, + ); + } + const holdId = await this.transactionService.getTransactionResult({ res, result: res.response?.holdId, @@ -136,6 +145,7 @@ export class CreateHoldCommandHandler parseInt(holdId, 16), res.error == undefined, res.id, + res.serializedTransactionData, ), ); } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHoldByController/CreateHoldByControllerCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHoldByController/CreateHoldByControllerCommand.ts index bc038e17b..863529373 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHoldByController/CreateHoldByControllerCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHoldByController/CreateHoldByControllerCommand.ts @@ -1,12 +1,14 @@ import { Command } from '../../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../../domain/context/transaction/TransactionResponse.js'; export class CreateHoldByControllerCommandResponse implements CommandResponse { constructor( public readonly holdId: number, public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHoldByController/CreateHoldByControllerCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHoldByController/CreateHoldByControllerCommandHandler.ts index 426b9fd1c..345f21fa6 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHoldByController/CreateHoldByControllerCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/createHoldByController/CreateHoldByControllerCommandHandler.ts @@ -125,6 +125,15 @@ export class CreateHoldByControllerCommandHandler targetId, ); + if (this.transactionService.isExternalWallet()) { + return new CreateHoldByControllerCommandResponse( + 0, + false, + res.id, + res.serializedTransactionData, + ); + } + const holdId = await this.transactionService.getTransactionResult({ res, result: res.response?.holdId, @@ -138,6 +147,7 @@ export class CreateHoldByControllerCommandHandler parseInt(holdId, 16), res.error == undefined, res.id, + res.serializedTransactionData, ), ); } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/executeHold/ExecuteHoldCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/executeHold/ExecuteHoldCommand.ts index e242aad83..9f9b852d2 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/executeHold/ExecuteHoldCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/executeHold/ExecuteHoldCommand.ts @@ -21,11 +21,13 @@ import { Command } from '../../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../../domain/context/transaction/TransactionResponse.js'; export class ExecuteHoldCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/executeHold/ExecuteHoldCommandHander.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/executeHold/ExecuteHoldCommandHander.ts index b6f9130c7..c0f97f053 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/executeHold/ExecuteHoldCommandHander.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/executeHold/ExecuteHoldCommandHander.ts @@ -149,7 +149,7 @@ export class ExecuteHoldCommandHandler ); return Promise.resolve( - new ExecuteHoldCommandResponse(res.error === undefined, res.id), + new ExecuteHoldCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/reclaimHold/ReclaimHoldCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/reclaimHold/ReclaimHoldCommand.ts index f9e7b5d05..262b1bda5 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/reclaimHold/ReclaimHoldCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/reclaimHold/ReclaimHoldCommand.ts @@ -21,11 +21,13 @@ import { Command } from '../../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../../domain/context/transaction/TransactionResponse.js'; export class ReclaimHoldCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/reclaimHold/ReclaimHoldCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/reclaimHold/ReclaimHoldCommandHandler.ts index 474675b6a..6105924a7 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/reclaimHold/ReclaimHoldCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/reclaimHold/ReclaimHoldCommandHandler.ts @@ -103,7 +103,7 @@ export class ReclaimHoldCommandHandler const res = await handler.reclaimHold(capabilities, sourceId, holdId); return Promise.resolve( - new ReclaimHoldCommandResponse(res.error === undefined, res.id), + new ReclaimHoldCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/releaseHold/ReleaseHoldCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/releaseHold/ReleaseHoldCommand.ts index ea8f120a5..04215678a 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/releaseHold/ReleaseHoldCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/releaseHold/ReleaseHoldCommand.ts @@ -21,11 +21,13 @@ import { Command } from '../../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../../domain/context/transaction/TransactionResponse.js'; export class ReleaseHoldCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/releaseHold/ReleaseHoldCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/releaseHold/ReleaseHoldCommandHandler.ts index bf8c74ebf..d5b1db327 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/releaseHold/ReleaseHoldCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/hold/releaseHold/ReleaseHoldCommandHandler.ts @@ -119,7 +119,7 @@ export class ReleaseHoldCommandHandler ); return Promise.resolve( - new ReleaseHoldCommandResponse(res.error === undefined, res.id), + new ReleaseHoldCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/pause/PauseCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/pause/PauseCommand.ts index 02e54a95e..4174ec4db 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/pause/PauseCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/pause/PauseCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class PauseCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/pause/PauseCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/pause/PauseCommandHandler.ts index 0656921a8..45f3fcaea 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/pause/PauseCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/pause/PauseCommandHandler.ts @@ -47,7 +47,7 @@ export class PauseCommandHandler implements ICommandHandler { ); const res = await handler.pause(capabilities, startDate); return Promise.resolve( - new PauseCommandResponse(res.error === undefined, res.id), + new PauseCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/rescue/RescueCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/rescue/RescueCommand.ts index 813625f55..fcde668ea 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/rescue/RescueCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/rescue/RescueCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class RescueCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/rescue/RescueCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/rescue/RescueCommandHandler.ts index e01fff5d5..8b4980abf 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/rescue/RescueCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/rescue/RescueCommandHandler.ts @@ -108,7 +108,7 @@ export class RescueCommandHandler implements ICommandHandler { } const res = await handler.rescue(capabilities, amountBd, startDate); return Promise.resolve( - new RescueCommandResponse(res.error === undefined, res.id), + new RescueCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/rescueHBAR/RescueHBARCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/rescueHBAR/RescueHBARCommand.ts index 66bd5a758..040a1cc4d 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/rescueHBAR/RescueHBARCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/rescueHBAR/RescueHBARCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class RescueHBARCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/rescueHBAR/RescueHBARCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/rescueHBAR/RescueHBARCommandHandler.ts index abbea653e..daf70fb43 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/rescueHBAR/RescueHBARCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/rescueHBAR/RescueHBARCommandHandler.ts @@ -91,7 +91,7 @@ export class RescueHBARCommandHandler const res = await handler.rescueHBAR(capabilities, amountBd, startDate); return Promise.resolve( - new RescueHBARCommandResponse(res.error === undefined, res.id), + new RescueHBARCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/revokeKyc/RevokeKycCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/revokeKyc/RevokeKycCommand.ts index 36a4a2e74..719ca75d6 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/revokeKyc/RevokeKycCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/revokeKyc/RevokeKycCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class RevokeKycCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/revokeKyc/RevokeKycCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/revokeKyc/RevokeKycCommandHandler.ts index c1cd2a049..224ea03d0 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/revokeKyc/RevokeKycCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/revokeKyc/RevokeKycCommandHandler.ts @@ -90,7 +90,7 @@ export class RevokeKycCommandHandler const res = await handler.revokeKyc(capabilities, targetId); return Promise.resolve( - new RevokeKycCommandResponse(res.error === undefined, res.id), + new RevokeKycCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/transfer/TransfersCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/transfer/TransfersCommand.ts index d7a647816..ace49588a 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/transfer/TransfersCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/transfer/TransfersCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class TransfersCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/transfer/TransfersCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/transfer/TransfersCommandHandler.ts index 9759e2873..de01977d2 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/transfer/TransfersCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/transfer/TransfersCommandHandler.ts @@ -156,7 +156,7 @@ export class TransfersCommandHandler targetId, ); return Promise.resolve( - new TransfersCommandResponse(res.error === undefined, res.id), + new TransfersCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/unfreeze/UnFreezeCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/unfreeze/UnFreezeCommand.ts index b26c66a92..dc16cddda 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/unfreeze/UnFreezeCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/unfreeze/UnFreezeCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class UnFreezeCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/unfreeze/UnFreezeCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/unfreeze/UnFreezeCommandHandler.ts index 3ff8b3fd8..dfe592b47 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/unfreeze/UnFreezeCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/unfreeze/UnFreezeCommandHandler.ts @@ -65,7 +65,7 @@ export class UnFreezeCommandHandler ); const res = await handler.unfreeze(capabilities, targetId, startDate); return Promise.resolve( - new UnFreezeCommandResponse(res.error === undefined, res.id), + new UnFreezeCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/unpause/UnPauseCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/unpause/UnPauseCommand.ts index 25041faab..b467a49a8 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/unpause/UnPauseCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/unpause/UnPauseCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class UnPauseCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/unpause/UnPauseCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/unpause/UnPauseCommandHandler.ts index 356e1f74c..b75aa43de 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/unpause/UnPauseCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/unpause/UnPauseCommandHandler.ts @@ -47,7 +47,7 @@ export class UnPauseCommandHandler implements ICommandHandler { ); const res = await handler.unpause(capabilities, startDate); return Promise.resolve( - new UnPauseCommandResponse(res.error === undefined, res.id), + new UnPauseCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/updateReserveAddress/UpdateReserveAddressCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/updateReserveAddress/UpdateReserveAddressCommand.ts index 8cbea60d7..30ab5627e 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/updateReserveAddress/UpdateReserveAddressCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/updateReserveAddress/UpdateReserveAddressCommand.ts @@ -22,11 +22,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import ContractId from '../../../../../../domain/context/contract/ContractId.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class UpdateReserveAddressCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/updateReserveAddress/UpdateReserveAddressCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/updateReserveAddress/UpdateReserveAddressCommandHandler.ts index 5af8cafb4..4b51049aa 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/updateReserveAddress/UpdateReserveAddressCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/updateReserveAddress/UpdateReserveAddressCommandHandler.ts @@ -59,10 +59,7 @@ export class UpdateReserveAddressCommandHandler reserveAddress, ); return Promise.resolve( - new UpdateReserveAddressCommandResponse( - res.error === undefined, - res.id, - ), + new UpdateReserveAddressCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/wipe/WipeCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/wipe/WipeCommand.ts index 1628ff630..39805d83c 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/wipe/WipeCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/wipe/WipeCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class WipeCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/operations/wipe/WipeCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/operations/wipe/WipeCommandHandler.ts index 6233a68f3..a3e4e8219 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/operations/wipe/WipeCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/operations/wipe/WipeCommandHandler.ts @@ -86,7 +86,7 @@ export class WipeCommandHandler implements ICommandHandler { startDate, ); return Promise.resolve( - new WipeCommandResponse(res.error === undefined, res.id), + new WipeCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/decreaseAllowance/DecreaseAllowanceCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/decreaseAllowance/DecreaseAllowanceCommand.ts index 0b5e3acf8..c6b1cc362 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/decreaseAllowance/DecreaseAllowanceCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/decreaseAllowance/DecreaseAllowanceCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class DecreaseAllowanceCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/decreaseAllowance/DecreaseAllowanceCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/decreaseAllowance/DecreaseAllowanceCommandHandler.ts index 1f3e0c281..2e0046734 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/decreaseAllowance/DecreaseAllowanceCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/decreaseAllowance/DecreaseAllowanceCommandHandler.ts @@ -67,10 +67,7 @@ export class DecreaseAllowanceCommandHandler ); // return Promise.resolve({ payload: res.response }); return Promise.resolve( - new DecreaseAllowanceCommandResponse( - res.error === undefined, - res.id, - ), + new DecreaseAllowanceCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/granUnlimitedSupplierRole/GrantUnlimitedSupplierRoleCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/granUnlimitedSupplierRole/GrantUnlimitedSupplierRoleCommand.ts index f1a654cb3..bc27bc6cf 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/granUnlimitedSupplierRole/GrantUnlimitedSupplierRoleCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/granUnlimitedSupplierRole/GrantUnlimitedSupplierRoleCommand.ts @@ -1,6 +1,7 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class GrantUnlimitedSupplierRoleCommandResponse implements CommandResponse @@ -8,6 +9,7 @@ export class GrantUnlimitedSupplierRoleCommandResponse constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/granUnlimitedSupplierRole/GrantUnlimitedSupplierRoleCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/granUnlimitedSupplierRole/GrantUnlimitedSupplierRoleCommandHandler.ts index 80e42b002..474c09f92 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/granUnlimitedSupplierRole/GrantUnlimitedSupplierRoleCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/granUnlimitedSupplierRole/GrantUnlimitedSupplierRoleCommandHandler.ts @@ -61,6 +61,7 @@ export class GrantUnlimitedSupplierRoleCommandHandler new GrantUnlimitedSupplierRoleCommandResponse( res.error === undefined, res.id, + res.serializedTransactionData, ), ); } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommand.ts index cb2e6478b..2742a466c 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommand.ts @@ -2,11 +2,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; import { StableCoinRole } from '../../../../../../domain/context/stablecoin/StableCoinRole.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class GrantMultiRolesCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommandHandler.ts index 1dc1f443c..ef90e07a0 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommandHandler.ts @@ -86,7 +86,7 @@ export class GrantMultiRolesCommandHandler ); return Promise.resolve( - new GrantMultiRolesCommandResponse(res.error === undefined, res.id), + new GrantMultiRolesCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantRole/GrantRoleCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantRole/GrantRoleCommand.ts index 02b31575b..3044cb4ab 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantRole/GrantRoleCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantRole/GrantRoleCommand.ts @@ -2,11 +2,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; import { StableCoinRole } from '../../../../../../domain/context/stablecoin/StableCoinRole.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class GrantRoleCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantRole/GrantRoleCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantRole/GrantRoleCommandHandler.ts index 8a4b9be49..05ab8c485 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantRole/GrantRoleCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantRole/GrantRoleCommandHandler.ts @@ -55,7 +55,7 @@ export class GrantRoleCommandHandler const res = await handler.grantRole(capabilities, targetId, role); // return Promise.resolve({ payload: res.response }); return Promise.resolve( - new GrantRoleCommandResponse(res.error === undefined, res.id), + new GrantRoleCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantSupplierRole/GrantSupplierRoleCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantSupplierRole/GrantSupplierRoleCommand.ts index cf5a40275..893ce357b 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantSupplierRole/GrantSupplierRoleCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantSupplierRole/GrantSupplierRoleCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class GrantSupplierRoleCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantSupplierRole/GrantSupplierRoleCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantSupplierRole/GrantSupplierRoleCommandHandler.ts index 78e7d6552..abe579e1c 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/grantSupplierRole/GrantSupplierRoleCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/grantSupplierRole/GrantSupplierRoleCommandHandler.ts @@ -60,10 +60,7 @@ export class GrantSupplierRoleCommandHandler ); // return Promise.resolve({ payload: res.response }); return Promise.resolve( - new GrantSupplierRoleCommandResponse( - res.error === undefined, - res.id, - ), + new GrantSupplierRoleCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/increaseAllowance/IncreaseAllowanceCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/increaseAllowance/IncreaseAllowanceCommand.ts index ca997c30d..d16c57770 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/increaseAllowance/IncreaseAllowanceCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/increaseAllowance/IncreaseAllowanceCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class IncreaseAllowanceCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/increaseAllowance/IncreaseAllowanceCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/increaseAllowance/IncreaseAllowanceCommandHandler.ts index c78dcb500..d3f09b982 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/increaseAllowance/IncreaseAllowanceCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/increaseAllowance/IncreaseAllowanceCommandHandler.ts @@ -67,10 +67,7 @@ export class IncreaseAllowanceCommandHandler ); // return Promise.resolve({ payload: res.response }); return Promise.resolve( - new IncreaseAllowanceCommandResponse( - res.error === undefined, - res.id, - ), + new IncreaseAllowanceCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/resetAllowance/ResetAllowanceCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/resetAllowance/ResetAllowanceCommand.ts index 6292d2814..9cf96f8f2 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/resetAllowance/ResetAllowanceCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/resetAllowance/ResetAllowanceCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class ResetAllowanceCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/resetAllowance/ResetAllowanceCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/resetAllowance/ResetAllowanceCommandHandler.ts index d0e03418a..e3a3ff7be 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/resetAllowance/ResetAllowanceCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/resetAllowance/ResetAllowanceCommandHandler.ts @@ -59,7 +59,7 @@ export class ResetAllowanceCommandHandler ); // return Promise.resolve({ payload: res.response ?? false }); return Promise.resolve( - new ResetAllowanceCommandResponse(res.error === undefined, res.id), + new ResetAllowanceCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeMultiRoles/RevokeMultiRolesCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeMultiRoles/RevokeMultiRolesCommand.ts index d912ff0ff..f30951267 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeMultiRoles/RevokeMultiRolesCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeMultiRoles/RevokeMultiRolesCommand.ts @@ -2,11 +2,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; import { StableCoinRole } from '../../../../../../domain/context/stablecoin/StableCoinRole.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class RevokeMultiRolesCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeMultiRoles/RevokeMultiRolesCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeMultiRoles/RevokeMultiRolesCommandHandler.ts index fdbdd07e3..b61842aa3 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeMultiRoles/RevokeMultiRolesCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeMultiRoles/RevokeMultiRolesCommandHandler.ts @@ -78,10 +78,7 @@ export class RevokeMultiRolesCommandHandler // return Promise.resolve({ payload: res.response ?? false }); return Promise.resolve( - new RevokeMultiRolesCommandResponse( - res.error === undefined, - res.id, - ), + new RevokeMultiRolesCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeRole/RevokeRoleCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeRole/RevokeRoleCommand.ts index 1e490a587..1483d00bb 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeRole/RevokeRoleCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeRole/RevokeRoleCommand.ts @@ -2,11 +2,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; import { StableCoinRole } from '../../../../../../domain/context/stablecoin/StableCoinRole.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class RevokeRoleCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeRole/RevokeRoleCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeRole/RevokeRoleCommandHandler.ts index f9e41d942..32e93bd82 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeRole/RevokeRoleCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeRole/RevokeRoleCommandHandler.ts @@ -56,7 +56,7 @@ export class RevokeRoleCommandHandler // return Promise.resolve({ payload: res.response ?? false }); return Promise.resolve( - new RevokeRoleCommandResponse(res.error === undefined, res.id), + new RevokeRoleCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeSupplierRole/RevokeSupplierRoleCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeSupplierRole/RevokeSupplierRoleCommand.ts index c2a892e84..f3272e652 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeSupplierRole/RevokeSupplierRoleCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeSupplierRole/RevokeSupplierRoleCommand.ts @@ -1,11 +1,13 @@ import { Command } from '../../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../../core/command/CommandResponse.js'; import { HederaId } from '../../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../../domain/context/transaction/TransactionResponse.js'; export class RevokeSupplierRoleCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeSupplierRole/RevokeSupplierRoleCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeSupplierRole/RevokeSupplierRoleCommandHandler.ts index ca10532fa..8000c55a2 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeSupplierRole/RevokeSupplierRoleCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/roles/revokeSupplierRole/RevokeSupplierRoleCommandHandler.ts @@ -55,10 +55,7 @@ export class RevokeSupplierRoleCommandHandler const res = await handler.revokeSupplierRole(capabilities, targetId); // return Promise.resolve({ payload: res.response ?? false }); return Promise.resolve( - new RevokeSupplierRoleCommandResponse( - res.error === undefined, - res.id, - ), + new RevokeSupplierRoleCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/update/UpdateCommand.ts b/packages/sdk/src/app/usecase/command/stablecoin/update/UpdateCommand.ts index bc5427d4b..bfe392433 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/update/UpdateCommand.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/update/UpdateCommand.ts @@ -22,11 +22,13 @@ import { Command } from '../../../../../core/command/Command.js'; import { CommandResponse } from '../../../../../core/command/CommandResponse.js'; import PublicKey from '../../../../../domain/context/account/PublicKey.js'; import { HederaId } from '../../../../../domain/context/shared/HederaId.js'; +import { SerializedTransactionData } from '../../../../../domain/context/transaction/TransactionResponse.js'; export class UpdateCommandResponse implements CommandResponse { constructor( public readonly payload: boolean, public readonly transactionId?: string, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/app/usecase/command/stablecoin/update/UpdateCommandHandler.ts b/packages/sdk/src/app/usecase/command/stablecoin/update/UpdateCommandHandler.ts index c5b0f90b8..5c6c46e0b 100644 --- a/packages/sdk/src/app/usecase/command/stablecoin/update/UpdateCommandHandler.ts +++ b/packages/sdk/src/app/usecase/command/stablecoin/update/UpdateCommandHandler.ts @@ -72,7 +72,7 @@ export class UpdateCommandHandler implements ICommandHandler { ); return Promise.resolve( - new UpdateCommandResponse(res.error === undefined, res.id), + new UpdateCommandResponse(res.error === undefined, res.id, res.serializedTransactionData), ); } } diff --git a/packages/sdk/src/core/Injectable.ts b/packages/sdk/src/core/Injectable.ts index cb98a2e07..45c4883f1 100644 --- a/packages/sdk/src/core/Injectable.ts +++ b/packages/sdk/src/core/Injectable.ts @@ -115,6 +115,8 @@ import { GetHoldCountForQueryHandler } from '../app/usecase/query/stablecoin/hol import { GetBurnableAmountQueryHandler } from '../app/usecase/query/stablecoin/burn/getBurnableAmount/GetBurnableAmountQueryHandler.js'; import { GetAccountAutoAssociationQueryHandler } from '../app/usecase/query/account/autoAssociation/GetAccountAutoAssociationQueryHandler'; import { ClientTransactionAdapter } from '../port/out/hs/client/ClientTransactionAdapter.js'; +import { ExternalHederaTransactionAdapter } from '../port/out/hs/external/ExternalHederaTransactionAdapter.js'; +import { ExternalEVMTransactionAdapter } from '../port/out/hs/external/ExternalEVMTransactionAdapter.js'; export const TOKENS = { COMMAND_HANDLER: Symbol('CommandHandler'), @@ -203,10 +205,6 @@ const COMMAND_HANDLERS = [ token: TOKENS.COMMAND_HANDLER, useClass: CreateHoldCommandHandler, }, - { - token: TOKENS.COMMAND_HANDLER, - useClass: CreateHoldCommandHandler, - }, { token: TOKENS.COMMAND_HANDLER, useClass: CreateHoldByControllerCommandHandler, @@ -439,6 +437,14 @@ const TRANSACTION_HANDLER = [ token: TOKENS.TRANSACTION_HANDLER, useClass: HederaWalletConnectTransactionAdapter, }, + { + token: TOKENS.TRANSACTION_HANDLER, + useClass: ExternalHederaTransactionAdapter, + }, + { + token: TOKENS.TRANSACTION_HANDLER, + useClass: ExternalEVMTransactionAdapter, + }, ]; const defaultNetworkProps: NetworkProps = { @@ -535,6 +541,8 @@ export default class Injectable { adapters.push(Injectable.resolve(ClientTransactionAdapter)); } adapters.push(Injectable.resolve(MultiSigTransactionAdapter)); + adapters.push(Injectable.resolve(ExternalHederaTransactionAdapter)); + adapters.push(Injectable.resolve(ExternalEVMTransactionAdapter)); return adapters; } diff --git a/packages/sdk/src/domain/context/account/PrivateKey.ts b/packages/sdk/src/domain/context/account/PrivateKey.ts index 5d6d5645b..611e18a5d 100644 --- a/packages/sdk/src/domain/context/account/PrivateKey.ts +++ b/packages/sdk/src/domain/context/account/PrivateKey.ts @@ -32,9 +32,9 @@ export default class PrivateKey implements KeyProps { const { key, type } = props; this.type = this.validateType(type); this.key = key; - this.publicKey = PublicKey.fromHederaKey( - this.toHashgraphKey().publicKey, - ); + this.publicKey = key + ? PublicKey.fromHederaKey(this.toHashgraphKey().publicKey) + : PublicKey.NULL; } // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -60,13 +60,8 @@ export default class PrivateKey implements KeyProps { } public toHashgraphKey(): HPrivateKey { - //try { return this.type === KeyType.ED25519 ? HPrivateKey.fromStringED25519(this.key) : HPrivateKey.fromStringECDSA(this.key); - //} catch (error) { - // console.log(error); - // throw new PrivateKeyNotValid(this.key); - //} } } diff --git a/packages/sdk/src/domain/context/network/Wallet.ts b/packages/sdk/src/domain/context/network/Wallet.ts index fc7812b4e..35a1968ba 100644 --- a/packages/sdk/src/domain/context/network/Wallet.ts +++ b/packages/sdk/src/domain/context/network/Wallet.ts @@ -29,6 +29,8 @@ export enum SupportedWallets { MULTISIG = 'MultiSig', AWSKMS = 'AWSKMS', HWALLETCONNECT = 'HederaWalletConnect', + EXTERNAL_HEDERA = 'ExternalHedera', + EXTERNAL_EVM = 'ExternalEVM', } export default interface Wallet { diff --git a/packages/sdk/src/domain/context/shared/HederaId.ts b/packages/sdk/src/domain/context/shared/HederaId.ts index c9a3860ed..8b9bba256 100644 --- a/packages/sdk/src/domain/context/shared/HederaId.ts +++ b/packages/sdk/src/domain/context/shared/HederaId.ts @@ -1,6 +1,9 @@ import { AccountId } from '@hiero-ledger/sdk'; import { InvalidIdFormat } from './error/InvalidIdFormat.js'; +// Note: the optional checksum group (?:-([a-z]{5}))? is accepted by the regex +// but is intentionally not validated. If checksum validation is required in the +// future, parse capture group 4 and compare against the expected checksum. const HEDERA_FORMAT_ID_REGEX = /^(0|(?:[1-9]\d*))\.(0|(?:[1-9]\d*))\.(0|(?:[1-9]\d*))(?:-([a-z]{5}))?$/; @@ -16,7 +19,8 @@ export class HederaId { } static from(value?: string): HederaId { - return new HederaId(value ?? ''); + if (!value) return HederaId.NULL; + return new HederaId(value); } toHederaAddress(): AccountId { @@ -28,6 +32,6 @@ export class HederaId { } isNull(): boolean { - return this.value == '0.0.0'; + return this.value === '0.0.0'; } } diff --git a/packages/sdk/src/domain/context/transaction/TransactionResponse.ts b/packages/sdk/src/domain/context/transaction/TransactionResponse.ts index 0da64cabf..b3ae3c68c 100644 --- a/packages/sdk/src/domain/context/transaction/TransactionResponse.ts +++ b/packages/sdk/src/domain/context/transaction/TransactionResponse.ts @@ -20,6 +20,17 @@ import { Response } from './Response.js'; +export interface TransactionMetadata { + transactionType: string; + description: string; + requiredSigners: string[]; +} + +export interface SerializedTransactionData { + serializedTransaction: string; + metadata: TransactionMetadata; +} + export default class TransactionResponse< T extends Response = Response, X extends Error = Error, @@ -28,5 +39,6 @@ export default class TransactionResponse< public readonly id?: string, public response?: T, public readonly error?: X, + public readonly serializedTransactionData?: SerializedTransactionData, ) {} } diff --git a/packages/sdk/src/port/in/CustomFees.ts b/packages/sdk/src/port/in/CustomFees.ts index d20da9923..be4abc4f3 100644 --- a/packages/sdk/src/port/in/CustomFees.ts +++ b/packages/sdk/src/port/in/CustomFees.ts @@ -20,6 +20,7 @@ import { CommandBus } from '../../core/command/CommandBus.js'; import Injectable from '../../core/Injectable.js'; +import { EmptyResponse } from '../../app/service/error/EmptyResponse.js'; import { AddFixedFeeRequest, AddFractionalFeeRequest, @@ -45,17 +46,18 @@ import { isRequestFixedFee, } from './request/BaseRequest.js'; import { TransactionResult } from '../../domain/context/transaction/TransactionResult.js'; +import { SerializedTransactionData } from '../../domain/context/transaction/TransactionResponse.js'; + export { HBAR_DECIMALS, MAX_PERCENTAGE_DECIMALS, MAX_CUSTOM_FEES }; interface ICustomFees { addFixedFee(request: AddFixedFeeRequest): Promise; - addFractionalFee( - request: AddFractionalFeeRequest, - ): Promise; - updateCustomFees( - request: UpdateCustomFeesRequest, - ): Promise; + buildAddFixedFee(request: AddFixedFeeRequest): Promise; + addFractionalFee(request: AddFractionalFeeRequest): Promise; + buildAddFractionalFee(request: AddFractionalFeeRequest): Promise; + updateCustomFees(request: UpdateCustomFeesRequest): Promise; + buildUpdateCustomFees(request: UpdateCustomFeesRequest): Promise; } class CustomFeesInPort implements ICustomFees { @@ -90,9 +92,32 @@ class CustomFeesInPort implements ICustomFees { } @LogError - async addFractionalFee( - request: AddFractionalFeeRequest, - ): Promise { + async buildAddFixedFee(request: AddFixedFeeRequest): Promise { + const { + tokenId, + collectorId, + tokenIdCollected, + amount, + decimals, + collectorsExempt, + } = request; + handleValidation('AddFixedFeeRequest', request); + + const response = await this.commandBus.execute( + new addFixedFeesCommand( + HederaId.from(tokenId), + HederaId.from(collectorId), + HederaId.from(tokenIdCollected), + BigDecimal.fromString(amount, decimals), + collectorsExempt, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + + @LogError + async addFractionalFee(request: AddFractionalFeeRequest): Promise { const { tokenId, collectorId, @@ -133,9 +158,49 @@ class CustomFeesInPort implements ICustomFees { } @LogError - async updateCustomFees( - request: UpdateCustomFeesRequest, - ): Promise { + async buildAddFractionalFee(request: AddFractionalFeeRequest): Promise { + const { + tokenId, + collectorId, + percentage, + amountNumerator, + amountDenominator, + min, + max, + decimals, + collectorsExempt, + net, + } = request; + handleValidation('AddFractionalFeeRequest', request); + + let _amountNumerator = amountNumerator ?? ''; + let _amountDenominator = amountDenominator ?? ''; + const _min = min ?? '0'; + const _max = max ?? '0'; + + if (_amountNumerator === '') { + [_amountNumerator, _amountDenominator] = + this.getFractionFromPercentage(percentage ?? ''); + } + + const response = await this.commandBus.execute( + new addFractionalFeesCommand( + HederaId.from(tokenId), + HederaId.from(collectorId), + parseInt(_amountNumerator), + parseInt(_amountDenominator), + BigDecimal.fromString(_min, decimals), + BigDecimal.fromString(_max, decimals), + net, + collectorsExempt, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + + @LogError + async updateCustomFees(request: UpdateCustomFeesRequest): Promise { const { tokenId, customFees } = request; handleValidation('UpdateCustomFeesRequest', request); @@ -196,6 +261,69 @@ class CustomFeesInPort implements ICustomFees { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildUpdateCustomFees(request: UpdateCustomFeesRequest): Promise { + const { tokenId, customFees } = request; + handleValidation('UpdateCustomFeesRequest', request); + + const requestedCustomFee: CustomFee[] = []; + + customFees.forEach((customFee) => { + if (isRequestFixedFee(customFee)) { + requestedCustomFee.push( + new FixedFee( + HederaId.from(customFee.collectorId), + BigDecimal.fromString( + customFee.amount, + customFee.decimals, + ), + HederaId.from(customFee.tokenIdCollected), + customFee.collectorsExempt, + ), + ); + } else if (isRequestFractionalFee(customFee)) { + let _amountNumerator = customFee.amountNumerator ?? ''; + let _amountDenominator = customFee.amountDenominator ?? ''; + + if (_amountNumerator === '') { + [_amountNumerator, _amountDenominator] = + this.getFractionFromPercentage(customFee.percentage); + } + + requestedCustomFee.push( + new FractionalFee( + HederaId.from(customFee.collectorId), + parseInt(_amountNumerator), + parseInt(_amountDenominator), + customFee.min + ? BigDecimal.fromString( + customFee.min, + customFee.decimals, + ) + : undefined, + customFee.max + ? BigDecimal.fromString( + customFee.max, + customFee.decimals, + ) + : undefined, + customFee.net, + customFee.collectorsExempt, + ), + ); + } + }); + + const response = await this.commandBus.execute( + new UpdateCustomFeesCommand( + HederaId.from(tokenId), + requestedCustomFee, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + getFractionFromPercentage(percentage: string): string[] { const fraction: string[] = []; @@ -207,9 +335,7 @@ class CustomFeesInPort implements ICustomFees { percentage, MAX_PERCENTAGE_DECIMALS, ); - const amountNumerator = Math.round( - numerator.toUnsafeFloat() * exponential, - ).toString(); + const amountNumerator = numerator.toBigInt().toString(); fraction.push(amountNumerator); fraction.push(amountDenominator); diff --git a/packages/sdk/src/port/in/Management.ts b/packages/sdk/src/port/in/Management.ts index ec51fb902..4b19eb68e 100644 --- a/packages/sdk/src/port/in/Management.ts +++ b/packages/sdk/src/port/in/Management.ts @@ -19,6 +19,7 @@ */ import Injectable from '../../core/Injectable.js'; +import { EmptyResponse } from '../../app/service/error/EmptyResponse.js'; import ContractId from '../../domain/context/contract/ContractId.js'; import GetConfigInfoRequest from './request/GetConfigInfoRequest'; import UpdateConfigRequest from './request/UpdateConfigRequest'; @@ -35,15 +36,17 @@ import { UpdateConfigCommand } from '../../app/usecase/command/stablecoin/manage import { GetConfigInfoQuery } from '../../app/usecase/query/stablecoin/management/getConfigInfo/GetConfigInfoQuery.js'; import ConfigInfoViewModel from './response/ConfigInfoViewModel.js'; import { TransactionResult } from '../../domain/context/transaction/TransactionResult.js'; +import { SerializedTransactionData } from '../../domain/context/transaction/TransactionResponse.js'; + interface IManagementInPort { - updateConfigVersion( - request: UpdateConfigVersionRequest, - ): Promise; + updateConfigVersion(request: UpdateConfigVersionRequest): Promise; + buildUpdateConfigVersion(request: UpdateConfigVersionRequest): Promise; updateConfig(request: UpdateConfigRequest): Promise; - + buildUpdateConfig(request: UpdateConfigRequest): Promise; getConfigInfo(request: GetConfigInfoRequest): Promise; updateResolver(request: UpdateResolverRequest): Promise; + buildUpdateResolver(request: UpdateResolverRequest): Promise; } class ManagementInPort implements IManagementInPort { @@ -71,9 +74,24 @@ class ManagementInPort implements IManagementInPort { } @LogError - async updateConfig( - request: UpdateConfigRequest, - ): Promise { + async buildUpdateConfigVersion( + request: UpdateConfigVersionRequest, + ): Promise { + const { configVersion, tokenId } = request; + handleValidation('UpdateConfigVersionRequest', request); + + const response = await this.commandBus.execute( + new UpdateConfigVersionCommand( + HederaId.from(tokenId), + configVersion, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + + @LogError + async updateConfig(request: UpdateConfigRequest): Promise { const { configId, configVersion, tokenId } = request; handleValidation('UpdateConfigRequest', request); @@ -88,9 +106,23 @@ class ManagementInPort implements IManagementInPort { } @LogError - async updateResolver( - request: UpdateResolverRequest, - ): Promise { + async buildUpdateConfig(request: UpdateConfigRequest): Promise { + const { configId, configVersion, tokenId } = request; + handleValidation('UpdateConfigRequest', request); + + const response = await this.commandBus.execute( + new UpdateConfigCommand( + HederaId.from(tokenId), + configId, + configVersion, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + + @LogError + async updateResolver(request: UpdateResolverRequest): Promise { const { configId, tokenId, resolver, configVersion } = request; handleValidation('UpdateResolverRequest', request); @@ -105,6 +137,23 @@ class ManagementInPort implements IManagementInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildUpdateResolver(request: UpdateResolverRequest): Promise { + const { configId, tokenId, resolver, configVersion } = request; + handleValidation('UpdateResolverRequest', request); + + const response = await this.commandBus.execute( + new UpdateResolverCommand( + HederaId.from(tokenId), + configVersion, + configId, + new ContractId(resolver), + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async getConfigInfo( request: GetConfigInfoRequest, diff --git a/packages/sdk/src/port/in/Network.ts b/packages/sdk/src/port/in/Network.ts index cd35de85a..c09b3cf8d 100644 --- a/packages/sdk/src/port/in/Network.ts +++ b/packages/sdk/src/port/in/Network.ts @@ -253,6 +253,7 @@ class NetworkInPort implements INetworkInPort { account, custodialSettings, hwcSettings, + req.externalWalletSettings, ), ); return res.payload; diff --git a/packages/sdk/src/port/in/ReserveDataFeed.ts b/packages/sdk/src/port/in/ReserveDataFeed.ts index e1db03b14..156480dca 100644 --- a/packages/sdk/src/port/in/ReserveDataFeed.ts +++ b/packages/sdk/src/port/in/ReserveDataFeed.ts @@ -20,6 +20,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import Injectable from '../../core/Injectable.js'; +import { EmptyResponse } from '../../app/service/error/EmptyResponse.js'; import BigDecimal from '../../domain/context/shared/BigDecimal.js'; import ContractId from '../../domain/context/contract/ContractId.js'; import { CommandBus } from '../../core/command/CommandBus.js'; @@ -35,12 +36,13 @@ import { QueryBus } from '../../core/query/QueryBus.js'; import { LogError } from '../../core/decorator/LogErrorDecorator.js'; import { MirrorNodeAdapter } from '../../port/out/mirror/MirrorNodeAdapter.js'; import { TransactionResult } from '../../domain/context/transaction/TransactionResult.js'; +import { SerializedTransactionData } from '../../domain/context/transaction/TransactionResponse.js'; + interface IReserveDataFeedInPort { getReserveAmount(request: GetReserveAmountRequest): Promise; - updateReserveAmount( - request: UpdateReserveAmountRequest, - ): Promise; + updateReserveAmount(request: UpdateReserveAmountRequest): Promise; + buildUpdateReserveAmount(request: UpdateReserveAmountRequest): Promise; } class ReserveDataFeedInPort implements IReserveDataFeedInPort { @@ -77,11 +79,36 @@ class ReserveDataFeedInPort implements IReserveDataFeedInPort { const response = await this.commandBus.execute( new UpdateReserveAmountCommand( new ContractId(reserveId), - BigDecimal.fromString(request.reserveAmount, RESERVE_DECIMALS), + BigDecimal.fromString( + request.reserveAmount, + RESERVE_DECIMALS, + ), ), ); return new TransactionResult(response.payload, response.transactionId); } + + @LogError + async buildUpdateReserveAmount( + request: UpdateReserveAmountRequest, + ): Promise { + handleValidation('UpdateReserveAmountRequest', request); + + const reserveId: string = ( + await this.mirrorNode.getContractInfo(request.reserveAddress) + ).id; + const response = await this.commandBus.execute( + new UpdateReserveAmountCommand( + new ContractId(reserveId), + BigDecimal.fromString( + request.reserveAmount, + RESERVE_DECIMALS, + ), + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } } const ReserveDataFeed = new ReserveDataFeedInPort(); diff --git a/packages/sdk/src/port/in/Role.ts b/packages/sdk/src/port/in/Role.ts index 6b4be8f83..fb50cd684 100644 --- a/packages/sdk/src/port/in/Role.ts +++ b/packages/sdk/src/port/in/Role.ts @@ -21,6 +21,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import Injectable from '../../core/Injectable.js'; +import { EmptyResponse } from '../../app/service/error/EmptyResponse.js'; import { QueryBus } from '../../core/query/QueryBus.js'; import { CommandBus } from '../../core/command/CommandBus.js'; import { @@ -63,34 +64,41 @@ import { RevokeMultiRolesCommand } from '../../app/usecase/command/stablecoin/ro import GetAccountsWithRolesRequest from './request/GetAccountsWithRolesRequest.js'; import { GetAccountsWithRolesQuery } from '../../app/usecase/query/stablecoin/roles/getAccountsWithRole/GetAccountsWithRolesQuery.js'; import { TransactionResult } from '../../domain/context/transaction/TransactionResult.js'; +import { SerializedTransactionData } from '../../domain/context/transaction/TransactionResponse.js'; + export { StableCoinRole, StableCoinRoleLabel, MAX_ACCOUNTS_ROLES }; interface IRole { hasRole(request: HasRoleRequest): Promise; grantRole(request: GrantRoleRequest): Promise; + buildGrantRole(request: GrantRoleRequest): Promise; revokeRole(request: RevokeRoleRequest): Promise; - grantMultiRoles( - request: GrantMultiRolesRequest, - ): Promise; - revokeMultiRoles( - request: RevokeMultiRolesRequest, - ): Promise; + buildRevokeRole(request: RevokeRoleRequest): Promise; + grantMultiRoles(request: GrantMultiRolesRequest): Promise; + buildGrantMultiRoles(request: GrantMultiRolesRequest): Promise; + revokeMultiRoles(request: RevokeMultiRolesRequest): Promise; + buildRevokeMultiRoles(request: RevokeMultiRolesRequest): Promise; getRoles(request: GetRolesRequest): Promise; getAccountsWithRole( request: GetAccountsWithRolesRequest, ): Promise; //Supplier getAllowance(request: GetSupplierAllowanceRequest): Promise; - resetAllowance( - request: ResetSupplierAllowanceRequest, - ): Promise; + resetAllowance(request: ResetSupplierAllowanceRequest): Promise; + buildResetAllowance(request: ResetSupplierAllowanceRequest): Promise; increaseAllowance( request: IncreaseSupplierAllowanceRequest, ): Promise; + buildIncreaseAllowance( + request: IncreaseSupplierAllowanceRequest, + ): Promise; decreaseAllowance( request: DecreaseSupplierAllowanceRequest, ): Promise; + buildDecreaseAllowance( + request: DecreaseSupplierAllowanceRequest, + ): Promise; isLimited(request: CheckSupplierLimitRequest): Promise; isUnlimited(request: CheckSupplierLimitRequest): Promise; } @@ -124,7 +132,7 @@ class RoleInPort implements IRole { handleValidation('GrantRoleRequest', request); if (role === StableCoinRole.CASHIN_ROLE) { - if (supplierType == 'limited') { + if (supplierType === 'limited') { const response = await this.commandBus.execute( new GrantSupplierRoleCommand( HederaId.from(targetId), @@ -132,10 +140,7 @@ class RoleInPort implements IRole { amount!, ), ); - return new TransactionResult( - response.payload, - response.transactionId, - ); + return new TransactionResult(response.payload, response.transactionId); } else { const response = await this.commandBus.execute( new GrantUnlimitedSupplierRoleCommand( @@ -143,10 +148,7 @@ class RoleInPort implements IRole { HederaId.from(tokenId), ), ); - return new TransactionResult( - response.payload, - response.transactionId, - ); + return new TransactionResult(response.payload, response.transactionId); } } else { const response = await this.commandBus.execute( @@ -156,17 +158,53 @@ class RoleInPort implements IRole { HederaId.from(tokenId), ), ); - return new TransactionResult( - response.payload, - response.transactionId, + return new TransactionResult(response.payload, response.transactionId); + } + } + + @LogError + async buildGrantRole(request: GrantRoleRequest): Promise { + const { tokenId, targetId, role, supplierType, amount } = request; + handleValidation('GrantRoleRequest', request); + + if (role === StableCoinRole.CASHIN_ROLE) { + if (supplierType === 'limited') { + const response = await this.commandBus.execute( + new GrantSupplierRoleCommand( + HederaId.from(targetId), + HederaId.from(tokenId), + amount!, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } else { + const response = await this.commandBus.execute( + new GrantUnlimitedSupplierRoleCommand( + HederaId.from(targetId), + HederaId.from(tokenId), + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + } else { + const response = await this.commandBus.execute( + new GrantRoleCommand( + role!, + HederaId.from(targetId), + HederaId.from(tokenId), + ), ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; } } @LogError async revokeRole(request: RevokeRoleRequest): Promise { const { tokenId, targetId, role } = request; - handleValidation('HasRoleRequest', request); + handleValidation('RevokeRoleRequest', request); if (role === StableCoinRole.CASHIN_ROLE) { const response = await this.commandBus.execute( @@ -175,10 +213,7 @@ class RoleInPort implements IRole { HederaId.from(tokenId), ), ); - return new TransactionResult( - response.payload, - response.transactionId, - ); + return new TransactionResult(response.payload, response.transactionId); } else { const response = await this.commandBus.execute( new RevokeRoleCommand( @@ -187,17 +222,39 @@ class RoleInPort implements IRole { HederaId.from(tokenId), ), ); - return new TransactionResult( - response.payload, - response.transactionId, + return new TransactionResult(response.payload, response.transactionId); + } + } + + @LogError + async buildRevokeRole(request: RevokeRoleRequest): Promise { + const { tokenId, targetId, role } = request; + handleValidation('RevokeRoleRequest', request); + + if (role === StableCoinRole.CASHIN_ROLE) { + const response = await this.commandBus.execute( + new RevokeSupplierRoleCommand( + HederaId.from(targetId), + HederaId.from(tokenId), + ), ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } else { + const response = await this.commandBus.execute( + new RevokeRoleCommand( + role!, + HederaId.from(targetId), + HederaId.from(tokenId), + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; } } @LogError - async grantMultiRoles( - request: GrantMultiRolesRequest, - ): Promise { + async grantMultiRoles(request: GrantMultiRolesRequest): Promise { const { tokenId, targetsId, roles, amounts, startDate } = request; handleValidation('GrantMultiRolesRequest', request); @@ -219,11 +276,32 @@ class RoleInPort implements IRole { } @LogError - async revokeMultiRoles( - request: RevokeMultiRolesRequest, - ): Promise { + async buildGrantMultiRoles(request: GrantMultiRolesRequest): Promise { + const { tokenId, targetsId, roles, amounts, startDate } = request; + handleValidation('GrantMultiRolesRequest', request); + + const targetsIdHederaIds: HederaId[] = []; + targetsId.forEach((targetId) => { + targetsIdHederaIds.push(HederaId.from(targetId)); + }); + + const response = await this.commandBus.execute( + new GrantMultiRolesCommand( + roles, + targetsIdHederaIds, + amounts ?? [], + HederaId.from(tokenId), + startDate, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + + @LogError + async revokeMultiRoles(request: RevokeMultiRolesRequest): Promise { const { tokenId, targetsId, roles, startDate } = request; - handleValidation('HasRoleRequest', request); + handleValidation('RevokeMultiRolesRequest', request); const targetsIdHederaIds: HederaId[] = []; targetsId.forEach((targetId) => { @@ -241,6 +319,28 @@ class RoleInPort implements IRole { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildRevokeMultiRoles(request: RevokeMultiRolesRequest): Promise { + const { tokenId, targetsId, roles, startDate } = request; + handleValidation('RevokeMultiRolesRequest', request); + + const targetsIdHederaIds: HederaId[] = []; + targetsId.forEach((targetId) => { + targetsIdHederaIds.push(HederaId.from(targetId)); + }); + + const response = await this.commandBus.execute( + new RevokeMultiRolesCommand( + roles, + targetsIdHederaIds, + HederaId.from(tokenId), + startDate, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async getRoles(request: GetRolesRequest): Promise { const { tokenId, targetId } = request; @@ -255,6 +355,7 @@ class RoleInPort implements IRole { ) ).payload; } + @LogError async getAccountsWithRole( request: GetAccountsWithRolesRequest, @@ -300,6 +401,24 @@ class RoleInPort implements IRole { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildResetAllowance( + request: ResetSupplierAllowanceRequest, + ): Promise { + const { tokenId, targetId, startDate } = request; + handleValidation('ResetSupplierAllowanceRequest', request); + + const response = await this.commandBus.execute( + new ResetAllowanceCommand( + HederaId.from(targetId), + HederaId.from(tokenId), + startDate, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async increaseAllowance( request: IncreaseSupplierAllowanceRequest, @@ -318,6 +437,25 @@ class RoleInPort implements IRole { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildIncreaseAllowance( + request: IncreaseSupplierAllowanceRequest, + ): Promise { + const { tokenId, amount, targetId, startDate } = request; + handleValidation('IncreaseSupplierAllowanceRequest', request); + + const response = await this.commandBus.execute( + new IncreaseAllowanceCommand( + amount, + HederaId.from(targetId), + HederaId.from(tokenId), + startDate, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async decreaseAllowance( request: DecreaseSupplierAllowanceRequest, @@ -336,6 +474,25 @@ class RoleInPort implements IRole { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildDecreaseAllowance( + request: DecreaseSupplierAllowanceRequest, + ): Promise { + const { tokenId, amount, targetId, startDate } = request; + handleValidation('DecreaseSupplierAllowanceRequest', request); + + const response = await this.commandBus.execute( + new DecreaseAllowanceCommand( + amount, + HederaId.from(targetId), + HederaId.from(tokenId), + startDate, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async isLimited(request: CheckSupplierLimitRequest): Promise { const { tokenId, targetId } = request; diff --git a/packages/sdk/src/port/in/StableCoin.ts b/packages/sdk/src/port/in/StableCoin.ts index e5cae965a..e048937f3 100644 --- a/packages/sdk/src/port/in/StableCoin.ts +++ b/packages/sdk/src/port/in/StableCoin.ts @@ -20,6 +20,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import Injectable from '../../core/Injectable.js'; +import { EmptyResponse } from '../../app/service/error/EmptyResponse.js'; import CreateRequest from './request/CreateRequest.js'; import CashInRequest from './request/CashInRequest.js'; import GetStableCoinDetailsRequest from './request/GetStableCoinDetailsRequest.js'; @@ -117,32 +118,19 @@ import GetHoldForRequest from './request/GetHoldForRequest.js'; import GetHeldAmountForRequest from './request/GetHeldAmountForRequest.js'; import GetHoldCountForRequest from './request/GetHoldCountForRequest.js'; import GetHoldsIdForRequest from './request/GetHoldsIdForRequest.js'; -import { - CreateHoldCommand, - CreateHoldCommandResponse, -} from '../../app/usecase/command/stablecoin/operations/hold/createHold/CreateHoldCommand.js'; -import { - ExecuteHoldCommand, - ExecuteHoldCommandResponse, -} from '../../app/usecase/command/stablecoin/operations/hold/executeHold/ExecuteHoldCommand.js'; -import { - ReleaseHoldCommand, - ReleaseHoldCommandResponse, -} from '../../app/usecase/command/stablecoin/operations/hold/releaseHold/ReleaseHoldCommand.js'; -import { - ReclaimHoldCommand, - ReclaimHoldCommandResponse, -} from '../../app/usecase/command/stablecoin/operations/hold/reclaimHold/ReclaimHoldCommand.js'; +import { CreateHoldCommand, CreateHoldCommandResponse } from '../../app/usecase/command/stablecoin/operations/hold/createHold/CreateHoldCommand.js'; +import { ExecuteHoldCommand, ExecuteHoldCommandResponse } from '../../app/usecase/command/stablecoin/operations/hold/executeHold/ExecuteHoldCommand.js'; +import { ReleaseHoldCommand, ReleaseHoldCommandResponse } from '../../app/usecase/command/stablecoin/operations/hold/releaseHold/ReleaseHoldCommand.js'; +import { ReclaimHoldCommand, ReclaimHoldCommandResponse } from '../../app/usecase/command/stablecoin/operations/hold/reclaimHold/ReclaimHoldCommand.js'; import { GetHoldForQuery } from '../../app/usecase/query/stablecoin/hold/getHoldFor/GetHoldForQuery.js'; import HoldViewModel from '../../port/in/response/HoldViewModel.js'; import { ONE_THOUSAND } from '../../core/Constants.js'; import { GetHoldCountForQuery } from '../../app/usecase/query/stablecoin/hold/getHoldCountFor/GetHoldCountForQuery.js'; import { GetHoldsIdForQuery } from '../../app/usecase/query/stablecoin/hold/getHoldsIdFor/GetHoldsIdForQuery.js'; import { GetHeldAmountForQuery } from '../../app/usecase/query/stablecoin/hold/getHeldAmountFor/GetHeldAmountForQuery.js'; -import { - CreateHoldByControllerCommand, - CreateHoldByControllerCommandResponse, -} from '../../app/usecase/command/stablecoin/operations/hold/createHoldByController/CreateHoldByControllerCommand.js'; +import { CreateHoldByControllerCommand, CreateHoldByControllerCommandResponse } from '../../app/usecase/command/stablecoin/operations/hold/createHoldByController/CreateHoldByControllerCommand.js'; +import { SerializedTransactionData } from '../../domain/context/transaction/TransactionResponse.js'; + export { StableCoinViewModel, @@ -157,30 +145,40 @@ export { StableCoinCapabilities, Capability, Access, Operation, Balance }; export { TokenSupplyType }; export { BigDecimal, HederaId, ContractId, EvmAddress, PublicKey }; -export type CreateHoldTransactionResult = { - holdId: number; -} & TransactionResult; +export type CreateHoldTransactionResult = { holdId: number } & TransactionResult; interface IStableCoinInPort { create(request: CreateRequest): Promise<{ coin: StableCoinViewModel; reserve: ReserveViewModel; }>; + buildCreate(request: CreateRequest): Promise; getInfo(request: GetStableCoinDetailsRequest): Promise; cashIn(request: CashInRequest): Promise; + buildCashIn(request: CashInRequest): Promise; burn(request: BurnRequest): Promise; + buildBurn(request: BurnRequest): Promise; rescue(request: RescueRequest): Promise; + buildRescue(request: RescueRequest): Promise; rescueHBAR(request: RescueHBARRequest): Promise; + buildRescueHBAR(request: RescueHBARRequest): Promise; wipe(request: WipeRequest): Promise; + buildWipe(request: WipeRequest): Promise; associate(request: AssociateTokenRequest): Promise; + buildAssociate(request: AssociateTokenRequest): Promise; getBalanceOf(request: GetAccountBalanceRequest): Promise; getBalanceOfHBAR(request: GetAccountBalanceHBARRequest): Promise; capabilities(request: CapabilitiesRequest): Promise; pause(request: PauseRequest): Promise; + buildPause(request: PauseRequest): Promise; unPause(request: PauseRequest): Promise; + buildUnPause(request: PauseRequest): Promise; delete(request: DeleteRequest): Promise; + buildDelete(request: DeleteRequest): Promise; freeze(request: FreezeAccountRequest): Promise; + buildFreeze(request: FreezeAccountRequest): Promise; unFreeze(request: FreezeAccountRequest): Promise; + buildUnFreeze(request: FreezeAccountRequest): Promise; isAccountFrozen(request: FreezeAccountRequest): Promise; isAccountAssociated( request: IsAccountAssociatedTokenRequest, @@ -189,33 +187,35 @@ interface IStableCoinInPort { updateReserveAddress( request: UpdateReserveAddressRequest, ): Promise; + buildUpdateReserveAddress( + request: UpdateReserveAddressRequest, + ): Promise; grantKyc(request: KYCRequest): Promise; + buildGrantKyc(request: KYCRequest): Promise; revokeKyc(request: KYCRequest): Promise; + buildRevokeKyc(request: KYCRequest): Promise; isAccountKYCGranted(request: KYCRequest): Promise; - createHold( - request: CreateHoldRequest, - ): Promise; - createHoldByController( - request: CreateHoldByControllerRequest, - ): Promise; + createHold(request: CreateHoldRequest): Promise; + buildCreateHold(request: CreateHoldRequest): Promise; + createHoldByController(request: CreateHoldByControllerRequest): Promise; + buildCreateHoldByController(request: CreateHoldByControllerRequest): Promise; executeHold(request: ExecuteHoldRequest): Promise; + buildExecuteHold(request: ExecuteHoldRequest): Promise; releaseHold(request: ReleaseHoldRequest): Promise; + buildReleaseHold(request: ReleaseHoldRequest): Promise; reclaimHold(request: ReclaimHoldRequest): Promise; + buildReclaimHold(request: ReclaimHoldRequest): Promise; getHoldFor(request: GetHoldForRequest): Promise; getHeldAmountFor(request: GetHeldAmountForRequest): Promise; getHoldCountFor(request: GetHoldCountForRequest): Promise; getHoldsIdFor(request: GetHoldsIdForRequest): Promise; transfers(request: TransfersRequest): Promise; + buildTransfers(request: TransfersRequest): Promise; update(request: UpdateRequest): Promise; - signTransaction( - request: SignTransactionRequest, - ): Promise; - submitTransaction( - request: SubmitTransactionRequest, - ): Promise; - removeTransaction( - request: RemoveTransactionRequest, - ): Promise; + buildUpdate(request: UpdateRequest): Promise; + signTransaction(request: SignTransactionRequest): Promise; + submitTransaction(request: SubmitTransactionRequest): Promise; + removeTransaction(request: RemoveTransactionRequest): Promise; getTransactions( request: GetTransactionsRequest, ): Promise; @@ -237,12 +237,8 @@ class StableCoinInPort implements IStableCoinInPort { MirrorNodeAdapter, ), ) {} - @LogError - async create(req: CreateRequest): Promise<{ - coin: StableCoinViewModel; - reserve: ReserveViewModel; - }> { - handleValidation('CreateRequest', req); + + private async _executeCreateCommand(req: CreateRequest) { const { reserveAddress, updatedAtThreshold, @@ -333,7 +329,7 @@ class StableCoinInPort implements IStableCoinInPort { ? (await this.mirrorNode.getContractInfo(reserveAddress)).id : undefined; - const createResponse = await this.commandBus.execute( + return await this.commandBus.execute( new CreateCommand( coin, createReserve, @@ -356,7 +352,15 @@ class StableCoinInPort implements IStableCoinInPort { reserveConfigId, ), ); + } + @LogError + async create(req: CreateRequest): Promise<{ + coin: StableCoinViewModel; + reserve: ReserveViewModel; + }> { + handleValidation('CreateRequest', req); + const createResponse = await this._executeCreateCommand(req); return { coin: createResponse.tokenId.toString() !== ContractId.NULL.toString() @@ -372,6 +376,14 @@ class StableCoinInPort implements IStableCoinInPort { }; } + @LogError + async buildCreate(req: CreateRequest): Promise { + handleValidation('CreateRequest', req); + const res = await this._executeCreateCommand(req); + if (!res.serializedTransactionData) throw new EmptyResponse('buildCreate'); + return res.serializedTransactionData; + } + @LogError async getInfo( request: GetStableCoinDetailsRequest, @@ -402,6 +414,23 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildCashIn(request: CashInRequest): Promise { + const { tokenId, amount, targetId, startDate } = request; + handleValidation('CashInRequest', request); + + const response = await this.commandBus.execute( + new CashInCommand( + amount, + HederaId.from(targetId), + HederaId.from(tokenId), + startDate, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async burn(request: BurnRequest): Promise { const { tokenId, amount, startDate } = request; @@ -413,6 +442,18 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildBurn(request: BurnRequest): Promise { + const { tokenId, amount, startDate } = request; + handleValidation('BurnRequest', request); + + const response = await this.commandBus.execute( + new BurnCommand(amount, HederaId.from(tokenId), startDate), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async rescue(request: RescueRequest): Promise { const { tokenId, amount, startDate } = request; @@ -424,17 +465,49 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildRescue(request: RescueRequest): Promise { + const { tokenId, amount, startDate } = request; + handleValidation('RescueRequest', request); + + const response = await this.commandBus.execute( + new RescueCommand(amount, HederaId.from(tokenId), startDate), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async rescueHBAR(request: RescueHBARRequest): Promise { const { tokenId, amount, startDate } = request; handleValidation('RescueHBARRequest', request); const response = await this.commandBus.execute( - new RescueHBARCommand(amount, HederaId.from(tokenId), startDate), + new RescueHBARCommand( + amount, + HederaId.from(tokenId), + startDate, + ), ); return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildRescueHBAR(request: RescueHBARRequest): Promise { + const { tokenId, amount, startDate } = request; + handleValidation('RescueHBARRequest', request); + + const response = await this.commandBus.execute( + new RescueHBARCommand( + amount, + HederaId.from(tokenId), + startDate, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async wipe(request: WipeRequest): Promise { const { tokenId, amount, targetId, startDate } = request; @@ -452,9 +525,24 @@ class StableCoinInPort implements IStableCoinInPort { } @LogError - async associate( - request: AssociateTokenRequest, - ): Promise { + async buildWipe(request: WipeRequest): Promise { + const { tokenId, amount, targetId, startDate } = request; + handleValidation('WipeRequest', request); + + const response = await this.commandBus.execute( + new WipeCommand( + amount, + HederaId.from(targetId), + HederaId.from(tokenId), + startDate, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + + @LogError + async associate(request: AssociateTokenRequest): Promise { const { tokenId, targetId } = request; handleValidation('AssociateTokenRequest', request); @@ -467,6 +555,21 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildAssociate(request: AssociateTokenRequest): Promise { + const { tokenId, targetId } = request; + handleValidation('AssociateTokenRequest', request); + + const response = await this.commandBus.execute( + new AssociateCommand( + HederaId.from(targetId), + HederaId.from(tokenId), + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async getBalanceOf(request: GetAccountBalanceRequest): Promise { handleValidation('GetAccountBalanceRequest', request); @@ -525,6 +628,18 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildPause(request: PauseRequest): Promise { + const { tokenId, startDate } = request; + handleValidation('PauseRequest', request); + + const response = await this.commandBus.execute( + new PauseCommand(HederaId.from(tokenId), startDate), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async unPause(request: PauseRequest): Promise { const { tokenId, startDate } = request; @@ -536,6 +651,18 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildUnPause(request: PauseRequest): Promise { + const { tokenId, startDate } = request; + handleValidation('PauseRequest', request); + + const response = await this.commandBus.execute( + new UnPauseCommand(HederaId.from(tokenId), startDate), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async delete(request: DeleteRequest): Promise { const { tokenId, startDate } = request; @@ -547,6 +674,18 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildDelete(request: DeleteRequest): Promise { + const { tokenId, startDate } = request; + handleValidation('DeleteRequest', request); + + const response = await this.commandBus.execute( + new DeleteCommand(HederaId.from(tokenId), startDate), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async freeze(request: FreezeAccountRequest): Promise { const { tokenId, targetId, startDate } = request; @@ -562,6 +701,22 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildFreeze(request: FreezeAccountRequest): Promise { + const { tokenId, targetId, startDate } = request; + handleValidation('FreezeAccountRequest', request); + + const response = await this.commandBus.execute( + new FreezeCommand( + HederaId.from(targetId), + HederaId.from(tokenId), + startDate, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async unFreeze(request: FreezeAccountRequest): Promise { const { tokenId, targetId, startDate } = request; @@ -577,6 +732,22 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildUnFreeze(request: FreezeAccountRequest): Promise { + const { tokenId, targetId, startDate } = request; + handleValidation('FreezeAccountRequest', request); + + const response = await this.commandBus.execute( + new UnFreezeCommand( + HederaId.from(targetId), + HederaId.from(tokenId), + startDate, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async isAccountFrozen(request: FreezeAccountRequest): Promise { const { tokenId, targetId } = request; @@ -608,6 +779,21 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildGrantKyc(request: KYCRequest): Promise { + const { tokenId, targetId } = request; + handleValidation('KYCRequest', request); + + const response = await this.commandBus.execute( + new GrantKycCommand( + HederaId.from(targetId), + HederaId.from(tokenId), + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async revokeKyc(request: KYCRequest): Promise { const { tokenId, targetId } = request; @@ -622,6 +808,21 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildRevokeKyc(request: KYCRequest): Promise { + const { tokenId, targetId } = request; + handleValidation('KYCRequest', request); + + const response = await this.commandBus.execute( + new RevokeKycCommand( + HederaId.from(targetId), + HederaId.from(tokenId), + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async isAccountKYCGranted(request: KYCRequest): Promise { const { tokenId, targetId } = request; @@ -676,9 +877,15 @@ class StableCoinInPort implements IStableCoinInPort { ): Promise { handleValidation('UpdateReserveAddressRequest', request); - const reserveAddressId: string = ( - await this.mirrorNode.getContractInfo(request.reserveAddress) - ).id; + // Handle special case: '0.0.0' (zero address) - don't query mirror node + let reserveAddressId: string; + if (request.reserveAddress === '0.0.0') { + reserveAddressId = '0.0.0'; + } else { + reserveAddressId = ( + await this.mirrorNode.getContractInfo(request.reserveAddress) + ).id; + } const response = await this.commandBus.execute( new UpdateReserveAddressCommand( @@ -689,6 +896,31 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildUpdateReserveAddress( + request: UpdateReserveAddressRequest, + ): Promise { + handleValidation('UpdateReserveAddressRequest', request); + + let reserveAddressId: string; + if (request.reserveAddress === '0.0.0') { + reserveAddressId = '0.0.0'; + } else { + reserveAddressId = ( + await this.mirrorNode.getContractInfo(request.reserveAddress) + ).id; + } + + const response = await this.commandBus.execute( + new UpdateReserveAddressCommand( + HederaId.from(request.tokenId), + new ContractId(reserveAddressId), + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async createHold( request: CreateHoldRequest, @@ -711,6 +943,25 @@ class StableCoinInPort implements IStableCoinInPort { } as CreateHoldTransactionResult; } + @LogError + async buildCreateHold( + request: CreateHoldRequest, + ): Promise { + handleValidation(CreateHoldRequest.name, request); + const { tokenId, targetId, amount, expirationDate, escrow } = request; + const response = await this.commandBus.execute( + new CreateHoldCommand( + HederaId.from(tokenId), + amount, + HederaId.from(escrow), + expirationDate, + targetId ? HederaId.from(targetId) : undefined, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async createHoldByController( request: CreateHoldByControllerRequest, @@ -735,6 +986,27 @@ class StableCoinInPort implements IStableCoinInPort { } as CreateHoldTransactionResult; } + @LogError + async buildCreateHoldByController( + request: CreateHoldByControllerRequest, + ): Promise { + handleValidation(CreateHoldByControllerRequest.name, request); + const { tokenId, targetId, sourceId, amount, expirationDate, escrow } = + request; + const response = await this.commandBus.execute( + new CreateHoldByControllerCommand( + HederaId.from(tokenId), + HederaId.from(sourceId), + amount, + HederaId.from(escrow), + expirationDate, + targetId ? HederaId.from(targetId) : undefined, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async executeHold(request: ExecuteHoldRequest): Promise { handleValidation(ExecuteHoldRequest.name, request); @@ -751,6 +1023,23 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildExecuteHold(request: ExecuteHoldRequest): Promise { + handleValidation(ExecuteHoldRequest.name, request); + const { tokenId, targetId, amount, sourceId, holdId } = request; + const response = await this.commandBus.execute( + new ExecuteHoldCommand( + HederaId.from(tokenId), + holdId, + HederaId.from(sourceId), + amount, + targetId ? HederaId.from(targetId) : undefined, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async releaseHold(request: ReleaseHoldRequest): Promise { handleValidation(ReleaseHoldRequest.name, request); @@ -766,6 +1055,22 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildReleaseHold(request: ReleaseHoldRequest): Promise { + handleValidation(ReleaseHoldRequest.name, request); + const { tokenId, amount, sourceId, holdId } = request; + const response = await this.commandBus.execute( + new ReleaseHoldCommand( + HederaId.from(tokenId), + holdId, + HederaId.from(sourceId), + amount, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async reclaimHold(request: ReclaimHoldRequest): Promise { handleValidation(ReclaimHoldRequest.name, request); @@ -780,6 +1085,21 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildReclaimHold(request: ReclaimHoldRequest): Promise { + handleValidation(ReclaimHoldRequest.name, request); + const { tokenId, sourceId, holdId } = request; + const response = await this.commandBus.execute( + new ReclaimHoldCommand( + HederaId.from(tokenId), + holdId, + HederaId.from(sourceId), + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async getHoldFor(request: GetHoldForRequest): Promise { handleValidation(GetHoldForRequest.name, request); @@ -883,6 +1203,29 @@ class StableCoinInPort implements IStableCoinInPort { return new TransactionResult(response.payload, response.transactionId); } + @LogError + async buildTransfers(request: TransfersRequest): Promise { + const { tokenId, targetsId, amounts, targetId } = request; + + handleValidation('TransfersRequest', request); + + const targetsIdHederaIds: HederaId[] = []; + targetsId.forEach((targetId) => { + targetsIdHederaIds.push(HederaId.from(targetId)); + }); + + const response = await this.commandBus.execute( + new TransfersCommand( + amounts, + targetsIdHederaIds, + HederaId.from(tokenId), + HederaId.from(targetId), + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + @LogError async update(request: UpdateRequest): Promise { const { @@ -905,7 +1248,9 @@ class StableCoinInPort implements IStableCoinInPort { name, symbol, autoRenewPeriod ? Number(autoRenewPeriod) : undefined, - expirationTimestamp ? Number(expirationTimestamp) : undefined, + expirationTimestamp + ? Number(expirationTimestamp) + : undefined, kycKey ? new PublicKey({ key: kycKey.key, @@ -943,30 +1288,84 @@ class StableCoinInPort implements IStableCoinInPort { } @LogError - async signTransaction( - request: SignTransactionRequest, - ): Promise { + async buildUpdate(request: UpdateRequest): Promise { + const { + tokenId, + name, + symbol, + autoRenewPeriod, + expirationTimestamp, + kycKey, + freezeKey, + feeScheduleKey, + pauseKey, + wipeKey, + metadata, + } = request; + handleValidation('UpdateRequest', request); + const response = await this.commandBus.execute( + new UpdateCommand( + HederaId.from(tokenId), + name, + symbol, + autoRenewPeriod ? Number(autoRenewPeriod) : undefined, + expirationTimestamp + ? Number(expirationTimestamp) + : undefined, + kycKey + ? new PublicKey({ + key: kycKey.key, + type: kycKey.type, + }) + : undefined, + freezeKey + ? new PublicKey({ + key: freezeKey.key, + type: freezeKey.type, + }) + : undefined, + feeScheduleKey + ? new PublicKey({ + key: feeScheduleKey.key, + type: feeScheduleKey.type, + }) + : undefined, + pauseKey + ? new PublicKey({ + key: pauseKey.key, + type: pauseKey.type, + }) + : undefined, + wipeKey + ? new PublicKey({ + key: wipeKey.key, + type: wipeKey.type, + }) + : undefined, + metadata, + ), + ); + if (!response.serializedTransactionData) throw new EmptyResponse("buildTransaction"); + return response.serializedTransactionData; + } + + @LogError + async signTransaction(request: SignTransactionRequest): Promise { const { transactionId } = request; handleValidation('SignTransactionRequest', request); - const response = await this.commandBus.execute( - new SignCommand(transactionId), - ); + const response = await this.commandBus.execute(new SignCommand(transactionId)); return new TransactionResult(response.payload, response.transactionId); } @LogError - async submitTransaction( - request: SubmitTransactionRequest, - ): Promise { + async submitTransaction(request: SubmitTransactionRequest): Promise { const { transactionId } = request; handleValidation('SubmitTransactionRequest', request); - const response = await this.commandBus.execute( - new SubmitCommand(transactionId), - ); + const response = await this.commandBus.execute(new SubmitCommand(transactionId)); return new TransactionResult(response.payload, response.transactionId); } @@ -978,9 +1377,7 @@ class StableCoinInPort implements IStableCoinInPort { handleValidation('RemoveTransactionRequest', request); - const response = await this.commandBus.execute( - new RemoveCommand(transactionId), - ); + const response = await this.commandBus.execute(new RemoveCommand(transactionId)); return new TransactionResult(response.payload, response.transactionId); } diff --git a/packages/sdk/src/port/in/index.ts b/packages/sdk/src/port/in/index.ts index 137b96413..585b4b4bd 100644 --- a/packages/sdk/src/port/in/index.ts +++ b/packages/sdk/src/port/in/index.ts @@ -50,3 +50,7 @@ export * from './Common.js'; export * from './ReserveDataFeed.js'; export * from './CustomFees.js'; export * from './Management.js'; +export type { + SerializedTransactionData, + TransactionMetadata, +} from '../../domain/context/transaction/TransactionResponse.js'; diff --git a/packages/sdk/src/port/in/request/ConnectRequest.ts b/packages/sdk/src/port/in/request/ConnectRequest.ts index 2deaaab97..0916ee6b9 100644 --- a/packages/sdk/src/port/in/request/ConnectRequest.ts +++ b/packages/sdk/src/port/in/request/ConnectRequest.ts @@ -72,6 +72,10 @@ export type CustodialSettings = | FireblocksConfigRequest | AWSKMSConfigRequest; +export interface ExternalWalletSettings { + validStartOffsetMinutes?: number; +} + export default class ConnectRequest extends ValidatedRequest implements BaseRequest @@ -85,6 +89,7 @@ export default class ConnectRequest custodialWalletSettings?: CustodialSettings; consensusNodes?: ConsensusNode[]; hwcSettings?: HWCRequestSettings; + externalWalletSettings?: ExternalWalletSettings; constructor({ account, @@ -95,6 +100,7 @@ export default class ConnectRequest custodialWalletSettings, consensusNodes, hwcSettings, + externalWalletSettings, }: { account?: RequestAccount; network: Environment; @@ -104,6 +110,7 @@ export default class ConnectRequest custodialWalletSettings?: CustodialSettings; consensusNodes?: ConsensusNode[]; hwcSettings?: HWCRequestSettings; + externalWalletSettings?: ExternalWalletSettings; }) { super({ account: Validation.checkAccount(), @@ -117,5 +124,6 @@ export default class ConnectRequest this.custodialWalletSettings = custodialWalletSettings; this.consensusNodes = consensusNodes; this.hwcSettings = hwcSettings; + this.externalWalletSettings = externalWalletSettings; } } diff --git a/packages/sdk/src/port/out/TransactionAdapter.ts b/packages/sdk/src/port/out/TransactionAdapter.ts index 8d7e51507..4bad128e0 100644 --- a/packages/sdk/src/port/out/TransactionAdapter.ts +++ b/packages/sdk/src/port/out/TransactionAdapter.ts @@ -71,7 +71,14 @@ interface ITransactionAdapter { reserveInitialAmount?: BigDecimal, ): Promise; init(): Promise; - register(account?: Account): Promise; + register( + input?: + | Account + | FireblocksSettings + | DfnsSettings + | AWSKMSSettings + | HWCSettings, + ): Promise; stop(): Promise; associateToken( tokenId: HederaId, diff --git a/packages/sdk/src/port/out/hs/EvmAddressResolver.ts b/packages/sdk/src/port/out/hs/EvmAddressResolver.ts index 59f4870e9..fe70a431a 100644 --- a/packages/sdk/src/port/out/hs/EvmAddressResolver.ts +++ b/packages/sdk/src/port/out/hs/EvmAddressResolver.ts @@ -38,6 +38,9 @@ export class EvmAddressResolver { async resolve(parameter: ContractId | HederaId | string): Promise { if (parameter instanceof ContractId) { + if (parameter.value == HederaId.NULL.value) { + return EVM_ZERO_ADDRESS; + } return ( await this.getMirrorNode().getContractInfo(parameter.toString()) ).evmAddress.toString(); diff --git a/packages/sdk/src/port/out/hs/external/ExternalEVMTransactionAdapter.ts b/packages/sdk/src/port/out/hs/external/ExternalEVMTransactionAdapter.ts new file mode 100644 index 000000000..4e2200963 --- /dev/null +++ b/packages/sdk/src/port/out/hs/external/ExternalEVMTransactionAdapter.ts @@ -0,0 +1,241 @@ +/* + * + * Hedera Stablecoin SDK + * + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Transaction, ContractExecuteTransaction } from '@hiero-ledger/sdk'; +import { ethers } from 'ethers'; +import { singleton } from 'tsyringe'; +import { lazyInject } from '../../../../core/decorator/LazyInjectDecorator.js'; +import { BaseHederaTransactionAdapter } from '../BaseHederaTransactionAdapter.js'; +import TransactionResponse, { + TransactionMetadata, +} from '../../../../domain/context/transaction/TransactionResponse.js'; +import { SupportedWallets } from '../../../../domain/context/network/Wallet.js'; +import { TransactionType } from '../../TransactionResponseEnums.js'; +import NetworkService from '../../../../app/service/NetworkService.js'; +import { MirrorNodeAdapter } from '../../mirror/MirrorNodeAdapter.js'; +import Account from '../../../../domain/context/account/Account.js'; +import { InitializationData } from '../../TransactionAdapter.js'; +import Injectable from '../../../../core/Injectable.js'; +import { + WalletEvents, + WalletPairedEvent, +} from '../../../../app/service/event/WalletEvent.js'; +import LogService from '../../../../app/service/LogService.js'; +import EventService from '../../../../app/service/event/EventService.js'; + +const HEDERA_CHAIN_IDS: Record = { + mainnet: 295, + testnet: 296, + previewnet: 297, +}; + +@singleton() +export class ExternalEVMTransactionAdapter extends BaseHederaTransactionAdapter { + private account: Account; + + constructor( + @lazyInject(EventService) + public readonly eventService: EventService, + @lazyInject(MirrorNodeAdapter) + public readonly mirrorNodeAdapter: MirrorNodeAdapter, + @lazyInject(NetworkService) + public readonly networkService: NetworkService, + ) { + super(); + } + + public setExternalWalletSettings(_offsetMinutes = 0): void { + // EVM transactions don't use Hedera TransactionId validity window + } + + public async processTransaction( + tx: Transaction, + _transactionType: TransactionType, + _startDate?: string, + ): Promise { + if (!(tx instanceof ContractExecuteTransaction)) { + return new TransactionResponse( + undefined, + undefined, + new Error( + `ExternalEVMTransactionAdapter only supports ContractExecuteTransaction. ` + + `Received: ${tx.constructor.name}. ` + + `Use ExternalHederaTransactionAdapter for native HTS operations.`, + ), + ); + } + return this.serializeEvmTransaction(tx); + } + + private async serializeEvmTransaction( + tx: ContractExecuteTransaction, + ): Promise { + try { + const contractId = tx.contractId; + if (!contractId) throw new Error('Contract ID is missing'); + if (!tx.functionParameters) + throw new Error('Function parameters are missing'); + + // Try to get EVM address from mirror node; fall back to long-zero computation + // for HTS tokens (not stored in the /contracts mirror node endpoint). + let toAddress: string | undefined; + try { + const contractInfo = + await this.mirrorNodeAdapter.getContractInfo( + contractId.toString(), + ); + toAddress = contractInfo.evmAddress; + } catch { + // Not a deployed contract (e.g., HTS token) β€” compute long-zero EVM address + } + if (!toAddress) { + const parts = contractId.toString().split('.'); + if (parts.length === 3) { + const num = BigInt(parts[2]); + toAddress = '0x' + num.toString(16).padStart(40, '0'); + } else { + throw new Error( + `Cannot determine EVM address for contract ${contractId}`, + ); + } + } + + const chainId = + HEDERA_CHAIN_IDS[this.networkService.environment] ?? 296; + + const evmTx = { + to: toAddress, + data: + '0x' + + Buffer.from(tx.functionParameters).toString('hex'), + value: tx.payableAmount + ? ethers.parseEther(tx.payableAmount.toString()) + : 0n, + gasLimit: tx.gas?.toNumber() ?? 1_000_000, + chainId, + }; + + const serializedBytes = + ethers.Transaction.from(evmTx).unsignedSerialized; + + const metadata: TransactionMetadata = { + transactionType: 'EVM Contract Call', + description: `Contract call to ${toAddress}`, + requiredSigners: [this.account.id.toString()], + }; + + return new TransactionResponse( + undefined, + undefined, + undefined, + { + serializedTransaction: serializedBytes, + metadata, + }, + ); + } catch (error) { + return new TransactionResponse( + undefined, + undefined, + error as Error, + ); + } + } + + public getAccount(): Account { + return this.account; + } + + public supportsEvmOperations(): boolean { + return true; + } + + public getNetworkService(): NetworkService { + return this.networkService; + } + + public getMirrorNodeAdapter(): MirrorNodeAdapter { + return this.mirrorNodeAdapter; + } + + public getSupportedWallet(): SupportedWallets { + return SupportedWallets.EXTERNAL_EVM; + } + + init(): Promise { + this.eventService.emit(WalletEvents.walletInit, { + wallet: SupportedWallets.EXTERNAL_EVM, + initData: {}, + }); + LogService.logTrace('ExternalEVMTransactionAdapter Initialized'); + return Promise.resolve(this.networkService.environment); + } + + async register(account: Account): Promise { + Injectable.registerTransactionHandler(this); + + LogService.logTrace('ExternalEVMTransactionAdapter: Getting account info for', account.id.toString()); + const accountMirror = await this.mirrorNodeAdapter.getAccountInfo( + account.id, + ); + LogService.logTrace('ExternalEVMTransactionAdapter: accountMirror =', accountMirror); + + this.account = account; + this.account.publicKey = accountMirror.publicKey; + this.account.evmAddress = accountMirror.accountEvmAddress; + + LogService.logTrace( + 'ExternalEVMTransactionAdapter: Account evmAddress set to: ', + this.account.evmAddress, + ); + + const eventData: WalletPairedEvent = { + wallet: SupportedWallets.EXTERNAL_EVM, + data: { + account: this.account, + pairing: '', + topic: '', + }, + network: { + name: this.networkService.environment, + recognized: true, + factoryId: this.networkService.configuration + ? this.networkService.configuration.factoryAddress + : '', + }, + }; + this.eventService.emit(WalletEvents.walletPaired, eventData); + LogService.logTrace( + 'ExternalEVMTransactionAdapter registered as handler: ', + eventData, + ); + return Promise.resolve({ + account: this.getAccount(), + }); + } + + stop(): Promise { + LogService.logTrace('ExternalEVMTransactionAdapter stopped'); + this.eventService.emit(WalletEvents.walletDisconnect, { + wallet: SupportedWallets.EXTERNAL_EVM, + }); + return Promise.resolve(true); + } +} diff --git a/packages/sdk/src/port/out/hs/external/ExternalHederaTransactionAdapter.ts b/packages/sdk/src/port/out/hs/external/ExternalHederaTransactionAdapter.ts new file mode 100644 index 000000000..16d547642 --- /dev/null +++ b/packages/sdk/src/port/out/hs/external/ExternalHederaTransactionAdapter.ts @@ -0,0 +1,190 @@ +/* + * + * Hedera Stablecoin SDK + * + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { + Transaction, + TransactionId, + AccountId, + Timestamp, + Client, +} from '@hiero-ledger/sdk'; +import { singleton } from 'tsyringe'; +import { lazyInject } from '../../../../core/decorator/LazyInjectDecorator.js'; +import { BaseHederaTransactionAdapter } from '../BaseHederaTransactionAdapter.js'; +import TransactionResponse, { + TransactionMetadata, +} from '../../../../domain/context/transaction/TransactionResponse.js'; +import { SupportedWallets } from '../../../../domain/context/network/Wallet.js'; +import { TransactionType } from '../../TransactionResponseEnums.js'; +import NetworkService from '../../../../app/service/NetworkService.js'; +import { MirrorNodeAdapter } from '../../mirror/MirrorNodeAdapter.js'; +import Account from '../../../../domain/context/account/Account.js'; +import { InitializationData } from '../../TransactionAdapter.js'; +import Injectable from '../../../../core/Injectable.js'; +import { + WalletEvents, + WalletPairedEvent, +} from '../../../../app/service/event/WalletEvent.js'; +import LogService from '../../../../app/service/LogService.js'; +import EventService from '../../../../app/service/event/EventService.js'; + +@singleton() +export class ExternalHederaTransactionAdapter extends BaseHederaTransactionAdapter { + private account: Account; + private validStartOffsetMinutes = 0; + + constructor( + @lazyInject(EventService) + public readonly eventService: EventService, + @lazyInject(MirrorNodeAdapter) + public readonly mirrorNodeAdapter: MirrorNodeAdapter, + @lazyInject(NetworkService) + public readonly networkService: NetworkService, + ) { + super(); + } + + public setExternalWalletSettings(offsetMinutes = 0): void { + this.validStartOffsetMinutes = offsetMinutes; + } + + public async processTransaction( + tx: Transaction, + _transactionType: TransactionType, + startDate?: string, + ): Promise { + try { + const accountId = AccountId.fromString( + this.account.id.toString(), + ); + + const validStartDate = startDate + ? new Date(startDate) + : new Date( + Date.now() + + this.validStartOffsetMinutes * 60000, + ); + + const txId = TransactionId.withValidStart( + accountId, + Timestamp.fromDate(validStartDate), + ); + tx.setTransactionId(txId).setTransactionValidDuration(180); + + const env = this.networkService.environment; + let client: Client; + if (env === 'mainnet') client = Client.forMainnet(); + else if (env === 'previewnet') client = Client.forPreviewnet(); + else client = Client.forTestnet(); + + const serializedBytes = Buffer.from( + tx.freezeWith(client).toBytes(), + ).toString('hex'); + + const metadata: TransactionMetadata = { + transactionType: tx.constructor.name, + description: `${tx.constructor.name} operation`, + requiredSigners: [this.account.id.toString()], + }; + + return new TransactionResponse(undefined, undefined, undefined, { + serializedTransaction: serializedBytes, + metadata, + }); + } catch (error) { + return new TransactionResponse( + undefined, + undefined, + error as Error, + ); + } + } + + public getAccount(): Account { + return this.account; + } + + public supportsEvmOperations(): boolean { + return false; + } + + public getNetworkService(): NetworkService { + return this.networkService; + } + + public getMirrorNodeAdapter(): MirrorNodeAdapter { + return this.mirrorNodeAdapter; + } + + public getSupportedWallet(): SupportedWallets { + return SupportedWallets.EXTERNAL_HEDERA; + } + + init(): Promise { + this.eventService.emit(WalletEvents.walletInit, { + wallet: SupportedWallets.EXTERNAL_HEDERA, + initData: {}, + }); + LogService.logTrace('ExternalHederaTransactionAdapter Initialized'); + return Promise.resolve(this.networkService.environment); + } + + async register(account: Account): Promise { + Injectable.registerTransactionHandler(this); + + const accountMirror = await this.mirrorNodeAdapter.getAccountInfo( + account.id, + ); + this.account = account; + this.account.publicKey = accountMirror.publicKey; + + const eventData: WalletPairedEvent = { + wallet: SupportedWallets.EXTERNAL_HEDERA, + data: { + account: this.account, + pairing: '', + topic: '', + }, + network: { + name: this.networkService.environment, + recognized: true, + factoryId: this.networkService.configuration + ? this.networkService.configuration.factoryAddress + : '', + }, + }; + this.eventService.emit(WalletEvents.walletPaired, eventData); + LogService.logTrace( + 'ExternalHederaTransactionAdapter registered as handler: ', + eventData, + ); + return Promise.resolve({ + account: this.getAccount(), + }); + } + + stop(): Promise { + LogService.logTrace('ExternalHederaTransactionAdapter stopped'); + this.eventService.emit(WalletEvents.walletDisconnect, { + wallet: SupportedWallets.EXTERNAL_HEDERA, + }); + return Promise.resolve(true); + } +} diff --git a/packages/sdk/src/port/out/hs/operations/TokenOperations.ts b/packages/sdk/src/port/out/hs/operations/TokenOperations.ts index 6f630f0b9..2e633e97a 100644 --- a/packages/sdk/src/port/out/hs/operations/TokenOperations.ts +++ b/packages/sdk/src/port/out/hs/operations/TokenOperations.ts @@ -28,7 +28,6 @@ import { } from '@hiero-ledger/sdk'; import { UINT256_MAX } from '../../../../core/Constants'; import { ethers } from 'ethers'; -import CheckEvmAddress from '../../../../core/checks/evmaddress/CheckEvmAddress'; import TransactionResponse from '../../../../domain/context/transaction/TransactionResponse'; import StableCoinCapabilities from '../../../../domain/context/stablecoin/StableCoinCapabilities'; import { HederaId } from '../../../../domain/context/shared/HederaId'; @@ -239,13 +238,11 @@ export class TokenOperations { // Check if EVM operations are supported and account has EVM address if (this.executor.supportsEvmOperations() && account.evmAddress) { - // EVM path - use IHRC.associate on the token contract - const tokenEvm = CheckEvmAddress.toEvmAddress( - tokenId.toHederaAddress().toSolidityAddress(), - ); - + // EVM path - use IHRC.associate on the token contract. + // Pass the Hedera ID format so ContractId.fromString() succeeds. + // ExternalEVMTransactionAdapter resolves the EVM address via getContractInfo(). return await this.executor.executeContractCall( - tokenEvm, + tokenId.toString(), new ethers.Interface(IHRC__factory.abi), 'associate', [], diff --git a/packages/sdk/src/port/out/hs/operations/UpdateOperations.ts b/packages/sdk/src/port/out/hs/operations/UpdateOperations.ts index 8b4f2761f..f93f65524 100644 --- a/packages/sdk/src/port/out/hs/operations/UpdateOperations.ts +++ b/packages/sdk/src/port/out/hs/operations/UpdateOperations.ts @@ -186,6 +186,7 @@ export class UpdateOperations { params, UPDATE_CONFIG_VERSION_GAS, undefined, + undefined, startDate, evmAddress, );