Data Contract#
Data Contract Overview#
Data contracts define the schema (structure) of data an application will store on Dash Platform. Contracts are described using JSON Schema which allows the platform to validate the contract-related data submitted to it.
The following sections provide details that developers need to construct valid contracts. All data contracts must define at least one document or token. A contract may define multiple documents and/or tokens.
Fees#
Dash Platform charges fees for registering data contracts based on complexity. These fees compensate evonodes for their role in storing and processing contract-related data.
The table below outlines the current fee structure for various data contract components. Fees are denominated in DASH and are charged at registration time based on the structure of the contract.
Fee Component |
Amount (DASH) |
Description |
|---|---|---|
|
0.1 |
Fixed fee for every data contract. Covers the baseline cost of anchoring a contract into platform state. |
|
0.02 |
Charged per document type. Reflects indexing and storage schema overhead. |
|
0.01 |
Per non-unique index in a document type. Supports query operations. |
|
0.01 |
Per unique index. Enforces uniqueness and adds validation complexity. |
|
1.0 |
Per contested index. Used for identity/username resolution; requires voting and conflict resolution by masternodes and evonodes. |
|
0.1 |
Per token defined in the contract. Reflects additional overhead from managing balances, transfers, and supply. |
|
0.1 |
Additional fee for tokens that use perpetual (e.g., block-based) distribution mechanisms. These create ongoing state changes triggered by network events. |
|
0.1 |
Charged when tokens use scheduled distributions (e.g., airdrops). Adds periodic complexity. |
|
0.1 per keyword |
Charged per search keyword defined. Keywords enable reverse lookups and indexing, increasing on-chain storage and filtering load. |
These fees are additive. For example, a contract that defines two document types, each with one unique index, and one token using a perpetual distribution will incur the following total fee:
0.1 (base contract) + 0.02×2 (document types) + 0.01×2 (1 unique index per document type × 2) = 0.16 DASH
0.1 (token registration) + 0.1 (perpetual distribution) = 0.2 DASH
Total fee: 0.16 + 0.2 = 0.36 DASH
General Constraints#
There are a variety of constraints currently defined for performance and security reasons. The following constraints are applicable to all aspects of data contracts.
Data Size#
Parameter |
Size |
|---|---|
Estimated maximum serialized data contract size |
|
Maximum field value size |
|
Maximum state transition size |
A document cannot exceed the maximum state transition size in any case. For example, although it is possible to define a data contract with 10 document fields that each support the maximum field size (5120), it is not possible to create a document where all 10 fields contain the full 5120 bytes. This is because the overall document and state transition containing it would be too large (5120 * 10 = 51200 bytes).
Additional Properties#
Although JSON Schema allows additional, undefined properties by default, they are not allowed in Dash Platform data contracts. Data contract validation will fail if they are not explicitly forbidden using the additionalProperties keyword anywhere properties are defined (including within document properties of type object).
Include the following at the same level as the properties keyword to ensure proper validation:
"additionalProperties": false
Data Contract Object#
The data contract object consists of the following fields as defined in the Rust reference client (rs-dpp):
Property |
Type |
Size |
Description |
|---|---|---|---|
array of bytes |
32 bytes |
Contract ID generated from |
|
unsigned integer |
32 bits |
The data contract version |
|
ownerId |
array of bytes |
32 bytes |
Identity that registered the data contract defining the document (content media type: |
object |
Varies |
(Optional *) Document definitions (see Contract Documents for details) |
|
DataContractConfig |
Varies |
(Optional) Internal configuration for the contract |
|
$defs |
object |
Varies |
(Optional) Definitions for |
Group |
Varies |
(Optional) Groups that allow for specific multiparty actions on the contract. |
|
object |
Varies |
(Optional *) Token definitions (see Contract Tokens for details) |
|
keywords |
array of strings |
Varies |
(Optional) Keywords associated with the contract to improve searchability. Maximum of 50 keywords. |
description |
string |
3-100 characters |
(Optional) Brief description of the contract. |
createdAt |
unsigned integer |
64 bits |
(Read-only) Timestamp in milliseconds when the contract was created. Set by platform. |
updatedAt |
unsigned integer |
64 bits |
(Read-only) Timestamp in milliseconds when the contract was last updated. Set by platform. |
createdAtBlockHeight |
unsigned integer |
64 bits |
(Read-only) Block height at contract creation. Set by platform. |
updatedAtBlockHeight |
unsigned integer |
64 bits |
(Read-only) Block height at last contract update. Set by platform. |
createdAtEpoch |
unsigned integer |
16 bits |
(Read-only) Epoch index at contract creation. Set by platform. |
updatedAtEpoch |
unsigned integer |
16 bits |
(Read-only) Epoch index at last contract update. Set by platform. |
* The data contract object must define documents or tokens. It may include both documents and tokens.
Document type meta-schema#
Each document type defined within a data contract is validated against the document meta-schema. The full schema is defined in rs-dpp, hosted on GitHub, and can be viewed by expanding this dropdown:
Full schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/dashpay/platform/blob/master/packages/rs-dpp/schema/meta_schemas/document/v0/document-meta.json",
"type": "object",
"$defs": {
"documentProperties": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9-_]{1,64}$": {
"type": "object",
"allOf": [
{
"$ref": "#/$defs/documentSchema"
}
],
"unevaluatedProperties": false
}
},
"propertyNames": {
"pattern": "^[a-zA-Z0-9-_]{1,64}$"
},
"minProperties": 1,
"maxProperties": 100
},
"documentSchemaArray": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"allOf": [
{
"$ref": "#/$defs/documentSchema"
}
],
"unevaluatedProperties": false
}
},
"documentSchema": {
"type": "object",
"properties": {
"$id": {
"type": "string",
"pattern": "^#",
"minLength": 1
},
"$ref": {
"type": "string",
"pattern": "^#",
"minLength": 1
},
"$comment": {
"$ref": "https://json-schema.org/draft/2020-12/meta/core#/properties/$comment"
},
"description": {
"$ref": "https://json-schema.org/draft/2020-12/meta/meta-data#/properties/description"
},
"examples": {
"$ref": "https://json-schema.org/draft/2020-12/meta/meta-data#/properties/examples"
},
"multipleOf": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/multipleOf"
},
"maximum": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/maximum"
},
"exclusiveMaximum": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/exclusiveMaximum"
},
"minimum": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/minimum"
},
"exclusiveMinimum": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/exclusiveMinimum"
},
"maxLength": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/maxLength"
},
"minLength": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/minLength"
},
"pattern": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/pattern"
},
"maxItems": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/maxItems"
},
"minItems": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/minItems"
},
"uniqueItems": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/uniqueItems"
},
"contains": {
"$ref": "https://json-schema.org/draft/2020-12/meta/applicator#/properties/contains"
},
"maxProperties": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/maxProperties"
},
"minProperties": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/minProperties"
},
"required": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/required"
},
"additionalProperties": {
"type": "boolean",
"const": false
},
"properties": {
"$ref": "#/$defs/documentProperties"
},
"dependentRequired": {
"type": "object",
"minProperties": 1,
"additionalProperties": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/$defs/stringArray"
}
},
"const": true,
"enum": {
"type": "array",
"items": true,
"minItems": 1,
"uniqueItems": true
},
"type": {
"$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/type"
},
"format": {
"$ref": "https://json-schema.org/draft/2020-12/meta/format-annotation#/properties/format"
},
"contentMediaType": {
"$ref": "https://json-schema.org/draft/2020-12/meta/content#/properties/contentMediaType"
},
"byteArray": {
"type": "boolean",
"const": true
},
"position": {
"type": "integer",
"minimum": 0
}
},
"dependentSchemas": {
"byteArray": {
"description": "should be used only with array type",
"properties": {
"type": {
"type": "string",
"const": "array"
}
}
},
"contentMediaType": {
"if": {
"properties": {
"contentMediaType": {
"const": "application/x.dash.dpp.identifier"
}
}
},
"then": {
"properties": {
"byteArray": {
"const": true
},
"minItems": {
"const": 32
},
"maxItems": {
"const": 32
}
},
"required": [
"byteArray",
"minItems",
"maxItems"
]
}
},
"pattern": {
"description": "prevent slow pattern matching of large strings",
"properties": {
"maxLength": {
"type": "integer",
"minimum": 0,
"maximum": 50000
}
},
"required": [
"maxLength"
]
},
"format": {
"description": "prevent slow format validation of large strings",
"properties": {
"maxLength": {
"type": "integer",
"minimum": 0,
"maximum": 50000
}
},
"required": [
"maxLength"
]
}
},
"allOf": [
{
"$comment": "require index for object properties",
"if": {
"properties": {
"type": {
"const": "object"
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"properties": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"position": true
},
"required": [
"position"
]
}
}
}
}
},
{
"$comment": "allow only byte arrays",
"if": {
"properties": {
"type": {
"const": "array"
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"byteArray": true
},
"required": [
"byteArray"
]
}
},
{
"$comment": "all object properties must be defined",
"if": {
"properties": {
"type": {
"const": "object"
}
},
"not": {
"properties": {
"$ref": true
},
"required": [
"$ref"
]
}
},
"then": {
"properties": {
"properties": {
"$ref": "#/$defs/documentProperties"
},
"additionalProperties": {
"$ref": "#/$defs/documentSchema/properties/additionalProperties"
}
},
"required": [
"properties",
"additionalProperties"
]
}
}
]
},
"documentActionTokenCost": {
"type": "object",
"properties": {
"contractId": {
"type": "array",
"contentMediaType": "application/x.dash.dpp.identifier",
"byteArray": true,
"minItems": 32,
"maxItems": 32
},
"tokenPosition": {
"type": "integer",
"minimum": 0,
"maximum": 65535
},
"amount": {
"type": "integer",
"minimum": 1,
"maximum": 281474976710655
},
"effect": {
"type": "integer",
"enum": [
0,
1
],
"description": "0 - TransferTokenToContractOwner (default), 1 - Burn"
},
"gasFeesPaidBy": {
"type": "integer",
"enum": [
0,
1,
2
],
"description": "0 - DocumentOwner (default), 1 - ContractOwner, 2 - PreferContractOwner"
}
},
"required": [
"tokenPosition",
"amount"
],
"additionalProperties": false
}
},
"properties": {
"type": {
"type": "string",
"const": "object"
},
"$schema": {
"type": "string",
"const": "https://github.com/dashpay/platform/blob/master/packages/rs-dpp/schema/meta_schemas/document/v0/document-meta.json"
},
"$defs": {
"$ref": "#/$defs/documentProperties"
},
"indices": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 32
},
"properties": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"maxLength": 256
},
"additionalProperties": {
"type": "string",
"enum": [
"asc"
]
},
"minProperties": 1,
"maxProperties": 1
},
"minItems": 1,
"maxItems": 10
},
"unique": {
"type": "boolean"
},
"nullSearchable": {
"type": "boolean"
},
"contested": {
"type": "object",
"properties": {
"fieldMatches": {
"type": "array",
"items": {
"type": "object",
"properties": {
"field": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"regexPattern": {
"type": "string",
"minLength": 1,
"maxLength": 256
}
},
"additionalProperties": false,
"required": [
"field",
"regexPattern"
]
},
"minItems": 1
},
"resolution": {
"type": "integer",
"enum": [
0
],
"description": "Resolution. 0 - Masternode Vote"
},
"description": {
"type": "string",
"minLength": 1,
"maxLength": 256
}
},
"required": [
"resolution"
],
"additionalProperties": false
}
},
"required": [
"properties",
"name"
],
"additionalProperties": false
},
"minItems": 1,
"maxItems": 10
},
"signatureSecurityLevelRequirement": {
"type": "integer",
"enum": [
1,
2,
3
],
"description": "Public key security level. 1 - Critical, 2 - High, 3 - Medium. If none specified, High level is used"
},
"documentsKeepHistory": {
"type": "boolean",
"description": "True if the documents keep all their history, default is false"
},
"documentsMutable": {
"type": "boolean",
"description": "True if the documents are mutable, default is true"
},
"canBeDeleted": {
"type": "boolean",
"description": "True if the documents can be deleted, default is true"
},
"transferable": {
"type": "integer",
"enum": [
0,
1
],
"description": "Transferable without a marketplace sell. 0 - Never, 1 - Always"
},
"tradeMode": {
"type": "integer",
"enum": [
0,
1
],
"description": "Built in marketplace system. 0 - None, 1 - Direct purchase (The user can buy the item without the need for an approval)"
},
"creationRestrictionMode": {
"type": "integer",
"enum": [
0,
1,
2
],
"description": "Restrictions of document creation. 0 - No restrictions, 1 - Owner only, 2 - No creation (System Only)"
},
"requiresIdentityEncryptionBoundedKey": {
"type": "integer",
"enum": [
0,
1,
2
],
"description": "Key requirements. 0 - Unique Non Replaceable, 1 - Multiple, 2 - Multiple with reference to latest key."
},
"requiresIdentityDecryptionBoundedKey": {
"type": "integer",
"enum": [
0,
1,
2
],
"description": "Key requirements. 0 - Unique Non Replaceable, 1 - Multiple, 2 - Multiple with reference to latest key."
},
"tokenCost": {
"type": "object",
"properties": {
"create": {
"$ref": "#/$defs/documentActionTokenCost"
},
"replace": {
"$ref": "#/$defs/documentActionTokenCost"
},
"delete": {
"$ref": "#/$defs/documentActionTokenCost"
},
"transfer": {
"$ref": "#/$defs/documentActionTokenCost"
},
"update_price": {
"$ref": "#/$defs/documentActionTokenCost"
},
"purchase": {
"$ref": "#/$defs/documentActionTokenCost"
}
},
"additionalProperties": false
},
"properties": {
"type": "object",
"additionalProperties": {
"type": "object",
"allOf": [
{
"$ref": "#/$defs/documentSchema"
}
],
"unevaluatedProperties": false
},
"properties": {
"$id": true,
"$ownerId": true,
"$revision": true,
"$createdAt": true,
"$updatedAt": true,
"$transferredAt": true,
"$createdAtBlockHeight": true,
"$updatedAtBlockHeight": true,
"$transferredAtBlockHeight": true,
"$createdAtCoreBlockHeight": true,
"$updatedAtCoreBlockHeight": true,
"$transferredAtCoreBlockHeight": true
},
"propertyNames": {
"oneOf": [
{
"type": "string",
"pattern": "^[a-zA-Z0-9-_]{1,64}$"
},
{
"type": "string",
"enum": [
"$id",
"$ownerId",
"$revision",
"$createdAt",
"$updatedAt",
"$transferredAt",
"$createdAtBlockHeight",
"$updatedAtBlockHeight",
"$transferredAtBlockHeight",
"$createdAtCoreBlockHeight",
"$updatedAtCoreBlockHeight",
"$transferredAtCoreBlockHeight"
]
}
]
},
"minProperties": 1,
"maxProperties": 100
},
"transient": {
"type": "array",
"items": {
"type": "string"
}
},
"keywords": {
"type": "array",
"description": "List of up to 20 descriptive keywords for the contract, used in the Keyword Search contract",
"items": {
"type": "string",
"minLength": 3,
"maxLength": 50
},
"maxItems": 20,
"uniqueItems": true
},
"additionalProperties": {
"type": "boolean",
"const": false
}
},
"required": [
"$schema",
"type",
"properties",
"additionalProperties"
]
}
Note
The keywords field appears at two levels with different limits. The document meta-schema above defines per-document-type keywords with a maximum of 20. The contract-level keywords field (shown in the data contract object table) has a separate maximum of 50 enforced by Rust validation.
Data Contract id#
The data contract id is a hash of the ownerId and identity_nonce as shown in the rs-dpp implementation.
// From the Rust reference implementation (rs-dpp)
// generate_data_contract.rs
/// Generate data contract id based on owner id and identity nonce
pub fn generate_data_contract_id_v0(
owner_id: impl AsRef<[u8]>,
identity_nonce: IdentityNonce,
) -> Identifier {
let mut b: Vec<u8> = vec![];
let _ = b.write(owner_id.as_ref());
let _ = b.write(identity_nonce.to_be_bytes().as_slice());
Identifier::from(hash_double(b))
}
Data Contract version#
The data contract version is an integer representing the current version of the contract. This
property must be incremented if the contract is updated.
Data Contract documents#
See the data contract documents page for details, including the aggregate query flags that opt document types into count/sum/average queries.
Data Contract config#
The data contract config defines configuration options for data contracts, controlling their lifecycle, mutability, history management, and encryption requirements. Data contracts support three categories of configuration options to provide flexibility in contract design. It is only necessary to include them in a data contract when non-default values are used. The default values for these configuration options are defined in the Rust DPP implementation.
Contract option |
Default |
Description |
|---|---|---|
|
|
Determines if the data contract itself can be deleted. Note: documents have a separate |
|
|
Determines if the contract is read-only. Read-only contracts cannot be updated. |
|
|
Determines if changes to the contract itself are tracked, maintaining a historical record of contract modifications. |
|
|
Enables sized integer types for the contract. |
Document default option |
Default |
Description |
|---|---|---|
|
|
Sets the default behavior for tracking historical changes of documents within the contract |
|
|
Sets the default mutability of documents within the contract, indicating if documents can be edited. |
|
|
Sets the default behavior for whether documents within the contract can be deleted |
Key Management#
Dash Platform provides an advanced level of security and control by enabling the isolation of encryption and decryption keys on a contract-specific or document-specific basis. This granular approach to key management enables developers to configure their applications for whatever level of security they require.
Security option |
Description |
|---|---|
|
Indicates the contract requires a contract-specific identity encryption key. Key options: |
|
Indicates the contract requires a contract-specific identity decryption key. Key options: |
Tip
These security options can be set at the root level of the data contract or the root level of specific documents within the contract depending on requirements.
Example
The following example (from the DashPay contract’s contactRequest document) demonstrates the use of both key-related options at the document level:
"contactRequest": {
"requiresIdentityEncryptionBoundedKey": 2,
"requiresIdentityDecryptionBoundedKey": 2,
}
See the data contract config implementation in rs-dpp for more details.
Data Contract groups#
Groups can be used to distribute contract configuration and update authorization across multiple identities. They are particularly useful for contracts where multiple parties are involved in controlling or managing contract-specific features. Each group defines a set of member identities, the voting power of each member, and the required power threshold to authorize an action.
Group Structure#
Field |
Type |
Description |
|---|---|---|
|
Map<Identifier, MemberPower> |
Map of identity IDs to their voting power |
|
GroupRequiredPower |
Threshold power needed to authorize actions |
Group Constants#
Constant |
Value |
Description |
|---|---|---|
Minimum group size |
Minimum members per group |
|
|
256 |
Maximum members per group |
Maximum member power |
65,535 (u32; cap enforced at u16::MAX) |
Maximum voting power per member. Each member’s power must also not exceed the group’s |
Maximum required power |
65,535 (u32; cap enforced at u16::MAX) |
Maximum threshold power |
Group Action Info#
When submitting a group-authorized action, the transition includes:
Field |
Type |
Description |
|---|---|---|
|
u16 |
Position of the group in the contract |
|
Identifier (32 bytes) |
The action identifier |
|
bool |
Whether the signer is the action proposer |
Use Cases#
Multi-party token control: Require multiple administrators to approve minting or burning
Governance: Implement weighted voting for configuration changes
Security: Distribute control to prevent single points of failure
Example: 2-of-3 Multisig
{
"groups": {
"0": {
"members": {
"<identity_id_1>": 1,
"<identity_id_2>": 1,
"<identity_id_3>": 1
},
"requiredPower": 2
}
}
}
In this example, any two of the three members can authorize an action.
See the groups implementation in rs-dpp for more details.
Data Contract tokens#
Added in version 2.0.0.
Tokens provide token-related functionality within the contract, such as base supply, maximum supply, and manual minting/burning rules.
Token configurations include change control rules, ensuring proper governance for modifying supply limits and token-related settings.
This enables contracts to define and manage tokens while ensuring compliance with governance rules (e.g., who can mint or burn tokens).
Data Contract State Transition Details#
There are two data contract-related state transitions: data contract create and data contract update. Details are provided in this section.
Data Contract Create#
Data contracts are created on the platform by submitting the data contract object in a data contract create state transition consisting of:
Field |
Type |
Size |
Description |
|---|---|---|---|
$version |
unsigned integer |
16 bits |
The state transition format version (currently |
type |
unsigned integer |
8 bits |
State transition type ( |
dataContract |
Varies |
Object containing the data contract details |
|
identityNonce |
unsigned integer |
64 bits |
Identity nonce for this transition to prevent replay attacks |
userFeeIncrease |
unsigned integer |
16 bits |
Extra fee to prioritize processing if the mempool is full. Typically set to zero. |
signaturePublicKeyId |
unsigned integer |
32 bits |
The |
signature |
array of bytes |
65 bytes |
Signature of state transition data |
See the data contract create implementation in rs-dpp for more details.
Data Contract Update#
Existing data contracts can be updated in certain backwards-compatible ways. The following aspects of a data contract can be updated:
Adding a new document
Adding a new optional property to an existing document
Adding non-unique indices for properties added in the update
Data contracts are updated on the platform by submitting the modified data contract
object in a data contract update state transition consisting of:
Field |
Type |
Size |
Description |
|---|---|---|---|
$version |
unsigned integer |
16 bits |
The state transition format version (currently |
type |
unsigned integer |
8 bits |
State transition type ( |
dataContract |
Varies |
Object containing the updated data contract details |
|
identityContractNonce |
unsigned integer |
64 bits |
Identity contract nonce for replay protection |
userFeeIncrease |
unsigned integer |
16 bits |
Extra fee to prioritize processing if the mempool is full. Typically set to zero. |
signaturePublicKeyId |
unsigned integer |
32 bits |
The |
signature |
array of bytes |
65 bytes |
Signature of state transition data |
See the data contract update implementation in rs-dpp for more details.
Data Contract State Transition Signing#
Data contract state transitions must be signed by a private key associated with the contract owner’s identity. See the state transition signing page for full signing details.