From 57961a4d1aa104ac6e1fcfbe3aba9ea765a10e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Abadesso?= Date: Tue, 23 Sep 2025 18:46:19 -0300 Subject: [PATCH 1/3] feat(wallet-service): returning nanoContractsEnabled on version api --- packages/common/src/types.ts | 16 ++++----- packages/wallet-service/src/nodeConfig.ts | 1 + packages/wallet-service/src/schemas.ts | 1 + packages/wallet-service/src/types.ts | 2 ++ packages/wallet-service/tests/api.test.ts | 1 + packages/wallet-service/tests/commons.test.ts | 2 ++ packages/wallet-service/tests/db.test.ts | 2 ++ .../wallet-service/tests/nodeConfig.test.ts | 34 ++++++++++++++++++- .../wallet-service/tests/txProposal.test.ts | 26 ++++++++++++++ 9 files changed, 76 insertions(+), 9 deletions(-) diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 3ad47c8f..e0376b6f 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -82,11 +82,11 @@ export type DecodedOutput = { } export interface TxNanoHeader { - id: string; - nc_seqnum: number; - nc_id: string; - nc_method: string; - nc_address: string; + id: string; + nc_seqnum: number; + nc_id: string; + nc_method: string; + nc_address: string; } export type TxHeader = TxNanoHeader; @@ -227,9 +227,9 @@ export class Balance { totalAmountSent = 0n, unlockedAmount = 0n, lockedAmount = 0n, - lockExpires: number|null = null, - unlockedAuthorities: Authorities|null = null, - lockedAuthorities: Authorities|null = null + lockExpires: number | null = null, + unlockedAuthorities: Authorities | null = null, + lockedAuthorities: Authorities | null = null ) { this.totalAmountSent = totalAmountSent; this.unlockedAmount = unlockedAmount; diff --git a/packages/wallet-service/src/nodeConfig.ts b/packages/wallet-service/src/nodeConfig.ts index 4038dfba..876288f7 100644 --- a/packages/wallet-service/src/nodeConfig.ts +++ b/packages/wallet-service/src/nodeConfig.ts @@ -41,6 +41,7 @@ export function convertApiVersionData(data: FullNodeApiVersionResponse): FullNod return { version: data.version, network: data.network, + nanoContractsEnabled: data.nano_contracts_enabled ?? false, minWeight: data.min_weight, minTxWeight: data.min_tx_weight, minTxWeightCoefficient: data.min_tx_weight_coefficient, diff --git a/packages/wallet-service/src/schemas.ts b/packages/wallet-service/src/schemas.ts index 949d795f..8bca7225 100644 --- a/packages/wallet-service/src/schemas.ts +++ b/packages/wallet-service/src/schemas.ts @@ -13,6 +13,7 @@ export const Sha256Schema = Joi.string().hex().length(64); export const FullnodeVersionSchema = Joi.object({ version: Joi.string().min(1).required(), network: Joi.string().min(1).required(), + nano_contracts_enabled: Joi.boolean().required(), min_weight: Joi.number().integer().positive().allow(0).required(), min_tx_weight: Joi.number().integer().positive().allow(0).required(), min_tx_weight_coefficient: Joi.number().positive().allow(0).required(), diff --git a/packages/wallet-service/src/types.ts b/packages/wallet-service/src/types.ts index 64ce266e..909a9328 100644 --- a/packages/wallet-service/src/types.ts +++ b/packages/wallet-service/src/types.ts @@ -88,6 +88,7 @@ export interface EnvironmentConfig { export interface FullNodeVersionData { version: string; network: string; + nanoContractsEnabled: boolean; minWeight: number; minTxWeight: number; minTxWeightCoefficient: number; @@ -107,6 +108,7 @@ export interface FullNodeVersionData { export interface FullNodeApiVersionResponse { version: string; network: string; + nano_contracts_enabled?: boolean; min_weight: number; min_tx_weight: number; min_tx_weight_coefficient: number; // float diff --git a/packages/wallet-service/tests/api.test.ts b/packages/wallet-service/tests/api.test.ts index 965bb0f7..2417d74d 100644 --- a/packages/wallet-service/tests/api.test.ts +++ b/packages/wallet-service/tests/api.test.ts @@ -1674,6 +1674,7 @@ test('GET /version', async () => { const mockData: FullNodeApiVersionResponse = { version: '0.38.0', network: 'mainnet', + nano_contracts_enabled: true, min_weight: 14, min_tx_weight: 14, min_tx_weight_coefficient: 1.6, diff --git a/packages/wallet-service/tests/commons.test.ts b/packages/wallet-service/tests/commons.test.ts index 8fa2eb18..ede095f7 100644 --- a/packages/wallet-service/tests/commons.test.ts +++ b/packages/wallet-service/tests/commons.test.ts @@ -519,6 +519,7 @@ test('getFullnodeData with an uninitialized version_data database should call th const mockData = { version: '0.38.0', network: 'mainnet', + nano_contracts_enabled: true, min_weight: 14, min_tx_weight: 14, min_tx_weight_coefficient: 1.6, @@ -561,6 +562,7 @@ test('getFullnodeData with an initialized version_data database should query dat const mockedVersionData: FullNodeApiVersionResponse = { version: '0.38.0', network: 'mainnet', + nano_contracts_enabled: true, min_weight: 14, min_tx_weight: 14, min_tx_weight_coefficient: 1.6, diff --git a/packages/wallet-service/tests/db.test.ts b/packages/wallet-service/tests/db.test.ts index 4eab3c25..3404c43a 100644 --- a/packages/wallet-service/tests/db.test.ts +++ b/packages/wallet-service/tests/db.test.ts @@ -1614,6 +1614,7 @@ test('updateVersionData', async () => { const mockData: FullNodeApiVersionResponse = { version: '0.38.0', network: 'mainnet', + nano_contracts_enabled: true, min_weight: 14, min_tx_weight: 14, min_tx_weight_coefficient: 1.6, @@ -1655,6 +1656,7 @@ test('getVersionData', async () => { const mockData: FullNodeApiVersionResponse = { version: '0.38.0', network: 'mainnet', + nano_contracts_enabled: true, min_weight: 14, min_tx_weight: 14, min_tx_weight_coefficient: 1.6, diff --git a/packages/wallet-service/tests/nodeConfig.test.ts b/packages/wallet-service/tests/nodeConfig.test.ts index 92352adb..a6c8cef8 100644 --- a/packages/wallet-service/tests/nodeConfig.test.ts +++ b/packages/wallet-service/tests/nodeConfig.test.ts @@ -10,8 +10,9 @@ import { convertApiVersionData, getRawFullnodeData } from '@src/nodeConfig'; const mysql = getDbConnection(); const VERSION_DATA: FullNodeApiVersionResponse = { - version: '0.63.1', + version: '0.65.2-alpha.1', network: 'mainnet', + nano_contracts_enabled: true, min_weight: 14, min_tx_weight: 14, min_tx_weight_coefficient: 1.6, @@ -63,6 +64,7 @@ test('convertApiVersionData', async () => { expect(convertApiVersionData(OLD_VERSION_DATA)).toStrictEqual({ version: OLD_VERSION_DATA.version, network: OLD_VERSION_DATA.network, + nanoContractsEnabled: false, minWeight: OLD_VERSION_DATA.min_weight, minTxWeight: OLD_VERSION_DATA.min_tx_weight, minTxWeightCoefficient: OLD_VERSION_DATA.min_tx_weight_coefficient, @@ -79,6 +81,7 @@ test('convertApiVersionData', async () => { expect(convertApiVersionData(VERSION_DATA)).toStrictEqual({ version: VERSION_DATA.version, network: VERSION_DATA.network, + nanoContractsEnabled: VERSION_DATA.nano_contracts_enabled, minWeight: VERSION_DATA.min_weight, minTxWeight: VERSION_DATA.min_tx_weight, minTxWeightCoefficient: VERSION_DATA.min_tx_weight_coefficient, @@ -92,3 +95,32 @@ test('convertApiVersionData', async () => { nativeTokenSymbol: VERSION_DATA.native_token.symbol, }); }); + +test('convertApiVersionData handles nano_contracts_enabled correctly', async () => { + // Test with nano_contracts_enabled = false + const versionWithNanoFalse: FullNodeApiVersionResponse = { + ...OLD_VERSION_DATA, + nano_contracts_enabled: false, + }; + expect(convertApiVersionData(versionWithNanoFalse).nanoContractsEnabled).toBe(false); + + // Test with nano_contracts_enabled = true + const versionWithNanoTrue: FullNodeApiVersionResponse = { + ...OLD_VERSION_DATA, + nano_contracts_enabled: true, + }; + expect(convertApiVersionData(versionWithNanoTrue).nanoContractsEnabled).toBe(true); + + // Test with nano_contracts_enabled = undefined (should default to false) + const versionWithNanoUndefined: FullNodeApiVersionResponse = { + ...OLD_VERSION_DATA, + nano_contracts_enabled: undefined, + }; + expect(convertApiVersionData(versionWithNanoUndefined).nanoContractsEnabled).toBe(false); + + // Test without nano_contracts_enabled field (should default to false) + const versionWithoutNano: FullNodeApiVersionResponse = { + ...OLD_VERSION_DATA, + }; + expect(convertApiVersionData(versionWithoutNano).nanoContractsEnabled).toBe(false); +}); diff --git a/packages/wallet-service/tests/txProposal.test.ts b/packages/wallet-service/tests/txProposal.test.ts index 718a57eb..b49a6405 100644 --- a/packages/wallet-service/tests/txProposal.test.ts +++ b/packages/wallet-service/tests/txProposal.test.ts @@ -47,6 +47,7 @@ beforeEach(async () => { max_number_inputs: 255, max_number_outputs: 255, decimal_places: 2, + nano_contracts_enabled: true, genesis_block_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', genesis_tx1_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', genesis_tx2_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', @@ -216,6 +217,7 @@ test('POST /txproposals with too many outputs should fail with ApiError.TOO_MANY token_deposit_percentage: 0.01, reward_spend_min_blocks: 300, max_number_inputs: 255, + nano_contracts_enabled: true, max_number_outputs: 2, // mocking to force a failure decimal_places: 2, genesis_block_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', @@ -769,6 +771,7 @@ test('PUT /txproposals/{proposalId} with an invalid txHex should fail and update success: true, version: '0.38.0', network: 'mainnet', + nano_contracts_enabled: true, min_weight: 14, min_tx_weight: 14, min_tx_weight_coefficient: 1.6, @@ -777,6 +780,11 @@ test('PUT /txproposals/{proposalId} with an invalid txHex should fail and update reward_spend_min_blocks: 300, max_number_inputs: 255, max_number_outputs: 255, + decimal_places: 2, + genesis_block_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + genesis_tx1_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + genesis_tx2_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + native_token: { name: 'Hathor', symbol: 'HTR' }, }, }), }); @@ -913,6 +921,7 @@ test('PUT /txproposals/{proposalId} should update tx_proposal to SEND_ERROR on f success: true, version: '0.38.0', network: 'mainnet', + nano_contracts_enabled: true, min_weight: 14, min_tx_weight: 14, min_tx_weight_coefficient: 1.6, @@ -921,6 +930,11 @@ test('PUT /txproposals/{proposalId} should update tx_proposal to SEND_ERROR on f reward_spend_min_blocks: 300, max_number_inputs: 255, max_number_outputs: 255, + decimal_places: 2, + genesis_block_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + genesis_tx1_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + genesis_tx2_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + native_token: { name: 'Hathor', symbol: 'HTR' }, }, }), }); @@ -1551,6 +1565,7 @@ test('PUT /txproposals/{proposalId} with txhex', async () => { success: true, version: '0.38.0', network: 'mainnet', + nano_contracts_enabled: true, min_weight: 14, min_tx_weight: 14, min_tx_weight_coefficient: 1.6, @@ -1559,6 +1574,11 @@ test('PUT /txproposals/{proposalId} with txhex', async () => { reward_spend_min_blocks: 300, max_number_inputs: 255, max_number_outputs: 255, + decimal_places: 2, + genesis_block_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + genesis_tx1_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + genesis_tx2_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + native_token: { name: 'Hathor', symbol: 'HTR' }, }, }), }); @@ -1693,6 +1713,7 @@ test('PUT /txproposals/{proposalId} with a different txhex than the one sent in success: true, version: '0.38.0', network: 'mainnet', + nano_contracts_enabled: true, min_weight: 14, min_tx_weight: 14, min_tx_weight_coefficient: 1.6, @@ -1701,6 +1722,11 @@ test('PUT /txproposals/{proposalId} with a different txhex than the one sent in reward_spend_min_blocks: 300, max_number_inputs: 255, max_number_outputs: 255, + decimal_places: 2, + genesis_block_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + genesis_tx1_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + genesis_tx2_hash: 'cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe', + native_token: { name: 'Hathor', symbol: 'HTR' }, }, }), }); From db26b6f4fb47049ccc913b29e551eb4e53c4abec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Abadesso?= Date: Thu, 25 Sep 2025 15:39:44 -0300 Subject: [PATCH 2/3] fix(wallet-service): feature activation strings handling --- packages/wallet-service/src/nodeConfig.ts | 2 +- packages/wallet-service/src/schemas.ts | 5 ++++- packages/wallet-service/src/types.ts | 2 +- .../wallet-service/tests/nodeConfig.test.ts | 21 +++++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/wallet-service/src/nodeConfig.ts b/packages/wallet-service/src/nodeConfig.ts index 876288f7..f0b06244 100644 --- a/packages/wallet-service/src/nodeConfig.ts +++ b/packages/wallet-service/src/nodeConfig.ts @@ -41,7 +41,7 @@ export function convertApiVersionData(data: FullNodeApiVersionResponse): FullNod return { version: data.version, network: data.network, - nanoContractsEnabled: data.nano_contracts_enabled ?? false, + nanoContractsEnabled: data.nano_contracts_enabled === true || data.nano_contracts_enabled === 'enabled' || data.nano_contracts_enabled === 'feature_activation', minWeight: data.min_weight, minTxWeight: data.min_tx_weight, minTxWeightCoefficient: data.min_tx_weight_coefficient, diff --git a/packages/wallet-service/src/schemas.ts b/packages/wallet-service/src/schemas.ts index 8bca7225..a7ca5871 100644 --- a/packages/wallet-service/src/schemas.ts +++ b/packages/wallet-service/src/schemas.ts @@ -13,7 +13,10 @@ export const Sha256Schema = Joi.string().hex().length(64); export const FullnodeVersionSchema = Joi.object({ version: Joi.string().min(1).required(), network: Joi.string().min(1).required(), - nano_contracts_enabled: Joi.boolean().required(), + nano_contracts_enabled: Joi.alternatives().try( + Joi.boolean(), + Joi.string().valid('disabled', 'enabled', 'feature_activation') + ).required(), min_weight: Joi.number().integer().positive().allow(0).required(), min_tx_weight: Joi.number().integer().positive().allow(0).required(), min_tx_weight_coefficient: Joi.number().positive().allow(0).required(), diff --git a/packages/wallet-service/src/types.ts b/packages/wallet-service/src/types.ts index 909a9328..b7f65f7d 100644 --- a/packages/wallet-service/src/types.ts +++ b/packages/wallet-service/src/types.ts @@ -108,7 +108,7 @@ export interface FullNodeVersionData { export interface FullNodeApiVersionResponse { version: string; network: string; - nano_contracts_enabled?: boolean; + nano_contracts_enabled?: boolean | 'disabled' | 'enabled' | 'feature_activation'; min_weight: number; min_tx_weight: number; min_tx_weight_coefficient: number; // float diff --git a/packages/wallet-service/tests/nodeConfig.test.ts b/packages/wallet-service/tests/nodeConfig.test.ts index a6c8cef8..064c3d8c 100644 --- a/packages/wallet-service/tests/nodeConfig.test.ts +++ b/packages/wallet-service/tests/nodeConfig.test.ts @@ -111,6 +111,27 @@ test('convertApiVersionData handles nano_contracts_enabled correctly', async () }; expect(convertApiVersionData(versionWithNanoTrue).nanoContractsEnabled).toBe(true); + // Test with nano_contracts_enabled = 'disabled' (should be false) + const versionWithDisabled: FullNodeApiVersionResponse = { + ...OLD_VERSION_DATA, + nano_contracts_enabled: 'disabled', + }; + expect(convertApiVersionData(versionWithDisabled).nanoContractsEnabled).toBe(false); + + // Test with nano_contracts_enabled = 'enabled' (should be true) + const versionWithEnabled: FullNodeApiVersionResponse = { + ...OLD_VERSION_DATA, + nano_contracts_enabled: 'enabled', + }; + expect(convertApiVersionData(versionWithEnabled).nanoContractsEnabled).toBe(true); + + // Test with nano_contracts_enabled = 'feature_activation' (should be true) + const versionWithFeatureActivation: FullNodeApiVersionResponse = { + ...OLD_VERSION_DATA, + nano_contracts_enabled: 'feature_activation', + }; + expect(convertApiVersionData(versionWithFeatureActivation).nanoContractsEnabled).toBe(true); + // Test with nano_contracts_enabled = undefined (should default to false) const versionWithNanoUndefined: FullNodeApiVersionResponse = { ...OLD_VERSION_DATA, From 95a2860eb51a96cb2da26b913bbef6fcf7b93883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Abadesso?= Date: Thu, 25 Sep 2025 15:41:27 -0300 Subject: [PATCH 3/3] docs(wallet-service): add todo to change feature activation to bool --- packages/wallet-service/src/nodeConfig.ts | 4 ++++ packages/wallet-service/src/schemas.ts | 3 +++ packages/wallet-service/src/types.ts | 3 +++ 3 files changed, 10 insertions(+) diff --git a/packages/wallet-service/src/nodeConfig.ts b/packages/wallet-service/src/nodeConfig.ts index f0b06244..eb21cf30 100644 --- a/packages/wallet-service/src/nodeConfig.ts +++ b/packages/wallet-service/src/nodeConfig.ts @@ -41,6 +41,10 @@ export function convertApiVersionData(data: FullNodeApiVersionResponse): FullNod return { version: data.version, network: data.network, + // NOTE: Due to a bug in older fullnode versions, nano_contracts_enabled may return + // string values ('disabled', 'enabled', 'feature_activation') instead of boolean. + // This will be fixed in future fullnode versions to return boolean only. + // Until then, we need to handle both string and boolean values. nanoContractsEnabled: data.nano_contracts_enabled === true || data.nano_contracts_enabled === 'enabled' || data.nano_contracts_enabled === 'feature_activation', minWeight: data.min_weight, minTxWeight: data.min_tx_weight, diff --git a/packages/wallet-service/src/schemas.ts b/packages/wallet-service/src/schemas.ts index a7ca5871..d35f518f 100644 --- a/packages/wallet-service/src/schemas.ts +++ b/packages/wallet-service/src/schemas.ts @@ -13,6 +13,9 @@ export const Sha256Schema = Joi.string().hex().length(64); export const FullnodeVersionSchema = Joi.object({ version: Joi.string().min(1).required(), network: Joi.string().min(1).required(), + // NOTE: Due to a bug in older fullnode versions, this field may be a string + // ('disabled', 'enabled', 'feature_activation') instead of boolean. + // Future fullnode versions will return boolean only. nano_contracts_enabled: Joi.alternatives().try( Joi.boolean(), Joi.string().valid('disabled', 'enabled', 'feature_activation') diff --git a/packages/wallet-service/src/types.ts b/packages/wallet-service/src/types.ts index b7f65f7d..08b3710e 100644 --- a/packages/wallet-service/src/types.ts +++ b/packages/wallet-service/src/types.ts @@ -108,6 +108,9 @@ export interface FullNodeVersionData { export interface FullNodeApiVersionResponse { version: string; network: string; + // NOTE: Due to a bug in older fullnode versions, this field may be a string + // ('disabled', 'enabled', 'feature_activation') instead of boolean. + // Future fullnode versions will return boolean only. nano_contracts_enabled?: boolean | 'disabled' | 'enabled' | 'feature_activation'; min_weight: number; min_tx_weight: number;