From cdd94eaee0373f3fe125351ad7391d90abc9ac33 Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Fri, 5 Apr 2024 09:47:41 +0200 Subject: [PATCH 1/4] core: v2.8.2 --- ios/Xaman.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Xaman.xcodeproj/project.pbxproj b/ios/Xaman.xcodeproj/project.pbxproj index 0f522d1b0..53cb01eb3 100644 --- a/ios/Xaman.xcodeproj/project.pbxproj +++ b/ios/Xaman.xcodeproj/project.pbxproj @@ -1145,7 +1145,7 @@ INFOPLIST_FILE = Xaman/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 2.8.1; + MARKETING_VERSION = 2.8.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1176,7 +1176,7 @@ INFOPLIST_FILE = Xaman/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 2.8.1; + MARKETING_VERSION = 2.8.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", From 146c22b0cd8f49e43e8c39d5767bbc0d3e1399a4 Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Fri, 5 Apr 2024 11:02:19 +0200 Subject: [PATCH 2/4] fix: add NetworkID to PrepareTxForHookFee if necessary --- src/common/utils/__tests__/fee.test.ts | 191 ++++++++++++++++++++----- src/common/utils/fee.ts | 19 ++- src/services/NetworkService.ts | 6 +- 3 files changed, 171 insertions(+), 45 deletions(-) diff --git a/src/common/utils/__tests__/fee.test.ts b/src/common/utils/__tests__/fee.test.ts index 653d20652..c6aea4845 100644 --- a/src/common/utils/__tests__/fee.test.ts +++ b/src/common/utils/__tests__/fee.test.ts @@ -1,45 +1,160 @@ /* eslint-disable spellcheck/spell-checker */ -import { NormalizeFeeDataSet } from '../fee'; +import { NormalizeFeeDataSet, PrepareTxForHookFee } from '../fee'; + +import * as AccountLib from 'xrpl-accountlib'; describe('Utils.Fee', () => { - it('Should return right values', () => { - const baseFees = { - 10: ['12', '15', '25'], - 12: ['12', '15', '25'], - 24: ['24', '30', '45'], - 100: ['100', '150', '200'], - 500: ['500', '600', '900'], - 1200: ['1200', '1500', '2500'], - 10000: ['10000', '15000', '20000'], - 50000: ['50000', '60000', '75000'], - 20000: ['20000', '25000', '35000'], - 800000: ['800000', '850000', '850000'], - }; - - Object.keys(baseFees).forEach((base) => { - expect( - NormalizeFeeDataSet({ - drops: { base_fee: Number(base) }, - fee_hooks_feeunits: 0, - }), - ).toMatchObject({ - availableFees: [ - { - type: 'LOW', - value: baseFees[base][0], - }, - { - type: 'MEDIUM', - value: baseFees[base][1], - }, - { - type: 'HIGH', - value: baseFees[base][2], - }, - ], - feeHooks: 0, - suggested: 'LOW', + describe('NormalizeFeeDataSet', () => { + it('Should return right values', () => { + const baseFees = { + 10: ['12', '15', '25'], + 12: ['12', '15', '25'], + 24: ['24', '30', '45'], + 100: ['100', '150', '200'], + 500: ['500', '600', '900'], + 1200: ['1200', '1500', '2500'], + 10000: ['10000', '15000', '20000'], + 50000: ['50000', '60000', '75000'], + 20000: ['20000', '25000', '35000'], + 800000: ['800000', '850000', '850000'], + }; + + Object.keys(baseFees).forEach((base) => { + expect( + NormalizeFeeDataSet({ + drops: { base_fee: base }, + fee_hooks_feeunits: '0', + }), + ).toMatchObject({ + availableFees: [ + { + type: 'LOW', + value: baseFees[base][0], + }, + { + type: 'MEDIUM', + value: baseFees[base][1], + }, + { + type: 'HIGH', + value: baseFees[base][2], + }, + ], + feeHooks: 0, + suggested: 'LOW', + }); }); }); }); + + describe('PrepareTxForHookFee', () => { + it('Should throw an error if txJson is not a valid object', () => { + expect(() => { + PrepareTxForHookFee(undefined, {}, 0); + }).toThrowError('PrepareTxForHookFee requires a json transaction to calculate the fee for'); + + expect(() => { + PrepareTxForHookFee('invalid', {}, 0); + }).toThrowError('PrepareTxForHookFee requires a json transaction to calculate the fee for'); + }); + + it('Should prepare the txJson correctly for signing', () => { + const txJson = { + Fee: '12', + SigningPubKey: 'SOME_PUB_KEY', + }; + + // mock and spy sign method + const signSpy = jest.spyOn(AccountLib, 'sign'); + + // call the method + PrepareTxForHookFee(txJson, undefined, 1); + + expect(signSpy).toBeCalledWith( + { + Fee: '0', + SigningPubKey: '', + Sequence: 0, + }, + expect.any(Object), + undefined, + ); + + signSpy.mockClear(); + }); + + it('Should include NetworkID if necessary', () => { + const txJson = {}; + + const signSpy = jest.spyOn(AccountLib, 'sign'); + PrepareTxForHookFee(txJson, undefined, 2337); + expect(signSpy).toBeCalledWith( + { + Fee: '0', + SigningPubKey: '', + Sequence: 0, + NetworkID: 2337, + }, + expect.any(Object), + undefined, + ); + signSpy.mockClear(); + }); + + it('Should not include NetworkID if networkId is less than or equal to 1024', () => { + const txJson = {}; + + const signSpy = jest.spyOn(AccountLib, 'sign'); + PrepareTxForHookFee(txJson, undefined, 1024); + expect(signSpy).toBeCalledWith( + { + Fee: '0', + SigningPubKey: '', + Sequence: 0, + }, + expect.any(Object), + undefined, + ); + signSpy.mockClear(); + }); + + it('Should set Amount to 0 if TransactionType is Payment and Amount is not set', () => { + const txJson = { + TransactionType: 'Payment', + }; + + const signSpy = jest.spyOn(AccountLib, 'sign'); + PrepareTxForHookFee(txJson, undefined, 0); + expect(signSpy).toBeCalledWith( + { + ...txJson, + Amount: '0', + Fee: '0', + SigningPubKey: '', + Sequence: 0, + }, + expect.any(Object), + undefined, + ); + signSpy.mockClear(); + }); + + it('Should set xrplDefinitions if definitions is an object', () => { + const txJson = {}; + + const signSpy = jest.spyOn(AccountLib, 'sign'); + PrepareTxForHookFee(txJson, AccountLib.binary.DEFAULT_DEFINITIONS, 0); + expect(signSpy).toBeCalledWith( + { + ...txJson, + Fee: '0', + SigningPubKey: '', + Sequence: 0, + }, + expect.any(Object), + expect.any(Object), + ); + signSpy.mockClear(); + }); + }); }); diff --git a/src/common/utils/fee.ts b/src/common/utils/fee.ts index 8e5b5e56e..180849d8d 100644 --- a/src/common/utils/fee.ts +++ b/src/common/utils/fee.ts @@ -6,14 +6,15 @@ import { sign, derive, XrplDefinitions } from 'xrpl-accountlib'; * Prepare transaction for getting hook tx fee * @param txJson * @param definitions + * @param networkId * @returns string */ -const PrepareTxForHookFee = (txJson: any, definitions: any): string => { +const PrepareTxForHookFee = (txJson: any, definitions: any, networkId: number): string => { if (!txJson || typeof txJson !== 'object') { throw new Error('PrepareTxForHookFee requires a json transaction to calculate the fee for'); } - // normalize the transaction + // Shallow copy txJson // Fee and SigningPubKey should be empty const transaction = { ...txJson, @@ -23,14 +24,24 @@ const PrepareTxForHookFee = (txJson: any, definitions: any): string => { // check if we need to populate the transaction with dummy details // set the Sequence if not set - if (!Object.prototype.hasOwnProperty.call(txJson, 'Sequence')) { + if (!Object.prototype.hasOwnProperty.call(transaction, 'Sequence')) { Object.assign(transaction, { Sequence: 0, }); } + // include Network ID if necessary + if (!Object.prototype.hasOwnProperty.call(transaction, 'NetworkID')) { + // legacy networks have ids less than 1024, these networks cannot specify NetworkID in txn + if (networkId > 1024) { + Object.assign(transaction, { + NetworkID: networkId, + }); + } + } + // Payment payloads can have no amount set - if (txJson.TransactionType === 'Payment' && !txJson.Amount) { + if (transaction.TransactionType === 'Payment' && !transaction.Amount) { Object.assign(transaction, { Amount: '0', }); diff --git a/src/services/NetworkService.ts b/src/services/NetworkService.ts index 789acf9fd..08506912f 100644 --- a/src/services/NetworkService.ts +++ b/src/services/NetworkService.ts @@ -300,7 +300,7 @@ class NetworkService extends EventEmitter { try { const resp = await this.send({ command: 'fee', - tx_blob: PrepareTxForHookFee(txJson, this.network.definitions), + tx_blob: PrepareTxForHookFee(txJson, this.network.definitions, this.network.networkId), }); if ('error' in resp) { @@ -308,8 +308,8 @@ class NetworkService extends EventEmitter { } resolve(NormalizeFeeDataSet(resp)); - } catch (e) { - this.logger.warn('Unable to calculate available network fees:', e); + } catch (error: any) { + this.logger.warn('Unable to calculate available network fees:', error); reject(new Error('Unable to calculate available network fees!')); } }); From 740b04f22629a0db97c456aa97f8b0a5bd8c77fd Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Fri, 5 Apr 2024 11:07:01 +0200 Subject: [PATCH 3/4] chore(android): v2.8.2 --- android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index c6a35a072..c67f46454 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -5,8 +5,8 @@ apply plugin: "com.google.firebase.crashlytics" import com.android.build.OutputFile -def canonicalVersionName = "2.8.1" -def canonicalVersionCode = 30_007 +def canonicalVersionName = "2.8.2" +def canonicalVersionCode = 30_008 /** * This is the configuration block to customize your React Native Android app. From e222dca39f92807b15ffe16bda520c13130960f0 Mon Sep 17 00:00:00 2001 From: N3TC4T Date: Fri, 5 Apr 2024 11:19:39 +0200 Subject: [PATCH 4/4] chore(node): v2.8.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 22d143c20..507be164d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xaman", - "version": "2.8.1", + "version": "2.8.2", "license": "SEE LICENSE IN ", "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start",