From 77221596ae0fc3b98b2abb550d51aba264d0e9b3 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Wed, 11 Dec 2024 13:51:55 +0100 Subject: [PATCH 01/10] Add axios dependency --- package-lock.json | 21 +++++++-------------- package.json | 1 + 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 04603663a..c7d2d8ea2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@scure/bip32": "^1.1.5", "@scure/bip39": "^1.2.0", "@types/ws": "^8.2.2", + "axios": "^1.7.9", "eventsource": "^2.0.2", "glob": "^7.1.6", "humanize-duration": "^3.24.0", @@ -2150,8 +2151,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/at-least-node": { "version": "1.0.0", @@ -2175,10 +2175,10 @@ } }, "node_modules/axios": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", - "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", - "dev": true, + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -2189,7 +2189,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -3065,7 +3064,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -3553,7 +3551,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -4605,7 +4602,6 @@ "version": "1.15.6", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "dev": true, "funding": [ { "type": "individual", @@ -6854,7 +6850,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -6863,7 +6858,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -7935,8 +7929,7 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/public-encrypt": { "version": "4.0.3", diff --git a/package.json b/package.json index 430ea5fab..2ec7495b6 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,7 @@ "@scure/bip32": "^1.1.5", "@scure/bip39": "^1.2.0", "@types/ws": "^8.2.2", + "axios": "^1.7.9", "eventsource": "^2.0.2", "glob": "^7.1.6", "humanize-duration": "^3.24.0", From 2a39f40616972793be154fe47a584a90415acfdc Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Wed, 11 Dec 2024 13:52:56 +0100 Subject: [PATCH 02/10] Add checksummed public key hex --- src/types/keypair/PublicKey.test.ts | 33 ++++++ src/types/keypair/PublicKey.ts | 149 ++++++++++++++++++++++++++-- 2 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 src/types/keypair/PublicKey.test.ts diff --git a/src/types/keypair/PublicKey.test.ts b/src/types/keypair/PublicKey.test.ts new file mode 100644 index 000000000..6d5c0e027 --- /dev/null +++ b/src/types/keypair/PublicKey.test.ts @@ -0,0 +1,33 @@ +import { expect } from 'chai'; +import { PublicKey } from "./PublicKey"; +import { PrivateKey } from "./PrivateKey"; +import { KeyAlgorithm } from "./Algorithm"; + + +describe('PublicKey', () => { + it('should work PublicKey fromHex and toHex', async function() { + const newKeySecp = await PrivateKey.generate(KeyAlgorithm.SECP256K1); + const hashSecp = newKeySecp.publicKey.toHex(); + const publicKeySecp = PublicKey.fromHex(hashSecp); + expect(hashSecp).to.deep.equal(publicKeySecp.toHex()); + + const newKeyEd = await PrivateKey.generate(KeyAlgorithm.ED25519); + const hashEd = newKeyEd.publicKey.toHex(); + const publicKeyEd = PublicKey.fromHex(hashEd); + expect(hashEd).to.deep.equal(publicKeyEd.toHex()); + }); + + it('should work PublicKey fromHex and toHex with exact hash', async function() { + const hash = '0203b9b4cd6085590b68bfccaf7ea1744766e5225928beba99155a1bd79870f7a986'; + + const publicKey = PublicKey.fromHex(hash); + expect(hash).to.deep.equal(publicKey.toHex()); + }); + + it('should produce checksummed public key hex', async function() { + const hash = '0203E0fb0E4c25be97F2fE87b3067984D28ee7e32b400ce240e52DEd452c49a79567'; + + const publicKey = PublicKey.fromHex(hash.toLowerCase(), true); + expect(hash).to.deep.equal(publicKey.toHex(true)); + }); +}); diff --git a/src/types/keypair/PublicKey.ts b/src/types/keypair/PublicKey.ts index 2e6cc6ca8..d385c0f21 100644 --- a/src/types/keypair/PublicKey.ts +++ b/src/types/keypair/PublicKey.ts @@ -1,5 +1,4 @@ import { jsonObject, jsonMember } from 'typedjson'; -import { Buffer } from 'buffer'; import { concat } from '@ethersproject/bytes'; import { PublicKey as Ed25519PublicKey } from './ed25519/PublicKey'; @@ -29,6 +28,14 @@ enum KeyAlgorithm { SECP256K1 = 2 } +const SMALL_BYTES_COUNT = 75; +// prettier-ignore +const HEX_CHARS = [ + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + 'A', 'B', 'C', 'D', 'E', 'F' +]; + /** * Interface representing the internal structure of a public key, which includes * methods for obtaining bytes and verifying signatures. @@ -85,12 +92,33 @@ export class PublicKey { } /** - * Converts the public key to a hexadecimal string. - * @returns A hex string representation of the public key. + * Converts the public key to a hexadecimal string representation. + * + * @param checksummed - A boolean indicating whether to return a checksummed version of the hex string. + * - `true`: Includes a checksum in the output. + * - `false` (default): Returns the raw hexadecimal string without a checksum. + * @returns The hexadecimal string representation of the public key. + * - If `checksummed` is `true`, the result includes a checksum. + * - If `checksummed` is `false`, the raw hex string is returned. + * @throws {Error} If the public key is not initialized properly (i.e., `this.key` is missing). */ - toHex(): string { - const bytes = this.bytes(); - return Conversions.encodeBase16(bytes); + toHex(checksummed = false): string { + if (!this.key) { + throw new Error('Public key initialised incorrectly. Missing key'); + } + + const rawHex = `0${this.cryptoAlg}${Conversions.encodeBase16( + this.key.bytes() + )}`; + + if (checksummed) { + const bytes = Conversions.decodeBase16(rawHex); + return ( + PublicKey.encode(bytes.slice(0, 1)) + PublicKey.encode(bytes.slice(1)) + ); + } + + return rawHex; } /** @@ -123,9 +151,32 @@ export class PublicKey { * @param source - The hexadecimal string. * @returns A new PublicKey instance. */ - static fromHex(source: string): PublicKey { - const buffer = Buffer.from(source, 'hex'); - return PublicKey.fromBuffer(buffer); + // static fromHex(source: string): PublicKey { + // const publicKeyHexBytes = Conversions.decodeBase16(source); + // + // return PublicKey.fromBuffer(publicKeyHexBytes); + // } + + /** + * Tries to decode PublicKey from its hex-representation. + * The hex format should be as produced by PublicKey.toHex + * @param publicKeyHex public key hex string contains key tag + * @param checksummed throws an Error if true and given string is not checksummed + */ + static fromHex(publicKeyHex: string, checksummed = false): PublicKey { + if (publicKeyHex.length < 2) { + throw new Error('Public key error: too short'); + } + + if (!/^0(1[0-9a-fA-F]{64}|2[0-9a-fA-F]{66})$/.test(publicKeyHex)) { + throw new Error('Invalid public key'); + } + + if (checksummed && !PublicKey.isChecksummed(publicKeyHex)) { + throw Error('Provided public key is not checksummed.'); + } + + return PublicKey.fromBuffer(Conversions.decodeBase16(publicKeyHex)); } /** @@ -248,6 +299,60 @@ export class PublicKey { return { result: new PublicKey(alg, key), bytes }; } + + /** + * Verify a mixed-case hexadecimal string that it conforms to the checksum scheme + * similar to scheme in [EIP-55](https://eips.ethereum.org/EIPS/eip-55). + * Key differences: + * - Works on any length of (decoded) data up to `SMALL_BYTES_COUNT`, not just 20-byte addresses + * - Uses Blake2b hashes rather than Keccak + * - Uses hash bits rather than nibbles + * For backward compatibility: if the hex string is all uppercase or all lowercase, the check is + * skipped. + * @param input string to check if it is checksummed + * @returns true if input is checksummed + */ + static isChecksummed = (input: string): boolean => { + const bytes = new Uint8Array(Buffer.from(input, 'hex')); + + if (bytes.length > SMALL_BYTES_COUNT || isSameCase(input)) return true; + + if (isValidPublicKey(input)) { + return ( + input === + PublicKey.encode(bytes.slice(0, 1)) + PublicKey.encode(bytes.slice(1)) + ); + } + + return input === PublicKey.encode(bytes); + }; + + /** + * Returns the bytes encoded as hexadecimal with mixed-case based checksums following a scheme + * similar to [EIP-55](https://eips.ethereum.org/EIPS/eip-55). + * Key differences: + * - Works on any length of data, not just 20-byte addresses + * - Uses Blake2b hashes rather than Keccak + * - Uses hash bits rather than nibbles + * @param input Uint8Array to generate checksummed hex string + * @returns checksummed hex presentation string of input + */ + static encode = (input: Uint8Array): string => { + const inputNibbles = bytesToNibbles(input); + const hashBits = bytesToBitsCycle(byteHash(input)).values(); + + const hexOutputString = inputNibbles.reduce((accum, nibble) => { + const c = HEX_CHARS[nibble]; + + if (/^[a-zA-Z()]+$/.test(c) && hashBits.next().value) { + return accum + c.toUpperCase(); + } else { + return accum + c.toLowerCase(); + } + }, ''); + + return hexOutputString; + }; } /** @@ -273,3 +378,29 @@ export class PublicKeyList { return this.keys.some(key => key.equals(target)); } } + +export function isSameCase(value: string) { + return /^[a-z0-9]+$|^[A-Z0-9]+$/.test(value); +} + +export function isValidPublicKey(key: string) { + return /^0(1[0-9a-fA-F]{64}|2[0-9a-fA-F]{66})$/.test(key); +} + +function bytesToNibbles(bytes: Uint8Array): Uint8Array { + const outputNibbles = bytes.reduce((accum, byte) => { + return concat([accum, Uint8Array.of(byte >>> 4, byte & 0x0f)]); + }, new Uint8Array()); + + return outputNibbles; +} + +function bytesToBitsCycle(bytes: Uint8Array) { + const output: boolean[] = []; + + for (let i = 0, k = 0; i < bytes.length; i++) + for (let j = 0; j < 8; j++) + output[k++] = ((bytes[i] >>> j) & 0x01) === 0x01; + + return output; +} From d41f539bb62df72c5e279f947fec97161cb61fa9 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Wed, 11 Dec 2024 13:53:27 +0100 Subject: [PATCH 03/10] Fix issue with secp256k1 bytes --- src/types/keypair/secp256k1/PublicKey.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/keypair/secp256k1/PublicKey.ts b/src/types/keypair/secp256k1/PublicKey.ts index e161a4475..b0832da8a 100644 --- a/src/types/keypair/secp256k1/PublicKey.ts +++ b/src/types/keypair/secp256k1/PublicKey.ts @@ -25,7 +25,7 @@ export class PublicKey { * @returns A compressed `Uint8Array` representation of the public key. */ bytes(): Uint8Array { - return secp256k1.Point.fromHex(this.key).toRawBytes(true); + return this.key; } /** From 930f529f4ed5323172d9073eb39e8d66cf40619d Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Wed, 11 Dec 2024 13:54:28 +0100 Subject: [PATCH 04/10] Fix StepPayload.executionEffects annotations --- src/sse/event.ts | 8 ++++---- src/types/keypair/ed25519/PrivateKey.ts | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sse/event.ts b/src/sse/event.ts index 3520ededd..9fc8fa9c1 100644 --- a/src/sse/event.ts +++ b/src/sse/event.ts @@ -2,7 +2,6 @@ import { jsonObject, jsonMember, jsonArrayMember, TypedJSON } from 'typedjson'; import { Effect, - Effects, ExecutionResult, ExecutionResultV1, Deploy, @@ -17,7 +16,8 @@ import { HexBytes, Timestamp, PublicKey, - Hash + Hash, + Transform } from '../types'; export enum EventType { @@ -758,8 +758,8 @@ export class StepPayload { @jsonMember({ name: 'execution_effect', constructor: Effect }) executionEffect: Effect; - @jsonMember({ name: 'execution_effects', constructor: Effects }) - executionEffects: Effects; + @jsonArrayMember(() => Transform, { name: 'execution_effects' }) + executionEffects: Transform[]; } @jsonObject diff --git a/src/types/keypair/ed25519/PrivateKey.ts b/src/types/keypair/ed25519/PrivateKey.ts index e1fadb372..6679ff88a 100644 --- a/src/types/keypair/ed25519/PrivateKey.ts +++ b/src/types/keypair/ed25519/PrivateKey.ts @@ -1,5 +1,4 @@ import * as ed25519 from '@noble/ed25519'; -import { Buffer } from 'buffer'; /** * Interface representing the structure and methods of a private key, including From 0717b5799fbd610cc60eff1c10e98abe23b3ae65 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Thu, 12 Dec 2024 13:23:56 +0100 Subject: [PATCH 05/10] Fix issue with ExecutionResult parsing --- src/types/ExecutionResult.test.ts | 365 ++++++++++++++++++++++++++++++ src/types/Transform.ts | 28 +-- src/types/key/Hash.ts | 4 +- 3 files changed, 381 insertions(+), 16 deletions(-) create mode 100644 src/types/ExecutionResult.test.ts diff --git a/src/types/ExecutionResult.test.ts b/src/types/ExecutionResult.test.ts new file mode 100644 index 000000000..3793db2cf --- /dev/null +++ b/src/types/ExecutionResult.test.ts @@ -0,0 +1,365 @@ +import { expect } from 'chai'; +import { InfoGetDeployResultV1Compatible } from '../rpc'; +import { ExecutionResult } from "./ExecutionResult"; + + +describe('ExecutionResult', () => { + it('should parse infoGetDeployResultV1Json to ExecutionResult', async function() { + const infoGetDeployResultV1Json = { + "deploy": { + "hash": "a2a4b37e33a04d5922e435e98ec8d555370a976eb7fa9913155a615fb2536649", + "header": { + "ttl": "30m", + "account": "020324b4bb39d5784e90ab616e0a69b0679efa6567efd15277c3cbf63ab2bc56946e", + "body_hash": "e9f37dd5a73a58a09694ae77f182fb6e2b5efc53a1605a70766cb94629125206", + "gas_price": 1, + "timestamp": "2024-12-04T10:26:37.952Z", + "chain_name": "casper-test", + "dependencies": [] + }, + "payment": { + "ModuleBytes": { + "args": [ + [ + "amount", + { + "bytes": "05005cb2ec22", + "parsed": "150000000000", + "cl_type": "U512" + } + ] + ], + "module_bytes": "" + } + }, + "session": { + "StoredContractByHash": { + "args": [ + [ + "deployment_threshold", + { + "bytes": "01", + "parsed": 1, + "cl_type": "U8" + } + ], + [ + "key_management_threshold", + { + "bytes": "01", + "parsed": 1, + "cl_type": "U8" + } + ], + [ + "weights", + { + "bytes": "020000000202", + "parsed": [ + 2, + 2 + ], + "cl_type": { + "List": "U8" + } + } + ], + [ + "keys", + { + "bytes": "02000000007f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba007dc2bcc676eba6196d16374e1a2dbfa1df336f779854d95a0b4e65de6d593158", + "parsed": [ + { + "Account": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba" + }, + { + "Account": "account-hash-7dc2bcc676eba6196d16374e1a2dbfa1df336f779854d95a0b4e65de6d593158" + } + ], + "cl_type": { + "List": "Key" + } + } + ] + ], + "hash": "676794cbbb35ff5642d0ae9c35302e244a7236a614d7e9ef58d0fb2cba6be3ed", + "entry_point": "set_associated_keys" + } + }, + "approvals": [ + { + "signer": "020324b4bb39d5784e90ab616e0a69b0679efa6567efd15277c3cbf63ab2bc56946e", + "signature": "027cdf87348450fe1e0418fda7036d6af847324115491cbbd237c947990a6ecd3b77cd667f9b4c0b163b157e72e9ca5a577dcbb3ab2596744e0419da58cb370d2f" + } + ] + }, + "api_version": "1.5.8", + "execution_results": [ + { + "result": { + "Success": { + "cost": "981979384", + "effect": { + "operations": [], + "transforms": [ + { + "key": "account-hash-6174cf2e6f8fed1715c9a3bace9c50bfe572eecb763b0ed3f644532616452008", + "transform": "Identity" + }, + { + "key": "hash-8cf5e4acf51f54eb59291599187838dc3bc234089c46fc6ca8ad17e762ae4401", + "transform": "Identity" + }, + { + "key": "hash-8cf5e4acf51f54eb59291599187838dc3bc234089c46fc6ca8ad17e762ae4401", + "transform": "Identity" + }, + { + "key": "hash-624dbe2395b9d9503fbee82162f1714ebff6b639f96d2084d26d944c354ec4c5", + "transform": "Identity" + }, + { + "key": "hash-8cf5e4acf51f54eb59291599187838dc3bc234089c46fc6ca8ad17e762ae4401", + "transform": "Identity" + }, + { + "key": "hash-010c3fe81b7b862e50c77ef9a958a05bfa98444f26f96f23d37a13c96244cfb7", + "transform": "Identity" + }, + { + "key": "hash-9824d60dc3a5c44a20b9fd260a412437933835b52fc683d8ae36e4ec2114843e", + "transform": "Identity" + }, + { + "key": "hash-010c3fe81b7b862e50c77ef9a958a05bfa98444f26f96f23d37a13c96244cfb7", + "transform": "Identity" + }, + { + "key": "balance-68b56afb28a42c3787326def04177eaa64ab31afdc7316e936f9ba1810019e4c", + "transform": "Identity" + }, + { + "key": "balance-98d945f5324f865243b7c02c0417ab6eac361c5c56602fd42ced834a1ba201b6", + "transform": "Identity" + }, + { + "key": "balance-68b56afb28a42c3787326def04177eaa64ab31afdc7316e936f9ba1810019e4c", + "transform": { + "WriteCLValue": { + "bytes": "06a9cbc26e5301", + "parsed": "1457852173225", + "cl_type": "U512" + } + } + }, + { + "key": "balance-98d945f5324f865243b7c02c0417ab6eac361c5c56602fd42ced834a1ba201b6", + "transform": { + "AddUInt512": "150000000000" + } + }, + { + "key": "hash-676794cbbb35ff5642d0ae9c35302e244a7236a614d7e9ef58d0fb2cba6be3ed", + "transform": "Identity" + }, + { + "key": "hash-ff9c3c0c447d2e3a79c02e13d048c03f6fac8a911fdc04118cc754c84ef6259e", + "transform": "Identity" + }, + { + "key": "hash-ebeecefb82070e662a8e9bb128dfbb126567e91c07de5cbd08cb587222542156", + "transform": "Identity" + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": "Identity" + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": { + "WriteAccount": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba" + } + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": "Identity" + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": { + "WriteAccount": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba" + } + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": "Identity" + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": { + "WriteAccount": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba" + } + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": "Identity" + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": { + "WriteAccount": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba" + } + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": "Identity" + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": { + "WriteAccount": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba" + } + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": "Identity" + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": { + "WriteAccount": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba" + } + }, + { + "key": "deploy-a2a4b37e33a04d5922e435e98ec8d555370a976eb7fa9913155a615fb2536649", + "transform": { + "WriteDeployInfo": { + "gas": "981979384", + "from": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "source": "uref-68b56afb28a42c3787326def04177eaa64ab31afdc7316e936f9ba1810019e4c-007", + "transfers": [], + "deploy_hash": "a2a4b37e33a04d5922e435e98ec8d555370a976eb7fa9913155a615fb2536649" + } + } + }, + { + "key": "hash-8cf5e4acf51f54eb59291599187838dc3bc234089c46fc6ca8ad17e762ae4401", + "transform": "Identity" + }, + { + "key": "hash-8cf5e4acf51f54eb59291599187838dc3bc234089c46fc6ca8ad17e762ae4401", + "transform": "Identity" + }, + { + "key": "hash-8cf5e4acf51f54eb59291599187838dc3bc234089c46fc6ca8ad17e762ae4401", + "transform": "Identity" + }, + { + "key": "hash-624dbe2395b9d9503fbee82162f1714ebff6b639f96d2084d26d944c354ec4c5", + "transform": "Identity" + }, + { + "key": "hash-8cf5e4acf51f54eb59291599187838dc3bc234089c46fc6ca8ad17e762ae4401", + "transform": "Identity" + }, + { + "key": "balance-98d945f5324f865243b7c02c0417ab6eac361c5c56602fd42ced834a1ba201b6", + "transform": "Identity" + }, + { + "key": "hash-8cf5e4acf51f54eb59291599187838dc3bc234089c46fc6ca8ad17e762ae4401", + "transform": "Identity" + }, + { + "key": "account-hash-7f3688d1dc6adff9ce5e3ec90628f88e2fb39b763724cbdf9a7f9f68de0205ba", + "transform": "Identity" + }, + { + "key": "hash-010c3fe81b7b862e50c77ef9a958a05bfa98444f26f96f23d37a13c96244cfb7", + "transform": "Identity" + }, + { + "key": "hash-9824d60dc3a5c44a20b9fd260a412437933835b52fc683d8ae36e4ec2114843e", + "transform": "Identity" + }, + { + "key": "hash-010c3fe81b7b862e50c77ef9a958a05bfa98444f26f96f23d37a13c96244cfb7", + "transform": "Identity" + }, + { + "key": "balance-98d945f5324f865243b7c02c0417ab6eac361c5c56602fd42ced834a1ba201b6", + "transform": "Identity" + }, + { + "key": "balance-68b56afb28a42c3787326def04177eaa64ab31afdc7316e936f9ba1810019e4c", + "transform": "Identity" + }, + { + "key": "balance-98d945f5324f865243b7c02c0417ab6eac361c5c56602fd42ced834a1ba201b6", + "transform": { + "WriteCLValue": { + "bytes": "0467295a93", + "parsed": "2472159591", + "cl_type": "U512" + } + } + }, + { + "key": "balance-68b56afb28a42c3787326def04177eaa64ab31afdc7316e936f9ba1810019e4c", + "transform": { + "AddUInt512": "147527840409" + } + }, + { + "key": "hash-010c3fe81b7b862e50c77ef9a958a05bfa98444f26f96f23d37a13c96244cfb7", + "transform": "Identity" + }, + { + "key": "hash-9824d60dc3a5c44a20b9fd260a412437933835b52fc683d8ae36e4ec2114843e", + "transform": "Identity" + }, + { + "key": "hash-010c3fe81b7b862e50c77ef9a958a05bfa98444f26f96f23d37a13c96244cfb7", + "transform": "Identity" + }, + { + "key": "balance-98d945f5324f865243b7c02c0417ab6eac361c5c56602fd42ced834a1ba201b6", + "transform": "Identity" + }, + { + "key": "balance-90bb02441c92b44b6b97bdb094aa4e30f318a82ba189a3f78166c5742a1b20fe", + "transform": "Identity" + }, + { + "key": "balance-98d945f5324f865243b7c02c0417ab6eac361c5c56602fd42ced834a1ba201b6", + "transform": { + "WriteCLValue": { + "bytes": "00", + "parsed": "0", + "cl_type": "U512" + } + } + }, + { + "key": "balance-90bb02441c92b44b6b97bdb094aa4e30f318a82ba189a3f78166c5742a1b20fe", + "transform": { + "AddUInt512": "2472159591" + } + } + ] + }, + "transfers": [] + } + }, + "block_hash": "ab96565a90981ec7c22020bec0be774297b4cc12fa5cb36f45e85c35251711d8" + } + ] + }; + + const info = InfoGetDeployResultV1Compatible.fromJSON(infoGetDeployResultV1Json); + expect(info).exist + + const result = ExecutionResult.fromV1(info!.executionResults[0].result) + expect(result).exist + }); +}); diff --git a/src/types/Transform.ts b/src/types/Transform.ts index 74a63aa63..5343f2e5e 100644 --- a/src/types/Transform.ts +++ b/src/types/Transform.ts @@ -72,7 +72,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteTransfer, otherwise `false`. */ public isWriteTransfer(): boolean { - return this.data.includes('WriteTransfer'); + return this.data.includes?.('WriteTransfer') ?? false; } /** @@ -81,7 +81,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteAccount, otherwise `false`. */ public isWriteAccount(): boolean { - return this.data.includes('WriteAccount'); + return this.data.includes?.('WriteAccount') ?? false; } /** @@ -99,7 +99,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteWithdraw, otherwise `false`. */ public isWriteWithdraw(): boolean { - return this.data.includes('WriteWithdraw'); + return this.data.includes?.('WriteWithdraw') ?? false; } /** @@ -108,7 +108,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteUnbonding, otherwise `false`. */ public isWriteUnbonding(): boolean { - return this.data.includes('WriteUnbonding'); + return this.data.includes?.('WriteUnbonding') ?? false; } /** @@ -117,7 +117,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteCLValue, otherwise `false`. */ public isWriteCLValue(): boolean { - return this.data.includes('CLValue'); + return this.data.includes?.('CLValue') ?? false; } /** @@ -126,7 +126,7 @@ export class TransformKind { * @returns `true` if the transformation is a WritePackage, otherwise `false`. */ public isWritePackage(): boolean { - return this.data.includes('"Package"'); + return this.data.includes?.('"Package"') ?? false; } /** @@ -135,7 +135,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteAddressableEntity, otherwise `false`. */ public isWriteAddressableEntity(): boolean { - return this.data.includes('"AddressableEntity"'); + return this.data.includes?.('"AddressableEntity"') ?? false; } /** @@ -144,7 +144,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteBidKind, otherwise `false`. */ public isWriteBidKind(): boolean { - return this.data.includes('"BidKind"'); + return this.data.includes?.('"BidKind"') ?? false; } /** @@ -153,7 +153,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteNamedKey, otherwise `false`. */ public isWriteNamedKey(): boolean { - return this.data.includes('"NamedKey"'); + return this.data.includes?.('"NamedKey"') ?? false; } /** @@ -162,7 +162,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteMessage, otherwise `false`. */ public isWriteMessage(): boolean { - return this.data.includes('"Message"'); + return this.data.includes?.('"Message"') ?? false; } /** @@ -171,7 +171,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteMessageTopic, otherwise `false`. */ public isWriteMessageTopic(): boolean { - return this.data.includes('"MessageTopic"'); + return this.data.includes?.('"MessageTopic"') ?? false; } /** @@ -180,7 +180,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteBid, otherwise `false`. */ public isWriteBid(): boolean { - return this.data.includes('WriteBid'); + return this.data.includes?.('WriteBid') ?? false; } /** @@ -189,7 +189,7 @@ export class TransformKind { * @returns `true` if the transformation is AddUInt512, otherwise `false`. */ public isAddUint512(): boolean { - return this.data.includes('AddUInt512'); + return this.data.includes?.('AddUInt512') ?? false; } /** @@ -198,7 +198,7 @@ export class TransformKind { * @returns `true` if the transformation is a WriteDeployInfo, otherwise `false`. */ public isWriteDeployInfo(): boolean { - return this.data.includes('WriteDeployInfo'); + return this.data.includes?.('WriteDeployInfo') ?? false; } /** diff --git a/src/types/key/Hash.ts b/src/types/key/Hash.ts index 1b3bb7e55..4ccdbf66d 100644 --- a/src/types/key/Hash.ts +++ b/src/types/key/Hash.ts @@ -85,8 +85,8 @@ export class Hash { * @throws Error if the byte array length does not match the expected hash length. */ static fromBytes(source: Uint8Array): IResultWithBytes { - if (source.length !== Hash.ByteHashLen) { - throw new Error('Key length is not equal to 32 bytes.'); + if (source.length < Hash.ByteHashLen) { + throw new Error('Key length is less than 32 bytes.'); } const hashBytes = source.subarray(0, Hash.ByteHashLen); From af3a92a0ad18f07e7df5d0e658efc23388ce2965 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Thu, 12 Dec 2024 13:26:59 +0100 Subject: [PATCH 06/10] Improve Transaction creation from JSON --- README.md | 8 ++-- src/rpc/client.ts | 4 +- src/rpc/request.ts | 4 +- src/rpc/rpc_client.ts | 11 +++--- src/types/Transaction.ts | 85 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 98 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e87834763..b111be09c 100644 --- a/README.md +++ b/README.md @@ -176,12 +176,14 @@ const transactionPayload = TransactionV1Payload.build({ pricingMode }); -const transaction = TransactionV1.makeTransactionV1( +const transactionV1 = TransactionV1.makeTransactionV1( transactionPayload ); -await transaction.sign(privateKey); +await transactionV1.sign(privateKey); -const result = await rpcClient.putTransactionV1(transaction); +const tx = Transaction.fromTransactionV1(transactionV1); + +const result = await rpcClient.putTransaction(tx); console.log(`Transaction Hash: ${result.transactionHash}`); ``` diff --git a/src/rpc/client.ts b/src/rpc/client.ts index 3ada7b28b..c4a4b9e2d 100644 --- a/src/rpc/client.ts +++ b/src/rpc/client.ts @@ -35,7 +35,7 @@ import { PurseIdentifier, RpcRequest } from './request'; -import { TransactionV1, Deploy, PublicKey } from '../types'; +import { Deploy, PublicKey, Transaction } from '../types'; export interface ClientPOS { getLatestAuctionInfo(): Promise; @@ -226,7 +226,7 @@ export interface ClientInformational { export interface ClientTransactional { putDeploy(deploy: Deploy): Promise; - putTransactionV1(transaction: TransactionV1): Promise; + putTransaction(transaction: Transaction): Promise; } export interface IClient diff --git a/src/rpc/request.ts b/src/rpc/request.ts index 380e7be3b..a55e0d2ff 100644 --- a/src/rpc/request.ts +++ b/src/rpc/request.ts @@ -385,10 +385,10 @@ export class PutDeployRequest { @jsonObject export class PutTransactionRequest { @jsonMember({ constructor: TransactionWrapper }) - transaction: TransactionWrapper; + transactionWrapper: TransactionWrapper; constructor(transaction: TransactionWrapper) { - this.transaction = transaction; + this.transactionWrapper = transaction; } } diff --git a/src/rpc/rpc_client.ts b/src/rpc/rpc_client.ts index 2cb38a37c..1b3e87425 100644 --- a/src/rpc/rpc_client.ts +++ b/src/rpc/rpc_client.ts @@ -58,11 +58,10 @@ import { import { IDValue } from './id_value'; import { TransactionHash, - TransactionV1, - TransactionWrapper, Deploy, PublicKey, - Hash + Hash, + Transaction } from '../types'; export class RpcClient implements IClient { @@ -903,12 +902,12 @@ export class RpcClient implements IClient { return result; } - async putTransactionV1( - transaction: TransactionV1 + async putTransaction( + transaction: Transaction ): Promise { const serializer = new TypedJSON(PutTransactionRequest); const transactionRequestParam = new PutTransactionRequest( - new TransactionWrapper(undefined, transaction) + transaction.getTransactionWrapper() ); const resp = await this.processRequest( diff --git a/src/types/Transaction.ts b/src/types/Transaction.ts index 601d3c4e2..a8d62f0e5 100644 --- a/src/types/Transaction.ts +++ b/src/types/Transaction.ts @@ -404,7 +404,6 @@ export class Transaction { * @param entryPoint The entry point of the transaction. * @param scheduling The scheduling information for the transaction. * @param approvals The list of approvals for this transaction. - * @param category The category of the transaction, indicating its type (e.g., minting, auction). * @param originTransactionV1 The original TransactionV1, if applicable. * @param originDeployV1 The original deploy, if applicable. */ @@ -437,6 +436,18 @@ export class Transaction { this.originDeployV1 = originDeployV1; this.originTransactionV1 = originTransactionV1; + + if (!(this.originDeployV1 || this.originTransactionV1)) { + throw new Error( + 'Incorrect Transaction instance. Missing originTransactionV1 or originDeploy' + ); + } + + if (this.originDeployV1 && this.originTransactionV1) { + throw new Error( + 'Incorrect Transaction instance. Should be only one of originTransactionV1 or originDeploy' + ); + } } /** @@ -455,6 +466,53 @@ export class Transaction { return this.originTransactionV1; } + public getTransactionWrapper(): TransactionWrapper { + return new TransactionWrapper(this.originDeployV1, this.originTransactionV1); + } + + /** + * Validates the transaction by checking the transaction hash and the approval signatures. + * @throws {TransactionError} Throws errors if validation fails. + */ + public validate(): boolean { + if (this.originTransactionV1) { + return this.originTransactionV1.validate(); + } else if (this.originDeployV1) { + return this.originDeployV1.validate(); + } + + throw new Error('Incorrect Transaction instance. Missing origin value'); + } + + /** + * Signs the transaction using the provided private key. + * @param key The private key to sign the transaction. + */ + async sign(key: PrivateKey): Promise { + const signatureBytes = await key.sign(this.hash.toBytes()); + this.setSignature(signatureBytes, key.publicKey); + } + + /** + * Sets an already generated signature to the transaction. + * @param signature The Ed25519 or Secp256K1 signature. + * @param publicKey The public key used to generate the signature. + */ + setSignature(signature: Uint8Array, publicKey: PublicKey) { + const hex = new HexBytes(signature); + const approval = new Approval(publicKey, hex); + + this.approvals.push(approval); + + if (this.originTransactionV1) { + this.originTransactionV1.approvals.push(approval); + } else if (this.originDeployV1) { + this.originDeployV1.approvals.push(approval); + } else { + throw new Error('Incorrect Transaction instance. Missing origin value'); + } + } + /** * Converts a `TransactionV1` to a `Transaction` object. * @param v1 The `TransactionV1` to convert. @@ -477,6 +535,31 @@ export class Transaction { undefined // originDeployV1 is not applicable for this method ); } + + /** + * Converts a `TransactionV1` to a `Transaction` object. + * @param deploy The `Deploy` to convert. + * @returns A new `Transaction` instance created from the given `Deploy`. + */ + static fromDeploy(deploy: Deploy): Transaction { + return Deploy.newTransactionFromDeploy(deploy); + } + + static fromJson(json: any): Transaction { + try { + const txV1 = TransactionV1.fromJSON(json); + + return Transaction.fromTransactionV1(txV1); + } catch (e) {} + + try { + const deploy = Deploy.fromJSON(json); + + return Transaction.fromDeploy(deploy); + } catch (e) {} + + throw new Error("The JSON can't be parsed as a Transaction."); + } } /** From 766589020d53e9b83108e1902bbcd45fd086e885 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Thu, 12 Dec 2024 13:27:43 +0100 Subject: [PATCH 07/10] Rename StoredValue.prepaid to StoredValue.prepayment --- src/types/StoredValue.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/StoredValue.ts b/src/types/StoredValue.ts index b605d376c..8b5aebe7e 100644 --- a/src/types/StoredValue.ts +++ b/src/types/StoredValue.ts @@ -154,8 +154,8 @@ export class StoredValue { /** * Stores location, type and data for a gas pre-payment. */ - @jsonMember({ name: 'Prepaid', constructor: PrepaymentKind }) - prepaid?: PrepaymentKind; + @jsonMember({ name: 'Prepayment', constructor: PrepaymentKind }) + prepayment?: PrepaymentKind; /** * The stored entry point value, typically representing an entry point in a smart contract. From c02aee31e429f9025ec53c2f8406dde17008eb97 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Thu, 12 Dec 2024 23:55:52 +0100 Subject: [PATCH 08/10] Fix serialisation issues --- src/rpc/http_handler.ts | 2 +- src/rpc/request.ts | 9 ++++++--- src/types/Transaction.ts | 12 +++++++++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/rpc/http_handler.ts b/src/rpc/http_handler.ts index 2a90bf59a..f634677cd 100644 --- a/src/rpc/http_handler.ts +++ b/src/rpc/http_handler.ts @@ -56,7 +56,7 @@ export class HttpHandler implements IHandler { }; try { - const response = await this.httpClient.request(config); + const response = await this.httpClient.request(config); if (response.status < 200 || response.status >= 300) { throw new HttpError(response.status, new Error(response.statusText)); diff --git a/src/rpc/request.ts b/src/rpc/request.ts index a55e0d2ff..5e08359c3 100644 --- a/src/rpc/request.ts +++ b/src/rpc/request.ts @@ -384,11 +384,14 @@ export class PutDeployRequest { @jsonObject export class PutTransactionRequest { - @jsonMember({ constructor: TransactionWrapper }) - transactionWrapper: TransactionWrapper; + @jsonMember({ + constructor: TransactionWrapper, + serializer: val => TransactionWrapper.toJSON(val) + }) + transaction: TransactionWrapper; constructor(transaction: TransactionWrapper) { - this.transactionWrapper = transaction; + this.transaction = transaction; } } diff --git a/src/types/Transaction.ts b/src/types/Transaction.ts index a8d62f0e5..7ce8badef 100644 --- a/src/types/Transaction.ts +++ b/src/types/Transaction.ts @@ -560,6 +560,11 @@ export class Transaction { throw new Error("The JSON can't be parsed as a Transaction."); } + + static toJSON(tx: Transaction) { + const serializer = new TypedJSON(Transaction); + return serializer.toPlainJson(tx); + } } /** @@ -572,7 +577,7 @@ export class TransactionWrapper { * The deployment object associated with the transaction, if applicable. * This will contain the details of the deploy transaction. */ - @jsonMember({ name: 'Deploy', constructor: () => Deploy }) + @jsonMember(() => Deploy, { name: 'Deploy' }) deploy?: Deploy; /** @@ -591,6 +596,11 @@ export class TransactionWrapper { this.deploy = deploy; this.transactionV1 = transactionV1; } + + static toJSON(wrapper: TransactionWrapper) { + const serializer = new TypedJSON(TransactionWrapper); + return serializer.toPlainJson(wrapper); + } } /** From 63f9d023f528bf03db50820d9e47a0c6e17e6af2 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Thu, 12 Dec 2024 23:57:27 +0100 Subject: [PATCH 09/10] Improve rpc client errors processing --- src/rpc/error.ts | 14 ++++++++++---- src/rpc/rpc_client.ts | 9 +++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/rpc/error.ts b/src/rpc/error.ts index 3507955b6..ffeef1f93 100644 --- a/src/rpc/error.ts +++ b/src/rpc/error.ts @@ -20,24 +20,30 @@ export class RpcError extends Error { } @jsonObject -export class HttpError extends Error { +export class HttpError extends Error { @jsonMember({ constructor: Error }) - sourceErr: Error; + sourceErr: T; @jsonMember({ constructor: Number }) statusCode: number; - constructor(statusCode = 0, sourceErr: Error = new Error()) { + constructor(statusCode = 0, sourceErr: T) { super(`Code: ${statusCode}, err: ${sourceErr.message}`); this.sourceErr = sourceErr; this.statusCode = statusCode; } - unwrap(): Error { + unwrap(): T { return this.sourceErr; } isNotFound(): boolean { return this.statusCode === 404; } + + static isHttpError( + err: any | HttpError + ): err is HttpError { + return err?.statusCode && err.sourceErr; + } } diff --git a/src/rpc/rpc_client.ts b/src/rpc/rpc_client.ts index 1b3e87425..2e651757e 100644 --- a/src/rpc/rpc_client.ts +++ b/src/rpc/rpc_client.ts @@ -63,6 +63,7 @@ import { Hash, Transaction } from '../types'; +import { HttpError } from './error'; export class RpcClient implements IClient { private handler: IHandler; @@ -1283,13 +1284,9 @@ export class RpcClient implements IClient { const resp = await this.handler.processCall(request); if (resp.error) { - throw new Error(`RPC call failed, details: ${resp.error.message}`); + throw new HttpError(resp.error.code, resp.error); } - try { - return resp; - } catch (err) { - throw new Error(`Error parsing result: ${err.message}`); - } + return resp; } } From 0e133aa363ba0d1ae34f7a0a558c6333f2e51dc4 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Thu, 12 Dec 2024 23:59:49 +0100 Subject: [PATCH 10/10] Update version to 5.0.0-rc7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c7d2d8ea2..3dd5cbeea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "casper-js-sdk", - "version": "5.0.0-rc6", + "version": "5.0.0-rc7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "casper-js-sdk", - "version": "5.0.0-rc6", + "version": "5.0.0-rc7", "license": "Apache 2.0", "dependencies": { "@ethersproject/bignumber": "^5.0.8", diff --git a/package.json b/package.json index 2ec7495b6..83d1f12ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "casper-js-sdk", - "version": "5.0.0-rc6", + "version": "5.0.0-rc7", "license": "Apache 2.0", "description": "SDK to interact with the Casper blockchain", "homepage": "https://github.com/casper-ecosystem/casper-js-sdk#README.md",