diff --git a/packages/js-dash-sdk/docs/platform/documents/broadcast.md b/packages/js-dash-sdk/docs/platform/documents/broadcast.md index 67e59a2d991..8ee2238d0ed 100644 --- a/packages/js-dash-sdk/docs/platform/documents/broadcast.md +++ b/packages/js-dash-sdk/docs/platform/documents/broadcast.md @@ -3,13 +3,17 @@ Parameters: -| parameters | type | required | Description | -|----------------------------|------------|----------|------------------------------------------------------------------------------| -| **documents** | Object | yes | | -| **documents.create** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to create | -| **documents.replace** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to replace | -| **documents.delete** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to delete | -| **identity** | Identity | yes | A valid [registered identity](../identities/register.md) | +| parameters | type | required | Description | +|---------------------------|--------------------|----------|----------------------------------------------------------------------------------------| +| **documents** | Object | yes | | +| **documents.create** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to create | +| **documents.replace** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to replace | +| **documents.delete** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to delete | +| **documents.transfer** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to transfer | +| **documents.updatePrice** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to set price | +| **documents.purchase** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to purchase | +| **identity** | Identity | yes | A valid [registered identity](../identities/register.md) | +| **options** | DocumentTransitionParams | no | An object with two optional fields `price` and `receiver` that is used for NFT actions | **Example**: diff --git a/packages/js-dash-sdk/docs/platform/documents/purchase.md b/packages/js-dash-sdk/docs/platform/documents/purchase.md new file mode 100644 index 00000000000..e88d17db08a --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/documents/purchase.md @@ -0,0 +1,33 @@ +**Usage**: `client.platform.documents.broadcast(documents, identity, options)` +**Description**: This method will broadcast a purchase state transition that buys the given document from other Identity. + +Parameters: + +| parameters | type | required | Description | +|------------------------|---------|------------------ |-------------------------------------------------------------------| +| **documents.purchase** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to buy | +| **identity** | Identity | yes | A valid [registered identity](../identities/register.md) | +| **options** | DocumentTransitionParams | no | An object with field `price` (BigInt) and `receiver` (Identifier) | + +**Example**: +```js +const identityId = '';// Your identity identifier +const receiverId = ''; // Receiver identity identifier +const documentId = '' // Your document id +const price = BigInt(1000000) + +const identity = await client.platform.identities.get(identityId); +const receiverIdentity = await client.platform.identities.get(receiverId); + +const identity = await client.platform.identities.get(identityId); + +const [document] = await dash.platform.documents.get( + 'helloWorldContract.note', + { where: [['$id', '==', documentId]] }, +); + +await dash.platform.documents.broadcast({ updatePrice: [document], }, identity, { price, receiver: receiverIdentity.getId() }); +``` +**Note**: This method will change the ownership of the document to your identity, and seller identity will be credited with the amount specified in the updatePrice deducted from your balance. + +Returns: DocumentsBatchTransition diff --git a/packages/js-dash-sdk/docs/platform/documents/transfer.md b/packages/js-dash-sdk/docs/platform/documents/transfer.md new file mode 100644 index 00000000000..9200618a547 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/documents/transfer.md @@ -0,0 +1,31 @@ +**Usage**: `client.platform.documents.broadcast(documents, identity, options)` +**Description**: This method will broadcast a document transfer + +Parameters: + +| parameters | type | required | Description | +|-------------------|---------|------------------ |-----------------------------------------------------------------------| +| **documents.transfer** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to transfer | +| **identity** | Identity | yes | A valid [registered identity](../identities/register.md) | +| **options** | DocumentTransitionParams | no | An object with `receiver` field | + +**Example**: +```js +const identityId = '';// Your identity identifier +const receiverId = ''; // Receiver identity identifier +const documentId = '' // Your document id + +const identity = await client.platform.identities.get(identityId); +const receiverIdentity = await client.platform.identities.get(receiverId); + +const [document] = await dash.platform.documents.get( + 'helloWorldContract.note', + { where: [['$id', '==', documentId]] }, +); + +await dash.platform.documents.broadcast({ transfer: [document], }, identity, { receiver: receiverIdentity.getId() }); +``` + +**Note**: Transfer transition changes the ownership of the given document to the receiver identity + +Returns: DocumentsBatchTransition diff --git a/packages/js-dash-sdk/docs/platform/documents/updatePrice.md b/packages/js-dash-sdk/docs/platform/documents/updatePrice.md new file mode 100644 index 00000000000..d1c4c284b10 --- /dev/null +++ b/packages/js-dash-sdk/docs/platform/documents/updatePrice.md @@ -0,0 +1,29 @@ +**Usage**: `client.platform.documents.broadcast(documents, identity, options)` +**Description**: This method will broadcast an update price state transition that sets a price for the given document. + +Parameters: + +| parameters | type | required | Description | +|---------------------------|---------|------------------ |---------------------------------------------------------------------------| +| **documents.updatePrice** | ExtendedDocument[] | no | array of valid [created document](../documents/create.md) to update price | +| **identity** | Identity | yes | A valid [registered identity](../identities/register.md) | +| **options** | DocumentTransitionParams | no | An object with field `price` (BigInt) | + +**Example**: +```js +const identityId = '';// Your identity identifier +const documentId = '' // Your document id +const price = BigInt(1000000) + +const identity = await client.platform.identities.get(identityId); + +const [document] = await dash.platform.documents.get( + 'helloWorldContract.note', + { where: [['$id', '==', documentId]] }, +); + +await dash.platform.documents.broadcast({ updatePrice: [document], }, identity, { price }); +``` +**Note**: This method sets the same price on all documents in the batch (only one is possible right now) + +Returns: DocumentsBatchTransition diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/broadcast.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/broadcast.ts index 4bbe6b6479d..82b8fb81f48 100644 --- a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/broadcast.ts +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/documents/broadcast.ts @@ -1,31 +1,44 @@ -import { ExtendedDocument } from '@dashevo/wasm-dpp'; +import { ExtendedDocument, Identifier } from '@dashevo/wasm-dpp'; import { Platform } from '../../Platform'; import broadcastStateTransition from '../../broadcastStateTransition'; import { signStateTransition } from '../../signStateTransition'; +class DocumentTransitionParams { + receiver?: Identifier; + + price?: bigint; +} + /** * Broadcast document onto the platform * - * @param {Platform} this - bound instance class * @param {Object} documents * @param {ExtendedDocument[]} [documents.create] * @param {ExtendedDocument[]} [documents.replace] * @param {ExtendedDocument[]} [documents.delete] - * @param identity - identity + * @param {Identity} identity + * @param options {DocumentTransitionParams} optional params for NFT functions */ export default async function broadcast( this: Platform, documents: { create?: ExtendedDocument[], replace?: ExtendedDocument[], - delete?: ExtendedDocument[] + delete?: ExtendedDocument[], + transfer?: ExtendedDocument[], + updatePrice?: ExtendedDocument[], + purchase?: ExtendedDocument[], }, identity: any, + options?: DocumentTransitionParams, ): Promise { this.logger.debug('[Document#broadcast] Broadcast documents', { create: documents.create?.length || 0, replace: documents.replace?.length || 0, delete: documents.delete?.length || 0, + transfer: documents.transfer?.length || 0, + updatePrice: documents.updatePrice?.length || 0, + purchase: documents.purchase?.length || 0, }); await this.initialize(); @@ -36,20 +49,46 @@ export default async function broadcast( ...(documents.create || []), ...(documents.replace || []), ...(documents.delete || []), + ...(documents.transfer || []), + ...(documents.updatePrice || []), + ...(documents.purchase || []), ][0]?.getDataContractId(); if (!dataContractId) { throw new Error('Data contract ID is not found'); } + if (documents.transfer?.length && !options?.receiver) { + throw new Error('Receiver identity is not found for transfer transition'); + } + + if (documents.updatePrice?.length && !options?.price) { + throw new Error('Price must be provided for UpdatePrice operation'); + } + + if (documents.purchase?.length) { + if (!options?.price && !options?.receiver) { + throw new Error('Price and Receiver must be provided for Purchase operation'); + } + + documents.purchase.forEach((document) => document.setOwnerId(options.receiver)); + } + const identityContractNonce = await this.nonceManager .bumpIdentityContractNonce(identityId, dataContractId); - const documentsBatchTransition = dpp.document.createStateTransition(documents, { + const identityNonceObj = { [identityId.toString()]: { [dataContractId.toString()]: identityContractNonce, }, - }); + }; + + const documentsBatchTransition = dpp.document.createStateTransition( + documents, + identityNonceObj, + options?.receiver, + options?.price, + ); this.logger.silly('[Document#broadcast] Created documents batch transition'); @@ -79,6 +118,7 @@ export default async function broadcast( create: documents.create?.length || 0, replace: documents.replace?.length || 0, delete: documents.delete?.length || 0, + transfer: documents.transfer?.length || 0, }); return documentsBatchTransition; diff --git a/packages/rs-dpp/src/document/document_factory/mod.rs b/packages/rs-dpp/src/document/document_factory/mod.rs index 75db720fd59..b8aa2f56e9b 100644 --- a/packages/rs-dpp/src/document/document_factory/mod.rs +++ b/packages/rs-dpp/src/document/document_factory/mod.rs @@ -12,6 +12,7 @@ use crate::data_contract::document_type::DocumentTypeRef; use crate::document::Document; #[cfg(feature = "extended-document")] use crate::document::ExtendedDocument; +use crate::fee::Credits; #[cfg(feature = "state-transitions")] use crate::state_transition::documents_batch_transition::{ document_transition::action_type::DocumentTransitionActionType, DocumentsBatchTransition, @@ -120,9 +121,13 @@ impl DocumentFactory { ), >, nonce_counter: &mut BTreeMap<(Identifier, Identifier), u64>, //IdentityID/ContractID -> nonce + recipient: Option, + price: Option, ) -> Result { match self { - DocumentFactory::V0(v0) => v0.create_state_transition(documents_iter, nonce_counter), + DocumentFactory::V0(v0) => { + v0.create_state_transition(documents_iter, nonce_counter, recipient, price) + } } } diff --git a/packages/rs-dpp/src/document/document_factory/v0/mod.rs b/packages/rs-dpp/src/document/document_factory/v0/mod.rs index 58fc96de91a..84508aa3cf5 100644 --- a/packages/rs-dpp/src/document/document_factory/v0/mod.rs +++ b/packages/rs-dpp/src/document/document_factory/v0/mod.rs @@ -22,12 +22,14 @@ use crate::document::{ extended_document::v0::ExtendedDocumentV0, serialization_traits::DocumentPlatformConversionMethodsV0, ExtendedDocument, }; +use crate::fee::Credits; use crate::prelude::{BlockHeight, CoreBlockHeight, TimestampMillis}; #[cfg(feature = "state-transitions")] use crate::state_transition::documents_batch_transition::{ document_transition::{ action_type::DocumentTransitionActionType, DocumentCreateTransition, - DocumentDeleteTransition, DocumentReplaceTransition, DocumentTransition, + DocumentDeleteTransition, DocumentPurchaseTransition, DocumentReplaceTransition, + DocumentTransferTransition, DocumentTransition, DocumentUpdatePriceTransition, }, DocumentsBatchTransition, DocumentsBatchTransitionV0, }; @@ -209,7 +211,9 @@ impl DocumentFactoryV0 { Vec<(Document, DocumentTypeRef<'a>, Bytes32)>, ), >, - nonce_counter: &mut BTreeMap<(Identifier, Identifier), u64>, //IdentityID/ContractID -> nonce + nonce_counter: &mut BTreeMap<(Identifier, Identifier), u64>, //IdentityID/ContractID -> nonce, + recipient: Option, + price: Option, ) -> Result { let platform_version = PlatformVersion::get(self.protocol_version)?; let documents: Vec<( @@ -262,9 +266,43 @@ impl DocumentFactoryV0 { nonce_counter, platform_version, ), - _ => Err(ProtocolError::InvalidStateTransitionType( - "action type not accounted for".to_string(), - )), + DocumentTransitionActionType::Transfer => Self::document_transfer_transitions( + documents + .into_iter() + .map(|(document, document_type, _)| (document, document_type)) + .collect(), + nonce_counter, + platform_version, + recipient, + ), + DocumentTransitionActionType::UpdatePrice => { + Self::document_update_price_transitions( + documents + .into_iter() + .map(|(document, document_type, _)| (document, document_type)) + .collect(), + nonce_counter, + platform_version, + price, + ) + } + DocumentTransitionActionType::Purchase => Self::document_purchase_transitions( + documents + .into_iter() + .map(|(document, document_type, _)| (document, document_type)) + .collect(), + nonce_counter, + platform_version, + price, + recipient, + ), + _ => { + let action_type_name: &str = action.into(); + + Err(ProtocolError::InvalidStateTransitionType( + action_type_name.to_string(), + )) + } }) .collect::, ProtocolError>>()? .into_iter() @@ -548,6 +586,140 @@ impl DocumentFactoryV0 { .collect() } + #[cfg(feature = "state-transitions")] + fn document_transfer_transitions( + documents: Vec<(Document, DocumentTypeRef)>, + nonce_counter: &mut BTreeMap<(Identifier, Identifier), u64>, //IdentityID/ContractID -> nonce + platform_version: &PlatformVersion, + recipient_owner_id: Option, + ) -> Result, ProtocolError> { + documents + .into_iter() + .map(|(mut document, document_type)| { + if !document_type.documents_transferable().is_transferable() { + return Err(DocumentError::TryingToTransferNonTransferableDocument { + document: Box::new(document), + } + .into()); + } + let Some(_document_revision) = document.revision() else { + return Err(DocumentError::RevisionAbsentError { + document: Box::new(document), + } + .into()); + }; + + document.increment_revision()?; + document.set_updated_at(Some(Utc::now().timestamp_millis() as TimestampMillis)); + + let nonce = nonce_counter + .entry((document.owner_id(), document_type.data_contract_id())) + .or_default(); + + let transition = DocumentTransferTransition::from_document( + document, + document_type, + *nonce, + recipient_owner_id.unwrap(), + platform_version, + None, + None, + )?; + + *nonce += 1; + + Ok(transition.into()) + }) + .collect() + } + + #[cfg(feature = "state-transitions")] + fn document_update_price_transitions( + documents: Vec<(Document, DocumentTypeRef)>, + nonce_counter: &mut BTreeMap<(Identifier, Identifier), u64>, //IdentityID/ContractID -> nonce + platform_version: &PlatformVersion, + price: Option, + ) -> Result, ProtocolError> { + documents + .into_iter() + .map(|(mut document, document_type)| { + let Some(_document_revision) = document.revision() else { + return Err(DocumentError::RevisionAbsentError { + document: Box::new(document), + } + .into()); + }; + + let nonce = nonce_counter + .entry((document.owner_id(), document_type.data_contract_id())) + .or_default(); + + let now = Utc::now().timestamp_millis() as TimestampMillis; + + document.increment_revision()?; + document.set_updated_at(Some(now)); + document.set_transferred_at(Some(now)); + + let transition = DocumentUpdatePriceTransition::from_document( + document, + document_type, + price.unwrap(), + *nonce, + platform_version, + None, + None, + )?; + + *nonce += 1; + + Ok(transition.into()) + }) + .collect() + } + + #[cfg(feature = "state-transitions")] + fn document_purchase_transitions( + documents: Vec<(Document, DocumentTypeRef)>, + nonce_counter: &mut BTreeMap<(Identifier, Identifier), u64>, //IdentityID/ContractID -> nonce + platform_version: &PlatformVersion, + price: Option, + recipient: Option, + ) -> Result, ProtocolError> { + documents + .into_iter() + .map(|(mut document, document_type)| { + let Some(_document_revision) = document.revision() else { + return Err(DocumentError::RevisionAbsentError { + document: Box::new(document), + } + .into()); + }; + + let nonce = nonce_counter + .entry((recipient.unwrap(), document_type.data_contract_id())) + .or_default(); + + //document.set_owner_id(recipient.unwrap()); + document.increment_revision()?; + document.set_updated_at(Some(Utc::now().timestamp_millis() as TimestampMillis)); + + let transition = DocumentPurchaseTransition::from_document( + document, + document_type, + price.unwrap(), + *nonce, + platform_version, + None, + None, + )?; + + *nonce += 1; + + Ok(transition.into()) + }) + .collect() + } + fn is_ownership_the_same<'a>(ids: impl IntoIterator) -> bool { ids.into_iter().all_equal() } diff --git a/packages/rs-dpp/src/document/errors.rs b/packages/rs-dpp/src/document/errors.rs index d16ca16f1ea..e053bf88605 100644 --- a/packages/rs-dpp/src/document/errors.rs +++ b/packages/rs-dpp/src/document/errors.rs @@ -47,6 +47,9 @@ pub enum DocumentError { #[error("Trying to delete indelible document")] TryingToDeleteIndelibleDocument { document: Box }, + #[error("Trying to transfer non-transferable document")] + TryingToTransferNonTransferableDocument { document: Box }, + #[error("Documents have mixed owner ids")] MismatchOwnerIdsError { documents: Vec }, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/document_transition/action_type.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/document_transition/action_type.rs index 4b61c44fe55..afc2943983f 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/document_transition/action_type.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/document_transition/action_type.rs @@ -39,11 +39,27 @@ impl TryFrom<&str> for DocumentTransitionActionType { "replace" => Ok(DocumentTransitionActionType::Replace), "delete" => Ok(DocumentTransitionActionType::Delete), "transfer" => Ok(DocumentTransitionActionType::Transfer), - "purchase" => Ok(DocumentTransitionActionType::Purchase), "updatePrice" => Ok(DocumentTransitionActionType::UpdatePrice), + "purchase" => Ok(DocumentTransitionActionType::Purchase), action_type => Err(ProtocolError::Generic(format!( "unknown action type {action_type}" ))), } } } + +impl From for &str { + fn from(value: DocumentTransitionActionType) -> Self { + match value { + DocumentTransitionActionType::Create => "Create", + DocumentTransitionActionType::Replace => "Replace", + DocumentTransitionActionType::Delete => "Delete", + DocumentTransitionActionType::Transfer => "Transfer", + DocumentTransitionActionType::Purchase => "Purchase", + DocumentTransitionActionType::UpdatePrice => "UpdatePrice", + DocumentTransitionActionType::IgnoreWhileBumpingRevision => { + "IgnoreWhileBumpingRevision" + } + } + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/document_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/document_transition/mod.rs index 477847b113a..56b41be407f 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/document_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/document_transition/mod.rs @@ -171,11 +171,11 @@ impl DocumentTransitionV0Methods for DocumentTransition { fn entropy(&self) -> Option> { match self { DocumentTransition::Create(t) => Some(Vec::from(t.entropy())), - DocumentTransition::Replace(t) => None, - DocumentTransition::Delete(t) => None, - DocumentTransition::Transfer(t) => None, - DocumentTransition::UpdatePrice(t) => None, - DocumentTransition::Purchase(t) => None, + DocumentTransition::Replace(_) => None, + DocumentTransition::Delete(_) => None, + DocumentTransition::Transfer(_) => None, + DocumentTransition::UpdatePrice(_) => None, + DocumentTransition::Purchase(_) => None, } } diff --git a/packages/rs-dpp/src/tests/fixtures/get_document_transitions_fixture.rs b/packages/rs-dpp/src/tests/fixtures/get_document_transitions_fixture.rs index dfb531b5546..9c78a2b2e6e 100644 --- a/packages/rs-dpp/src/tests/fixtures/get_document_transitions_fixture.rs +++ b/packages/rs-dpp/src/tests/fixtures/get_document_transitions_fixture.rs @@ -24,7 +24,7 @@ pub fn get_document_transitions_fixture<'a>( DocumentFactory::new(protocol_version).expect("expected to get document factory"); document_factory - .create_state_transition(documents, nonce_counter) + .create_state_transition(documents, nonce_counter, None, None) .expect("the transitions should be created") .transitions() .to_owned() diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/transformer/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/transformer/v0/mod.rs index 68ec4de478e..1de6aa765cd 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/transformer/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/transformer/v0/mod.rs @@ -720,7 +720,7 @@ impl DocumentsBatchTransitionInternalTransformerV0 for DocumentsBatchTransition StateError::InvalidDocumentRevisionError(InvalidDocumentRevisionError::new( document_id, Some(previous_revision), - transition_revision, + expected_revision, )), )) } diff --git a/packages/wasm-dpp/src/document/document_facade.rs b/packages/wasm-dpp/src/document/document_facade.rs index 609794eaa99..8df64a97f35 100644 --- a/packages/wasm-dpp/src/document/document_facade.rs +++ b/packages/wasm-dpp/src/document/document_facade.rs @@ -1,10 +1,11 @@ -use std::rc::Rc; -use wasm_bindgen::{prelude::*, JsValue}; - use crate::document::factory::DocumentFactoryWASM; use crate::{DataContractWasm, ExtendedDocumentWasm}; +use dpp::fee::Credits; +use std::rc::Rc; +use wasm_bindgen::{prelude::*, JsValue}; use crate::document::state_transition::document_batch_transition::DocumentsBatchTransitionWasm; +use crate::identifier::IdentifierWrapper; #[derive(Clone)] #[wasm_bindgen(js_name=DocumentFacade)] @@ -95,10 +96,12 @@ impl DocumentFacadeWasm { pub fn create_state_transition( &self, documents: &JsValue, - nonce_counter_value: &js_sys::Object, //IdentityID/ContractID -> nonce + nonce_counter_value: &js_sys::Object, //IdentityID/ContractID -> nonce, + recipient: Option, + price: Option, ) -> Result { self.factory - .create_state_transition(documents, nonce_counter_value) + .create_state_transition(documents, nonce_counter_value, recipient, price) } // /// Creates Documents State Transition diff --git a/packages/wasm-dpp/src/document/errors/mod.rs b/packages/wasm-dpp/src/document/errors/mod.rs index 7b90ac63dbd..3b3b2018d38 100644 --- a/packages/wasm-dpp/src/document/errors/mod.rs +++ b/packages/wasm-dpp/src/document/errors/mod.rs @@ -6,6 +6,7 @@ use crate::document::errors::invalid_action_error::InvalidActionError; use crate::document::errors::revision_absent_error::RevisionAbsentError; use crate::document::errors::trying_to_delete_immutable_document_error::TryingToDeleteImmutableDocumentError; use crate::document::errors::trying_to_replace_immutable_document_error::TryingToReplaceImmutableDocumentError; +use crate::document::errors::trying_to_transfer_nontransferable_document_error::TryingToTransferNonTransferableDocumentError; pub use document_already_exists_error::*; pub use document_not_provided_error::*; use dpp::document::errors::DocumentError; @@ -32,6 +33,7 @@ mod no_documents_supplied_error; mod revision_absent_error; mod trying_to_delete_immutable_document_error; mod trying_to_replace_immutable_document_error; +mod trying_to_transfer_nontransferable_document_error; pub fn from_document_to_js_error(e: DocumentError) -> JsValue { match e { @@ -78,5 +80,8 @@ pub fn from_document_to_js_error(e: DocumentError) -> JsValue { DocumentError::TryingToDeleteIndelibleDocument { document } => { TryingToDeleteImmutableDocumentError::new((*document).into()).into() } + DocumentError::TryingToTransferNonTransferableDocument { document } => { + TryingToTransferNonTransferableDocumentError::new((*document).into()).into() + } } } diff --git a/packages/wasm-dpp/src/document/errors/trying_to_transfer_nontransferable_document_error.rs b/packages/wasm-dpp/src/document/errors/trying_to_transfer_nontransferable_document_error.rs new file mode 100644 index 00000000000..60ff00f03d4 --- /dev/null +++ b/packages/wasm-dpp/src/document/errors/trying_to_transfer_nontransferable_document_error.rs @@ -0,0 +1,19 @@ +use crate::document::DocumentWasm; +use thiserror::Error; + +use super::*; + +#[wasm_bindgen] +#[derive(Error, Debug)] +#[error("Trying to transfer an non transferable document")] +pub struct TryingToTransferNonTransferableDocumentError { + document: DocumentWasm, +} + +#[wasm_bindgen] +impl TryingToTransferNonTransferableDocumentError { + #[wasm_bindgen(constructor)] + pub fn new(document: DocumentWasm) -> Self { + TryingToTransferNonTransferableDocumentError { document } + } +} diff --git a/packages/wasm-dpp/src/document/factory.rs b/packages/wasm-dpp/src/document/factory.rs index 0b927a60803..80aaa3f2ca9 100644 --- a/packages/wasm-dpp/src/document/factory.rs +++ b/packages/wasm-dpp/src/document/factory.rs @@ -13,18 +13,19 @@ use dpp::document::Document; use dpp::prelude::ExtendedDocument; -use dpp::identifier::Identifier; -use dpp::state_transition::documents_batch_transition::document_transition::action_type::DocumentTransitionActionType; -use dpp::version::PlatformVersion; -use std::convert::TryFrom; - use crate::document_batch_transition::DocumentsBatchTransitionWasm; use crate::entropy_generator::ExternalEntropyGenerator; +use crate::identifier::IdentifierWrapper; use crate::{ identifier::identifier_from_js_value, utils::{IntoWasm, ToSerdeJSONExt, WithJsError}, DataContractWasm, ExtendedDocumentWasm, }; +use dpp::fee::Credits; +use dpp::identifier::Identifier; +use dpp::state_transition::documents_batch_transition::document_transition::action_type::DocumentTransitionActionType; +use dpp::version::PlatformVersion; +use std::convert::TryFrom; #[wasm_bindgen(js_name=DocumentTransitions)] #[derive(Debug, Default)] @@ -109,6 +110,8 @@ impl DocumentFactoryWASM { &self, documents: &JsValue, nonce_counter_value: &js_sys::Object, //IdentityID/ContractID -> nonce + recipient: Option, + price: Option, ) -> Result { let mut nonce_counter = BTreeMap::new(); let mut contract_ids_to_check = HashSet::<&Identifier>::new(); @@ -176,7 +179,12 @@ impl DocumentFactoryWASM { let batch_transition = self .0 - .create_state_transition(documents, &mut nonce_counter) + .create_state_transition( + documents, + &mut nonce_counter, + recipient.map(|e| Identifier::from(e)), + price, + ) .with_js_error()?; Ok(batch_transition.into()) @@ -278,10 +286,20 @@ fn extract_documents_by_action( let documents_create = extract_documents_of_action(documents, "create").with_js_error()?; let documents_replace = extract_documents_of_action(documents, "replace").with_js_error()?; let documents_delete = extract_documents_of_action(documents, "delete").with_js_error()?; + let documents_transfer = extract_documents_of_action(documents, "transfer").with_js_error()?; + let documents_update_price = + extract_documents_of_action(documents, "updatePrice").with_js_error()?; + let documents_purchase = extract_documents_of_action(documents, "purchase").with_js_error()?; documents_by_action.insert(DocumentTransitionActionType::Create, documents_create); documents_by_action.insert(DocumentTransitionActionType::Replace, documents_replace); documents_by_action.insert(DocumentTransitionActionType::Delete, documents_delete); + documents_by_action.insert(DocumentTransitionActionType::Transfer, documents_transfer); + documents_by_action.insert( + DocumentTransitionActionType::UpdatePrice, + documents_update_price, + ); + documents_by_action.insert(DocumentTransitionActionType::Purchase, documents_purchase); Ok(documents_by_action) }