From cbdd3e4bc7ea1bf081af4ce7b986c827458de76a Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Tue, 21 Jan 2025 01:28:54 +0100 Subject: [PATCH 1/3] Update Conversions.motesToCSPR --- src/types/Conversions.test.ts | 20 ++++++++++++++++++++ src/types/Conversions.ts | 16 ++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/types/Conversions.test.ts diff --git a/src/types/Conversions.test.ts b/src/types/Conversions.test.ts new file mode 100644 index 000000000..4d444a6f0 --- /dev/null +++ b/src/types/Conversions.test.ts @@ -0,0 +1,20 @@ +import { expect } from 'chai'; + +import { Conversions } from "./Conversions"; + +describe('Conversions', () => { + it('should convert motes to CSPR correctly', function() { + const bal1 = Conversions.motesToCSPR('1'); + const bal2 = Conversions.motesToCSPR('123400047000'); + const bal3 = Conversions.motesToCSPR('1000000040234023040234023040234023042040230420402000000'); + const bal4 = Conversions.motesToCSPR('0'); + const bal5 = Conversions.motesToCSPR('100000000000'); + + expect(bal1).to.deep.equal('0.000000001'); + expect(bal2).to.deep.equal('123.400047'); + expect(bal3).to.deep.equal('1000000040234023040234023040234023042040230420.402'); + expect(bal4).to.deep.equal('0'); + expect(bal5).to.deep.equal('100'); + expect(() => Conversions.motesToCSPR('-100')).to.throw('Motes cannot be negative number'); + }); +}); diff --git a/src/types/Conversions.ts b/src/types/Conversions.ts index a9ef6b503..a5ed862a1 100644 --- a/src/types/Conversions.ts +++ b/src/types/Conversions.ts @@ -113,7 +113,19 @@ export class Conversions { * const cspr = Conversions.motesToCSPR(motes); * console.log(cspr.toString()); // Outputs: "1" */ - static motesToCSPR(motes: BigNumberish): BigNumber { - return BigNumber.from(motes).div('1000000000'); + static motesToCSPR(motes: BigNumberish): string { + const motesBigNumber = BigNumber.from(motes); + + if (motesBigNumber.lt(0)) { + throw new Error('Motes cannot be negative number'); + } + + const result = motesBigNumber.toBigInt() * BigInt(10 ** 9) / BigInt(10 ** 9); + const resultStr = result.toString(); + + const integerPart = resultStr.slice(0, -9) || "0"; + const decimalPart = resultStr.slice(-9).padStart(9, '0'); + + return `${integerPart}.${decimalPart}`.replace(/\.?0+$/, ''); } } From e76f28925fa5bb3de7a7ecd00f4e266f419ff209 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Wed, 22 Jan 2025 11:55:25 +0100 Subject: [PATCH 2/3] Improve csprToMotes and motesToCSPR Conversions --- src/types/Conversions.test.ts | 22 ++++++++++++++++++++++ src/types/Conversions.ts | 23 ++++++++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/types/Conversions.test.ts b/src/types/Conversions.test.ts index 4d444a6f0..774540d42 100644 --- a/src/types/Conversions.test.ts +++ b/src/types/Conversions.test.ts @@ -3,6 +3,28 @@ import { expect } from 'chai'; import { Conversions } from "./Conversions"; describe('Conversions', () => { + it('should convert CSPR to motes correctly', function() { + const bal1 = Conversions.csprToMotes(1).toString(); + const bal2 = Conversions.csprToMotes('123400047000').toString(); + const bal3 = Conversions.csprToMotes('1000000040234023040234023040234023042040230420402000000.4022').toString(); + const bal4 = Conversions.csprToMotes('0').toString(); + const bal5 = Conversions.csprToMotes('0.111').toString(); + const bal6 = Conversions.csprToMotes('0.000000001').toString(); + const bal7 = Conversions.csprToMotes('0.000000000001').toString(); + + expect(bal1).to.deep.equal('1000000000'); + expect(bal2).to.deep.equal('123400047000000000000'); + expect(bal3).to.deep.equal('1000000040234023040234023040234023042040230420402000000402200000'); + expect(bal4).to.deep.equal('0'); + expect(bal5).to.deep.equal('111000000'); + expect(bal6).to.deep.equal('1'); + expect(bal7).to.deep.equal('0'); + expect(() => Conversions.csprToMotes('-100')).to.throw('Invalid input: cspr must be a string representing a valid positive number.'); + expect(() => Conversions.csprToMotes('100,3')).to.throw('Invalid input: cspr must be a string representing a valid positive number.'); + expect(() => Conversions.csprToMotes('abc')).to.throw('Invalid input: cspr must be a string representing a valid positive number.'); + expect(() => Conversions.csprToMotes('34.34.34')).to.throw('Invalid input: cspr must be a string representing a valid positive number.'); + }); + it('should convert motes to CSPR correctly', function() { const bal1 = Conversions.motesToCSPR('1'); const bal2 = Conversions.motesToCSPR('123400047000'); diff --git a/src/types/Conversions.ts b/src/types/Conversions.ts index a5ed862a1..5803f1146 100644 --- a/src/types/Conversions.ts +++ b/src/types/Conversions.ts @@ -83,7 +83,7 @@ export class Conversions { /** * Converts a CSPR amount to its mote equivalent. * - * @param cspr - A `BigNumberish` amount of CSPR to convert to motes. + * @param cspr - A `string` amount of CSPR to convert to motes. * @returns A `BigNumber` containing the equivalent amount in motes. * * @remarks @@ -94,8 +94,22 @@ export class Conversions { * const motes = Conversions.csprToMotes(cspr); * console.log(motes.toString()); // Outputs: "1000000000" */ - static csprToMotes(cspr: BigNumberish): BigNumber { - return BigNumber.from(cspr).mul('1000000000'); + static csprToMotes(cspr: string | number): BigNumber { + if (typeof cspr === 'string' && !/^\d+(\.\d+)?$/.test(cspr)) { + throw new Error('Invalid input: cspr must be a string representing a valid positive number.'); + } + + // eslint-disable-next-line prefer-const + let [integerPart, decimalPart = ""] = cspr.toString().split('.'); + let result = BigNumber.from(integerPart).mul(10 ** 9); + + if (decimalPart.length > 0) { + decimalPart = (decimalPart + '000000000').slice(0, 9); + + result = result.add(decimalPart); + } + + return result; } /** @@ -120,8 +134,7 @@ export class Conversions { throw new Error('Motes cannot be negative number'); } - const result = motesBigNumber.toBigInt() * BigInt(10 ** 9) / BigInt(10 ** 9); - const resultStr = result.toString(); + const resultStr = motesBigNumber.toBigInt().toString(); const integerPart = resultStr.slice(0, -9) || "0"; const decimalPart = resultStr.slice(-9).padStart(9, '0'); From cef2a37260c6a3d3f7c9dfc0d6fcbf014e00adbf Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Wed, 22 Jan 2025 11:56:30 +0100 Subject: [PATCH 3/3] Fix issue with CLValueMap.fromBytes --- src/types/Transaction.test.ts | 72 ++++++++++++++++++++++++++++++++++- src/types/clvalue/Map.ts | 20 +++++----- 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/src/types/Transaction.test.ts b/src/types/Transaction.test.ts index 00a8d93b7..255c40b02 100644 --- a/src/types/Transaction.test.ts +++ b/src/types/Transaction.test.ts @@ -2,7 +2,7 @@ import { BigNumber } from '@ethersproject/bignumber'; import { assert, expect } from 'chai'; import { Duration, Timestamp } from './Time'; -import { TransactionV1 } from './Transaction'; +import { Transaction, TransactionV1 } from './Transaction'; import { InitiatorAddr } from './InitiatorAddr'; import { FixedMode, PricingMode } from './PricingMode'; import { KeyAlgorithm, PrivateKey, PublicKey } from './keypair'; @@ -119,4 +119,74 @@ describe('Test Transaction', () => { assert.deepEqual(parseInt(transactionPaymentAmount, 10), 25000000000); expect(transactionV1.payload.chainName).to.deep.equal('casper-net-1'); }); + + it('should parse deploy json', async () => { + const json = { + "deploy": { + "approvals": [], + "hash": "076E77DE17De7F262c5531017c214afd664D9702D4b5b771996aE4dcAf9C01f9", + "header": { + "account": "02024570ae3c361650d5b1Bcd1724a1aF09ffe067d7F69ebd75B567c10c8379a7719", + "timestamp": "2025-01-21T18:07:52.214Z", + "ttl": "30m", + "dependencies": [], + "gas_price": 1, + "body_hash": "D52E4B46542D9C83BA6EF894dBA82e475011166A4C5F434b0d99248cfDC9Abc5", + "chain_name": "casper-test" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "0400943577", + "parsed": "2000000000" + } + ] + ] + } + }, + "session": { + "StoredVersionedContractByHash": { + "hash": "6497c59f1bcfBBBC468Dc889dd73dCd542827fb966a24Eb33e4140Ab5BB4aE28", + "version": null, + "entry_point": "mint", + "args": [ + [ + "recipient", + { + "cl_type": "Key", + "bytes": "00ABbc44715BA77Ea117f9379A3f5b62879Be71D105b7508b610a676AEf4c3C26a", + "parsed": { + "Account": "account-hash-ABBC44715ba77ea117f9379a3f5b62879Be71D105B7508B610a676AeF4C3C26A" + } + } + ], + [ + "token_metas", + { + "cl_type": { + "List": { + "Map": { + "key": "String", + "value": "String" + } + } + }, + "bytes": "0100000003000000040000006e616d65080000004e6f6f6f6f6f6f6f0b0000006465736372697074696f6e04000000747275650500000061737365745400000068747470733a2f2f697066732d676174657761792e637370722e73747564696f2f697066732f516d5a62714d767a5036706a45415554753135376d5470736e38526b4d637a585a48767a56784454647854436572", + "parsed": "" + } + ] + ] + } + } + } + }; + + const tx = Transaction.fromJSON(json); + expect(tx.hash.toHex()).to.equal('076e77de17de7f262c5531017c214afd664d9702d4b5b771996ae4dcaf9c01f9') + }); }); diff --git a/src/types/clvalue/Map.ts b/src/types/clvalue/Map.ts index 79e576252..800fe7226 100644 --- a/src/types/clvalue/Map.ts +++ b/src/types/clvalue/Map.ts @@ -195,28 +195,26 @@ export class CLValueMap { const { result: u32, bytes: u32Bytes } = CLValueUInt32.fromBytes(bytes); const size = u32.toNumber(); - const remainder = u32Bytes; + let remainder = u32Bytes; if (size === 0) { return { result: mapResult, bytes: remainder }; } for (let i = 0; i < size; i++) { - if (remainder.length) { - const keyVal = CLValueParser.fromBytesByType(remainder, mapType.key); + if (!remainder.length) { + continue; + } - if (!keyVal.bytes || !keyVal.result) { - continue; - } + try { + const keyVal = CLValueParser.fromBytesByType(remainder, mapType.key); + remainder = keyVal?.bytes ?? []; const valVal = CLValueParser.fromBytesByType(keyVal.bytes, mapType.val); - - if (!valVal.bytes || !valVal.result) { - continue; - } + remainder = valVal?.bytes ?? []; mapResult.append(keyVal?.result, valVal?.result); - } + } catch {} } return { result: mapResult, bytes: remainder };