diff --git a/src/rpc/response.ts b/src/rpc/response.ts index d5b20adc..0dbb75ea 100644 --- a/src/rpc/response.ts +++ b/src/rpc/response.ts @@ -432,6 +432,13 @@ export class InfoGetTransactionResultV1Compatible { } throw new Error('Incorrect RPC response structure'); } + + public static fromJSON( + json: any + ): InfoGetTransactionResultV1Compatible | undefined { + const serializer = new TypedJSON(InfoGetTransactionResultV1Compatible); + return serializer.parse(json); + } } @jsonObject diff --git a/src/types/EntryPoint.ts b/src/types/EntryPoint.ts index 9164700d..0ae7626c 100644 --- a/src/types/EntryPoint.ts +++ b/src/types/EntryPoint.ts @@ -69,21 +69,6 @@ export class EntryPointArg { } } -/** - * Class representing access control options for an entry point. - * This class is used for controlling the permissions required to call the entry point. - * - * TODO: Match with Go code when ready - */ -@jsonObject -export class EntryPointAccess { - /** - * The access control options for this entry point. - */ - @jsonMember({ name: 'access_control_options', constructor: AnyT }) - accessControlOptions: any; -} - /** * Class representing version 1 of an entry point in the Casper VM. * It contains the entry point's access, arguments, type, payment type, name, and return type. @@ -95,9 +80,9 @@ export class EntryPointV1 { */ @jsonMember({ name: 'access', - constructor: EntryPointAccess + constructor: AnyT }) - access: EntryPointAccess; + access: any; /** * A list of arguments for the entry point. @@ -157,7 +142,7 @@ export class EntryPointV1 { * @param ret The return type of the entry point. */ constructor( - access: EntryPointAccess, + access: any, args: EntryPointArg[], entryPointType: EntryPointType, entryPointPayment: EntryPointPayment, diff --git a/src/types/Transaction.ts b/src/types/Transaction.ts index af6bf227..dcbd8402 100644 --- a/src/types/Transaction.ts +++ b/src/types/Transaction.ts @@ -170,9 +170,7 @@ export class TransactionV1 { * @param keys The private key to sign the transaction. */ sign(keys: PrivateKey): void { - const signatureBytes = keys.signAndAddAlgorithmBytes( - this.hash.toBytes() - ); + const signatureBytes = keys.signAndAddAlgorithmBytes(this.hash.toBytes()); const signature = new HexBytes(signatureBytes); if (!this.approvals) { @@ -494,9 +492,7 @@ export class Transaction { * @param key The private key to sign the transaction. */ sign(key: PrivateKey): void { - const signatureBytes = key.signAndAddAlgorithmBytes( - this.hash.toBytes() - ); + const signatureBytes = key.signAndAddAlgorithmBytes(this.hash.toBytes()); this.setSignature(signatureBytes, key.publicKey); } @@ -572,7 +568,7 @@ export class Transaction { } if (this.originDeployV1) { - return Deploy.toJSON(this.originDeployV1) + return Deploy.toJSON(this.originDeployV1); } throw new Error('Incorrect Transaction instance. Missing origin value'); diff --git a/src/types/TransactionEntryPoint.ts b/src/types/TransactionEntryPoint.ts index 8000dcc3..a49dcc49 100644 --- a/src/types/TransactionEntryPoint.ts +++ b/src/types/TransactionEntryPoint.ts @@ -125,7 +125,9 @@ export class TransactionEntryPoint { this.type === TransactionEntryPointEnum.Custom && this.customEntryPoint ) { - const entryPointBytes = CLValueString.newCLString(this.customEntryPoint).bytes() + const entryPointBytes = CLValueString.newCLString( + this.customEntryPoint + ).bytes(); calltableSerialization.addField(1, entryPointBytes); } @@ -156,7 +158,7 @@ export class TransactionEntryPoint { * @throws An error if the JSON is invalid or the entry point is unknown. */ static fromJSON(json: any): TransactionEntryPoint { - if (json instanceof Object && json.Custom) { + if (json?.Custom) { return new TransactionEntryPoint( TransactionEntryPointEnum.Custom, json.Custom diff --git a/src/types/Transform.test.ts b/src/types/Transform.test.ts index f58f9258..b963b72d 100644 --- a/src/types/Transform.test.ts +++ b/src/types/Transform.test.ts @@ -1,6 +1,7 @@ import { assert, expect } from 'chai'; +import { TypedJSON } from 'typedjson'; -import { TransformKind } from './Transform'; +import { Transform, TransformKey, TransformKind } from './Transform'; import { CLValue } from './clvalue'; describe('TransformKind JSON Parsing and CLValue Transformation', () => { @@ -143,4 +144,489 @@ describe('TransformKind JSON Parsing and CLValue Transformation', () => { transformWriteJson.WriteTransfer.target ); }); + + it('should correctly parse and match the ContractPackage V1', () => { + const transformContractPackageV1Json = { + key: + 'hash-f1740bb7fecb174954378ced69b2ced7d0fedd0f37957f6bf00da27670a113ae', + transform: 'WriteContractPackage' + }; + const serializer = new TypedJSON(TransformKey); + const jsonRes = serializer.parse(transformContractPackageV1Json); + + const isWriteContractPackage = jsonRes?.transform?.isWriteContractPackage(); + expect(isWriteContractPackage).to.be.true; + }); + + it('should correctly parse and match the ContractPackage V2', () => { + const transformContractPackageV2Json = { + key: + 'hash-b71675d8cf701d9bc584cb5152706873110e5158b004fb966ed28be49c66b39a', + kind: { + Write: { + ContractPackage: { + access_key: + 'uref-5268245ec93d03335ed91c860ac48885fa6ee15156871458a84daa93b2d04f06-007', + versions: [ + { + protocol_version_major: 2, + contract_version: 1, + contract_hash: + 'contract-94b21891ae273b17eeb6a1899a52ab952bcc4da2e19626563f88d6cf7ab6a2bd' + } + ], + disabled_versions: [], + groups: [], + lock_status: 'Locked' + } + } + } + }; + + const serializer = new TypedJSON(Transform); + const jsonRes = serializer.parse(transformContractPackageV2Json); + const transformKind = jsonRes?.kind; + + const isWriteContractPackage = transformKind?.isWriteContractPackage(); + const contractPackage = transformKind?.parseAsWriteContractPackage(); + + expect(isWriteContractPackage).to.be.true; + expect(contractPackage).to.not.be.undefined; + expect(contractPackage?.groups).to.deep.equal([]); + expect(contractPackage?.disabledVersions).to.deep.equal([]); + expect(contractPackage?.lockStatus).to.deep.equal( + transformContractPackageV2Json.kind.Write.ContractPackage.lock_status + ); + expect(contractPackage?.accessKey.toPrefixedString()).to.deep.equal( + transformContractPackageV2Json.kind.Write.ContractPackage.access_key + ); + + expect( + contractPackage?.versions[0]?.contractHash?.toPrefixedString() + ).to.deep.equal( + transformContractPackageV2Json.kind.Write.ContractPackage.versions[0] + .contract_hash + ); + expect(contractPackage?.versions[0]?.contractVersion).to.deep.equal( + transformContractPackageV2Json.kind.Write.ContractPackage.versions[0] + .contract_version + ); + expect(contractPackage?.versions[0]?.protocolVersionMajor).to.deep.equal( + transformContractPackageV2Json.kind.Write.ContractPackage.versions[0] + .protocol_version_major + ); + }); + + it('should correctly parse and match the Contract V1', () => { + const transformContractV1Json = { + key: + 'hash-000022b5e09c1c45c35b228f0be16864d7da3985c69ea3c4e16133e9f476e03e', + transform: 'WriteContract' + }; + const serializer = new TypedJSON(TransformKey); + const jsonRes = serializer.parse(transformContractV1Json); + + const isWriteContract = jsonRes?.transform?.isWriteContract(); + expect(isWriteContract).to.be.true; + }); + + it('should correctly parse and match the Contract V2', () => { + const transformContractV2Json = { + key: + 'hash-94b21891ae273b17eeb6a1899a52ab952bcc4da2e19626563f88d6cf7ab6a2bd', + kind: { + Write: { + Contract: { + contract_package_hash: + 'contract-package-b71675d8cf701d9bc584cb5152706873110e5158b004fb966ed28be49c66b39a', + contract_wasm_hash: + 'contract-wasm-a9fb7ec293465829432a8e543a2c2d5bba6d622c512af7e0cf204f6f536a1cbf', + named_keys: [ + { + name: '__events', + key: + 'uref-821900c35dcc7bedfa01d4901621990552d80870228b4d9a37e47364ae7d12a6-007' + }, + { + name: '__events_ces_version', + key: + 'uref-993d25549280a839ef18c81d1211e86766ff29e7b7685912bc0e4e4a4964d886-007' + }, + { + name: '__events_length', + key: + 'uref-c640355c74023b0eb6025d376285235767ffe73d9be57d83576e647aa720eacc-007' + }, + { + name: '__events_schema', + key: + 'uref-5bf9cb0884874cc7d80e5bcab54c5100bfc8773908b85099ab569b32bd6bd72d-007' + }, + { + name: 'state', + key: + 'uref-c5ae802a50fb72194d3c543805bab1a612186bdf2cc5d62758595694a1928fff-007' + } + ], + entry_points: [ + { + name: 'add_to_the_pool', + args: [], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'allowance', + args: [ + { + name: 'owner', + cl_type: 'Key' + }, + { + name: 'spender', + cl_type: 'Key' + } + ], + ret: 'U256', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'approve', + args: [ + { + name: 'spender', + cl_type: 'Key' + }, + { + name: 'amount', + cl_type: 'U256' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'balance_of', + args: [ + { + name: 'account', + cl_type: 'Key' + } + ], + ret: 'U256', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'burn', + args: [ + { + name: 'owner', + cl_type: 'Key' + }, + { + name: 'amount', + cl_type: 'U256' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'change_security', + args: [ + { + name: 'admin_list', + cl_type: { + List: 'Key' + } + }, + { + name: 'minter_list', + cl_type: { + List: 'Key' + } + }, + { + name: 'none_list', + cl_type: { + List: 'Key' + } + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'claim', + args: [ + { + name: 'receipt_id', + cl_type: 'U32' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'decimals', + args: [], + ret: 'U8', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'decrease_allowance', + args: [ + { + name: 'spender', + cl_type: 'Key' + }, + { + name: 'decr_by', + cl_type: 'U256' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'grant_role', + args: [ + { + name: 'role', + cl_type: { + ByteArray: 32 + } + }, + { + name: 'address', + cl_type: 'Key' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'has_role', + args: [ + { + name: 'role', + cl_type: { + ByteArray: 32 + } + }, + { + name: 'address', + cl_type: 'Key' + } + ], + ret: 'Bool', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'increase_allowance', + args: [ + { + name: 'spender', + cl_type: 'Key' + }, + { + name: 'inc_by', + cl_type: 'U256' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'init', + args: [ + { + name: 'validator_address', + cl_type: 'PublicKey' + }, + { + name: 'claim_time', + cl_type: 'U64' + } + ], + ret: 'Unit', + access: { + Groups: ['constructor_group'] + }, + entry_point_type: 'Called' + }, + { + name: 'mint', + args: [ + { + name: 'owner', + cl_type: 'Key' + }, + { + name: 'amount', + cl_type: 'U256' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'name', + args: [], + ret: 'String', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'remove_from_the_pool', + args: [ + { + name: 'amount', + cl_type: 'U512' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'revoke_role', + args: [ + { + name: 'role', + cl_type: { + ByteArray: 32 + } + }, + { + name: 'address', + cl_type: 'Key' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'stake', + args: [], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'staked_cspr', + args: [], + ret: 'U512', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'symbol', + args: [], + ret: 'String', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'total_supply', + args: [], + ret: 'U256', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'transfer', + args: [ + { + name: 'recipient', + cl_type: 'Key' + }, + { + name: 'amount', + cl_type: 'U256' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'transfer_from', + args: [ + { + name: 'owner', + cl_type: 'Key' + }, + { + name: 'recipient', + cl_type: 'Key' + }, + { + name: 'amount', + cl_type: 'U256' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'unstake', + args: [ + { + name: 'scspr_amount', + cl_type: 'U256' + } + ], + ret: 'U32', + access: 'Public', + entry_point_type: 'Called' + }, + { + name: 'withdraw_from_the_pool', + args: [ + { + name: 'amount', + cl_type: 'U512' + } + ], + ret: 'Unit', + access: 'Public', + entry_point_type: 'Called' + } + ], + protocol_version: '2.0.0' + } + } + } + }; + + const serializer = new TypedJSON(Transform); + const jsonRes = serializer.parse(transformContractV2Json); + const transformKind = jsonRes?.kind; + const isWriteContract = transformKind?.isWriteContract(); + const contract = transformKind?.parseAsWriteContract(); + + expect(isWriteContract).to.be.true; + expect(contract?.contractPackageHash.toPrefixedString()).to.deep.equal( + transformContractV2Json.kind.Write.Contract.contract_package_hash + ); + expect(contract?.contractWasmHash.toPrefixedWasmString()).to.deep.equal( + transformContractV2Json.kind.Write.Contract.contract_wasm_hash + ); + }); }); diff --git a/src/types/Transform.ts b/src/types/Transform.ts index 8c73038e..84ded94c 100644 --- a/src/types/Transform.ts +++ b/src/types/Transform.ts @@ -20,6 +20,8 @@ import { RawWriteAccount, RawWriteCLValue, RawWriteCLValueV2, + RawWriteContract, + RawWriteContractPackage, RawWriteDeployInfo, RawWriteTransferTransform, RawWriteUnbonding, @@ -27,6 +29,8 @@ import { TranformAddressableEntityRawData, WriteTransfer } from './TransformRaw'; +import { Contract } from './Contract'; +import { ContractPackage } from './ContractPackage'; /** * Represents different types of transformation that can be applied. @@ -109,7 +113,20 @@ export class TransformKind { * @returns `true` if the transformation is a WriteContract, otherwise `false`. */ public isWriteContract(): boolean { - return this.isTransformation('WriteContract'); + /** + * v1 compatible check + */ + if (this.isTransformation('WriteContract')) { + return true; + } + + /** + * v2 compatible check + */ + const serializer = new TypedJSON(RawWriteContract); + const jsonRes = serializer.parse(this.data); + + return !!jsonRes?.Write?.Contract; } /** @@ -220,6 +237,28 @@ export class TransformKind { return this.isTransformation('WriteDeployInfo'); } + /** + * Checks if the transformation is a WriteContractPackage. + * + * @returns `true` if the transformation is a WriteContractPackage, otherwise `false`. + */ + public isWriteContractPackage(): boolean { + /** + * v1 compatible check + */ + if (this.isTransformation('WriteContractPackage')) { + return true; + } + + /** + * v2 compatible check + */ + const serializer = new TypedJSON(RawWriteContractPackage); + const jsonRes = serializer.parse(this.data); + + return !!jsonRes?.Write?.ContractPackage; + } + /** * Attempts to parse the transformation as a WriteTransfer. * @@ -434,6 +473,38 @@ export class TransformKind { return jsonRes2.Write?.CLValue; } + /** + * Attempts to parse the transformation as a WriteContract. + * + * @returns A `Contract` object if the data matches, otherwise `throw an error`. + */ + public parseAsWriteContract(): Contract { + const serializer = new TypedJSON(RawWriteContract); + const jsonRes = serializer.parse(this.data); + + if (!jsonRes || !jsonRes.Write?.Contract) { + throw new Error(`Error parsing as WriteContract`); + } + + return jsonRes.Write.Contract; + } + + /** + * Attempts to parse the transformation as a WriteContractPackage. + * + * @returns A `ContractPackage` object if the data matches, otherwise `throw an error`. + */ + public parseAsWriteContractPackage(): ContractPackage { + const serializer = new TypedJSON(RawWriteContractPackage); + const jsonRes = serializer.parse(this.data); + + if (!jsonRes || !jsonRes.Write?.ContractPackage) { + throw new Error(`Error parsing as WriteContractPackage`); + } + + return jsonRes.Write.ContractPackage; + } + /** * Checks if `TransformKind` has the transformation specified by name. * diff --git a/src/types/TransformRaw.ts b/src/types/TransformRaw.ts index 5cca5b01..d132a773 100644 --- a/src/types/TransformRaw.ts +++ b/src/types/TransformRaw.ts @@ -10,6 +10,8 @@ import { CLValue, CLValueParser, CLValueUInt512 } from './clvalue'; import { DeployInfo } from './DeployInfo'; import { BigNumber } from '@ethersproject/bignumber'; import { AccountHash, Hash, URef } from './key'; +import { Contract } from './Contract'; +import { ContractPackage } from './ContractPackage'; /** * Represents a transfer operation in a transaction. @@ -407,3 +409,39 @@ export class RawWriteCLValueV2 { }) Write?: WriteCLValue; } + +@jsonObject +export class WriteContract { + @jsonMember({ + name: 'Contract', + constructor: Contract + }) + Contract?: Contract; +} + +@jsonObject +export class RawWriteContract { + @jsonMember({ + name: 'Write', + constructor: WriteContract + }) + Write?: WriteContract; +} + +@jsonObject +export class WriteContractPackage { + @jsonMember({ + name: 'ContractPackage', + constructor: ContractPackage + }) + ContractPackage?: ContractPackage; +} + +@jsonObject +export class RawWriteContractPackage { + @jsonMember({ + name: 'Write', + constructor: WriteContractPackage + }) + Write?: WriteContractPackage; +}