From 44f2ab7edab7f0c3357a65a1bdbf54fbc3b1bb53 Mon Sep 17 00:00:00 2001 From: Alexandcoats Date: Mon, 31 Jul 2023 14:34:50 -0400 Subject: [PATCH] Clean up by privatizing more DTOs (#893) * Remove some dtos * Remove Input dtos * access * tidy up * more * amounts * MORE * MOOOOORRREEE * clippy * Fix feature dtos * cleanup * more cleanup * format python * license * Fix nodejs mqtt example * clippy * Update sdk/src/types/block/signature/ed25519.rs Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * udep * no_std * Fix copy derive * kind of annoying clippy * recreate tests with serde * more clippy annoyances * PR suggestions * really? fmt? --------- Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> --- bindings/core/src/method/account.rs | 44 +++-- bindings/core/src/method/client.rs | 36 ++-- bindings/core/src/method/utils.rs | 4 +- bindings/core/src/method_handler/account.rs | 88 ++------- bindings/core/src/method_handler/client.rs | 31 ++-- .../core/src/method_handler/secret_manager.rs | 9 +- bindings/core/src/method_handler/utils.rs | 8 +- bindings/core/src/response.rs | 18 +- .../api/block_builder/input_selection/burn.rs | 52 +----- .../api/block_builder/input_selection/mod.rs | 6 +- sdk/src/client/api/types.rs | 8 +- sdk/src/client/secret/ledger_nano.rs | 4 +- sdk/src/client/secret/mod.rs | 2 +- sdk/src/types/block/address/account.rs | 31 ++-- sdk/src/types/block/address/ed25519.rs | 33 ++-- sdk/src/types/block/address/mod.rs | 106 +---------- sdk/src/types/block/address/nft.rs | 28 ++- sdk/src/types/block/core.rs | 11 +- sdk/src/types/block/input/mod.rs | 34 +--- sdk/src/types/block/input/utxo.rs | 29 ++- sdk/src/types/block/macro.rs | 29 +++ sdk/src/types/block/output/account.rs | 100 ++++------ sdk/src/types/block/output/basic.rs | 64 +++---- sdk/src/types/block/output/delegation.rs | 68 ++++--- sdk/src/types/block/output/feature/issuer.rs | 33 +++- .../types/block/output/feature/metadata.rs | 36 +++- sdk/src/types/block/output/feature/mod.rs | 168 +---------------- sdk/src/types/block/output/feature/sender.rs | 33 +++- sdk/src/types/block/output/feature/staking.rs | 45 ++++- sdk/src/types/block/output/feature/tag.rs | 36 +++- sdk/src/types/block/output/foundry.rs | 79 +++----- sdk/src/types/block/output/mod.rs | 31 +--- sdk/src/types/block/output/nft.rs | 89 +++------ .../types/block/output/token_scheme/mod.rs | 35 +--- .../types/block/output/token_scheme/simple.rs | 16 +- .../block/output/unlock_condition/address.rs | 31 ++-- .../output/unlock_condition/expiration.rs | 32 ++-- .../unlock_condition/governor_address.rs | 31 ++-- .../immutable_account_address.rs | 68 +++---- .../block/output/unlock_condition/mod.rs | 175 +++--------------- .../state_controller_address.rs | 29 +-- .../storage_deposit_return.rs | 55 ++++-- .../block/output/unlock_condition/timelock.rs | 16 +- sdk/src/types/block/payload/mod.rs | 12 +- .../types/block/payload/tagged_data/mod.rs | 14 +- .../payload/transaction/essence/regular.rs | 34 ++-- .../types/block/payload/transaction/mod.rs | 15 +- sdk/src/types/block/public_key/ed25519.rs | 2 +- sdk/src/types/block/signature/ed25519.rs | 20 +- sdk/src/types/block/signature/mod.rs | 46 +---- sdk/src/types/block/unlock/account.rs | 33 +++- sdk/src/types/block/unlock/mod.rs | 147 +-------------- sdk/src/types/block/unlock/nft.rs | 33 +++- sdk/src/types/block/unlock/reference.rs | 33 +++- sdk/src/types/block/unlock/signature.rs | 23 ++- sdk/src/utils/serde.rs | 51 ++++- sdk/src/wallet/account/mod.rs | 6 +- .../operations/transaction/high_level/send.rs | 3 +- .../account/operations/transaction/mod.rs | 2 +- .../account/operations/transaction/options.rs | 43 +---- .../operations/transaction/prepare_output.rs | 3 +- sdk/src/wallet/account/types/balance.rs | 19 +- sdk/src/wallet/account/types/mod.rs | 8 +- sdk/tests/types/address/account.rs | 51 ++--- sdk/tests/types/address/ed25519.rs | 53 +++--- sdk/tests/types/address/nft.rs | 54 +++--- sdk/tests/types/input/utxo.rs | 64 +------ sdk/tests/types/payload.rs | 4 +- sdk/tests/types/transaction_payload.rs | 14 +- .../types/transaction_regular_essence.rs | 4 +- sdk/tests/types/unlock/mod.rs | 2 +- 71 files changed, 1003 insertions(+), 1671 deletions(-) diff --git a/bindings/core/src/method/account.rs b/bindings/core/src/method/account.rs index 1750f15de0..b5e4e90e8f 100644 --- a/bindings/core/src/method/account.rs +++ b/bindings/core/src/method/account.rs @@ -9,7 +9,7 @@ use iota_sdk::{ }; use iota_sdk::{ client::{ - api::{input_selection::BurnDto, PreparedTransactionDataDto, SignedTransactionDataDto}, + api::{input_selection::Burn, PreparedTransactionDataDto, SignedTransactionDataDto}, secret::GenerateAddressOptions, }, types::block::{ @@ -20,7 +20,7 @@ use iota_sdk::{ wallet::{ account::{ ConsolidationParams, CreateAccountParams, CreateNativeTokenParams, FilterOptions, MintNftParams, - OutputParams, OutputsToClaim, SyncOptions, TransactionOptionsDto, + OutputParams, OutputsToClaim, SyncOptions, TransactionOptions, }, SendNativeTokensParams, SendNftParams, SendParams, }, @@ -137,8 +137,8 @@ pub enum AccountMethod { /// /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) PrepareBurn { - burn: BurnDto, - options: Option, + burn: Burn, + options: Option, }, /// Consolidate outputs. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) @@ -147,21 +147,24 @@ pub enum AccountMethod { /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) PrepareCreateAccountOutput { params: Option, - options: Option, + options: Option, }, /// Prepare to create a native token. /// Expected response: /// [`PreparedCreateNativeTokenTransaction`](crate::Response::PreparedCreateNativeTokenTransaction) PrepareCreateNativeToken { params: CreateNativeTokenParams, - options: Option, + options: Option, }, /// Reduces an account's "voting power" by a given amount. /// This will stop voting, but the voting data isn't lost and calling `Vote` without parameters will revote. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) #[cfg(feature = "participation")] #[cfg_attr(docsrs, doc(cfg(feature = "participation")))] - PrepareDecreaseVotingPower { amount: String }, + PrepareDecreaseVotingPower { + #[serde(with = "iota_sdk::utils::serde::string")] + amount: u64, + }, /// Designates a given amount of tokens towards an account's "voting power" by creating a /// special output, which is really a basic one with some metadata. /// This will stop voting in most cases (if there is a remainder output), but the voting data isn't lost and @@ -169,7 +172,10 @@ pub enum AccountMethod { /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) #[cfg(feature = "participation")] #[cfg_attr(docsrs, doc(cfg(feature = "participation")))] - PrepareIncreaseVotingPower { amount: String }, + PrepareIncreaseVotingPower { + #[serde(with = "iota_sdk::utils::serde::string")] + amount: u64, + }, /// Prepare to melt native tokens. This happens with the foundry output which minted them, by increasing it's /// `melted_tokens` field. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) @@ -179,7 +185,7 @@ pub enum AccountMethod { token_id: TokenId, /// To be melted amount melt_amount: U256, - options: Option, + options: Option, }, /// Prepare to mint additional native tokens. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) @@ -189,38 +195,38 @@ pub enum AccountMethod { token_id: TokenId, /// To be minted amount mint_amount: U256, - options: Option, + options: Option, }, /// Prepare to mint NFTs. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) PrepareMintNfts { params: Vec, - options: Option, + options: Option, }, /// Prepare an output. /// Expected response: [`Output`](crate::Response::Output) #[serde(rename_all = "camelCase")] PrepareOutput { params: Box, - transaction_options: Option, + transaction_options: Option, }, /// Prepare to send base coins. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) PrepareSend { params: Vec, - options: Option, + options: Option, }, /// Prepare to send native tokens. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) PrepareSendNativeTokens { params: Vec, - options: Option, + options: Option, }, /// Prepare to Send nft. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) PrepareSendNft { params: Vec, - options: Option, + options: Option, }, /// Stop participating for an event. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) @@ -232,7 +238,7 @@ pub enum AccountMethod { /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) PrepareTransaction { outputs: Vec, - options: Option, + options: Option, }, /// Vote for a participation event. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) @@ -270,19 +276,19 @@ pub enum AccountMethod { #[serde(with = "iota_sdk::utils::serde::string")] amount: u64, address: Bech32Address, - options: Option, + options: Option, }, /// Send base coins to multiple addresses, or with additional parameters. /// Expected response: [`SentTransaction`](crate::Response::SentTransaction) SendWithParams { params: Vec, - options: Option, + options: Option, }, /// Send outputs in a transaction. /// Expected response: [`SentTransaction`](crate::Response::SentTransaction) SendOutputs { outputs: Vec, - options: Option, + options: Option, }, /// Set the alias of the account. /// Expected response: [`Ok`](crate::Response::Ok) diff --git a/bindings/core/src/method/client.rs b/bindings/core/src/method/client.rs index a111297440..6336351f75 100644 --- a/bindings/core/src/method/client.rs +++ b/bindings/core/src/method/client.rs @@ -9,15 +9,13 @@ use iota_sdk::{ types::block::{ address::{Bech32Address, Hrp}, output::{ - dto::{OutputDto, TokenSchemeDto}, - feature::dto::FeatureDto, - unlock_condition::dto::UnlockConditionDto, - AccountId, FoundryId, NativeToken, NftId, OutputId, + dto::OutputDto, feature::Feature, unlock_condition::dto::UnlockConditionDto, AccountId, FoundryId, + NativeToken, NftId, OutputId, TokenScheme, }, payload::{dto::PayloadDto, transaction::TransactionId}, BlockDto, BlockId, }, - utils::serde::string, + utils::serde::{option_string, string}, }; use serde::{Deserialize, Serialize}; @@ -33,7 +31,8 @@ pub enum ClientMethod { #[serde(rename_all = "camelCase")] BuildAccountOutput { // If not provided, minimum storage deposit will be used - amount: Option, + #[serde(default, with = "option_string")] + amount: Option, // TODO: Determine if `default` is wanted here #[serde(default, with = "string")] mana: u64, @@ -43,8 +42,8 @@ pub enum ClientMethod { state_metadata: Option, foundry_counter: Option, unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + features: Option>, + immutable_features: Option>, }, /// Build a BasicOutput. /// Expected response: [`Output`](crate::Response::Output) @@ -52,13 +51,14 @@ pub enum ClientMethod { #[serde(rename_all = "camelCase")] BuildBasicOutput { // If not provided, minimum storage deposit will be used - amount: Option, + #[serde(default, with = "option_string")] + amount: Option, // TODO: Determine if `default` is wanted here #[serde(default, with = "string")] mana: u64, native_tokens: Option>, unlock_conditions: Vec, - features: Option>, + features: Option>, }, /// Build a FoundryOutput. /// Expected response: [`Output`](crate::Response::Output) @@ -66,13 +66,14 @@ pub enum ClientMethod { #[serde(rename_all = "camelCase")] BuildFoundryOutput { // If not provided, minimum storage deposit will be used - amount: Option, + #[serde(default, with = "option_string")] + amount: Option, native_tokens: Option>, serial_number: u32, - token_scheme: TokenSchemeDto, + token_scheme: TokenScheme, unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + features: Option>, + immutable_features: Option>, }, /// Build an NftOutput. /// Expected response: [`Output`](crate::Response::Output) @@ -80,15 +81,16 @@ pub enum ClientMethod { #[serde(rename_all = "camelCase")] BuildNftOutput { // If not provided, minimum storage deposit will be used - amount: Option, + #[serde(default, with = "option_string")] + amount: Option, // TODO: Determine if `default` is wanted here #[serde(default, with = "string")] mana: u64, native_tokens: Option>, nft_id: NftId, unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + features: Option>, + immutable_features: Option>, }, /// Removes all listeners for the provided topics. /// Expected response: [`Ok`](crate::Response::Ok) diff --git a/bindings/core/src/method/utils.rs b/bindings/core/src/method/utils.rs index b336cbf918..08aba5b01a 100644 --- a/bindings/core/src/method/utils.rs +++ b/bindings/core/src/method/utils.rs @@ -9,7 +9,7 @@ use iota_sdk::types::block::{ dto::{TransactionEssenceDto, TransactionPayloadDto}, TransactionId, }, - signature::dto::Ed25519SignatureDto, + signature::Ed25519Signature, BlockDto, }; use serde::{Deserialize, Serialize}; @@ -135,7 +135,7 @@ pub enum UtilsMethod { }, /// Verify an ed25519 signature against a message. VerifyEd25519Signature { - signature: Ed25519SignatureDto, + signature: Ed25519Signature, message: String, }, /// Verify a Secp256k1Ecdsa signature against a message. diff --git a/bindings/core/src/method_handler/account.rs b/bindings/core/src/method_handler/account.rs index 1b7db16092..2ac7447d9b 100644 --- a/bindings/core/src/method_handler/account.rs +++ b/bindings/core/src/method_handler/account.rs @@ -1,9 +1,6 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -#[cfg(feature = "participation")] -use std::str::FromStr; - use iota_sdk::{ client::api::{ PreparedTransactionData, PreparedTransactionDataDto, SignedTransactionData, SignedTransactionDataDto, @@ -12,9 +9,7 @@ use iota_sdk::{ block::output::{dto::OutputDto, Output}, TryFromDto, }, - wallet::account::{ - types::TransactionDto, Account, OutputDataDto, PreparedCreateNativeTokenTransactionDto, TransactionOptions, - }, + wallet::account::{types::TransactionDto, Account, OutputDataDto, PreparedCreateNativeTokenTransactionDto}, }; use crate::{method::AccountMethod, Response, Result}; @@ -110,9 +105,7 @@ pub(crate) async fn call_account_method_internal(account: &Account, method: Acco Response::Transactions(transactions.iter().map(TransactionDto::from).collect()) } AccountMethod::PrepareBurn { burn, options } => { - let data = account - .prepare_burn(burn, options.map(TransactionOptions::try_from_dto).transpose()?) - .await?; + let data = account.prepare_burn(burn, options).await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } AccountMethod::PrepareConsolidateOutputs { params } => { @@ -120,9 +113,7 @@ pub(crate) async fn call_account_method_internal(account: &Account, method: Acco Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } AccountMethod::PrepareCreateAccountOutput { params, options } => { - let data = account - .prepare_create_account_output(params, options.map(TransactionOptions::try_from_dto).transpose()?) - .await?; + let data = account.prepare_create_account_output(params, options).await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } AccountMethod::PrepareMeltNativeToken { @@ -131,21 +122,13 @@ pub(crate) async fn call_account_method_internal(account: &Account, method: Acco options, } => { let data = account - .prepare_melt_native_token( - token_id, - melt_amount, - options.map(TransactionOptions::try_from_dto).transpose()?, - ) + .prepare_melt_native_token(token_id, melt_amount, options) .await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } #[cfg(feature = "participation")] AccountMethod::PrepareDecreaseVotingPower { amount } => { - let data = account - .prepare_decrease_voting_power( - u64::from_str(&amount).map_err(|_| iota_sdk::client::Error::InvalidAmount(amount.clone()))?, - ) - .await?; + let data = account.prepare_decrease_voting_power(amount).await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } AccountMethod::PrepareMintNativeToken { @@ -154,69 +137,40 @@ pub(crate) async fn call_account_method_internal(account: &Account, method: Acco options, } => { let data = account - .prepare_mint_native_token( - token_id, - mint_amount, - options.map(TransactionOptions::try_from_dto).transpose()?, - ) + .prepare_mint_native_token(token_id, mint_amount, options) .await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } #[cfg(feature = "participation")] AccountMethod::PrepareIncreaseVotingPower { amount } => { - let data = account - .prepare_increase_voting_power( - u64::from_str(&amount).map_err(|_| iota_sdk::client::Error::InvalidAmount(amount.clone()))?, - ) - .await?; + let data = account.prepare_increase_voting_power(amount).await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } AccountMethod::PrepareMintNfts { params, options } => { - let data = account - .prepare_mint_nfts(params, options.map(TransactionOptions::try_from_dto).transpose()?) - .await?; + let data = account.prepare_mint_nfts(params, options).await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } AccountMethod::PrepareCreateNativeToken { params, options } => { - let data = account - .prepare_create_native_token(params, options.map(TransactionOptions::try_from_dto).transpose()?) - .await?; + let data = account.prepare_create_native_token(params, options).await?; Response::PreparedCreateNativeTokenTransaction(PreparedCreateNativeTokenTransactionDto::from(&data)) } AccountMethod::PrepareOutput { params, transaction_options, } => { - let output = account - .prepare_output( - *params, - transaction_options.map(TransactionOptions::try_from_dto).transpose()?, - ) - .await?; + let output = account.prepare_output(*params, transaction_options).await?; Response::Output(OutputDto::from(&output)) } AccountMethod::PrepareSend { params, options } => { - let data = account - .prepare_send(params, options.map(TransactionOptions::try_from_dto).transpose()?) - .await?; + let data = account.prepare_send(params, options).await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } AccountMethod::PrepareSendNativeTokens { params, options } => { - let data = account - .prepare_send_native_tokens( - params.clone(), - options.map(TransactionOptions::try_from_dto).transpose()?, - ) - .await?; + let data = account.prepare_send_native_tokens(params.clone(), options).await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } AccountMethod::PrepareSendNft { params, options } => { - let data = account - .prepare_send_nft( - params.clone(), - options.map(TransactionOptions::try_from_dto).transpose()?, - ) - .await?; + let data = account.prepare_send_nft(params.clone(), options).await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } #[cfg(feature = "participation")] @@ -232,7 +186,7 @@ pub(crate) async fn call_account_method_internal(account: &Account, method: Acco .into_iter() .map(|o| Ok(Output::try_from_dto_with_params(o, token_supply)?)) .collect::>>()?, - options.map(TransactionOptions::try_from_dto).transpose()?, + options, ) .await?; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) @@ -262,19 +216,11 @@ pub(crate) async fn call_account_method_internal(account: &Account, method: Acco address, options, } => { - let transaction = account - .send( - amount, - address, - options.map(TransactionOptions::try_from_dto).transpose()?, - ) - .await?; + let transaction = account.send(amount, address, options).await?; Response::SentTransaction(TransactionDto::from(&transaction)) } AccountMethod::SendWithParams { params, options } => { - let transaction = account - .send_with_params(params, options.map(TransactionOptions::try_from_dto).transpose()?) - .await?; + let transaction = account.send_with_params(params, options).await?; Response::SentTransaction(TransactionDto::from(&transaction)) } AccountMethod::SendOutputs { outputs, options } => { @@ -285,7 +231,7 @@ pub(crate) async fn call_account_method_internal(account: &Account, method: Acco .into_iter() .map(|o| Ok(Output::try_from_dto_with_params(o, token_supply)?)) .collect::>>()?, - options.map(TransactionOptions::try_from_dto).transpose()?, + options, ) .await?; Response::SentTransaction(TransactionDto::from(&transaction)) diff --git a/bindings/core/src/method_handler/client.rs b/bindings/core/src/method_handler/client.rs index 0b56535048..05f673ea84 100644 --- a/bindings/core/src/method_handler/client.rs +++ b/bindings/core/src/method_handler/client.rs @@ -8,10 +8,8 @@ use iota_sdk::{ types::{ api::core::response::OutputWithMetadataResponse, block::{ - input::dto::UtxoInputDto, output::{ - dto::{OutputBuilderAmountDto, OutputDto}, - AccountOutput, BasicOutput, FoundryOutput, NftOutput, Output, Rent, + dto::OutputDto, AccountOutput, BasicOutput, FoundryOutput, NftOutput, Output, OutputBuilderAmount, Rent, }, payload::Payload, Block, BlockDto, @@ -71,9 +69,9 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM } => { let output = Output::from(AccountOutput::try_from_dtos( if let Some(amount) = amount { - OutputBuilderAmountDto::Amount(amount) + OutputBuilderAmount::Amount(amount) } else { - OutputBuilderAmountDto::MinimumStorageDeposit(client.get_rent_structure().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_structure().await?) }, mana, native_tokens, @@ -98,9 +96,9 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM } => { let output = Output::from(BasicOutput::try_from_dtos( if let Some(amount) = amount { - OutputBuilderAmountDto::Amount(amount) + OutputBuilderAmount::Amount(amount) } else { - OutputBuilderAmountDto::MinimumStorageDeposit(client.get_rent_structure().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_structure().await?) }, mana, native_tokens, @@ -122,9 +120,9 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM } => { let output = Output::from(FoundryOutput::try_from_dtos( if let Some(amount) = amount { - OutputBuilderAmountDto::Amount(amount) + OutputBuilderAmount::Amount(amount) } else { - OutputBuilderAmountDto::MinimumStorageDeposit(client.get_rent_structure().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_structure().await?) }, native_tokens, serial_number, @@ -148,9 +146,9 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM } => { let output = Output::from(NftOutput::try_from_dtos( if let Some(amount) = amount { - OutputBuilderAmountDto::Amount(amount) + OutputBuilderAmount::Amount(amount) } else { - OutputBuilderAmountDto::MinimumStorageDeposit(client.get_rent_structure().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_structure().await?) }, mana, native_tokens, @@ -288,14 +286,9 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM .collect(); Response::RetryUntilIncludedSuccessful(res) } - ClientMethod::FindInputs { addresses, amount } => Response::Inputs( - client - .find_inputs(addresses, amount) - .await? - .iter() - .map(UtxoInputDto::from) - .collect(), - ), + ClientMethod::FindInputs { addresses, amount } => { + Response::Inputs(client.find_inputs(addresses, amount).await?) + } ClientMethod::Reattach { block_id } => { let (block_id, block) = client.reattach(&block_id).await?; Response::Reattached((block_id, BlockDto::from(&block))) diff --git a/bindings/core/src/method_handler/secret_manager.rs b/bindings/core/src/method_handler/secret_manager.rs index f109631861..ff26572766 100644 --- a/bindings/core/src/method_handler/secret_manager.rs +++ b/bindings/core/src/method_handler/secret_manager.rs @@ -6,10 +6,7 @@ use iota_sdk::{ api::PreparedTransactionData, secret::{SecretManage, SecretManager}, }, - types::{ - block::{signature::dto::Ed25519SignatureDto, unlock::Unlock}, - TryFromDto, - }, + types::{block::unlock::Unlock, TryFromDto}, }; use tokio::sync::RwLock; @@ -55,12 +52,12 @@ pub(crate) async fn call_secret_manager_method_internal( .signature_unlock(&transaction_essence_hash, chain) .await?; - Response::SignatureUnlock((&unlock).into()) + Response::SignatureUnlock(unlock) } SecretManagerMethod::SignEd25519 { message, chain } => { let msg: Vec = prefix_hex::decode(message)?; let signature = secret_manager.sign_ed25519(&msg, chain).await?; - Response::Ed25519Signature(Ed25519SignatureDto::from(&signature)) + Response::Ed25519Signature(signature) } SecretManagerMethod::SignSecp256k1Ecdsa { message, chain } => { let msg: Vec = prefix_hex::decode(message)?; diff --git a/bindings/core/src/method_handler/utils.rs b/bindings/core/src/method_handler/utils.rs index beb11e061c..c0186f1ac7 100644 --- a/bindings/core/src/method_handler/utils.rs +++ b/bindings/core/src/method_handler/utils.rs @@ -6,11 +6,10 @@ use iota_sdk::{ client::{hex_public_key_to_bech32_address, hex_to_bech32, verify_mnemonic, Client}, types::{ block::{ - address::{dto::AddressDto, AccountAddress, Address, ToBech32Ext}, + address::{AccountAddress, Address, ToBech32Ext}, input::UtxoInput, output::{AccountId, FoundryId, InputsCommitment, NftId, Output, OutputId, Rent, TokenId}, payload::{transaction::TransactionEssence, TransactionPayload}, - signature::Ed25519Signature, Block, }, TryFromDto, @@ -31,7 +30,7 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result { Response::Bech32Address(hex_public_key_to_bech32_address(&hex, bech32_hrp)?) } - UtilsMethod::ParseBech32Address { address } => Response::ParsedBech32Address(AddressDto::from(address.inner())), + UtilsMethod::ParseBech32Address { address } => Response::ParsedBech32Address(Address::from(address.inner())), UtilsMethod::IsAddressValid { address } => Response::Bool(Address::is_valid_bech32(&address)), UtilsMethod::GenerateMnemonic => Response::GeneratedMnemonic(Client::generate_mnemonic()?.to_string()), UtilsMethod::MnemonicToHexSeed { mnemonic } => { @@ -86,7 +85,6 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result { - let signature = Ed25519Signature::try_from(signature)?; let message: Vec = prefix_hex::decode(message)?; Response::Bool(signature.verify(&message)) } @@ -104,7 +102,7 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result = prefix_hex::decode(message)?; Response::Bool(public_key.verify_keccak256(&signature, &message)) } - UtilsMethod::OutputIdToUtxoInput { output_id } => Response::Input((&UtxoInput::from(output_id)).into()), + UtilsMethod::OutputIdToUtxoInput { output_id } => Response::Input(UtxoInput::from(output_id)), }; Ok(response) } diff --git a/bindings/core/src/response.rs b/bindings/core/src/response.rs index 4a568ccbfb..8979cac123 100644 --- a/bindings/core/src/response.rs +++ b/bindings/core/src/response.rs @@ -21,13 +21,13 @@ use iota_sdk::{ plugins::indexer::OutputIdsResponse, }, block::{ - address::{dto::AddressDto, Bech32Address, Hrp}, - input::dto::UtxoInputDto, + address::{Address, Bech32Address, Hrp}, + input::UtxoInput, output::{dto::OutputDto, AccountId, FoundryId, NftId, OutputId, OutputMetadata, TokenId}, payload::{dto::TransactionPayloadDto, transaction::TransactionId}, protocol::ProtocolParameters, - signature::dto::Ed25519SignatureDto, - unlock::dto::UnlockDto, + signature::Ed25519Signature, + unlock::Unlock, BlockDto, BlockId, }, }, @@ -78,10 +78,10 @@ pub enum Response { SignedTransaction(TransactionPayloadDto), /// Response for: /// - [`SignatureUnlock`](crate::method::SecretManagerMethod::SignatureUnlock) - SignatureUnlock(UnlockDto), + SignatureUnlock(Unlock), /// Response for: /// - [`SignEd25519`](crate::method::SecretManagerMethod::SignEd25519) - Ed25519Signature(Ed25519SignatureDto), + Ed25519Signature(Ed25519Signature), /// Response for: /// - [`SignSecp256k1Ecdsa`](crate::method::SecretManagerMethod::SignSecp256k1Ecdsa) #[serde(rename_all = "camelCase")] @@ -147,10 +147,10 @@ pub enum Response { RetryUntilIncludedSuccessful(Vec<(BlockId, BlockDto)>), /// Response for: /// - [`FindInputs`](crate::method::ClientMethod::FindInputs) - Inputs(Vec), + Inputs(Vec), /// Response for: /// [`OutputIdToUtxoInput`](crate::method::UtilsMethod::OutputIdToUtxoInput) - Input(UtxoInputDto), + Input(UtxoInput), /// Response for: /// - [`Reattach`](crate::method::ClientMethod::Reattach) /// - [`ReattachUnchecked`](crate::method::ClientMethod::ReattachUnchecked) @@ -164,7 +164,7 @@ pub enum Response { Bech32ToHex(String), /// Response for: /// - [`ParseBech32Address`](crate::method::UtilsMethod::ParseBech32Address) - ParsedBech32Address(AddressDto), + ParsedBech32Address(Address), /// Response for: /// - [`MnemonicToHexSeed`](crate::method::UtilsMethod::MnemonicToHexSeed) MnemonicHexSeed(#[derivative(Debug(format_with = "OmittedDebug::omitted_fmt"))] String), diff --git a/sdk/src/client/api/block_builder/input_selection/burn.rs b/sdk/src/client/api/block_builder/input_selection/burn.rs index 0915f546ff..a9670a269b 100644 --- a/sdk/src/client/api/block_builder/input_selection/burn.rs +++ b/sdk/src/client/api/block_builder/input_selection/burn.rs @@ -11,15 +11,20 @@ use crate::types::block::output::{AccountId, FoundryId, NativeToken, NftId, Toke /// A type to specify what needs to be burned during input selection. /// Nothing will be burned that has not been explicitly set with this struct. -#[derive(Debug, Default, Clone, Eq, PartialEq)] +#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct Burn { /// Accounts to burn. + #[serde(default, skip_serializing_if = "HashSet::is_empty")] pub(crate) accounts: HashSet, /// NFTs to burn. + #[serde(default, skip_serializing_if = "HashSet::is_empty")] pub(crate) nfts: HashSet, /// Foundries to burn. + #[serde(default, skip_serializing_if = "HashSet::is_empty")] pub(crate) foundries: HashSet, /// Amounts of native tokens to burn. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] pub(crate) native_tokens: BTreeMap, } @@ -124,48 +129,3 @@ impl From for Burn { Self::new().add_native_token(*native_token.token_id(), native_token.amount()) } } - -/// A DTO for [`Burn`]. -#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct BurnDto { - /// Accounts to burn. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) accounts: Option>, - /// NFTs to burn. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) nfts: Option>, - /// Foundries to burn. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) foundries: Option>, - /// Amounts of native tokens to burn. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) native_tokens: Option>, -} - -impl From<&Burn> for BurnDto { - fn from(value: &Burn) -> Self { - Self { - accounts: (!value.accounts.is_empty()).then_some(value.accounts.clone()), - nfts: (!value.nfts.is_empty()).then_some(value.nfts.clone()), - foundries: (!value.foundries.is_empty()).then_some(value.foundries.clone()), - native_tokens: (!value.native_tokens.is_empty()).then_some(BTreeMap::from_iter( - value - .native_tokens - .iter() - .map(|(token_id, amount)| (*token_id, *amount)), - )), - } - } -} - -impl From for Burn { - fn from(value: BurnDto) -> Self { - Self { - accounts: value.accounts.unwrap_or_default(), - nfts: value.nfts.unwrap_or_default(), - foundries: value.foundries.unwrap_or_default(), - native_tokens: value.native_tokens.unwrap_or_default(), - } - } -} diff --git a/sdk/src/client/api/block_builder/input_selection/mod.rs b/sdk/src/client/api/block_builder/input_selection/mod.rs index 6918427478..947893b4d7 100644 --- a/sdk/src/client/api/block_builder/input_selection/mod.rs +++ b/sdk/src/client/api/block_builder/input_selection/mod.rs @@ -15,11 +15,7 @@ use std::collections::{HashMap, HashSet}; use packable::PackableExt; pub(crate) use requirement::is_account_transition; -pub use self::{ - burn::{Burn, BurnDto}, - error::Error, - requirement::Requirement, -}; +pub use self::{burn::Burn, error::Error, requirement::Requirement}; use crate::{ client::{api::types::RemainderData, secret::types::InputSigningData}, types::block::{ diff --git a/sdk/src/client/api/types.rs b/sdk/src/client/api/types.rs index 116943eb06..c1dba38224 100644 --- a/sdk/src/client/api/types.rs +++ b/sdk/src/client/api/types.rs @@ -8,7 +8,7 @@ use crate::{ client::secret::types::{InputSigningData, InputSigningDataDto}, types::{ block::{ - address::{dto::AddressDto, Address}, + address::Address, output::{dto::OutputDto, Output}, payload::{ transaction::{ @@ -148,7 +148,7 @@ pub struct RemainderDataDto { #[serde(with = "option_bip44", default)] pub chain: Option, /// The remainder address - pub address: AddressDto, + pub address: Address, } impl TryFromDto for RemainderData { @@ -159,7 +159,7 @@ impl TryFromDto for RemainderData { Ok(Self { output: Output::try_from_dto_with_params_inner(dto.output, params)?, chain: dto.chain, - address: Address::try_from(dto.address)?, + address: dto.address, }) } } @@ -168,7 +168,7 @@ impl From<&RemainderData> for RemainderDataDto { Self { output: OutputDto::from(&remainder.output), chain: remainder.chain, - address: AddressDto::from(&remainder.address), + address: remainder.address, } } } diff --git a/sdk/src/client/secret/ledger_nano.rs b/sdk/src/client/secret/ledger_nano.rs index 5eaa1e6ad8..6c6eb4c29b 100644 --- a/sdk/src/client/secret/ledger_nano.rs +++ b/sdk/src/client/secret/ledger_nano.rs @@ -223,7 +223,9 @@ impl SecretManage for LedgerSecretManager { // Unpack and return signature. return match Unlock::unpack::<_, true>(&mut unpacker, &())? { - Unlock::Signature(SignatureUnlock(Signature::Ed25519(signature))) => Ok(*signature), + Unlock::Signature(s) => match *s { + SignatureUnlock(Signature::Ed25519(signature)) => Ok(signature), + }, _ => Err(Error::UnsupportedOperation.into()), }; } diff --git a/sdk/src/client/secret/mod.rs b/sdk/src/client/secret/mod.rs index ad73f68ce2..1690f4e2fc 100644 --- a/sdk/src/client/secret/mod.rs +++ b/sdk/src/client/secret/mod.rs @@ -91,7 +91,7 @@ pub trait SecretManage: Send + Sync { /// Signs `essence_hash` using the given `chain`, returning an [`Unlock`]. async fn signature_unlock(&self, essence_hash: &[u8; 32], chain: Bip44) -> Result { - Ok(Unlock::Signature(SignatureUnlock::new(Signature::from( + Ok(Unlock::from(SignatureUnlock::new(Signature::from( self.sign_ed25519(essence_hash, chain).await?, )))) } diff --git a/sdk/src/types/block/address/account.rs b/sdk/src/types/block/address/account.rs index cff5185e30..9961c53b1f 100644 --- a/sdk/src/types/block/address/account.rs +++ b/sdk/src/types/block/address/account.rs @@ -37,9 +37,6 @@ impl AccountAddress { } } -#[cfg(feature = "serde")] -string_serde_impl!(AccountAddress); - impl FromStr for AccountAddress { type Err = Error; @@ -60,40 +57,36 @@ impl core::fmt::Debug for AccountAddress { } } -pub(crate) mod dto { - use alloc::string::{String, ToString}; +mod dto { + use alloc::format; use serde::{Deserialize, Serialize}; use super::*; - use crate::types::block::Error; /// Describes an account address. - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] - pub struct AccountAddressDto { + struct AccountAddressDto { #[serde(rename = "type")] - pub kind: u8, - pub account_id: String, + kind: u8, + account_id: AccountId, } impl From<&AccountAddress> for AccountAddressDto { fn from(value: &AccountAddress) -> Self { Self { kind: AccountAddress::KIND, - account_id: value.to_string(), + account_id: value.0, } } } - impl TryFrom for AccountAddress { - type Error = Error; - - fn try_from(value: AccountAddressDto) -> Result { - value - .account_id - .parse::() - .map_err(|_| Error::InvalidField("accountId")) + impl From for AccountAddress { + fn from(value: AccountAddressDto) -> Self { + Self(value.account_id) } } + + impl_serde_typed_dto!(AccountAddress, AccountAddressDto, "account address"); } diff --git a/sdk/src/types/block/address/ed25519.rs b/sdk/src/types/block/address/ed25519.rs index 3a8cb2fdad..f4877b10ad 100644 --- a/sdk/src/types/block/address/ed25519.rs +++ b/sdk/src/types/block/address/ed25519.rs @@ -26,9 +26,6 @@ impl Ed25519Address { } } -#[cfg(feature = "serde")] -string_serde_impl!(Ed25519Address); - impl FromStr for Ed25519Address { type Err = Error; @@ -49,40 +46,38 @@ impl core::fmt::Debug for Ed25519Address { } } -pub(crate) mod dto { - use alloc::string::{String, ToString}; +mod dto { + use alloc::format; use serde::{Deserialize, Serialize}; use super::*; - use crate::types::block::Error; + use crate::utils::serde::prefix_hex_bytes; /// Describes an Ed25519 address. - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] - pub struct Ed25519AddressDto { + struct Ed25519AddressDto { #[serde(rename = "type")] - pub kind: u8, - pub pub_key_hash: String, + kind: u8, + #[serde(with = "prefix_hex_bytes")] + pub_key_hash: [u8; Ed25519Address::LENGTH], } impl From<&Ed25519Address> for Ed25519AddressDto { fn from(value: &Ed25519Address) -> Self { Self { kind: Ed25519Address::KIND, - pub_key_hash: value.to_string(), + pub_key_hash: value.0, } } } - impl TryFrom for Ed25519Address { - type Error = Error; - - fn try_from(value: Ed25519AddressDto) -> Result { - value - .pub_key_hash - .parse::() - .map_err(|_| Error::InvalidField("pubKeyHash")) + impl From for Ed25519Address { + fn from(value: Ed25519AddressDto) -> Self { + Self(value.pub_key_hash) } } + + impl_serde_typed_dto!(Ed25519Address, Ed25519AddressDto, "ed25519 address"); } diff --git a/sdk/src/types/block/address/mod.rs b/sdk/src/types/block/address/mod.rs index 89c987f208..381ebd7072 100644 --- a/sdk/src/types/block/address/mod.rs +++ b/sdk/src/types/block/address/mod.rs @@ -26,6 +26,7 @@ use crate::types::block::{ #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] #[packable(tag_type = u8, with_error = Error::InvalidAddressKind)] #[packable(unpack_error = Error)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] pub enum Address { /// An Ed25519 address. #[packable(tag = Ed25519Address::KIND)] @@ -209,108 +210,3 @@ impl From<&Self> for Address { *value } } - -pub mod dto { - use alloc::format; - - use serde::{Deserialize, Serialize, Serializer}; - use serde_json::Value; - - use super::*; - pub use super::{account::dto::AccountAddressDto, ed25519::dto::Ed25519AddressDto, nft::dto::NftAddressDto}; - use crate::types::block::Error; - - /// Describes all the different address types. - #[derive(Clone, Debug, Eq, PartialEq, From)] - pub enum AddressDto { - /// An Ed25519 address. - Ed25519(Ed25519AddressDto), - /// An account address. - Account(AccountAddressDto), - /// A NFT address. - Nft(NftAddressDto), - } - - impl From<&Address> for AddressDto { - fn from(value: &Address) -> Self { - match value { - Address::Ed25519(a) => Self::Ed25519(a.into()), - Address::Account(a) => Self::Account(a.into()), - Address::Nft(a) => Self::Nft(a.into()), - } - } - } - - impl TryFrom for Address { - type Error = Error; - - fn try_from(value: AddressDto) -> Result { - match value { - AddressDto::Ed25519(a) => Ok(Self::Ed25519(a.try_into()?)), - AddressDto::Account(a) => Ok(Self::Account(a.try_into()?)), - AddressDto::Nft(a) => Ok(Self::Nft(a.try_into()?)), - } - } - } - - impl<'de> Deserialize<'de> for AddressDto { - fn deserialize>(d: D) -> Result { - let value = Value::deserialize(d)?; - Ok( - match value - .get("type") - .and_then(Value::as_u64) - .ok_or_else(|| serde::de::Error::custom("invalid address type"))? as u8 - { - Ed25519Address::KIND => { - Self::Ed25519(Ed25519AddressDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!("cannot deserialize ed25519 address: {e}")) - })?) - } - AccountAddress::KIND => { - Self::Account(AccountAddressDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!("cannot deserialize account address: {e}")) - })?) - } - NftAddress::KIND => Self::Nft( - NftAddressDto::deserialize(value) - .map_err(|e| serde::de::Error::custom(format!("cannot deserialize NFT address: {e}")))?, - ), - _ => return Err(serde::de::Error::custom("invalid address type")), - }, - ) - } - } - - impl Serialize for AddressDto { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - #[derive(Serialize)] - #[serde(untagged)] - enum AddressDto_<'a> { - T1(&'a Ed25519AddressDto), - T2(&'a AccountAddressDto), - T3(&'a NftAddressDto), - } - #[derive(Serialize)] - struct TypedAddress<'a> { - #[serde(flatten)] - address: AddressDto_<'a>, - } - let address = match self { - Self::Ed25519(o) => TypedAddress { - address: AddressDto_::T1(o), - }, - Self::Account(o) => TypedAddress { - address: AddressDto_::T2(o), - }, - Self::Nft(o) => TypedAddress { - address: AddressDto_::T3(o), - }, - }; - address.serialize(serializer) - } - } -} diff --git a/sdk/src/types/block/address/nft.rs b/sdk/src/types/block/address/nft.rs index 0593042147..ddbc06b030 100644 --- a/sdk/src/types/block/address/nft.rs +++ b/sdk/src/types/block/address/nft.rs @@ -37,9 +37,6 @@ impl NftAddress { } } -#[cfg(feature = "serde")] -string_serde_impl!(NftAddress); - impl FromStr for NftAddress { type Err = Error; @@ -60,37 +57,36 @@ impl core::fmt::Debug for NftAddress { } } -pub(crate) mod dto { - use alloc::string::{String, ToString}; +mod dto { + use alloc::format; use serde::{Deserialize, Serialize}; use super::*; - use crate::types::block::Error; /// Describes an NFT address. - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] - pub struct NftAddressDto { + struct NftAddressDto { #[serde(rename = "type")] - pub kind: u8, - pub nft_id: String, + kind: u8, + nft_id: NftId, } impl From<&NftAddress> for NftAddressDto { fn from(value: &NftAddress) -> Self { Self { kind: NftAddress::KIND, - nft_id: value.to_string(), + nft_id: value.0, } } } - impl TryFrom for NftAddress { - type Error = Error; - - fn try_from(value: NftAddressDto) -> Result { - value.nft_id.parse::().map_err(|_| Error::InvalidField("nftId")) + impl From for NftAddress { + fn from(value: NftAddressDto) -> Self { + Self(value.nft_id) } } + + impl_serde_typed_dto!(NftAddress, NftAddressDto, "nft address"); } diff --git a/sdk/src/types/block/core.rs b/sdk/src/types/block/core.rs index c9938546a0..0f2d1314c1 100644 --- a/sdk/src/types/block/core.rs +++ b/sdk/src/types/block/core.rs @@ -284,9 +284,12 @@ pub(crate) mod dto { use serde::{Deserialize, Serialize}; use super::*; - use crate::types::{ - block::{payload::dto::PayloadDto, Error}, - TryFromDto, ValidationParams, + use crate::{ + types::{ + block::{payload::dto::PayloadDto, Error}, + TryFromDto, ValidationParams, + }, + utils::serde::string, }; /// The block object that nodes gossip around in the network. @@ -302,7 +305,7 @@ pub(crate) mod dto { /// #[serde(default, skip_serializing_if = "Option::is_none")] pub payload: Option, - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] pub burned_mana: u64, } diff --git a/sdk/src/types/block/input/mod.rs b/sdk/src/types/block/input/mod.rs index 82799c6690..d0757d8554 100644 --- a/sdk/src/types/block/input/mod.rs +++ b/sdk/src/types/block/input/mod.rs @@ -23,6 +23,7 @@ pub const INPUT_INDEX_RANGE: RangeInclusive = 0..=INPUT_INDEX_MAX; // [0..1 #[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, From, packable::Packable)] #[packable(unpack_error = Error)] #[packable(tag_type = u8, with_error = Error::InvalidInputKind)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] pub enum Input { /// A UTXO input. #[packable(tag = UtxoInput::KIND)] @@ -57,36 +58,3 @@ impl Input { input } } - -pub mod dto { - use serde::{Deserialize, Serialize}; - - pub use super::utxo::dto::UtxoInputDto; - use super::*; - use crate::types::block::Error; - - /// Describes all the different input types. - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, From)] - #[serde(untagged)] - pub enum InputDto { - Utxo(UtxoInputDto), - } - - impl From<&Input> for InputDto { - fn from(value: &Input) -> Self { - match value { - Input::Utxo(u) => Self::Utxo(u.into()), - } - } - } - - impl TryFrom for Input { - type Error = Error; - - fn try_from(value: InputDto) -> Result { - match value { - InputDto::Utxo(u) => Ok(Self::Utxo(u.try_into()?)), - } - } - } -} diff --git a/sdk/src/types/block/input/utxo.rs b/sdk/src/types/block/input/utxo.rs index 99b9bf28f8..2c5616bf3b 100644 --- a/sdk/src/types/block/input/utxo.rs +++ b/sdk/src/types/block/input/utxo.rs @@ -26,9 +26,6 @@ impl UtxoInput { } } -#[cfg(feature = "serde")] -string_serde_impl!(UtxoInput); - impl FromStr for UtxoInput { type Err = Error; @@ -49,8 +46,8 @@ impl core::fmt::Debug for UtxoInput { } } -pub(crate) mod dto { - use alloc::string::{String, ToString}; +mod dto { + use alloc::format; use serde::{Deserialize, Serialize}; @@ -58,20 +55,20 @@ pub(crate) mod dto { use crate::types::block::Error; /// Describes an input which references an unspent transaction output to consume. - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] - pub struct UtxoInputDto { + struct UtxoInputDto { #[serde(rename = "type")] - pub kind: u8, - pub transaction_id: String, - pub transaction_output_index: u16, + kind: u8, + transaction_id: TransactionId, + transaction_output_index: u16, } impl From<&UtxoInput> for UtxoInputDto { fn from(value: &UtxoInput) -> Self { Self { kind: UtxoInput::KIND, - transaction_id: value.output_id().transaction_id().to_string(), + transaction_id: *value.output_id().transaction_id(), transaction_output_index: value.output_id().index(), } } @@ -81,13 +78,9 @@ pub(crate) mod dto { type Error = Error; fn try_from(value: UtxoInputDto) -> Result { - Self::new( - value - .transaction_id - .parse::() - .map_err(|_| Error::InvalidField("transactionId"))?, - value.transaction_output_index, - ) + Self::new(value.transaction_id, value.transaction_output_index) } } + + impl_serde_typed_dto!(UtxoInput, UtxoInputDto, "UTXO input"); } diff --git a/sdk/src/types/block/macro.rs b/sdk/src/types/block/macro.rs index 0af0b48023..850537287d 100644 --- a/sdk/src/types/block/macro.rs +++ b/sdk/src/types/block/macro.rs @@ -169,3 +169,32 @@ macro_rules! create_bitflags { }; } pub(crate) use create_bitflags; + +#[macro_export] +macro_rules! impl_serde_typed_dto { + ($base:ty, $dto:ty, $type_str:literal) => { + impl<'de> Deserialize<'de> for $base { + fn deserialize>(d: D) -> Result { + let dto = <$dto>::deserialize(d)?; + if dto.kind != Self::KIND { + return Err(serde::de::Error::custom(format!( + "invalid {} type: expected {}, found {}", + $type_str, + Self::KIND, + dto.kind + ))); + } + dto.try_into().map_err(serde::de::Error::custom) + } + } + + impl Serialize for $base { + fn serialize(&self, s: S) -> Result + where + S: serde::Serializer, + { + <$dto>::from(self).serialize(s) + } + } + }; +} diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index e5f13e662e..1fa1df7668 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -734,25 +734,17 @@ fn verify_unlock_conditions(unlock_conditions: &UnlockConditions, account_id: &A } pub(crate) mod dto { - use alloc::{ - boxed::Box, - string::{String, ToString}, - }; + use alloc::boxed::Box; use serde::{Deserialize, Serialize}; use super::*; use crate::{ types::{ - block::{ - output::{ - dto::OutputBuilderAmountDto, feature::dto::FeatureDto, unlock_condition::dto::UnlockConditionDto, - }, - Error, - }, + block::{output::unlock_condition::dto::UnlockConditionDto, Error}, TryFromDto, }, - utils::serde::prefix_hex_bytes, + utils::serde::{prefix_hex_bytes, string}, }; /// Describes an account in the ledger that can be controlled by the state and governance controllers. @@ -762,8 +754,9 @@ pub(crate) mod dto { #[serde(rename = "type")] pub kind: u8, // Amount of IOTA tokens held by the output. - pub amount: String, - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] + pub amount: u64, + #[serde(with = "string")] pub mana: u64, // Native tokens held by the output. #[serde(skip_serializing_if = "Vec::is_empty", default)] @@ -781,17 +774,17 @@ pub(crate) mod dto { pub unlock_conditions: Vec, // #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub features: Vec, + pub features: Vec, // #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub immutable_features: Vec, + pub immutable_features: Vec, } impl From<&AccountOutput> for AccountOutputDto { fn from(value: &AccountOutput) -> Self { Self { kind: AccountOutput::KIND, - amount: value.amount().to_string(), + amount: value.amount(), mana: value.mana(), native_tokens: value.native_tokens().to_vec(), account_id: *value.account_id(), @@ -799,8 +792,8 @@ pub(crate) mod dto { state_metadata: value.state_metadata().into(), foundry_counter: value.foundry_counter(), unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(), - features: value.features().iter().map(Into::into).collect::<_>(), - immutable_features: value.immutable_features().iter().map(Into::into).collect::<_>(), + features: value.features().to_vec(), + immutable_features: value.immutable_features().to_vec(), } } } @@ -810,31 +803,14 @@ pub(crate) mod dto { type Error = Error; fn try_from_dto_with_params_inner(dto: Self::Dto, params: ValidationParams<'_>) -> Result { - let mut builder = AccountOutputBuilder::new_with_amount( - dto.amount.parse::().map_err(|_| Error::InvalidField("amount"))?, - dto.account_id, - ) - .with_mana(dto.mana); - - builder = builder.with_state_index(dto.state_index); - - if !dto.state_metadata.is_empty() { - builder = builder.with_state_metadata(dto.state_metadata); - } - - builder = builder.with_foundry_counter(dto.foundry_counter); - - for t in dto.native_tokens { - builder = builder.add_native_token(t); - } - - for b in dto.features { - builder = builder.add_feature(Feature::try_from(b)?); - } - - for b in dto.immutable_features { - builder = builder.add_immutable_feature(Feature::try_from(b)?); - } + let mut builder = AccountOutputBuilder::new_with_amount(dto.amount, dto.account_id) + .with_mana(dto.mana) + .with_state_index(dto.state_index) + .with_foundry_counter(dto.foundry_counter) + .with_native_tokens(dto.native_tokens) + .with_features(dto.features) + .with_immutable_features(dto.immutable_features) + .with_state_metadata(dto.state_metadata); for u in dto.unlock_conditions { builder = builder.add_unlock_condition(UnlockCondition::try_from_dto_with_params(u, ¶ms)?); @@ -847,7 +823,7 @@ pub(crate) mod dto { impl AccountOutput { #[allow(clippy::too_many_arguments)] pub fn try_from_dtos<'a>( - amount: OutputBuilderAmountDto, + amount: OutputBuilderAmount, mana: u64, native_tokens: Option>, account_id: &AccountId, @@ -855,17 +831,14 @@ pub(crate) mod dto { state_metadata: Option>, foundry_counter: Option, unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + features: Option>, + immutable_features: Option>, params: impl Into> + Send, ) -> Result { let params = params.into(); let mut builder = match amount { - OutputBuilderAmountDto::Amount(amount) => AccountOutputBuilder::new_with_amount( - amount.parse().map_err(|_| Error::InvalidField("amount"))?, - *account_id, - ), - OutputBuilderAmountDto::MinimumStorageDeposit(rent_structure) => { + OutputBuilderAmount::Amount(amount) => AccountOutputBuilder::new_with_amount(amount, *account_id), + OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { AccountOutputBuilder::new_with_minimum_storage_deposit(rent_structure, *account_id) } } @@ -894,18 +867,10 @@ pub(crate) mod dto { builder = builder.with_unlock_conditions(unlock_conditions); if let Some(features) = features { - let features = features - .into_iter() - .map(Feature::try_from) - .collect::, Error>>()?; builder = builder.with_features(features); } if let Some(immutable_features) = immutable_features { - let immutable_features = immutable_features - .into_iter() - .map(Feature::try_from) - .collect::, Error>>()?; builder = builder.with_immutable_features(immutable_features); } @@ -922,10 +887,7 @@ mod tests { use crate::types::{ block::{ address::AccountAddress, - output::{ - dto::{OutputBuilderAmountDto, OutputDto}, - FoundryId, SimpleTokenScheme, TokenId, - }, + output::{dto::OutputDto, FoundryId, SimpleTokenScheme, TokenId}, protocol::protocol_parameters, rand::{ address::rand_account_address, @@ -1032,7 +994,7 @@ mod tests { assert_eq!(&output, output_ver.as_account()); let output_split = AccountOutput::try_from_dtos( - OutputBuilderAmountDto::Amount(output.amount().to_string()), + OutputBuilderAmount::Amount(output.amount()), output.mana(), Some(output.native_tokens().to_vec()), output.account_id(), @@ -1040,8 +1002,8 @@ mod tests { output.state_metadata().to_owned().into(), output.foundry_counter().into(), output.unlock_conditions().iter().map(Into::into).collect(), - Some(output.features().iter().map(Into::into).collect()), - Some(output.immutable_features().iter().map(Into::into).collect()), + Some(output.features().to_vec()), + Some(output.immutable_features().to_vec()), &protocol_parameters, ) .unwrap(); @@ -1054,7 +1016,7 @@ mod tests { let test_split_dto = |builder: AccountOutputBuilder| { let output_split = AccountOutput::try_from_dtos( - (&builder.amount).into(), + builder.amount, builder.mana, Some(builder.native_tokens.iter().copied().collect()), &builder.account_id, @@ -1062,8 +1024,8 @@ mod tests { builder.state_metadata.to_owned().into(), builder.foundry_counter, builder.unlock_conditions.iter().map(Into::into).collect(), - Some(builder.features.iter().map(Into::into).collect()), - Some(builder.immutable_features.iter().map(Into::into).collect()), + Some(builder.features.iter().cloned().collect()), + Some(builder.immutable_features.iter().cloned().collect()), &protocol_parameters, ) .unwrap(); diff --git a/sdk/src/types/block/output/basic.rs b/sdk/src/types/block/output/basic.rs index 676ceb9ad7..8967aa704d 100644 --- a/sdk/src/types/block/output/basic.rs +++ b/sdk/src/types/block/output/basic.rs @@ -351,19 +351,15 @@ fn verify_features_packable(blocks: &Features, _: &ProtocolP } pub(crate) mod dto { - use alloc::string::{String, ToString}; - use serde::{Deserialize, Serialize}; use super::*; - use crate::types::{ - block::{ - output::{ - dto::OutputBuilderAmountDto, feature::dto::FeatureDto, unlock_condition::dto::UnlockConditionDto, - }, - Error, + use crate::{ + types::{ + block::{output::unlock_condition::dto::UnlockConditionDto, Error}, + TryFromDto, }, - TryFromDto, + utils::serde::string, }; /// Describes a basic output. @@ -373,26 +369,27 @@ pub(crate) mod dto { #[serde(rename = "type")] pub kind: u8, // Amount of IOTA tokens held by the output. - pub amount: String, - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] + pub amount: u64, + #[serde(with = "string")] pub mana: u64, // Native tokens held by the output. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub native_tokens: Vec, pub unlock_conditions: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub features: Vec, + pub features: Vec, } impl From<&BasicOutput> for BasicOutputDto { fn from(value: &BasicOutput) -> Self { Self { kind: BasicOutput::KIND, - amount: value.amount().to_string(), + amount: value.amount(), mana: value.mana(), native_tokens: value.native_tokens().to_vec(), unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(), - features: value.features().iter().map(Into::into).collect::<_>(), + features: value.features().to_vec(), } } } @@ -402,14 +399,10 @@ pub(crate) mod dto { type Error = Error; fn try_from_dto_with_params_inner(dto: Self::Dto, params: ValidationParams<'_>) -> Result { - let mut builder = - BasicOutputBuilder::new_with_amount(dto.amount.parse().map_err(|_| Error::InvalidField("amount"))?); - - builder = builder.with_native_tokens(dto.native_tokens).with_mana(dto.mana); - - for b in dto.features { - builder = builder.add_feature(Feature::try_from(b)?); - } + let mut builder = BasicOutputBuilder::new_with_amount(dto.amount) + .with_native_tokens(dto.native_tokens) + .with_mana(dto.mana) + .with_features(dto.features); for u in dto.unlock_conditions { builder = builder.add_unlock_condition(UnlockCondition::try_from_dto_with_params(u, ¶ms)?); @@ -421,19 +414,17 @@ pub(crate) mod dto { impl BasicOutput { pub fn try_from_dtos<'a>( - amount: OutputBuilderAmountDto, + amount: OutputBuilderAmount, mana: u64, native_tokens: Option>, unlock_conditions: Vec, - features: Option>, + features: Option>, params: impl Into> + Send, ) -> Result { let params = params.into(); let mut builder = match amount { - OutputBuilderAmountDto::Amount(amount) => { - BasicOutputBuilder::new_with_amount(amount.parse().map_err(|_| Error::InvalidField("amount"))?) - } - OutputBuilderAmountDto::MinimumStorageDeposit(rent_structure) => { + OutputBuilderAmount::Amount(amount) => BasicOutputBuilder::new_with_amount(amount), + OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) } } @@ -450,10 +441,6 @@ pub(crate) mod dto { builder = builder.with_unlock_conditions(unlock_conditions); if let Some(features) = features { - let features = features - .into_iter() - .map(Feature::try_from) - .collect::, Error>>()?; builder = builder.with_features(features); } @@ -469,10 +456,7 @@ mod tests { use super::*; use crate::types::{ block::{ - output::{ - dto::{OutputBuilderAmountDto, OutputDto}, - FoundryId, SimpleTokenScheme, TokenId, - }, + output::{dto::OutputDto, FoundryId, SimpleTokenScheme, TokenId}, protocol::protocol_parameters, rand::{ address::rand_account_address, @@ -550,11 +534,11 @@ mod tests { assert_eq!(&output, output_ver.as_basic()); let output_split = BasicOutput::try_from_dtos( - OutputBuilderAmountDto::Amount(output.amount().to_string()), + OutputBuilderAmount::Amount(output.amount()), output.mana(), Some(output.native_tokens().to_vec()), output.unlock_conditions().iter().map(Into::into).collect(), - Some(output.features().iter().map(Into::into).collect()), + Some(output.features().to_vec()), protocol_parameters.token_supply(), ) .unwrap(); @@ -565,11 +549,11 @@ mod tests { let test_split_dto = |builder: BasicOutputBuilder| { let output_split = BasicOutput::try_from_dtos( - (&builder.amount).into(), + builder.amount, builder.mana, Some(builder.native_tokens.iter().copied().collect()), builder.unlock_conditions.iter().map(Into::into).collect(), - Some(builder.features.iter().map(Into::into).collect()), + Some(builder.features.iter().cloned().collect()), protocol_parameters.token_supply(), ) .unwrap(); diff --git a/sdk/src/types/block/output/delegation.rs b/sdk/src/types/block/output/delegation.rs index 48849cd7d3..c214193c29 100644 --- a/sdk/src/types/block/output/delegation.rs +++ b/sdk/src/types/block/output/delegation.rs @@ -413,17 +413,18 @@ fn verify_unlock_conditions(unlock_conditions: &UnlockCondit } pub(crate) mod dto { - use alloc::string::{String, ToString}; - use serde::{Deserialize, Serialize}; use super::*; - use crate::types::{ - block::{ - output::{dto::OutputBuilderAmountDto, unlock_condition::dto::UnlockConditionDto}, - Error, + use crate::{ + types::{ + block::{ + output::{unlock_condition::dto::UnlockConditionDto, OutputBuilderAmount}, + Error, + }, + TryFromDto, }, - TryFromDto, + utils::serde::string, }; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -431,11 +432,15 @@ pub(crate) mod dto { pub struct DelegationOutputDto { #[serde(rename = "type")] pub kind: u8, - pub amount: String, - pub delegated_amount: String, + #[serde(with = "string")] + pub amount: u64, + #[serde(with = "string")] + pub delegated_amount: u64, pub delegation_id: DelegationId, pub validator_id: AccountId, + #[serde(with = "string")] start_epoch: u64, + #[serde(with = "string")] end_epoch: u64, pub unlock_conditions: Vec, } @@ -444,8 +449,8 @@ pub(crate) mod dto { fn from(value: &DelegationOutput) -> Self { Self { kind: DelegationOutput::KIND, - amount: value.amount().to_string(), - delegated_amount: value.delegated_amount().to_string(), + amount: value.amount(), + delegated_amount: value.delegated_amount(), delegation_id: *value.delegation_id(), validator_id: *value.validator_id(), start_epoch: value.start_epoch(), @@ -464,16 +469,13 @@ pub(crate) mod dto { params: crate::types::ValidationParams<'_>, ) -> Result { let mut builder = DelegationOutputBuilder::new_with_amount( - dto.amount.parse::().map_err(|_| Error::InvalidField("amount"))?, - dto.delegated_amount - .parse::() - .map_err(|_| Error::InvalidField("delegatedAmount"))?, + dto.amount, + dto.delegated_amount, dto.delegation_id, dto.validator_id, - ); - - builder = builder.with_start_epoch(dto.start_epoch); - builder = builder.with_end_epoch(dto.end_epoch); + ) + .with_start_epoch(dto.start_epoch) + .with_end_epoch(dto.end_epoch); for u in dto.unlock_conditions { builder = builder.add_unlock_condition(UnlockCondition::try_from_dto_with_params(u, ¶ms)?); @@ -486,8 +488,8 @@ pub(crate) mod dto { impl DelegationOutput { #[allow(clippy::too_many_arguments)] pub fn try_from_dtos<'a>( - amount: OutputBuilderAmountDto, - delegated_amount: String, + amount: OutputBuilderAmount, + delegated_amount: u64, delegation_id: &DelegationId, validator_id: &AccountId, start_epoch: u64, @@ -497,28 +499,20 @@ pub(crate) mod dto { ) -> Result { let params = params.into(); let mut builder = match amount { - OutputBuilderAmountDto::Amount(amount) => DelegationOutputBuilder::new_with_amount( - amount.parse().map_err(|_| Error::InvalidField("amount"))?, - delegated_amount - .parse() - .map_err(|_| Error::InvalidField("delegatedAmount"))?, - *delegation_id, - *validator_id, - ), - OutputBuilderAmountDto::MinimumStorageDeposit(rent_structure) => { + OutputBuilderAmount::Amount(amount) => { + DelegationOutputBuilder::new_with_amount(amount, delegated_amount, *delegation_id, *validator_id) + } + OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { DelegationOutputBuilder::new_with_minimum_storage_deposit( rent_structure, - delegated_amount - .parse() - .map_err(|_| Error::InvalidField("delegatedAmount"))?, + delegated_amount, *delegation_id, *validator_id, ) } - }; - - builder = builder.with_start_epoch(start_epoch); - builder = builder.with_end_epoch(end_epoch); + } + .with_start_epoch(start_epoch) + .with_end_epoch(end_epoch); let unlock_conditions = unlock_conditions .into_iter() diff --git a/sdk/src/types/block/output/feature/issuer.rs b/sdk/src/types/block/output/feature/issuer.rs index b245138b33..ed7db8f808 100644 --- a/sdk/src/types/block/output/feature/issuer.rs +++ b/sdk/src/types/block/output/feature/issuer.rs @@ -7,7 +7,7 @@ use crate::types::block::address::Address; /// Identifies the validated issuer of the UTXO state machine. #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] -pub struct IssuerFeature(Address); +pub struct IssuerFeature(pub(crate) Address); impl IssuerFeature { /// The [`Feature`](crate::types::block::output::Feature) kind of an [`IssuerFeature`]. @@ -26,15 +26,34 @@ impl IssuerFeature { } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; - use crate::types::block::address::dto::AddressDto; + use super::*; - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct IssuerFeatureDto { + #[derive(Serialize, Deserialize)] + struct IssuerFeatureDto { #[serde(rename = "type")] - pub kind: u8, - pub address: AddressDto, + kind: u8, + address: Address, + } + + impl From<&IssuerFeature> for IssuerFeatureDto { + fn from(value: &IssuerFeature) -> Self { + Self { + kind: IssuerFeature::KIND, + address: value.0, + } + } } + + impl From for IssuerFeature { + fn from(value: IssuerFeatureDto) -> Self { + Self(value.address) + } + } + + impl_serde_typed_dto!(IssuerFeature, IssuerFeatureDto, "issuer feature"); } diff --git a/sdk/src/types/block/output/feature/metadata.rs b/sdk/src/types/block/output/feature/metadata.rs index eedc8ae75d..fdf9444f76 100644 --- a/sdk/src/types/block/output/feature/metadata.rs +++ b/sdk/src/types/block/output/feature/metadata.rs @@ -16,7 +16,7 @@ pub(crate) type MetadataFeatureLength = #[packable(unpack_error = Error, with = |err| Error::InvalidMetadataFeatureLength(err.into_prefix_err().into()))] pub struct MetadataFeature( // Binary data. - BoxedSlicePrefix, + pub(crate) BoxedSlicePrefix, ); impl TryFrom> for MetadataFeature { @@ -74,18 +74,36 @@ impl core::fmt::Debug for MetadataFeature { } } -pub(crate) mod dto { - use alloc::boxed::Box; +mod dto { + use alloc::{borrow::Cow, format}; use serde::{Deserialize, Serialize}; - use crate::utils::serde::prefix_hex_bytes; + use super::*; + use crate::utils::serde::cow_boxed_slice_prefix; - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct MetadataFeatureDto { + #[derive(Serialize, Deserialize)] + struct MetadataFeatureDto<'a> { #[serde(rename = "type")] - pub kind: u8, - #[serde(skip_serializing_if = "<[_]>::is_empty", default, with = "prefix_hex_bytes")] - pub data: Box<[u8]>, + kind: u8, + #[serde(with = "cow_boxed_slice_prefix")] + data: Cow<'a, BoxedSlicePrefix>, } + + impl<'a> From<&'a MetadataFeature> for MetadataFeatureDto<'a> { + fn from(value: &'a MetadataFeature) -> Self { + Self { + kind: MetadataFeature::KIND, + data: Cow::Borrowed(&value.0), + } + } + } + + impl<'a> From> for MetadataFeature { + fn from(value: MetadataFeatureDto<'a>) -> Self { + Self(value.data.into_owned()) + } + } + + impl_serde_typed_dto!(MetadataFeature, MetadataFeatureDto<'_>, "metadata feature"); } diff --git a/sdk/src/types/block/output/feature/mod.rs b/sdk/src/types/block/output/feature/mod.rs index b520fd2a2e..b9430bbe7b 100644 --- a/sdk/src/types/block/output/feature/mod.rs +++ b/sdk/src/types/block/output/feature/mod.rs @@ -24,6 +24,7 @@ use crate::types::block::{create_bitflags, Error}; #[derive(Clone, Eq, PartialEq, Hash, From, Packable)] #[packable(unpack_error = Error)] #[packable(tag_type = u8, with_error = Error::InvalidFeatureKind)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] pub enum Feature { /// A sender feature. #[packable(tag = SenderFeature::KIND)] @@ -315,170 +316,3 @@ mod test { ); } } - -pub mod dto { - use alloc::format; - - use serde::{Deserialize, Serialize, Serializer}; - use serde_json::Value; - - pub use self::{ - issuer::dto::IssuerFeatureDto, metadata::dto::MetadataFeatureDto, sender::dto::SenderFeatureDto, - staking::dto::StakingFeatureDto, tag::dto::TagFeatureDto, - }; - use super::*; - use crate::types::block::{address::Address, Error}; - - #[derive(Clone, Debug, Eq, PartialEq, From)] - pub enum FeatureDto { - /// A sender feature. - Sender(SenderFeatureDto), - /// An issuer feature. - Issuer(IssuerFeatureDto), - /// A metadata feature. - Metadata(MetadataFeatureDto), - /// A tag feature. - Tag(TagFeatureDto), - /// A staking feature. - Staking(StakingFeatureDto), - } - - impl<'de> Deserialize<'de> for FeatureDto { - fn deserialize>(d: D) -> Result { - let value = Value::deserialize(d)?; - Ok( - match value - .get("type") - .and_then(Value::as_u64) - .ok_or_else(|| serde::de::Error::custom("invalid feature type"))? as u8 - { - SenderFeature::KIND => Self::Sender( - SenderFeatureDto::deserialize(value) - .map_err(|e| serde::de::Error::custom(format!("cannot deserialize sender feature: {e}")))?, - ), - IssuerFeature::KIND => Self::Issuer( - IssuerFeatureDto::deserialize(value) - .map_err(|e| serde::de::Error::custom(format!("cannot deserialize issuer feature: {e}")))?, - ), - MetadataFeature::KIND => { - Self::Metadata(MetadataFeatureDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!("cannot deserialize metadata feature: {e}")) - })?) - } - TagFeature::KIND => Self::Tag( - TagFeatureDto::deserialize(value) - .map_err(|e| serde::de::Error::custom(format!("cannot deserialize tag feature: {e}")))?, - ), - StakingFeature::KIND => { - Self::Staking(StakingFeatureDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!("cannot deserialize staking feature: {e}")) - })?) - } - _ => return Err(serde::de::Error::custom("invalid feature type")), - }, - ) - } - } - - impl Serialize for FeatureDto { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - #[derive(Serialize)] - #[serde(untagged)] - enum FeatureDto_<'a> { - T1(&'a SenderFeatureDto), - T2(&'a IssuerFeatureDto), - T3(&'a MetadataFeatureDto), - T4(&'a TagFeatureDto), - T6(&'a StakingFeatureDto), - } - #[derive(Serialize)] - struct TypedFeature<'a> { - #[serde(flatten)] - feature: FeatureDto_<'a>, - } - let feature = match self { - Self::Sender(o) => TypedFeature { - feature: FeatureDto_::T1(o), - }, - Self::Issuer(o) => TypedFeature { - feature: FeatureDto_::T2(o), - }, - Self::Metadata(o) => TypedFeature { - feature: FeatureDto_::T3(o), - }, - Self::Tag(o) => TypedFeature { - feature: FeatureDto_::T4(o), - }, - Self::Staking(o) => TypedFeature { - feature: FeatureDto_::T6(o), - }, - }; - feature.serialize(serializer) - } - } - - impl From<&Feature> for FeatureDto { - fn from(value: &Feature) -> Self { - match value { - Feature::Sender(v) => Self::Sender(SenderFeatureDto { - kind: SenderFeature::KIND, - address: v.address().into(), - }), - Feature::Issuer(v) => Self::Issuer(IssuerFeatureDto { - kind: IssuerFeature::KIND, - address: v.address().into(), - }), - Feature::Metadata(v) => Self::Metadata(MetadataFeatureDto { - kind: MetadataFeature::KIND, - data: v.data().into(), - }), - Feature::Tag(v) => Self::Tag(TagFeatureDto { - kind: TagFeature::KIND, - tag: v.tag().into(), - }), - Feature::Staking(v) => Self::Staking(StakingFeatureDto { - kind: StakingFeature::KIND, - staked_amount: v.staked_amount(), - fixed_cost: v.fixed_cost(), - start_epoch: v.start_epoch(), - end_epoch: v.end_epoch(), - }), - } - } - } - - impl TryFrom for Feature { - type Error = Error; - - fn try_from(value: FeatureDto) -> Result { - Ok(match value { - FeatureDto::Sender(v) => Self::Sender(SenderFeature::new(Address::try_from(v.address)?)), - FeatureDto::Issuer(v) => Self::Issuer(IssuerFeature::new(Address::try_from(v.address)?)), - FeatureDto::Metadata(v) => Self::Metadata(MetadataFeature::new(v.data)?), - FeatureDto::Tag(v) => Self::Tag(TagFeature::new(v.tag)?), - FeatureDto::Staking(v) => Self::Staking(StakingFeature::new( - v.staked_amount, - v.fixed_cost, - v.start_epoch, - v.end_epoch, - )), - }) - } - } - - impl FeatureDto { - /// Return the feature kind of a `FeatureDto`. - pub fn kind(&self) -> u8 { - match self { - Self::Sender(_) => SenderFeature::KIND, - Self::Issuer(_) => IssuerFeature::KIND, - Self::Metadata(_) => MetadataFeature::KIND, - Self::Tag(_) => TagFeature::KIND, - Self::Staking(_) => StakingFeature::KIND, - } - } - } -} diff --git a/sdk/src/types/block/output/feature/sender.rs b/sdk/src/types/block/output/feature/sender.rs index 599a9d5c66..7094ef67c3 100644 --- a/sdk/src/types/block/output/feature/sender.rs +++ b/sdk/src/types/block/output/feature/sender.rs @@ -7,7 +7,7 @@ use crate::types::block::address::Address; /// Identifies the validated sender of an output. #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] -pub struct SenderFeature(Address); +pub struct SenderFeature(pub(crate) Address); impl SenderFeature { /// The [`Feature`](crate::types::block::output::Feature) kind of a [`SenderFeature`]. @@ -26,15 +26,34 @@ impl SenderFeature { } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; - use crate::types::block::address::dto::AddressDto; + use super::*; - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct SenderFeatureDto { + #[derive(Serialize, Deserialize)] + struct SenderFeatureDto { #[serde(rename = "type")] - pub kind: u8, - pub address: AddressDto, + kind: u8, + address: Address, + } + + impl From<&SenderFeature> for SenderFeatureDto { + fn from(value: &SenderFeature) -> Self { + Self { + kind: SenderFeature::KIND, + address: value.0, + } + } } + + impl From for SenderFeature { + fn from(value: SenderFeatureDto) -> Self { + Self(value.address) + } + } + + impl_serde_typed_dto!(SenderFeature, SenderFeatureDto, "sender feature"); } diff --git a/sdk/src/types/block/output/feature/staking.rs b/sdk/src/types/block/output/feature/staking.rs index 03fb59ae82..04acdbd76c 100644 --- a/sdk/src/types/block/output/feature/staking.rs +++ b/sdk/src/types/block/output/feature/staking.rs @@ -3,7 +3,6 @@ /// Stakes coins to become eligible for committee selection, validate the network and receive Mana rewards. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, packable::Packable)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct StakingFeature { /// The amount of coins that are locked and staked in the containing account. staked_amount: u64, @@ -50,23 +49,51 @@ impl StakingFeature { } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; + use super::*; use crate::utils::serde::string; - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] - pub struct StakingFeatureDto { + struct StakingFeatureDto { #[serde(rename = "type")] - pub kind: u8, + kind: u8, #[serde(with = "string")] - pub staked_amount: u64, + staked_amount: u64, #[serde(with = "string")] - pub fixed_cost: u64, + fixed_cost: u64, #[serde(with = "string")] - pub start_epoch: u64, + start_epoch: u64, #[serde(with = "string")] - pub end_epoch: u64, + end_epoch: u64, + } + + impl From<&StakingFeature> for StakingFeatureDto { + fn from(value: &StakingFeature) -> Self { + Self { + kind: StakingFeature::KIND, + staked_amount: value.staked_amount, + fixed_cost: value.fixed_cost, + start_epoch: value.start_epoch, + end_epoch: value.end_epoch, + } + } } + + impl From for StakingFeature { + fn from(value: StakingFeatureDto) -> Self { + Self::new( + value.staked_amount, + value.fixed_cost, + value.start_epoch, + value.end_epoch, + ) + } + } + + impl_serde_typed_dto!(StakingFeature, StakingFeatureDto, "staking feature"); } diff --git a/sdk/src/types/block/output/feature/tag.rs b/sdk/src/types/block/output/feature/tag.rs index 689d612553..ffa192664c 100644 --- a/sdk/src/types/block/output/feature/tag.rs +++ b/sdk/src/types/block/output/feature/tag.rs @@ -16,7 +16,7 @@ pub(crate) type TagFeatureLength = #[packable(unpack_error = Error, with = |e| Error::InvalidTagFeatureLength(e.into_prefix_err().into()))] pub struct TagFeature( // Binary tag. - BoxedSlicePrefix, + pub(crate) BoxedSlicePrefix, ); impl TryFrom> for TagFeature { @@ -66,18 +66,36 @@ impl core::fmt::Debug for TagFeature { } } -pub(crate) mod dto { - use alloc::boxed::Box; +mod dto { + use alloc::{borrow::Cow, format}; use serde::{Deserialize, Serialize}; - use crate::utils::serde::prefix_hex_bytes; + use super::*; + use crate::utils::serde::cow_boxed_slice_prefix; - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct TagFeatureDto { + #[derive(Serialize, Deserialize)] + struct TagFeatureDto<'a> { #[serde(rename = "type")] - pub kind: u8, - #[serde(skip_serializing_if = "<[_]>::is_empty", default, with = "prefix_hex_bytes")] - pub tag: Box<[u8]>, + kind: u8, + #[serde(with = "cow_boxed_slice_prefix")] + tag: Cow<'a, BoxedSlicePrefix>, } + + impl<'a> From<&'a TagFeature> for TagFeatureDto<'a> { + fn from(value: &'a TagFeature) -> Self { + Self { + kind: TagFeature::KIND, + tag: Cow::Borrowed(&value.0), + } + } + } + + impl<'a> From> for TagFeature { + fn from(value: TagFeatureDto<'a>) -> Self { + Self(value.tag.into_owned()) + } + } + + impl_serde_typed_dto!(TagFeature, TagFeatureDto<'_>, "tag feature"); } diff --git a/sdk/src/types/block/output/foundry.rs b/sdk/src/types/block/output/foundry.rs index 6a346eeb40..09223d1c01 100644 --- a/sdk/src/types/block/output/foundry.rs +++ b/sdk/src/types/block/output/foundry.rs @@ -368,7 +368,7 @@ impl FoundryOutput { // A FoundryOutput must have an ImmutableAccountAddressUnlockCondition. self.unlock_conditions .immutable_account_address() - .map(|unlock_condition| unlock_condition.account_address()) + .map(|unlock_condition| unlock_condition.address()) .unwrap() } @@ -614,20 +614,15 @@ fn verify_unlock_conditions(unlock_conditions: &UnlockConditions) -> Result<(), } pub(crate) mod dto { - use alloc::string::{String, ToString}; - use serde::{Deserialize, Serialize}; use super::*; - use crate::types::{ - block::{ - output::{ - dto::OutputBuilderAmountDto, feature::dto::FeatureDto, token_scheme::dto::TokenSchemeDto, - unlock_condition::dto::UnlockConditionDto, - }, - Error, + use crate::{ + types::{ + block::{output::unlock_condition::dto::UnlockConditionDto, Error}, + TryFromDto, }, - TryFromDto, + utils::serde::string, }; /// Describes a foundry output that is controlled by an account. @@ -637,31 +632,32 @@ pub(crate) mod dto { #[serde(rename = "type")] pub kind: u8, // Amount of IOTA tokens held by the output. - pub amount: String, + #[serde(with = "string")] + pub amount: u64, // Native tokens held by the output. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub native_tokens: Vec, // The serial number of the foundry with respect to the controlling account. pub serial_number: u32, - pub token_scheme: TokenSchemeDto, + pub token_scheme: TokenScheme, pub unlock_conditions: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub features: Vec, + pub features: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub immutable_features: Vec, + pub immutable_features: Vec, } impl From<&FoundryOutput> for FoundryOutputDto { fn from(value: &FoundryOutput) -> Self { Self { kind: FoundryOutput::KIND, - amount: value.amount().to_string(), + amount: value.amount(), native_tokens: value.native_tokens().to_vec(), serial_number: value.serial_number(), - token_scheme: value.token_scheme().into(), + token_scheme: value.token_scheme().clone(), unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(), - features: value.features().iter().map(Into::into).collect::<_>(), - immutable_features: value.immutable_features().iter().map(Into::into).collect::<_>(), + features: value.features().to_vec(), + immutable_features: value.immutable_features().to_vec(), } } } @@ -671,22 +667,18 @@ pub(crate) mod dto { type Error = Error; fn try_from_dto_with_params_inner(dto: Self::Dto, params: ValidationParams<'_>) -> Result { - let mut builder = FoundryOutputBuilder::new_with_amount( - dto.amount.parse::().map_err(|_| Error::InvalidField("amount"))?, - dto.serial_number, - dto.token_scheme.try_into()?, - ); + let mut builder = FoundryOutputBuilder::new_with_amount(dto.amount, dto.serial_number, dto.token_scheme); for t in dto.native_tokens { builder = builder.add_native_token(t); } for b in dto.features { - builder = builder.add_feature(Feature::try_from(b)?); + builder = builder.add_feature(b); } for b in dto.immutable_features { - builder = builder.add_immutable_feature(Feature::try_from(b)?); + builder = builder.add_immutable_feature(b); } for u in dto.unlock_conditions { @@ -700,25 +692,22 @@ pub(crate) mod dto { impl FoundryOutput { #[allow(clippy::too_many_arguments)] pub fn try_from_dtos<'a>( - amount: OutputBuilderAmountDto, + amount: OutputBuilderAmount, native_tokens: Option>, serial_number: u32, - token_scheme: TokenSchemeDto, + token_scheme: TokenScheme, unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + features: Option>, + immutable_features: Option>, params: impl Into> + Send, ) -> Result { let params = params.into(); - let token_scheme = TokenScheme::try_from(token_scheme)?; let mut builder = match amount { - OutputBuilderAmountDto::Amount(amount) => FoundryOutputBuilder::new_with_amount( - amount.parse().map_err(|_| Error::InvalidField("amount"))?, - serial_number, - token_scheme, - ), - OutputBuilderAmountDto::MinimumStorageDeposit(rent_structure) => { + OutputBuilderAmount::Amount(amount) => { + FoundryOutputBuilder::new_with_amount(amount, serial_number, token_scheme) + } + OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_structure, serial_number, token_scheme) } }; @@ -734,18 +723,10 @@ pub(crate) mod dto { builder = builder.with_unlock_conditions(unlock_conditions); if let Some(features) = features { - let features = features - .into_iter() - .map(Feature::try_from) - .collect::, Error>>()?; builder = builder.with_features(features); } if let Some(immutable_features) = immutable_features { - let immutable_features = immutable_features - .into_iter() - .map(Feature::try_from) - .collect::, Error>>()?; builder = builder.with_immutable_features(immutable_features); } @@ -846,13 +827,13 @@ mod tests { let test_split_dto = |builder: FoundryOutputBuilder| { let output_split = FoundryOutput::try_from_dtos( - (&builder.amount).into(), + builder.amount, Some(builder.native_tokens.iter().copied().collect()), builder.serial_number, - (&builder.token_scheme).into(), + builder.token_scheme.clone(), builder.unlock_conditions.iter().map(Into::into).collect(), - Some(builder.features.iter().map(Into::into).collect()), - Some(builder.immutable_features.iter().map(Into::into).collect()), + Some(builder.features.iter().cloned().collect()), + Some(builder.immutable_features.iter().cloned().collect()), protocol_parameters.clone(), ) .unwrap(); diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index 1abbebca73..cab4db489f 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -28,7 +28,6 @@ pub mod nft; /// pub mod unlock_condition; -use alloc::string::ToString; use core::ops::RangeInclusive; use derive_more::From; @@ -82,8 +81,8 @@ pub const OUTPUT_INDEX_MAX: u16 = OUTPUT_COUNT_MAX - 1; // 127 /// The range of valid indices of outputs of a transaction . pub const OUTPUT_INDEX_RANGE: RangeInclusive = 0..=OUTPUT_INDEX_MAX; // [0..127] -#[derive(Clone)] -pub(crate) enum OutputBuilderAmount { +#[derive(Copy, Clone)] +pub enum OutputBuilderAmount { Amount(u64), MinimumStorageDeposit(RentStructure), } @@ -507,38 +506,18 @@ fn minimum_storage_deposit(address: &Address, rent_structure: RentStructure, tok } pub mod dto { - use alloc::{format, string::String}; + use alloc::format; use serde::{Deserialize, Serialize, Serializer}; use serde_json::Value; use super::*; pub use super::{ - account::dto::AccountOutputDto, - basic::dto::BasicOutputDto, - delegation::dto::DelegationOutputDto, - foundry::dto::FoundryOutputDto, - nft::dto::NftOutputDto, - token_scheme::dto::{SimpleTokenSchemeDto, TokenSchemeDto}, + account::dto::AccountOutputDto, basic::dto::BasicOutputDto, delegation::dto::DelegationOutputDto, + foundry::dto::FoundryOutputDto, nft::dto::NftOutputDto, }; use crate::types::{block::Error, TryFromDto}; - #[derive(Clone, Debug, From)] - #[cfg_attr(feature = "serde", derive(serde::Deserialize))] - pub enum OutputBuilderAmountDto { - Amount(String), - MinimumStorageDeposit(RentStructure), - } - - impl From<&OutputBuilderAmount> for OutputBuilderAmountDto { - fn from(value: &OutputBuilderAmount) -> Self { - match value { - OutputBuilderAmount::Amount(a) => Self::Amount(a.to_string()), - OutputBuilderAmount::MinimumStorageDeposit(r) => Self::MinimumStorageDeposit(*r), - } - } - } - /// Describes all the different output types. #[derive(Clone, Debug, Eq, PartialEq, From)] pub enum OutputDto { diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index 8ca61158da..af97d15ea8 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -505,19 +505,15 @@ fn verify_unlock_conditions(unlock_conditions: &UnlockConditions, nft_id: &NftId } pub(crate) mod dto { - use alloc::string::{String, ToString}; - use serde::{Deserialize, Serialize}; use super::*; - use crate::types::{ - block::{ - output::{ - dto::OutputBuilderAmountDto, feature::dto::FeatureDto, unlock_condition::dto::UnlockConditionDto, - }, - Error, + use crate::{ + types::{ + block::{output::unlock_condition::dto::UnlockConditionDto, Error}, + TryFromDto, }, - TryFromDto, + utils::serde::string, }; /// Describes an NFT output, a globally unique token with metadata attached. @@ -527,8 +523,9 @@ pub(crate) mod dto { #[serde(rename = "type")] pub kind: u8, // Amount of IOTA tokens held by the output. - pub amount: String, - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] + pub amount: u64, + #[serde(with = "string")] pub mana: u64, // Native tokens held by the output. #[serde(skip_serializing_if = "Vec::is_empty", default)] @@ -537,22 +534,22 @@ pub(crate) mod dto { pub nft_id: NftId, pub unlock_conditions: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub features: Vec, + pub features: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub immutable_features: Vec, + pub immutable_features: Vec, } impl From<&NftOutput> for NftOutputDto { fn from(value: &NftOutput) -> Self { Self { kind: NftOutput::KIND, - amount: value.amount().to_string(), + amount: value.amount(), mana: value.mana(), native_tokens: value.native_tokens().to_vec(), nft_id: *value.nft_id(), unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(), - features: value.features().iter().map(Into::into).collect::<_>(), - immutable_features: value.immutable_features().iter().map(Into::into).collect::<_>(), + features: value.features().to_vec(), + immutable_features: value.immutable_features().to_vec(), } } } @@ -562,23 +559,11 @@ pub(crate) mod dto { type Error = Error; fn try_from_dto_with_params_inner(dto: Self::Dto, params: ValidationParams<'_>) -> Result { - let mut builder = NftOutputBuilder::new_with_amount( - dto.amount.parse::().map_err(|_| Error::InvalidField("amount"))?, - dto.nft_id, - ) - .with_mana(dto.mana); - - for t in dto.native_tokens { - builder = builder.add_native_token(t); - } - - for b in dto.features { - builder = builder.add_feature(Feature::try_from(b)?); - } - - for b in dto.immutable_features { - builder = builder.add_immutable_feature(Feature::try_from(b)?); - } + let mut builder = NftOutputBuilder::new_with_amount(dto.amount, dto.nft_id) + .with_mana(dto.mana) + .with_native_tokens(dto.native_tokens) + .with_features(dto.features) + .with_immutable_features(dto.immutable_features); for u in dto.unlock_conditions { builder = builder.add_unlock_condition(UnlockCondition::try_from_dto_with_params(u, ¶ms)?); @@ -591,22 +576,19 @@ pub(crate) mod dto { impl NftOutput { #[allow(clippy::too_many_arguments)] pub fn try_from_dtos<'a>( - amount: OutputBuilderAmountDto, + amount: OutputBuilderAmount, mana: u64, native_tokens: Option>, nft_id: &NftId, unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + features: Option>, + immutable_features: Option>, params: impl Into> + Send, ) -> Result { let params = params.into(); let mut builder = match amount { - OutputBuilderAmountDto::Amount(amount) => NftOutputBuilder::new_with_amount( - amount.parse().map_err(|_| Error::InvalidField("amount"))?, - *nft_id, - ), - OutputBuilderAmountDto::MinimumStorageDeposit(rent_structure) => { + OutputBuilderAmount::Amount(amount) => NftOutputBuilder::new_with_amount(amount, *nft_id), + OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure, *nft_id) } } @@ -623,18 +605,10 @@ pub(crate) mod dto { builder = builder.with_unlock_conditions(unlock_conditions); if let Some(features) = features { - let features = features - .into_iter() - .map(Feature::try_from) - .collect::, Error>>()?; builder = builder.with_features(features); } if let Some(immutable_features) = immutable_features { - let immutable_features = immutable_features - .into_iter() - .map(Feature::try_from) - .collect::, Error>>()?; builder = builder.with_immutable_features(immutable_features); } @@ -650,10 +624,7 @@ mod tests { use super::*; use crate::types::{ block::{ - output::{ - dto::{OutputBuilderAmountDto, OutputDto}, - FoundryId, SimpleTokenScheme, TokenId, - }, + output::{dto::OutputDto, FoundryId, SimpleTokenScheme, TokenId}, protocol::protocol_parameters, rand::{ address::rand_account_address, @@ -735,13 +706,13 @@ mod tests { let foundry_id = FoundryId::build(&rand_account_address(), 0, SimpleTokenScheme::KIND); let output_split = NftOutput::try_from_dtos( - OutputBuilderAmountDto::Amount(output.amount().to_string()), + OutputBuilderAmount::Amount(output.amount()), output.mana(), Some(output.native_tokens().to_vec()), output.nft_id(), output.unlock_conditions().iter().map(Into::into).collect(), - Some(output.features().iter().map(Into::into).collect()), - Some(output.immutable_features().iter().map(Into::into).collect()), + Some(output.features().to_vec()), + Some(output.immutable_features().to_vec()), &protocol_parameters, ) .unwrap(); @@ -749,13 +720,13 @@ mod tests { let test_split_dto = |builder: NftOutputBuilder| { let output_split = NftOutput::try_from_dtos( - (&builder.amount).into(), + builder.amount, builder.mana, Some(builder.native_tokens.iter().copied().collect()), &builder.nft_id, builder.unlock_conditions.iter().map(Into::into).collect(), - Some(builder.features.iter().map(Into::into).collect()), - Some(builder.immutable_features.iter().map(Into::into).collect()), + Some(builder.features.iter().cloned().collect()), + Some(builder.immutable_features.iter().cloned().collect()), &protocol_parameters, ) .unwrap(); diff --git a/sdk/src/types/block/output/token_scheme/mod.rs b/sdk/src/types/block/output/token_scheme/mod.rs index 0c081b1194..186a866749 100644 --- a/sdk/src/types/block/output/token_scheme/mod.rs +++ b/sdk/src/types/block/output/token_scheme/mod.rs @@ -10,6 +10,7 @@ use crate::types::block::Error; #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, derive_more::From, packable::Packable)] #[packable(unpack_error = Error)] #[packable(tag_type = u8, with_error = Error::InvalidTokenSchemeKind)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] pub enum TokenScheme { /// #[packable(tag = SimpleTokenScheme::KIND)] @@ -32,37 +33,3 @@ impl TokenScheme { } } } - -pub(crate) mod dto { - use derive_more::From; - use serde::{Deserialize, Serialize}; - - pub use super::simple::dto::SimpleTokenSchemeDto; - use super::*; - use crate::types::block::Error; - - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, From)] - #[serde(untagged)] - pub enum TokenSchemeDto { - /// A simple token scheme. - Simple(SimpleTokenSchemeDto), - } - - impl From<&TokenScheme> for TokenSchemeDto { - fn from(value: &TokenScheme) -> Self { - match value { - TokenScheme::Simple(v) => Self::Simple(v.into()), - } - } - } - - impl TryFrom for TokenScheme { - type Error = Error; - - fn try_from(value: TokenSchemeDto) -> Result { - Ok(match value { - TokenSchemeDto::Simple(v) => Self::Simple(v.try_into()?), - }) - } - } -} diff --git a/sdk/src/types/block/output/token_scheme/simple.rs b/sdk/src/types/block/output/token_scheme/simple.rs index 15d77cdeb1..0c186ea96e 100644 --- a/sdk/src/types/block/output/token_scheme/simple.rs +++ b/sdk/src/types/block/output/token_scheme/simple.rs @@ -116,7 +116,9 @@ fn verify_supply(minted_tokens: &U256, melted_tokens: &U256, maximum_supply: &U2 Ok(()) } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; use super::*; @@ -125,15 +127,15 @@ pub(crate) mod dto { /// Describes a foundry output that is controlled by an account. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] - pub struct SimpleTokenSchemeDto { + struct SimpleTokenSchemeDto { #[serde(rename = "type")] - pub kind: u8, + kind: u8, // Amount of tokens minted by a foundry. - pub minted_tokens: U256, + minted_tokens: U256, // Amount of tokens melted by a foundry. - pub melted_tokens: U256, + melted_tokens: U256, // Maximum supply of tokens controlled by a foundry. - pub maximum_supply: U256, + maximum_supply: U256, } impl From<&SimpleTokenScheme> for SimpleTokenSchemeDto { @@ -154,4 +156,6 @@ pub(crate) mod dto { Self::new(value.minted_tokens, value.melted_tokens, value.maximum_supply) } } + + impl_serde_typed_dto!(SimpleTokenScheme, SimpleTokenSchemeDto, "simple token scheme"); } diff --git a/sdk/src/types/block/output/unlock_condition/address.rs b/sdk/src/types/block/output/unlock_condition/address.rs index 2fd42cc7b1..1017ba58a6 100644 --- a/sdk/src/types/block/output/unlock_condition/address.rs +++ b/sdk/src/types/block/output/unlock_condition/address.rs @@ -26,35 +26,38 @@ impl AddressUnlockCondition { } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; use super::*; - use crate::types::block::{address::dto::AddressDto, Error}; - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct AddressUnlockConditionDto { + #[derive(Serialize, Deserialize)] + struct AddressUnlockConditionDto { #[serde(rename = "type")] - pub kind: u8, - pub address: AddressDto, + kind: u8, + address: Address, } impl From<&AddressUnlockCondition> for AddressUnlockConditionDto { fn from(value: &AddressUnlockCondition) -> Self { Self { kind: AddressUnlockCondition::KIND, - address: value.address().into(), + address: value.0, } } } - impl TryFrom for AddressUnlockCondition { - type Error = Error; - - fn try_from(value: AddressUnlockConditionDto) -> Result { - Ok(Self::new( - Address::try_from(value.address).map_err(|_e| Error::InvalidField("addressUnlockCondition"))?, - )) + impl From for AddressUnlockCondition { + fn from(value: AddressUnlockConditionDto) -> Self { + Self(value.address) } } + + impl_serde_typed_dto!( + AddressUnlockCondition, + AddressUnlockConditionDto, + "address unlock condition" + ); } diff --git a/sdk/src/types/block/output/unlock_condition/expiration.rs b/sdk/src/types/block/output/unlock_condition/expiration.rs index be42f773dd..bc9711937b 100644 --- a/sdk/src/types/block/output/unlock_condition/expiration.rs +++ b/sdk/src/types/block/output/unlock_condition/expiration.rs @@ -64,27 +64,29 @@ fn verify_timestamp(timestamp: &u32, _: &()) -> Result<(), E } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; use super::*; - use crate::types::block::{address::dto::AddressDto, Error}; + use crate::types::block::Error; - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] - pub struct ExpirationUnlockConditionDto { + struct ExpirationUnlockConditionDto { #[serde(rename = "type")] - pub kind: u8, - pub return_address: AddressDto, + kind: u8, + return_address: Address, #[serde(rename = "unixTime")] - pub timestamp: u32, + timestamp: u32, } impl From<&ExpirationUnlockCondition> for ExpirationUnlockConditionDto { fn from(value: &ExpirationUnlockCondition) -> Self { Self { kind: ExpirationUnlockCondition::KIND, - return_address: value.return_address().into(), + return_address: *value.return_address(), timestamp: value.timestamp(), } } @@ -94,12 +96,14 @@ pub(crate) mod dto { type Error = Error; fn try_from(value: ExpirationUnlockConditionDto) -> Result { - Self::new( - Address::try_from(value.return_address) - .map_err(|_e| Error::InvalidField("expirationUnlockCondition"))?, - value.timestamp, - ) - .map_err(|_| Error::InvalidField("expirationUnlockCondition")) + Self::new(value.return_address, value.timestamp) + .map_err(|_| Error::InvalidField("expirationUnlockCondition")) } } + + impl_serde_typed_dto!( + ExpirationUnlockCondition, + ExpirationUnlockConditionDto, + "expiration unlock condition" + ); } diff --git a/sdk/src/types/block/output/unlock_condition/governor_address.rs b/sdk/src/types/block/output/unlock_condition/governor_address.rs index 9553a9a864..4abc7d6a56 100644 --- a/sdk/src/types/block/output/unlock_condition/governor_address.rs +++ b/sdk/src/types/block/output/unlock_condition/governor_address.rs @@ -28,35 +28,38 @@ impl GovernorAddressUnlockCondition { } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; use super::*; - use crate::types::block::{address::dto::AddressDto, Error}; - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct GovernorAddressUnlockConditionDto { + #[derive(Serialize, Deserialize)] + struct GovernorAddressUnlockConditionDto { #[serde(rename = "type")] - pub kind: u8, - pub address: AddressDto, + kind: u8, + address: Address, } impl From<&GovernorAddressUnlockCondition> for GovernorAddressUnlockConditionDto { fn from(value: &GovernorAddressUnlockCondition) -> Self { Self { kind: GovernorAddressUnlockCondition::KIND, - address: value.address().into(), + address: value.0, } } } - impl TryFrom for GovernorAddressUnlockCondition { - type Error = Error; - - fn try_from(value: GovernorAddressUnlockConditionDto) -> Result { - Ok(Self::new( - Address::try_from(value.address).map_err(|_e| Error::InvalidField("governorAddressUnlockCondition"))?, - )) + impl From for GovernorAddressUnlockCondition { + fn from(value: GovernorAddressUnlockConditionDto) -> Self { + Self(value.address) } } + + impl_serde_typed_dto!( + GovernorAddressUnlockCondition, + GovernorAddressUnlockConditionDto, + "governor address unlock condition" + ); } diff --git a/sdk/src/types/block/output/unlock_condition/immutable_account_address.rs b/sdk/src/types/block/output/unlock_condition/immutable_account_address.rs index 2233b71c4d..4bc11ee4da 100644 --- a/sdk/src/types/block/output/unlock_condition/immutable_account_address.rs +++ b/sdk/src/types/block/output/unlock_condition/immutable_account_address.rs @@ -3,14 +3,11 @@ use derive_more::From; -use crate::types::block::{ - address::{AccountAddress, Address}, - Error, -}; +use crate::types::block::address::AccountAddress; /// Defines the permanent [`AccountAddress`] that owns this output. #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] -pub struct ImmutableAccountAddressUnlockCondition(#[packable(verify_with = verify_account_address)] Address); +pub struct ImmutableAccountAddressUnlockCondition(AccountAddress); impl ImmutableAccountAddressUnlockCondition { /// The [`UnlockCondition`](crate::types::block::output::UnlockCondition) kind of an @@ -20,70 +17,47 @@ impl ImmutableAccountAddressUnlockCondition { /// Creates a new [`ImmutableAccountAddressUnlockCondition`]. #[inline(always)] pub fn new(address: impl Into) -> Self { - Self(Address::Account(address.into())) - } - - /// Returns the address of an [`ImmutableAccountAddressUnlockCondition`]. - #[inline(always)] - pub fn address(&self) -> &Address { - // An ImmutableAccountAddressUnlockCondition must have an AccountAddress. - // It has already been validated at construction that the address is an `AccountAddress`. - debug_assert!(&self.0.is_account()); - &self.0 + Self(address.into()) } /// Returns the account address of an [`ImmutableAccountAddressUnlockCondition`]. - pub fn account_address(&self) -> &AccountAddress { - // It has already been validated at construction that the address is an `AccountAddress`. - self.0.as_account() + pub fn address(&self) -> &AccountAddress { + &self.0 } } -fn verify_account_address(address: &Address, _: &()) -> Result<(), Error> { - if VERIFY && !address.is_account() { - Err(Error::InvalidAddressKind(address.kind())) - } else { - Ok(()) - } -} +mod dto { + use alloc::format; -pub(crate) mod dto { use serde::{Deserialize, Serialize}; use super::*; - use crate::types::block::{address::dto::AddressDto, Error}; - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct ImmutableAccountAddressUnlockConditionDto { + #[derive(Serialize, Deserialize)] + struct ImmutableAccountAddressUnlockConditionDto { #[serde(rename = "type")] - pub kind: u8, - pub address: AddressDto, + kind: u8, + address: AccountAddress, } impl From<&ImmutableAccountAddressUnlockCondition> for ImmutableAccountAddressUnlockConditionDto { fn from(value: &ImmutableAccountAddressUnlockCondition) -> Self { Self { kind: ImmutableAccountAddressUnlockCondition::KIND, - address: value.address().into(), + address: value.0, } } } - impl TryFrom for ImmutableAccountAddressUnlockCondition { - type Error = Error; - - fn try_from(value: ImmutableAccountAddressUnlockConditionDto) -> Result { - let address: Address = value - .address - .try_into() - .map_err(|_e| Error::InvalidField("immutableAccountAddressUnlockCondition"))?; - - // An ImmutableAccountAddressUnlockCondition must have an AccountAddress. - if let Address::Account(account_address) = address { - Ok(Self::new(account_address)) - } else { - Err(Error::InvalidField("immutableAccountAddressUnlockCondition")) - } + impl From for ImmutableAccountAddressUnlockCondition { + fn from(value: ImmutableAccountAddressUnlockConditionDto) -> Self { + Self(value.address) } } + + impl_serde_typed_dto!( + ImmutableAccountAddressUnlockCondition, + ImmutableAccountAddressUnlockConditionDto, + "immutable account adress unlock condition" + ); } diff --git a/sdk/src/types/block/output/unlock_condition/mod.rs b/sdk/src/types/block/output/unlock_condition/mod.rs index 219f686c19..c5bedf261f 100644 --- a/sdk/src/types/block/output/unlock_condition/mod.rs +++ b/sdk/src/types/block/output/unlock_condition/mod.rs @@ -498,162 +498,41 @@ mod test { } pub mod dto { - use alloc::format; - - use serde::{Deserialize, Serialize, Serializer}; - use serde_json::Value; - - pub use self::{ - address::dto::AddressUnlockConditionDto, expiration::dto::ExpirationUnlockConditionDto, - governor_address::dto::GovernorAddressUnlockConditionDto, - immutable_account_address::dto::ImmutableAccountAddressUnlockConditionDto, - state_controller_address::dto::StateControllerAddressUnlockConditionDto, - storage_deposit_return::dto::StorageDepositReturnUnlockConditionDto, timelock::dto::TimelockUnlockConditionDto, - }; + use serde::{Deserialize, Serialize}; + + pub use self::storage_deposit_return::dto::StorageDepositReturnUnlockConditionDto; use super::*; use crate::types::{block::Error, TryFromDto}; - #[derive(Clone, Debug, Eq, PartialEq, From)] + #[derive(Clone, Debug, Eq, PartialEq, From, Serialize, Deserialize)] + #[serde(untagged)] pub enum UnlockConditionDto { /// An address unlock condition. - Address(AddressUnlockConditionDto), + Address(AddressUnlockCondition), /// A storage deposit return unlock condition. StorageDepositReturn(StorageDepositReturnUnlockConditionDto), /// A timelock unlock condition. - Timelock(TimelockUnlockConditionDto), + Timelock(TimelockUnlockCondition), /// An expiration unlock condition. - Expiration(ExpirationUnlockConditionDto), + Expiration(ExpirationUnlockCondition), /// A state controller address unlock condition. - StateControllerAddress(StateControllerAddressUnlockConditionDto), + StateControllerAddress(StateControllerAddressUnlockCondition), /// A governor address unlock condition. - GovernorAddress(GovernorAddressUnlockConditionDto), + GovernorAddress(GovernorAddressUnlockCondition), /// An immutable account address unlock condition. - ImmutableAccountAddress(ImmutableAccountAddressUnlockConditionDto), - } - - impl<'de> Deserialize<'de> for UnlockConditionDto { - fn deserialize>(d: D) -> Result { - let value = Value::deserialize(d)?; - Ok( - match value - .get("type") - .and_then(Value::as_u64) - .ok_or_else(|| serde::de::Error::custom("invalid unlock condition type"))? - as u8 - { - AddressUnlockCondition::KIND => { - Self::Address(AddressUnlockConditionDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!("cannot deserialize address unlock condition: {e}")) - })?) - } - StorageDepositReturnUnlockCondition::KIND => Self::StorageDepositReturn( - StorageDepositReturnUnlockConditionDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!( - "cannot deserialize storage deposit unlock condition: {e}" - )) - })?, - ), - TimelockUnlockCondition::KIND => { - Self::Timelock(TimelockUnlockConditionDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!("cannot deserialize timelock unlock condition: {e}")) - })?) - } - ExpirationUnlockCondition::KIND => { - Self::Expiration(ExpirationUnlockConditionDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!("cannot deserialize expiration unlock condition: {e}")) - })?) - } - StateControllerAddressUnlockCondition::KIND => Self::StateControllerAddress( - StateControllerAddressUnlockConditionDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!( - "cannot deserialize state controller unlock condition: {e}" - )) - })?, - ), - GovernorAddressUnlockCondition::KIND => { - Self::GovernorAddress(GovernorAddressUnlockConditionDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!("cannot deserialize governor unlock condition: {e}")) - })?) - } - ImmutableAccountAddressUnlockCondition::KIND => Self::ImmutableAccountAddress( - ImmutableAccountAddressUnlockConditionDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!( - "cannot deserialize immutable account address unlock condition: {e}" - )) - })?, - ), - _ => return Err(serde::de::Error::custom("invalid unlock condition type")), - }, - ) - } - } - - impl Serialize for UnlockConditionDto { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - #[derive(Serialize)] - #[serde(untagged)] - enum UnlockConditionDto_<'a> { - T1(&'a AddressUnlockConditionDto), - T2(&'a StorageDepositReturnUnlockConditionDto), - T3(&'a TimelockUnlockConditionDto), - T4(&'a ExpirationUnlockConditionDto), - T5(&'a StateControllerAddressUnlockConditionDto), - T6(&'a GovernorAddressUnlockConditionDto), - T7(&'a ImmutableAccountAddressUnlockConditionDto), - } - #[derive(Serialize)] - struct TypedUnlockCondition<'a> { - #[serde(flatten)] - unlock_condition: UnlockConditionDto_<'a>, - } - let unlock_condition = match self { - Self::Address(o) => TypedUnlockCondition { - unlock_condition: UnlockConditionDto_::T1(o), - }, - Self::StorageDepositReturn(o) => TypedUnlockCondition { - unlock_condition: UnlockConditionDto_::T2(o), - }, - Self::Timelock(o) => TypedUnlockCondition { - unlock_condition: UnlockConditionDto_::T3(o), - }, - Self::Expiration(o) => TypedUnlockCondition { - unlock_condition: UnlockConditionDto_::T4(o), - }, - Self::StateControllerAddress(o) => TypedUnlockCondition { - unlock_condition: UnlockConditionDto_::T5(o), - }, - Self::GovernorAddress(o) => TypedUnlockCondition { - unlock_condition: UnlockConditionDto_::T6(o), - }, - Self::ImmutableAccountAddress(o) => TypedUnlockCondition { - unlock_condition: UnlockConditionDto_::T7(o), - }, - }; - unlock_condition.serialize(serializer) - } + ImmutableAccountAddress(ImmutableAccountAddressUnlockCondition), } impl From<&UnlockCondition> for UnlockConditionDto { fn from(value: &UnlockCondition) -> Self { match value { - UnlockCondition::Address(v) => Self::Address(AddressUnlockConditionDto::from(v)), - UnlockCondition::StorageDepositReturn(v) => { - Self::StorageDepositReturn(StorageDepositReturnUnlockConditionDto::from(v)) - } - UnlockCondition::Timelock(v) => Self::Timelock(TimelockUnlockConditionDto::from(v)), - UnlockCondition::Expiration(v) => Self::Expiration(ExpirationUnlockConditionDto::from(v)), - UnlockCondition::StateControllerAddress(v) => { - Self::StateControllerAddress(StateControllerAddressUnlockConditionDto::from(v)) - } - UnlockCondition::GovernorAddress(v) => { - Self::GovernorAddress(GovernorAddressUnlockConditionDto::from(v)) - } - UnlockCondition::ImmutableAccountAddress(v) => { - Self::ImmutableAccountAddress(ImmutableAccountAddressUnlockConditionDto::from(v)) - } + UnlockCondition::Address(v) => Self::Address(*v), + UnlockCondition::StorageDepositReturn(v) => Self::StorageDepositReturn(v.into()), + UnlockCondition::Timelock(v) => Self::Timelock(*v), + UnlockCondition::Expiration(v) => Self::Expiration(*v), + UnlockCondition::StateControllerAddress(v) => Self::StateControllerAddress(*v), + UnlockCondition::GovernorAddress(v) => Self::GovernorAddress(*v), + UnlockCondition::ImmutableAccountAddress(v) => Self::ImmutableAccountAddress(*v), } } } @@ -664,21 +543,15 @@ pub mod dto { fn try_from_dto_with_params_inner(dto: Self::Dto, params: ValidationParams<'_>) -> Result { Ok(match dto { - UnlockConditionDto::Address(v) => Self::Address(AddressUnlockCondition::try_from(v)?), + UnlockConditionDto::Address(v) => Self::Address(v), UnlockConditionDto::StorageDepositReturn(v) => Self::StorageDepositReturn( StorageDepositReturnUnlockCondition::try_from_dto_with_params_inner(v, params)?, ), - UnlockConditionDto::Timelock(v) => Self::Timelock(TimelockUnlockCondition::try_from(v)?), - UnlockConditionDto::Expiration(v) => Self::Expiration(ExpirationUnlockCondition::try_from(v)?), - UnlockConditionDto::StateControllerAddress(v) => { - Self::StateControllerAddress(StateControllerAddressUnlockCondition::try_from(v)?) - } - UnlockConditionDto::GovernorAddress(v) => { - Self::GovernorAddress(GovernorAddressUnlockCondition::try_from(v)?) - } - UnlockConditionDto::ImmutableAccountAddress(v) => { - Self::ImmutableAccountAddress(ImmutableAccountAddressUnlockCondition::try_from(v)?) - } + UnlockConditionDto::Timelock(v) => Self::Timelock(v), + UnlockConditionDto::Expiration(v) => Self::Expiration(v), + UnlockConditionDto::StateControllerAddress(v) => Self::StateControllerAddress(v), + UnlockConditionDto::GovernorAddress(v) => Self::GovernorAddress(v), + UnlockConditionDto::ImmutableAccountAddress(v) => Self::ImmutableAccountAddress(v), }) } } diff --git a/sdk/src/types/block/output/unlock_condition/state_controller_address.rs b/sdk/src/types/block/output/unlock_condition/state_controller_address.rs index d233595ec4..c820a94214 100644 --- a/sdk/src/types/block/output/unlock_condition/state_controller_address.rs +++ b/sdk/src/types/block/output/unlock_condition/state_controller_address.rs @@ -28,17 +28,18 @@ impl StateControllerAddressUnlockCondition { } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; use super::*; - use crate::types::block::{address::dto::AddressDto, Error}; - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct StateControllerAddressUnlockConditionDto { + #[derive(Serialize, Deserialize)] + struct StateControllerAddressUnlockConditionDto { #[serde(rename = "type")] - pub kind: u8, - pub address: AddressDto, + kind: u8, + address: Address, } impl From<&StateControllerAddressUnlockCondition> for StateControllerAddressUnlockConditionDto { @@ -50,13 +51,15 @@ pub(crate) mod dto { } } - impl TryFrom for StateControllerAddressUnlockCondition { - type Error = Error; - - fn try_from(value: StateControllerAddressUnlockConditionDto) -> Result { - Ok(Self::new(Address::try_from(value.address).map_err(|_e| { - Error::InvalidField("stateControllerAddressUnlockCondition") - })?)) + impl From for StateControllerAddressUnlockCondition { + fn from(value: StateControllerAddressUnlockConditionDto) -> Self { + Self(value.address) } } + + impl_serde_typed_dto!( + StateControllerAddressUnlockCondition, + StateControllerAddressUnlockConditionDto, + "state controller address unlock condition" + ); } diff --git a/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs b/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs index 81aeb80956..5a7c507515 100644 --- a/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs +++ b/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs @@ -61,32 +61,48 @@ fn verify_amount_packable( verify_amount::(amount, &protocol_parameters.token_supply()) } -pub(crate) mod dto { - use alloc::string::{String, ToString}; +pub(super) mod dto { + use alloc::format; use serde::{Deserialize, Serialize}; use super::*; - use crate::types::{ - block::{address::dto::AddressDto, Error}, - TryFromDto, + use crate::{ + types::{block::Error, TryFromDto}, + utils::serde::string, }; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct StorageDepositReturnUnlockConditionDto { - #[serde(rename = "type")] + #[serde(rename = "type", deserialize_with = "deserialize_kind")] pub kind: u8, - pub return_address: AddressDto, - pub amount: String, + pub return_address: Address, + #[serde(with = "string")] + pub amount: u64, + } + + fn deserialize_kind<'de, D>(d: D) -> Result + where + D: serde::Deserializer<'de>, + { + let kind = u8::deserialize(d)?; + if kind != StorageDepositReturnUnlockCondition::KIND { + return Err(serde::de::Error::custom(format!( + "invalid storage deposit return unlock condition type: expected {}, found {}", + StorageDepositReturnUnlockCondition::KIND, + kind + ))); + } + Ok(kind) } impl From<&StorageDepositReturnUnlockCondition> for StorageDepositReturnUnlockConditionDto { fn from(value: &StorageDepositReturnUnlockCondition) -> Self { Self { kind: StorageDepositReturnUnlockCondition::KIND, - return_address: AddressDto::from(value.return_address()), - amount: value.amount().to_string(), + return_address: value.return_address, + amount: value.amount, } } } @@ -97,17 +113,22 @@ pub(crate) mod dto { fn try_from_dto_with_params_inner(dto: Self::Dto, params: ValidationParams<'_>) -> Result { Ok(if let Some(token_supply) = params.token_supply() { - Self::new( - Address::try_from(dto.return_address)?, - dto.amount.parse::().map_err(|_| Error::InvalidField("amount"))?, - token_supply, - )? + Self::new(dto.return_address, dto.amount, token_supply)? } else { Self { - return_address: Address::try_from(dto.return_address)?, - amount: dto.amount.parse::().map_err(|_| Error::InvalidField("amount"))?, + return_address: dto.return_address, + amount: dto.amount, } }) } } + + impl Serialize for StorageDepositReturnUnlockCondition { + fn serialize(&self, s: S) -> Result + where + S: serde::Serializer, + { + StorageDepositReturnUnlockConditionDto::from(self).serialize(s) + } + } } diff --git a/sdk/src/types/block/output/unlock_condition/timelock.rs b/sdk/src/types/block/output/unlock_condition/timelock.rs index 983f4bc310..42a62e981f 100644 --- a/sdk/src/types/block/output/unlock_condition/timelock.rs +++ b/sdk/src/types/block/output/unlock_condition/timelock.rs @@ -38,18 +38,20 @@ fn verify_timestamp(timestamp: &u32, _: &()) -> Result<(), E } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; use super::*; use crate::types::block::Error; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct TimelockUnlockConditionDto { + struct TimelockUnlockConditionDto { #[serde(rename = "type")] - pub kind: u8, + kind: u8, #[serde(rename = "unixTime")] - pub timestamp: u32, + timestamp: u32, } impl From<&TimelockUnlockCondition> for TimelockUnlockConditionDto { @@ -68,4 +70,10 @@ pub(crate) mod dto { Self::new(value.timestamp).map_err(|_| Error::InvalidField("timelockUnlockCondition")) } } + + impl_serde_typed_dto!( + TimelockUnlockCondition, + TimelockUnlockConditionDto, + "timelock unlock condition" + ); } diff --git a/sdk/src/types/block/payload/mod.rs b/sdk/src/types/block/payload/mod.rs index 76236a0777..b962f9359e 100644 --- a/sdk/src/types/block/payload/mod.rs +++ b/sdk/src/types/block/payload/mod.rs @@ -175,8 +175,8 @@ impl Packable for OptionalPayload { pub mod dto { use serde::{Deserialize, Serialize}; + pub use super::transaction::dto::TransactionPayloadDto; use super::*; - pub use super::{tagged_data::dto::TaggedDataPayloadDto, transaction::dto::TransactionPayloadDto}; use crate::types::{block::Error, TryFromDto, ValidationParams}; /// Describes all the different payload types. @@ -184,7 +184,7 @@ pub mod dto { #[serde(untagged)] pub enum PayloadDto { Transaction(Box), - TaggedData(Box), + TaggedData(Box), } impl From for PayloadDto { @@ -193,8 +193,8 @@ pub mod dto { } } - impl From for PayloadDto { - fn from(payload: TaggedDataPayloadDto) -> Self { + impl From for PayloadDto { + fn from(payload: TaggedDataPayload) -> Self { Self::TaggedData(Box::new(payload)) } } @@ -203,7 +203,7 @@ pub mod dto { fn from(value: &Payload) -> Self { match value { Payload::Transaction(p) => Self::Transaction(Box::new(TransactionPayloadDto::from(p.as_ref()))), - Payload::TaggedData(p) => Self::TaggedData(Box::new(TaggedDataPayloadDto::from(p.as_ref()))), + Payload::TaggedData(p) => Self::TaggedData(p.clone()), } } } @@ -217,7 +217,7 @@ pub mod dto { PayloadDto::Transaction(p) => { Self::from(TransactionPayload::try_from_dto_with_params_inner(*p, params)?) } - PayloadDto::TaggedData(p) => Self::from(TaggedDataPayload::try_from(*p)?), + PayloadDto::TaggedData(p) => Self::from(*p), }) } } diff --git a/sdk/src/types/block/payload/tagged_data/mod.rs b/sdk/src/types/block/payload/tagged_data/mod.rs index 0564bbd4cd..6c06c49498 100644 --- a/sdk/src/types/block/payload/tagged_data/mod.rs +++ b/sdk/src/types/block/payload/tagged_data/mod.rs @@ -67,7 +67,9 @@ impl core::fmt::Debug for TaggedDataPayload { } } -pub mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; use super::*; @@ -75,13 +77,13 @@ pub mod dto { /// The payload type to define a tagged data payload. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct TaggedDataPayloadDto { + struct TaggedDataPayloadDto { #[serde(rename = "type")] - pub kind: u32, + kind: u32, #[serde(skip_serializing_if = "<[_]>::is_empty", default, with = "prefix_hex_bytes")] - pub tag: Box<[u8]>, + tag: Box<[u8]>, #[serde(skip_serializing_if = "<[_]>::is_empty", default, with = "prefix_hex_bytes")] - pub data: Box<[u8]>, + data: Box<[u8]>, } impl From<&TaggedDataPayload> for TaggedDataPayloadDto { @@ -101,4 +103,6 @@ pub mod dto { Self::new(value.tag, value.data) } } + + impl_serde_typed_dto!(TaggedDataPayload, TaggedDataPayloadDto, "tagged data payload"); } diff --git a/sdk/src/types/block/payload/transaction/essence/regular.rs b/sdk/src/types/block/payload/transaction/essence/regular.rs index ddd1ca8fb8..9dbd3e092a 100644 --- a/sdk/src/types/block/payload/transaction/essence/regular.rs +++ b/sdk/src/types/block/payload/transaction/essence/regular.rs @@ -22,11 +22,11 @@ use crate::types::{ #[must_use] pub struct RegularTransactionEssenceBuilder { network_id: u64, - creation_time: Option, inputs: Vec, inputs_commitment: InputsCommitment, outputs: Vec, payload: OptionalPayload, + creation_time: Option, } impl RegularTransactionEssenceBuilder { @@ -34,11 +34,11 @@ impl RegularTransactionEssenceBuilder { pub fn new(network_id: u64, inputs_commitment: InputsCommitment) -> Self { Self { network_id, - creation_time: None, inputs: Vec::new(), inputs_commitment, outputs: Vec::new(), payload: OptionalPayload::default(), + creation_time: None, } } @@ -309,18 +309,18 @@ fn verify_payload_packable( } pub(crate) mod dto { - use alloc::{ - boxed::Box, - string::{String, ToString}, - }; + use alloc::string::{String, ToString}; use core::str::FromStr; use serde::{Deserialize, Serialize}; use super::*; - use crate::types::{ - block::{input::dto::InputDto, output::dto::OutputDto, payload::dto::PayloadDto, Error}, - TryFromDto, + use crate::{ + types::{ + block::{output::dto::OutputDto, payload::dto::PayloadDto, Error}, + TryFromDto, + }, + utils::serde::string, }; /// Describes the essence data making up a transaction by defining its inputs and outputs and an optional payload. @@ -330,8 +330,9 @@ pub(crate) mod dto { #[serde(rename = "type")] pub kind: u8, pub network_id: String, + #[serde(with = "string")] pub creation_time: u64, - pub inputs: Vec, + pub inputs: Vec, pub inputs_commitment: String, pub outputs: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -344,11 +345,11 @@ pub(crate) mod dto { kind: RegularTransactionEssence::KIND, network_id: value.network_id().to_string(), creation_time: value.creation_time(), - inputs: value.inputs().iter().map(Into::into).collect::>(), + inputs: value.inputs().to_vec(), inputs_commitment: value.inputs_commitment().to_string(), outputs: value.outputs().iter().map(Into::into).collect::>(), payload: match value.payload() { - Some(Payload::TaggedData(i)) => Some(PayloadDto::TaggedData(Box::new(i.as_ref().into()))), + Some(p @ Payload::TaggedData(_)) => Some(p.into()), Some(_) => unimplemented!(), None => None, }, @@ -365,11 +366,6 @@ pub(crate) mod dto { .network_id .parse::() .map_err(|_| Error::InvalidField("network_id"))?; - let inputs = dto - .inputs - .into_iter() - .map(TryInto::try_into) - .collect::, Error>>()?; let outputs = dto .outputs .into_iter() @@ -378,12 +374,12 @@ pub(crate) mod dto { let mut builder = Self::builder(network_id, InputsCommitment::from_str(&dto.inputs_commitment)?) .with_creation_time(dto.creation_time) - .with_inputs(inputs) + .with_inputs(dto.inputs) .with_outputs(outputs); builder = if let Some(p) = dto.payload { if let PayloadDto::TaggedData(i) = p { - builder.with_payload(Payload::TaggedData(Box::new((*i).try_into()?))) + builder.with_payload(*i) } else { return Err(Error::InvalidField("payload")); } diff --git a/sdk/src/types/block/payload/transaction/mod.rs b/sdk/src/types/block/payload/transaction/mod.rs index bfd22d9189..885bc2bf30 100644 --- a/sdk/src/types/block/payload/transaction/mod.rs +++ b/sdk/src/types/block/payload/transaction/mod.rs @@ -100,14 +100,14 @@ fn verify_essence_unlocks(essence: &TransactionEssence, unlocks: &Unlocks) -> Re } pub mod dto { - use alloc::{boxed::Box, vec::Vec}; + use alloc::vec::Vec; use serde::{Deserialize, Serialize}; pub use super::essence::dto::{RegularTransactionEssenceDto, TransactionEssenceDto}; use super::*; use crate::types::{ - block::{unlock::dto::UnlockDto, Error}, + block::{unlock::Unlock, Error}, TryFromDto, }; @@ -117,7 +117,7 @@ pub mod dto { #[serde(rename = "type")] pub kind: u32, pub essence: TransactionEssenceDto, - pub unlocks: Vec, + pub unlocks: Vec, } impl From<&TransactionPayload> for TransactionPayloadDto { @@ -125,7 +125,7 @@ pub mod dto { Self { kind: TransactionPayload::KIND, essence: value.essence().into(), - unlocks: value.unlocks().iter().map(Into::into).collect::>(), + unlocks: value.unlocks().to_vec(), } } } @@ -137,12 +137,7 @@ pub mod dto { fn try_from_dto_with_params_inner(dto: Self::Dto, params: ValidationParams<'_>) -> Result { Self::new( TransactionEssence::try_from_dto_with_params_inner(dto.essence, params)?, - Unlocks::new( - dto.unlocks - .into_iter() - .map(TryInto::try_into) - .collect::, _>>()?, - )?, + Unlocks::new(dto.unlocks)?, ) } } diff --git a/sdk/src/types/block/public_key/ed25519.rs b/sdk/src/types/block/public_key/ed25519.rs index 46aeeb0e02..3928234766 100644 --- a/sdk/src/types/block/public_key/ed25519.rs +++ b/sdk/src/types/block/public_key/ed25519.rs @@ -13,7 +13,7 @@ use packable::{ use crate::types::block::Error; /// An Ed25519 public key. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deref, AsRef, From)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deref, AsRef, From)] #[as_ref(forward)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Ed25519PublicKey(PublicKey); diff --git a/sdk/src/types/block/signature/ed25519.rs b/sdk/src/types/block/signature/ed25519.rs index a49085bbd1..959a5ca5f0 100644 --- a/sdk/src/types/block/signature/ed25519.rs +++ b/sdk/src/types/block/signature/ed25519.rs @@ -17,7 +17,7 @@ use packable::{ use crate::types::block::{address::Ed25519Address, public_key::Ed25519PublicKey, Error}; /// An Ed25519 signature. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Ed25519Signature { public_key: Ed25519PublicKey, signature: Signature, @@ -63,7 +63,7 @@ impl Ed25519Signature { /// Verifies the [`Ed25519Signature`] for a message against an [`Ed25519Address`]. pub fn is_valid(&self, message: &[u8], address: &Ed25519Address) -> Result<(), Error> { - let signature_address: [u8; Self::PUBLIC_KEY_LENGTH] = Blake2b256::digest(&self.public_key).into(); + let signature_address: [u8; Self::PUBLIC_KEY_LENGTH] = Blake2b256::digest(self.public_key).into(); if address.deref() != &signature_address { return Err(Error::SignaturePublicKeyMismatch { @@ -127,8 +127,8 @@ impl Packable for Ed25519Signature { } } -pub(crate) mod dto { - use alloc::string::String; +mod dto { + use alloc::{format, string::String}; use serde::{Deserialize, Serialize}; @@ -136,13 +136,13 @@ pub(crate) mod dto { use crate::types::block::Error; /// Defines an Ed25519 signature. - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] - pub struct Ed25519SignatureDto { + struct Ed25519SignatureDto { #[serde(rename = "type")] - pub kind: u8, - pub public_key: String, - pub signature: String, + kind: u8, + public_key: String, + signature: String, } impl From<&Ed25519Signature> for Ed25519SignatureDto { @@ -165,4 +165,6 @@ pub(crate) mod dto { ) } } + + impl_serde_typed_dto!(Ed25519Signature, Ed25519SignatureDto, "ed25519 signature"); } diff --git a/sdk/src/types/block/signature/mod.rs b/sdk/src/types/block/signature/mod.rs index 3268f862b2..fa7e47add4 100644 --- a/sdk/src/types/block/signature/mod.rs +++ b/sdk/src/types/block/signature/mod.rs @@ -3,8 +3,6 @@ mod ed25519; -use alloc::boxed::Box; - use derive_more::From; pub use self::ed25519::Ed25519Signature; @@ -15,19 +13,14 @@ use crate::types::block::Error; /// This is defined as part of the Unspent Transaction Output (UTXO) transaction protocol. /// /// RFC: -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, packable::Packable)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, packable::Packable, From)] #[packable(unpack_error = Error)] #[packable(tag_type = u8, with_error = Error::InvalidSignatureKind)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] pub enum Signature { /// An Ed25519 signature. #[packable(tag = Ed25519Signature::KIND)] - Ed25519(Box), -} - -impl From for Signature { - fn from(value: Ed25519Signature) -> Self { - Self::Ed25519(Box::new(value)) - } + Ed25519(Ed25519Signature), } impl core::fmt::Debug for Signature { @@ -46,36 +39,3 @@ impl Signature { } } } - -pub mod dto { - use serde::{Deserialize, Serialize}; - - pub use super::ed25519::dto::Ed25519SignatureDto; - use super::*; - use crate::types::block::Error; - - /// Describes all the different signature types. - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, From)] - #[serde(untagged)] - pub enum SignatureDto { - Ed25519(Box), - } - - impl From<&Signature> for SignatureDto { - fn from(value: &Signature) -> Self { - match value { - Signature::Ed25519(s) => Self::Ed25519(Box::new(s.as_ref().into())), - } - } - } - - impl TryFrom for Signature { - type Error = Error; - - fn try_from(value: SignatureDto) -> Result { - match value { - SignatureDto::Ed25519(s) => Ok(Self::Ed25519(Box::new((*s).try_into()?))), - } - } - } -} diff --git a/sdk/src/types/block/unlock/account.rs b/sdk/src/types/block/unlock/account.rs index 7c1799f0cb..e496be7d3b 100644 --- a/sdk/src/types/block/unlock/account.rs +++ b/sdk/src/types/block/unlock/account.rs @@ -36,15 +36,38 @@ impl AccountUnlock { } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; + use super::*; + /// Points to the unlock of a consumed account output. - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct AccountUnlockDto { + #[derive(Serialize, Deserialize)] + struct AccountUnlockDto { #[serde(rename = "type")] - pub kind: u8, + kind: u8, #[serde(rename = "reference")] - pub index: u16, + index: u16, + } + + impl From<&AccountUnlock> for AccountUnlockDto { + fn from(value: &AccountUnlock) -> Self { + Self { + kind: AccountUnlock::KIND, + index: value.0.get(), + } + } } + + impl TryFrom for AccountUnlock { + type Error = Error; + + fn try_from(value: AccountUnlockDto) -> Result { + Self::new(value.index) + } + } + + impl_serde_typed_dto!(AccountUnlock, AccountUnlockDto, "account unlock"); } diff --git a/sdk/src/types/block/unlock/mod.rs b/sdk/src/types/block/unlock/mod.rs index 3fd6f0ef7c..232a60ff53 100644 --- a/sdk/src/types/block/unlock/mod.rs +++ b/sdk/src/types/block/unlock/mod.rs @@ -34,10 +34,12 @@ pub(crate) type UnlockIndex = BoundedU16<{ *UNLOCK_INDEX_RANGE.start() }, { *UNL #[derive(Clone, Eq, PartialEq, Hash, From, Packable)] #[packable(unpack_error = Error)] #[packable(tag_type = u8, with_error = Error::InvalidUnlockKind)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] pub enum Unlock { /// A signature unlock. #[packable(tag = SignatureUnlock::KIND)] - Signature(SignatureUnlock), + #[from(ignore)] + Signature(Box), /// A reference unlock. #[packable(tag = ReferenceUnlock::KIND)] Reference(ReferenceUnlock), @@ -49,6 +51,12 @@ pub enum Unlock { Nft(NftUnlock), } +impl From for Unlock { + fn from(value: SignatureUnlock) -> Self { + Self::Signature(value.into()) + } +} + impl core::fmt::Debug for Unlock { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { @@ -136,140 +144,3 @@ fn verify_unlocks(unlocks: &[Unlock], _: &()) -> Result<(), Ok(()) } - -pub mod dto { - use alloc::format; - - use serde::{Deserialize, Serialize, Serializer}; - use serde_json::Value; - - use super::*; - pub use super::{ - account::dto::AccountUnlockDto, nft::dto::NftUnlockDto, reference::dto::ReferenceUnlockDto, - signature::dto::SignatureUnlockDto, - }; - use crate::types::block::{ - signature::{dto::SignatureDto, Ed25519Signature, Signature}, - Error, - }; - - /// Describes all the different unlock types. - #[derive(Clone, Debug, Eq, PartialEq, From)] - pub enum UnlockDto { - Signature(SignatureUnlockDto), - Reference(ReferenceUnlockDto), - Account(AccountUnlockDto), - Nft(NftUnlockDto), - } - - impl From<&Unlock> for UnlockDto { - fn from(value: &Unlock) -> Self { - match value { - Unlock::Signature(signature) => match signature.signature() { - Signature::Ed25519(ed) => Self::Signature(SignatureUnlockDto { - kind: SignatureUnlock::KIND, - signature: SignatureDto::Ed25519(Box::new(ed.as_ref().into())), - }), - }, - Unlock::Reference(r) => Self::Reference(ReferenceUnlockDto { - kind: ReferenceUnlock::KIND, - index: r.index(), - }), - Unlock::Account(a) => Self::Account(AccountUnlockDto { - kind: AccountUnlock::KIND, - index: a.index(), - }), - Unlock::Nft(n) => Self::Nft(NftUnlockDto { - kind: NftUnlock::KIND, - index: n.index(), - }), - } - } - } - - impl TryFrom for Unlock { - type Error = Error; - - fn try_from(value: UnlockDto) -> Result { - match value { - UnlockDto::Signature(s) => match s.signature { - SignatureDto::Ed25519(ed) => Ok(Self::Signature(SignatureUnlock::from(Signature::Ed25519( - Ed25519Signature::try_from(*ed)?.into(), - )))), - }, - UnlockDto::Reference(r) => Ok(Self::Reference(ReferenceUnlock::new(r.index)?)), - UnlockDto::Account(a) => Ok(Self::Account(AccountUnlock::new(a.index)?)), - UnlockDto::Nft(n) => Ok(Self::Nft(NftUnlock::new(n.index)?)), - } - } - } - - impl<'de> Deserialize<'de> for UnlockDto { - fn deserialize>(d: D) -> Result { - let value = Value::deserialize(d)?; - Ok( - match value - .get("type") - .and_then(Value::as_u64) - .ok_or_else(|| serde::de::Error::custom("invalid unlock type"))? as u8 - { - SignatureUnlock::KIND => { - Self::Signature(SignatureUnlockDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!("cannot deserialize signature unlock: {e}")) - })?) - } - ReferenceUnlock::KIND => { - Self::Reference(ReferenceUnlockDto::deserialize(value).map_err(|e| { - serde::de::Error::custom(format!("cannot deserialize reference unlock: {e}")) - })?) - } - AccountUnlock::KIND => Self::Account( - AccountUnlockDto::deserialize(value) - .map_err(|e| serde::de::Error::custom(format!("cannot deserialize account unlock: {e}")))?, - ), - NftUnlock::KIND => Self::Nft( - NftUnlockDto::deserialize(value) - .map_err(|e| serde::de::Error::custom(format!("cannot deserialize NFT unlock: {e}")))?, - ), - _ => return Err(serde::de::Error::custom("invalid unlock type")), - }, - ) - } - } - - impl Serialize for UnlockDto { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - #[derive(Serialize)] - #[serde(untagged)] - enum UnlockDto_<'a> { - T1(&'a SignatureUnlockDto), - T2(&'a ReferenceUnlockDto), - T3(&'a AccountUnlockDto), - T4(&'a NftUnlockDto), - } - #[derive(Serialize)] - struct TypedUnlock<'a> { - #[serde(flatten)] - unlock: UnlockDto_<'a>, - } - let unlock = match self { - Self::Signature(o) => TypedUnlock { - unlock: UnlockDto_::T1(o), - }, - Self::Reference(o) => TypedUnlock { - unlock: UnlockDto_::T2(o), - }, - Self::Account(o) => TypedUnlock { - unlock: UnlockDto_::T3(o), - }, - Self::Nft(o) => TypedUnlock { - unlock: UnlockDto_::T4(o), - }, - }; - unlock.serialize(serializer) - } - } -} diff --git a/sdk/src/types/block/unlock/nft.rs b/sdk/src/types/block/unlock/nft.rs index bc28564784..30ebd89a05 100644 --- a/sdk/src/types/block/unlock/nft.rs +++ b/sdk/src/types/block/unlock/nft.rs @@ -36,15 +36,38 @@ impl NftUnlock { } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; + use super::*; + /// Points to the unlock of a consumed NFT output. - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct NftUnlockDto { + #[derive(Serialize, Deserialize)] + struct NftUnlockDto { #[serde(rename = "type")] - pub kind: u8, + kind: u8, #[serde(rename = "reference")] - pub index: u16, + index: u16, + } + + impl From<&NftUnlock> for NftUnlockDto { + fn from(value: &NftUnlock) -> Self { + Self { + kind: NftUnlock::KIND, + index: value.0.get(), + } + } } + + impl TryFrom for NftUnlock { + type Error = Error; + + fn try_from(value: NftUnlockDto) -> Result { + Self::new(value.index) + } + } + + impl_serde_typed_dto!(NftUnlock, NftUnlockDto, "nft unlock"); } diff --git a/sdk/src/types/block/unlock/reference.rs b/sdk/src/types/block/unlock/reference.rs index 4c693754aa..2d5a52957b 100644 --- a/sdk/src/types/block/unlock/reference.rs +++ b/sdk/src/types/block/unlock/reference.rs @@ -33,16 +33,39 @@ impl ReferenceUnlock { } } -pub(crate) mod dto { +mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; + use super::*; + /// References a previous unlock in order to substitute the duplication of the same unlock data for inputs which /// unlock through the same data. - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] - pub struct ReferenceUnlockDto { + #[derive(Serialize, Deserialize)] + struct ReferenceUnlockDto { #[serde(rename = "type")] - pub kind: u8, + kind: u8, #[serde(rename = "reference")] - pub index: u16, + index: u16, + } + + impl From<&ReferenceUnlock> for ReferenceUnlockDto { + fn from(value: &ReferenceUnlock) -> Self { + Self { + kind: ReferenceUnlock::KIND, + index: value.0.get(), + } + } } + + impl TryFrom for ReferenceUnlock { + type Error = Error; + + fn try_from(value: ReferenceUnlockDto) -> Result { + Self::new(value.index) + } + } + + impl_serde_typed_dto!(ReferenceUnlock, ReferenceUnlockDto, "reference unlock"); } diff --git a/sdk/src/types/block/unlock/signature.rs b/sdk/src/types/block/unlock/signature.rs index 48162b8c55..3047f76113 100644 --- a/sdk/src/types/block/unlock/signature.rs +++ b/sdk/src/types/block/unlock/signature.rs @@ -28,15 +28,34 @@ impl SignatureUnlock { } pub(crate) mod dto { + use alloc::format; + use serde::{Deserialize, Serialize}; - use crate::types::block::signature::dto::SignatureDto; + use super::*; /// Defines an unlock containing signature(s) unlocking input(s). #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct SignatureUnlockDto { #[serde(rename = "type")] pub kind: u8, - pub signature: SignatureDto, + pub signature: Signature, + } + + impl From<&SignatureUnlock> for SignatureUnlockDto { + fn from(value: &SignatureUnlock) -> Self { + Self { + kind: SignatureUnlock::KIND, + signature: value.0, + } + } } + + impl From for SignatureUnlock { + fn from(value: SignatureUnlockDto) -> Self { + Self::new(value.signature) + } + } + + impl_serde_typed_dto!(SignatureUnlock, SignatureUnlockDto, "signature unlock"); } diff --git a/sdk/src/utils/serde.rs b/sdk/src/utils/serde.rs index 6b144e3da3..b6363fef80 100644 --- a/sdk/src/utils/serde.rs +++ b/sdk/src/utils/serde.rs @@ -73,7 +73,9 @@ pub mod prefix_hex_bytes { D: Deserializer<'de>, T: FromHexPrefixed, { - prefix_hex::decode(String::deserialize(deserializer)?).map_err(de::Error::custom) + prefix_hex::decode(String::deserialize(deserializer)?) + .map_err(crate::types::block::Error::Hex) + .map_err(de::Error::custom) } } @@ -129,6 +131,53 @@ pub mod string_prefix { } } +pub mod boxed_slice_prefix { + use alloc::boxed::Box; + + use packable::{bounded::Bounded, prefix::BoxedSlicePrefix}; + use prefix_hex::{FromHexPrefixed, ToHexPrefixed}; + use serde::{de, Deserializer, Serializer}; + + pub fn serialize(value: &BoxedSlicePrefix, serializer: S) -> Result + where + S: Serializer, + for<'a> &'a Box<[T]>: ToHexPrefixed, + { + super::prefix_hex_bytes::serialize(&**value, serializer) + } + + pub fn deserialize<'de, D, T, B: Bounded>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + Box<[T]>: FromHexPrefixed, + >::Error: core::fmt::Display, + { + super::prefix_hex_bytes::deserialize::<_, Box<[T]>>(deserializer)? + .try_into() + .map_err(de::Error::custom) + } +} + +pub mod cow_boxed_slice_prefix { + use alloc::{borrow::Cow, boxed::Box}; + + use packable::{bounded::Bounded, prefix::BoxedSlicePrefix}; + use prefix_hex::FromHexPrefixed; + use serde::Deserializer; + + pub use super::boxed_slice_prefix::serialize; + + pub fn deserialize<'de, 'a, D, B>(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + B: Bounded + Clone, + Box<[u8]>: FromHexPrefixed, + >::Error: core::fmt::Display, + { + Ok(Cow::Owned(super::boxed_slice_prefix::deserialize(deserializer)?)) + } +} + #[cfg(feature = "client")] pub mod bip44 { use crypto::keys::bip44::Bip44; diff --git a/sdk/src/wallet/account/mod.rs b/sdk/src/wallet/account/mod.rs index a02fa0e1be..1d9a06f415 100644 --- a/sdk/src/wallet/account/mod.rs +++ b/sdk/src/wallet/account/mod.rs @@ -48,7 +48,7 @@ pub use self::{ }, }, prepare_output::{Assets, Features, OutputParams, ReturnStrategy, StorageDeposit, Unlocks}, - RemainderValueStrategy, TransactionOptions, TransactionOptionsDto, + RemainderValueStrategy, TransactionOptions, }, }, types::OutputDataDto, @@ -647,8 +647,8 @@ fn serialize() { let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); - let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); + let sig_unlock = Unlock::from(SignatureUnlock::from(Signature::from(signature))); + let ref_unlock = Unlock::from(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new([sig_unlock, ref_unlock]).unwrap(); let tx_payload = TransactionPayload::new(essence, unlocks).unwrap(); diff --git a/sdk/src/wallet/account/operations/transaction/high_level/send.rs b/sdk/src/wallet/account/operations/transaction/high_level/send.rs index 5ff6a8d1bd..c1ca3514a6 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/send.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/send.rs @@ -16,6 +16,7 @@ use crate::{ }, ConvertTo, }, + utils::serde::string, wallet::{ account::{ constants::DEFAULT_EXPIRATION_TIME, operations::transaction::Transaction, Account, TransactionOptions, @@ -28,7 +29,7 @@ use crate::{ #[derive(Debug, Clone, Serialize, Deserialize, Getters)] pub struct SendParams { /// Amount - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] #[getset(get = "pub")] amount: u64, /// Bech32 encoded address diff --git a/sdk/src/wallet/account/operations/transaction/mod.rs b/sdk/src/wallet/account/operations/transaction/mod.rs index 040521bd3f..10a51ffdb8 100644 --- a/sdk/src/wallet/account/operations/transaction/mod.rs +++ b/sdk/src/wallet/account/operations/transaction/mod.rs @@ -10,7 +10,7 @@ mod prepare_transaction; mod sign_transaction; pub(crate) mod submit_transaction; -pub use self::options::{RemainderValueStrategy, TransactionOptions, TransactionOptionsDto}; +pub use self::options::{RemainderValueStrategy, TransactionOptions}; use crate::{ client::{ api::{verify_semantic, PreparedTransactionData, SignedTransactionData}, diff --git a/sdk/src/wallet/account/operations/transaction/options.rs b/sdk/src/wallet/account/operations/transaction/options.rs index 0dd329d3e4..24098c078c 100644 --- a/sdk/src/wallet/account/operations/transaction/options.rs +++ b/sdk/src/wallet/account/operations/transaction/options.rs @@ -4,59 +4,26 @@ use serde::{Deserialize, Serialize}; use crate::{ - client::api::input_selection::{Burn, BurnDto}, - types::block::{ - output::OutputId, - payload::{dto::TaggedDataPayloadDto, tagged_data::TaggedDataPayload}, - Error, - }, + client::api::input_selection::Burn, + types::block::{output::OutputId, payload::tagged_data::TaggedDataPayload}, wallet::account::types::address::AccountAddress, }; /// Options for transactions -#[derive(Debug, Clone, Default)] -pub struct TransactionOptions { - pub remainder_value_strategy: RemainderValueStrategy, - pub tagged_data_payload: Option, - // If custom inputs are provided only they are used. If also other additional inputs should be used, - // `mandatory_inputs` should be used instead. - pub custom_inputs: Option>, - pub mandatory_inputs: Option>, - pub burn: Option, - pub note: Option, - pub allow_micro_amount: bool, -} - -impl TransactionOptions { - /// Conversion from TransactionOptionsDto to TransactionOptions. - pub fn try_from_dto(value: TransactionOptionsDto) -> Result { - Ok(Self { - remainder_value_strategy: value.remainder_value_strategy, - tagged_data_payload: value.tagged_data_payload.map(TaggedDataPayload::try_from).transpose()?, - custom_inputs: value.custom_inputs, - mandatory_inputs: value.mandatory_inputs, - burn: value.burn.map(Burn::try_from).transpose()?, - note: value.note, - allow_micro_amount: value.allow_micro_amount, - }) - } -} - -/// Dto for transaction options #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(rename_all = "camelCase")] -pub struct TransactionOptionsDto { +pub struct TransactionOptions { #[serde(default)] pub remainder_value_strategy: RemainderValueStrategy, #[serde(default)] - pub tagged_data_payload: Option, + pub tagged_data_payload: Option, // If custom inputs are provided only they are used. If also other additional inputs should be used, // `mandatory_inputs` should be used instead. #[serde(default)] pub custom_inputs: Option>, #[serde(default)] pub mandatory_inputs: Option>, - pub burn: Option, + pub burn: Option, pub note: Option, #[serde(default)] pub allow_micro_amount: bool, diff --git a/sdk/src/wallet/account/operations/transaction/prepare_output.rs b/sdk/src/wallet/account/operations/transaction/prepare_output.rs index 9550ac997c..b622ea54e7 100644 --- a/sdk/src/wallet/account/operations/transaction/prepare_output.rs +++ b/sdk/src/wallet/account/operations/transaction/prepare_output.rs @@ -18,6 +18,7 @@ use crate::{ }, Error, }, + utils::serde::string, wallet::account::{ operations::transaction::RemainderValueStrategy, types::OutputData, Account, TransactionOptions, }, @@ -313,7 +314,7 @@ where #[serde(rename_all = "camelCase")] pub struct OutputParams { pub recipient_address: Bech32Address, - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] pub amount: u64, #[serde(default)] pub assets: Option, diff --git a/sdk/src/wallet/account/types/balance.rs b/sdk/src/wallet/account/types/balance.rs index 661f214429..98d4a79584 100644 --- a/sdk/src/wallet/account/types/balance.rs +++ b/sdk/src/wallet/account/types/balance.rs @@ -7,7 +7,10 @@ use getset::{CopyGetters, Getters}; use primitive_types::U256; use serde::{Deserialize, Serialize}; -use crate::types::block::output::{feature::MetadataFeature, AccountId, FoundryId, NftId, OutputId, TokenId}; +use crate::{ + types::block::output::{feature::MetadataFeature, AccountId, FoundryId, NftId, OutputId, TokenId}, + utils::serde::string, +}; /// The balance of an account, returned from [`crate::wallet::account::Account::sync()`] and /// [`crate::wallet::account::Account::balance()`]. @@ -63,14 +66,14 @@ impl std::ops::AddAssign for Balance { #[getset(get_copy = "pub")] pub struct BaseCoinBalance { /// Total amount - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] pub(crate) total: u64, /// Balance that can currently be spent - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] pub(crate) available: u64, /// Voting power #[cfg(feature = "participation")] - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] pub(crate) voting_power: u64, } @@ -88,13 +91,13 @@ impl std::ops::AddAssign for BaseCoinBalance { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, CopyGetters)] #[getset(get_copy = "pub")] pub struct RequiredStorageDeposit { - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] pub(crate) account: u64, - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] pub(crate) basic: u64, - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] pub(crate) foundry: u64, - #[serde(with = "crate::utils::serde::string")] + #[serde(with = "string")] pub(crate) nft: u64, } diff --git a/sdk/src/wallet/account/types/mod.rs b/sdk/src/wallet/account/types/mod.rs index 3ed12d6412..8cd497a7ee 100644 --- a/sdk/src/wallet/account/types/mod.rs +++ b/sdk/src/wallet/account/types/mod.rs @@ -21,7 +21,7 @@ use crate::{ types::{ api::core::response::OutputWithMetadataResponse, block::{ - address::{dto::AddressDto, Address}, + address::Address, output::{dto::OutputDto, AccountTransition, Output, OutputId, OutputMetadata}, payload::transaction::{dto::TransactionPayloadDto, TransactionId, TransactionPayload}, BlockId, Error as BlockError, @@ -105,7 +105,7 @@ pub struct OutputDataDto { /// If an output is spent pub is_spent: bool, /// Associated account address. - pub address: AddressDto, + pub address: Address, /// Network ID pub network_id: String, /// Remainder @@ -122,7 +122,7 @@ impl From<&OutputData> for OutputDataDto { metadata: value.metadata, output: OutputDto::from(&value.output), is_spent: value.is_spent, - address: AddressDto::from(&value.address), + address: value.address, network_id: value.network_id.to_string(), remainder: value.remainder, chain: value.chain, @@ -143,7 +143,7 @@ impl TryFromDto for OutputData { metadata: dto.metadata, output: Output::try_from_dto_with_params(dto.output, params)?, is_spent: dto.is_spent, - address: dto.address.try_into()?, + address: dto.address, network_id: dto .network_id .parse() diff --git a/sdk/tests/types/address/account.rs b/sdk/tests/types/address/account.rs index 7ba89c9e4b..708616899c 100644 --- a/sdk/tests/types/address/account.rs +++ b/sdk/tests/types/address/account.rs @@ -4,14 +4,11 @@ use std::str::FromStr; use iota_sdk::types::block::{ - address::{ - dto::{AccountAddressDto, AddressDto}, - AccountAddress, Address, Bech32Address, ToBech32Ext, - }, + address::{AccountAddress, Address, Bech32Address, ToBech32Ext}, output::AccountId, - Error, }; use packable::PackableExt; +use serde_json::json; const ACCOUNT_ID: &str = "0xe9ba80ad1561e437b663a1f1efbfabd544b0d7da7bb33e0a62e99b20ee450bee"; const ACCOUNT_BECH32: &str = "rms1pr5m4q9dz4s7gdakvwslrmal4025fvxhmfamx0s2vt5ekg8wg597um6lcnn"; @@ -102,42 +99,50 @@ fn bech32_roundtrip() { } #[test] -fn dto_fields() { +fn serde_fields() { let account_address = AccountAddress::from_str(ACCOUNT_ID).unwrap(); - let account_dto = AccountAddressDto::from(&account_address); + let account_address_ser = serde_json::to_value(account_address).unwrap(); - assert_eq!(account_dto.kind, AccountAddress::KIND); - assert_eq!(account_dto.account_id, ACCOUNT_ID.to_string()); + assert_eq!( + account_address_ser, + json!({ + "type": AccountAddress::KIND, + "accountId": ACCOUNT_ID, + }) + ); let address = Address::from(account_address); - let dto = AddressDto::from(&address); + let address_ser = serde_json::to_value(address).unwrap(); - assert_eq!(dto, AddressDto::Account(account_dto)); + assert_eq!(address_ser, account_address_ser); } #[test] -fn dto_roundtrip() { +fn serde_roundtrip() { let account_address = AccountAddress::from_str(ACCOUNT_ID).unwrap(); - let account_dto = AccountAddressDto::from(&account_address); + let account_address_ser = serde_json::to_string(&account_address).unwrap(); - assert_eq!(AccountAddress::try_from(account_dto).unwrap(), account_address); + assert_eq!( + serde_json::from_str::(&account_address_ser).unwrap(), + account_address + ); let address = Address::from(account_address); - let dto = AddressDto::from(&address); + let address_ser = serde_json::to_string(&address).unwrap(); - assert_eq!(Address::try_from(dto).unwrap(), address); + assert_eq!(serde_json::from_str::
(&address_ser).unwrap(), address); } #[test] -fn dto_invalid_account_id() { - let dto = AccountAddressDto { - kind: AccountAddress::KIND, - account_id: ACCOUNT_ID_INVALID.to_string(), - }; +fn serde_invalid_account_id() { + let account_address_ser = json!({ + "type": AccountAddress::KIND, + "accountId": ACCOUNT_ID_INVALID, + }); assert!(matches!( - AccountAddress::try_from(dto), - Err(Error::InvalidField("accountId")) + serde_json::from_value::(account_address_ser), + Err(e) if e.to_string() == "hex error: Invalid hex string length for slice: expected 64 got 61" )); } diff --git a/sdk/tests/types/address/ed25519.rs b/sdk/tests/types/address/ed25519.rs index 7635642c45..c96aa81f85 100644 --- a/sdk/tests/types/address/ed25519.rs +++ b/sdk/tests/types/address/ed25519.rs @@ -3,14 +3,9 @@ use core::str::FromStr; -use iota_sdk::types::block::{ - address::{ - dto::{AddressDto, Ed25519AddressDto}, - Address, Bech32Address, Ed25519Address, ToBech32Ext, - }, - Error, -}; +use iota_sdk::types::block::address::{Address, Bech32Address, Ed25519Address, ToBech32Ext}; use packable::PackableExt; +use serde_json::json; const ED25519_ADDRESS: &str = "0xebe40a263480190dcd7939447ee01aefa73d6f3cc33c90ef7bf905abf8728655"; const ED25519_BECH32: &str = "rms1qr47gz3xxjqpjrwd0yu5glhqrth6w0t08npney8000ust2lcw2r92j5a8rt"; @@ -93,42 +88,50 @@ fn bech32_roundtrip() { } #[test] -fn dto_fields() { +fn serde_fields() { let ed25519_address = Ed25519Address::from_str(ED25519_ADDRESS).unwrap(); - let ed25519_dto = Ed25519AddressDto::from(&ed25519_address); + let ed25519_address_ser = serde_json::to_value(ed25519_address).unwrap(); - assert_eq!(ed25519_dto.kind, Ed25519Address::KIND); - assert_eq!(ed25519_dto.pub_key_hash, ED25519_ADDRESS.to_string()); + assert_eq!( + ed25519_address_ser, + json!({ + "type": Ed25519Address::KIND, + "pubKeyHash": ED25519_ADDRESS, + }) + ); let address = Address::from(ed25519_address); - let dto = AddressDto::from(&address); + let address_ser = serde_json::to_value(address).unwrap(); - assert_eq!(dto, AddressDto::Ed25519(ed25519_dto)); + assert_eq!(address_ser, ed25519_address_ser); } #[test] -fn dto_roundtrip() { +fn serde_roundtrip() { let ed25519_address = Ed25519Address::from_str(ED25519_ADDRESS).unwrap(); - let ed25519_dto = Ed25519AddressDto::from(&ed25519_address); + let ed25519_address_ser = serde_json::to_string(&ed25519_address).unwrap(); - assert_eq!(Ed25519Address::try_from(ed25519_dto).unwrap(), ed25519_address); + assert_eq!( + serde_json::from_str::(&ed25519_address_ser).unwrap(), + ed25519_address + ); let address = Address::from(ed25519_address); - let dto = AddressDto::from(&address); + let address_ser = serde_json::to_string(&address).unwrap(); - assert_eq!(Address::try_from(dto).unwrap(), address); + assert_eq!(serde_json::from_str::
(&address_ser).unwrap(), address); } #[test] -fn dto_invalid_pub_key_hash() { - let dto = Ed25519AddressDto { - kind: Ed25519Address::KIND, - pub_key_hash: ED25519_ADDRESS_INVALID.to_string(), - }; +fn serde_invalid_ed25519_address() { + let ed25519_address_ser = json!({ + "type": Ed25519Address::KIND, + "pubKeyHash": ED25519_ADDRESS_INVALID, + }); assert!(matches!( - Ed25519Address::try_from(dto), - Err(Error::InvalidField("pubKeyHash")) + serde_json::from_value::(ed25519_address_ser), + Err(e) if e.to_string() == "hex error: Invalid hex string length for slice: expected 64 got 63" )); } diff --git a/sdk/tests/types/address/nft.rs b/sdk/tests/types/address/nft.rs index bf346076ef..56a426e59b 100644 --- a/sdk/tests/types/address/nft.rs +++ b/sdk/tests/types/address/nft.rs @@ -4,14 +4,11 @@ use std::str::FromStr; use iota_sdk::types::block::{ - address::{ - dto::{AddressDto, NftAddressDto}, - Address, Bech32Address, NftAddress, ToBech32Ext, - }, + address::{Address, Bech32Address, NftAddress, ToBech32Ext}, output::NftId, - Error, }; use packable::PackableExt; +use serde_json::json; const NFT_ID: &str = "0xa9ede98a7f0223fa7a49fbc586f7a88bb4f0d152f282b19bcebd05c9e8a02370"; const NFT_BECH32: &str = "rms1zz57m6v20upz87n6f8autphh4z9mfux32teg9vvme67stj0g5q3hqd6l53z"; @@ -102,40 +99,51 @@ fn bech32_roundtrip() { } #[test] -fn dto_fields() { +fn serde_fields() { let nft_address = NftAddress::from_str(NFT_ID).unwrap(); - let nft_dto = NftAddressDto::from(&nft_address); + let nft_address_ser = serde_json::to_value(nft_address).unwrap(); - assert_eq!(nft_dto.kind, NftAddress::KIND); - assert_eq!(nft_dto.nft_id, NFT_ID.to_string()); + assert_eq!( + nft_address_ser, + json!({ + "type": NftAddress::KIND, + "nftId": NFT_ID, + }) + ); let address = Address::from(nft_address); - let dto = AddressDto::from(&address); + let address_ser = serde_json::to_value(address).unwrap(); - assert_eq!(dto, AddressDto::Nft(nft_dto)); + assert_eq!(address_ser, nft_address_ser); } #[test] -fn dto_roundtrip() { +fn serde_roundtrip() { let nft_address = NftAddress::from_str(NFT_ID).unwrap(); - let nft_dto = NftAddressDto::from(&nft_address); + let nft_address_ser = serde_json::to_string(&nft_address).unwrap(); - assert_eq!(NftAddress::try_from(nft_dto).unwrap(), nft_address); + assert_eq!( + serde_json::from_str::(&nft_address_ser).unwrap(), + nft_address + ); let address = Address::from(nft_address); - let dto = AddressDto::from(&address); + let address_ser = serde_json::to_string(&address).unwrap(); - assert_eq!(Address::try_from(dto).unwrap(), address); + assert_eq!(serde_json::from_str::
(&address_ser).unwrap(), address); } #[test] -fn dto_invalid_nft_id() { - let dto = NftAddressDto { - kind: NftAddress::KIND, - nft_id: NFT_ID_INVALID.to_string(), - }; - - assert!(matches!(NftAddress::try_from(dto), Err(Error::InvalidField("nftId")))); +fn serde_invalid_account_id() { + let nft_address_ser = json!({ + "type": NftAddress::KIND, + "nftId": NFT_ID_INVALID, + }); + + assert!(matches!( + serde_json::from_value::(nft_address_ser), + Err(e) if e.to_string() == "hex error: Invalid hex string length for slice: expected 64 got 61" + )); } #[test] diff --git a/sdk/tests/types/input/utxo.rs b/sdk/tests/types/input/utxo.rs index e3ab4868e2..072044cffc 100644 --- a/sdk/tests/types/input/utxo.rs +++ b/sdk/tests/types/input/utxo.rs @@ -4,18 +4,12 @@ use core::str::FromStr; use iota_sdk::types::block::{ - input::{ - dto::{InputDto, UtxoInputDto}, - Input, UtxoInput, - }, + input::{Input, UtxoInput}, output::OutputId, - Error, }; -use packable::{bounded::InvalidBoundedU16, PackableExt}; +use packable::PackableExt; const OUTPUT_ID: &str = "0x52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c6492a00"; -const TRANSACTION_ID: &str = "0x52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649"; -const TRANSACTION_ID_INVALID: &str = "0x52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c6"; #[test] fn kind() { @@ -78,60 +72,6 @@ fn from_str() { ); } -#[test] -fn dto_fields() { - let output_id = OutputId::from_str(OUTPUT_ID).unwrap(); - let utxo_input = UtxoInput::from(output_id); - let utxo_dto = UtxoInputDto::from(&utxo_input); - - assert_eq!(utxo_dto.kind, UtxoInput::KIND); - assert_eq!(utxo_dto.transaction_id, output_id.transaction_id().to_string()); - assert_eq!(utxo_dto.transaction_output_index, output_id.index()); - - let input = Input::from(utxo_input); - let dto = InputDto::from(&input); - - assert_eq!(dto, InputDto::Utxo(utxo_dto)); -} - -#[test] -fn dto_roundtrip() { - let utxo_input = UtxoInput::from_str(OUTPUT_ID).unwrap(); - let utxo_dto = UtxoInputDto::from(&utxo_input); - - assert_eq!(UtxoInput::try_from(utxo_dto).unwrap(), utxo_input); - - let input = Input::from(utxo_input); - let dto = InputDto::from(&input); - - assert_eq!(Input::try_from(dto).unwrap(), input); -} - -#[test] -fn dto_invalid() { - let dto = UtxoInputDto { - kind: UtxoInput::KIND, - transaction_id: TRANSACTION_ID_INVALID.to_string(), - transaction_output_index: 0, - }; - - assert!(matches!( - UtxoInput::try_from(dto), - Err(Error::InvalidField("transactionId")) - )); - - let dto = UtxoInputDto { - kind: UtxoInput::KIND, - transaction_id: TRANSACTION_ID.to_string(), - transaction_output_index: 1000, - }; - - assert!(matches!( - UtxoInput::try_from(dto), - Err(Error::InvalidInputOutputIndex(InvalidBoundedU16(1000))) - )); -} - #[test] fn packed_len() { let output_id = OutputId::from_str(OUTPUT_ID).unwrap(); diff --git a/sdk/tests/types/payload.rs b/sdk/tests/types/payload.rs index 47ad3bcdea..baa5ce3790 100644 --- a/sdk/tests/types/payload.rs +++ b/sdk/tests/types/payload.rs @@ -47,8 +47,8 @@ fn transaction() { let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); - let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); + let sig_unlock = Unlock::from(SignatureUnlock::from(Signature::from(signature))); + let ref_unlock = Unlock::from(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new(vec![sig_unlock, ref_unlock]).unwrap(); let tx_payload = TransactionPayload::new(essence, unlocks).unwrap(); diff --git a/sdk/tests/types/transaction_payload.rs b/sdk/tests/types/transaction_payload.rs index e060926079..347de6099e 100644 --- a/sdk/tests/types/transaction_payload.rs +++ b/sdk/tests/types/transaction_payload.rs @@ -53,7 +53,7 @@ fn builder_no_essence_too_few_unlocks() { let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); + let sig_unlock = Unlock::from(SignatureUnlock::from(Signature::from(signature))); let unlocks = Unlocks::new([sig_unlock]).unwrap(); assert!(matches!( @@ -90,8 +90,8 @@ fn builder_no_essence_too_many_unlocks() { let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); - let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); + let sig_unlock = Unlock::from(SignatureUnlock::from(Signature::from(signature))); + let ref_unlock = Unlock::from(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new([sig_unlock, ref_unlock]).unwrap(); @@ -130,8 +130,8 @@ fn pack_unpack_valid() { let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); - let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); + let sig_unlock = Unlock::from(SignatureUnlock::from(Signature::from(signature))); + let ref_unlock = Unlock::from(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new([sig_unlock, ref_unlock]).unwrap(); let tx_payload = TransactionPayload::new(essence, unlocks).unwrap(); @@ -172,8 +172,8 @@ fn getters() { let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); - let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); + let sig_unlock = Unlock::from(SignatureUnlock::from(Signature::from(signature))); + let ref_unlock = Unlock::from(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new([sig_unlock, ref_unlock]).unwrap(); let tx_payload = TransactionPayload::new(essence.clone(), unlocks.clone()).unwrap(); diff --git a/sdk/tests/types/transaction_regular_essence.rs b/sdk/tests/types/transaction_regular_essence.rs index 2ba9ea9ec9..ed00c71825 100644 --- a/sdk/tests/types/transaction_regular_essence.rs +++ b/sdk/tests/types/transaction_regular_essence.rs @@ -136,8 +136,8 @@ fn build_invalid_payload_kind() { let pub_key_bytes: [u8; 32] = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); let sig_bytes: [u8; 64] = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); - let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); + let sig_unlock = Unlock::from(SignatureUnlock::from(Signature::from(signature))); + let ref_unlock = Unlock::from(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new([sig_unlock, ref_unlock]).unwrap(); let tx_payload = TransactionPayload::new(essence, unlocks).unwrap(); diff --git a/sdk/tests/types/unlock/mod.rs b/sdk/tests/types/unlock/mod.rs index 9ce6a90259..91ca3bd114 100644 --- a/sdk/tests/types/unlock/mod.rs +++ b/sdk/tests/types/unlock/mod.rs @@ -73,7 +73,7 @@ fn new_invalid_duplicate_signature() { ReferenceUnlock::new(0).unwrap().into(), ReferenceUnlock::new(0).unwrap().into(), SignatureUnlock::from(rand_signature()).into(), - SignatureUnlock::from(dup.clone()).into(), + SignatureUnlock::from(dup).into(), SignatureUnlock::from(dup).into(), SignatureUnlock::from(rand_signature()).into(), ReferenceUnlock::new(3).unwrap().into()