Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/js-evo-sdk/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,4 @@ export { GroupFacade } from './group/facade.js';
export { VotingFacade } from './voting/facade.js';
export { wallet } from './wallet/functions.js';
export { verifyIdentityResponse, verifyDataContract, verifyDocuments, start } from './wasm.js';
export { DataContract } from './wasm.js';
22 changes: 20 additions & 2 deletions packages/wasm-sdk/src/dpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ impl IdentityWasm {
// }
// }

#[wasm_bindgen]
#[wasm_bindgen(js_name=DataContract)]
pub struct DataContractWasm(DataContract);

impl From<DataContract> for DataContractWasm {
Expand All @@ -303,12 +303,30 @@ impl From<DataContract> for DataContractWasm {
}
}

#[wasm_bindgen]
#[wasm_bindgen(js_class=DataContract)]
impl DataContractWasm {
pub fn id(&self) -> String {
self.0.id().to_string(Encoding::Base58)
}

#[wasm_bindgen(js_name=fromJSON)]
pub fn from_json(json: &JsValue, platform_version: u32) -> Result<DataContractWasm, WasmSdkError> {
let platform_version = &PlatformVersion::get(platform_version).map_err(|e| {
WasmSdkError::invalid_argument(format!(
"unknown platform version {platform_version}: {e}"
))
})?;

let data_contract = DataContract::from_json(serde_wasm_bindgen::from_value(json.clone()).map_err(|e| {
WasmSdkError::serialization(format!("failed to convert json: {}", e))
})?, true, platform_version)
.map_err(|e| {
WasmSdkError::serialization(format!("failed to create DataContract from json: {}", e))
})?;

Ok(data_contract.into())
}

#[wasm_bindgen(js_name=toJSON)]
pub fn to_json(&self) -> Result<JsValue, WasmSdkError> {
let platform_version = PlatformVersion::first();
Expand Down
23 changes: 23 additions & 0 deletions packages/wasm-sdk/tests/unit/data-contract.spec.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import init, * as sdk from '../../dist/sdk.compressed.js';
import contractFixture from './fixtures/data-contract-crypto-card-game.mjs';

const PLATFORM_VERSION = 1;

describe('DataContract', () => {
before(async () => {
await init();
});

it('should create a contract from JSON and expose identifiers', async () => {
const contract = sdk.DataContract.fromJSON(contractFixture, PLATFORM_VERSION);

expect(contract).to.be.ok();
expect(contract.id()).to.equal(contractFixture.id);

const roundTripped = contract.toJSON();
expect(roundTripped).to.be.an('object');
expect(roundTripped.id).to.equal(contractFixture.id);

contract.free();
});
});
Copy link
Collaborator

@thephez thephez Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using a contract that includes tokens and groups also in the tests? Like https://testnet.platform-explorer.com/dataContract/Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i (schema below). Really tests should include an example of all contract version so any issues are caught, right?

v1 contract with documents, tokens, and groups
{ "$format_version": "1", "id": "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i", "config": { "$format_version": "1", "canBeDeleted": false, "readonly": false, "keepsHistory": false, "documentsKeepHistoryContractDefault": false, "documentsMutableContractDefault": true, "documentsCanBeDeletedContractDefault": true, "requiresIdentityEncryptionBoundedKey": null, "requiresIdentityDecryptionBoundedKey": null, "sizedIntegerTypes": true }, "version": 1, "ownerId": "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC", "schemaDefs": null, "documentSchemas": { "card": { "type": "object", "documentsMutable": false, "canBeDeleted": true, "transferable": 1, "tradeMode": 1, "creationRestrictionMode": 1, "properties": { "name": { "type": "string", "description": "Name of the card", "minLength": 0, "maxLength": 63, "position": 0 }, "description": { "type": "string", "description": "Description of the card", "minLength": 0, "maxLength": 256, "position": 1 }, "attack": { "type": "integer", "description": "Attack power of the card", "position": 2 }, "defense": { "type": "integer", "description": "Defense level of the card", "position": 3 } }, "indices": [ { "name": "owner", "properties": [ { "$ownerId": "asc" } ] }, { "name": "attack", "properties": [ { "attack": "asc" } ] }, { "name": "defense", "properties": [ { "defense": "asc" } ] } ], "required": [ "name", "attack", "defense" ], "additionalProperties": false } }, "createdAt": 1756237255149, "updatedAt": null, "createdAtBlockHeight": 174305, "updatedAtBlockHeight": null, "createdAtEpoch": 9690, "updatedAtEpoch": null, "groups": { "0": { "$format_version": "0", "members": { "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC": 1, "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG": 1 }, "required_power": 2 } }, "tokens": { "0": { "$format_version": "0", "conventions": { "$format_version": "0", "localizations": { "en": { "$format_version": "0", "shouldCapitalize": true, "singularForm": "stt-99", "pluralForm": "stt-99s" } }, "decimals": 0 }, "conventionsChangeRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "baseSupply": 100, "maxSupply": null, "keepsHistory": { "$format_version": "0", "keepsTransferHistory": true, "keepsFreezingHistory": true, "keepsMintingHistory": true, "keepsBurningHistory": true, "keepsDirectPricingHistory": true, "keepsDirectPurchaseHistory": true }, "startAsPaused": false, "allowTransferToFrozenBalance": true, "maxSupplyChangeRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "distributionRules": { "$format_version": "0", "perpetualDistribution": { "$format_version": "0", "distributionType": { "BlockBasedDistribution": { "interval": 100, "function": { "FixedAmount": { "amount": 1 } } } }, "distributionRecipient": "ContractOwner" }, "perpetualDistributionRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "preProgrammedDistribution": null, "newTokensDestinationIdentity": "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC", "newTokensDestinationIdentityRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "mintingAllowChoosingDestination": false, "mintingAllowChoosingDestinationRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "changeDirectPurchasePricingRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } } }, "marketplaceRules": { "$format_version": "0", "tradeMode": "NotTradeable", "tradeModeChangeRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } } }, "manualMintingRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "manualBurningRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "freezeRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "unfreezeRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "destroyFrozenFundsRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "emergencyActionRules": { "V0": { "authorized_to_make_change": "ContractOwner", "admin_action_takers": "ContractOwner", "changing_authorized_action_takers_to_no_one_allowed": true, "changing_admin_action_takers_to_no_one_allowed": true, "self_changing_admin_action_takers_allowed": true } }, "mainControlGroup": 0, "mainControlGroupCanBeModified": "ContractOwner", "description": null } }, "keywords": [ "stt-99" ], "description": null }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Let's do it in a separate PR though.

Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
const contract = {
$format_version: '0',
id: '86LHvdC1Tqx5P97LQUSibGFqf2vnKFpB6VkqQ7oso86e',
ownerId: '2QjL594djCH2NyDsn45vd6yQjEDHupMKo7CEGVTHtQxU',
version: 1,
config: {
$format_version: '0',
canBeDeleted: false,
readonly: false,
keepsHistory: false,
documentsKeepHistoryContractDefault: false,
documentsMutableContractDefault: true,
documentsCanBeDeletedContractDefault: true,
requiresIdentityEncryptionBoundedKey: null,
requiresIdentityDecryptionBoundedKey: null,
},
documentSchemas: {
card: {
type: 'object',
documentsMutable: false,
canBeDeleted: true,
transferable: 1,
tradeMode: 1,
properties: {
name: {
type: 'string',
description: 'Name of the card',
maxLength: 63,
position: 0,
},
description: {
type: 'string',
description: 'Description of the card',
maxLength: 256,
position: 1,
},
imageUrl: {
type: 'string',
description: 'URL of the image associated with the card',
maxLength: 2048,
format: 'uri',
position: 2,
},
imageHash: {
type: 'array',
description: 'SHA256 hash of the bytes of the image specified by imageUrl',
byteArray: true,
minItems: 32,
maxItems: 32,
position: 3,
},
rarity: {
type: 'string',
description: 'Rarity level of the card',
enum: [
'common',
'uncommon',
'rare',
'legendary',
],
position: 4,
},
},
required: [
'$createdAt',
'$updatedAt',
'name',
'description',
'imageUrl',
'imageHash',
'rarity',
],
additionalProperties: false,
indices: [
{
name: 'name',
properties: [
{
name: 'asc',
},
],
unique: true,
},
{
name: 'rarity',
properties: [
{
rarity: 'asc',
},
],
unique: false,
},
],
},
},
};

export default contract;
Loading