Skip to content

Commit

Permalink
Added new auction contract hash for integration-test network, fixed d…
Browse files Browse the repository at this point in the history
…eserialization of CES schema, Update contract package parsing compatible 1.x, added unit tests to cover mentioned bugs / features
  • Loading branch information
alexmyshchyshyn committed Jan 10, 2025
1 parent 05e9adc commit 7135881
Show file tree
Hide file tree
Showing 12 changed files with 643 additions and 323 deletions.
3 changes: 2 additions & 1 deletion src/@types/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export enum CasperNetworkName {
Mainnet = 'casper',
Testnet = 'casper-test'
Testnet = 'casper-test',
Integration = 'integration-test'
}

export enum AuctionManagerEntryPoint {
Expand Down
8 changes: 4 additions & 4 deletions src/types/Contract.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { jsonArrayMember, jsonMember, jsonObject } from 'typedjson';
import { ContractHash, ContractPackageHash } from './key';
import { EntryPointV1 } from './EntryPoint';
import { NamedKeys } from './NamedKey';
import { NamedEntryPoint } from './AddressableEntity';

/**
* Represents a smart contract on the blockchain, including its unique identifiers, entry points, named keys, and protocol version.
Expand Down Expand Up @@ -33,8 +33,8 @@ export class Contract {
/**
* The list of entry points (functions) that can be called on this contract.
*/
@jsonArrayMember(EntryPointV1, { name: 'entry_points' })
entryPoints: EntryPointV1[];
@jsonArrayMember(NamedEntryPoint, { name: 'entry_points' })
entryPoints: NamedEntryPoint[];

/**
* The named keys associated with the contract, providing access to specific values or data stored by the contract.
Expand All @@ -59,7 +59,7 @@ export class Contract {
constructor(
contractPackageHash: ContractPackageHash,
contractWasmHash: ContractHash,
entryPoints: EntryPointV1[],
entryPoints: NamedEntryPoint[],
namedKeys: NamedKeys,
protocolVersion: string
) {
Expand Down
158 changes: 158 additions & 0 deletions src/types/ContractPackage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { TypedJSON } from 'typedjson';
import { expect } from 'chai';

import { ContractPackage } from './ContractPackage';

describe('ContractPackage', () => {
const createSerializerAndParse = (json: any) => {
const serializer = new TypedJSON(ContractPackage);
return serializer.parse(json);
};

const commonAssertions = (
contractPackage: ContractPackage | undefined,
json: any
) => {
expect(contractPackage?.accessKey.toPrefixedString()).to.deep.equal(
json.access_key
);
expect(contractPackage?.lockStatus).to.deep.equal(json.lock_status);
expect(contractPackage?.versions).to.not.be.empty;
expect(contractPackage?.groups).to.not.be.empty;

json.versions.forEach((version: any, index: number) => {
expect(
contractPackage?.versions[index].contractHash.toPrefixedString()
).to.deep.equal(version.contract_hash);
expect(contractPackage?.versions[index].contractVersion).to.deep.equal(
version.contract_version
);
expect(
contractPackage?.versions[index].protocolVersionMajor
).to.deep.equal(version.protocol_version_major);
});
};

it('should parse ContractPackage V1', () => {
const jsonV1 = {
access_key:
'uref-dc06bab1599b6163eab6786ca9a13e2ef28f157bd69a3bc3d8a4018a70450f8b-007',
versions: [
{
protocol_version_major: 1,
contract_version: 1,
contract_hash:
'contract-55cc25981545886d019401a40768adf71084ff4c251734b54280c3f1d600c9d1'
},
{
protocol_version_major: 1,
contract_version: 2,
contract_hash:
'contract-171f1bac4d4b12e66bdd1dfe2575c463b2e2ad8706cfa574b87ff5566b4c644c'
},
{
protocol_version_major: 1,
contract_version: 3,
contract_hash:
'contract-52809f5150e80b49096f25964082de4bcc3bdbb243d38414bbb22091a4ebdf96'
}
],
disabled_versions: [
{
protocol_version_major: 1,
contract_version: 1
},
{
protocol_version_major: 1,
contract_version: 2
}
],
groups: [
{
group: 'constructor',
keys: [
'uref-1ff80105947906fa593f18198825aab2033047253edf839b16f36ab949158d2b-007'
]
}
],
lock_status: 'Unlocked'
};

const contractPackage = createSerializerAndParse(jsonV1);
commonAssertions(contractPackage, jsonV1);

expect(contractPackage?.disabledVersions[0][0]).to.deep.equal(
jsonV1.disabled_versions[0].protocol_version_major
);
expect(contractPackage?.disabledVersions[0][1]).to.deep.equal(
jsonV1.disabled_versions[0].contract_version
);

jsonV1.groups.forEach((group, index: number) => {
expect(contractPackage?.groups[index].groupName).to.deep.equal(
group.group
);
expect(
contractPackage?.groups[index].groupUsers[0].toPrefixedString()
).to.deep.equal(group.keys?.[0]);
});
});

it('should parse ContractPackage V2', () => {
const jsonV2 = {
access_key:
'uref-dc06bab1599b6163eab6786ca9a13e2ef28f157bd69a3bc3d8a4018a70450f8b-007',
versions: [
{
protocol_version_major: 1,
contract_version: 1,
contract_hash:
'contract-55cc25981545886d019401a40768adf71084ff4c251734b54280c3f1d600c9d1'
},
{
protocol_version_major: 1,
contract_version: 2,
contract_hash:
'contract-171f1bac4d4b12e66bdd1dfe2575c463b2e2ad8706cfa574b87ff5566b4c644c'
},
{
protocol_version_major: 1,
contract_version: 3,
contract_hash:
'contract-52809f5150e80b49096f25964082de4bcc3bdbb243d38414bbb22091a4ebdf96'
}
],
disabled_versions: [
[1, 1],
[1, 2]
],
groups: [
{
group_name: 'constructor',
group_users: [
'uref-1ff80105947906fa593f18198825aab2033047253edf839b16f36ab949158d2b-007'
]
}
],
lock_status: 'Unlocked'
};

const contractPackage = createSerializerAndParse(jsonV2);
commonAssertions(contractPackage, jsonV2);

expect(contractPackage?.disabledVersions[0][0]).to.deep.equal(
jsonV2.disabled_versions[0][0]
);
expect(contractPackage?.disabledVersions[0][1]).to.deep.equal(
jsonV2.disabled_versions[0][1]
);
jsonV2.groups.forEach((group, index: number) => {
expect(contractPackage?.groups[index].groupName).to.deep.equal(
group.group_name
);
expect(
contractPackage?.groups[index].groupUsers[0].toPrefixedString()
).to.deep.equal(group.group_users?.[0]);
});
});
});
94 changes: 52 additions & 42 deletions src/types/ContractPackage.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,10 @@
import { jsonArrayMember, jsonMember, jsonObject } from 'typedjson';
import { jsonArrayMember, jsonMember, jsonObject, TypedJSON } from 'typedjson';
import { ContractHash, URef } from './key';
import { deserializeDisabledVersions } from './SerializationUtils';

/**
* Represents a disabled contract version, marking specific versions as incompatible with the current protocol.
*/
@jsonObject
class DisabledContractVersion {
/**
* The version number of the contract.
*/
@jsonMember({ name: 'contract_version', constructor: Number })
contractVersion: number;

/**
* Major version of the protocol that disables this contract version.
*/
@jsonMember({ name: 'protocol_version_major', constructor: Number })
protocolVersionMajor: number;

/**
* Constructs a new `DisabledContractVersion` instance.
* @param contractVersion - The version of the contract.
* @param protocolVersionMajor - The major protocol version disabling this contract version.
*/
constructor(contractVersion: number, protocolVersionMajor: number) {
this.contractVersion = contractVersion;
this.protocolVersionMajor = protocolVersionMajor;
}
export interface IDisabledVersion {
protocol_version_major: number;
contract_version: number;
}

/**
Expand All @@ -37,27 +15,46 @@ export class ContractGroup {
/**
* The name of the group.
*/
@jsonMember({ name: 'group', constructor: String })
group: string;
@jsonMember({ name: 'group_name', constructor: String, preserveNull: true })
groupName: string;

/**
* The list of URef keys associated with this group, defining permissions for contract interaction.
*/
@jsonArrayMember(URef, {
name: 'keys',
name: 'group_users',
serializer: (value: URef[]) => value.map(it => it.toJSON()),
deserializer: (json: any) => json.map((it: string) => URef.fromJSON(it))
deserializer: (json: any) => {
if (!json) return;
return json.map((it: string) => URef.fromJSON(it));
}
})
keys: URef[];
groupUsers: URef[];

/**
* Constructs a new `ContractGroup` instance.
* @param group - The name of the group.
* @param keys - An array of URef keys associated with the group.
* Converts a plain JSON object into an instance of `ContractGroup`.
*
* @param json The JSON object to parse.
* @returns An instance of `ContractGroup` or `undefined` if parsing fails.
*/
constructor(group: string, keys: URef[]) {
this.group = group;
this.keys = keys;
public static fromJSON(json: any): ContractGroup | undefined {
const serializer = new TypedJSON(ContractGroup);
const contractGroup = serializer.parse(json);

// V1 Compatible
if (contractGroup) {
if (Array.isArray(json?.keys)) {
contractGroup.groupUsers = json.keys.map((it: any) =>
URef.fromJSON(it)
);
}

if (typeof json?.group === 'string') {
contractGroup.groupName = json.group;
}
}

return contractGroup;
}
}

Expand Down Expand Up @@ -125,13 +122,26 @@ export class ContractPackage {
/**
* Array of disabled contract versions, marking incompatible versions.
*/
@jsonArrayMember(DisabledContractVersion, { name: 'disabled_versions' })
disabledVersions: DisabledContractVersion[];
@jsonArrayMember(Number, {
name: 'disabled_versions',
dimensions: 2,
deserializer: json => {
if (!json) return;
return deserializeDisabledVersions(json);
}
})
disabledVersions: number[][];

/**
* Array of contract groups, managing access control with sets of URef keys.
*/
@jsonArrayMember(ContractGroup, { name: 'groups' })
@jsonArrayMember(ContractGroup, {
name: 'groups',
deserializer: json => {
if (!json) return;
return json.map((it: ContractGroup) => ContractGroup.fromJSON(it));
}
})
groups: ContractGroup[];

/**
Expand All @@ -156,7 +166,7 @@ export class ContractPackage {
*/
constructor(
accessKey: URef,
disabledVersions: DisabledContractVersion[],
disabledVersions: number[][],
groups: ContractGroup[],
versions: ContractVersion[],
lockStatus: string
Expand Down
5 changes: 4 additions & 1 deletion src/types/Package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ export class NamedUserGroup {
@jsonArrayMember(URef, {
name: 'group_users',
serializer: (value: URef[]) => value.map(it => it.toJSON()),
deserializer: (json: any) => json.map((it: string) => URef.fromJSON(it))
deserializer: (json: any) => {
if (!json) return;
return json.map((it: string) => URef.fromJSON(it));
}
})
groupUsers: URef[];

Expand Down
26 changes: 26 additions & 0 deletions src/types/SerializationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import humanizeDuration from 'humanize-duration';
import { Args } from './Args';
import { Conversions } from './Conversions';
import { CLValueUInt512 } from './clvalue';
import { IDisabledVersion } from './ContractPackage';

/**
* Serializes a `Uint8Array` into a hexadecimal string.
Expand Down Expand Up @@ -208,3 +209,28 @@ export const serializeRewards = (map: Map<string, CLValueUInt512[]>) => {
return [key, serializedValue];
});
};

/**
* Parses disabled versions into a standardized array of tuples.
*
* @param disabledVersions - The input array, which can be:
* - An array of tuples (`[number, number][]`), or
* - An array of objects with `protocol_version_major` and `contract_version` properties.
* @returns An array of tuples (`[number, number][]`) representing `[protocol_version_major, contract_version]`.
*/
export const deserializeDisabledVersions = (
disabledVersions: [number, number][] | IDisabledVersion[]
): [number, number][] => {
// V2 Compatible
if (Array.isArray(disabledVersions) && Array.isArray(disabledVersions[0])) {
return disabledVersions as [number, number][];
}

// V1 Compatible
return (disabledVersions as IDisabledVersion[]).map(
({ protocol_version_major, contract_version }) => [
contract_version,
protocol_version_major
]
);
};
Loading

0 comments on commit 7135881

Please sign in to comment.