Repository: mintlayer/mintlayer-core
Pull request #798 (2023-03-31 10:06:13)

Merge OutputPurpose with TxOutput to make output value part of the arms in preparation for more complex output types
We don't always need value to be present for every output type. Hence, it makes more sense to make it part of the enum instead of existent in every type.


Patch:
diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs
index e96946a5f..b093d9457 100644
--- a/chainstate/src/detail/ban_score.rs
+++ b/chainstate/src/detail/ban_score.rs
@@ -110,10 +110,9 @@ impl BanScore for ConnectTransactionError {
             ConnectTransactionError::AttemptToUseInvalidOutputInTx => 100,
             ConnectTransactionError::MissingPoSAccountingUndo(_) => 0,
             ConnectTransactionError::PoSAccountingError(err) => err.ban_score(),
-            ConnectTransactionError::TokenOutputInPoSAccountingOperation(_) => 100,
             ConnectTransactionError::AccountingBlockUndoError(_) => 100,
             ConnectTransactionError::SpendStakeError(_) => 100,
-            ConnectTransactionError::InvalidOutputPurposeInReward(_) => 100,
+            ConnectTransactionError::InvalidOutputTypeInReward(_) => 100,
             ConnectTransactionError::PoolDataNotFound(_) => 0,
         }
     }
@@ -297,7 +296,7 @@ impl BanScore for ConsensusPoSError {
 impl BanScore for PoSRandomnessError {
     fn ban_score(&self) -> u32 {
         match self {
-            PoSRandomnessError::InvalidOutputPurposeInStakeKernel(_) => 100,
+            PoSRandomnessError::InvalidOutputTypeInStakeKernel(_) => 100,
             PoSRandomnessError::VRFDataVerificationFailed(_) => 100,
         }
     }
diff --git a/chainstate/src/detail/chainstateref/epoch_seal.rs b/chainstate/src/detail/chainstateref/epoch_seal.rs
index 94b4c66b3..33687fbca 100644
--- a/chainstate/src/detail/chainstateref/epoch_seal.rs
+++ b/chainstate/src/detail/chainstateref/epoch_seal.rs
@@ -20,7 +20,7 @@ use chainstate_types::{pos_randomness::PoSRandomness, EpochData};
 use common::{
     chain::{
         block::{consensus_data::PoSData, ConsensusData},
-        Block, ChainConfig, OutputPurpose,
+        Block, ChainConfig, TxOutput,
     },
     primitives::BlockHeight,
 };
@@ -141,17 +141,15 @@ fn create_randomness_from_block<S: BlockchainStorageRead>(
         .get(0)
         .ok_or(SpendStakeError::NoBlockRewardOutputs)?;
 
-    let vrf_pub_key = match reward_output.purpose() {
-        OutputPurpose::Transfer(_)
-        | OutputPurpose::LockThenTransfer(_, _)
-        | OutputPurpose::Burn => {
+    let vrf_pub_key = match reward_output {
+        TxOutput::Transfer(_, _) | TxOutput::LockThenTransfer(_, _, _) | TxOutput::Burn(_) => {
             // only pool outputs can be staked
             return Err(BlockError::SpendStakeError(
                 SpendStakeError::InvalidBlockRewardPurpose,
             ));
         }
-        OutputPurpose::StakePool(d) => d.as_ref().vrf_public_key().clone(),
-        OutputPurpose::ProduceBlockFromStake(_, pool_id) => {
+        TxOutput::StakePool(d) => d.as_ref().vrf_public_key().clone(),
+        TxOutput::ProduceBlockFromStake(_, _, pool_id) => {
             let pos_view = PoSAccountingDB::<_, TipStorageTag>::new(db_tx);
             let pool_data = pos_view
                 .get_pool_data(*pool_id)?
@@ -241,8 +239,7 @@ mod tests {
             block::{consensus_data::PoSData, timestamp::BlockTimestamp, BlockReward},
             config::{Builder as ConfigBuilder, EpochIndex},
             stakelock::StakePoolData,
-            tokens::OutputValue,
-            Block, Destination, OutputPurpose, PoolId, TxOutput,
+            Block, Destination, PoolId, TxOutput,
         },
         primitives::{Amount, Compact, H256},
     };
@@ -260,16 +257,14 @@ mod tests {
         let vrf_data = vrf_sk.produce_vrf_data(vrf_transcript.into());
 
         let stake_pool_data = StakePoolData::new(
+            Amount::from_atoms(1),
             Destination::AnyoneCanSpend,
             vrf_pk,
             Destination::AnyoneCanSpend,
             0,
             Amount::ZERO,
         );
-        let reward_output = TxOutput::new(
-            OutputValue::Coin(Amount::from_atoms(1)),
-            OutputPurpose::StakePool(Box::new(stake_pool_data)),
-        );
+        let reward_output = TxOutput::StakePool(Box::new(stake_pool_data));
         let pos_data = PoSData::new(vec![], vec![], pool_id, vrf_data, Compact(1));
         Block::new(
             vec![],
diff --git a/chainstate/src/detail/chainstateref/mod.rs b/chainstate/src/detail/chainstateref/mod.rs
index 5e726e9cd..29ed2965f 100644
--- a/chainstate/src/detail/chainstateref/mod.rs
+++ b/chainstate/src/detail/chainstateref/mod.rs
@@ -30,7 +30,7 @@ use common::{
         timelock::OutputTimeLock,
         tokens::TokenAuxiliaryData,
         tokens::{get_tokens_issuance_count, OutputValue, TokenId},
-        Block, ChainConfig, GenBlock, GenBlockId, OutPointSourceId, OutputPurpose, Transaction,
+        Block, ChainConfig, GenBlock, GenBlockId, OutPointSourceId, Transaction, TxOutput,
     },
     primitives::{id::WithId, BlockDistance, BlockHeight, Id, Idable},
     time_getter::TimeGetter,
@@ -423,19 +423,15 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
 
     fn check_block_reward_maturity_settings(&self, block: &Block) -> Result<(), CheckBlockError> {
         block.block_reward().outputs().iter().try_for_each(|output| {
-            match output.purpose() {
-                OutputPurpose::LockThenTransfer(_, tl) => {
-                    self.check_block_reward_timelock(block, tl)
-                }
-                OutputPurpose::ProduceBlockFromStake(_, _) => {
+            match output {
+                TxOutput::LockThenTransfer(_, _, tl) => self.check_block_reward_timelock(block, tl),
+                TxOutput::ProduceBlockFromStake(_, _, _) => {
                     // The output can be reused in block reward right away
                     Ok(())
                 }
-                OutputPurpose::Transfer(_) | OutputPurpose::StakePool(_) | OutputPurpose::Burn => {
-                    Err(CheckBlockError::InvalidBlockRewardOutputType(
-                        block.get_id(),
-                    ))
-                }
+                TxOutput::Transfer(_, _) | TxOutput::StakePool(_) | TxOutput::Burn(_) => Err(
+                    CheckBlockError::InvalidBlockRewardOutputType(block.get_id()),
+                ),
             }
         })
     }
@@ -545,7 +541,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
                 .try_for_each(|token_data| {
                     check_tokens_data(
                         self.chain_config,
-                        token_data,
+                        token_data.as_ref(),
                         tx.transaction(),
                         block.get_id(),
                     )
diff --git a/chainstate/src/detail/query.rs b/chainstate/src/detail/query.rs
index d25429e47..4dbff5c98 100644
--- a/chainstate/src/detail/query.rs
+++ b/chainstate/src/detail/query.rs
@@ -222,7 +222,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
                 OutputValue::Token(token_data) => Some(token_data),
             })
             // Find issuance data and return RPCTokenInfo
-            .find_map(|token_data| match &**token_data {
+            .find_map(|token_data| match &*token_data {
                 TokenData::TokenIssuance(issuance) => {
                     Some(RPCTokenInfo::new_fungible(RPCFungibleTokenInfo::new(
                         token_id,
diff --git a/chainstate/src/interface/chainstate_interface_impl.rs b/chainstate/src/interface/chainstate_interface_impl.rs
index a32bc4d47..ef8527dd1 100644
--- a/chainstate/src/interface/chainstate_interface_impl.rs
+++ b/chainstate/src/interface/chainstate_interface_impl.rs
@@ -365,7 +365,7 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> ChainstateInterfa
             |mut values, outpoint| {
                 if let Some(utxo) = utxo_view.utxo(outpoint) {
                     match utxo.output().value() {
-                        OutputValue::Coin(amount) => values.push(Some(*amount)),
+                        OutputValue::Coin(amount) => values.push(Some(amount)),
                         _ => {
                             return Err(ChainstateError::FailedToReadProperty(
                                 PropertyQueryError::ExpectedCoinOutpointAndFoundToken,
diff --git a/chainstate/tx-verifier/src/transaction_verifier/error.rs b/chainstate/tx-verifier/src/transaction_verifier/error.rs
index 5db614297..6089c2e53 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/error.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/error.rs
@@ -97,12 +97,10 @@ pub enum ConnectTransactionError {
     PoSAccountingError(#[from] pos_accounting::Error),
     #[error("PoS accounting undo is missing for transaction {0}")]
     MissingPoSAccountingUndo(Id<Transaction>),
-    #[error("No token outputs are allowed in PoS accounting operations {0}")]
-    TokenOutputInPoSAccountingOperation(Id<Transaction>),
     #[error("Error during stake spending")]
     SpendStakeError(#[from] SpendStakeError),
     #[error("Attempted to use a non-locked stake as output in block reward {0}")]
-    InvalidOutputPurposeInReward(Id<Block>),
+    InvalidOutputTypeInReward(Id<Block>),
     #[error("Data of pool {0} not found")]
     PoolDataNotFound(PoolId),
 }
diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy.rs
index 5afff34f7..37ec7af0c 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy.rs
@@ -13,11 +13,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use common::chain::{OutputPurpose, Transaction, TxOutput};
+use common::chain::{Transaction, TxOutput};
 
 use super::error::ConnectTransactionError;
 
-/// Not all `OutputPurposes` can be used in a transaction.
+/// Not all `OutputType`s can be used in a transaction.
 /// For example spending `ProduceBlockFromStake` and `StakePool` in a tx is not supported
 /// at the moment and considered invalid.
 pub fn check_tx_inputs_outputs_purposes(
@@ -31,22 +31,22 @@ pub fn check_tx_inputs_outputs_purposes(
 
 /// Indicates whether an output purpose can be used in a tx as an input
 fn is_valid_input_for_tx(output: &TxOutput) -> bool {
-    match output.purpose() {
-        OutputPurpose::Transfer(_) | OutputPurpose::LockThenTransfer(_, _) => true,
-        OutputPurpose::Burn
-        | OutputPurpose::StakePool(_)
-        | OutputPurpose::ProduceBlockFromStake(_, _) => false,
+    match output {
+        TxOutput::Transfer(_, _) | TxOutput::LockThenTransfer(_, _, _) => true,
+        TxOutput::Burn(_) | TxOutput::StakePool(_) | TxOutput::ProduceBlockFromStake(_, _, _) => {
+            false
+        }
     }
 }
 
 /// Indicates whether an output purpose can be used in a tx as an output
 fn is_valid_output_for_tx(output: &TxOutput) -> bool {
-    match output.purpose() {
-        OutputPurpose::Transfer(_)
-        | OutputPurpose::LockThenTransfer(_, _)
-        | OutputPurpose::Burn
-        | OutputPurpose::StakePool(_) => true,
-        OutputPurpose::ProduceBlockFromStake(_, _) => false,
+    match output {
+        TxOutput::Transfer(_, _)
+        | TxOutput::LockThenTransfer(_, _, _)
+        | TxOutput::Burn(_)
+        | TxOutput::StakePool(_) => true,
+        TxOutput::ProduceBlockFromStake(_, _, _) => false,
     }
 }
 
diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs
index 036b33e9d..590729af5 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs
@@ -61,8 +61,8 @@ use common::{
         signed_transaction::SignedTransaction,
         stakelock::StakePoolData,
         tokens::{get_tokens_issuance_count, OutputValue, TokenId},
-        Block, ChainConfig, GenBlock, OutPointSourceId, OutputPurpose, Transaction, TxInput,
-        TxMainChainIndex, TxOutput,
+        Block, ChainConfig, GenBlock, OutPointSourceId, Transaction, TxInput, TxMainChainIndex,
+        TxOutput,
     },
     primitives::{id::WithId, Amount, BlockHeight, Id, Idable, H256},
 };
@@ -285,13 +285,15 @@ where
                         self.get_token_id_from_issuance_tx(tx_id)
                             .map_err(ConnectTransactionError::TransactionVerifierError)
                     };
-                let (key, amount) =
-                    get_input_token_id_and_amount(utxo.output().value(), issuance_token_id_getter)?;
+                let (key, amount) = get_input_token_id_and_amount(
+                    &utxo.output().value(),
+                    issuance_token_id_getter,
+                )?;
                 Ok((key, amount))
             }
             OutPointSourceId::BlockReward(_) => {
                 let (key, amount) =
-                    get_input_token_id_and_amount(utxo.output().value(), || Ok(None))?;
+                    get_input_token_id_and_amount(&utxo.output().value(), || Ok(None))?;
                 match key {
                     CoinOrTokenId::Coin => Ok((CoinOrTokenId::Coin, amount)),
                     CoinOrTokenId::TokenId(tid) => Ok((CoinOrTokenId::TokenId(tid), amount)),
@@ -346,7 +348,7 @@ where
         let total_burned = tx
             .outputs()
             .iter()
-            .filter(|o| *o.purpose() == OutputPurpose::Burn)
+            .filter(|o| matches!(*o, TxOutput::Burn(_)))
             .filter_map(|o| o.value().coin_amount())
             .try_fold(Amount::ZERO, |so_far, v| {
                 (so_far + v).ok_or_else(|| ConnectTransactionError::BurnAmountSumError(tx.get_id()))
@@ -369,19 +371,18 @@ where
         output: &TxOutput,
         block_id: Id<Block>,
     ) -> Result<StakePoolData, ConnectTransactionError> {
-        match output.purpose() {
-            OutputPurpose::Transfer(_)
-            | OutputPurpose::LockThenTransfer(_, _)
-            | OutputPurpose::Burn => Err(ConnectTransactionError::InvalidOutputPurposeInReward(
-                block_id,
-            )),
-            OutputPurpose::StakePool(d) => Ok(d.as_ref().clone()),
-            OutputPurpose::ProduceBlockFromStake(d, pool_id) => {
+        match output {
+            TxOutput::Transfer(_, _) | TxOutput::LockThenTransfer(_, _, _) | TxOutput::Burn(_) => {
+                Err(ConnectTransactionError::InvalidOutputTypeInReward(block_id))
+            }
+            TxOutput::StakePool(d) => Ok(d.as_ref().clone()),
+            TxOutput::ProduceBlockFromStake(v, d, pool_id) => {
                 let pool_data = self
                     .accounting_delta
                     .get_pool_data(*pool_id)?
                     .ok_or(ConnectTransactionError::PoolDataNotFound(*pool_id))?;
                 Ok(StakePoolData::new(
+                    *v,
                     d.clone(),
                     pool_data.vrf_public_key().clone(),
                     pool_data.decommission_destination().clone(),
@@ -512,7 +513,7 @@ where
 
             // TODO: see if a different treatment should be done for different output purposes
             // TODO: ensure that signature verification is tested in the test-suite, they seem to be tested only internally
-            match utxo.output().purpose().destination() {
+            match utxo.output().destination() {
                 Some(d) => verify_signature(
                     self.chain_config.as_ref(),
                     d,
@@ -539,21 +540,19 @@ where
         let tx_undo = tx
             .outputs()
             .iter()
-            .filter_map(|output| match output.purpose() {
-                OutputPurpose::StakePool(data) => Some((data, output.value())),
-                OutputPurpose::Transfer(_)
-                | OutputPurpose::LockThenTransfer(_, _)
-                | OutputPurpose::Burn
-                | OutputPurpose::ProduceBlockFromStake(_, _) => None,
+            .filter_map(|output| match output {
+                TxOutput::StakePool(data) => Some(data),
+                TxOutput::Transfer(_, _)
+                | TxOutput::LockThenTransfer(_, _, _)
+                | TxOutput::Burn(_)
+                | TxOutput::ProduceBlockFromStake(_, _, _) => None,
             })
             .map(
-                |(pool_data, output_value)| -> Result<PoSAccountingUndo, ConnectTransactionError> {
+                |pool_data| -> Result<PoSAccountingUndo, ConnectTransactionError> {
                     let input0 = input0_getter()?;
 
                     // TODO: check StakePoolData fields
-                    let delegation_amount = output_value.coin_amount().ok_or_else(|| {
-                        ConnectTransactionError::TokenOutputInPoSAccountingOperation(tx.get_id())
-                    })?;
+                    let delegation_amount = pool_data.value();
 
                     let mut temp_delta = PoSAccountingDelta::new(&self.accounting_delta);
                     let (_, undo) = temp_delta
@@ -595,8 +594,8 @@ where
         tx_source: TransactionSource,
         tx: &Transaction,
     ) -> Result<(), ConnectTransactionError> {
-        tx.outputs().iter().try_for_each(|output| match output.purpose() {
-            OutputPurpose::StakePool(_) => {
+        tx.outputs().iter().try_for_each(|output| match output {
+            TxOutput::StakePool(_) => {
                 let block_undo_fetcher = |id: Id<Block>| self.storage.get_accounting_undo(id);
                 self.accounting_block_undo
                     .take_tx_undo(&tx_source, &tx.get_id(), block_undo_fetcher)?
@@ -616,10 +615,10 @@ where
                     })
                     .map_err(ConnectTransactionError::PoSAccountingError)
             }
-            OutputPurpose::Transfer(_)
-            | OutputPurpose::LockThenTransfer(_, _)
-            | OutputPurpose::Burn
-            | OutputPurpose::ProduceBlockFromStake(_, _) => Ok(()),
+            TxOutput::Transfer(_, _)
+            | TxOutput::LockThenTransfer(_, _, _)
+            | TxOutput::Burn(_)
+            | TxOutput::ProduceBlockFromStake(_, _, _) => Ok(()),
         })
     }
 
diff --git a/chainstate/tx-verifier/src/transaction_verifier/timelock_check.rs b/chainstate/tx-verifier/src/transaction_verifier/timelock_check.rs
index 05f779473..491cba1a4 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/timelock_check.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/timelock_check.rs
@@ -16,8 +16,7 @@
 use chainstate_types::{block_index_ancestor_getter, BlockIndex, GenBlockIndex};
 use common::{
     chain::{
-        block::timestamp::BlockTimestamp, signature::Transactable, ChainConfig, GenBlock,
-        OutputPurpose, TxOutput,
+        block::timestamp::BlockTimestamp, signature::Transactable, ChainConfig, GenBlock, TxOutput,
     },
     primitives::{BlockDistance, BlockHeight, Id},
 };
@@ -37,12 +36,12 @@ fn check_timelock(
 ) -> Result<(), ConnectTransactionError> {
     use common::chain::timelock::OutputTimeLock;
 
-    let timelock = match output.purpose() {
-        OutputPurpose::Transfer(_)
-        | OutputPurpose::Burn
-        | OutputPurpose::StakePool(_)
-        | OutputPurpose::ProduceBlockFromStake(_, _) => return Ok(()),
-        OutputPurpose::LockThenTransfer(_, tl) => tl,
+    let timelock = match output {
+        TxOutput::Transfer(_, _)
+        | TxOutput::Burn(_)
+        | TxOutput::StakePool(_)
+        | TxOutput::ProduceBlockFromStake(_, _, _) => return Ok(()),
+        TxOutput::LockThenTransfer(_, _, tl) => tl,
     };
 
     let source_block_height = source_block_index.block_height();
diff --git a/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs b/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs
index d02b84dc2..8f66ccd22 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs
@@ -75,7 +75,8 @@ impl TokenIssuanceCache {
         block_id: Option<Id<Block>>,
         tx: &Transaction,
     ) -> Result<(), TokensError> {
-        let was_token_issued = tx.outputs().iter().any(|output| is_tokens_issuance(output.value()));
+        let was_token_issued =
+            tx.outputs().iter().any(|output| is_tokens_issuance(&output.value()));
         if was_token_issued {
             self.write_issuance(&block_id.unwrap_or_else(|| H256::zero().into()), tx)?;
         }
@@ -84,7 +85,7 @@ impl TokenIssuanceCache {
 
     pub fn unregister(&mut self, tx: &Transaction) -> Result<(), TokensError> {
         let was_tokens_issued =
-            tx.outputs().iter().any(|output| is_tokens_issuance(output.value()));
+            tx.outputs().iter().any(|output| is_tokens_issuance(&output.value()));
 
         if was_tokens_issued {
             self.write_undo_issuance(tx)?;
@@ -146,7 +147,7 @@ impl TokenIssuanceCache {
         tx: &Transaction,
     ) -> Result<(), ConnectTransactionError> {
         let has_token_issuance =
-            tx.outputs().iter().any(|output| is_tokens_issuance(output.value()));
+            tx.outputs().iter().any(|output| is_tokens_issuance(&output.value()));
         if has_token_issuance {
             let token_id = token_id(tx).ok_or(TokensError::TokenIdCantBeCalculated)?;
             match self.data.entry(token_id) {
diff --git a/chainstate/tx-verifier/src/transaction_verifier/utils.rs b/chainstate/tx-verifier/src/transaction_verifier/utils.rs
index f275d602d..70d436110 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/utils.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/utils.rs
@@ -73,7 +73,7 @@ pub fn calculate_total_outputs(
 ) -> Result<BTreeMap<CoinOrTokenId, Amount>, ConnectTransactionError> {
     let iter = outputs
         .iter()
-        .map(|output| get_output_token_id_and_amount(output.value(), include_issuance));
+        .map(|output| get_output_token_id_and_amount(&output.value(), include_issuance));
     let iter = fallible_iterator::convert(iter).filter_map(Ok).map_err(Into::into);
 
     let result = AmountsMap::from_fallible_iter(iter)?;
diff --git a/chainstate/types/src/pos_randomness.rs b/chainstate/types/src/pos_randomness.rs
index f277b0a64..30a9906b3 100644
--- a/chainstate/types/src/pos_randomness.rs
+++ b/chainstate/types/src/pos_randomness.rs
@@ -30,7 +30,7 @@ use crate::vrf_tools::{verify_vrf_and_get_vrf_output, ProofOfStakeVRFError};
 #[derive(Error, Debug, PartialEq, Eq, Clone)]
 pub enum PoSRandomnessError {
     #[error("Attempted to use a non-locked stake as stake kernel in block {0}")]
-    InvalidOutputPurposeInStakeKernel(Id<Block>),
+    InvalidOutputTypeInStakeKernel(Id<Block>),
     #[error("Failed to verify VRF data with error: {0}")]
     VRFDataVerificationFailed(#[from] ProofOfStakeVRFError),
 }
diff --git a/common/src/chain/block/mod.rs b/common/src/chain/block/mod.rs
index bc544cb56..3496cbf80 100644
--- a/common/src/chain/block/mod.rs
+++ b/common/src/chain/block/mod.rs
@@ -226,7 +226,7 @@ mod tests {
     use crate::{
         chain::{
             signature::inputsig::InputWitness, tokens::OutputValue, transaction::Transaction,
-            Destination, OutPointSourceId, OutputPurpose, TxInput, TxOutput,
+            Destination, OutPointSourceId, TxInput, TxOutput,
         },
         primitives::{id, Amount},
     };
@@ -311,9 +311,9 @@ mod tests {
             timestamp: BlockTimestamp::from_int_seconds(rng.gen()),
         };
 
-        let reward = BlockReward::new(vec![TxOutput::new(
+        let reward = BlockReward::new(vec![TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(1)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         )]);
         let body = BlockBody {
             reward,
diff --git a/common/src/chain/config/mod.rs b/common/src/chain/config/mod.rs
index be409587f..61f45b548 100644
--- a/common/src/chain/config/mod.rs
+++ b/common/src/chain/config/mod.rs
@@ -24,7 +24,7 @@ use crate::chain::block::timestamp::BlockTimestamp;
 use crate::chain::tokens::OutputValue;
 use crate::chain::transaction::Destination;
 use crate::chain::upgrades::NetUpgrades;
-use crate::chain::OutputPurpose;
+use crate::chain::TxOutput;
 use crate::chain::{Block, GenBlock, Genesis};
 use crate::chain::{PoWChainConfig, UpgradeVersion};
 use crate::primitives::id::{Id, Idable, WithId};
@@ -294,8 +294,6 @@ const TOKEN_MAX_URI_LEN: usize = 1024;
 const MAX_CLASSIC_MULTISIG_PUBLIC_KEYS_COUNT: usize = 16;
 
 fn create_mainnet_genesis() -> Genesis {
-    use crate::chain::transaction::TxOutput;
-
     // TODO: replace this with our mint key
     // Private key: "0080732e24bb0b704cb455e233b539f2c63ab411989a54984f84a6a2eb2e933e160f"
     // Public key:  "008090f5aee58be97ce2f7c014fa97ffff8c459a0c491f8124950724a187d134e25c"
@@ -311,9 +309,9 @@ fn create_mainnet_genesis() -> Genesis {
     let genesis_message = String::new();
 
     // TODO: replace this with the real genesis mint value
-    let output = TxOutput::new(
+    let output = TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(100_000_000_000_000)),
-        OutputPurpose::Transfer(genesis_mint_destination),
+        genesis_mint_destination,
     );
 
     Genesis::new(
@@ -324,13 +322,11 @@ fn create_mainnet_genesis() -> Genesis {
 }
 
 fn create_unit_test_genesis(premine_destination: Destination) -> Genesis {
-    use crate::chain::transaction::TxOutput;
-
     let genesis_message = String::new();
 
-    let output = TxOutput::new(
+    let output = TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(100_000_000_000_000_000_000)),
-        OutputPurpose::Transfer(premine_destination),
+        premine_destination,
     );
 
     Genesis::new(
diff --git a/common/src/chain/tokens/tokens_utils.rs b/common/src/chain/tokens/tokens_utils.rs
index ccede2704..c4b9267be 100644
--- a/common/src/chain/tokens/tokens_utils.rs
+++ b/common/src/chain/tokens/tokens_utils.rs
@@ -34,5 +34,5 @@ pub fn is_tokens_issuance(output_value: &OutputValue) -> bool {
 }
 
 pub fn get_tokens_issuance_count(outputs: &[TxOutput]) -> usize {
-    outputs.iter().filter(|&output| is_tokens_issuance(output.value())).count()
+    outputs.iter().filter(|&output| is_tokens_issuance(&output.value())).count()
 }
diff --git a/common/src/chain/transaction/output/mod.rs b/common/src/chain/transaction/output/mod.rs
index 2ed40876c..2af9acb59 100644
--- a/common/src/chain/transaction/output/mod.rs
+++ b/common/src/chain/transaction/output/mod.rs
@@ -16,7 +16,7 @@
 use crate::{
     address::pubkeyhash::PublicKeyHash,
     chain::{tokens::OutputValue, PoolId},
-    primitives::Id,
+    primitives::{Amount, Id},
 };
 use script::Script;
 use serialization::{Decode, Encode};
@@ -41,71 +41,64 @@ pub enum Destination {
     ClassicMultisig(PublicKeyHash),
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)]
-pub enum OutputPurpose {
+#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
+pub enum TxOutput {
     #[codec(index = 0)]
-    Transfer(Destination),
+    Transfer(OutputValue, Destination),
     #[codec(index = 1)]
-    LockThenTransfer(Destination, OutputTimeLock),
+    LockThenTransfer(OutputValue, Destination, OutputTimeLock),
     #[codec(index = 2)]
-    Burn,
+    Burn(OutputValue),
     /// Output type that is used to create a stake pool
     #[codec(index = 3)]
     StakePool(Box<StakePoolData>),
     /// Output type that represents spending of a stake pool output in a block reward
     /// in order to produce a block
     #[codec(index = 4)]
-    ProduceBlockFromStake(Destination, PoolId),
+    ProduceBlockFromStake(Amount, Destination, PoolId),
 }
 
-impl OutputPurpose {
+impl TxOutput {
+    // TODO: this has to go
     pub fn destination(&self) -> Option<&Destination> {
         match self {
-            OutputPurpose::Transfer(d) => Some(d),
-            OutputPurpose::LockThenTransfer(d, _) => Some(d),
-            OutputPurpose::Burn => None,
-            OutputPurpose::StakePool(d) => Some(d.staker()),
-            OutputPurpose::ProduceBlockFromStake(d, _) => Some(d),
+            TxOutput::Transfer(_, d) => Some(d),
+            TxOutput::LockThenTransfer(_, d, _) => Some(d),
+            TxOutput::Burn(_) => None,
+            TxOutput::StakePool(d) => Some(d.staker()),
+            TxOutput::ProduceBlockFromStake(_, d, _) => Some(d),
         }
     }
 
     pub fn is_burn(&self) -> bool {
         match self {
-            OutputPurpose::Transfer(_)
-            | OutputPurpose::LockThenTransfer(_, _)
-            | OutputPurpose::StakePool(_)
-            | OutputPurpose::ProduceBlockFromStake(_, _) => false,
-            OutputPurpose::Burn => true,
+            TxOutput::Transfer(_, _)
+            | TxOutput::LockThenTransfer(_, _, _)
+            | TxOutput::StakePool(_)
+            | TxOutput::ProduceBlockFromStake(_, _, _) => false,
+            TxOutput::Burn(_) => true,
         }
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
-pub struct TxOutput {
-    value: OutputValue,
-    purpose: OutputPurpose,
-}
-
 impl TxOutput {
-    pub fn new(value: OutputValue, purpose: OutputPurpose) -> Self {
-        TxOutput { value, purpose }
-    }
-
-    pub fn value(&self) -> &OutputValue {
-        &self.value
-    }
-
-    pub fn purpose(&self) -> &OutputPurpose {
-        &self.purpose
+    pub fn value(&self) -> OutputValue {
+        match self {
+            TxOutput::Transfer(v, _) => v.clone(),
+            TxOutput::LockThenTransfer(v, _, _) => v.clone(),
+            TxOutput::Burn(v) => v.clone(),
+            TxOutput::StakePool(d) => OutputValue::Coin(d.value()),
+            TxOutput::ProduceBlockFromStake(v, _, _) => OutputValue::Coin(*v),
+        }
     }
 
     pub fn has_timelock(&self) -> bool {
-        match &self.purpose {
-            OutputPurpose::Transfer(_)
-            | OutputPurpose::Burn
-            | OutputPurpose::StakePool(_)
-            | OutputPurpose::ProduceBlockFromStake(_, _) => false,
-            OutputPurpose::LockThenTransfer(_, _) => true,
+        match self {
+            TxOutput::Transfer(_, _)
+            | TxOutput::Burn(_)
+            | TxOutput::StakePool(_)
+            | TxOutput::ProduceBlockFromStake(_, _, _) => false,
+            TxOutput::LockThenTransfer(_, _, _) => true,
         }
     }
 }
diff --git a/common/src/chain/transaction/output/stakelock.rs b/common/src/chain/transaction/output/stakelock.rs
index 9505f1ef3..26f3552df 100644
--- a/common/src/chain/transaction/output/stakelock.rs
+++ b/common/src/chain/transaction/output/stakelock.rs
@@ -22,6 +22,7 @@ use super::Destination;
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)]
 pub struct StakePoolData {
+    value: Amount,
     staker: Destination,
     vrf_public_key: VRFPublicKey,
     decommission_key: Destination,
@@ -33,6 +34,7 @@ pub struct StakePoolData {
 
 impl StakePoolData {
     pub fn new(
+        value: Amount,
         staker: Destination,
         vrf_public_key: VRFPublicKey,
         decommission_key: Destination,
@@ -40,6 +42,7 @@ impl StakePoolData {
         cost_per_epoch: Amount,
     ) -> Self {
         Self {
+            value,
             staker,
             vrf_public_key,
             decommission_key,
@@ -67,4 +70,8 @@ impl StakePoolData {
     pub fn cost_per_epoch(&self) -> Amount {
         self.cost_per_epoch
     }
+
+    pub fn value(&self) -> Amount {
+        self.value
+    }
 }
diff --git a/consensus/src/pos/mod.rs b/consensus/src/pos/mod.rs
index c324485b7..fbc5f8aef 100644
--- a/consensus/src/pos/mod.rs
+++ b/consensus/src/pos/mod.rs
@@ -24,7 +24,7 @@ use common::{
     chain::{
         block::{consensus_data::PoSData, timestamp::BlockTimestamp, BlockHeader},
         config::EpochIndex,
-        ChainConfig, OutputPurpose,
+        ChainConfig, TxOutput,
     },
     primitives::{Amount, BlockHeight, Idable},
     Uint256, Uint512,
@@ -117,20 +117,18 @@ where
     let current_epoch_index = chain_config.epoch_index_from_height(&current_height);
     let kernel_output = get_kernel_output(pos_data.kernel_inputs(), utxos_view)?;
 
-    let vrf_pub_key = match kernel_output.purpose() {
-        OutputPurpose::Transfer(_)
-        | OutputPurpose::LockThenTransfer(_, _)
-        | OutputPurpose::Burn => {
+    let vrf_pub_key = match kernel_output {
+        TxOutput::Transfer(_, _) | TxOutput::LockThenTransfer(_, _, _) | TxOutput::Burn(_) => {
             // only pool outputs can be staked
             return Err(ConsensusPoSError::RandomnessError(
-                PoSRandomnessError::InvalidOutputPurposeInStakeKernel(header.get_id()),
+                PoSRandomnessError::InvalidOutputTypeInStakeKernel(header.get_id()),
             ));
         }
-        OutputPurpose::StakePool(d) => d.as_ref().vrf_public_key().clone(),
-        OutputPurpose::ProduceBlockFromStake(_, pool_id) => {
+        TxOutput::StakePool(d) => d.as_ref().vrf_public_key().clone(),
+        TxOutput::ProduceBlockFromStake(_, _, pool_id) => {
             let pool_data = pos_accounting_view
-                .get_pool_data(*pool_id)?
-                .ok_or(ConsensusPoSError::PoolDataNotFound(*pool_id))?;
+                .get_pool_data(pool_id)?
+                .ok_or(ConsensusPoSError::PoolDataNotFound(pool_id))?;
             pool_data.vrf_public_key().clone()
         }
     };
diff --git a/mempool/src/pool/store.rs b/mempool/src/pool/store.rs
index f41406e1d..ce2eece2d 100644
--- a/mempool/src/pool/store.rs
+++ b/mempool/src/pool/store.rs
@@ -157,7 +157,7 @@ impl MempoolStore {
                     .ok_or_else(make_err)
             })
             .map(|output| match output.value() {
-                OutputValue::Coin(coin) => *coin,
+                OutputValue::Coin(coin) => coin,
                 OutputValue::Token(_) => Amount::from_atoms(0),
             })
     }
diff --git a/mempool/src/pool/try_get_fee.rs b/mempool/src/pool/try_get_fee.rs
index 6e4202529..2e993c240 100644
--- a/mempool/src/pool/try_get_fee.rs
+++ b/mempool/src/pool/try_get_fee.rs
@@ -67,7 +67,7 @@ where
             .outputs()
             .iter()
             .filter_map(|output| match output.value() {
-                OutputValue::Coin(coin) => Some(*coin),
+                OutputValue::Coin(coin) => Some(coin),
                 OutputValue::Token(_) => None,
             })
             .sum::<Option<_>>()
diff --git a/utxo/src/cache.rs b/utxo/src/cache.rs
index b2437ce9b..55bc9ad53 100644
--- a/utxo/src/cache.rs
+++ b/utxo/src/cache.rs
@@ -147,7 +147,7 @@ impl<P: UtxosView> UtxosCache<P> {
             .iter()
             .enumerate()
             // burned outputs should not be included into utxo set
-            .filter(|(_, output)| !output.purpose().is_burn())
+            .filter(|(_, output)| !output.is_burn())
             .try_for_each(|(idx, output)| {
                 let outpoint = OutPoint::new(id.clone(), idx as u32);
                 // by default no overwrite allowed.
@@ -190,7 +190,7 @@ impl<P: UtxosView> UtxosCache<P> {
         for (i, output) in tx.outputs().iter().enumerate() {
             let tx_outpoint = OutPoint::new(OutPointSourceId::from(tx.get_id()), i as u32);
 
-            if !output.purpose().is_burn() {
+            if !output.is_burn() {
                 self.spend_utxo(&tx_outpoint)?;
             }
         }
@@ -221,7 +221,7 @@ impl<P: UtxosView> UtxosCache<P> {
         if let Some(outputs) = reward_transactable.outputs() {
             let source_id = OutPointSourceId::from(*block_id);
             for (idx, output) in outputs.iter().enumerate() {
-                if output.purpose().is_burn() {
+                if output.is_burn() {
                     return Err(Error::InvalidBlockRewardOutputType(*block_id));
                 }
                 let outpoint = OutPoint::new(source_id.clone(), idx as u32);
diff --git a/utxo/src/utxo_entry.rs b/utxo/src/utxo_entry.rs
index 6a5747dbf..244ab567b 100644
--- a/utxo/src/utxo_entry.rs
+++ b/utxo/src/utxo_entry.rs
@@ -138,16 +138,16 @@ mod unit_test {
     use super::*;
     use crate::UtxoSource;
     use common::{
-        chain::{tokens::OutputValue, Destination, OutputPurpose, TxOutput},
+        chain::{tokens::OutputValue, Destination, TxOutput},
         primitives::Amount,
     };
     use rstest::rstest;
 
     fn some_utxo() -> Option<Utxo> {
         Some(Utxo::new(
-            TxOutput::new(
+            TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(1)),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ),
             false,
             UtxoSource::Mempool,


Test patch:
diff --git a/chainstate/storage/src/internal/test.rs b/chainstate/storage/src/internal/test.rs
index c125f5932..0064cf45e 100644
--- a/chainstate/storage/src/internal/test.rs
+++ b/chainstate/storage/src/internal/test.rs
@@ -16,7 +16,7 @@
 use super::*;
 use common::chain::tokens::OutputValue;
 use common::chain::transaction::signed_transaction::SignedTransaction;
-use common::chain::{Block, Destination, OutputPurpose, TxOutput};
+use common::chain::{Block, Destination, TxOutput};
 use common::primitives::{Amount, Idable, H256};
 use crypto::key::{KeyKind, PrivateKey};
 use crypto::random::{CryptoRng, Rng};
@@ -252,9 +252,9 @@ fn create_rand_utxo(rng: &mut (impl Rng + CryptoRng), block_height: u64) -> (Utx
     // just a random value generated, and also a random `is_block_reward` value.
     let random_value = rng.gen_range(0..(u128::MAX - 1));
     let (_, pub_key) = PrivateKey::new_from_rng(rng, KeyKind::Secp256k1Schnorr);
-    let output = TxOutput::new(
+    let output = TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(random_value)),
-        OutputPurpose::Transfer(Destination::PublicKey(pub_key)),
+        Destination::PublicKey(pub_key),
     );
     let is_block_reward = random_value % 3 == 0;
 
diff --git a/chainstate/test-framework/src/framework.rs b/chainstate/test-framework/src/framework.rs
index 60002a06f..f401158ea 100644
--- a/chainstate/test-framework/src/framework.rs
+++ b/chainstate/test-framework/src/framework.rs
@@ -235,7 +235,7 @@ fn process_block(#[case] seed: test_utils::random::Seed) {
     use common::{
         chain::{
             signature::inputsig::InputWitness, tokens::OutputValue, Destination, GenBlock,
-            OutPointSourceId, OutputPurpose, TxInput, TxOutput,
+            OutPointSourceId, TxInput, TxOutput,
         },
         primitives::{Amount, Id, Idable},
     };
@@ -254,9 +254,9 @@ fn process_block(#[case] seed: test_utils::random::Seed) {
                     ),
                     InputWitness::NoSignature(None),
                 )
-                .add_output(TxOutput::new(
+                .add_output(TxOutput::Transfer(
                     OutputValue::Coin(Amount::from_atoms(0)),
-                    OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                    Destination::AnyoneCanSpend,
                 ))
                 .build(),
         )
diff --git a/chainstate/test-framework/src/transaction_builder.rs b/chainstate/test-framework/src/transaction_builder.rs
index ca607f9eb..9c51223ee 100644
--- a/chainstate/test-framework/src/transaction_builder.rs
+++ b/chainstate/test-framework/src/transaction_builder.rs
@@ -16,7 +16,7 @@
 use common::{
     chain::{
         signature::inputsig::InputWitness, signed_transaction::SignedTransaction,
-        tokens::OutputValue, Destination, OutputPurpose, Transaction, TxInput, TxOutput,
+        tokens::OutputValue, Destination, Transaction, TxInput, TxOutput,
     },
     primitives::Amount,
 };
@@ -75,9 +75,9 @@ impl TransactionBuilder {
 
     /// Adds an output with the "anyone can spend" destination.
     pub fn add_anyone_can_spend_output(self, amount: u128) -> Self {
-        self.add_output(TxOutput::new(
+        self.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(amount)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
     }
 
@@ -119,13 +119,13 @@ fn build_transaction(#[case] seed: test_utils::random::Seed) {
         .with_inputs(vec![input.clone()])
         .with_witnesses(vec![InputWitness::NoSignature(None)])
         .add_input(input.clone(), witness)
-        .with_outputs(vec![TxOutput::new(
+        .with_outputs(vec![TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(100)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         )])
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(200)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
         .with_lock_time(lock_time)
         .build();
diff --git a/chainstate/test-framework/src/utils.rs b/chainstate/test-framework/src/utils.rs
index f1f192106..d039e523c 100644
--- a/chainstate/test-framework/src/utils.rs
+++ b/chainstate/test-framework/src/utils.rs
@@ -19,7 +19,7 @@ use common::{
     chain::{
         signature::inputsig::InputWitness,
         tokens::{OutputValue, TokenData, TokenTransfer},
-        Block, Destination, Genesis, OutPointSourceId, OutputPurpose, TxInput, TxOutput,
+        Block, Destination, Genesis, OutPointSourceId, TxInput, TxOutput,
     },
     primitives::{Amount, Idable},
 };
@@ -61,18 +61,14 @@ pub fn create_utxo_data(
     let new_output = match output.value() {
         OutputValue::Coin(output_value) => {
             let spent_value = Amount::from_atoms(rng.gen_range(0..output_value.into_atoms()));
-            let new_value = (*output_value - spent_value).unwrap();
+            let new_value = (output_value - spent_value).unwrap();
             utils::ensure!(new_value >= Amount::from_atoms(1));
-            TxOutput::new(
-                OutputValue::Coin(new_value),
-                OutputPurpose::Transfer(anyonecanspend_address()),
-            )
+            TxOutput::Transfer(OutputValue::Coin(new_value), anyonecanspend_address())
         }
-        OutputValue::Token(token_data) => match &**token_data {
-            TokenData::TokenTransfer(_transfer) => TxOutput::new(
-                OutputValue::Token(token_data.clone()),
-                OutputPurpose::Transfer(anyonecanspend_address()),
-            ),
+        OutputValue::Token(token_data) => match &*token_data {
+            TokenData::TokenTransfer(_transfer) => {
+                TxOutput::Transfer(OutputValue::Token(token_data), anyonecanspend_address())
+            }
             TokenData::TokenIssuance(issuance) => {
                 new_token_transfer_output(chainstate, &outsrc, issuance.amount_to_issue)
             }
@@ -104,15 +100,15 @@ pub fn create_multiple_utxo_data(
             if switch == 0 {
                 // issue nft
                 let min_tx_fee = chainstate.get_chain_config().token_min_issuance_fee();
-                if *output_value >= min_tx_fee {
+                if output_value >= min_tx_fee {
                     // Coin output is created intentionally besides issuance output in order to not waste utxo
                     // (e.g. single genesis output on issuance)
                     vec![
-                        TxOutput::new(
+                        TxOutput::Transfer(
                             random_nft_issuance(chainstate.get_chain_config().clone(), rng).into(),
-                            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                            Destination::AnyoneCanSpend,
                         ),
-                        TxOutput::new(OutputValue::Coin(min_tx_fee), OutputPurpose::Burn),
+                        TxOutput::Burn(OutputValue::Coin(min_tx_fee)),
                     ]
                 } else {
                     return None;
@@ -120,16 +116,16 @@ pub fn create_multiple_utxo_data(
             } else if switch == 1 {
                 // issue token
                 let min_tx_fee = chainstate.get_chain_config().token_min_issuance_fee();
-                if *output_value >= min_tx_fee {
+                if output_value >= min_tx_fee {
                     // Coin output is created intentionally besides issuance output in order to not waste utxo
                     // (e.g. single genesis output on issuance)
                     vec![
-                        TxOutput::new(
+                        TxOutput::Transfer(
                             random_token_issuance(chainstate.get_chain_config().clone(), rng)
                                 .into(),
-                            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                            Destination::AnyoneCanSpend,
                         ),
-                        TxOutput::new(OutputValue::Coin(min_tx_fee), OutputPurpose::Burn),
+                        TxOutput::Burn(OutputValue::Coin(min_tx_fee)),
                     ]
                 } else {
                     return None;
@@ -140,15 +136,12 @@ pub fn create_multiple_utxo_data(
                     .map(|_| {
                         let new_value = Amount::from_atoms(output_value.into_atoms() / num_outputs);
                         debug_assert!(new_value >= Amount::from_atoms(1));
-                        TxOutput::new(
-                            OutputValue::Coin(new_value),
-                            OutputPurpose::Transfer(anyonecanspend_address()),
-                        )
+                        TxOutput::Transfer(OutputValue::Coin(new_value), anyonecanspend_address())
                     })
                     .collect()
             }
         }
-        OutputValue::Token(token_data) => match &**token_data {
+        OutputValue::Token(token_data) => match &*token_data {
             TokenData::TokenTransfer(transfer) => {
                 if rng.gen::<bool>() {
                     // burn transferred tokens
@@ -157,13 +150,12 @@ pub fn create_multiple_utxo_data(
                     } else {
                         transfer.amount
                     };
-                    vec![TxOutput::new(
+                    vec![TxOutput::Burn(
                         TokenTransfer {
                             token_id: transfer.token_id,
                             amount: amount_to_burn,
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     )]
                 } else {
                     // transfer tokens again
@@ -173,21 +165,21 @@ pub fn create_multiple_utxo_data(
                             .map(|_| {
                                 let amount =
                                     Amount::from_atoms(transfer.amount.into_atoms() / num_outputs);
-                                TxOutput::new(
+                                TxOutput::Transfer(
                                     TokenTransfer {
                                         token_id: transfer.token_id,
                                         amount,
                                     }
                                     .into(),
-                                    OutputPurpose::Transfer(anyonecanspend_address()),
+                                    anyonecanspend_address(),
                                 )
                             })
                             .collect()
                     } else {
                         // transfer with a single output
-                        vec![TxOutput::new(
-                            OutputValue::Token(token_data.clone()),
-                            OutputPurpose::Transfer(anyonecanspend_address()),
+                        vec![TxOutput::Transfer(
+                            OutputValue::Token(token_data),
+                            anyonecanspend_address(),
                         )]
                     }
                 }
@@ -225,7 +217,7 @@ fn new_token_transfer_output(
     outsrc: &OutPointSourceId,
     amount: Amount,
 ) -> TxOutput {
-    TxOutput::new(
+    TxOutput::Transfer(
         TokenTransfer {
             token_id: match outsrc {
                 OutPointSourceId::Transaction(prev_tx) => {
@@ -238,7 +230,7 @@ fn new_token_transfer_output(
             amount,
         }
         .into(),
-        OutputPurpose::Transfer(anyonecanspend_address()),
+        anyonecanspend_address(),
     )
 }
 
@@ -247,7 +239,7 @@ fn new_token_burn_output(
     outsrc: &OutPointSourceId,
     amount_to_burn: Amount,
 ) -> TxOutput {
-    TxOutput::new(
+    TxOutput::Burn(
         TokenTransfer {
             token_id: match outsrc {
                 OutPointSourceId::Transaction(prev_tx) => {
@@ -260,7 +252,6 @@ fn new_token_burn_output(
             amount: amount_to_burn,
         }
         .into(),
-        OutputPurpose::Burn,
     )
 }
 
diff --git a/chainstate/test-suite/src/tests/chainstate_accounting_storage_tests.rs b/chainstate/test-suite/src/tests/chainstate_accounting_storage_tests.rs
index bf828da6f..b3f623861 100644
--- a/chainstate/test-suite/src/tests/chainstate_accounting_storage_tests.rs
+++ b/chainstate/test-suite/src/tests/chainstate_accounting_storage_tests.rs
@@ -76,20 +76,18 @@ fn make_tx_with_stake_pool(
 ) -> (SignedTransaction, PoolId, PoolData) {
     let destination = new_pub_key_destination(rng);
     let pool_data = create_pool_data(rng, destination, amount_to_stake);
-    let stake_output = TxOutput::new(
-        OutputValue::Coin(amount_to_stake),
-        OutputPurpose::StakePool(Box::new(StakePoolData::new(
-            anyonecanspend_address(),
-            pool_data.vrf_public_key().clone(),
-            pool_data.decommission_destination().clone(),
-            pool_data.margin_ratio_per_thousand(),
-            pool_data.cost_per_epoch(),
-        ))),
-    );
-
-    let transfer_output = TxOutput::new(
+    let stake_output = TxOutput::StakePool(Box::new(StakePoolData::new(
+        amount_to_stake,
+        anyonecanspend_address(),
+        pool_data.vrf_public_key().clone(),
+        pool_data.decommission_destination().clone(),
+        pool_data.margin_ratio_per_thousand(),
+        pool_data.cost_per_epoch(),
+    )));
+
+    let transfer_output = TxOutput::Transfer(
         OutputValue::Coin(amount_to_transfer),
-        OutputPurpose::Transfer(anyonecanspend_address()),
+        anyonecanspend_address(),
     );
 
     let input0_outpoint = OutPoint::new(outpoint.tx_id(), 0);
@@ -654,9 +652,9 @@ fn accounting_storage_no_accounting_data(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(100)),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
 
diff --git a/chainstate/test-suite/src/tests/chainstate_storage_tests.rs b/chainstate/test-suite/src/tests/chainstate_storage_tests.rs
index 46d852330..6464d592a 100644
--- a/chainstate/test-suite/src/tests/chainstate_storage_tests.rs
+++ b/chainstate/test-suite/src/tests/chainstate_storage_tests.rs
@@ -39,9 +39,9 @@ fn store_coin(#[case] seed: Seed) {
         let mut rng = make_seedable_rng(seed);
         let mut tf = TestFramework::builder(&mut rng).with_storage(storage.clone()).build();
 
-        let tx_output = TxOutput::new(
+        let tx_output = TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(100)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         );
 
         // spend coin
@@ -121,7 +121,7 @@ fn store_token(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenIssuance {
                     token_ticker: "XXXX".as_bytes().to_vec(),
                     amount_to_issue: Amount::from_atoms(rng.gen_range(1..u128::MAX)),
@@ -129,12 +129,11 @@ fn store_token(#[case] seed: Seed) {
                     metadata_uri: "http://uri".as_bytes().to_vec(),
                 }
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(tf.chainstate.get_chain_config().token_min_issuance_fee()),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(
+                tf.chainstate.get_chain_config().token_min_issuance_fee(),
+            )))
             .build();
         let tx_id = tx.transaction().get_id();
         let tx_outpoint = OutPoint::new(OutPointSourceId::Transaction(tx_id), 0);
@@ -196,9 +195,9 @@ fn reorg_store_coin(#[case] seed: Seed) {
         let genesis_id = tf.genesis().get_id();
 
         // create block
-        let tx_1_output = TxOutput::new(
+        let tx_1_output = TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(300)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         );
         let tx_1 = TransactionBuilder::new()
             .add_input(
@@ -217,9 +216,9 @@ fn reorg_store_coin(#[case] seed: Seed) {
         tf.process_block(block_1, BlockSource::Local).unwrap();
 
         // create parallel chain
-        let tx_2_output = TxOutput::new(
+        let tx_2_output = TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(200)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         );
         let tx_2 = TransactionBuilder::new()
             .add_input(
@@ -240,9 +239,9 @@ fn reorg_store_coin(#[case] seed: Seed) {
         tf.process_block(block_2, BlockSource::Local).unwrap();
 
         // produce one more block to cause reorg
-        let tx_3_output = TxOutput::new(
+        let tx_3_output = TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(100)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         );
         let tx_3 = TransactionBuilder::new()
             .add_input(
@@ -372,7 +371,7 @@ fn reorg_store_token(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenIssuance {
                     token_ticker: "AAAA".as_bytes().to_vec(),
                     amount_to_issue: Amount::from_atoms(rng.gen_range(1..u128::MAX)),
@@ -380,12 +379,11 @@ fn reorg_store_token(#[case] seed: Seed) {
                     metadata_uri: "http://uri".as_bytes().to_vec(),
                 }
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(tf.chainstate.get_chain_config().token_min_issuance_fee()),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(
+                tf.chainstate.get_chain_config().token_min_issuance_fee(),
+            )))
             .build();
         let tx_1_outpoint = OutPoint::new(
             OutPointSourceId::Transaction(tx_1.transaction().get_id()),
@@ -407,7 +405,7 @@ fn reorg_store_token(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenIssuance {
                     token_ticker: "BBBB".as_bytes().to_vec(),
                     amount_to_issue: bbbb_tokens_amount,
@@ -415,12 +413,11 @@ fn reorg_store_token(#[case] seed: Seed) {
                     metadata_uri: "http://uri".as_bytes().to_vec(),
                 }
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(tf.chainstate.get_chain_config().token_min_issuance_fee()),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(
+                tf.chainstate.get_chain_config().token_min_issuance_fee(),
+            )))
             .build();
         let tx_2_id = tx_2.transaction().get_id();
         let tx_2_outpoint = OutPoint::new(OutPointSourceId::Transaction(tx_2_id), 0);
@@ -440,13 +437,13 @@ fn reorg_store_token(#[case] seed: Seed) {
                 TxInput::new(OutPointSourceId::Transaction(tx_2_id), 0),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenData::TokenTransfer(TokenTransfer {
                     token_id: token_2_id,
                     amount: bbbb_tokens_amount,
                 })
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
             .build();
         let tx_3_id = tx_3.transaction().get_id();
@@ -553,9 +550,9 @@ fn reorg_store_coin_no_tx_index(#[case] seed: Seed, #[case] tx_index_enabled: bo
             let genesis_id = tf.genesis().get_id();
 
             // create block
-            let tx_1_output = TxOutput::new(
+            let tx_1_output = TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(300)),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             );
             let tx_1 = TransactionBuilder::new()
                 .add_input(
@@ -571,9 +568,9 @@ fn reorg_store_coin_no_tx_index(#[case] seed: Seed, #[case] tx_index_enabled: bo
             tf.process_block(block_1, BlockSource::Local).unwrap();
 
             // create parallel chain
-            let tx_2_output = TxOutput::new(
+            let tx_2_output = TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(200)),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             );
             let tx_2 = TransactionBuilder::new()
                 .add_input(
@@ -593,9 +590,9 @@ fn reorg_store_coin_no_tx_index(#[case] seed: Seed, #[case] tx_index_enabled: bo
             tf.process_block(block_2, BlockSource::Local).unwrap();
 
             // produce one more block to cause reorg
-            let tx_3_output = TxOutput::new(
+            let tx_3_output = TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(100)),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             );
             let tx_3 = TransactionBuilder::new()
                 .add_input(
diff --git a/chainstate/test-suite/src/tests/double_spend_tests.rs b/chainstate/test-suite/src/tests/double_spend_tests.rs
index 4acc0d850..d08b39459 100644
--- a/chainstate/test-suite/src/tests/double_spend_tests.rs
+++ b/chainstate/test-suite/src/tests/double_spend_tests.rs
@@ -248,9 +248,9 @@ fn overspend_multiple_outputs(#[case] seed: Seed) {
         let tx1 = tx_from_genesis(&tf.genesis(), &mut rng, tx1_output_value);
 
         let tx2_output_value = tx1_output_value - 1;
-        let tx2_output = TxOutput::new(
+        let tx2_output = TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(tx2_output_value)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         );
         let tx2 = TransactionBuilder::new()
             .add_input(
@@ -305,9 +305,9 @@ fn duplicate_input_in_the_same_tx(#[case] seed: Seed) {
         let second_tx = TransactionBuilder::new()
             .add_input(input.clone(), witness.clone())
             .add_input(input, witness)
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
         let second_tx_id = second_tx.transaction().get_id();
@@ -360,9 +360,9 @@ fn same_input_diff_sig_in_the_same_tx(#[case] seed: Seed) {
         let second_tx = TransactionBuilder::new()
             .add_input(input1, witness1)
             .add_input(input2, witness2)
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
         let second_tx_id = second_tx.transaction().get_id();
@@ -477,10 +477,9 @@ fn try_spend_burned_output_same_block(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::Burn,
-            ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(Amount::from_atoms(
+                rng.gen_range(100_000..200_000),
+            ))))
             .build();
         let second_tx = tx_from_tx(&first_tx, rng.gen_range(1000..2000));
 
@@ -512,10 +511,9 @@ fn try_spend_burned_output_different_blocks(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::Burn,
-            ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(Amount::from_atoms(
+                rng.gen_range(100_000..200_000),
+            ))))
             .build();
         let block = tf.make_block_builder().with_transactions(vec![first_tx.clone()]).build();
         tf.process_block(block, BlockSource::Local).unwrap();
@@ -538,9 +536,9 @@ fn tx_from_genesis(genesis: &Genesis, rng: &mut impl Rng, output_value: u128) ->
             TxInput::new(OutPointSourceId::BlockReward(genesis.get_id().into()), 0),
             empty_witness(rng),
         )
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(output_value)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         ))
         .build()
 }
@@ -548,9 +546,9 @@ fn tx_from_genesis(genesis: &Genesis, rng: &mut impl Rng, output_value: u128) ->
 // Creates a transaction with an input based on the specified transaction id.
 fn tx_from_tx(tx: &SignedTransaction, output_value: u128) -> SignedTransaction {
     let input = TxInput::new(tx.transaction().get_id().into(), 0);
-    let output = TxOutput::new(
+    let output = TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(output_value)),
-        OutputPurpose::Transfer(anyonecanspend_address()),
+        anyonecanspend_address(),
     );
     SignedTransaction::new(
         Transaction::new(0, vec![input], vec![output], 0).unwrap(),
diff --git a/chainstate/test-suite/src/tests/fungible_tokens.rs b/chainstate/test-suite/src/tests/fungible_tokens.rs
index 01e39e07d..7478cea60 100644
--- a/chainstate/test-suite/src/tests/fungible_tokens.rs
+++ b/chainstate/test-suite/src/tests/fungible_tokens.rs
@@ -26,7 +26,7 @@ use common::{
     chain::{
         signature::inputsig::InputWitness,
         tokens::{token_id, OutputValue, TokenData, TokenId},
-        Destination, OutPointSourceId, OutputPurpose, TxInput, TxOutput,
+        Destination, OutPointSourceId, TxInput, TxOutput,
     },
     primitives::{Amount, Idable},
 };
@@ -60,7 +60,7 @@ fn token_issue_test(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenIssuance {
                             token_ticker: random_string(&mut rng, 10..u16::MAX as usize)
                                 .as_bytes()
@@ -70,12 +70,9 @@ fn token_issue_test(#[case] seed: Seed) {
                             metadata_uri: random_string(&mut rng, 1..1024).as_bytes().to_vec(),
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process();
@@ -100,7 +97,7 @@ fn token_issue_test(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenIssuance {
                             token_ticker: b"".to_vec(),
                             amount_to_issue: Amount::from_atoms(rng.gen_range(1..u128::MAX)),
@@ -108,12 +105,9 @@ fn token_issue_test(#[case] seed: Seed) {
                             metadata_uri: random_string(&mut rng, 1..1024).as_bytes().to_vec(),
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process();
@@ -152,7 +146,7 @@ fn token_issue_test(#[case] seed: Seed) {
                                 TxInput::new(outpoint_source_id.clone(), 0),
                                 InputWitness::NoSignature(None),
                             )
-                            .add_output(TxOutput::new(
+                            .add_output(TxOutput::Transfer(
                                 TokenIssuance {
                                     token_ticker,
                                     amount_to_issue: Amount::from_atoms(
@@ -164,12 +158,9 @@ fn token_issue_test(#[case] seed: Seed) {
                                         .to_vec(),
                                 }
                                 .into(),
-                                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                            ))
-                            .add_output(TxOutput::new(
-                                OutputValue::Coin(token_min_issuance_fee),
-                                OutputPurpose::Burn,
+                                Destination::AnyoneCanSpend,
                             ))
+                            .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                             .build(),
                     )
                     .build_and_process();
@@ -196,7 +187,7 @@ fn token_issue_test(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenIssuance {
                             token_ticker: random_string(&mut rng, 1..5).as_bytes().to_vec(),
                             amount_to_issue: Amount::from_atoms(0),
@@ -204,12 +195,9 @@ fn token_issue_test(#[case] seed: Seed) {
                             metadata_uri: random_string(&mut rng, 1..1024).as_bytes().to_vec(),
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process();
@@ -235,7 +223,7 @@ fn token_issue_test(#[case] seed: Seed) {
                             TxInput::new(outpoint_source_id.clone(), 0),
                             InputWitness::NoSignature(None),
                         )
-                        .add_output(TxOutput::new(
+                        .add_output(TxOutput::Transfer(
                             TokenIssuance {
                                 token_ticker: random_string(&mut rng, 1..5).as_bytes().to_vec(),
                                 amount_to_issue: Amount::from_atoms(rng.gen_range(1..u128::MAX)),
@@ -243,12 +231,9 @@ fn token_issue_test(#[case] seed: Seed) {
                                 metadata_uri: random_string(&mut rng, 1..1024).as_bytes().to_vec(),
                             }
                             .into(),
-                            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                        ))
-                        .add_output(TxOutput::new(
-                            OutputValue::Coin(token_min_issuance_fee),
-                            OutputPurpose::Burn,
+                            Destination::AnyoneCanSpend,
                         ))
+                        .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                         .build(),
                 )
                 .build_and_process();
@@ -278,7 +263,7 @@ fn token_issue_test(#[case] seed: Seed) {
                             TxInput::new(outpoint_source_id.clone(), 0),
                             InputWitness::NoSignature(None),
                         )
-                        .add_output(TxOutput::new(
+                        .add_output(TxOutput::Transfer(
                             TokenIssuance {
                                 token_ticker: random_string(&mut rng, 1..5).as_bytes().to_vec(),
                                 amount_to_issue: Amount::from_atoms(rng.gen_range(1..u128::MAX)),
@@ -288,12 +273,9 @@ fn token_issue_test(#[case] seed: Seed) {
                                     .to_vec(),
                             }
                             .into(),
-                            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                        ))
-                        .add_output(TxOutput::new(
-                            OutputValue::Coin(token_min_issuance_fee),
-                            OutputPurpose::Burn,
+                            Destination::AnyoneCanSpend,
                         ))
+                        .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                         .build(),
                 )
                 .build_and_process();
@@ -319,7 +301,7 @@ fn token_issue_test(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenIssuance {
                             token_ticker: random_string(&mut rng, 1..5).as_bytes().to_vec(),
                             amount_to_issue: Amount::from_atoms(rng.gen_range(1..u128::MAX)),
@@ -327,12 +309,9 @@ fn token_issue_test(#[case] seed: Seed) {
                             metadata_uri: "https://💖🚁🌭.🦠🚀🚖🚧".as_bytes().to_vec(),
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process();
@@ -363,14 +342,11 @@ fn token_issue_test(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value.clone().into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -380,7 +356,7 @@ fn token_issue_test(#[case] seed: Seed) {
         let block = tf.block(*block_index.block_id());
         assert_eq!(
             block.transactions()[0].transaction().outputs()[0].value(),
-            &output_value.into()
+            output_value.into()
         );
     });
 }
@@ -414,14 +390,11 @@ fn token_transfer_test(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value.clone().into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -431,7 +404,7 @@ fn token_transfer_test(#[case] seed: Seed) {
         let token_id = token_id(block.transactions()[0].transaction()).unwrap();
         assert_eq!(
             block.transactions()[0].transaction().outputs()[0].value(),
-            &output_value.clone().into()
+            output_value.clone().into()
         );
         let issuance_outpoint_id: OutPointSourceId =
             block.transactions()[0].transaction().get_id().into();
@@ -446,9 +419,9 @@ fn token_transfer_test(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value.into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -469,13 +442,13 @@ fn token_transfer_test(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: Amount::from_atoms(total_funds.into_atoms() + 1),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -497,13 +470,13 @@ fn token_transfer_test(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id: TokenId::random_using(&mut rng),
                             amount: total_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -525,13 +498,13 @@ fn token_transfer_test(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: Amount::from_atoms(0),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -555,13 +528,13 @@ fn token_transfer_test(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: total_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -599,13 +572,13 @@ fn multiple_token_issuance_in_one_tx(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         issuance_value.clone().into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         issuance_value.clone().into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -630,14 +603,11 @@ fn multiple_token_issuance_in_one_tx(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         issuance_value.clone().into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -647,7 +617,7 @@ fn multiple_token_issuance_in_one_tx(#[case] seed: Seed) {
         let block = tf.block(*block_index.block_id());
         assert_eq!(
             block.transactions()[0].transaction().outputs()[0].value(),
-            &issuance_value.into()
+            issuance_value.into()
         );
     })
 }
@@ -661,7 +631,7 @@ fn token_issuance_with_insufficient_fee(#[case] seed: Seed) {
         let mut tf = TestFramework::builder(&mut rng).build();
         let total_funds = Amount::from_atoms(rng.gen_range(1..u128::MAX));
         let token_min_issuance_fee = tf.chainstate.get_chain_config().token_min_issuance_fee();
-        let coins_value = (tf.genesis().utxos()[0].value().clone().coin_amount().unwrap()
+        let coins_value = (tf.genesis().utxos()[0].value().coin_amount().unwrap()
             - token_min_issuance_fee)
             .unwrap();
         let genesis_outpoint_id = tf.genesis().get_id().into();
@@ -682,20 +652,17 @@ fn token_issuance_with_insufficient_fee(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         issuance_data.clone().into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(coins_value),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(
-                            (token_min_issuance_fee - Amount::from_atoms(1)).unwrap(),
-                        ),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(
+                        (token_min_issuance_fee - Amount::from_atoms(1)).unwrap(),
+                    )))
                     .build(),
             )
             .build();
@@ -722,14 +689,11 @@ fn token_issuance_with_insufficient_fee(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         issuance_data.into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -767,14 +731,11 @@ fn transfer_split_and_combine_tokens(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value.into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -795,21 +756,21 @@ fn transfer_split_and_combine_tokens(#[case] seed: Seed) {
                         InputWitness::NoSignature(None),
                     )
                     // One piece of tokens in the first output, other piece of tokens in the second output
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: (total_funds - quarter_funds).unwrap(),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: quarter_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -831,13 +792,13 @@ fn transfer_split_and_combine_tokens(#[case] seed: Seed) {
                         TxInput::new(split_outpoint_id, 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: total_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -879,14 +840,11 @@ fn burn_tokens(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value,
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -907,13 +865,12 @@ fn burn_tokens(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Burn(
                         TokenTransfer {
                             token_id,
                             amount: Amount::from_atoms(total_funds.into_atoms() + 1),
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     ))
                     .build(),
             )
@@ -935,21 +892,20 @@ fn burn_tokens(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Burn(
                         TokenTransfer {
                             token_id,
                             amount: quarter_funds,
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: half_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -969,21 +925,20 @@ fn burn_tokens(#[case] seed: Seed) {
                         TxInput::new(first_burn_outpoint_id, 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Burn(
                         TokenTransfer {
                             token_id,
                             amount: quarter_funds,
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: quarter_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1003,13 +958,12 @@ fn burn_tokens(#[case] seed: Seed) {
                         TxInput::new(second_burn_outpoint_id.clone(), 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Burn(
                         TokenTransfer {
                             token_id,
                             amount: quarter_funds,
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     ))
                     .build(),
             )
@@ -1028,13 +982,13 @@ fn burn_tokens(#[case] seed: Seed) {
                         TxInput::new(second_burn_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenTransfer {
                             token_id,
                             amount: quarter_funds,
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1087,18 +1041,15 @@ fn reorg_and_try_to_double_spend_tokens(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         issuance_data,
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -1123,17 +1074,16 @@ fn reorg_and_try_to_double_spend_tokens(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id.clone(), 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Burn(
                         TokenTransfer {
                             token_id,
                             amount: total_funds,
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(Amount::from_atoms(123455)),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1153,17 +1103,17 @@ fn reorg_and_try_to_double_spend_tokens(#[case] seed: Seed) {
                         TxInput::new(b1_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: total_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(Amount::from_atoms(123455)),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1186,9 +1136,9 @@ fn reorg_and_try_to_double_spend_tokens(#[case] seed: Seed) {
                         TxInput::new(b1_outpoint_id, 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value.clone(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1207,9 +1157,9 @@ fn reorg_and_try_to_double_spend_tokens(#[case] seed: Seed) {
                         TxInput::new(c1_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value,
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1229,17 +1179,17 @@ fn reorg_and_try_to_double_spend_tokens(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: total_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(Amount::from_atoms(123454)),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1265,17 +1215,16 @@ fn reorg_and_try_to_double_spend_tokens(#[case] seed: Seed) {
                         TxInput::new(b2_outpoint_id, 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Burn(
                         TokenTransfer {
                             token_id,
                             amount: total_funds,
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(Amount::from_atoms(123454)),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1301,17 +1250,16 @@ fn reorg_and_try_to_double_spend_tokens(#[case] seed: Seed) {
                         TxInput::new(c2_outpoint_id, 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Burn(
                         TokenTransfer {
                             token_id,
                             amount: total_funds,
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(Amount::from_atoms(123454)),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1336,9 +1284,9 @@ fn reorg_and_try_to_double_spend_tokens(#[case] seed: Seed) {
                         TxInput::new(d2_outpoint_id, 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(Amount::from_atoms(123453)),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1382,14 +1330,11 @@ fn attempt_to_print_tokens_one_output(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value,
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -1410,13 +1355,13 @@ fn attempt_to_print_tokens_one_output(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: (total_funds + Amount::from_atoms(1)).unwrap(),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1438,13 +1383,13 @@ fn attempt_to_print_tokens_one_output(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: total_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1483,14 +1428,11 @@ fn attempt_to_print_tokens_two_outputs(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value,
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -1511,21 +1453,21 @@ fn attempt_to_print_tokens_two_outputs(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: total_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: total_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1547,13 +1489,13 @@ fn attempt_to_print_tokens_two_outputs(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: total_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1590,18 +1532,15 @@ fn spend_different_token_than_one_in_input(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value,
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin((token_min_issuance_fee * 2).unwrap()),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -1626,15 +1565,15 @@ fn spend_different_token_than_one_in_input(#[case] seed: Seed) {
                         TxInput::new(first_issuance_outpoint_id, 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id: first_token_id,
                             amount: total_funds,
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenIssuance {
                             token_ticker: random_string(&mut rng, 1..5).as_bytes().to_vec(),
                             amount_to_issue: total_funds,
@@ -1642,16 +1581,13 @@ fn spend_different_token_than_one_in_input(#[case] seed: Seed) {
                             metadata_uri: b"https://some_site.meta".to_vec(),
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -1682,17 +1618,17 @@ fn spend_different_token_than_one_in_input(#[case] seed: Seed) {
                         TxInput::new(second_issuance_outpoint_id, 2),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id: first_token_id,
                             amount: Amount::from_atoms(total_funds.into_atoms() + 1),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin((token_min_issuance_fee * 2).unwrap()),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1735,14 +1671,11 @@ fn tokens_reorgs_and_cleanup_data(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         issuance_value.clone().into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -1764,11 +1697,11 @@ fn tokens_reorgs_and_cleanup_data(#[case] seed: Seed) {
         // Check issuance storage in the chain and in the storage
         assert_eq!(
             issuance_tx.transaction().outputs()[0].value(),
-            &issuance_value.clone().into()
+            issuance_value.clone().into()
         );
         assert_eq!(
             token_aux_data.issuance_tx().outputs()[0].value(),
-            &issuance_value.into()
+            issuance_value.into()
         );
 
         // Cause reorg
@@ -1818,7 +1751,7 @@ fn token_issuance_in_block_reward(#[case] seed: Seed) {
             crypto::key::PrivateKey::new_from_rng(&mut rng, crypto::key::KeyKind::Secp256k1Schnorr);
 
         // Check if it issuance
-        let reward_output = TxOutput::new(
+        let reward_output = TxOutput::Transfer(
             TokenIssuance {
                 token_ticker: random_string(&mut rng, 1..5).as_bytes().to_vec(),
                 amount_to_issue: total_funds,
@@ -1826,7 +1759,7 @@ fn token_issuance_in_block_reward(#[case] seed: Seed) {
                 metadata_uri: random_string(&mut rng, 1..1024).as_bytes().to_vec(),
             }
             .into(),
-            OutputPurpose::Transfer(Destination::PublicKey(pub_key.clone())),
+            Destination::PublicKey(pub_key.clone()),
         );
         let block = tf
             .make_block_builder()
@@ -1842,13 +1775,13 @@ fn token_issuance_in_block_reward(#[case] seed: Seed) {
         ));
 
         // Check if it transfer
-        let reward_output = TxOutput::new(
+        let reward_output = TxOutput::Transfer(
             TokenData::TokenTransfer(TokenTransfer {
                 token_id: TokenId::random_using(&mut rng),
                 amount: total_funds,
             })
             .into(),
-            OutputPurpose::Transfer(Destination::PublicKey(pub_key)),
+            Destination::PublicKey(pub_key),
         );
         let block = tf
             .make_block_builder()
@@ -1864,13 +1797,12 @@ fn token_issuance_in_block_reward(#[case] seed: Seed) {
         ));
 
         // Check if it burn
-        let reward_output = TxOutput::new(
+        let reward_output = TxOutput::Burn(
             TokenTransfer {
                 token_id: TokenId::random_using(&mut rng),
                 amount: total_funds,
             }
             .into(),
-            OutputPurpose::Burn,
         );
         let block = tf
             .make_block_builder()
@@ -1956,7 +1888,7 @@ fn issue_and_transfer_in_the_same_block(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenIssuance {
                     token_ticker: "XXXX".as_bytes().to_vec(),
                     amount_to_issue: Amount::from_atoms(rng.gen_range(100_000..u128::MAX)),
@@ -1964,12 +1896,9 @@ fn issue_and_transfer_in_the_same_block(#[case] seed: Seed) {
                     metadata_uri: "http://uri".as_bytes().to_vec(),
                 }
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(token_min_issuance_fee),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
             .build();
 
         let tx_2 = TransactionBuilder::new()
@@ -1980,13 +1909,13 @@ fn issue_and_transfer_in_the_same_block(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenData::TokenTransfer(TokenTransfer {
                     token_id: token_id(tx_1.transaction()).unwrap(),
                     amount: Amount::from_atoms(rng.gen_range(1..100_000)),
                 })
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
             .build();
 
diff --git a/chainstate/test-suite/src/tests/helpers/mod.rs b/chainstate/test-suite/src/tests/helpers/mod.rs
index 92206324e..b353100c2 100644
--- a/chainstate/test-suite/src/tests/helpers/mod.rs
+++ b/chainstate/test-suite/src/tests/helpers/mod.rs
@@ -17,8 +17,7 @@ use chainstate_test_framework::{anyonecanspend_address, TestFramework, Transacti
 use common::{
     chain::{
         block::timestamp::BlockTimestamp, signature::inputsig::InputWitness,
-        timelock::OutputTimeLock, tokens::OutputValue, OutputPurpose, Transaction, TxInput,
-        TxOutput,
+        timelock::OutputTimeLock, tokens::OutputValue, Transaction, TxInput, TxOutput,
     },
     primitives::{Amount, BlockDistance, Id, Idable},
 };
@@ -41,9 +40,10 @@ pub fn add_block_with_locked_output(
             InputWitness::NoSignature(None),
         )
         .add_anyone_can_spend_output(10000)
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::LockThenTransfer(
             OutputValue::Coin(Amount::from_atoms(100000)),
-            OutputPurpose::LockThenTransfer(anyonecanspend_address(), output_time_lock),
+            anyonecanspend_address(),
+            output_time_lock,
         ))
         .build();
     let tx_id = tx.transaction().get_id();
diff --git a/chainstate/test-suite/src/tests/homomorphism.rs b/chainstate/test-suite/src/tests/homomorphism.rs
index e5d7899ab..39c48ce66 100644
--- a/chainstate/test-suite/src/tests/homomorphism.rs
+++ b/chainstate/test-suite/src/tests/homomorphism.rs
@@ -61,9 +61,9 @@ fn coins_homomorphism(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
 
@@ -75,9 +75,9 @@ fn coins_homomorphism(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(rng.gen_range(1000..2000))),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
 
@@ -89,9 +89,9 @@ fn coins_homomorphism(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(rng.gen_range(100..200))),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
 
@@ -146,7 +146,7 @@ fn tokens_homomorphism(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenIssuance {
                     token_ticker: "XXXX".as_bytes().to_vec(),
                     amount_to_issue: Amount::from_atoms(rng.gen_range(100_000..u128::MAX)),
@@ -154,12 +154,11 @@ fn tokens_homomorphism(#[case] seed: Seed) {
                     metadata_uri: "http://uri".as_bytes().to_vec(),
                 }
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(tf.chainstate.get_chain_config().token_min_issuance_fee()),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(
+                tf.chainstate.get_chain_config().token_min_issuance_fee(),
+            )))
             .build();
         let token_id = token_id(tx_1.transaction()).unwrap();
 
@@ -171,13 +170,13 @@ fn tokens_homomorphism(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenData::TokenTransfer(TokenTransfer {
                     token_id,
                     amount: Amount::from_atoms(rng.gen_range(1..100_000)),
                 })
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
             .build();
 
diff --git a/chainstate/test-suite/src/tests/mod.rs b/chainstate/test-suite/src/tests/mod.rs
index 26d6d2992..93c4e61d0 100644
--- a/chainstate/test-suite/src/tests/mod.rs
+++ b/chainstate/test-suite/src/tests/mod.rs
@@ -20,10 +20,7 @@ use chainstate::BlockSource;
 use chainstate_test_framework::TestFramework;
 use common::chain::Destination;
 use common::{
-    chain::{
-        config::create_regtest, signature::inputsig::InputWitness, Block, GenBlock, Genesis,
-        OutputPurpose,
-    },
+    chain::{config::create_regtest, signature::inputsig::InputWitness, Block, GenBlock, Genesis},
     primitives::{BlockHeight, Id},
     Uint256,
 };
diff --git a/chainstate/test-suite/src/tests/nft_burn.rs b/chainstate/test-suite/src/tests/nft_burn.rs
index 865829536..5133f72a7 100644
--- a/chainstate/test-suite/src/tests/nft_burn.rs
+++ b/chainstate/test-suite/src/tests/nft_burn.rs
@@ -22,7 +22,7 @@ use common::chain::OutPointSourceId;
 use common::chain::{
     signature::inputsig::InputWitness,
     tokens::{token_id, OutputValue, TokenData, TokenTransfer},
-    Destination, OutputPurpose, TxInput, TxOutput,
+    Destination, TxInput, TxOutput,
 };
 use common::primitives::Amount;
 use common::primitives::Idable;
@@ -51,14 +51,11 @@ fn nft_burn_invalid_amount(#[case] seed: Seed) {
                 TxInput::new(genesis_outpoint_id, 0),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 random_nft_issuance(chain_config.clone(), &mut rng).into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(token_min_issuance_fee),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
             .build();
         let issuance_outpoint_id: OutPointSourceId = tx.transaction().get_id().into();
         let block_index = tf
@@ -80,13 +77,12 @@ fn nft_burn_invalid_amount(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Burn(
                         TokenTransfer {
                             token_id,
                             amount: Amount::from_atoms(rng.gen_range(2..123)),
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     ))
                     .build(),
             )
@@ -108,13 +104,12 @@ fn nft_burn_invalid_amount(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Burn(
                         TokenTransfer {
                             token_id,
                             amount: Amount::from_atoms(0),
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     ))
                     .build(),
             )
@@ -150,14 +145,11 @@ fn nft_burn_valid_case(#[case] seed: Seed) {
                 TxInput::new(genesis_outpoint_id, 0),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 random_nft_issuance(chain_config.clone(), &mut rng).into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(token_min_issuance_fee),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
             .build();
         let issuance_outpoint_id: OutPointSourceId = tx.transaction().get_id().into();
         let block_index = tf
@@ -176,13 +168,12 @@ fn nft_burn_valid_case(#[case] seed: Seed) {
                 TxInput::new(issuance_outpoint_id, 0),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Burn(
                 TokenTransfer {
                     token_id,
                     amount: Amount::from_atoms(1),
                 }
                 .into(),
-                OutputPurpose::Burn,
             ))
             .build();
         let first_burn_outpoint_id: OutPointSourceId = tx.transaction().get_id().into();
@@ -206,13 +197,13 @@ fn nft_burn_valid_case(#[case] seed: Seed) {
                         TxInput::new(first_burn_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: Amount::from_atoms(1),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
diff --git a/chainstate/test-suite/src/tests/nft_issuance.rs b/chainstate/test-suite/src/tests/nft_issuance.rs
index 2be484201..74b3a5e84 100644
--- a/chainstate/test-suite/src/tests/nft_issuance.rs
+++ b/chainstate/test-suite/src/tests/nft_issuance.rs
@@ -25,7 +25,7 @@ use common::chain::OutPointSourceId;
 use common::chain::{
     signature::inputsig::InputWitness,
     tokens::{Metadata, NftIssuance},
-    Destination, OutputPurpose, TxInput, TxOutput,
+    Destination, TxInput, TxOutput,
 };
 use common::primitives::Idable;
 use crypto::random::{CryptoRng, Rng};
@@ -59,7 +59,7 @@ fn nft_name_too_long(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         NftIssuance {
                             metadata: Metadata {
                                 creator: Some(random_creator(&mut rng)),
@@ -77,7 +77,7 @@ fn nft_name_too_long(#[case] seed: Seed) {
                             },
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -116,7 +116,7 @@ fn nft_empty_name(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         NftIssuance {
                             metadata: Metadata {
                                 creator: Some(random_creator(&mut rng)),
@@ -130,7 +130,7 @@ fn nft_empty_name(#[case] seed: Seed) {
                             },
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -180,7 +180,7 @@ fn nft_invalid_name(#[case] seed: Seed) {
                             TxInput::new(outpoint_source_id.clone(), 0),
                             InputWitness::NoSignature(None),
                         )
-                        .add_output(TxOutput::new(
+                        .add_output(TxOutput::Transfer(
                             NftIssuance {
                                 metadata: Metadata {
                                     creator: Some(random_creator(&mut rng)),
@@ -195,7 +195,7 @@ fn nft_invalid_name(#[case] seed: Seed) {
                                 },
                             }
                             .into(),
-                            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                            Destination::AnyoneCanSpend,
                         ))
                         .build(),
                 )
@@ -237,7 +237,7 @@ fn nft_ticker_too_long(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         NftIssuance {
                             metadata: Metadata {
                                 creator: Some(random_creator(&mut rng)),
@@ -255,7 +255,7 @@ fn nft_ticker_too_long(#[case] seed: Seed) {
                             },
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -295,7 +295,7 @@ fn nft_empty_ticker(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         NftIssuance {
                             metadata: Metadata {
                                 creator: Some(random_creator(&mut rng)),
@@ -309,7 +309,7 @@ fn nft_empty_ticker(#[case] seed: Seed) {
                             },
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -359,7 +359,7 @@ fn nft_invalid_ticker(#[case] seed: Seed) {
                             TxInput::new(outpoint_source_id.clone(), 0),
                             InputWitness::NoSignature(None),
                         )
-                        .add_output(TxOutput::new(
+                        .add_output(TxOutput::Transfer(
                             NftIssuance {
                                 metadata: Metadata {
                                     creator: Some(random_creator(&mut rng)),
@@ -374,7 +374,7 @@ fn nft_invalid_ticker(#[case] seed: Seed) {
                                 },
                             }
                             .into(),
-                            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                            Destination::AnyoneCanSpend,
                         ))
                         .build(),
                 )
@@ -416,7 +416,7 @@ fn nft_description_too_long(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         NftIssuance {
                             metadata: Metadata {
                                 creator: Some(random_creator(&mut rng)),
@@ -434,7 +434,7 @@ fn nft_description_too_long(#[case] seed: Seed) {
                             },
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -474,7 +474,7 @@ fn nft_empty_description(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         NftIssuance {
                             metadata: Metadata {
                                 creator: Some(random_creator(&mut rng)),
@@ -488,7 +488,7 @@ fn nft_empty_description(#[case] seed: Seed) {
                             },
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -538,7 +538,7 @@ fn nft_invalid_description(#[case] seed: Seed) {
                             TxInput::new(outpoint_source_id.clone(), 0),
                             InputWitness::NoSignature(None),
                         )
-                        .add_output(TxOutput::new(
+                        .add_output(TxOutput::Transfer(
                             NftIssuance {
                                 metadata: Metadata {
                                     creator: Some(random_creator(&mut rng)),
@@ -552,7 +552,7 @@ fn nft_invalid_description(#[case] seed: Seed) {
                                 },
                             }
                             .into(),
-                            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                            Destination::AnyoneCanSpend,
                         ))
                         .build(),
                 )
@@ -594,7 +594,7 @@ fn nft_icon_uri_too_long(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         NftIssuance {
                             metadata: Metadata {
                                 creator: Some(random_creator(&mut rng)),
@@ -611,7 +611,7 @@ fn nft_icon_uri_too_long(#[case] seed: Seed) {
                             },
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -667,14 +667,11 @@ fn nft_icon_uri_empty(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value.clone(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -691,7 +688,7 @@ fn nft_icon_uri_empty(#[case] seed: Seed) {
             tf.outputs_from_genblock(block.get_id().into()).values().next().unwrap().clone();
         let issuance_output = &outputs[0];
 
-        assert_eq!(issuance_output.value(), &output_value);
+        assert_eq!(issuance_output.value(), output_value);
     })
 }
 
@@ -728,7 +725,7 @@ fn nft_icon_uri_invalid(#[case] seed: Seed) {
                             TxInput::new(outpoint_source_id.clone(), 0),
                             InputWitness::NoSignature(None),
                         )
-                        .add_output(TxOutput::new(
+                        .add_output(TxOutput::Transfer(
                             NftIssuance {
                                 metadata: Metadata {
                                     creator: Some(random_creator(&mut rng)),
@@ -743,7 +740,7 @@ fn nft_icon_uri_invalid(#[case] seed: Seed) {
                                 },
                             }
                             .into(),
-                            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                            Destination::AnyoneCanSpend,
                         ))
                         .build(),
                 )
@@ -786,7 +783,7 @@ fn nft_metadata_uri_too_long(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         NftIssuance {
                             metadata: Metadata {
                                 creator: Some(random_creator(&mut rng)),
@@ -803,7 +800,7 @@ fn nft_metadata_uri_too_long(#[case] seed: Seed) {
                             },
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -858,14 +855,11 @@ fn nft_metadata_uri_empty(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value.clone(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -882,7 +876,7 @@ fn nft_metadata_uri_empty(#[case] seed: Seed) {
             tf.outputs_from_genblock(block.get_id().into()).values().next().unwrap().clone();
         let issuance_output = &outputs[0];
 
-        assert_eq!(issuance_output.value(), &output_value);
+        assert_eq!(issuance_output.value(), output_value);
     })
 }
 
@@ -919,7 +913,7 @@ fn nft_metadata_uri_invalid(#[case] seed: Seed) {
                             TxInput::new(outpoint_source_id.clone(), 0),
                             InputWitness::NoSignature(None),
                         )
-                        .add_output(TxOutput::new(
+                        .add_output(TxOutput::Transfer(
                             NftIssuance {
                                 metadata: Metadata {
                                     creator: Some(random_creator(&mut rng)),
@@ -934,7 +928,7 @@ fn nft_metadata_uri_invalid(#[case] seed: Seed) {
                                 },
                             }
                             .into(),
-                            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                            Destination::AnyoneCanSpend,
                         ))
                         .build(),
                 )
@@ -977,7 +971,7 @@ fn nft_media_uri_too_long(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         NftIssuance {
                             metadata: Metadata {
                                 creator: Some(random_creator(&mut rng)),
@@ -994,7 +988,7 @@ fn nft_media_uri_too_long(#[case] seed: Seed) {
                             },
                         }
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -1051,14 +1045,11 @@ fn nft_media_uri_empty(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value.clone(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -1075,7 +1066,7 @@ fn nft_media_uri_empty(#[case] seed: Seed) {
             tf.outputs_from_genblock(block.get_id().into()).values().next().unwrap().clone();
         let issuance_output = &outputs[0];
 
-        assert_eq!(issuance_output.value(), &output_value);
+        assert_eq!(issuance_output.value(), output_value);
     })
 }
 
@@ -1112,7 +1103,7 @@ fn nft_media_uri_invalid(#[case] seed: Seed) {
                             TxInput::new(outpoint_source_id.clone(), 0),
                             InputWitness::NoSignature(None),
                         )
-                        .add_output(TxOutput::new(
+                        .add_output(TxOutput::Transfer(
                             NftIssuance {
                                 metadata: Metadata {
                                     creator: Some(random_creator(&mut rng)),
@@ -1127,7 +1118,7 @@ fn nft_media_uri_invalid(#[case] seed: Seed) {
                                 },
                             }
                             .into(),
-                            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                            Destination::AnyoneCanSpend,
                         ))
                         .build(),
                 )
@@ -1170,7 +1161,7 @@ fn new_block_with_media_hash(
                     TxInput::new(input_source_id.clone(), 0),
                     InputWitness::NoSignature(None),
                 )
-                .add_output(TxOutput::new(
+                .add_output(TxOutput::Transfer(
                     NftIssuance {
                         metadata: Metadata {
                             creator: Some(random_creator(rng)),
@@ -1184,12 +1175,9 @@ fn new_block_with_media_hash(
                         },
                     }
                     .into(),
-                    OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                ))
-                .add_output(TxOutput::new(
-                    OutputValue::Coin(token_min_issuance_fee),
-                    OutputPurpose::Burn,
+                    Destination::AnyoneCanSpend,
                 ))
+                .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                 .build(),
         )
         .build()
@@ -1344,14 +1332,11 @@ fn nft_valid_case(#[case] seed: Seed) {
                         TxInput::new(outpoint_source_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value.clone().into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -1368,6 +1353,6 @@ fn nft_valid_case(#[case] seed: Seed) {
             tf.outputs_from_genblock(block.get_id().into()).values().next().unwrap().clone();
         let issuance_output = &outputs[0];
 
-        assert_eq!(issuance_output.value(), &output_value.into());
+        assert_eq!(issuance_output.value(), output_value.into());
     })
 }
diff --git a/chainstate/test-suite/src/tests/nft_reorgs.rs b/chainstate/test-suite/src/tests/nft_reorgs.rs
index 0d49273db..a50045433 100644
--- a/chainstate/test-suite/src/tests/nft_reorgs.rs
+++ b/chainstate/test-suite/src/tests/nft_reorgs.rs
@@ -19,7 +19,7 @@ use common::{
     chain::{
         signature::inputsig::InputWitness,
         tokens::{token_id, Metadata, NftIssuance, OutputValue, TokenData, TokenTransfer},
-        Destination, OutPointSourceId, OutputPurpose, TxInput, TxOutput,
+        Destination, OutPointSourceId, TxInput, TxOutput,
     },
     primitives::{Amount, Idable},
 };
@@ -81,18 +81,15 @@ fn reorg_and_try_to_double_spend_nfts(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         issuance_data,
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -121,17 +118,16 @@ fn reorg_and_try_to_double_spend_nfts(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id.clone(), 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Burn(
                         TokenTransfer {
                             token_id,
                             amount: Amount::from_atoms(1),
                         }
                         .into(),
-                        OutputPurpose::Burn,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(Amount::from_atoms(123455)),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -155,17 +151,17 @@ fn reorg_and_try_to_double_spend_nfts(#[case] seed: Seed) {
                         TxInput::new(b1_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: Amount::from_atoms(1),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(Amount::from_atoms(123455)),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -188,9 +184,9 @@ fn reorg_and_try_to_double_spend_nfts(#[case] seed: Seed) {
                         TxInput::new(b1_outpoint_id, 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value.clone(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -213,9 +209,9 @@ fn reorg_and_try_to_double_spend_nfts(#[case] seed: Seed) {
                         TxInput::new(c1_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         output_value,
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -236,17 +232,17 @@ fn reorg_and_try_to_double_spend_nfts(#[case] seed: Seed) {
                 TxInput::new(issuance_outpoint_id, 0),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenData::TokenTransfer(TokenTransfer {
                     token_id,
                     amount: Amount::from_atoms(1),
                 })
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(123454)),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
             .build();
         let b2_outpoint_id: OutPointSourceId = tx_2.transaction().get_id().into();
@@ -270,17 +266,16 @@ fn reorg_and_try_to_double_spend_nfts(#[case] seed: Seed) {
                 TxInput::new(b2_outpoint_id, 1),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Burn(
                 TokenTransfer {
                     token_id,
                     amount: Amount::from_atoms(1),
                 }
                 .into(),
-                OutputPurpose::Burn,
             ))
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(123454)),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
             .build();
         let c2_outpoint_id: OutPointSourceId = tx_2.transaction().get_id().into();
@@ -304,17 +299,16 @@ fn reorg_and_try_to_double_spend_nfts(#[case] seed: Seed) {
                 TxInput::new(c2_outpoint_id, 1),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Burn(
                 TokenTransfer {
                     token_id,
                     amount: Amount::from_atoms(1),
                 }
                 .into(),
-                OutputPurpose::Burn,
             ))
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(123454)),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
             .build();
         let d2_outpoint_id: OutPointSourceId = tx_2.transaction().get_id().into();
@@ -341,9 +335,9 @@ fn reorg_and_try_to_double_spend_nfts(#[case] seed: Seed) {
                         TxInput::new(d2_outpoint_id, 1),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin(Amount::from_atoms(123453)),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -396,14 +390,11 @@ fn nft_reorgs_and_cleanup_data(#[case] seed: Seed) {
                         TxInput::new(genesis_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         issuance_value.clone().into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-                    ))
-                    .add_output(TxOutput::new(
-                        OutputValue::Coin(token_min_issuance_fee),
-                        OutputPurpose::Burn,
+                        Destination::AnyoneCanSpend,
                     ))
+                    .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
                     .build(),
             )
             .build_and_process()
@@ -425,11 +416,11 @@ fn nft_reorgs_and_cleanup_data(#[case] seed: Seed) {
         // Check issuance storage in the chain and in the storage
         assert_eq!(
             issuance_tx.outputs()[0].value(),
-            &issuance_value.clone().into()
+            issuance_value.clone().into()
         );
         assert_eq!(
             token_aux_data.issuance_tx().outputs()[0].value(),
-            &issuance_value.into()
+            issuance_value.into()
         );
 
         // Cause reorg
diff --git a/chainstate/test-suite/src/tests/nft_transfer.rs b/chainstate/test-suite/src/tests/nft_transfer.rs
index f3d3ffb08..bb33d47d1 100644
--- a/chainstate/test-suite/src/tests/nft_transfer.rs
+++ b/chainstate/test-suite/src/tests/nft_transfer.rs
@@ -23,7 +23,7 @@ use common::{
     chain::{
         signature::inputsig::InputWitness,
         tokens::{token_id, Metadata, NftIssuance, OutputValue, TokenData, TokenId, TokenTransfer},
-        Destination, OutPointSourceId, OutputPurpose, TxInput, TxOutput,
+        Destination, OutPointSourceId, TxInput, TxOutput,
     },
     primitives::{Amount, BlockHeight},
 };
@@ -70,14 +70,11 @@ fn nft_transfer_wrong_id(#[case] seed: Seed) {
                 TxInput::new(genesis_outpoint_id, 0),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 output_value.clone().into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(token_min_issuance_fee),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
             .build();
         let issuance_outpoint_id: OutPointSourceId = tx.transaction().get_id().into();
         let block_index = tf
@@ -89,7 +86,7 @@ fn nft_transfer_wrong_id(#[case] seed: Seed) {
         let block = tf.block(*block_index.block_id());
         assert_eq!(
             block.transactions()[0].outputs()[0].value(),
-            &output_value.into()
+            output_value.into()
         );
         assert!(tf
             .outputs_from_genblock(block.get_id().into())
@@ -104,13 +101,13 @@ fn nft_transfer_wrong_id(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id: TokenId::random_using(&mut rng),
                             amount: Amount::from_atoms(1),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -159,14 +156,11 @@ fn nft_invalid_transfer(#[case] seed: Seed) {
                 TxInput::new(genesis_outpoint_id, 0),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 output_value.clone().into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(token_min_issuance_fee),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
             .build();
         let issuance_outpoint_id: OutPointSourceId = tx.transaction().get_id().into();
         let block_index = tf
@@ -178,7 +172,7 @@ fn nft_invalid_transfer(#[case] seed: Seed) {
         let block = tf.block(*block_index.block_id());
         let token_id = token_id(block.transactions()[0].transaction()).unwrap();
         assert_eq!(block.transactions()[0], tx);
-        assert_eq!(tx.outputs()[0].value(), &output_value.into());
+        assert_eq!(tx.outputs()[0].value(), output_value.into());
 
         // Try to transfer 0 NFT
         let result = tf
@@ -189,13 +183,13 @@ fn nft_invalid_transfer(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id.clone(), 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: Amount::from_atoms(0),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -219,13 +213,13 @@ fn nft_invalid_transfer(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id,
                             amount: Amount::from_atoms(rng.gen_range(2..123)),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -273,18 +267,15 @@ fn spend_different_nft_than_one_in_input(#[case] seed: Seed) {
                 TxInput::new(genesis_outpoint_id, 0),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 output_value,
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin((token_min_issuance_fee * 2).unwrap()),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(token_min_issuance_fee),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
             .build();
         let first_issuance_outpoint_id: OutPointSourceId = tx.transaction().get_id().into();
         let block_index = tf
@@ -307,15 +298,15 @@ fn spend_different_nft_than_one_in_input(#[case] seed: Seed) {
                 TxInput::new(first_issuance_outpoint_id, 1),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenData::TokenTransfer(TokenTransfer {
                     token_id: first_token_id,
                     amount: Amount::from_atoms(1),
                 })
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 NftIssuance {
                     metadata: Metadata {
                         creator: Some(random_creator(&mut rng)),
@@ -329,16 +320,13 @@ fn spend_different_nft_than_one_in_input(#[case] seed: Seed) {
                     },
                 }
                 .into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(token_min_issuance_fee),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(token_min_issuance_fee),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
             .build();
         let second_issuance_outpoint_id: OutPointSourceId = tx.transaction().get_id().into();
         let block_index = tf
@@ -370,17 +358,17 @@ fn spend_different_nft_than_one_in_input(#[case] seed: Seed) {
                         TxInput::new(second_issuance_outpoint_id, 2),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         TokenData::TokenTransfer(TokenTransfer {
                             token_id: first_token_id,
                             amount: Amount::from_atoms(2),
                         })
                         .into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         OutputValue::Coin((token_min_issuance_fee * 2).unwrap()),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -429,14 +417,11 @@ fn nft_valid_transfer(#[case] seed: Seed) {
                 TxInput::new(genesis_outpoint_id, 0),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 output_value.clone().into(),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(token_min_issuance_fee),
-                OutputPurpose::Burn,
+                Destination::AnyoneCanSpend,
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
             .build();
         let issuance_outpoint_id: OutPointSourceId = tx.transaction().get_id().into();
         let block_index = tf
@@ -449,7 +434,7 @@ fn nft_valid_transfer(#[case] seed: Seed) {
         let token_id = token_id(block.transactions()[0].transaction()).unwrap();
         assert_eq!(
             block.transactions()[0].outputs()[0].value(),
-            &output_value.into()
+            output_value.into()
         );
 
         // Valid case
@@ -465,9 +450,9 @@ fn nft_valid_transfer(#[case] seed: Seed) {
                         TxInput::new(issuance_outpoint_id, 0),
                         InputWitness::NoSignature(None),
                     )
-                    .add_output(TxOutput::new(
+                    .add_output(TxOutput::Transfer(
                         transfer_value.clone().into(),
-                        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                        Destination::AnyoneCanSpend,
                     ))
                     .build(),
             )
@@ -482,6 +467,6 @@ fn nft_valid_transfer(#[case] seed: Seed) {
             tf.outputs_from_genblock(block.get_id().into()).values().next().unwrap().clone();
         let transfer_output = &outputs[0];
 
-        assert_eq!(transfer_output.value(), &transfer_value.into());
+        assert_eq!(transfer_output.value(), transfer_value.into());
     })
 }
diff --git a/chainstate/test-suite/src/tests/output_timelock.rs b/chainstate/test-suite/src/tests/output_timelock.rs
index e9d0d0661..51363af28 100644
--- a/chainstate/test-suite/src/tests/output_timelock.rs
+++ b/chainstate/test-suite/src/tests/output_timelock.rs
@@ -24,7 +24,7 @@ use common::{
         signature::inputsig::InputWitness,
         timelock::OutputTimeLock,
         tokens::OutputValue,
-        OutPointSourceId, OutputPurpose, TxInput, TxOutput,
+        OutPointSourceId, TxInput, TxOutput,
     },
     primitives::{Amount, BlockHeight, Id, Idable},
 };
@@ -172,12 +172,10 @@ fn output_lock_until_height_but_spend_at_same_block(#[case] seed: Seed) {
                 InputWitness::NoSignature(None),
             )
             .add_anyone_can_spend_output(10000)
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::LockThenTransfer(
                 OutputValue::Coin(Amount::from_atoms(100000)),
-                OutputPurpose::LockThenTransfer(
-                    anyonecanspend_address(),
-                    OutputTimeLock::UntilHeight(BlockHeight::new(block_height_that_unlocks)),
-                ),
+                anyonecanspend_address(),
+                OutputTimeLock::UntilHeight(BlockHeight::new(block_height_that_unlocks)),
             ))
             .build();
         let tx2 = TransactionBuilder::new()
@@ -326,12 +324,10 @@ fn output_lock_for_block_count_but_spend_at_same_block(#[case] seed: Seed) {
                 InputWitness::NoSignature(None),
             )
             .add_anyone_can_spend_output(10000)
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::LockThenTransfer(
                 OutputValue::Coin(Amount::from_atoms(100000)),
-                OutputPurpose::LockThenTransfer(
-                    anyonecanspend_address(),
-                    OutputTimeLock::ForBlockCount(block_count_that_unlocks),
-                ),
+                anyonecanspend_address(),
+                OutputTimeLock::ForBlockCount(block_count_that_unlocks),
             ))
             .build();
         let tx2 = TransactionBuilder::new()
@@ -509,12 +505,10 @@ fn output_lock_until_time_but_spend_at_same_block(#[case] seed: Seed) {
                 InputWitness::NoSignature(None),
             )
             .add_anyone_can_spend_output(10000)
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::LockThenTransfer(
                 OutputValue::Coin(Amount::from_atoms(100000)),
-                OutputPurpose::LockThenTransfer(
-                    anyonecanspend_address(),
-                    OutputTimeLock::UntilTime(BlockTimestamp::from_int_seconds(lock_time)),
-                ),
+                anyonecanspend_address(),
+                OutputTimeLock::UntilTime(BlockTimestamp::from_int_seconds(lock_time)),
             ))
             .build();
 
@@ -656,12 +650,10 @@ fn output_lock_for_seconds_but_spend_at_same_block(#[case] seed: Seed) {
                 InputWitness::NoSignature(None),
             )
             .add_anyone_can_spend_output(10000)
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::LockThenTransfer(
                 OutputValue::Coin(Amount::from_atoms(100000)),
-                OutputPurpose::LockThenTransfer(
-                    anyonecanspend_address(),
-                    OutputTimeLock::ForSeconds(100),
-                ),
+                anyonecanspend_address(),
+                OutputTimeLock::ForSeconds(100),
             ))
             .build();
 
diff --git a/chainstate/test-suite/src/tests/pos_accounting_reorg.rs b/chainstate/test-suite/src/tests/pos_accounting_reorg.rs
index 63a1d052b..111444f92 100644
--- a/chainstate/test-suite/src/tests/pos_accounting_reorg.rs
+++ b/chainstate/test-suite/src/tests/pos_accounting_reorg.rs
@@ -75,16 +75,14 @@ fn stake_pool_reorg(#[case] seed: Seed) {
                     TxInput::new(OutPointSourceId::BlockReward(genesis_id.into()), 0),
                     empty_witness(&mut rng),
                 )
-                .add_output(TxOutput::new(
-                    OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                    OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                        anyonecanspend_address(),
-                        vrf_pub_key_a,
-                        destination_a,
-                        0,
-                        Amount::ZERO,
-                    ))),
-                ))
+                .add_output(TxOutput::StakePool(Box::new(StakePoolData::new(
+                    Amount::from_atoms(rng.gen_range(100_000..200_000)),
+                    anyonecanspend_address(),
+                    vrf_pub_key_a,
+                    destination_a,
+                    0,
+                    Amount::ZERO,
+                ))))
                 .build();
             let pool_id_a = pos_accounting::make_pool_id(&OutPoint::new(
                 OutPointSourceId::BlockReward(tf.genesis().get_id().into()),
@@ -97,9 +95,9 @@ fn stake_pool_reorg(#[case] seed: Seed) {
                     TxInput::new(OutPointSourceId::BlockReward(genesis_id.into()), 0),
                     empty_witness(&mut rng),
                 )
-                .add_output(TxOutput::new(
+                .add_output(TxOutput::Transfer(
                     OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                    OutputPurpose::Transfer(anyonecanspend_address()),
+                    anyonecanspend_address(),
                 ))
                 .build();
 
@@ -114,16 +112,14 @@ fn stake_pool_reorg(#[case] seed: Seed) {
                     ),
                     empty_witness(&mut rng),
                 )
-                .add_output(TxOutput::new(
-                    OutputValue::Coin(Amount::from_atoms(rng.gen_range(1000..100_000))),
-                    OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                        anyonecanspend_address(),
-                        vrf_pub_key_c,
-                        destination_c,
-                        0,
-                        Amount::ZERO,
-                    ))),
-                ))
+                .add_output(TxOutput::StakePool(Box::new(StakePoolData::new(
+                    Amount::from_atoms(rng.gen_range(1000..100_000)),
+                    anyonecanspend_address(),
+                    vrf_pub_key_c,
+                    destination_c,
+                    0,
+                    Amount::ZERO,
+                ))))
                 .build();
 
             // create block a
diff --git a/chainstate/test-suite/src/tests/pos_processing_tests.rs b/chainstate/test-suite/src/tests/pos_processing_tests.rs
index 601784709..d94a921d0 100644
--- a/chainstate/test-suite/src/tests/pos_processing_tests.rs
+++ b/chainstate/test-suite/src/tests/pos_processing_tests.rs
@@ -34,8 +34,8 @@ use common::{
         signature::inputsig::InputWitness,
         stakelock::StakePoolData,
         tokens::OutputValue,
-        ConsensusUpgrade, NetUpgrades, OutPoint, OutPointSourceId, OutputPurpose, PoolId, TxInput,
-        TxOutput, UpgradeVersion,
+        ConsensusUpgrade, NetUpgrades, OutPoint, OutPointSourceId, PoolId, TxInput, TxOutput,
+        UpgradeVersion,
     },
     primitives::{Amount, BlockHeight, Compact, Idable, H256},
     Uint256,
@@ -58,9 +58,14 @@ const TEST_EPOCH_LENGTH: NonZeroU64 = match NonZeroU64::new(2) {
 };
 const TEST_SEALED_EPOCH_DISTANCE: usize = 0;
 
-fn create_stake_pool_data(rng: &mut (impl Rng + CryptoRng), vrf_pk: VRFPublicKey) -> StakePoolData {
+fn create_stake_pool_data(
+    rng: &mut (impl Rng + CryptoRng),
+    amount: Amount,
+    vrf_pk: VRFPublicKey,
+) -> StakePoolData {
     let destination = super::new_pub_key_destination(rng);
     StakePoolData::new(
+        amount,
         anyonecanspend_address(),
         vrf_pk,
         destination,
@@ -80,10 +85,7 @@ fn add_block_with_stake_pool(
             TxInput::new(OutPointSourceId::BlockReward(genesis_id.into()), 0),
             empty_witness(rng),
         )
-        .add_output(TxOutput::new(
-            OutputValue::Coin(Amount::from_atoms(1)),
-            OutputPurpose::StakePool(Box::new(stake_pool_data)),
-        ))
+        .add_output(TxOutput::StakePool(Box::new(stake_pool_data)))
         .build();
     let tx_id = tx.transaction().get_id();
     let pool_id = pos_accounting::make_pool_id(&OutPoint::new(
@@ -111,13 +113,10 @@ fn add_block_with_2_stake_pools(
     );
     let tx1 = TransactionBuilder::new()
         .add_input(TxInput::from(outpoint_genesis.clone()), empty_witness(rng))
-        .add_output(TxOutput::new(
-            OutputValue::Coin(Amount::from_atoms(1)),
-            OutputPurpose::StakePool(Box::new(stake_pool_data1)),
-        ))
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::StakePool(Box::new(stake_pool_data1)))
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(1)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         ))
         .build();
     let stake_outpoint1 =
@@ -130,10 +129,7 @@ fn add_block_with_2_stake_pools(
             TxInput::from(transfer_outpoint1.clone()),
             empty_witness(rng),
         )
-        .add_output(TxOutput::new(
-            OutputValue::Coin(Amount::from_atoms(1)),
-            OutputPurpose::StakePool(Box::new(stake_pool_data2)),
-        ))
+        .add_output(TxOutput::StakePool(Box::new(stake_pool_data2)))
         .build();
     let outpoint2 = OutPoint::new(OutPointSourceId::Transaction(tx2.transaction().get_id()), 0);
 
@@ -217,7 +213,7 @@ fn setup_test_chain_with_staked_pool(
 
     let mut tf = TestFramework::builder(rng).with_chain_config(chain_config).build();
 
-    let stake_pool_data = create_stake_pool_data(rng, vrf_pk);
+    let stake_pool_data = create_stake_pool_data(rng, Amount::from_atoms(1), vrf_pk);
     let (stake_pool_outpoint, pool_id) = add_block_with_stake_pool(rng, &mut tf, stake_pool_data);
 
     (tf, stake_pool_outpoint, pool_id)
@@ -269,10 +265,8 @@ fn pos_basic(#[case] seed: Seed) {
     );
 
     // valid case
-    let reward_output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(1)),
-        OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
-    );
+    let reward_output =
+        TxOutput::ProduceBlockFromStake(Amount::from_atoms(1), anyonecanspend_address(), pool_id);
     tf.make_block_builder()
         .with_consensus_data(consensus_data)
         .with_timestamp(block_timestamp)
@@ -349,7 +343,7 @@ fn pos_invalid_kernel_input(#[case] seed: Seed) {
         ChainstateError::ProcessBlockError(BlockError::CheckBlockFailed(
             CheckBlockError::ConsensusVerificationFailed(ConsensusVerificationError::PoSError(
                 ConsensusPoSError::RandomnessError(
-                    PoSRandomnessError::InvalidOutputPurposeInStakeKernel(_)
+                    PoSRandomnessError::InvalidOutputTypeInStakeKernel(_)
                 )
             ))
         ))
@@ -495,9 +489,10 @@ fn pos_invalid_vrf(#[case] seed: Seed) {
     {
         // valid case
         let consensus_data = ConsensusData::PoS(Box::new(valid_pos_data));
-        let reward_output = TxOutput::new(
-            OutputValue::Coin(Amount::from_atoms(1)),
-            OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
+        let reward_output = TxOutput::ProduceBlockFromStake(
+            Amount::from_atoms(1),
+            anyonecanspend_address(),
+            pool_id,
         );
         tf.make_block_builder()
             .with_consensus_data(consensus_data)
@@ -563,10 +558,8 @@ fn pos_invalid_pool_id(#[case] seed: Seed) {
     );
 
     // test valid case
-    let reward_output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(1)),
-        OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
-    );
+    let reward_output =
+        TxOutput::ProduceBlockFromStake(Amount::from_atoms(1), anyonecanspend_address(), pool_id);
     tf.make_block_builder()
         .with_consensus_data(ConsensusData::PoS(Box::new(valid_pos_data)))
         .with_reward(vec![reward_output])
@@ -603,7 +596,7 @@ fn not_sealed_pool_cannot_be_used(#[case] seed: Seed) {
         .build();
     let mut tf = TestFramework::builder(&mut rng).with_chain_config(chain_config).build();
 
-    let stake_pool_data = create_stake_pool_data(&mut rng, vrf_pk.clone());
+    let stake_pool_data = create_stake_pool_data(&mut rng, Amount::from_atoms(1), vrf_pk.clone());
     let (stake_pool_outpoint, pool_id) =
         add_block_with_stake_pool(&mut rng, &mut tf, stake_pool_data);
 
@@ -672,10 +665,8 @@ fn spend_stake_pool_in_block_reward(#[case] seed: Seed) {
         1,
     )
     .expect("should be able to mine");
-    let reward_output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(1)),
-        OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
-    );
+    let reward_output =
+        TxOutput::ProduceBlockFromStake(Amount::from_atoms(1), anyonecanspend_address(), pool_id);
     tf.make_block_builder()
         .with_consensus_data(ConsensusData::PoS(Box::new(pos_data)))
         .with_reward(vec![reward_output])
@@ -700,10 +691,8 @@ fn spend_stake_pool_in_block_reward(#[case] seed: Seed) {
         1,
     )
     .expect("should be able to mine");
-    let reward_output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(1)),
-        OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
-    );
+    let reward_output =
+        TxOutput::ProduceBlockFromStake(Amount::from_atoms(1), anyonecanspend_address(), pool_id);
     tf.make_block_builder()
         .with_consensus_data(ConsensusData::PoS(Box::new(pos_data)))
         .with_reward(vec![reward_output])
@@ -735,10 +724,8 @@ fn spend_stake_pool_in_block_reward(#[case] seed: Seed) {
         2,
     )
     .expect("should be able to mine");
-    let reward_output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(1)),
-        OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
-    );
+    let reward_output =
+        TxOutput::ProduceBlockFromStake(Amount::from_atoms(1), anyonecanspend_address(), pool_id);
     tf.make_block_builder()
         .with_consensus_data(ConsensusData::PoS(Box::new(pos_data)))
         .with_reward(vec![reward_output])
@@ -787,8 +774,9 @@ fn mismatched_pools_in_kernel_and_reward(#[case] seed: Seed) {
 
     // create initial chain: genesis <- block_1
     // block1 creates 2 separate pools
-    let stake_pool_data1 = create_stake_pool_data(&mut rng, vrf_pk_1.clone());
-    let stake_pool_data2 = create_stake_pool_data(&mut rng, vrf_pk_2);
+    let stake_pool_data1 =
+        create_stake_pool_data(&mut rng, Amount::from_atoms(1), vrf_pk_1.clone());
+    let stake_pool_data2 = create_stake_pool_data(&mut rng, Amount::from_atoms(1), vrf_pk_2);
     let (stake_pool_outpoint1, pool_id1, _, pool_id2) =
         add_block_with_2_stake_pools(&mut rng, &mut tf, stake_pool_data1, stake_pool_data2);
 
@@ -810,10 +798,8 @@ fn mismatched_pools_in_kernel_and_reward(#[case] seed: Seed) {
         1,
     )
     .expect("should be able to mine");
-    let reward_output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(1)),
-        OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id2),
-    );
+    let reward_output =
+        TxOutput::ProduceBlockFromStake(Amount::from_atoms(1), anyonecanspend_address(), pool_id2);
     let res = tf
         .make_block_builder()
         .with_consensus_data(ConsensusData::PoS(Box::new(pos_data)))
@@ -843,12 +829,8 @@ fn stake_pool_as_reward_output(#[case] seed: Seed) {
     let mut tf = TestFramework::builder(&mut rng).with_chain_config(chain_config).build();
 
     let (_, vrf_pk) = VRFPrivateKey::new_from_rng(&mut rng, VRFKeyKind::Schnorrkel);
-    let stake_pool_data = create_stake_pool_data(&mut rng, vrf_pk);
-
-    let reward_output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(1)),
-        OutputPurpose::StakePool(Box::new(stake_pool_data)),
-    );
+    let stake_pool_data = create_stake_pool_data(&mut rng, Amount::from_atoms(1), vrf_pk);
+    let reward_output = TxOutput::StakePool(Box::new(stake_pool_data));
     let block = tf.make_block_builder().with_reward(vec![reward_output]).build();
     let block_id = block.get_id();
     assert_eq!(
@@ -896,10 +878,8 @@ fn check_pool_balance_after_reorg(#[case] seed: Seed) {
         1,
     )
     .expect("should be able to mine");
-    let reward_output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(1)),
-        OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
-    );
+    let reward_output =
+        TxOutput::ProduceBlockFromStake(Amount::from_atoms(1), anyonecanspend_address(), pool_id);
     tf.make_block_builder()
         .with_consensus_data(ConsensusData::PoS(Box::new(pos_data)))
         .with_reward(vec![reward_output])
@@ -925,10 +905,8 @@ fn check_pool_balance_after_reorg(#[case] seed: Seed) {
         1,
     )
     .expect("should be able to mine");
-    let reward_output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(1)),
-        OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
-    );
+    let reward_output =
+        TxOutput::ProduceBlockFromStake(Amount::from_atoms(1), anyonecanspend_address(), pool_id);
     let block_c_index = tf
         .make_block_builder()
         .with_consensus_data(ConsensusData::PoS(Box::new(pos_data)))
@@ -963,10 +941,8 @@ fn check_pool_balance_after_reorg(#[case] seed: Seed) {
         2,
     )
     .expect("should be able to mine");
-    let reward_output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(1)),
-        OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
-    );
+    let reward_output =
+        TxOutput::ProduceBlockFromStake(Amount::from_atoms(1), anyonecanspend_address(), pool_id);
     tf.make_block_builder()
         .with_consensus_data(ConsensusData::PoS(Box::new(pos_data)))
         .with_reward(vec![reward_output])
diff --git a/chainstate/test-suite/src/tests/processing_tests.rs b/chainstate/test-suite/src/tests/processing_tests.rs
index b2797768b..bbddc69f5 100644
--- a/chainstate/test-suite/src/tests/processing_tests.rs
+++ b/chainstate/test-suite/src/tests/processing_tests.rs
@@ -37,7 +37,7 @@ use common::{
         timelock::OutputTimeLock,
         tokens::OutputValue,
         Block, ConsensusUpgrade, Destination, GenBlock, NetUpgrades, OutPointSourceId,
-        OutputPurpose, OutputSpentState, TxInput, TxOutput, UpgradeVersion,
+        OutputSpentState, TxInput, TxOutput, UpgradeVersion,
     },
     primitives::{Amount, BlockHeight, Compact, Id, Idable},
     Uint256,
@@ -71,10 +71,7 @@ fn invalid_block_reward_types(#[case] seed: Seed) {
         // Case 1: reward is a simple transfer
         let block = tf
             .make_block_builder()
-            .with_reward(vec![TxOutput::new(
-                coins.clone(),
-                OutputPurpose::Transfer(destination.clone()),
-            )])
+            .with_reward(vec![TxOutput::Transfer(coins.clone(), destination.clone())])
             .add_test_transaction_from_best_block(&mut rng)
             .build();
 
@@ -89,12 +86,10 @@ fn invalid_block_reward_types(#[case] seed: Seed) {
         // Case 2: reward is locked until height
         let block = tf
             .make_block_builder()
-            .with_reward(vec![TxOutput::new(
+            .with_reward(vec![TxOutput::LockThenTransfer(
                 coins.clone(),
-                OutputPurpose::LockThenTransfer(
-                    destination.clone(),
-                    OutputTimeLock::UntilHeight(BlockHeight::max()),
-                ),
+                destination.clone(),
+                OutputTimeLock::UntilHeight(BlockHeight::max()),
             )])
             .add_test_transaction_from_best_block(&mut rng)
             .build();
@@ -110,12 +105,10 @@ fn invalid_block_reward_types(#[case] seed: Seed) {
         // Case 3: reward is locked until a specific time
         let block = tf
             .make_block_builder()
-            .with_reward(vec![TxOutput::new(
+            .with_reward(vec![TxOutput::LockThenTransfer(
                 coins.clone(),
-                OutputPurpose::LockThenTransfer(
-                    destination.clone(),
-                    OutputTimeLock::UntilTime(BlockTimestamp::from_int_seconds(u64::MAX)),
-                ),
+                destination.clone(),
+                OutputTimeLock::UntilTime(BlockTimestamp::from_int_seconds(u64::MAX)),
             )])
             .add_test_transaction_from_best_block(&mut rng)
             .build();
@@ -131,12 +124,10 @@ fn invalid_block_reward_types(#[case] seed: Seed) {
         // Case 4: reward is locked for an amount of seconds
         let block = tf
             .make_block_builder()
-            .with_reward(vec![TxOutput::new(
+            .with_reward(vec![TxOutput::LockThenTransfer(
                 coins.clone(),
-                OutputPurpose::LockThenTransfer(
-                    destination.clone(),
-                    OutputTimeLock::ForSeconds(u64::MAX),
-                ),
+                destination.clone(),
+                OutputTimeLock::ForSeconds(u64::MAX),
             )])
             .add_test_transaction_from_best_block(&mut rng)
             .build();
@@ -152,12 +143,10 @@ fn invalid_block_reward_types(#[case] seed: Seed) {
         // Case 5: reward is locked for an invalid number
         let block = tf
             .make_block_builder()
-            .with_reward(vec![TxOutput::new(
+            .with_reward(vec![TxOutput::LockThenTransfer(
                 coins.clone(),
-                OutputPurpose::LockThenTransfer(
-                    destination.clone(),
-                    OutputTimeLock::ForBlockCount(u64::MAX),
-                ),
+                destination.clone(),
+                OutputTimeLock::ForBlockCount(u64::MAX),
             )])
             .add_test_transaction_from_best_block(&mut rng)
             .build();
@@ -179,12 +168,10 @@ fn invalid_block_reward_types(#[case] seed: Seed) {
 
         let block = tf
             .make_block_builder()
-            .with_reward(vec![TxOutput::new(
+            .with_reward(vec![TxOutput::LockThenTransfer(
                 coins.clone(),
-                OutputPurpose::LockThenTransfer(
-                    destination.clone(),
-                    OutputTimeLock::ForBlockCount(reward_lock_distance as u64 - 1),
-                ),
+                destination.clone(),
+                OutputTimeLock::ForBlockCount(reward_lock_distance as u64 - 1),
             )])
             .add_test_transaction_from_best_block(&mut rng)
             .build();
@@ -206,16 +193,14 @@ fn invalid_block_reward_types(#[case] seed: Seed) {
         let (_, vrf_pub_key) = VRFPrivateKey::new_from_rng(&mut rng, VRFKeyKind::Schnorrkel);
         let block = tf
             .make_block_builder()
-            .with_reward(vec![TxOutput::new(
-                coins.clone(),
-                OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                    anyonecanspend_address(),
-                    vrf_pub_key,
-                    decommission_destination,
-                    0,
-                    Amount::ZERO,
-                ))),
-            )])
+            .with_reward(vec![TxOutput::StakePool(Box::new(StakePoolData::new(
+                Amount::from_atoms(10),
+                anyonecanspend_address(),
+                vrf_pub_key,
+                decommission_destination,
+                0,
+                Amount::ZERO,
+            )))])
             .add_test_transaction_from_best_block(&mut rng)
             .build();
 
@@ -235,12 +220,10 @@ fn invalid_block_reward_types(#[case] seed: Seed) {
 
         let block = tf
             .make_block_builder()
-            .with_reward(vec![TxOutput::new(
+            .with_reward(vec![TxOutput::LockThenTransfer(
                 coins,
-                OutputPurpose::LockThenTransfer(
-                    destination,
-                    OutputTimeLock::ForBlockCount(reward_lock_distance as u64),
-                ),
+                destination,
+                OutputTimeLock::ForBlockCount(reward_lock_distance as u64),
             )])
             .add_test_transaction_from_best_block(&mut rng)
             .build();
@@ -394,10 +377,7 @@ fn transaction_processing_order(#[case] seed: Seed) {
             Transaction::new(
                 0,
                 vec![TxInput::new(tf.genesis().get_id().into(), 0)],
-                vec![TxOutput::new(
-                    tf.genesis().utxos()[0].value().clone(),
-                    OutputPurpose::Transfer(anyonecanspend_address()),
-                )],
+                vec![TxOutput::Transfer(tf.genesis().utxos()[0].value(), anyonecanspend_address())],
                 0,
             )
             .unwrap(),
@@ -410,9 +390,9 @@ fn transaction_processing_order(#[case] seed: Seed) {
             Transaction::new(
                 0,
                 vec![TxInput::new(tx1.transaction().get_id().into(), 0)],
-                vec![TxOutput::new(
-                    tx1.transaction().outputs()[0].value().clone(),
-                    OutputPurpose::Transfer(anyonecanspend_address()),
+                vec![TxOutput::Transfer(
+                    tx1.transaction().outputs()[0].value(),
+                    anyonecanspend_address(),
                 )],
                 0,
             )
@@ -773,12 +753,10 @@ fn consensus_type(#[case] seed: Seed) {
         let mut mined_block = tf
             .make_block_builder()
             .with_parent(prev_block.get_id().into())
-            .with_reward(vec![TxOutput::new(
+            .with_reward(vec![TxOutput::LockThenTransfer(
                 OutputValue::Coin(Amount::from_atoms(10)),
-                OutputPurpose::LockThenTransfer(
-                    Destination::PublicKey(pub_key),
-                    OutputTimeLock::ForBlockCount(reward_lock_distance as u64),
-                ),
+                Destination::PublicKey(pub_key),
+                OutputTimeLock::ForBlockCount(reward_lock_distance as u64),
             )])
             .add_test_transaction_from_block(&prev_block, &mut rng)
             .build();
@@ -843,12 +821,10 @@ fn consensus_type(#[case] seed: Seed) {
         let mut mined_block = tf
             .make_block_builder()
             .with_parent(prev_block.get_id().into())
-            .with_reward(vec![TxOutput::new(
+            .with_reward(vec![TxOutput::LockThenTransfer(
                 OutputValue::Coin(Amount::from_atoms(10)),
-                OutputPurpose::LockThenTransfer(
-                    Destination::PublicKey(pub_key),
-                    OutputTimeLock::ForBlockCount(reward_lock_distance as u64),
-                ),
+                Destination::PublicKey(pub_key),
+                OutputTimeLock::ForBlockCount(reward_lock_distance as u64),
             )])
             .add_test_transaction_from_block(&prev_block, &mut rng)
             .build();
@@ -902,12 +878,10 @@ fn pow(#[case] seed: Seed) {
     let (_, pub_key) = PrivateKey::new_from_rng(&mut rng, KeyKind::Secp256k1Schnorr);
     let mut random_invalid_block = tf
         .make_block_builder()
-        .with_reward(vec![TxOutput::new(
+        .with_reward(vec![TxOutput::LockThenTransfer(
             OutputValue::Coin(Amount::from_atoms(10)),
-            OutputPurpose::LockThenTransfer(
-                Destination::PublicKey(pub_key),
-                OutputTimeLock::ForBlockCount(reward_lock_distance as u64),
-            ),
+            Destination::PublicKey(pub_key),
+            OutputTimeLock::ForBlockCount(reward_lock_distance as u64),
         )])
         .add_test_transaction_from_best_block(&mut rng)
         .build();
@@ -974,12 +948,10 @@ fn read_block_reward_from_storage(#[case] seed: Seed) {
         .map(|_| {
             let amount = Amount::from_atoms(rng.gen::<u128>() % 50);
             let pub_key = PrivateKey::new_from_rng(&mut rng, KeyKind::Secp256k1Schnorr).1;
-            TxOutput::new(
+            TxOutput::LockThenTransfer(
                 OutputValue::Coin(amount),
-                OutputPurpose::LockThenTransfer(
-                    Destination::PublicKey(pub_key),
-                    OutputTimeLock::ForBlockCount(reward_lock_distance as u64),
-                ),
+                Destination::PublicKey(pub_key),
+                OutputTimeLock::ForBlockCount(reward_lock_distance as u64),
             )
         })
         .collect::<Vec<_>>();
@@ -1149,9 +1121,9 @@ fn empty_outputs_in_tx(#[case] seed: Seed) {
         let mut tf = TestFramework::builder(&mut rng).build();
 
         let first_tx = TransactionBuilder::new()
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(1)),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
         let first_tx_id = first_tx.transaction().get_id();
diff --git a/chainstate/test-suite/src/tests/signature_tests.rs b/chainstate/test-suite/src/tests/signature_tests.rs
index 6e7f74dbd..f36bc3a49 100644
--- a/chainstate/test-suite/src/tests/signature_tests.rs
+++ b/chainstate/test-suite/src/tests/signature_tests.rs
@@ -22,7 +22,7 @@ use common::{
     chain::{
         signature::{inputsig::InputWitness, sighashtype::SigHashType},
         tokens::OutputValue,
-        Destination, OutPointSourceId, OutputPurpose, TxInput, TxOutput,
+        Destination, OutPointSourceId, TxInput, TxOutput,
     },
     primitives::Amount,
 };
@@ -61,9 +61,9 @@ fn signed_tx(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(100)),
-                OutputPurpose::Transfer(Destination::PublicKey(public_key.clone())),
+                Destination::PublicKey(public_key.clone()),
             ))
             .build();
 
@@ -75,9 +75,9 @@ fn signed_tx(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(100)),
-                OutputPurpose::Transfer(Destination::PublicKey(public_key.clone())),
+                Destination::PublicKey(public_key.clone()),
             ))
             .build()
             .transaction()
@@ -147,9 +147,9 @@ fn signed_classical_multisig_tx(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(100)),
-                OutputPurpose::Transfer(destination),
+                destination,
             ))
             .build();
 
@@ -161,9 +161,9 @@ fn signed_classical_multisig_tx(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(100)),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
             .build()
             .transaction()
@@ -252,9 +252,9 @@ fn signed_classical_multisig_tx_missing_sigs(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(100)),
-                OutputPurpose::Transfer(destination),
+                destination,
             ))
             .build();
 
@@ -266,9 +266,9 @@ fn signed_classical_multisig_tx_missing_sigs(#[case] seed: Seed) {
                 ),
                 InputWitness::NoSignature(None),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(100)),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
             .build()
             .transaction()
diff --git a/chainstate/test-suite/src/tests/stake_pool_tests.rs b/chainstate/test-suite/src/tests/stake_pool_tests.rs
index 99c42d857..798e14adf 100644
--- a/chainstate/test-suite/src/tests/stake_pool_tests.rs
+++ b/chainstate/test-suite/src/tests/stake_pool_tests.rs
@@ -48,16 +48,14 @@ fn stake_pool_basic(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                    anyonecanspend_address(),
-                    vrf_pub_key,
-                    destination,
-                    0,
-                    Amount::ZERO,
-                ))),
-            ))
+            .add_output(TxOutput::StakePool(Box::new(StakePoolData::new(
+                Amount::from_atoms(rng.gen_range(100_000..200_000)),
+                anyonecanspend_address(),
+                vrf_pub_key,
+                destination,
+                0,
+                Amount::ZERO,
+            ))))
             .build();
 
         let block = tf.make_block_builder().add_transaction(tx).build();
@@ -87,19 +85,17 @@ fn stake_pool_and_spend_coin_same_tx(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                    anyonecanspend_address(),
-                    vrf_pub_key,
-                    destination,
-                    0,
-                    Amount::ZERO,
-                ))),
-            ))
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::StakePool(Box::new(StakePoolData::new(
+                Amount::from_atoms(rng.gen_range(100_000..200_000)),
+                anyonecanspend_address(),
+                vrf_pub_key,
+                destination,
+                0,
+                Amount::ZERO,
+            ))))
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
 
@@ -130,24 +126,21 @@ fn stake_pool_and_issue_tokens_same_tx(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                    anyonecanspend_address(),
-                    vrf_pub_key,
-                    destination,
-                    0,
-                    Amount::ZERO,
-                ))),
-            ))
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::StakePool(Box::new(StakePoolData::new(
+                Amount::from_atoms(rng.gen_range(100_000..200_000)),
+                anyonecanspend_address(),
+                vrf_pub_key,
+                destination,
+                0,
+                Amount::ZERO,
+            ))))
+            .add_output(TxOutput::Transfer(
                 random_token_issuance(tf.chainstate.get_chain_config().clone(), &mut rng).into(),
-                OutputPurpose::Transfer(anyonecanspend_address()),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(tf.chainstate.get_chain_config().token_min_issuance_fee()),
-                OutputPurpose::Burn,
+                anyonecanspend_address(),
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(
+                tf.chainstate.get_chain_config().token_min_issuance_fee(),
+            )))
             .build();
 
         let block = tf.make_block_builder().add_transaction(tx).build();
@@ -178,18 +171,17 @@ fn stake_pool_and_transfer_tokens_same_tx(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 token_issuance_data.into(),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::Transfer(anyonecanspend_address()),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(tf.chainstate.get_chain_config().token_min_issuance_fee()),
-                OutputPurpose::Burn,
+                anyonecanspend_address(),
             ))
+            .add_output(TxOutput::Burn(OutputValue::Coin(
+                tf.chainstate.get_chain_config().token_min_issuance_fee(),
+            )))
             .build();
         let tx0_id = tx0.transaction().get_id();
         let token_id = common::chain::tokens::token_id(tx0.transaction()).unwrap();
@@ -207,24 +199,22 @@ fn stake_pool_and_transfer_tokens_same_tx(#[case] seed: Seed) {
                 TxInput::new(OutPointSourceId::Transaction(tx0_id), 1),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 TokenData::TokenTransfer(TokenTransfer {
                     token_id,
                     amount: amount_to_issue,
                 })
                 .into(),
-                OutputPurpose::Transfer(anyonecanspend_address()),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100..100_000))),
-                OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                    anyonecanspend_address(),
-                    vrf_pub_key,
-                    destination,
-                    0,
-                    Amount::ZERO,
-                ))),
-            ))
+                anyonecanspend_address(),
+            ))
+            .add_output(TxOutput::StakePool(Box::new(StakePoolData::new(
+                Amount::from_atoms(rng.gen_range(100..100_000)),
+                anyonecanspend_address(),
+                vrf_pub_key,
+                destination,
+                0,
+                Amount::ZERO,
+            ))))
             .build();
 
         let best_block_index = tf
@@ -241,80 +231,6 @@ fn stake_pool_and_transfer_tokens_same_tx(#[case] seed: Seed) {
     });
 }
 
-#[rstest]
-#[trace]
-#[case(Seed::from_entropy())]
-fn stake_pool_with_tokens_as_input_value(#[case] seed: Seed) {
-    utils::concurrency::model(move || {
-        let mut rng = make_seedable_rng(seed);
-        let mut tf = TestFramework::builder(&mut rng).build();
-
-        let token_issuance_data =
-            random_token_issuance(tf.chainstate.get_chain_config().clone(), &mut rng);
-        let amount_to_issue = token_issuance_data.amount_to_issue;
-
-        // create a tx with coins utxo and token issuance utxo
-        let tx0 = TransactionBuilder::new()
-            .add_input(
-                TxInput::new(
-                    OutPointSourceId::BlockReward(tf.genesis().get_id().into()),
-                    0,
-                ),
-                empty_witness(&mut rng),
-            )
-            .add_output(TxOutput::new(
-                token_issuance_data.into(),
-                OutputPurpose::Transfer(anyonecanspend_address()),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::Transfer(anyonecanspend_address()),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(tf.chainstate.get_chain_config().token_min_issuance_fee()),
-                OutputPurpose::Burn,
-            ))
-            .build();
-        let tx0_id = tx0.transaction().get_id();
-        let token_id = common::chain::tokens::token_id(tx0.transaction()).unwrap();
-
-        let destination = new_pub_key_destination(&mut rng);
-        let (_, vrf_pub_key) = VRFPrivateKey::new_from_rng(&mut rng, VRFKeyKind::Schnorrkel);
-
-        // use token input to stake pool with tokens
-        let tx1 = TransactionBuilder::new()
-            .add_input(
-                TxInput::new(OutPointSourceId::Transaction(tx0_id), 0),
-                empty_witness(&mut rng),
-            )
-            .add_output(TxOutput::new(
-                TokenData::TokenTransfer(TokenTransfer {
-                    token_id,
-                    amount: amount_to_issue,
-                })
-                .into(),
-                OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                    anyonecanspend_address(),
-                    vrf_pub_key,
-                    destination,
-                    0,
-                    Amount::ZERO,
-                ))),
-            ))
-            .build();
-        let tx1_id = tx1.transaction().get_id();
-
-        let result = tf.make_block_builder().with_transactions(vec![tx0, tx1]).build_and_process();
-
-        assert_eq!(
-            result.unwrap_err(),
-            ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
-                ConnectTransactionError::TokenOutputInPoSAccountingOperation(tx1_id)
-            ))
-        );
-    });
-}
-
 #[rstest]
 #[trace]
 #[case(Seed::from_entropy())]
@@ -334,26 +250,22 @@ fn stake_pool_twice(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                    anyonecanspend_address(),
-                    vrf_pub_key.clone(),
-                    destination.clone(),
-                    0,
-                    Amount::ZERO,
-                ))),
-            ))
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                    anyonecanspend_address(),
-                    vrf_pub_key,
-                    destination,
-                    0,
-                    Amount::ZERO,
-                ))),
-            ))
+            .add_output(TxOutput::StakePool(Box::new(StakePoolData::new(
+                Amount::from_atoms(rng.gen_range(100_000..200_000)),
+                anyonecanspend_address(),
+                vrf_pub_key.clone(),
+                destination.clone(),
+                0,
+                Amount::ZERO,
+            ))))
+            .add_output(TxOutput::StakePool(Box::new(StakePoolData::new(
+                Amount::from_atoms(rng.gen_range(100_000..200_000)),
+                anyonecanspend_address(),
+                vrf_pub_key,
+                destination,
+                0,
+                Amount::ZERO,
+            ))))
             .build();
 
         let result = tf.make_block_builder().add_transaction(tx).build_and_process();
@@ -396,16 +308,14 @@ fn stake_pool_overspend(#[case] seed: Seed) {
                 TxInput::new(OutPointSourceId::BlockReward(genesis_id), 0),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(genesis_overspend_amount),
-                OutputPurpose::StakePool(Box::new(StakePoolData::new(
-                    anyonecanspend_address(),
-                    vrf_pub_key,
-                    destination,
-                    0,
-                    Amount::ZERO,
-                ))),
-            ))
+            .add_output(TxOutput::StakePool(Box::new(StakePoolData::new(
+                genesis_overspend_amount,
+                anyonecanspend_address(),
+                vrf_pub_key,
+                destination,
+                0,
+                Amount::ZERO,
+            ))))
             .build();
 
         let result = tf.make_block_builder().add_transaction(tx).build_and_process();
@@ -434,6 +344,7 @@ fn spend_stake_pool_in_transaction(#[case] seed: Seed) {
         let destination = new_pub_key_destination(&mut rng);
         let (_, vrf_pub_key) = VRFPrivateKey::new_from_rng(&mut rng, VRFKeyKind::Schnorrkel);
         let stake_pool_data = StakePoolData::new(
+            Amount::from_atoms(rng.gen_range(100_000..200_000)),
             anyonecanspend_address(),
             vrf_pub_key,
             destination,
@@ -449,10 +360,7 @@ fn spend_stake_pool_in_transaction(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::StakePool(Box::new(stake_pool_data)),
-            ))
+            .add_output(TxOutput::StakePool(Box::new(stake_pool_data)))
             .build();
         let tx1_id = tx1.transaction().get_id();
         let pool_id =
@@ -464,9 +372,10 @@ fn spend_stake_pool_in_transaction(#[case] seed: Seed) {
                 TxInput::new(OutPointSourceId::Transaction(tx1_id), 0),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(1..100_000))),
-                OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
+            .add_output(TxOutput::ProduceBlockFromStake(
+                Amount::from_atoms(rng.gen_range(1..100_000)),
+                anyonecanspend_address(),
+                pool_id,
             ))
             .build();
 
@@ -492,6 +401,7 @@ fn transfer_stake_pool_in_transaction(#[case] seed: Seed) {
         let destination = new_pub_key_destination(&mut rng);
         let (_, vrf_pub_key) = VRFPrivateKey::new_from_rng(&mut rng, VRFKeyKind::Schnorrkel);
         let stake_pool_data = StakePoolData::new(
+            Amount::from_atoms(rng.gen_range(100_000..200_000)),
             anyonecanspend_address(),
             vrf_pub_key,
             destination,
@@ -507,10 +417,7 @@ fn transfer_stake_pool_in_transaction(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::StakePool(Box::new(stake_pool_data)),
-            ))
+            .add_output(TxOutput::StakePool(Box::new(stake_pool_data)))
             .build();
         let tx1_id = tx1.transaction().get_id();
         tf.make_block_builder().add_transaction(tx1).build_and_process().unwrap();
@@ -520,9 +427,9 @@ fn transfer_stake_pool_in_transaction(#[case] seed: Seed) {
                 TxInput::new(OutPointSourceId::Transaction(tx1_id), 0),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(rng.gen_range(1..100_000))),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
 
@@ -548,6 +455,7 @@ fn transfer_spend_stake_pool_in_transaction(#[case] seed: Seed) {
         let destination = new_pub_key_destination(&mut rng);
         let (_, vrf_pub_key) = VRFPrivateKey::new_from_rng(&mut rng, VRFKeyKind::Schnorrkel);
         let stake_pool_data = StakePoolData::new(
+            Amount::from_atoms(rng.gen_range(100_000..200_000)),
             anyonecanspend_address(),
             vrf_pub_key,
             destination,
@@ -563,18 +471,16 @@ fn transfer_spend_stake_pool_in_transaction(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::StakePool(Box::new(stake_pool_data)),
-            ))
+            .add_output(TxOutput::StakePool(Box::new(stake_pool_data)))
             .build();
         let pool_id =
             pos_accounting::make_pool_id(tx1.transaction().inputs().get(0).unwrap().outpoint());
         tf.make_block_builder().add_transaction(tx1).build_and_process().unwrap();
 
-        let spend_stake_output = TxOutput::new(
-            OutputValue::Coin(Amount::from_atoms(rng.gen_range(50_000..100_000))),
-            OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
+        let spend_stake_output = TxOutput::ProduceBlockFromStake(
+            Amount::from_atoms(rng.gen_range(50_000..100_000)),
+            anyonecanspend_address(),
+            pool_id,
         );
         let block2_index = tf
             .make_block_builder()
@@ -591,9 +497,9 @@ fn transfer_spend_stake_pool_in_transaction(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(rng.gen_range(1..50_000))),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
         let res = tf.make_block_builder().add_transaction(tx2).build_and_process().unwrap_err();
@@ -625,9 +531,10 @@ fn use_produce_block_output_in_tx(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
-                OutputValue::Coin(Amount::from_atoms(rng.gen_range(100_000..200_000))),
-                OutputPurpose::ProduceBlockFromStake(anyonecanspend_address(), pool_id),
+            .add_output(TxOutput::ProduceBlockFromStake(
+                Amount::from_atoms(rng.gen_range(100_000..200_000)),
+                anyonecanspend_address(),
+                pool_id,
             ))
             .build();
 
diff --git a/chainstate/test-suite/src/tests/tx_verifier_disconnect.rs b/chainstate/test-suite/src/tests/tx_verifier_disconnect.rs
index ed777cb29..1d1bb7c88 100644
--- a/chainstate/test-suite/src/tests/tx_verifier_disconnect.rs
+++ b/chainstate/test-suite/src/tests/tx_verifier_disconnect.rs
@@ -118,9 +118,9 @@ fn connect_disconnect_tx_mempool(#[case] seed: Seed) {
                 ),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(1000)),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
         let tx0_id = tx0.transaction().get_id();
@@ -147,9 +147,9 @@ fn connect_disconnect_tx_mempool(#[case] seed: Seed) {
                 TxInput::new(OutPointSourceId::Transaction(tx0_id), 0),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(100)),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
 
@@ -163,9 +163,9 @@ fn connect_disconnect_tx_mempool(#[case] seed: Seed) {
                 TxInput::new(OutPointSourceId::Transaction(tx1.transaction().get_id()), 0),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(10)),
-                OutputPurpose::Transfer(anyonecanspend_address()),
+                anyonecanspend_address(),
             ))
             .build();
 
diff --git a/chainstate/tx-verifier/src/transaction_verifier/tests/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/tests/mod.rs
index 1b9aee29c..b9d864595 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/tests/mod.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/tests/mod.rs
@@ -19,7 +19,7 @@ mod mock;
 
 use super::*;
 use common::{
-    chain::{Destination, OutPoint, OutputPurpose},
+    chain::{Destination, OutPoint},
     primitives::{amount::UnsignedIntType, H256},
 };
 use crypto::{
@@ -35,9 +35,9 @@ fn create_utxo(rng: &mut (impl Rng + CryptoRng), value: UnsignedIntType) -> (Out
         0,
     );
     let (_, pub_key1) = PrivateKey::new_from_rng(rng, KeyKind::Secp256k1Schnorr);
-    let output1 = TxOutput::new(
+    let output1 = TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(value)),
-        OutputPurpose::Transfer(Destination::PublicKey(pub_key1)),
+        Destination::PublicKey(pub_key1),
     );
     let utxo = Utxo::new_for_blockchain(output1, false, BlockHeight::new(1));
     (outpoint, utxo)
diff --git a/common/src/chain/transaction/signature/tests/mod.rs b/common/src/chain/transaction/signature/tests/mod.rs
index d66328ef4..376f3b7aa 100644
--- a/common/src/chain/transaction/signature/tests/mod.rs
+++ b/common/src/chain/transaction/signature/tests/mod.rs
@@ -29,7 +29,7 @@ use crate::{
         },
         signed_transaction::SignedTransaction,
         tokens::OutputValue,
-        ChainConfig, Destination, OutPointSourceId, OutputPurpose, Transaction, TxInput, TxOutput,
+        ChainConfig, Destination, OutPointSourceId, Transaction, TxInput, TxOutput,
     },
     primitives::{Amount, Id, H256},
 };
@@ -650,9 +650,9 @@ fn check_insert_input(
     let outpoint_source_id =
         OutPointSourceId::Transaction(Id::<Transaction>::new(H256::random_using(rng)));
 
-    let inputs_utxo = TxOutput::new(
+    let inputs_utxo = TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(123)),
-        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+        Destination::AnyoneCanSpend,
     );
     let mut inputs_utxos = inputs_utxos.to_vec();
     inputs_utxos.push(&inputs_utxo);
@@ -705,9 +705,9 @@ fn check_insert_output(
 ) {
     let mut tx_updater = MutableTransaction::from(original_tx);
     let (_, pub_key) = PrivateKey::new_from_rng(rng, KeyKind::Secp256k1Schnorr);
-    tx_updater.outputs.push(TxOutput::new(
+    tx_updater.outputs.push(TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(1234567890)),
-        OutputPurpose::Transfer(Destination::PublicKey(pub_key)),
+        Destination::PublicKey(pub_key),
     ));
     let tx = tx_updater.generate_tx().unwrap();
     let res = verify_signature(chain_config, destination, &tx, inputs_utxos, 0);
@@ -718,6 +718,13 @@ fn check_insert_output(
     }
 }
 
+fn add_value(output_value: OutputValue) -> OutputValue {
+    match output_value {
+        OutputValue::Coin(v) => OutputValue::Coin((v + Amount::from_atoms(100)).unwrap()),
+        OutputValue::Token(v) => OutputValue::Token(v),
+    }
+}
+
 fn check_mutate_output(
     chain_config: &ChainConfig,
     original_tx: &SignedTransaction,
@@ -727,15 +734,14 @@ fn check_mutate_output(
 ) {
     // Should failed due to change in output value
     let mut tx_updater = MutableTransaction::from(original_tx);
-    tx_updater.outputs[0] = TxOutput::new(
-        match tx_updater.outputs[0].value() {
-            OutputValue::Coin(coin) => {
-                OutputValue::Coin((*coin + Amount::from_atoms(100)).unwrap())
-            }
-            OutputValue::Token(asset) => OutputValue::Token(asset.clone()),
-        },
-        tx_updater.outputs[0].purpose().clone(),
-    );
+    tx_updater.outputs[0] = match tx_updater.outputs[0].clone() {
+        TxOutput::Transfer(v, d) => TxOutput::Transfer(add_value(v), d),
+        TxOutput::LockThenTransfer(v, d, l) => TxOutput::LockThenTransfer(add_value(v), d, l),
+        TxOutput::Burn(v) => TxOutput::Burn(add_value(v)),
+        TxOutput::StakePool(_) => unreachable!(), // TODO: come back to this later
+        TxOutput::ProduceBlockFromStake(_, _, _) => unreachable!(), // TODO: come back to this later
+    };
+
     let tx = tx_updater.generate_tx().unwrap();
     let res = verify_signature(chain_config, destination, &tx, inputs_utxos, 0);
     if should_fail {
@@ -776,9 +782,9 @@ fn check_mutate_inputs_utxos(
     outpoint_dest: &Destination,
 ) {
     for input in 0..inputs_utxos.len() {
-        let inputs_utxo = TxOutput::new(
+        let inputs_utxo = TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(123456789012345)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         );
         let mut inputs_utxos = inputs_utxos.to_owned();
         inputs_utxos[input] = &inputs_utxo;
diff --git a/common/src/chain/transaction/signature/tests/sign_and_mutate.rs b/common/src/chain/transaction/signature/tests/sign_and_mutate.rs
index 8f6260818..93924f0e1 100644
--- a/common/src/chain/transaction/signature/tests/sign_and_mutate.rs
+++ b/common/src/chain/transaction/signature/tests/sign_and_mutate.rs
@@ -1080,17 +1080,23 @@ fn add_output(_rng: &mut impl Rng, tx: &SignedTransactionWithUtxo) -> SignedTran
     }
 }
 
+fn add_value(output_value: OutputValue) -> OutputValue {
+    match output_value {
+        OutputValue::Coin(v) => OutputValue::Coin((v + Amount::from_atoms(100)).unwrap()),
+        OutputValue::Token(v) => OutputValue::Token(v),
+    }
+}
+
 fn mutate_output(_rng: &mut impl Rng, tx: &SignedTransactionWithUtxo) -> SignedTransactionWithUtxo {
     let mut updater = MutableTransaction::from(&tx.tx);
-    updater.outputs[0] = TxOutput::new(
-        match updater.outputs[0].value() {
-            OutputValue::Coin(coin) => {
-                OutputValue::Coin((*coin + Amount::from_atoms(100)).unwrap())
-            }
-            OutputValue::Token(asset) => OutputValue::Token(asset.clone()),
-        },
-        updater.outputs[0].purpose().clone(),
-    );
+    // Should fail due to change in output value
+    updater.outputs[0] = match updater.outputs[0].clone() {
+        TxOutput::Transfer(v, d) => TxOutput::Transfer(add_value(v), d),
+        TxOutput::LockThenTransfer(v, d, l) => TxOutput::LockThenTransfer(add_value(v), d, l),
+        TxOutput::Burn(v) => TxOutput::Burn(add_value(v)),
+        TxOutput::StakePool(_) => unreachable!(), // TODO: come back to this later
+        TxOutput::ProduceBlockFromStake(_, _, _) => unreachable!(), // TODO: come back to this later
+    };
     SignedTransactionWithUtxo {
         tx: updater.generate_tx().unwrap(),
         inputs_utxos: tx.inputs_utxos.clone(),
diff --git a/common/src/chain/transaction/signature/tests/utils.rs b/common/src/chain/transaction/signature/tests/utils.rs
index a6d0c77fd..d8857ec9c 100644
--- a/common/src/chain/transaction/signature/tests/utils.rs
+++ b/common/src/chain/transaction/signature/tests/utils.rs
@@ -31,8 +31,7 @@ use crate::{
         },
         signed_transaction::SignedTransaction,
         tokens::OutputValue,
-        ChainConfig, Destination, OutputPurpose, Transaction, TransactionCreationError, TxInput,
-        TxOutput,
+        ChainConfig, Destination, Transaction, TransactionCreationError, TxInput, TxOutput,
     },
     primitives::{amount::UnsignedIntType, Amount, Id, H256},
 };
@@ -44,10 +43,7 @@ pub fn generate_input_utxo(
     let destination = Destination::PublicKey(public_key);
     let output_value =
         crate::chain::tokens::OutputValue::Coin(Amount::from_atoms(rng.next_u64() as u128));
-    let utxo = TxOutput::new(
-        output_value,
-        crate::chain::OutputPurpose::Transfer(destination),
-    );
+    let utxo = TxOutput::Transfer(output_value, destination);
     (utxo, private_key)
 }
 
@@ -111,9 +107,9 @@ pub fn generate_unsigned_tx(
     .collect();
 
     let outputs = std::iter::from_fn(|| {
-        Some(TxOutput::new(
+        Some(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(rng.gen::<UnsignedIntType>())),
-            OutputPurpose::Transfer(destination.clone()),
+            destination.clone(),
         ))
     })
     .take(outputs_count)
diff --git a/common/src/chain/transaction/transaction_index/tests.rs b/common/src/chain/transaction/transaction_index/tests.rs
index 08094d169..33d1bdb33 100644
--- a/common/src/chain/transaction/transaction_index/tests.rs
+++ b/common/src/chain/transaction/transaction_index/tests.rs
@@ -21,7 +21,6 @@ use crate::chain::block::timestamp::BlockTimestamp;
 use crate::chain::signature::inputsig::standard_signature::StandardInputSignature;
 use crate::chain::signed_transaction::SignedTransaction;
 use crate::chain::tokens::OutputValue;
-use crate::chain::OutputPurpose;
 use crate::{
     chain::{
         block::{BlockReward, ConsensusData},
@@ -245,9 +244,9 @@ fn generate_random_invalid_input(rng: &mut impl Rng) -> TxInput {
 
 fn generate_random_invalid_output(rng: &mut (impl Rng + CryptoRng)) -> TxOutput {
     let (_, pub_key) = PrivateKey::new_from_rng(rng, KeyKind::Secp256k1Schnorr);
-    TxOutput::new(
+    TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(rng.next_u64() as u128)),
-        OutputPurpose::Transfer(Destination::PublicKey(pub_key)),
+        Destination::PublicKey(pub_key),
     )
 }
 
diff --git a/common/tests/transaction.rs b/common/tests/transaction.rs
index db65a6b90..c62ab4f34 100644
--- a/common/tests/transaction.rs
+++ b/common/tests/transaction.rs
@@ -25,9 +25,9 @@ fn transaction_id_snapshots() {
     let hash1 = H256([0x51; 32]);
     let hash2 = H256([0x52; 32]);
 
-    let outs0: Vec<TxOutput> = [TxOutput::new(
+    let outs0: Vec<TxOutput> = [TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(25)),
-        OutputPurpose::Transfer(Destination::ScriptHash(Id::new(hash0))),
+        Destination::ScriptHash(Id::new(hash0)),
     )]
     .to_vec();
     let ins0: Vec<TxInput> = [TxInput::new(Id::<Transaction>::new(hash0).into(), 5)].to_vec();
@@ -75,7 +75,7 @@ fn transaction_id_snapshots() {
     let tx = Transaction::new(0x00, ins0, outs0.clone(), 0x123456).unwrap();
     let signed_tx = SignedTransaction::new(tx, vec![InputWitness::NoSignature(None)]).unwrap();
     expect![[r#"
-        0x1205cd5f2da893cc6b48946309c5c5e5f2d97ecc475b47e3f7ffff0db0b126e0
+        0xc702999390064f5bec31b087007abbffc764da468abf6f092897b846df995456
     "#]]
     .assert_debug_eq(&signed_tx.transaction().get_id().get());
 
@@ -89,7 +89,7 @@ fn transaction_id_snapshots() {
     )
     .unwrap();
     expect![[r#"
-        0xed07c7ea4e6e70715dc0a02883966eea60c33d5aa83f50fe5c87bc3b54e4e775
+        0x502421db45587e3d733bdeb495a32540fec6f6d8697e502e69233a2dd211f423
     "#]]
     .assert_debug_eq(&signed_tx.transaction().get_id().get());
 }
diff --git a/mempool/src/pool/tests/expiry.rs b/mempool/src/pool/tests/expiry.rs
index f36018621..12a92387c 100644
--- a/mempool/src/pool/tests/expiry.rs
+++ b/mempool/src/pool/tests/expiry.rs
@@ -37,13 +37,13 @@ async fn descendant_of_expired_entry(#[case] seed: Seed) -> anyhow::Result<()> {
             TxInput::new(OutPointSourceId::BlockReward(genesis.get_id().into()), 0),
             empty_witness(&mut rng),
         )
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(1_000)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(1_000)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
         .build();
 
@@ -101,9 +101,9 @@ async fn only_expired_entries_removed(#[case] seed: Seed) -> anyhow::Result<()>
     );
 
     for _ in 0..num_outputs {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(999_999_999_000)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         ));
     }
     let parent = tx_builder.build();
diff --git a/mempool/src/pool/tests/mod.rs b/mempool/src/pool/tests/mod.rs
index f745851a8..dd8c85620 100644
--- a/mempool/src/pool/tests/mod.rs
+++ b/mempool/src/pool/tests/mod.rs
@@ -31,7 +31,7 @@ use common::{
         signature::inputsig::InputWitness,
         tokens::OutputValue,
         transaction::{Destination, TxInput, TxOutput},
-        OutPointSourceId, OutputPurpose, Transaction,
+        OutPointSourceId, Transaction,
     },
     primitives::{Id, H256},
 };
@@ -75,9 +75,9 @@ fn real_size(#[case] seed: Seed) -> anyhow::Result<()> {
     );
 
     for _ in 0..400 {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(1)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         ));
     }
 
@@ -149,9 +149,9 @@ async fn txs_sorted(#[case] seed: Seed) -> anyhow::Result<()> {
         empty_witness(&mut rng),
     );
     for i in 0..target_txs {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(1000 * (target_txs + 1 - i))),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
     }
     let initial_tx = tx_builder.build();
@@ -163,9 +163,9 @@ async fn txs_sorted(#[case] seed: Seed) -> anyhow::Result<()> {
                 TxInput::new(OutPointSourceId::Transaction(initial_tx_id), i as u32),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(0)),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
             .build();
         mempool.add_transaction(tx.clone()).await?;
@@ -399,9 +399,9 @@ async fn tx_too_big(#[case] seed: Seed) -> anyhow::Result<()> {
     let tf = TestFramework::builder(&mut rng).build();
     let genesis = tf.genesis();
 
-    let single_output_size = TxOutput::new(
+    let single_output_size = TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(100)),
-        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+        Destination::AnyoneCanSpend,
     )
     .encoded_size();
     let too_many_outputs = MAX_BLOCK_SIZE_BYTES / single_output_size;
@@ -410,9 +410,9 @@ async fn tx_too_big(#[case] seed: Seed) -> anyhow::Result<()> {
         empty_witness(&mut rng),
     );
     for _ in 0..too_many_outputs {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(100)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
     }
     let tx = tx_builder.build();
@@ -498,14 +498,8 @@ async fn tx_spend_several_inputs<M: GetMemoryUsage + Send + Sync>(
         flags,
         inputs.clone(),
         vec![
-            TxOutput::new(
-                OutputValue::Coin(spent),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ),
-            TxOutput::new(
-                OutputValue::Coin(change),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
-            ),
+            TxOutput::Transfer(OutputValue::Coin(spent), Destination::AnyoneCanSpend),
+            TxOutput::Transfer(OutputValue::Coin(change), Destination::AnyoneCanSpend),
         ],
         locktime,
     )
@@ -530,9 +524,9 @@ async fn one_ancestor_replaceability_signal_is_enough(#[case] seed: Seed) -> any
     let num_outputs = 2;
 
     for _ in 0..num_outputs {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(2_000)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         ));
     }
     let tx = tx_builder.build();
@@ -580,9 +574,9 @@ async fn one_ancestor_replaceability_signal_is_enough(#[case] seed: Seed) -> any
 
     // TODO compute minimum necessary relay fee instead of just overestimating it
     let original_fee: Fee = Amount::from_atoms(200).into();
-    let dummy_output = TxOutput::new(
+    let dummy_output = TxOutput::Transfer(
         OutputValue::Coin(*original_fee),
-        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+        Destination::AnyoneCanSpend,
     );
     let replaced_tx = tx_spend_several_inputs(
         &mempool,
@@ -751,9 +745,9 @@ async fn test_bip125_max_replacements(
         .with_flags(1);
 
     for _ in 0..(num_potential_replacements - 1) {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(999_999_999_000_000_000)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         ));
     }
 
@@ -846,9 +840,9 @@ async fn spends_new_unconfirmed(#[case] seed: Seed) -> anyhow::Result<()> {
         .with_flags(1);
 
     for _ in 0..2 {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(999_999_999_000)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         ));
     }
 
@@ -931,9 +925,9 @@ async fn rolling_fee(#[case] seed: Seed) -> anyhow::Result<()> {
 
     let num_outputs = 3;
     for _ in 0..num_outputs {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(999_999_999_000)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         ));
     }
     let parent = tx_builder.build();
@@ -1097,9 +1091,9 @@ async fn rolling_fee(#[case] seed: Seed) -> anyhow::Result<()> {
             TxInput::new(OutPointSourceId::Transaction(child_2_high_fee_id), 0),
             InputWitness::NoSignature(Some(DUMMY_WITNESS_MSG.to_vec())),
         )
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(499999999105 - 84)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
         .build();
     log::debug!(
@@ -1154,9 +1148,9 @@ async fn rolling_fee(#[case] seed: Seed) -> anyhow::Result<()> {
             TxInput::new(OutPointSourceId::Transaction(child_1_id), 0),
             InputWitness::NoSignature(Some(DUMMY_WITNESS_MSG.to_vec())),
         )
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(499999999105 - 77)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
         .build();
 
@@ -1186,9 +1180,9 @@ async fn different_size_txs(#[case] seed: Seed) -> anyhow::Result<()> {
         empty_witness(&mut rng),
     );
     for _ in 0..10_000 {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(1_000)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
     }
     let initial_tx = tx_builder.build();
@@ -1220,9 +1214,9 @@ async fn different_size_txs(#[case] seed: Seed) -> anyhow::Result<()> {
 
         let before_outputs = Instant::now();
         for _ in 0..num_outputs {
-            tx_builder = tx_builder.add_output(TxOutput::new(
+            tx_builder = tx_builder.add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(100)),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
         }
         log::debug!(
@@ -1259,13 +1253,13 @@ async fn ancestor_score(#[case] seed: Seed) -> anyhow::Result<()> {
             TxInput::new(OutPointSourceId::BlockReward(genesis.get_id().into()), 0),
             empty_witness(&mut rng),
         )
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(10_000)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(10_000)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
         .build();
     let tx_id = tx.transaction().get_id();
@@ -1414,13 +1408,13 @@ async fn descendant_score(#[case] seed: Seed) -> anyhow::Result<()> {
             TxInput::new(OutPointSourceId::BlockReward(genesis.get_id().into()), 0),
             empty_witness(&mut rng),
         )
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(10_000)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(10_000)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
         .build();
     let tx_id = tx.transaction().get_id();
@@ -1551,9 +1545,9 @@ async fn mempool_full(#[case] seed: Seed) -> anyhow::Result<()> {
             TxInput::new(OutPointSourceId::BlockReward(genesis.get_id().into()), 0),
             empty_witness(&mut rng),
         )
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(100)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
         .build();
     log::debug!(
@@ -1583,9 +1577,9 @@ async fn no_empty_bags_in_indices(#[case] seed: Seed) -> anyhow::Result<()> {
 
     let num_outputs = 100;
     for _ in 0..num_outputs {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(2_000)),
-            OutputPurpose::Transfer(anyonecanspend_address()),
+            anyonecanspend_address(),
         ));
     }
     let parent = tx_builder.build();
@@ -1653,9 +1647,9 @@ async fn collect_transactions(#[case] seed: Seed) -> anyhow::Result<()> {
         empty_witness(&mut rng),
     );
     for i in 0..target_txs {
-        tx_builder = tx_builder.add_output(TxOutput::new(
+        tx_builder = tx_builder.add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(1000 * (target_txs + 1 - i))),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
     }
     let initial_tx = tx_builder.build();
@@ -1667,9 +1661,9 @@ async fn collect_transactions(#[case] seed: Seed) -> anyhow::Result<()> {
                 TxInput::new(OutPointSourceId::Transaction(initial_tx_id), i as u32),
                 empty_witness(&mut rng),
             )
-            .add_output(TxOutput::new(
+            .add_output(TxOutput::Transfer(
                 OutputValue::Coin(Amount::from_atoms(0)),
-                OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+                Destination::AnyoneCanSpend,
             ))
             .build();
         mempool.add_transaction(tx.clone()).await?;
diff --git a/mempool/src/pool/tests/replacement.rs b/mempool/src/pool/tests/replacement.rs
index 911d168f5..10c435136 100644
--- a/mempool/src/pool/tests/replacement.rs
+++ b/mempool/src/pool/tests/replacement.rs
@@ -188,9 +188,9 @@ async fn tx_replace_child(#[case] seed: Seed) -> anyhow::Result<()> {
             TxInput::new(OutPointSourceId::BlockReward(genesis.get_id().into()), 0),
             empty_witness(&mut rng),
         )
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(2_000)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
         .with_flags(1)
         .build();
@@ -243,9 +243,9 @@ async fn pays_more_than_conflicts_with_descendants(#[case] seed: Seed) -> anyhow
             TxInput::new(OutPointSourceId::BlockReward(genesis.get_id().into()), 0),
             empty_witness(&mut rng),
         )
-        .add_output(TxOutput::new(
+        .add_output(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(1_000)),
-            OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+            Destination::AnyoneCanSpend,
         ))
         .with_flags(1)
         .build();
diff --git a/mempool/src/pool/tests/utils.rs b/mempool/src/pool/tests/utils.rs
index df8eb4f39..2057a9464 100644
--- a/mempool/src/pool/tests/utils.rs
+++ b/mempool/src/pool/tests/utils.rs
@@ -50,8 +50,7 @@ fn dummy_input() -> TxInput {
 
 fn dummy_output() -> TxOutput {
     let value = Amount::from_atoms(0);
-    let purpose = OutputPurpose::Transfer(Destination::AnyoneCanSpend);
-    TxOutput::new(OutputValue::Coin(value), purpose)
+    TxOutput::Transfer(OutputValue::Coin(value), Destination::AnyoneCanSpend)
 }
 
 pub fn estimate_tx_size(num_inputs: usize, num_outputs: usize) -> usize {
diff --git a/p2p/src/sync/tests/tx_announcement.rs b/p2p/src/sync/tests/tx_announcement.rs
index 6b0b89abb..a33f94f8e 100644
--- a/p2p/src/sync/tests/tx_announcement.rs
+++ b/p2p/src/sync/tests/tx_announcement.rs
@@ -20,8 +20,7 @@ use chainstate_test_framework::TestFramework;
 use common::{
     chain::{
         config::create_unit_test_config, signature::inputsig::InputWitness, tokens::OutputValue,
-        GenBlock, OutPointSourceId, OutputPurpose, SignedTransaction, Transaction, TxInput,
-        TxOutput,
+        GenBlock, OutPointSourceId, SignedTransaction, Transaction, TxInput, TxOutput,
     },
     primitives::{Amount, Id},
 };
@@ -343,10 +342,7 @@ fn transaction(out_point: Id<GenBlock>) -> SignedTransaction {
     let tx = Transaction::new(
         0x00,
         vec![TxInput::new(OutPointSourceId::from(out_point), 0)],
-        vec![TxOutput::new(
-            OutputValue::Coin(Amount::from_atoms(1)),
-            OutputPurpose::Burn,
-        )],
+        vec![TxOutput::Burn(OutputValue::Coin(Amount::from_atoms(1)))],
         0x01,
     )
     .unwrap();
diff --git a/test/functional/feature_lmdb_backend_test.py b/test/functional/feature_lmdb_backend_test.py
index 289d64223..1eaa3961b 100755
--- a/test/functional/feature_lmdb_backend_test.py
+++ b/test/functional/feature_lmdb_backend_test.py
@@ -33,11 +33,11 @@ class ExampleTest(BitcoinTestFramework):
 
     def run_test(self):
         blocks = [
-            "01fe760cab5844216e725dd27b8bcaa79d8a3c8dfde851dbf5ae50790fc035a7dfa29ebec302d2ba3238d291955815542eeac8716d02f9045b8e933206da5c46edfea4f6c903c99ea8d42aef3485905f82d1075d969f69d805c86107f64e15c64303240ac06101ffff7f2000000000000000000000000000000000000401000000000401fe760cab5844216e725dd27b8bcaa79d8a3c8dfde851dbf5ae50790fc035a7df000000000400176eed560ffb1dc02d010000000000000400018d013c05300d431215134f2055472e2201452c3721414a071e4b173d2a111b3a03543e5a49531048560b2b42354c0c461c09191d25340a0e2940161a59513f393318585d3b085c04385f632f5b60060223274424361f260f614e572d5e504d283114523262",
-            "0172109d501d682678c14e255020e9a15b84f9a30707d57347915d5d88d5e8e12b4350e4dbaab8f89b61271c8c8f43f8d3a525f2b8a797f83d39a3f99d2d4e4b796122fc79ee843c065ab3634b51e7f00d3f52cc1be680a9ebd3df0a99dd7344e803240ac06101ffff7f20000000000000000000000000000000000004010000000004002e410340bb8aa842b774d52a7edcb97c4f6f94afd37e6a16d2e8a63161e20598000000000400171bdcf10def069320010000000000000400018d013a2722524e3641140d593518345c4b4820232a0c5419622b3b6116440a06261e1260333e4c531c09514358152c5539102108131f3f013056472d175a0245034d11505e242f1a313742040b4946073c1b2e28055b5f6338324f294a400f0e1d5d573d25",
-            "01a930067c8594b6900f98b31eec106237156d4b772224a9c49e7f94fdc2dd2e09da6a6f3900b1a895537ab29ff96454f49f73b4a925e2fad3dcd5fb456929795bff7af37b19956d218d9c3b9f6ad99ccdcc297fb9ea159aea03cfbecda77cb11303240ac06101ffff7f2001000000000000000000000000000000000401000000000400ab31ffbde23687d6b2e0178a6bd02d83059ef91dd4416c3095bf5ac4a0caebdb0000000004001316a4c2d5376134e60000000000000400018d011e3a371b02440449474b634a5a352c58190103542e3036260b05063109402d4d3316430a3c500f5e55481d080e573d4f132b18460c20126260294c5211100724613851532542273e3b392a3414231f5d22210d414e3245175c15593f5b5f56281a1c2f",
-            "01f723d973e9a7481d6329b617c09b4f203fe6f771dc2f13fbc9904df6cf87846f21924f3a285a2c7837bdcdaa32c2278c9c97b33b704cf4ee365b6131afbd219e9771e5c55f07ee27a870e5e8fbd44a492e4fa0ae15e13c2732f2e138f0c5859f03240ac06101ffff7f2001000000000000000000000000000000000401000000000400ecac06f74a0c2d2442e9780fe502d7ce206fd5c74dcc1c5885510bdf5c57056e0000000004001340a38ca09a55e6960000000000000400018d01255f3a3509062703172e4c60365b593b3346501332311663434a0b4e026151013c2d1a5805222b0e30543f0c2347295a483e2f1519412a621c28444b20420f5e0a183d554d102456522c3908492114121f3840575d4511075c341e37040d531b4f261d",
-            "0152253a98036ce633b6b3b07d48799e3e13664ce76cc32ae7442e3b6ddfa19913c266b9ddd44f774ff80d745cb0cf8b8b4381e7a75358510032e4efe7df62458d4a9975ef5e1b161781cd5471e0ab57e647439bcb7f2cffdcea390f3fdcb506c403240ac06101ffff7f20030000000000000000000000000000000004010000000004006d5d825a4c7590e11891e3f364ae8bc2e27b4dae6547d5b74ee9ecad4b26e60b000000000400138f6497535df18d430000000000000400018d01104f504b2e353a2345184720195c2c3339264c421a572f0f1f49341c054d285d12025f4a325e084e09373b29545b36383f581d2b3d1652433e150e220c1e512403530b3062553c2740256104172a56591b112106482d440a4660416301135a0d311407"
+            "01916b5099a91acadd46431d4eab261579f00b02b2ad79b69cedbd60163370b15a59029712042385c7294190649ea1e0e72bb3bce3bc7544abd891fd47c95147f3ce297058329579611c0d654705f320efa149cf62937bb47dce6c9abbfba1f20003240ac06101ffff7f2003000000000000000000000000000000000401000000000401916b5099a91acadd46431d4eab261579f00b02b2ad79b69cedbd60163370b15a0000000004000017d6acd5778fa0a7130300000000000400018d010a123029134f482d06190b045c432c3550620f5e02254d4e5d600c231f1c3d49321b474459285841013442461e08105224055107560e553b4b38111a61361d394a2b152017265314401803452254315a37093c3e63162a2e0d3a335f2f5b2721573f4c",
+            "01b2a927982524f8e846a711c018f56f9b5a86e109a512bb6cb054bb1dc9ee26043bd673e4b5cc5b4291e17c4323bc32d3794b08a6710abfd2fc282a4c8fd4713da437ddd9d6028a05c5df87037d5ffa00b0c3a739619495c4213f50c407524f0e03240ac06101ffff7f20000000000000000000000000000000000004010000000004004b67c508383e511bd86b7b69acffd108ce0427b91b1893bd16da5fde260ab711000000000400001394dfde730d95905600000000000400018d0104100859335e3f3227024e0126481e13623a35114d214c1b513b50560d2f0e36243c3d072e5c0a0b252d19473816423009490f465a3141394f28341c63372c3e402a53231757145d1f054303201d4b29545861550c451812155f2206602b441a4a525b",
+            "01d62b11e192f398f86f2cda6d9155faa8dda8b1eb6268e96134158e9f9b727437fbc6239997dd0bc6e8cc3dcb876d8682b7543efe5aeac6b38ddc5cb9779d45ced4800f1a6b5af4f916b6b780496e5a206349bf7cf64e3788d895ea9e35030b0b03240ac06101ffff7f2006000000000000000000000000000000000401000000000400a7fa2b0cf7b03fb29e86332c91766f2abcffc7bc53da10a8139f442da7dec06f00000000040000137240b98f7204ab3900000000000400018d010562443d38371a0f164e46135a044b0a0d59073224575b10502c255f631c603b4f2911153512185e39302f5d2709171e312126482a34034320421b4d06220c2d1f2852010e024561333f362b145551531d2e490b4756544c3a3e41584a5c081923403c",
+            "01da6d966628bb6a06f19a5a670e3c0c0375dcc9bc8dbf1a8ad967f42c28456b5b5335e506f97bd2dbe3ef75ca1432382ee3b1e6d966799a0212ee50bbdc008e9d08f8f0500dd24c14da9bf3db3596e5ba427836f87a19dd4b387a2070ca5d6c3103240ac06101ffff7f2000000000000000000000000000000000000401000000000400d2bbeb01c8c7551fd49f9f5963c36c745ebaaadf3f78c9d33eeb3398a5b36b53000000000400000f7cda6416900b6e00000000000400018d010b544b15555726513143204a342342185e0358563c121f47353b10130f28504f1a39633e2e1d0a1b3853223f2a020d5c455f5d04363a090e49333d1c6244272c524861304c5b320c252124196059172d291406115a404e2f46072b08014d41051e1637",
+            "01eb3f5f1ff6ec671a473bdf081b57ca6c07daac42200c5543a76c331b9b11de1fd07f5abee88300827abc8f0b9390410c15bcec7029be776b7c7f181c162e56c4793557c1baae51df6d325e6348e933598d98f7fcb16fd13374958792812e21e003240ac06101ffff7f200000000000000000000000000000000000040100000000040009acea7cbacd37171a7dd95637cf58e331ab5037868348a743a4bd55573db742000000000400000fe73deab81a9b1f00000000000400018d011e455c072c604c281c3a54203055112227170832445b3803425e4a2d401b2e1418622931492f0904263702410b505a342124585156520d535d190e3e23133906460533434b2b121f3d2a4e165f4f014d59256110470a3b150f3c1d571a0c3f35366348",
         ]
 
         node = self.nodes[0]
diff --git a/utxo/src/tests/mod.rs b/utxo/src/tests/mod.rs
index 5ee2d850e..978d1b0e4 100644
--- a/utxo/src/tests/mod.rs
+++ b/utxo/src/tests/mod.rs
@@ -37,8 +37,7 @@ use common::{
         },
         signature::inputsig::InputWitness,
         tokens::OutputValue,
-        Destination, OutPoint, OutPointSourceId, OutputPurpose, PoolId, Transaction, TxInput,
-        TxOutput,
+        Destination, OutPoint, OutPointSourceId, PoolId, Transaction, TxInput, TxOutput,
     },
     primitives::{Amount, BlockHeight, Compact, Id, Idable, H256},
 };
@@ -615,10 +614,7 @@ fn check_burn_spend_undo_spend(#[case] seed: Seed) {
     cache.add_utxo(&outpoint, u, false).unwrap();
 
     // burn output in a tx
-    let output = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(10)),
-        OutputPurpose::Burn,
-    );
+    let output = TxOutput::Burn(OutputValue::Coin(Amount::from_atoms(10)));
     let input = TxInput::new(outpoint.tx_id(), outpoint.output_index());
     let tx = Transaction::new(0x00, vec![input], vec![output], 0x01).unwrap();
     let undo1 = cache.connect_transaction(&tx, BlockHeight::new(1)).unwrap();
@@ -799,10 +795,7 @@ fn check_burn_output_in_block_reward(#[case] seed: Seed) {
     cache.add_utxo(&outpoint, utxo, false).unwrap();
 
     let inputs = vec![TxInput::new(outpoint.tx_id(), outpoint.output_index())];
-    let outputs = vec![TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(10)),
-        OutputPurpose::Burn,
-    )];
+    let outputs = vec![TxOutput::Burn(OutputValue::Coin(Amount::from_atoms(10)))];
 
     let (sk, _pk) = crypto::vrf::VRFPrivateKey::new_from_rng(&mut rng, VRFKeyKind::Schnorrkel);
     let vrf_data = sk.produce_vrf_data(TranscriptAssembler::new(b"abc").finalize().into());
@@ -849,14 +842,11 @@ fn check_burn_output_indexing(#[case] seed: Seed) {
     cache.add_utxo(&outpoint, u, false).unwrap();
 
     // create burn output at index 0
-    let output1 = TxOutput::new(
-        OutputValue::Coin(Amount::from_atoms(10)),
-        OutputPurpose::Burn,
-    );
+    let output1 = TxOutput::Burn(OutputValue::Coin(Amount::from_atoms(10)));
     // create transfer output at index 1
-    let output2 = TxOutput::new(
+    let output2 = TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(10)),
-        OutputPurpose::Transfer(Destination::AnyoneCanSpend),
+        Destination::AnyoneCanSpend,
     );
     let input = TxInput::new(outpoint.tx_id(), outpoint.output_index());
     let tx = Transaction::new(0x00, vec![input], vec![output1, output2], 0x01).unwrap();
diff --git a/utxo/src/tests/test_helper.rs b/utxo/src/tests/test_helper.rs
index c6ccc6385..ba437763c 100644
--- a/utxo/src/tests/test_helper.rs
+++ b/utxo/src/tests/test_helper.rs
@@ -19,8 +19,8 @@ use crate::{
 };
 use common::{
     chain::{
-        tokens::OutputValue, Destination, GenBlock, OutPoint, OutPointSourceId, OutputPurpose,
-        Transaction, TxInput, TxOutput,
+        tokens::OutputValue, Destination, GenBlock, OutPoint, OutPointSourceId, Transaction,
+        TxInput, TxOutput,
     },
     primitives::{Amount, BlockHeight, Id, H256},
 };
@@ -68,9 +68,9 @@ pub fn create_tx_outputs(rng: &mut (impl Rng + CryptoRng), size: u32) -> Vec<TxO
     for _ in 0..size {
         let random_amt = rng.gen_range(1..u128::MAX);
         let (_, pub_key) = PrivateKey::new_from_rng(rng, KeyKind::Secp256k1Schnorr);
-        tx_outputs.push(TxOutput::new(
+        tx_outputs.push(TxOutput::Transfer(
             OutputValue::Coin(Amount::from_atoms(random_amt)),
-            OutputPurpose::Transfer(Destination::PublicKey(pub_key)),
+            Destination::PublicKey(pub_key),
         ));
     }
 
@@ -132,9 +132,9 @@ fn inner_create_utxo(
     // just a random value generated, and also a random `is_block_reward` value.
     let output_value = rng.gen_range(0..u128::MAX);
     let (_, pub_key) = PrivateKey::new_from_rng(rng, KeyKind::Secp256k1Schnorr);
-    let output = TxOutput::new(
+    let output = TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(output_value)),
-        OutputPurpose::Transfer(Destination::PublicKey(pub_key)),
+        Destination::PublicKey(pub_key),
     );
 
     // generate utxo
diff --git a/wallet/storage/src/internal/test.rs b/wallet/storage/src/internal/test.rs
index 770cf7903..5dd43602f 100644
--- a/wallet/storage/src/internal/test.rs
+++ b/wallet/storage/src/internal/test.rs
@@ -16,7 +16,7 @@
 use super::*;
 use crate::DefaultBackend;
 use common::chain::tokens::OutputValue;
-use common::chain::{Destination, OutPointSourceId, OutputPurpose, TxOutput};
+use common::chain::{Destination, OutPointSourceId, TxOutput};
 use common::primitives::{Amount, Id, H256};
 use crypto::key::{KeyKind, PrivateKey};
 use crypto::random::Rng;
@@ -44,9 +44,9 @@ fn read_write_utxo_in_db_transaction(#[case] seed: Seed) {
 
     // generate a utxo and outpoint
     let (_, pub_key) = PrivateKey::new_from_rng(&mut rng, KeyKind::Secp256k1Schnorr);
-    let output = TxOutput::new(
+    let output = TxOutput::Transfer(
         OutputValue::Coin(Amount::from_atoms(rng.gen_range(0..(u128::MAX - 1)))),
-        OutputPurpose::Transfer(Destination::PublicKey(pub_key)),
+        Destination::PublicKey(pub_key),
     );
     let utxo = Utxo::new_for_mempool(output, false);
     let outpoint = OutPoint::new(