From d8a2e00af8c2f7233980a6478fab588b2257740e Mon Sep 17 00:00:00 2001 From: Oleksandr Myshchyshyn Date: Wed, 13 Nov 2024 17:57:09 +0200 Subject: [PATCH] Update deserializer and serializer, added tests for Deploy and Transaction --- .../workflows/publish-casper-client-sdk.yml | 7 +- package-lock.json | 4 +- package.json | 2 +- src/rpc/request.ts | 144 +++++++- src/rpc/response.ts | 193 +++++++++- src/sse/event.ts | 143 +++++++- src/types/Account.ts | 10 +- src/types/AddressableEntity.ts | 14 +- src/types/AuctionState.ts | 7 +- src/types/Bid.ts | 127 ++++++- src/types/Block.ts | 267 ++++++++++++-- src/types/BlockProposer.ts | 14 +- src/types/Contract.ts | 11 +- src/types/ContractPackage.ts | 20 +- src/types/Deploy.test.ts | 182 ++++++++++ src/types/Deploy.ts | 95 ++++- src/types/DeployInfo.ts | 28 +- src/types/EntryPoint.ts | 2 +- src/types/EraEnd.ts | 74 +++- src/types/EraInfo.ts | 35 +- src/types/EraSummary.ts | 14 +- src/types/ExecutableDeployItem.ts | 83 ++++- src/types/ExecutionResult.ts | 57 ++- src/types/HexBytes.ts | 4 +- src/types/InitiatorAddr.test.ts | 2 +- src/types/InitiatorAddr.ts | 27 +- src/types/MessageTopic.ts | 12 +- src/types/MinimalBlockInfo.ts | 28 +- src/types/NamedKey.ts | 7 +- src/types/Package.ts | 10 +- src/types/PricingMode.ts | 7 +- src/types/Reservation.ts | 14 +- src/types/StoredValue.ts | 13 +- src/types/Time.ts | 26 +- src/types/Transaction.test.ts | 23 +- src/types/Transaction.ts | 153 ++++++-- src/types/TransactionEntryPoint.test.ts | 2 +- src/types/TransactionEntryPoint.ts | 15 +- src/types/TransactionScheduling.ts | 47 +-- src/types/TransactionTarget.test.ts | 2 +- src/types/TransactionTarget.ts | 63 ++-- src/types/Transfer.ts | 118 +++++- src/types/Transform.ts | 75 +++- src/types/UnbondingPurse.ts | 35 +- src/types/ValidatorWeight.ts | 25 +- src/types/clvalue/Uint512.ts | 28 +- src/types/key/Account.ts | 4 - src/types/key/BalanceHoldAddr.ts | 7 +- src/types/key/BidAddr.ts | 21 +- src/types/key/EntryPointAddr.ts | 14 +- src/types/key/Hash.ts | 16 + src/types/key/Key.ts | 340 ++++++++++++++++-- src/types/key/MessageAddr.ts | 7 +- 53 files changed, 2290 insertions(+), 388 deletions(-) create mode 100644 src/types/Deploy.test.ts diff --git a/.github/workflows/publish-casper-client-sdk.yml b/.github/workflows/publish-casper-client-sdk.yml index 2c7dd638..764fef47 100644 --- a/.github/workflows/publish-casper-client-sdk.yml +++ b/.github/workflows/publish-casper-client-sdk.yml @@ -20,12 +20,7 @@ jobs: steps: - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b #v3.0.2 - name: Set release tag - run: | - if [ ${{ github.event.release.target_commitish }} == 'feat-5.0.0' ]; then - echo "release_tag=condor" >> $GITHUB_ENV - else - echo "release_tag=latest" >> $GITHUB_ENV - fi + run: echo "release_tag=condor" >> $GITHUB_ENV - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@5b949b50c3461bbcd5a540b150c368278160234a #v3.4.0 with: diff --git a/package-lock.json b/package-lock.json index a9beab11..7f5e0361 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "casper-js-sdk", - "version": "5.0.0-rc1", + "version": "5.0.0-rc2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "casper-js-sdk", - "version": "5.0.0-rc1", + "version": "5.0.0-rc2", "license": "Apache 2.0", "dependencies": { "@ethersproject/bignumber": "^5.0.8", diff --git a/package.json b/package.json index 8849f4ed..4140f425 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "casper-js-sdk", - "version": "5.0.0-rc1", + "version": "5.0.0-rc2", "license": "Apache 2.0", "description": "SDK to interact with the Casper blockchain", "homepage": "https://github.com/casper-ecosystem/casper-js-sdk#README.md", diff --git a/src/rpc/request.ts b/src/rpc/request.ts index 7db9495e..a0db3d6a 100644 --- a/src/rpc/request.ts +++ b/src/rpc/request.ts @@ -44,7 +44,12 @@ export class RpcRequest { @jsonMember({ constructor: String }) version: string; - @jsonMember(() => ({ constructor: IDValue, isRequired: false })) + @jsonMember(() => ({ + constructor: IDValue, + isRequired: false, + deserializer: (json: string) => IDValue.fromJSON(json), + serializer: (value: IDValue) => value.toJSON() + })) id?: IDValue; @jsonMember({ constructor: String }) @@ -199,10 +204,32 @@ export class ParamGetStateEntity { @jsonObject export class AccountIdentifier { - @jsonMember(() => ({ constructor: AccountHash, isRequired: false })) + @jsonMember(() => ({ + constructor: AccountHash, + isRequired: false, + deserializer: (json: string) => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: (value: AccountHash) => { + if (!value) return; + return value.toJSON(); + } + })) accountHash?: AccountHash; - @jsonMember(() => ({ constructor: PublicKey, isRequired: false })) + @jsonMember(() => ({ + constructor: PublicKey, + isRequired: false, + deserializer: (json: string) => { + if (!json) return; + return PublicKey.fromJSON(json); + }, + serializer: (value: PublicKey) => { + if (!value) return; + return value.toJSON(); + } + })) publicKey?: PublicKey; constructor(accountHash?: AccountHash, publicKey?: PublicKey) { @@ -213,13 +240,46 @@ export class AccountIdentifier { @jsonObject export class EntityIdentifier { - @jsonMember(() => ({ constructor: AccountHash, isRequired: false })) + @jsonMember(() => ({ + constructor: AccountHash, + isRequired: false, + deserializer: (json: string) => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: (value: AccountHash) => { + if (!value) return; + return value.toJSON(); + } + })) accountHash?: AccountHash; - @jsonMember(() => ({ constructor: PublicKey, isRequired: false })) + @jsonMember(() => ({ + constructor: PublicKey, + isRequired: false, + deserializer: (json: string) => { + if (!json) return; + return PublicKey.fromJSON(json); + }, + serializer: (value: PublicKey) => { + if (!value) return; + return value.toJSON(); + } + })) publicKey?: PublicKey; - @jsonMember(() => ({ constructor: EntityAddr, isRequired: false })) + @jsonMember(() => ({ + constructor: EntityAddr, + isRequired: false, + deserializer: (json: string) => { + if (!json) return; + return EntityAddr.fromJSON(json); + }, + serializer: (value: EntityAddr) => { + if (!value) return; + return value.toJSON(); + } + })) entityAddr?: EntityAddr; constructor( @@ -428,16 +488,60 @@ export class SpeculativeExecParams { @jsonObject export class PurseIdentifier { - @jsonMember(() => ({ constructor: PublicKey, isRequired: false })) + @jsonMember(() => ({ + constructor: PublicKey, + isRequired: false, + deserializer: (json: string) => { + if (!json) return; + return PublicKey.fromJSON(json); + }, + serializer: (value: PublicKey) => { + if (!value) return; + return value.toJSON(); + } + })) mainPurseUnderPublicKey?: PublicKey; - @jsonMember(() => ({ constructor: AccountHash, isRequired: false })) + @jsonMember(() => ({ + constructor: AccountHash, + isRequired: false, + deserializer: (json: string) => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: (value: AccountHash) => { + if (!value) return; + return value.toJSON(); + } + })) mainPurseUnderAccountHash?: AccountHash; - @jsonMember(() => ({ constructor: EntityAddr, isRequired: false })) + @jsonMember(() => ({ + constructor: EntityAddr, + isRequired: false, + deserializer: (json: string) => { + if (!json) return; + return EntityAddr.fromJSON(json); + }, + serializer: (value: EntityAddr) => { + if (!value) return; + return value.toJSON(); + } + })) mainPurseUnderEntityAddr?: EntityAddr; - @jsonMember(() => ({ constructor: URef, isRequired: false })) + @jsonMember(() => ({ + constructor: URef, + isRequired: false, + deserializer: (json: string) => { + if (!json) return; + return URef.fromJSON(json); + }, + serializer: (value: URef) => { + if (!value) return; + return value.toJSON(); + } + })) purseUref?: URef; constructor( @@ -505,10 +609,26 @@ export class QueryBalanceDetailsRequest { @jsonObject export class InfoGetRewardRequest { - @jsonMember(() => ({ constructor: PublicKey, isRequired: false })) + @jsonMember(() => ({ + constructor: PublicKey, + isRequired: false, + deserializer: (json: string) => PublicKey.fromJSON(json), + serializer: (value: PublicKey) => value.toJSON() + })) validator: PublicKey; - @jsonMember({ isRequired: false, constructor: PublicKey }) + @jsonMember({ + isRequired: false, + constructor: PublicKey, + deserializer: (json: string) => { + if (!json) return; + return PublicKey.fromJSON(json); + }, + serializer: (value: PublicKey) => { + if (!value) return; + return value.toJSON(); + } + }) delegator?: PublicKey; @jsonMember({ isRequired: false, constructor: EraIdentifier }) diff --git a/src/rpc/response.ts b/src/rpc/response.ts index a634b6df..6e91bfb3 100644 --- a/src/rpc/response.ts +++ b/src/rpc/response.ts @@ -67,7 +67,12 @@ export class StateGetBalanceResult { @jsonMember({ name: 'api_version', constructor: String }) apiVersion: string; - @jsonMember(() => ({ name: 'balance_value', constructor: CLValueUInt512 })) + @jsonMember(() => ({ + name: 'balance_value', + constructor: CLValueUInt512, + deserializer: (json: string) => CLValueUInt512.fromJSON(json), + serializer: (value: CLValueUInt512) => value.toJSON() + })) balanceValue: CLValueUInt512; public rawJSON: string; @@ -194,7 +199,10 @@ export class ChainGetBlockTransfersResult { @jsonMember({ name: 'block_hash', constructor: String }) public blockHash: string; - @jsonArrayMember(Transfer, { name: 'transfers' }) + @jsonArrayMember(Transfer, { + name: 'transfers', + deserializer: (json: any) => json.map((it: string) => Transfer.fromJSON(it)) + }) public transfers: Transfer[]; public rawJSON?: string; @@ -289,7 +297,18 @@ export class InfoGetDeployResultV1Compatible { @jsonArrayMember(DeployExecutionResult, { name: 'execution_results' }) executionResults: DeployExecutionResult[]; - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) blockHash?: Hash; @jsonMember({ name: 'block_height', constructor: Number }) @@ -315,7 +334,17 @@ export class InfoGetTransactionResultV1Compatible { @jsonArrayMember(DeployExecutionResult) executionResults: DeployExecutionResult[] = []; - @jsonMember({ constructor: Hash }) + @jsonMember({ + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) blockHash?: Hash; @jsonMember({ constructor: Number }) @@ -475,7 +504,18 @@ export class ChainGetStateRootHashResult { @jsonMember({ name: 'api_version', constructor: String }) version: string; - @jsonMember({ name: 'state_root_hash', constructor: Hash }) + @jsonMember({ + name: 'state_root_hash', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) stateRootHash: Hash; rawJSON?: string; @@ -502,7 +542,18 @@ export class StatusChanges { @jsonObject export class ValidatorChanges { - @jsonMember({ name: 'public_key', constructor: PublicKey }) + @jsonMember({ + name: 'public_key', + constructor: PublicKey, + deserializer: json => { + if (!json) return; + return PublicKey.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) publicKey: PublicKey; @jsonArrayMember(StatusChanges, { name: 'status_changes' }) @@ -560,10 +611,32 @@ export class InfoGetStatusResult { @jsonMember({ name: 'reactor_state', constructor: String }) reactorState: string; - @jsonMember({ name: 'last_progress', constructor: Timestamp }) + @jsonMember({ + name: 'last_progress', + constructor: Timestamp, + deserializer: json => { + if (!json) return; + return Timestamp.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) lastProgress: Timestamp; - @jsonMember({ name: 'latest_switch_block_hash', constructor: Hash }) + @jsonMember({ + name: 'latest_switch_block_hash', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) latestSwitchBlockHash: Hash; @jsonMember({ @@ -607,7 +680,18 @@ export class PutDeployResult { @jsonMember({ name: 'api_version', constructor: String }) apiVersion: string; - @jsonMember({ name: 'deploy_hash', constructor: Hash }) + @jsonMember({ + name: 'deploy_hash', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) deployHash: Hash; rawJSON?: string; @@ -629,7 +713,18 @@ export class SpeculativeExecResult { @jsonMember({ name: 'api_version', constructor: String }) apiVersion: string; - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) blockHash: Hash; @jsonMember({ name: 'execution_result', constructor: ExecutionResult }) @@ -643,7 +738,18 @@ export class QueryBalanceResult { @jsonMember({ name: 'api_version', constructor: String }) apiVersion: string; - @jsonMember({ name: 'balance', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'balance', + constructor: CLValueUInt512, + deserializer: json => { + if (!json) return; + return CLValueUInt512.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) balance: CLValueUInt512; rawJSON?: string; @@ -654,10 +760,32 @@ export class QueryBalanceDetailsResult { @jsonMember({ name: 'api_version', constructor: String }) apiVersion: string; - @jsonMember({ name: 'total_balance', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'total_balance', + constructor: CLValueUInt512, + deserializer: json => { + if (!json) return; + return CLValueUInt512.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) totalBalance: CLValueUInt512; - @jsonMember({ name: 'available_balance', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'available_balance', + constructor: CLValueUInt512, + deserializer: json => { + if (!json) return; + return CLValueUInt512.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) availableBalance: CLValueUInt512; @jsonMember({ name: 'total_balance_proof', constructor: String }) @@ -680,10 +808,32 @@ export class InfoGetRewardResult { @jsonMember({ name: 'era_id', constructor: Number }) eraID: number; - @jsonMember({ name: 'reward_amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'reward_amount', + constructor: CLValueUInt512, + deserializer: json => { + if (!json) return; + return CLValueUInt512.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) rewardAmount: CLValueUInt512; - @jsonMember({ name: 'switch_block_hash', constructor: Hash }) + @jsonMember({ + name: 'switch_block_hash', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) switchBlockHash: Hash; rawJSON?: string; @@ -691,7 +841,18 @@ export class InfoGetRewardResult { @jsonObject export class BalanceHoldWithProof { - @jsonMember({ name: 'amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'amount', + constructor: CLValueUInt512, + deserializer: json => { + if (!json) return; + return CLValueUInt512.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) amount: CLValueUInt512; @jsonMember({ name: 'proof', constructor: String }) diff --git a/src/sse/event.ts b/src/sse/event.ts index feb14572..2a7be3c1 100644 --- a/src/sse/event.ts +++ b/src/sse/event.ts @@ -234,10 +234,20 @@ export class BlockAddedEventWrapper { @jsonObject export class DeployProcessedPayload { - @jsonMember({ name: 'deploy_hash', constructor: Hash }) + @jsonMember({ + name: 'deploy_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) deployHash: Hash; - @jsonMember({ name: 'account', constructor: PublicKey }) + @jsonMember({ + name: 'account', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) account: PublicKey; @jsonMember({ name: 'timestamp', constructor: Date }) @@ -246,7 +256,12 @@ export class DeployProcessedPayload { @jsonMember({ name: 'ttl', constructor: String }) ttl: string; - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) blockHash: Hash; @jsonMember({ name: 'execution_result', constructor: ExecutionResultV1 }) @@ -270,7 +285,12 @@ export class DeployAcceptedEvent { @jsonObject export class DeployExpiredPayload { - @jsonMember({ name: 'deploy_hash', constructor: Hash }) + @jsonMember({ + name: 'deploy_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) deployHash: Hash; } @@ -390,13 +410,23 @@ export class TransactionExpiredEvent { @jsonObject export class TransactionProcessedPayload { - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) blockHash: Hash; @jsonMember({ name: 'transaction_hash', constructor: TransactionHash }) transactionHash: TransactionHash; - @jsonMember({ name: 'initiator_addr', constructor: InitiatorAddr }) + @jsonMember({ + name: 'initiator_addr', + constructor: InitiatorAddr, + deserializer: json => InitiatorAddr.fromJSON(json), + serializer: value => value.toJSON() + }) initiatorAddr: InitiatorAddr; @jsonMember({ name: 'timestamp', constructor: Date }) @@ -467,37 +497,79 @@ export class TransactionProcessedEvent { @jsonObject export class FinalitySignatureV1 { - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) blockHash: Hash; @jsonMember({ name: 'era_id', constructor: Number }) eraID: number; - @jsonMember({ name: 'signature', constructor: HexBytes }) + @jsonMember({ + name: 'signature', + constructor: HexBytes, + deserializer: json => HexBytes.fromJSON(json), + serializer: value => value.toJSON() + }) signature: HexBytes; - @jsonMember({ name: 'public_key', constructor: PublicKey }) + @jsonMember({ + name: 'public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) publicKey: PublicKey; } @jsonObject export class FinalitySignatureV2 { - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) blockHash: Hash; @jsonMember({ isRequired: false, name: 'block_height', constructor: Number }) blockHeight?: number; - @jsonMember({ isRequired: false, name: 'chain_name_hash', constructor: Hash }) + @jsonMember({ + isRequired: false, + name: 'chain_name_hash', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) chainNameHash?: Hash; @jsonMember({ name: 'era_id', constructor: Number }) eraID: number; - @jsonMember({ name: 'signature', constructor: HexBytes }) + @jsonMember({ + name: 'signature', + constructor: HexBytes, + deserializer: json => HexBytes.fromJSON(json), + serializer: value => value.toJSON() + }) signature: HexBytes; - @jsonMember({ name: 'public_key', constructor: PublicKey }) + @jsonMember({ + name: 'public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) publicKey: PublicKey; } @@ -520,7 +592,12 @@ export class FinalitySignatureWrapper { @jsonObject export class FinalitySignature { - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) blockHash: Hash; @jsonMember({ @@ -533,17 +610,35 @@ export class FinalitySignature { @jsonMember({ isRequired: false, name: 'chain_name_hash', - constructor: Hash + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } }) chainNameHash?: Hash; @jsonMember({ name: 'era_id', constructor: Number }) eraID: number; - @jsonMember({ name: 'signature', constructor: HexBytes }) + @jsonMember({ + name: 'signature', + constructor: HexBytes, + deserializer: json => HexBytes.fromJSON(json), + serializer: value => value.toJSON() + }) signature: HexBytes; - @jsonMember({ name: 'public_key', constructor: PublicKey }) + @jsonMember({ + name: 'public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) publicKey: PublicKey; @jsonMember(FinalitySignatureV1, { @@ -626,10 +721,20 @@ export class FaultPayload { @jsonMember({ name: 'era_id', constructor: Number }) eraID: number; - @jsonMember({ name: 'public_key', constructor: PublicKey }) + @jsonMember({ + name: 'public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) publicKey: PublicKey; - @jsonMember({ name: 'timestamp', constructor: Timestamp }) + @jsonMember({ + name: 'timestamp', + constructor: Timestamp, + deserializer: json => Timestamp.fromJSON(json), + serializer: value => value.toJSON() + }) timestamp: Timestamp; } diff --git a/src/types/Account.ts b/src/types/Account.ts index 556bc05e..75d67596 100644 --- a/src/types/Account.ts +++ b/src/types/Account.ts @@ -6,7 +6,9 @@ import { NamedKeys } from './NamedKey'; export class AssociatedKey { @jsonMember({ name: 'account_hash', - constructor: AccountHash + constructor: AccountHash, + deserializer: json => AccountHash.fromJSON(json), + serializer: value => value.toJSON() }) accountHash: AccountHash; @@ -36,6 +38,8 @@ export class ActionThresholds { export class Account { @jsonMember({ name: 'account_hash', + deserializer: json => AccountHash.fromJSON(json), + serializer: value => value.toJSON(), constructor: AccountHash }) accountHash: AccountHash; @@ -48,7 +52,9 @@ export class Account { @jsonMember({ name: 'main_purse', - constructor: URef + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: value => value.toJSON() }) mainPurse: URef; diff --git a/src/types/AddressableEntity.ts b/src/types/AddressableEntity.ts index 7dfc8b19..713ef52d 100644 --- a/src/types/AddressableEntity.ts +++ b/src/types/AddressableEntity.ts @@ -14,7 +14,15 @@ export class EntityKind { @jsonMember({ name: 'Account', - constructor: AccountHash + constructor: AccountHash, + deserializer: json => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } }) account?: AccountHash; @@ -62,7 +70,9 @@ export class AddressableEntity { @jsonMember({ name: 'main_purse', - constructor: URef + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: value => value.toJSON() }) mainPurse: URef; diff --git a/src/types/AuctionState.ts b/src/types/AuctionState.ts index 3df3a543..147f5e6b 100644 --- a/src/types/AuctionState.ts +++ b/src/types/AuctionState.ts @@ -5,7 +5,12 @@ import { PublicKey } from './keypair'; @jsonObject export class PublicKeyAndBid { - @jsonMember({ name: 'public_key', constructor: PublicKey }) + @jsonMember({ + name: 'public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) publicKey: PublicKey; @jsonMember({ name: 'bid', constructor: Bid }) diff --git a/src/types/Bid.ts b/src/types/Bid.ts index c2c04eeb..cab32b86 100644 --- a/src/types/Bid.ts +++ b/src/types/Bid.ts @@ -8,13 +8,23 @@ export class VestingSchedule { @jsonMember({ name: 'initial_release_timestamp_millis', constructor: Number }) initialReleaseTimestampMillis: number; - @jsonArrayMember(CLValueUInt512, { name: 'locked_amounts' }) + @jsonArrayMember(CLValueUInt512, { + name: 'locked_amounts', + serializer: (value: CLValueUInt512[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => + json.map((it: string) => CLValueUInt512.fromJSON(it)) + }) lockedAmounts: CLValueUInt512[]; } @jsonObject export class ValidatorBid { - @jsonMember({ name: 'bonding_purse', constructor: URef }) + @jsonMember({ + name: 'bonding_purse', + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: value => value.toJSON() + }) bondingPurse: URef; @jsonMember({ name: 'delegation_rate', constructor: Number }) @@ -23,7 +33,12 @@ export class ValidatorBid { @jsonMember({ name: 'inactive', constructor: Boolean }) inactive: boolean; - @jsonMember({ name: 'staked_amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'staked_amount', + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) stakedAmount: CLValueUInt512; @jsonMember({ name: 'minimum_delegation_amount', constructor: Number }) @@ -62,16 +77,36 @@ export class Delegator { ); } - @jsonMember({ name: 'bonding_purse', constructor: URef }) + @jsonMember({ + name: 'bonding_purse', + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: value => value.toJSON() + }) bondingPurse: URef; - @jsonMember({ name: 'staked_amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'staked_amount', + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) stakedAmount: CLValueUInt512; - @jsonMember({ name: 'delegator_public_key', constructor: PublicKey }) + @jsonMember({ + name: 'delegator_public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) delegatorPublicKey: PublicKey; - @jsonMember({ name: 'validator_public_key', constructor: PublicKey }) + @jsonMember({ + name: 'validator_public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) validatorPublicKey: PublicKey; @jsonMember({ name: 'vesting_schedule', constructor: VestingSchedule }) @@ -80,7 +115,12 @@ export class Delegator { @jsonObject export class Bid { - @jsonMember({ name: 'bonding_purse', constructor: URef }) + @jsonMember({ + name: 'bonding_purse', + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: value => value.toJSON() + }) bondingPurse: URef; @jsonMember({ name: 'delegation_rate', constructor: Number }) @@ -89,9 +129,20 @@ export class Bid { @jsonMember({ name: 'inactive', constructor: Boolean }) inactive: boolean; - @jsonMember({ name: 'staked_amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'staked_amount', + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) stakedAmount: CLValueUInt512; - @jsonMember({ name: 'validator_public_key', constructor: PublicKey }) + + @jsonMember({ + name: 'validator_public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) validatorPublicKey: PublicKey; @jsonArrayMember(Delegator, { name: 'delegators' }) @@ -103,16 +154,36 @@ export class Bid { @jsonObject export class DelegatorV1 { - @jsonMember({ name: 'bonding_purse', constructor: URef }) + @jsonMember({ + name: 'bonding_purse', + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: value => value.toJSON() + }) bondingPurse: URef; - @jsonMember({ name: 'staked_amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'staked_amount', + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) stakedAmount: CLValueUInt512; - @jsonMember({ name: 'delegatee', constructor: PublicKey }) + @jsonMember({ + name: 'delegatee', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) delegatee: PublicKey; - @jsonMember({ name: 'public_key', constructor: PublicKey }) + @jsonMember({ + name: 'public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) publicKey: PublicKey; @jsonMember({ name: 'vesting_schedule', constructor: VestingSchedule }) @@ -124,10 +195,20 @@ export class Credit { @jsonMember({ name: 'era_id', constructor: Number }) eraID: number; - @jsonMember({ name: 'validator_public_key', constructor: PublicKey }) + @jsonMember({ + name: 'validator_public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) validatorPublicKey: PublicKey; - @jsonMember({ name: 'amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'amount', + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) amount: CLValueUInt512; } @@ -136,9 +217,19 @@ export class Bridge { @jsonMember({ name: 'era_id', constructor: Number }) eraID: number; - @jsonMember({ name: 'old_validator_public_key', constructor: PublicKey }) + @jsonMember({ + name: 'old_validator_public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) oldValidatorPublicKey: PublicKey; - @jsonMember({ name: 'new_validator_public_key', constructor: PublicKey }) + @jsonMember({ + name: 'new_validator_public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) newValidatorPublicKey: PublicKey; } diff --git a/src/types/Block.ts b/src/types/Block.ts index 5d107907..c72e8949 100644 --- a/src/types/Block.ts +++ b/src/types/Block.ts @@ -13,37 +13,86 @@ import { HexBytes } from './HexBytes'; @jsonObject export class Proof { - @jsonMember(() => ({ constructor: PublicKey, name: 'public_key' })) + @jsonMember(() => ({ + constructor: PublicKey, + name: 'public_key', + deserializer: (json: string) => PublicKey.fromJSON(json), + serializer: (value: PublicKey) => value.toJSON() + })) public publicKey: PublicKey; - @jsonMember(() => ({ name: 'signature', constructor: HexBytes })) + @jsonMember(() => ({ + name: 'signature', + constructor: HexBytes, + deserializer: (json: string) => HexBytes.fromJSON(json), + serializer: (value: HexBytes) => value.toJSON() + })) public signature: HexBytes; } @jsonObject export class Block { - @jsonMember(() => ({ name: 'hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public hash: Hash; @jsonMember({ name: 'height', constructor: Number }) public height: number; - @jsonMember(() => ({ name: 'state_root_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'state_root_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public stateRootHash: Hash; - @jsonMember(() => ({ name: 'last_switch_block_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'last_switch_block_hash', + constructor: Hash, + deserializer: (json: string) => (json ? Hash.fromJSON(json) : null), + serializer: (value: Hash) => (value ? value.toJSON() : null), + preserveNull: true + })) public lastSwitchBlockHash: Hash | null; - @jsonMember(() => ({ name: 'parent_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'parent_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public parentHash: Hash; @jsonMember({ name: 'era_id', constructor: Number }) public eraID: number; - @jsonMember(() => ({ name: 'timestamp', constructor: Timestamp })) + @jsonMember(() => ({ + name: 'timestamp', + constructor: Timestamp, + deserializer: (json: string) => Timestamp.fromJSON(json), + serializer: (value: Timestamp) => value.toJSON() + })) public timestamp: Timestamp; - @jsonMember(() => ({ name: 'accumulated_seed', constructor: Hash })) + @jsonMember(() => ({ + name: 'accumulated_seed', + constructor: Hash, + deserializer: (json: string) => { + if (!json) return; + + return Hash.fromJSON(json); + }, + serializer: (value: Hash) => { + if (!value) return; + + return value.toJSON(); + } + })) public accumulatedSeed?: Hash; @jsonMember({ name: 'random_bit', constructor: Boolean }) @@ -52,7 +101,12 @@ export class Block { @jsonMember({ name: 'current_gas_price', constructor: Number }) public currentGasPrice: number; - @jsonMember(() => ({ name: 'proposer', constructor: Proposer })) + @jsonMember(() => ({ + name: 'proposer', + constructor: Proposer, + deserializer: (json: string) => Proposer.fromJSON(json), + serializer: (value: Proposer) => value.toJSON() + })) public proposer: Proposer; @jsonMember({ name: 'protocol_version', constructor: String }) @@ -61,7 +115,10 @@ export class Block { @jsonMember(() => ({ name: 'era_end', constructor: EraEnd })) public eraEnd?: EraEnd; - @jsonArrayMember(() => BlockTransaction, { name: 'transactions' }) + @jsonArrayMember(() => BlockTransaction, { + name: 'transactions', + deserializer: (json: string) => BlockTransaction.fromJSON(json) + }) public transactions: BlockTransaction[]; @jsonArrayMember(Number, { name: 'rewarded_signatures' }) @@ -209,7 +266,12 @@ export class BlockTransaction { @jsonMember({ name: 'version', constructor: Number }) public version: TransactionVersion; - @jsonMember(() => ({ name: 'hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public hash: Hash; constructor( @@ -347,7 +409,12 @@ export function parseBlockTransactions(data: string): BlockTransaction[] { @jsonObject export class BlockV1 { - @jsonMember(() => ({ name: 'hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public hash: Hash; @jsonMember({ name: 'header', constructor: () => BlockHeaderV1 }) @@ -362,19 +429,37 @@ export class BlockV1 { @jsonObject export class BlockBodyV1 { - @jsonArrayMember(() => Hash, { name: 'deploy_hashes' }) + @jsonArrayMember(() => Hash, { + name: 'deploy_hashes', + serializer: (value: Hash[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => json.map((it: string) => Hash.fromJSON(it)) + }) public deployHashes: Hash[]; - @jsonMember(() => ({ name: 'proposer', constructor: Proposer })) + @jsonMember(() => ({ + name: 'proposer', + constructor: Proposer, + deserializer: (json: string) => Proposer.fromJSON(json), + serializer: (value: Proposer) => value.toJSON() + })) public proposer: Proposer; - @jsonArrayMember(() => Hash, { name: 'transfer_hashes' }) + @jsonArrayMember(() => Hash, { + name: 'transfer_hashes', + serializer: (value: Hash[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => json.map((it: string) => Hash.fromJSON(it)) + }) public transferHashes: Hash[]; } @jsonObject export class BlockV2 { - @jsonMember(() => ({ name: 'hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public hash: Hash; @jsonMember({ name: 'header', constructor: () => BlockHeaderV2 }) @@ -386,10 +471,26 @@ export class BlockV2 { @jsonObject export class BlockHeaderV1 { - @jsonMember(() => ({ name: 'accumulated_seed', constructor: Hash })) + @jsonMember(() => ({ + name: 'accumulated_seed', + constructor: Hash, + deserializer: (json: string) => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: (value: Hash) => { + if (!value) return; + return value.toJSON(); + } + })) public accumulatedSeed?: Hash; - @jsonMember(() => ({ name: 'body_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'body_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public bodyHash: Hash; @jsonMember({ name: 'era_id', constructor: Number }) @@ -398,7 +499,12 @@ export class BlockHeaderV1 { @jsonMember({ name: 'height', constructor: Number }) public height: number; - @jsonMember(() => ({ name: 'parent_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'parent_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public parentHash: Hash; @jsonMember({ name: 'protocol_version', constructor: String }) @@ -407,10 +513,20 @@ export class BlockHeaderV1 { @jsonMember({ name: 'random_bit', constructor: Boolean }) public randomBit: boolean; - @jsonMember(() => ({ name: 'state_root_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'state_root_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public stateRootHash: Hash; - @jsonMember(() => ({ name: 'timestamp', constructor: Timestamp })) + @jsonMember(() => ({ + name: 'timestamp', + constructor: Timestamp, + deserializer: (json: string) => Timestamp.fromJSON(json), + serializer: (value: Timestamp) => value.toJSON() + })) public timestamp: Timestamp; @jsonMember(() => ({ name: 'era_end', constructor: EraEndV1 })) @@ -419,10 +535,26 @@ export class BlockHeaderV1 { @jsonObject export class BlockHeaderV2 { - @jsonMember(() => ({ name: 'accumulated_seed', constructor: Hash })) + @jsonMember(() => ({ + name: 'accumulated_seed', + constructor: Hash, + deserializer: (json: string) => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: (value: Hash) => { + if (!value) return; + return value.toJSON(); + } + })) public accumulatedSeed?: Hash; - @jsonMember(() => ({ name: 'body_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'body_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public bodyHash: Hash; @jsonMember({ name: 'era_id', constructor: Number }) @@ -434,10 +566,20 @@ export class BlockHeaderV2 { @jsonMember({ name: 'height', constructor: Number }) public height: number; - @jsonMember(() => ({ name: 'parent_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'parent_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public parentHash: Hash; - @jsonMember(() => ({ name: 'proposer', constructor: Proposer })) + @jsonMember(() => ({ + name: 'proposer', + constructor: Proposer, + deserializer: (json: string) => Proposer.fromJSON(json), + serializer: (value: Proposer) => value.toJSON() + })) public proposer: Proposer; @jsonMember({ name: 'protocol_version', constructor: String }) @@ -446,13 +588,28 @@ export class BlockHeaderV2 { @jsonMember({ name: 'random_bit', constructor: Boolean }) public randomBit: boolean; - @jsonMember(() => ({ name: 'state_root_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'state_root_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public stateRootHash: Hash; - @jsonMember(() => ({ name: 'last_switch_block_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'last_switch_block_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public lastSwitchBlockHash: Hash; - @jsonMember(() => ({ name: 'timestamp', constructor: Timestamp })) + @jsonMember(() => ({ + name: 'timestamp', + constructor: Timestamp, + deserializer: (json: string) => Timestamp.fromJSON(json), + serializer: (value: Timestamp) => value.toJSON() + })) public timestamp: Timestamp; @jsonMember(() => ({ name: 'era_end', constructor: EraEndV2 })) @@ -461,7 +618,11 @@ export class BlockHeaderV2 { @jsonObject export class BlockBodyV2 { - @jsonArrayMember(BlockTransaction, { name: 'transactions' }) + @jsonArrayMember(BlockTransaction, { + name: 'transactions', + deserializer: (json: any) => + json.map((it: string) => BlockTransaction.fromJSON(it)) + }) public transactions: BlockTransaction[]; @jsonArrayMember(Number, { name: 'rewarded_signatures' }) @@ -497,10 +658,26 @@ export class BlockHeaderWrapper { @jsonObject export class BlockHeader { - @jsonMember(() => ({ name: 'accumulated_seed', constructor: Hash })) + @jsonMember(() => ({ + name: 'accumulated_seed', + constructor: Hash, + deserializer: (json: string) => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: (value: Hash) => { + if (!value) return; + return value.toJSON(); + } + })) public accumulatedSeed?: Hash; - @jsonMember(() => ({ name: 'body_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'body_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public bodyHash: Hash; @jsonMember({ name: 'era_id', constructor: Number }) @@ -512,10 +689,20 @@ export class BlockHeader { @jsonMember({ name: 'height', constructor: Number }) public height: number; - @jsonMember(() => ({ name: 'parent_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'parent_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public parentHash: Hash; - @jsonMember(() => ({ name: 'proposer', constructor: Proposer })) + @jsonMember(() => ({ + name: 'proposer', + constructor: Proposer, + deserializer: (json: string) => Proposer.fromJSON(json), + serializer: (value: Proposer) => value.toJSON() + })) public proposer: Proposer; @jsonMember({ name: 'protocol_version', constructor: String }) @@ -524,10 +711,20 @@ export class BlockHeader { @jsonMember({ name: 'random_bit', constructor: Boolean }) public randomBit: boolean; - @jsonMember(() => ({ name: 'state_root_hash', constructor: Hash })) + @jsonMember(() => ({ + name: 'state_root_hash', + constructor: Hash, + deserializer: (json: string) => Hash.fromJSON(json), + serializer: (value: Hash) => value.toJSON() + })) public stateRootHash: Hash; - @jsonMember(() => ({ name: 'timestamp', constructor: Timestamp })) + @jsonMember(() => ({ + name: 'timestamp', + constructor: Timestamp, + deserializer: (json: string) => Timestamp.fromJSON(json), + serializer: (value: Timestamp) => value.toJSON() + })) public timestamp: Timestamp; @jsonMember(() => ({ name: 'era_end', constructor: EraEnd })) diff --git a/src/types/BlockProposer.ts b/src/types/BlockProposer.ts index 715f9387..456d4709 100644 --- a/src/types/BlockProposer.ts +++ b/src/types/BlockProposer.ts @@ -6,7 +6,19 @@ export class Proposer { @jsonMember({ name: 'isSystem', constructor: Boolean }) isSystem: boolean; - @jsonMember({ name: 'publicKey', constructor: keypair.PublicKey }) + @jsonMember({ + name: 'publicKey', + constructor: keypair.PublicKey, + deserializer: json => { + if (!json) return; + + return keypair.PublicKey.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) publicKey?: keypair.PublicKey; constructor(isSystem = false, publicKey?: keypair.PublicKey) { diff --git a/src/types/Contract.ts b/src/types/Contract.ts index 3b57e87f..2f5b8ecd 100644 --- a/src/types/Contract.ts +++ b/src/types/Contract.ts @@ -8,11 +8,18 @@ import { NamedKeys } from './NamedKey'; export class Contract { @jsonMember({ name: 'contract_package_hash', - constructor: ContractPackageHash + constructor: ContractPackageHash, + deserializer: json => ContractPackageHash.fromJSON(json), + serializer: (value: ContractPackageHash) => value.toJSON() }) contractPackageHash: ContractPackageHash; - @jsonMember({ name: 'contract_wasm_hash', constructor: ContractHash }) + @jsonMember({ + name: 'contract_wasm_hash', + constructor: ContractHash, + deserializer: json => ContractHash.fromJSON(json), + serializer: (value: ContractHash) => value.toJSON() + }) contractWasmHash: ContractHash; @jsonArrayMember(EntryPointV1, { name: 'entry_points' }) diff --git a/src/types/ContractPackage.ts b/src/types/ContractPackage.ts index 542100a6..89d3007e 100644 --- a/src/types/ContractPackage.ts +++ b/src/types/ContractPackage.ts @@ -20,7 +20,11 @@ export class ContractGroup { @jsonMember({ name: 'group', constructor: String }) group: string; - @jsonArrayMember(URef, { name: 'keys' }) + @jsonArrayMember(URef, { + name: 'keys', + serializer: (value: URef[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => json.map((it: string) => URef.fromJSON(it)) + }) keys: URef[]; constructor(group: string, keys: URef[]) { @@ -31,7 +35,12 @@ export class ContractGroup { @jsonObject export class ContractVersion { - @jsonMember({ name: 'contract_hash', constructor: ContractHash }) + @jsonMember({ + name: 'contract_hash', + constructor: ContractHash, + deserializer: json => ContractHash.fromJSON(json), + serializer: value => value.toJSON() + }) contractHash: ContractHash; @jsonMember({ name: 'contract_version', constructor: Number }) @@ -53,7 +62,12 @@ export class ContractVersion { @jsonObject export class ContractPackage { - @jsonMember({ name: 'access_key', constructor: URef }) + @jsonMember({ + name: 'access_key', + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: value => value.toJSON() + }) accessKey: URef; @jsonArrayMember(DisabledContractVersion, { name: 'disabled_versions' }) diff --git a/src/types/Deploy.test.ts b/src/types/Deploy.test.ts new file mode 100644 index 00000000..1ae041cf --- /dev/null +++ b/src/types/Deploy.test.ts @@ -0,0 +1,182 @@ +import { expect, assert } from 'chai'; +import { TypedJSON } from 'typedjson'; + +import { + ExecutableDeployItem, + TransferDeployItem +} from './ExecutableDeployItem'; +import { Deploy, DeployHeader } from './Deploy'; +import { PrivateKey } from './keypair/PrivateKey'; +import { KeyAlgorithm } from './keypair/Algorithm'; +import { Duration, Timestamp } from './Time'; +import { Hash } from './key'; +import { dehumanizerTTL, humanizerTTL } from './SerializationUtils'; + +describe('Deploy', () => { + it('should stringify/parse DeployHeader correctly', async function() { + const key = await PrivateKey.generate(KeyAlgorithm.ED25519); + + const deployHeader = new DeployHeader( + 'test-network', + Hash.createHashArray(Uint8Array.from(Array(32).fill(2))), + 10, + new Timestamp(new Date(123456)), + new Duration(654321), + key.publicKey, + new Hash(Uint8Array.from(Array(32).fill(42))) + ); + + const serializer = new TypedJSON(DeployHeader); + const json = serializer.stringify(deployHeader); + const deployHeader1 = serializer.parse(json); + expect(deployHeader1).to.deep.equal(deployHeader); + }); + + it('should allow to extract data from Transfer', async function() { + const senderKey = await PrivateKey.generate(KeyAlgorithm.ED25519); + const recipientKey = await PrivateKey.generate(KeyAlgorithm.ED25519); + const networkName = 'test-network'; + const paymentAmount = '10000000000000'; + const transferAmount = '10'; + const id = 34; + const executableDeployItem = new ExecutableDeployItem(); + + const deployHeader = new DeployHeader( + networkName, + undefined, + undefined, + undefined, + undefined, + senderKey.publicKey, + undefined + ); + + executableDeployItem.transfer = TransferDeployItem.newTransfer( + transferAmount, + recipientKey.publicKey, + undefined, + id + ); + + const payment = ExecutableDeployItem.standardPayment(paymentAmount); + let deploy = Deploy.fromHeaderAndItems( + deployHeader, + payment, + executableDeployItem + ); + await deploy.sign(senderKey); + await deploy.sign(recipientKey); + + const json = Deploy.toJson(deploy); + + deploy = Deploy.fromJSON(json); + + const deployPayment = deploy.payment.getArgByName('amount')!.toString(); + const deployTransferAmount = deploy.session + .getArgByName('amount')! + .toString(); + const deployId = deploy.session.getArgByName('id')!.toString(); + + assert.isTrue(deploy.isTransfer()); + assert.isTrue(deploy.isStandardPayment()); + assert.deepEqual(deploy.header.account, senderKey.publicKey); + assert.deepEqual(deployPayment, paymentAmount); + assert.deepEqual(deployPayment, paymentAmount); + assert.deepEqual(deployTransferAmount, transferAmount); + assert.deepEqual( + deploy.session.getArgByName('target')!.publicKey, + recipientKey.publicKey + ); + assert.deepEqual(parseInt(deployId, 10), id); + assert.deepEqual(deploy.approvals[0].signer, senderKey.publicKey); + assert.deepEqual(deploy.approvals[1].signer, recipientKey.publicKey); + }); + + it('Should convert ms to humanized string', function() { + const strTtl30m = humanizerTTL(1800000); + const strTtl45m = humanizerTTL(2700000); + const strTtl1h = humanizerTTL(3600000); + const strTtl1h30m = humanizerTTL(5400000); + const strTtl1day = humanizerTTL(86400000); + const strTtlCustom = humanizerTTL(86103000); + + expect(strTtl30m).to.be.eq('30m'); + expect(strTtl45m).to.be.eq('45m'); + expect(strTtl1h).to.be.eq('1h'); + expect(strTtl1h30m).to.be.eq('1h 30m'); + expect(strTtl1day).to.be.eq('1day'); + expect(strTtlCustom).to.be.eq('23h 55m 3s'); + }); + + it('Should convert humanized string to ms', function() { + const msTtl30m = dehumanizerTTL('30m'); + const msTtl45m = dehumanizerTTL('45m'); + const msTtl1h = dehumanizerTTL('1h'); + const msTtl1h30m = dehumanizerTTL('1h 30m'); + const msTtl1day = dehumanizerTTL('1day'); + const msTtlCustom = dehumanizerTTL('23h 55m 3s'); + + expect(msTtl30m).to.be.eq(1800000); + expect(msTtl45m).to.be.eq(2700000); + expect(msTtl1h).to.be.eq(3600000); + expect(msTtl1h30m).to.be.eq(5400000); + expect(msTtl1day).to.be.eq(86400000); + expect(msTtlCustom).to.be.eq(86103000); + }); + + it('Is possible to chain deploys using dependencies', async () => { + const senderKey = await PrivateKey.generate(KeyAlgorithm.ED25519); + const recipientKey = await PrivateKey.generate(KeyAlgorithm.ED25519); + const networkName = 'test-network'; + const paymentAmount = '10000000000000'; + const transferAmount = '10'; + const transferId = 35; + const payment = ExecutableDeployItem.standardPayment(paymentAmount); + + const executableDeployItem = new ExecutableDeployItem(); + + const deployHeader = new DeployHeader( + networkName, + undefined, + undefined, + undefined, + undefined, + senderKey.publicKey, + undefined + ); + + executableDeployItem.transfer = TransferDeployItem.newTransfer( + transferAmount, + recipientKey.publicKey, + undefined, + transferId + ); + + const firstDeploy = Deploy.fromHeaderAndItems( + deployHeader, + payment, + executableDeployItem + ); + + // Build second deploy with the first one as a dependency. + const gasPrice = 1; + const ttl = 1800000; + const dependencies = [firstDeploy.hash]; + const secondDeployHeader = new DeployHeader( + networkName, + dependencies, + gasPrice, + undefined, + new Duration(ttl), + senderKey.publicKey + ); + + const secondDeploy = Deploy.fromHeaderAndItems( + secondDeployHeader, + payment, + executableDeployItem + ); + + assert.deepEqual(secondDeploy.header.dependencies, [firstDeploy.hash]); + }); +}); diff --git a/src/types/Deploy.ts b/src/types/Deploy.ts index b140e22f..c9080751 100644 --- a/src/types/Deploy.ts +++ b/src/types/Deploy.ts @@ -20,30 +20,62 @@ import { ClassicMode, PricingMode } from './PricingMode'; import { TransactionTarget } from './TransactionTarget'; import { TransactionScheduling } from './TransactionScheduling'; import { ExecutableDeployItem } from './ExecutableDeployItem'; -import { byteHash } from './ByteConverters'; +import { byteHash, toBytesU32 } from './ByteConverters'; import { Conversions } from './Conversions'; @jsonObject export class DeployHeader { - @jsonMember({ constructor: PublicKey }) + @jsonMember({ + constructor: PublicKey, + deserializer: json => { + if (!json) return; + return PublicKey.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) public account?: PublicKey; - @jsonMember({ constructor: Hash }) + @jsonMember({ + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) public bodyHash?: Hash; @jsonMember({ name: 'chain_name', constructor: String }) public chainName = ''; - @jsonArrayMember(Hash, { name: 'dependencies' }) + @jsonArrayMember(Hash, { + name: 'dependencies', + serializer: (value: Hash[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => json.map((it: string) => Hash.fromJSON(it)) + }) public dependencies: Hash[] = []; @jsonMember({ name: 'gas_price', constructor: Number }) public gasPrice = 1; - @jsonMember({ constructor: Timestamp }) + @jsonMember({ + constructor: Timestamp, + deserializer: json => Timestamp.fromJSON(json), + serializer: value => value.toJSON() + }) public timestamp: Timestamp = new Timestamp(new Date()); - @jsonMember({ constructor: Duration }) + @jsonMember({ + constructor: Duration, + deserializer: json => Duration.fromJSON(json), + serializer: value => value.toJSON() + }) public ttl: Duration = new Duration(30 * 60 * 1000); constructor( @@ -123,7 +155,10 @@ export class Deploy { @jsonArrayMember(() => Approval) public approvals: Approval[] = []; - @jsonMember({ constructor: Hash }) + @jsonMember({ + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) public hash: Hash; @jsonMember({ constructor: DeployHeader }) @@ -193,6 +228,15 @@ export class Deploy { this.approvals.push(new Approval(keys.publicKey, signature)); } + toBytes(): Uint8Array { + return concat([ + this.header.toBytes(), + this.hash.toBytes(), + concat([this.payment.bytes(), this.session.bytes()]), + serializeApprovals(this.approvals) + ]); + } + /** * Sets already generated signature * @@ -346,6 +390,25 @@ export class Deploy { return { deploy: serializer.toPlainJson(deploy) }; }; + + /** + * Identifies whether this `Deploy` represents a transfer of CSPR + * @returns `true` if the `Deploy` is a `Transfer`, and `false` otherwise + */ + public isTransfer(): boolean { + return this.session.isTransfer(); + } + + /** + * Identifies whether this `Deploy` represents a standard payment, like that of gas payment + * @returns `true` if the `Deploy` is a standard payment, and `false` otherwise + */ + public isStandardPayment(): boolean { + if (this.payment.isModuleBytes()) { + return this.payment.asModuleBytes()?.moduleBytes.length === 0; + } + return false; + } } /** @@ -392,6 +455,24 @@ export function makeDeploy( return Deploy.fromHeaderAndItems(header, payment, session); } +/** + * Serializes an array of `Approval`s into a `Uint8Array` typed byte array + * @param approvals An array of `Approval`s to be serialized + * @returns `Uint8Array` typed byte array that can be deserialized to an array of `Approval`s + */ +export const serializeApprovals = (approvals: Approval[]): Uint8Array => { + const len = toBytesU32(approvals.length); + const bytes = concat( + approvals.map(approval => { + return concat([ + Uint8Array.from(Buffer.from(approval.signer.toString(), 'hex')), + Uint8Array.from(Buffer.from(approval.signature.toString(), 'hex')) + ]); + }) + ); + return concat([len, bytes]); +}; + /** @deprecated The parameters of a `Deploy` object. Use Deploy.fromHeaderAndItems */ export class DeployParams { /** diff --git a/src/types/DeployInfo.ts b/src/types/DeployInfo.ts index f9137565..87977dc3 100644 --- a/src/types/DeployInfo.ts +++ b/src/types/DeployInfo.ts @@ -3,18 +3,38 @@ import { Hash, AccountHash, URef, TransferHash } from './key'; @jsonObject export class DeployInfo { - @jsonMember({ name: 'deploy_hash', constructor: Hash }) + @jsonMember({ + name: 'deploy_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) deployHash: Hash; - @jsonMember({ name: 'from', constructor: AccountHash }) + @jsonMember({ + name: 'from', + constructor: AccountHash, + deserializer: json => AccountHash.fromJSON(json), + serializer: value => value.toJSON() + }) from: AccountHash; @jsonMember({ name: 'gas', constructor: String }) gas: string; - @jsonMember({ name: 'source', constructor: URef }) + @jsonMember({ + name: 'source', + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: value => value.toJSON() + }) source: URef; - @jsonArrayMember(TransferHash, { name: 'transfers' }) + @jsonArrayMember(TransferHash, { + name: 'transfers', + serializer: (value: TransferHash[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => + json.map((it: string) => TransferHash.fromJSON(it)) + }) transfers: TransferHash[]; } diff --git a/src/types/EntryPoint.ts b/src/types/EntryPoint.ts index a6adb4ee..0e87c411 100644 --- a/src/types/EntryPoint.ts +++ b/src/types/EntryPoint.ts @@ -59,7 +59,7 @@ export class EntryPointV1 { @jsonMember({ name: 'entry_point_payment', - constructor: () => EntryPointPayment + constructor: String }) entryPointPayment: EntryPointPayment; diff --git a/src/types/EraEnd.ts b/src/types/EraEnd.ts index 0839e5f3..51b920fb 100644 --- a/src/types/EraEnd.ts +++ b/src/types/EraEnd.ts @@ -6,14 +6,28 @@ import { } from 'typedjson'; import { PublicKey } from './keypair'; import { ValidatorWeightEraEnd } from './ValidatorWeight'; -import { CLValueUInt512 } from './clvalue'; +import { + CLValueUInt512, + deserializeRewards, + serializeRewards +} from './clvalue'; @jsonObject export class EraReward { - @jsonMember({ name: 'validator', constructor: PublicKey }) + @jsonMember({ + name: 'validator', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) public validator: PublicKey; - @jsonMember({ name: 'amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'amount', + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) public amount: CLValueUInt512; constructor(validator: PublicKey, amount: CLValueUInt512) { @@ -24,10 +38,20 @@ export class EraReward { @jsonObject export class EraReport { - @jsonArrayMember(PublicKey, { name: 'equivocators' }) + @jsonArrayMember(PublicKey, { + name: 'equivocators', + serializer: (value: PublicKey[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => + json.map((it: string) => PublicKey.fromJSON(it)) + }) public equivocators: PublicKey[]; - @jsonArrayMember(PublicKey, { name: 'inactive_validators' }) + @jsonArrayMember(PublicKey, { + name: 'inactive_validators', + serializer: (value: PublicKey[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => + json.map((it: string) => PublicKey.fromJSON(it)) + }) public inactiveValidators: PublicKey[]; @jsonArrayMember(EraReward, { name: 'rewards' }) @@ -46,10 +70,20 @@ export class EraReport { @jsonObject export class EraEndV2 { - @jsonArrayMember(PublicKey, { name: 'equivocators' }) + @jsonArrayMember(PublicKey, { + name: 'equivocators', + serializer: (value: PublicKey[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => + json.map((it: string) => PublicKey.fromJSON(it)) + }) public equivocators: PublicKey[]; - @jsonArrayMember(PublicKey, { name: 'inactive_validators' }) + @jsonArrayMember(PublicKey, { + name: 'inactive_validators', + serializer: (value: PublicKey[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => + json.map((it: string) => PublicKey.fromJSON(it)) + }) public inactiveValidators: PublicKey[]; @jsonArrayMember(ValidatorWeightEraEnd, { @@ -57,7 +91,11 @@ export class EraEndV2 { }) public nextEraValidatorWeights: ValidatorWeightEraEnd[]; - @jsonMapMember(String, CLValueUInt512, { name: 'rewards' }) + @jsonMapMember(String, Array, { + name: 'rewards', + serializer: serializeRewards, + deserializer: deserializeRewards + }) public rewards: Map; @jsonMember({ name: 'next_era_gas_price', constructor: Number }) @@ -99,10 +137,20 @@ export class EraEndV1 { @jsonObject export class EraEnd { - @jsonArrayMember(PublicKey, { name: 'equivocators' }) + @jsonArrayMember(PublicKey, { + name: 'equivocators', + serializer: (value: PublicKey[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => + json.map((it: string) => PublicKey.fromJSON(it)) + }) public equivocators: PublicKey[]; - @jsonArrayMember(PublicKey, { name: 'inactive_validators' }) + @jsonArrayMember(PublicKey, { + name: 'inactive_validators', + serializer: (value: PublicKey[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => + json.map((it: string) => PublicKey.fromJSON(it)) + }) public inactiveValidators: PublicKey[]; @jsonArrayMember(ValidatorWeightEraEnd, { @@ -110,7 +158,11 @@ export class EraEnd { }) public nextEraValidatorWeights: ValidatorWeightEraEnd[]; - @jsonMapMember(String, CLValueUInt512, { name: 'rewards' }) + @jsonMapMember(String, CLValueUInt512, { + name: 'rewards', + deserializer: deserializeRewards, + serializer: serializeRewards + }) public rewards: Map; @jsonMember({ name: 'next_era_gas_price', constructor: Number }) diff --git a/src/types/EraInfo.ts b/src/types/EraInfo.ts index 6191db18..df3c89da 100644 --- a/src/types/EraInfo.ts +++ b/src/types/EraInfo.ts @@ -4,13 +4,28 @@ import { CLValueUInt512 } from './clvalue'; @jsonObject export class DelegatorAllocation { - @jsonMember({ name: 'delegator_public_key', constructor: PublicKey }) + @jsonMember({ + name: 'delegator_public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) delegatorPublicKey: PublicKey; - @jsonMember({ name: 'validator_public_key', constructor: PublicKey }) + @jsonMember({ + name: 'validator_public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) validatorPublicKey: PublicKey; - @jsonMember({ name: 'amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'amount', + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) amount: CLValueUInt512; constructor( @@ -26,10 +41,20 @@ export class DelegatorAllocation { @jsonObject export class ValidatorAllocation { - @jsonMember({ name: 'validator_public_key', constructor: PublicKey }) + @jsonMember({ + name: 'validator_public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) validatorPublicKey: PublicKey; - @jsonMember({ name: 'amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'amount', + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) amount: CLValueUInt512; constructor(validatorPublicKey: PublicKey, amount: CLValueUInt512) { diff --git a/src/types/EraSummary.ts b/src/types/EraSummary.ts index 398db659..cf4da400 100644 --- a/src/types/EraSummary.ts +++ b/src/types/EraSummary.ts @@ -4,7 +4,12 @@ import { StoredValue } from './StoredValue'; @jsonObject export class EraSummary { - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) public blockHash: Hash; @jsonMember({ name: 'era_id', constructor: Number }) @@ -13,7 +18,12 @@ export class EraSummary { @jsonMember({ name: 'stored_value', constructor: StoredValue }) public storedValue: StoredValue; - @jsonMember({ name: 'state_root_hash', constructor: Hash }) + @jsonMember({ + name: 'state_root_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) public stateRootHash: Hash; @jsonMember({ name: 'merkle_proof', constructor: String }) diff --git a/src/types/ExecutableDeployItem.ts b/src/types/ExecutableDeployItem.ts index 11611bad..85a945f0 100644 --- a/src/types/ExecutableDeployItem.ts +++ b/src/types/ExecutableDeployItem.ts @@ -29,6 +29,7 @@ enum ExecutableDeployItemType { export class ModuleBytes { @jsonMember({ name: 'module_bytes', constructor: String }) moduleBytes: string; + @jsonMember({ constructor: Args, name: 'args', @@ -64,7 +65,13 @@ export class ModuleBytes { @jsonObject export class StoredContractByHash { - @jsonMember({ name: 'hash', constructor: ContractHash }) hash: ContractHash; + @jsonMember({ + name: 'hash', + constructor: ContractHash, + deserializer: json => ContractHash.fromJSON(json), + serializer: value => value.toJSON() + }) + hash: ContractHash; @jsonMember({ name: 'entry_point', constructor: String }) entryPoint: string; @jsonMember({ constructor: Args, @@ -118,7 +125,13 @@ export class StoredContractByName { @jsonObject export class StoredVersionedContractByHash { - @jsonMember({ name: 'hash', constructor: ContractHash }) hash: ContractHash; + @jsonMember({ + name: 'hash', + constructor: ContractHash, + deserializer: json => ContractHash.fromJSON(json), + serializer: value => value.toJSON() + }) + hash: ContractHash; @jsonMember({ name: 'entry_point', constructor: String }) entryPoint: string; @jsonMember({ constructor: Args, @@ -264,6 +277,16 @@ export class ExecutableDeployItem { @jsonMember({ name: 'Transfer', constructor: TransferDeployItem }) transfer?: TransferDeployItem; + public getArgByName(name: string): CLValue | undefined { + const deployItemArgs = this.getArgs(); + return deployItemArgs.args.get(name); + } + + public setArg(name: string, value: CLValue) { + const deployItemArgs = this.getArgs(); + deployItemArgs.insert(name, value); + } + getArgs(): Args { if (this.moduleBytes) return this.moduleBytes.args; if (this.storedContractByHash) return this.storedContractByHash.args; @@ -327,4 +350,60 @@ export class ExecutableDeployItem { ); return executableDeployItem; } + + /** + * Casts the `ExecutableDeployItem` to `ModuleBytes` if possible + * @returns `ModuleBytes` representation of `ExecutableDeployItem`, or `undefined` if the `ExecutableDeployItem` cannot be cast + */ + public asModuleBytes(): ModuleBytes | undefined { + return this.moduleBytes; + } + + /** + * Identifies whether the `ExecutableDeployItem` is of the original type `Transfer` + * @returns `true` is the `ExecutableDeployItem` conforms to `Transfer`, and `false` otherwise. + */ + public isTransfer() { + return !!this.transfer; + } + + /** + * Identifies whether the `ExecutableDeployItem` is of the original type `StoredVersionedContractByHash` + * @returns `true` is the `ExecutableDeployItem` conforms to `StoredVersionedContractByHash`, and `false` otherwise. + */ + public isStoredVersionContractByHash(): boolean { + return !!this.storedVersionedContractByHash; + } + + /** + * Identifies whether the `ExecutableDeployItem` is of the original type `StoredVersionedContractByName` + * @returns `true` is the `ExecutableDeployItem` conforms to `StoredVersionedContractByName`, and `false` otherwise. + */ + public isStoredVersionContractByName(): boolean { + return !!this.storedVersionedContractByName; + } + + /** + * Identifies whether the `ExecutableDeployItem` is of the original type `StoredContractByName` + * @returns `true` is the `ExecutableDeployItem` conforms to `StoredContractByName`, and `false` otherwise. + */ + public isStoredContractByName(): boolean { + return !!this.storedContractByName; + } + + /** + * Identifies whether the `ExecutableDeployItem` is of the original type `StoredContractByHash` + * @returns `true` is the `ExecutableDeployItem` conforms to `StoredContractByHash`, and `false` otherwise. + */ + public isStoredContractByHash(): boolean { + return !!this.storedContractByHash; + } + + /** + * Identifies whether the `ExecutableDeployItem` is of the original type `ModuleBytes` + * @returns `true` is the `ExecutableDeployItem` conforms to `ModuleBytes`, and `false` otherwise. + */ + public isModuleBytes(): boolean { + return !!this.moduleBytes; + } } diff --git a/src/types/ExecutionResult.ts b/src/types/ExecutionResult.ts index c861cbf1..8507859c 100644 --- a/src/types/ExecutionResult.ts +++ b/src/types/ExecutionResult.ts @@ -13,7 +13,12 @@ import { TransactionHash } from './Transaction'; @jsonObject export class Operation { - @jsonMember({ name: 'key', constructor: Key }) + @jsonMember({ + name: 'key', + constructor: Key, + deserializer: json => Key.fromJSON(json), + serializer: (value: Key) => value.toJSON() + }) public key: Key; @jsonMember({ name: 'kind', constructor: String }) @@ -37,7 +42,12 @@ export class ExecutionResultStatusData { @jsonMember({ name: 'effect', constructor: Effect }) public effect: Effect; - @jsonArrayMember(TransferHash, { name: 'transfers' }) + @jsonArrayMember(TransferHash, { + name: 'transfers', + serializer: (value: TransferHash[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => + json.map((it: string) => TransferHash.fromJSON(it)) + }) public transfers: TransferHash[] = []; @jsonMember({ name: 'cost', constructor: Number }) @@ -49,7 +59,12 @@ export class ExecutionResultStatusData { @jsonObject export class ExecutionResultV2 { - @jsonMember({ name: 'initiator', constructor: InitiatorAddr }) + @jsonMember({ + name: 'initiator', + constructor: InitiatorAddr, + deserializer: json => InitiatorAddr.fromJSON(json), + serializer: value => value.toJSON() + }) public initiator: InitiatorAddr; @jsonMember({ name: 'error_message', constructor: String }) @@ -67,7 +82,10 @@ export class ExecutionResultV2 { @jsonMember({ name: 'payment', constructor: AnyT }) public payment?: any; - @jsonArrayMember(Transfer, { name: 'transfers' }) + @jsonArrayMember(Transfer, { + name: 'transfers', + deserializer: (json: any) => json.map((it: string) => Transfer.fromJSON(it)) + }) public transfers: Transfer[] = []; @jsonMember({ name: 'size_estimate', constructor: Number }) @@ -88,7 +106,12 @@ export class ExecutionResultV1 { @jsonObject export class DeployExecutionResult { - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) public blockHash: Hash; @jsonMember({ name: 'result', constructor: ExecutionResultV1 }) @@ -97,7 +120,11 @@ export class DeployExecutionResult { @jsonObject export class ExecutionResult { - @jsonMember({ constructor: InitiatorAddr }) + @jsonMember({ + constructor: InitiatorAddr, + deserializer: json => InitiatorAddr.fromJSON(json), + serializer: (value: InitiatorAddr) => value.toJSON() + }) public initiator: InitiatorAddr; @jsonMember({ name: 'error_message', constructor: String }) @@ -115,7 +142,9 @@ export class ExecutionResult { @jsonMember({ constructor: AnyT }) public payment?: any; - @jsonArrayMember(Transfer) + @jsonArrayMember(Transfer, { + deserializer: (json: any) => json.map((it: string) => Transfer.fromJSON(it)) + }) public transfers: Transfer[] = []; @jsonMember({ constructor: Number }) @@ -228,7 +257,12 @@ export class ExecutionResult { @jsonObject export class ExecutionInfo { - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) public blockHash: Hash; @jsonMember({ name: 'block_height', constructor: Number }) @@ -268,7 +302,12 @@ export class ExecutionInfo { @jsonObject export class DeployExecutionInfo { - @jsonMember({ name: 'block_hash', constructor: Hash }) + @jsonMember({ + name: 'block_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) public blockHash: Hash; @jsonMember({ name: 'block_height', constructor: Number }) diff --git a/src/types/HexBytes.ts b/src/types/HexBytes.ts index 1c3abb7e..a3711fe1 100644 --- a/src/types/HexBytes.ts +++ b/src/types/HexBytes.ts @@ -1,8 +1,8 @@ -import { jsonObject, jsonArrayMember } from 'typedjson'; +import { jsonObject, jsonMember } from 'typedjson'; @jsonObject export class HexBytes { - @jsonArrayMember(Uint8Array) + @jsonMember(Uint8Array) bytes: Uint8Array; constructor(bytes: Uint8Array) { diff --git a/src/types/InitiatorAddr.test.ts b/src/types/InitiatorAddr.test.ts index 3606fb45..c344ef94 100644 --- a/src/types/InitiatorAddr.test.ts +++ b/src/types/InitiatorAddr.test.ts @@ -6,7 +6,7 @@ import { InitiatorAddr } from './InitiatorAddr'; @jsonObject class UnderTest { @jsonMember({ - deserializer: json => InitiatorAddr.fromJSON(JSON.stringify(json)), + deserializer: json => InitiatorAddr.fromJSON(json), serializer: value => value.toJSON() }) public a: InitiatorAddr; diff --git a/src/types/InitiatorAddr.ts b/src/types/InitiatorAddr.ts index dbe6ee3a..4ace40cc 100644 --- a/src/types/InitiatorAddr.ts +++ b/src/types/InitiatorAddr.ts @@ -6,10 +6,16 @@ import { AccountHash } from './key'; @jsonObject export class InitiatorAddr { - @jsonMember({ name: 'PublicKey', constructor: PublicKey }) + @jsonMember({ + name: 'PublicKey', + constructor: PublicKey + }) public publicKey?: PublicKey; - @jsonMember({ name: 'AccountHash', constructor: AccountHash }) + @jsonMember({ + name: 'AccountHash', + constructor: AccountHash + }) public accountHash?: AccountHash; constructor(publicKey?: PublicKey, accountHash?: AccountHash) { @@ -34,20 +40,13 @@ export class InitiatorAddr { return result; } - static fromJSON(json: string): InitiatorAddr { - let parsed; - try { - parsed = JSON.parse(json); - } catch { - throw new Error('Invalid JSON input for InitiatorAddr'); - } - + static fromJSON(json: any): InitiatorAddr { const initiatorAddr = new InitiatorAddr(); - if (parsed.publicKey) { - initiatorAddr.publicKey = PublicKey.fromHex(parsed.publicKey); - } else if (parsed.accountHash) { - initiatorAddr.accountHash = AccountHash.fromString(parsed.accountHash); + if (json.publicKey) { + initiatorAddr.publicKey = PublicKey.fromHex(json.publicKey); + } else if (json.accountHash) { + initiatorAddr.accountHash = AccountHash.fromString(json.accountHash); } return initiatorAddr; diff --git a/src/types/MessageTopic.ts b/src/types/MessageTopic.ts index 967f4baa..6d496142 100644 --- a/src/types/MessageTopic.ts +++ b/src/types/MessageTopic.ts @@ -20,7 +20,9 @@ export class MessageTopic { @jsonMember({ name: 'topic_name_hash', - constructor: Hash + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() }) topicNameHash: Hash; } @@ -50,13 +52,17 @@ export class Message { @jsonMember({ name: 'topic_name_hash', - constructor: Hash + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() }) topicNameHash: Hash; @jsonMember({ name: 'entity_hash', - constructor: EntityAddr + constructor: EntityAddr, + deserializer: json => EntityAddr.fromJSON(json), + serializer: value => value.toJSON() }) entityHash: EntityAddr; diff --git a/src/types/MinimalBlockInfo.ts b/src/types/MinimalBlockInfo.ts index adff7be6..a81a1b72 100644 --- a/src/types/MinimalBlockInfo.ts +++ b/src/types/MinimalBlockInfo.ts @@ -5,21 +5,41 @@ import { Timestamp } from './Time'; @jsonObject export class MinimalBlockInfo { - @jsonMember({ name: 'creator', constructor: PublicKey }) + @jsonMember({ + name: 'creator', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) creator: PublicKey; @jsonMember({ name: 'era_id', constructor: Number }) eraID: number; - @jsonMember({ name: 'hash', constructor: Hash }) + @jsonMember({ + name: 'hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) hash: Hash; @jsonMember({ name: 'height', constructor: Number }) height: number; - @jsonMember({ name: 'state_root_hash', constructor: Hash }) + @jsonMember({ + name: 'state_root_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) stateRootHash: Hash; - @jsonMember({ name: 'timestamp', constructor: Timestamp }) + @jsonMember({ + name: 'timestamp', + constructor: Timestamp, + deserializer: json => Timestamp.fromJSON(json), + serializer: value => value.toJSON() + }) timestamp: Timestamp; } diff --git a/src/types/NamedKey.ts b/src/types/NamedKey.ts index 0c718c49..281a2137 100644 --- a/src/types/NamedKey.ts +++ b/src/types/NamedKey.ts @@ -9,7 +9,12 @@ export class NamedKey { @jsonMember({ name: 'name', constructor: String }) name: string; - @jsonMember({ name: 'key', constructor: Key }) + @jsonMember({ + name: 'key', + constructor: Key, + deserializer: json => Key.fromJSON(json), + serializer: (value: Key) => value.toJSON() + }) key: Key; constructor(name: string, key: Key) { diff --git a/src/types/Package.ts b/src/types/Package.ts index 2009ab00..a4858cd7 100644 --- a/src/types/Package.ts +++ b/src/types/Package.ts @@ -5,7 +5,9 @@ import { AddressableEntityHash, URef } from './key'; export class EntityVersionAndHash { @jsonMember({ name: 'addressable_entity_hash', - constructor: AddressableEntityHash + constructor: AddressableEntityHash, + deserializer: json => AddressableEntityHash.fromJSON(json), + serializer: (value: AddressableEntityHash) => value.toJSON() }) addressableEntityHash: AddressableEntityHash; @@ -58,7 +60,11 @@ export class NamedUserGroup { @jsonMember({ name: 'group_name', constructor: String }) groupName: string; - @jsonArrayMember(URef, { name: 'group_users' }) + @jsonArrayMember(URef, { + name: 'group_users', + serializer: (value: URef[]) => value.map(it => it.toJSON()), + deserializer: (json: any) => json.map((it: string) => URef.fromJSON(it)) + }) groupUsers: URef[]; constructor(groupName: string, groupUsers: URef[]) { diff --git a/src/types/PricingMode.ts b/src/types/PricingMode.ts index d509e82c..30a562f3 100644 --- a/src/types/PricingMode.ts +++ b/src/types/PricingMode.ts @@ -30,7 +30,12 @@ export class FixedMode { @jsonObject export class ReservedMode { - @jsonMember({ name: 'receipt', constructor: Hash }) + @jsonMember({ + name: 'receipt', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) receipt: Hash; } diff --git a/src/types/Reservation.ts b/src/types/Reservation.ts index d1ab5790..b606d2ec 100644 --- a/src/types/Reservation.ts +++ b/src/types/Reservation.ts @@ -4,10 +4,20 @@ import { Hash } from './key'; @jsonObject export class ReservationKind { - @jsonMember({ name: 'receipt', constructor: Hash }) + @jsonMember({ + name: 'receipt', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) receipt: Hash; - @jsonMember({ name: 'reservation_data', constructor: HexBytes }) + @jsonMember({ + name: 'reservation_data', + constructor: HexBytes, + deserializer: json => HexBytes.fromJSON(json), + serializer: value => value.toJSON() + }) reservationData: HexBytes; @jsonMember({ name: 'reservation_kind', constructor: Number }) diff --git a/src/types/StoredValue.ts b/src/types/StoredValue.ts index 1406d64f..c9ae3782 100644 --- a/src/types/StoredValue.ts +++ b/src/types/StoredValue.ts @@ -73,7 +73,18 @@ export class StoredValue { @jsonMember({ name: 'Package', constructor: Package }) package?: Package; - @jsonMember({ name: 'ByteCode', constructor: ByteCode }) + @jsonMember({ + name: 'ByteCode', + constructor: ByteCode, + deserializer: json => { + if (!json) return; + return ByteCode.fromJSON(json); + }, + serializer: (value: ByteCode) => { + if (!value) return; + return value.toJSON(); + } + }) byteCode?: ByteCode; @jsonMember({ name: 'MessageTopic', constructor: MessageTopicSummary }) diff --git a/src/types/Time.ts b/src/types/Time.ts index 72847510..98fe069c 100644 --- a/src/types/Time.ts +++ b/src/types/Time.ts @@ -1,4 +1,5 @@ import { jsonObject, jsonMember } from 'typedjson'; +import { dehumanizerTTL, humanizerTTL } from './SerializationUtils'; @jsonObject export class Timestamp { @@ -37,32 +38,21 @@ export class Duration { } toJSON(): string { - let durationStr = this.toDurationString(); - if (durationStr === '24h0m0s') { - durationStr = '1day'; - } - if (durationStr.endsWith('h0m0s')) { - durationStr = durationStr.replace('0m0s', ''); - } else if (durationStr.endsWith('m0s')) { - durationStr = durationStr.replace('0s', ''); - } - return durationStr; + return humanizerTTL(this.duration); } static fromJSON(data: string): Duration { - let parsedData = data.replace(' ', ''); - if (parsedData === '1day') { - parsedData = '24h'; - } - const ms = Duration.parseDurationString(parsedData); - return new Duration(ms); + const duration = dehumanizerTTL(data); + + return new Duration(duration); } - private toDurationString(): string { + toDurationString(): string { return new Date(this.duration).toISOString().substring(11, 19); } - private static parseDurationString(durationStr: string): number { + static parseDurationString(durationStr: string): number { + console.log(durationStr); const parts = durationStr.match(/(\d+)([smhd])/g); if (!parts) throw new Error('Invalid duration format'); diff --git a/src/types/Transaction.test.ts b/src/types/Transaction.test.ts index d9d03d9d..f006ae09 100644 --- a/src/types/Transaction.test.ts +++ b/src/types/Transaction.test.ts @@ -1,4 +1,5 @@ import { BigNumber } from '@ethersproject/bignumber'; +import { assert, expect } from 'chai'; import { Duration, Timestamp } from './Time'; import { @@ -28,8 +29,6 @@ describe('Test Transaction', () => { it('should create a Transaction from TransactionV1', async () => { const keys = await PrivateKey.generate(KeyAlgorithm.ED25519); const timestamp = new Timestamp(new Date()); - const duration = new Duration(18000000000); - const initiatorAddr = new InitiatorAddr(keys.publicKey); const paymentAmount = 20000000000000; const pricingMode = new PricingMode(); @@ -40,8 +39,8 @@ describe('Test Transaction', () => { const transactionHeader = TransactionV1Header.build({ chainName: 'casper-net-1', timestamp, - ttl: duration, - initiatorAddr, + ttl: new Duration(1800000), + initiatorAddr: new InitiatorAddr(keys.publicKey), pricingMode }); @@ -72,5 +71,21 @@ describe('Test Transaction', () => { transactionBody ); await transaction.sign(keys); + + const toJson = TransactionV1.toJson(transaction); + const parsed = TransactionV1.fromJSON(toJson.transaction); + + console.log(toJson); + console.log(parsed); + console.log(JSON.stringify(toJson)); + + const transactionPaymentAmount = parsed.body.args.args + .get('amount')! + .toString(); + + assert.deepEqual(parsed.approvals[0].signer, keys.publicKey); + expect(transaction.body).to.deep.equal(transactionBody); + expect(transaction.header).to.deep.equal(transactionHeader); + assert.deepEqual(parseInt(transactionPaymentAmount, 10), paymentAmount); }); }); diff --git a/src/types/Transaction.ts b/src/types/Transaction.ts index a12683dd..8371b928 100644 --- a/src/types/Transaction.ts +++ b/src/types/Transaction.ts @@ -45,10 +45,20 @@ export enum TransactionVersion { @jsonObject export class Approval { - @jsonMember({ name: 'signer', constructor: PublicKey }) + @jsonMember({ + name: 'signer', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) public signer: PublicKey; - @jsonMember({ name: 'signature', constructor: HexBytes }) + @jsonMember({ + name: 'signature', + constructor: HexBytes, + deserializer: json => HexBytes.fromJSON(json), + serializer: value => value.toJSON() + }) public signature: HexBytes; constructor(signer: PublicKey, signature: HexBytes) { @@ -62,19 +72,39 @@ export class TransactionV1Header { @jsonMember({ name: 'chain_name', constructor: String }) public chainName: string; - @jsonMember({ name: 'timestamp', constructor: Timestamp }) + @jsonMember({ + name: 'timestamp', + constructor: Timestamp, + deserializer: json => Timestamp.fromJSON(json), + serializer: value => value.toJSON() + }) public timestamp: Timestamp; - @jsonMember({ name: 'ttl', constructor: Duration }) + @jsonMember({ + name: 'ttl', + constructor: Duration, + deserializer: json => Duration.fromJSON(json), + serializer: value => value.toJSON() + }) public ttl: Duration; - @jsonMember({ name: 'initiator_addr', constructor: InitiatorAddr }) + @jsonMember({ + name: 'initiator_addr', + constructor: InitiatorAddr, + deserializer: json => InitiatorAddr.fromJSON(json), + serializer: value => value.toJSON() + }) public initiatorAddr: InitiatorAddr; @jsonMember({ name: 'pricing_mode', constructor: PricingMode }) public pricingMode: PricingMode; - @jsonMember({ name: 'body_hash', constructor: Hash }) + @jsonMember({ + name: 'body_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) public bodyHash: Hash; static build({ @@ -134,16 +164,31 @@ export class TransactionV1Body { }) public args: Args; - @jsonMember({ name: 'target', constructor: TransactionTarget }) + @jsonMember({ + name: 'target', + constructor: TransactionTarget, + deserializer: json => TransactionTarget.fromJSON(json), + serializer: value => value.toJSON() + }) public target: TransactionTarget; - @jsonMember({ name: 'entry_point', constructor: TransactionEntryPoint }) + @jsonMember({ + name: 'entry_point', + constructor: TransactionEntryPoint, + deserializer: json => TransactionEntryPoint.fromJSON(json), + serializer: value => value.toJSON() + }) public entryPoint: TransactionEntryPoint; @jsonMember({ name: 'transaction_category', constructor: Number }) public category: number; - @jsonMember({ name: 'scheduling', constructor: TransactionScheduling }) + @jsonMember({ + name: 'scheduling', + constructor: TransactionScheduling, + deserializer: json => TransactionScheduling.fromJSON(json), + serializer: value => value.toJSON() + }) public scheduling: TransactionScheduling; static build({ @@ -187,7 +232,12 @@ export class TransactionV1Body { @jsonObject export class TransactionV1 { - @jsonMember({ name: 'hash', constructor: Hash }) + @jsonMember({ + name: 'hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) public hash: Hash; @jsonMember({ name: 'header', constructor: TransactionV1Header }) @@ -196,7 +246,7 @@ export class TransactionV1 { @jsonMember({ name: 'body', constructor: TransactionV1Body }) public body: TransactionV1Body; - @jsonArrayMember(Approval) + @jsonArrayMember(() => Approval) public approvals: Approval[]; constructor( @@ -243,17 +293,14 @@ export class TransactionV1 { } async sign(keys: PrivateKey): Promise { - const signature = await keys.sign(this.hash.toBytes()); - const approval: Approval = { - signer: keys.publicKey, - signature: new HexBytes(signature) - }; + const signatureBytes = await keys.sign(this.hash.toBytes()); + const signature = new HexBytes(signatureBytes); if (!this.approvals) { this.approvals = []; } - this.approvals.push(approval); + this.approvals.push(new Approval(keys.publicKey, signature)); } /** @@ -352,13 +399,28 @@ export class TransactionHeader { @jsonMember({ name: 'chain_name', constructor: String }) public chainName: string; - @jsonMember({ name: 'timestamp', constructor: Timestamp }) + @jsonMember({ + name: 'timestamp', + constructor: Timestamp, + deserializer: json => Timestamp.fromJSON(json), + serializer: value => value.toJSON() + }) public timestamp: Timestamp; - @jsonMember({ name: 'ttl', constructor: Duration }) + @jsonMember({ + name: 'ttl', + constructor: Duration, + deserializer: json => Duration.fromJSON(json), + serializer: value => value.toJSON() + }) public ttl: Duration; - @jsonMember({ name: 'initiator_addr', constructor: InitiatorAddr }) + @jsonMember({ + name: 'initiator_addr', + constructor: InitiatorAddr, + deserializer: json => InitiatorAddr.fromJSON(json), + serializer: value => value.toJSON() + }) public initiatorAddr: InitiatorAddr; @jsonMember({ name: 'pricing_mode', constructor: PricingMode }) @@ -389,13 +451,28 @@ export class TransactionBody { }) public args: Args; - @jsonMember({ name: 'target', constructor: TransactionTarget }) + @jsonMember({ + name: 'target', + constructor: TransactionTarget, + serializer: value => value.toJSON(), + deserializer: json => TransactionTarget.fromJSON(json) + }) public target: TransactionTarget; - @jsonMember({ name: 'entry_point', constructor: TransactionEntryPoint }) + @jsonMember({ + name: 'entry_point', + constructor: TransactionEntryPoint, + serializer: value => value.toJSON(), + deserializer: json => TransactionEntryPoint.fromJSON(json) + }) public entryPoint: TransactionEntryPoint; - @jsonMember({ name: 'scheduling', constructor: TransactionScheduling }) + @jsonMember({ + name: 'scheduling', + constructor: TransactionScheduling, + serializer: value => value.toJSON(), + deserializer: json => TransactionScheduling.fromJSON(json) + }) public scheduling: TransactionScheduling; @jsonMember({ name: 'transaction_category', constructor: Number }) @@ -418,7 +495,12 @@ export class TransactionBody { @jsonObject export class Transaction { - @jsonMember({ name: 'hash', constructor: Hash }) + @jsonMember({ + name: 'hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) public hash: Hash; @jsonMember({ name: 'header', constructor: TransactionHeader }) @@ -496,10 +578,29 @@ export class TransactionWrapper { @jsonObject export class TransactionHash { - @jsonMember({ name: 'Deploy', constructor: Hash }) + @jsonMember({ + name: 'Deploy', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) public deploy?: Hash; - @jsonMember({ name: 'Version1', constructor: Hash }) + @jsonMember({ + name: 'Version1', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => value.toJSON() + }) public transactionV1?: Hash; constructor(deploy?: Hash, transactionV1?: Hash) { diff --git a/src/types/TransactionEntryPoint.test.ts b/src/types/TransactionEntryPoint.test.ts index 743f1efe..55c0564e 100644 --- a/src/types/TransactionEntryPoint.test.ts +++ b/src/types/TransactionEntryPoint.test.ts @@ -7,7 +7,7 @@ import { TransactionEntryPoint } from './TransactionEntryPoint'; class UnderTest { @jsonMember({ serializer: value => value.toJSON(), - deserializer: json => TransactionEntryPoint.fromJSON(JSON.stringify(json)) + deserializer: json => TransactionEntryPoint.fromJSON(json) }) public a: TransactionEntryPoint; } diff --git a/src/types/TransactionEntryPoint.ts b/src/types/TransactionEntryPoint.ts index 5d459e9b..1db2e9f7 100644 --- a/src/types/TransactionEntryPoint.ts +++ b/src/types/TransactionEntryPoint.ts @@ -128,21 +128,14 @@ export class TransactionEntryPoint { throw new Error('Unknown entry point'); } - static fromJSON(json: string): TransactionEntryPoint { - let parsed; - try { - parsed = JSON.parse(json); - } catch { - throw new Error('Invalid JSON input for TransactionEntryPoint'); - } - + static fromJSON(json: any): TransactionEntryPoint { const entryPoint = new TransactionEntryPoint(); - if (parsed instanceof Object && parsed.Custom) { - entryPoint.custom = parsed.Custom; + if (json instanceof Object && json.Custom) { + entryPoint.custom = json.Custom; return entryPoint; } - switch (parsed) { + switch (json) { case TransactionEntryPointEnum.Transfer: entryPoint.transfer = {}; break; diff --git a/src/types/TransactionScheduling.ts b/src/types/TransactionScheduling.ts index f1d54412..006935d5 100644 --- a/src/types/TransactionScheduling.ts +++ b/src/types/TransactionScheduling.ts @@ -22,7 +22,12 @@ class FutureEraScheduling { @jsonObject class FutureTimestampScheduling { - @jsonMember({ name: 'FutureTimestamp', constructor: Timestamp }) + @jsonMember({ + name: 'FutureTimestamp', + constructor: Timestamp, + deserializer: json => Timestamp.fromJSON(json), + serializer: value => value.toJSON() + }) timestamp: Timestamp; constructor(timestamp: Timestamp) { @@ -77,51 +82,25 @@ export class TransactionScheduling { return tagBytes; } - static fromJSON(json: string): TransactionScheduling { - const parsed = JSON.parse(json); - if (parsed === 'Standard') { + static fromJSON(json: any): TransactionScheduling { + if (json === 'Standard') { return new TransactionScheduling({}); } - if (typeof parsed === 'object') { - if ('FutureEra' in parsed) { - return new TransactionScheduling( - undefined, - new FutureEraScheduling(parsed.FutureEra) - ); - } - if ('FutureTimestamp' in parsed) { + if (typeof json === 'object') { + if ('FutureEra' in json) { return new TransactionScheduling( undefined, - undefined, - new FutureTimestampScheduling(parsed.FutureEra) + new FutureEraScheduling(json.FutureEra) ); } - } - throw new Error('Invalid JSON format for TransactionScheduling'); - } - - fromJSON(json: string): TransactionScheduling { - const data = JSON.parse(json); - - if (typeof data === 'object' && data !== null) { - if ('FutureTimestamp' in data) { + if ('FutureTimestamp' in json) { return new TransactionScheduling( undefined, undefined, - new FutureTimestampScheduling(data.FutureTimestamp) - ); - } - - if ('FutureEra' in data) { - return new TransactionScheduling( - undefined, - new FutureEraScheduling(data.FutureEra) + new FutureTimestampScheduling(json.FutureEra) ); } - } else if (data === 'Standard') { - return new TransactionScheduling({}); } - throw new Error('Invalid JSON format for TransactionScheduling'); } diff --git a/src/types/TransactionTarget.test.ts b/src/types/TransactionTarget.test.ts index 3c7f3885..b3ab6bc8 100644 --- a/src/types/TransactionTarget.test.ts +++ b/src/types/TransactionTarget.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; @jsonObject class UnderTest { @jsonMember({ - deserializer: json => TransactionTarget.fromJSON(JSON.stringify(json)), + deserializer: json => TransactionTarget.fromJSON(json), serializer: value => value.toJSON() }) public a: TransactionTarget; diff --git a/src/types/TransactionTarget.ts b/src/types/TransactionTarget.ts index 54e3ea1f..cb689985 100644 --- a/src/types/TransactionTarget.ts +++ b/src/types/TransactionTarget.ts @@ -22,7 +22,12 @@ enum InvocationTargetTag { @jsonObject export class ByPackageHashInvocationTarget { - @jsonMember({ name: 'addr', constructor: Hash }) + @jsonMember({ + name: 'addr', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) addr: Hash; @jsonMember({ name: 'version', isRequired: false, constructor: Number }) @@ -40,7 +45,19 @@ export class ByPackageNameInvocationTarget { @jsonObject export class TransactionInvocationTarget { - @jsonMember({ name: 'ByHash', isRequired: false, constructor: Hash }) + @jsonMember({ + name: 'ByHash', + isRequired: false, + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) byHash?: Hash; @jsonMember({ name: 'ByName', isRequired: false, constructor: String }) @@ -180,48 +197,40 @@ export class TransactionTarget { return result; } - static fromJSON(json: string): TransactionTarget { - let parsed; - try { - parsed = JSON.parse(json); - } catch { - throw new Error('Invalid JSON input for TransactionTarget'); - } - + static fromJSON(json: any): TransactionTarget { const target = new TransactionTarget(); - if (typeof parsed === 'string' && parsed === 'Native') { + if (typeof json === 'string' && json === 'Native') { target.native = {}; - } else if (parsed.Stored) { + } else if (json.Stored) { const storedTarget = new StoredTarget(); - storedTarget.runtime = parsed.Stored.runtime; + storedTarget.runtime = json.Stored.runtime; const invocationTarget = new TransactionInvocationTarget(); - if (parsed.Stored.id.byHash) { - invocationTarget.byHash = Hash.fromHex(parsed.Stored.id.byHash); - } else if (parsed.Stored.id.byName) { - invocationTarget.byName = parsed.Stored.id.byName; - } else if (parsed.Stored.id.byPackageHash) { + if (json.Stored.id.byHash) { + invocationTarget.byHash = Hash.fromHex(json.Stored.id.byHash); + } else if (json.Stored.id.byName) { + invocationTarget.byName = json.Stored.id.byName; + } else if (json.Stored.id.byPackageHash) { invocationTarget.byPackageHash = new ByPackageHashInvocationTarget(); invocationTarget.byPackageHash.addr = Hash.fromHex( - parsed.Stored.id.byPackageHash.addr + json.Stored.id.byPackageHash.addr ); invocationTarget.byPackageHash.version = - parsed.Stored.id.byPackageHash.version; - } else if (parsed.Stored.id.byPackageName) { + json.Stored.id.byPackageHash.version; + } else if (json.Stored.id.byPackageName) { invocationTarget.byPackageName = new ByPackageNameInvocationTarget(); - invocationTarget.byPackageName.name = - parsed.Stored.id.byPackageName.name; + invocationTarget.byPackageName.name = json.Stored.id.byPackageName.name; invocationTarget.byPackageName.version = - parsed.Stored.id.byPackageName.version; + json.Stored.id.byPackageName.version; } storedTarget.id = invocationTarget; target.stored = storedTarget; - } else if (parsed.Session) { + } else if (json.Session) { const sessionTarget = new SessionTarget(); - sessionTarget.runtime = parsed.Session.runtime; - sessionTarget.moduleBytes = parsed.Session.module_bytes; + sessionTarget.runtime = json.Session.runtime; + sessionTarget.moduleBytes = json.Session.module_bytes; target.session = sessionTarget; } diff --git a/src/types/Transfer.ts b/src/types/Transfer.ts index 3e2a45f6..881bf4ac 100644 --- a/src/types/Transfer.ts +++ b/src/types/Transfer.ts @@ -6,13 +6,26 @@ import { CLValueUInt512 } from './clvalue'; @jsonObject export class TransferV1 { - @jsonMember({ constructor: CLValueUInt512 }) + @jsonMember({ + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) public amount: CLValueUInt512; - @jsonMember({ name: 'deploy_hash', constructor: Hash }) + @jsonMember({ + name: 'deploy_hash', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) public deployHash: Hash; - @jsonMember({ constructor: AccountHash }) + @jsonMember({ + constructor: AccountHash, + deserializer: json => AccountHash.fromJSON(json), + serializer: value => value.toJSON() + }) public from: AccountHash; @jsonMember({ constructor: Number }) @@ -21,25 +34,53 @@ export class TransferV1 { @jsonMember({ constructor: Number }) public id?: number; - @jsonMember({ constructor: URef }) + @jsonMember({ + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: (value: URef) => value.toJSON() + }) public source: URef; - @jsonMember({ constructor: URef }) + @jsonMember({ + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: (value: URef) => value.toJSON() + }) public target: URef; - @jsonMember({ constructor: AccountHash }) + @jsonMember({ + constructor: AccountHash, + deserializer: json => { + if (!json) return; + + return AccountHash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + + return value.toJSON(); + } + }) public to?: AccountHash; } @jsonObject export class TransferV2 { - @jsonMember({ constructor: CLValueUInt512 }) + @jsonMember({ + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) public amount: CLValueUInt512; @jsonMember({ name: 'transaction_hash', constructor: TransactionHash }) public transactionHash: TransactionHash; - @jsonMember({ constructor: InitiatorAddr }) + @jsonMember({ + constructor: InitiatorAddr, + deserializer: json => InitiatorAddr.fromJSON(json), + serializer: value => value.toJSON() + }) public from: InitiatorAddr; @jsonMember({ constructor: Number }) @@ -48,13 +89,31 @@ export class TransferV2 { @jsonMember({ constructor: Number }) public id?: number; - @jsonMember({ constructor: URef }) + @jsonMember({ + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: (value: URef) => value.toJSON() + }) public source: URef; - @jsonMember({ constructor: URef }) + @jsonMember({ + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: (value: URef) => value.toJSON() + }) public target: URef; - @jsonMember({ constructor: AccountHash }) + @jsonMember({ + constructor: AccountHash, + deserializer: json => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: (value: AccountHash) => { + if (!value) return; + return value.toJSON(); + } + }) public to?: AccountHash; } @@ -69,13 +128,21 @@ class TransferVersioned { @jsonObject export class Transfer { - @jsonMember({ constructor: CLValueUInt512 }) + @jsonMember({ + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) public amount: CLValueUInt512; @jsonMember({ name: 'transaction_hash', constructor: TransactionHash }) public transactionHash: TransactionHash; - @jsonMember({ constructor: InitiatorAddr }) + @jsonMember({ + constructor: InitiatorAddr, + deserializer: json => InitiatorAddr.fromJSON(json), + serializer: value => value.toJSON() + }) public from: InitiatorAddr; @jsonMember({ constructor: Number }) @@ -84,13 +151,32 @@ export class Transfer { @jsonMember({ constructor: Number }) public id?: number; - @jsonMember({ constructor: URef }) + @jsonMember({ + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: (value: URef) => value.toJSON() + }) public source: URef; - @jsonMember({ constructor: URef }) + @jsonMember({ + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: (value: URef) => value.toJSON() + }) public target: URef; - @jsonMember({ constructor: AccountHash }) + @jsonMember({ + constructor: AccountHash, + deserializer: json => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: (value: AccountHash) => { + if (!value) return; + + return value.toJSON(); + } + }) public to?: AccountHash; private originTransferV1?: TransferV1; diff --git a/src/types/Transform.ts b/src/types/Transform.ts index d41435b2..2ecbe2a8 100644 --- a/src/types/Transform.ts +++ b/src/types/Transform.ts @@ -16,8 +16,14 @@ export class RawWriteCLValue { @jsonMember({ constructor: Args, name: 'WriteCLValue', - deserializer: deserializeArgs, - serializer: serializeArgs + deserializer: json => { + if (!json) return; + return deserializeArgs(json); + }, + serializer: value => { + if (!value) return; + return serializeArgs(value); + } }) writeCLValue?: Args; } @@ -27,8 +33,14 @@ class Write { @jsonMember({ constructor: Args, name: 'CLValue', - deserializer: deserializeArgs, - serializer: serializeArgs + deserializer: json => { + if (!json) return; + return deserializeArgs(json); + }, + serializer: value => { + if (!value) return; + return serializeArgs(value); + } }) clValue?: Args; } @@ -295,7 +307,12 @@ export class TransformKind { @jsonObject export class Transform { - @jsonMember({ name: 'key', constructor: Key }) + @jsonMember({ + name: 'key', + constructor: Key, + deserializer: json => Key.fromJSON(json), + serializer: (value: Key) => value.toJSON() + }) public key: Key; @jsonMember({ name: 'kind', constructor: TransformKind }) @@ -309,7 +326,12 @@ export class Transform { @jsonObject export class TransformKey { - @jsonMember({ name: 'key', constructor: Key }) + @jsonMember({ + name: 'key', + constructor: Key, + deserializer: json => Key.fromJSON(json), + serializer: (value: Key) => value.toJSON() + }) public key: Key; @jsonMember({ name: 'transform', constructor: TransformKind }) @@ -340,22 +362,53 @@ export class WriteTransfer { @jsonMember({ name: 'id', constructor: Number }) public id?: number; - @jsonMember({ name: 'to', constructor: AccountHash }) + @jsonMember({ + name: 'to', + constructor: AccountHash, + deserializer: json => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: (value: AccountHash) => { + if (!value) return; + return value.toJSON(); + } + }) public to?: AccountHash; @jsonMember({ name: 'deploy_hash', constructor: Hash }) public deployHash: Hash; - @jsonMember({ name: 'from', constructor: AccountHash }) + @jsonMember({ + name: 'from', + constructor: AccountHash, + deserializer: json => AccountHash.fromJSON(json), + serializer: (value: AccountHash) => value.toJSON() + }) public from: AccountHash; - @jsonMember({ name: 'amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'amount', + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: (value: CLValueUInt512) => value.toJSON() + }) public amount: CLValueUInt512; - @jsonMember({ name: 'source', constructor: URef }) + @jsonMember({ + name: 'source', + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: (value: URef) => value.toJSON() + }) public source: URef; - @jsonMember({ name: 'target', constructor: URef }) + @jsonMember({ + name: 'target', + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: (value: URef) => value.toJSON() + }) public target: URef; @jsonMember({ name: 'gas', constructor: Number }) diff --git a/src/types/UnbondingPurse.ts b/src/types/UnbondingPurse.ts index 2c7ac3af..ef4c1858 100644 --- a/src/types/UnbondingPurse.ts +++ b/src/types/UnbondingPurse.ts @@ -5,10 +5,20 @@ import { CLValueUInt512 } from './clvalue'; @jsonObject export class UnbondingPurse { - @jsonMember({ name: 'amount', constructor: CLValueUInt512 }) + @jsonMember({ + name: 'amount', + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) amount: CLValueUInt512; - @jsonMember({ name: 'bonding_purse', constructor: URef }) + @jsonMember({ + name: 'bonding_purse', + constructor: URef, + deserializer: json => URef.fromJSON(json), + serializer: value => value.toJSON() + }) bondingPurse: URef; @jsonMember({ name: 'era_of_creation', constructor: Number }) @@ -16,16 +26,31 @@ export class UnbondingPurse { @jsonMember({ name: 'unbonder_public_key', - constructor: PublicKey + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() }) unbonderPublicKey: PublicKey; @jsonMember({ name: 'validator_public_key', - constructor: PublicKey + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() }) validatorPublicKey: PublicKey; - @jsonMember({ name: 'new_validator', constructor: PublicKey }) + @jsonMember({ + name: 'new_validator', + constructor: PublicKey, + deserializer: json => { + if (!json) return; + return PublicKey.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) newValidator?: PublicKey; } diff --git a/src/types/ValidatorWeight.ts b/src/types/ValidatorWeight.ts index 5402141d..e3b1d62c 100644 --- a/src/types/ValidatorWeight.ts +++ b/src/types/ValidatorWeight.ts @@ -4,18 +4,35 @@ import { CLValueUInt512 } from './clvalue'; @jsonObject export class ValidatorWeightEraEnd { - @jsonMember({ constructor: PublicKey }) + @jsonMember({ + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) validator: PublicKey; - @jsonMember({ constructor: CLValueUInt512 }) + @jsonMember({ + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) weight: CLValueUInt512; } @jsonObject export class ValidatorWeightAuction { - @jsonMember({ name: 'public_key', constructor: PublicKey }) + @jsonMember({ + name: 'public_key', + constructor: PublicKey, + deserializer: json => PublicKey.fromJSON(json), + serializer: value => value.toJSON() + }) validator: PublicKey; - @jsonMember({ constructor: CLValueUInt512 }) + @jsonMember({ + constructor: CLValueUInt512, + deserializer: json => CLValueUInt512.fromJSON(json), + serializer: value => value.toJSON() + }) weight: CLValueUInt512; } diff --git a/src/types/clvalue/Uint512.ts b/src/types/clvalue/Uint512.ts index 9c0ba286..d329a761 100644 --- a/src/types/clvalue/Uint512.ts +++ b/src/types/clvalue/Uint512.ts @@ -9,8 +9,8 @@ import { toBytesU512 } from '../ByteConverters'; * Represents a 512-bit unsigned integer value in the CasperLabs type system. */ export class CLValueUInt512 { - private val: BigNumber; - private isStringFmt: boolean; + public val: BigNumber; + public isStringFmt: boolean; /** * Constructs a new CLValueUInt512 instance. @@ -97,3 +97,27 @@ export class CLValueUInt512 { return res; } } + +export const deserializeRewards = (arr: any) => { + const parsed = new Map( + Array.from(arr, ([key, value]) => { + const valuesArray = value.map((item: any) => + CLValueUInt512.fromJSON(item) + ); + return [key, valuesArray]; + }) + ); + + if (parsed.size !== Array.from(arr).length) { + throw Error(`Duplicate key exists.`); + } + + return parsed; +}; + +export const serializeRewards = (map: Map) => { + return Array.from(map, ([key, value]) => { + const serializedValue = value.map(item => item.toJSON()); + return [key, serializedValue]; + }); +}; diff --git a/src/types/key/Account.ts b/src/types/key/Account.ts index c742f313..6c261a7e 100644 --- a/src/types/key/Account.ts +++ b/src/types/key/Account.ts @@ -63,10 +63,6 @@ export class AccountHash extends Hash { * @throws {Error} If the input is not a valid JSON string. */ public static fromJSON(data: string): AccountHash { - const parsed = JSON.parse(data); - if (typeof parsed !== 'string') { - throw new Error('Invalid JSON data for AccountHash'); - } return AccountHash.fromString(data); } } diff --git a/src/types/key/BalanceHoldAddr.ts b/src/types/key/BalanceHoldAddr.ts index 9a991d0f..46e081bb 100644 --- a/src/types/key/BalanceHoldAddr.ts +++ b/src/types/key/BalanceHoldAddr.ts @@ -53,7 +53,12 @@ export class Hold { /** * The block time of the hold. */ - @jsonMember({ name: 'BlockTime', constructor: Date }) + @jsonMember({ + name: 'BlockTime', + constructor: Date, + serializer: (n: number) => new Date(n).toISOString(), + deserializer: (s: string) => Date.parse(s) + }) blockTime: Date; constructor(purseAddr: URefAddr, blockTime: Date) { diff --git a/src/types/key/BidAddr.ts b/src/types/key/BidAddr.ts index 5b560029..6ee12fe4 100644 --- a/src/types/key/BidAddr.ts +++ b/src/types/key/BidAddr.ts @@ -54,16 +54,31 @@ const CreditAddrLen = 41; @jsonObject export class DelegatorInfo { - @jsonMember({ name: 'validator', constructor: Hash }) + @jsonMember({ + name: 'validator', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) validator: Hash; - @jsonMember({ name: 'delegator', constructor: Hash }) + @jsonMember({ + name: 'delegator', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) delegator: Hash; } @jsonObject export class CreditInfo { - @jsonMember({ name: 'validator', constructor: Hash }) + @jsonMember({ + name: 'validator', + constructor: Hash, + deserializer: json => Hash.fromJSON(json), + serializer: value => value.toJSON() + }) validator: Hash; @jsonMember({ name: 'eraId', constructor: Number }) diff --git a/src/types/key/EntryPointAddr.ts b/src/types/key/EntryPointAddr.ts index 0ba0b042..d7c4e910 100644 --- a/src/types/key/EntryPointAddr.ts +++ b/src/types/key/EntryPointAddr.ts @@ -49,7 +49,12 @@ const SelectorBytesLen = 4; */ @jsonObject class VmCasperV1 { - @jsonMember({ name: 'EntityAddr', constructor: EntityAddr }) + @jsonMember({ + name: 'EntityAddr', + constructor: EntityAddr, + deserializer: json => EntityAddr.fromJSON(json), + serializer: (value: EntityAddr) => value.toJSON() + }) entityAddr: EntityAddr; @jsonArrayMember(Uint8Array, { name: 'NameBytes' }) @@ -66,7 +71,12 @@ class VmCasperV1 { */ @jsonObject class VmCasperV2 { - @jsonMember({ name: 'EntityAddr', constructor: EntityAddr }) + @jsonMember({ + name: 'EntityAddr', + constructor: EntityAddr, + deserializer: json => EntityAddr.fromJSON(json), + serializer: value => value.toJSON() + }) entityAddr: EntityAddr; @jsonMember({ name: 'Selector', constructor: Number }) diff --git a/src/types/key/Hash.ts b/src/types/key/Hash.ts index bf277355..794a354b 100644 --- a/src/types/key/Hash.ts +++ b/src/types/key/Hash.ts @@ -110,6 +110,22 @@ export class Hash { return new Hash(new Uint8Array(buffer.slice(0, Hash.ByteHashLen))); } + public static createHashArray(byteArray: Uint8Array): Hash[] { + if (byteArray.length % Hash.ByteHashLen !== 0) { + throw new Error( + `Byte array length must be a multiple of ${Hash.ByteHashLen}.` + ); + } + + const hashes: Hash[] = []; + for (let i = 0; i < byteArray.length; i += Hash.ByteHashLen) { + const chunk = byteArray.subarray(i, i + Hash.ByteHashLen); + hashes.push(new Hash(chunk)); + } + + return hashes; + } + /** * Compares this Hash with another Hash for equality. * @param other - The other Hash to compare with. diff --git a/src/types/key/Key.ts b/src/types/key/Key.ts index 2e565c1b..39fd8082 100644 --- a/src/types/key/Key.ts +++ b/src/types/key/Key.ts @@ -1,4 +1,4 @@ -import { jsonObject, jsonMember } from 'typedjson'; +import { jsonObject, jsonMember, TypedJSON } from 'typedjson'; import { AccountHash } from './Account'; import { Hash } from './Hash'; import { TransferHash } from './Transfer'; @@ -148,76 +148,340 @@ export class Key { @jsonMember({ name: 'Type', constructor: Number }) type: TypeID; - @jsonMember({ name: 'Account', constructor: AccountHash }) + @jsonMember({ + name: 'Account', + constructor: AccountHash, + deserializer: json => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) account?: AccountHash; - @jsonMember({ name: 'Hash', constructor: Hash }) + @jsonMember({ + name: 'Hash', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) hash?: Hash; - @jsonMember({ name: 'URef', constructor: URef }) + @jsonMember({ + name: 'URef', + constructor: URef, + deserializer: json => { + if (!json) return; + return URef.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) uRef?: URef; - @jsonMember({ name: 'Transfer', constructor: TransferHash }) + @jsonMember({ + name: 'Transfer', + constructor: TransferHash, + deserializer: json => { + if (!json) return; + return TransferHash.fromJSON(json); + }, + serializer: (value: TransferHash) => { + if (!value) return; + return value.toJSON(); + } + }) transfer?: TransferHash; - @jsonMember({ name: 'Deploy', constructor: Hash }) + @jsonMember({ + name: 'Deploy', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) deploy?: Hash; - @jsonMember({ name: 'Era', constructor: Era }) + @jsonMember({ + name: 'Era', + constructor: Era, + deserializer: json => { + if (!json) return; + return Era.fromJSON(json); + }, + serializer: (value: Era) => { + if (!value) return; + return value.toJSON(); + } + }) era?: Era; - @jsonMember({ name: 'Balance', constructor: Hash }) + @jsonMember({ + name: 'Balance', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) balance?: Hash; - @jsonMember({ name: 'Bid', constructor: AccountHash }) + @jsonMember({ + name: 'Bid', + constructor: AccountHash, + deserializer: json => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) bid?: AccountHash; - @jsonMember({ name: 'Withdraw', constructor: AccountHash }) + @jsonMember({ + name: 'Withdraw', + constructor: AccountHash, + deserializer: json => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) withdraw?: AccountHash; - @jsonMember({ name: 'Dictionary', constructor: Hash }) + @jsonMember({ + name: 'Dictionary', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) dictionary?: Hash; - @jsonMember({ name: 'SystemContactRegistry', constructor: Hash }) + @jsonMember({ + name: 'SystemContactRegistry', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) systemContactRegistry?: Hash; - @jsonMember({ name: 'EraSummary', constructor: Hash }) + @jsonMember({ + name: 'EraSummary', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) eraSummary?: Hash; - @jsonMember({ name: 'Unbond', constructor: AccountHash }) + @jsonMember({ + name: 'Unbond', + constructor: AccountHash, + deserializer: json => { + if (!json) return; + return AccountHash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) unbond?: AccountHash; - @jsonMember({ name: 'ChainspecRegistry', constructor: Hash }) + @jsonMember({ + name: 'ChainspecRegistry', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) chainspecRegistry?: Hash; - @jsonMember({ name: 'ChecksumRegistry', constructor: Hash }) + @jsonMember({ + name: 'ChecksumRegistry', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) checksumRegistry?: Hash; - @jsonMember({ name: 'BidAddr', constructor: BidAddr }) + @jsonMember({ + name: 'BidAddr', + constructor: BidAddr, + deserializer: json => { + if (!json) return; + return BidAddr.fromJSON(json); + }, + serializer: (value: BidAddr) => { + if (!value) return; + return value.toJSON(); + } + }) bidAddr?: BidAddr; - @jsonMember({ name: 'Package', constructor: Hash }) + @jsonMember({ + name: 'Package', + constructor: Hash, + deserializer: json => { + if (!json) return; + return Hash.fromJSON(json); + }, + serializer: value => { + if (!value) return; + return value.toJSON(); + } + }) package?: Hash; - @jsonMember({ name: 'AddressableEntity', constructor: EntityAddr }) + @jsonMember({ + name: 'AddressableEntity', + constructor: EntityAddr, + deserializer: json => { + if (!json) return; + return EntityAddr.fromJSON(json); + }, + serializer: (value: EntityAddr) => { + if (!value) return; + return value.toJSON(); + } + }) addressableEntity?: EntityAddr; - @jsonMember({ name: 'ByteCode', constructor: ByteCode }) + @jsonMember({ + name: 'ByteCode', + constructor: ByteCode, + deserializer: json => { + if (!json) return; + return ByteCode.fromJSON(json); + }, + serializer: (value: ByteCode) => { + if (!value) return; + return value.toJSON(); + } + }) byteCode?: ByteCode; - @jsonMember({ name: 'Message', constructor: MessageAddr }) + @jsonMember({ + name: 'Message', + constructor: MessageAddr, + deserializer: json => { + if (!json) return; + return MessageAddr.fromString(json); + }, + serializer: (value: MessageAddr) => { + if (!value) return; + return value.toJSON(); + } + }) message?: MessageAddr; - @jsonMember({ name: 'NamedKey', constructor: NamedKeyAddr }) + @jsonMember({ + name: 'NamedKey', + constructor: NamedKeyAddr, + deserializer: json => { + if (!json) return; + return NamedKeyAddr.fromString(json); + }, + serializer: (value: NamedKeyAddr) => { + if (!value) return; + return value.toJSON(); + } + }) namedKey?: NamedKeyAddr; - @jsonMember({ name: 'BlockGlobal', constructor: BlockGlobalAddr }) + @jsonMember({ + name: 'BlockGlobal', + constructor: BlockGlobalAddr, + deserializer: json => { + if (!json) return; + return BlockGlobalAddr.fromJSON(json); + }, + serializer: (value: BlockGlobalAddr) => { + if (!value) return; + return value.toJSON(); + } + }) blockGlobal?: BlockGlobalAddr; - @jsonMember({ name: 'BalanceHold', constructor: BalanceHoldAddr }) + @jsonMember({ + name: 'BalanceHold', + constructor: BalanceHoldAddr, + deserializer: json => { + if (!json) return; + return BalanceHoldAddr.fromJSON(json); + }, + serializer: (value: BalanceHoldAddr) => { + if (!value) return; + return value.toJSON(); + } + }) balanceHold?: BalanceHoldAddr; - @jsonMember({ name: 'EntryPoint', constructor: EntryPointAddr }) + @jsonMember({ + name: 'EntryPoint', + constructor: EntryPointAddr, + deserializer: json => { + if (!json) return; + return EntryPointAddr.fromJSON(json); + }, + serializer: (value: EntryPointAddr) => { + if (!value) return; + return value.toJSON(); + } + }) entryPoint?: EntryPointAddr; /** @@ -764,6 +1028,32 @@ export class Key { return Key.createByType(source, keyIDbyPrefix.get(prefix)!); } + + // Inside the Key class + + /** + * Converts the Key instance to a JSON object. + * @returns The JSON representation of the Key instance. + */ + toJSON(): object { + return TypedJSON.toPlainJson(this, Key) as object; + } + + /** + * Creates a Key instance from a JSON object. + * @param json - The JSON object representing the Key instance. + * @returns A new Key instance. + */ + static fromJSON(json: object): Key { + const serializer = new TypedJSON(Key); + const keyInstance = serializer.parse(json); + + if (!keyInstance) { + throw new Error('Failed to parse JSON into Key'); + } + + return keyInstance; + } } /** diff --git a/src/types/key/MessageAddr.ts b/src/types/key/MessageAddr.ts index 3daaa399..5fbd26dd 100644 --- a/src/types/key/MessageAddr.ts +++ b/src/types/key/MessageAddr.ts @@ -37,7 +37,12 @@ export class MessageAddr { } /** The address of the associated entity. */ - @jsonMember({ name: 'EntityAddr', constructor: EntityAddr }) + @jsonMember({ + name: 'EntityAddr', + constructor: EntityAddr, + deserializer: json => EntityAddr.fromJSON(json), + serializer: value => value.toJSON() + }) public entityAddr: EntityAddr; /** The hash of the topic name. */