From 490f10e3e0a55f1e7d8f503e681d052c525545b7 Mon Sep 17 00:00:00 2001
From: Hans Moog <hm@mkjc.net>
Date: Mon, 23 Mar 2020 03:34:09 +0100
Subject: [PATCH] Feat: Refactor and cleanup some code (#301)

* Feat: refactored the address

* Feat: started adding solidification of transferOutputs

* Feat: cleaned up checker code

* Feat: going to bed - good night world

* Feat: refactored transferoutput package

* Fix: fixed a bug from refactoring

* Refactor: removed old transferoutputmetadata package

* Refactor: started refactoring transfer package

* Refactor: refactored transfer/id package

* Refactor: moved transfer struct to new package

* Refactor: fixed issues after refactor

* Refactor: fixed sth

* Refactor: continued to move files

* Refactor: commit before migration of last refactored files

* Refactor: another refactor before move

* Refactor: refactor test

* Fix: fixed some bugs connected to refactor

* Refactor: continued refactor

* Refactor: fixed some bugs

* Refactor: does it work now?

* Feat: added a method to generate a random transferid

* Feat: rename transfer to transaction

* Refactor: refactor according to new names

* Refactor: continued to refactor transaction package

* Refactor: moved payload id to payload package

* Refactor: moved signatures to transaction package

* Refactor: moved signature to transaction

* Fix: fixed bug due to refactor

* Fix: fixed bugs due to refactor

* Refactor: fixed some bugs after refactor

* Fix: fixed additional bugs

* Fix: bug fix

* Refactor: moved signature to signaturescheme package

* Fix: fixed signatures test

* Fix: fixed bug in tangle

* Fix: fixed payloadmetadata test

* Fix: fixed payload test

* Refactor: moved payloadmetadata to payload package

* Fix: fixed some refactor bugs

* Fix: fixed a bug due to refactor

* Fix: fixed broken test

* Refactor: moved approver to payload package

* Refactor: moved missingpayload to payload package

* Refactor: refactored coloredbalance package

* Fix: fixed bug due to refactor

* Refactor: moved address signaturescheme to address package

* Fix: fixed refactor bug

* Feat: added missing outputs to tangle

* Fix: fixed issues due to refactor

* Refactor: started moving tangle classes to tangle package

* Refactor: moved payloadmetadata to tangle

* Fix: fixed bugs due to refactor

* Refactor: moved TransactionMetadata to tangle package

* Refactor: moved some files to tangle

* Fix: fixed bug due to refactor

* Feat: added TransactionMetadata storage

* Fix: fixed some issues

* Fix: fixed some issues

* Fix: fixed missing release
---
 .../datastructure/orderedmap/orderedmap.go    |   6 +-
 packages/binary/spammer/spammer.go            |  10 +-
 packages/binary/tangle/events.go              |   8 +-
 .../binary/tangle/model/approver/approver.go  |  22 +-
 .../cached_transaction.go                     |   2 +-
 .../model/{transaction => message}/id.go      |   2 +-
 packages/binary/tangle/model/message/init.go  |  10 +
 .../payload/data/data.go                      |   2 +-
 .../payload/data/init.go                      |   2 +-
 .../{transaction => message}/payload/id.go    |   0
 .../payload/payload.go                        |   0
 .../{transaction => message}/payload/type.go  |   0
 .../payload/type_register.go                  |   0
 .../test/transaction_test.go                  |  12 +-
 .../{transaction => message}/transaction.go   |   4 +-
 .../missingtransaction/missingtransaction.go  |   8 +-
 .../binary/tangle/model/transaction/init.go   |  10 -
 .../transactionmetadata.go                    |   6 +-
 packages/binary/tangle/tangle.go              |  58 +--
 packages/binary/tangle/tangle_test.go         |  26 +-
 packages/binary/tangle/tipselector/events.go  |   4 +-
 .../binary/tangle/tipselector/tipselector.go  |  16 +-
 .../tangle/tipselector/tipselector_test.go    |  14 +-
 .../transaction_signature_filter.go           |  16 +-
 .../transactionparser/transaction_filter.go   |   8 +-
 .../transactionparser/transactionparser.go    |  12 +-
 .../transactionparser_test.go                 |  12 +-
 .../transactionrequester.go                   |  12 +-
 .../binary/valuetransfer/address/address.go   |  18 +-
 .../signaturescheme}/ed25519.go               |   7 +-
 .../address/signaturescheme/signature.go      |  15 +
 .../signaturescheme/signaturescheme.go        |  17 +
 .../binary/valuetransfer/balance/balance.go   |  79 ++++
 .../valuetransfer/balance/balance_test.go     |  24 ++
 .../color => balance}/color.go                |  16 +-
 .../coloredbalance/coloredbalance.go          |  70 ----
 .../valuetransfer/payload/cached_object.go    |  39 --
 .../valuetransfer/payload/{id => }/id.go      |  30 +-
 .../valuetransfer/payload/{id => }/id_test.go |   6 +-
 .../binary/valuetransfer/payload/payload.go   |  96 +++--
 .../valuetransfer/payload/payload_test.go     | 120 +++++++
 .../valuetransfer/payload/transfer/id/id.go   |  48 ---
 .../payload/transfer/signatures/interfaces.go |  27 --
 .../payload/transfer/transfer.go              | 296 ---------------
 .../binary/valuetransfer/tangle/attachment.go |  23 ++
 .../binary/valuetransfer/tangle/events.go     |  17 +-
 .../valuetransfer/tangle/missingoutput.go     | 109 ++++++
 .../{missingpayload => }/missingpayload.go    |  26 +-
 .../valuetransfer/tangle/payloadapprover.go   | 129 +++++++
 .../tangle/payloadapprover/cached_object.go   |  49 ---
 .../tangle/payloadapprover/payloadapprover.go |  85 -----
 .../{payloadmetadata => }/payloadmetadata.go  |  70 +++-
 .../tangle/payloadmetadata/cached_object.go   |  39 --
 .../payloadmetadata_test.go                   |  12 +-
 .../binary/valuetransfer/tangle/tangle.go     | 153 +++++---
 .../valuetransfer/tangle/tangle_test.go       |  28 +-
 .../tangle/transactionmetadata.go             | 213 +++++++++++
 .../tangle/transactionoutputmetadata.go       | 213 +++++++++++
 .../binary/valuetransfer/test/payload_test.go | 124 -------
 .../binary/valuetransfer/transaction/id.go    |  86 +++++
 .../transfer/inputs => transaction}/inputs.go |  76 ++--
 .../valuetransfer/transaction/output.go       | 126 +++++++
 .../output_test.go}                           |   2 +-
 .../valuetransfer/transaction/outputid.go     |  71 ++++
 .../outputs => transaction}/outputs.go        |  24 +-
 .../signatures => transaction}/signatures.go  |  62 +++-
 .../signatures_test.go                        |  15 +-
 .../valuetransfer/transaction/transaction.go  | 339 ++++++++++++++++++
 .../valuetransfer/transferoutput/id/id.go     |  43 ---
 .../transferoutput/transferoutput.go          | 127 -------
 packages/gossip/manager.go                    |   6 +-
 packages/gossip/manager_test.go               |   4 +-
 plugins/gossip/gossip.go                      |   6 +-
 plugins/gossip/plugin.go                      |   8 +-
 plugins/metrics/plugin.go                     |   4 +-
 plugins/spa/explorer_routes.go                |  12 +-
 plugins/spa/livefeed.go                       |   6 +-
 plugins/tangle/plugin.go                      |  10 +-
 plugins/webapi/gtta/plugin.go                 |   6 +-
 79 files changed, 2159 insertions(+), 1354 deletions(-)
 rename packages/binary/tangle/model/{transaction => message}/cached_transaction.go (97%)
 rename packages/binary/tangle/model/{transaction => message}/id.go (98%)
 create mode 100644 packages/binary/tangle/model/message/init.go
 rename packages/binary/tangle/model/{transaction => message}/payload/data/data.go (96%)
 rename packages/binary/tangle/model/{transaction => message}/payload/data/init.go (56%)
 rename packages/binary/tangle/model/{transaction => message}/payload/id.go (100%)
 rename packages/binary/tangle/model/{transaction => message}/payload/payload.go (100%)
 rename packages/binary/tangle/model/{transaction => message}/payload/type.go (100%)
 rename packages/binary/tangle/model/{transaction => message}/payload/type_register.go (100%)
 rename packages/binary/tangle/model/{transaction => message}/test/transaction_test.go (65%)
 rename packages/binary/tangle/model/{transaction => message}/transaction.go (98%)
 delete mode 100644 packages/binary/tangle/model/transaction/init.go
 rename packages/binary/valuetransfer/{payload/transfer/signatures => address/signaturescheme}/ed25519.go (95%)
 create mode 100644 packages/binary/valuetransfer/address/signaturescheme/signature.go
 create mode 100644 packages/binary/valuetransfer/address/signaturescheme/signaturescheme.go
 create mode 100644 packages/binary/valuetransfer/balance/balance.go
 create mode 100644 packages/binary/valuetransfer/balance/balance_test.go
 rename packages/binary/valuetransfer/{coloredbalance/color => balance}/color.go (51%)
 delete mode 100644 packages/binary/valuetransfer/coloredbalance/coloredbalance.go
 delete mode 100644 packages/binary/valuetransfer/payload/cached_object.go
 rename packages/binary/valuetransfer/payload/{id => }/id.go (63%)
 rename packages/binary/valuetransfer/payload/{id => }/id_test.go (70%)
 create mode 100644 packages/binary/valuetransfer/payload/payload_test.go
 delete mode 100644 packages/binary/valuetransfer/payload/transfer/id/id.go
 delete mode 100644 packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
 delete mode 100644 packages/binary/valuetransfer/payload/transfer/transfer.go
 create mode 100644 packages/binary/valuetransfer/tangle/attachment.go
 create mode 100644 packages/binary/valuetransfer/tangle/missingoutput.go
 rename packages/binary/valuetransfer/tangle/{missingpayload => }/missingpayload.go (72%)
 create mode 100644 packages/binary/valuetransfer/tangle/payloadapprover.go
 delete mode 100644 packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go
 delete mode 100644 packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go
 rename packages/binary/valuetransfer/tangle/{payloadmetadata => }/payloadmetadata.go (59%)
 delete mode 100644 packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go
 rename packages/binary/valuetransfer/tangle/{payloadmetadata => }/payloadmetadata_test.go (80%)
 create mode 100644 packages/binary/valuetransfer/tangle/transactionmetadata.go
 create mode 100644 packages/binary/valuetransfer/tangle/transactionoutputmetadata.go
 delete mode 100644 packages/binary/valuetransfer/test/payload_test.go
 create mode 100644 packages/binary/valuetransfer/transaction/id.go
 rename packages/binary/valuetransfer/{payload/transfer/inputs => transaction}/inputs.go (50%)
 create mode 100644 packages/binary/valuetransfer/transaction/output.go
 rename packages/binary/valuetransfer/{transferoutput/transferoutput_test.go => transaction/output_test.go} (70%)
 create mode 100644 packages/binary/valuetransfer/transaction/outputid.go
 rename packages/binary/valuetransfer/{payload/transfer/outputs => transaction}/outputs.go (79%)
 rename packages/binary/valuetransfer/{payload/transfer/signatures => transaction}/signatures.go (66%)
 rename packages/binary/valuetransfer/{payload/transfer/signatures => transaction}/signatures_test.go (65%)
 create mode 100644 packages/binary/valuetransfer/transaction/transaction.go
 delete mode 100644 packages/binary/valuetransfer/transferoutput/id/id.go
 delete mode 100644 packages/binary/valuetransfer/transferoutput/transferoutput.go

diff --git a/packages/binary/datastructure/orderedmap/orderedmap.go b/packages/binary/datastructure/orderedmap/orderedmap.go
index fe055542..8bc97443 100644
--- a/packages/binary/datastructure/orderedmap/orderedmap.go
+++ b/packages/binary/datastructure/orderedmap/orderedmap.go
@@ -66,20 +66,22 @@ func (orderedMap *OrderedMap) Set(key interface{}, newValue interface{}) bool {
 	return true
 }
 
-func (orderedMap *OrderedMap) ForEach(consumer func(key, value interface{}) bool) {
+func (orderedMap *OrderedMap) ForEach(consumer func(key, value interface{}) bool) bool {
 	orderedMap.mutex.RLock()
 	currentEntry := orderedMap.head
 	orderedMap.mutex.RUnlock()
 
 	for currentEntry != nil {
 		if !consumer(currentEntry.key, currentEntry.value) {
-			return
+			return false
 		}
 
 		orderedMap.mutex.RLock()
 		currentEntry = currentEntry.next
 		orderedMap.mutex.RUnlock()
 	}
+
+	return true
 }
 
 func (orderedMap *OrderedMap) Delete(key interface{}) bool {
diff --git a/packages/binary/spammer/spammer.go b/packages/binary/spammer/spammer.go
index e59cf9df..ea0efc5a 100644
--- a/packages/binary/spammer/spammer.go
+++ b/packages/binary/spammer/spammer.go
@@ -7,8 +7,8 @@ import (
 	"github.com/iotaledger/hive.go/types"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/tipselector"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser"
 )
@@ -53,7 +53,7 @@ func (spammer *Spammer) run(tps int, processId int64) {
 
 		trunkTransactionId, branchTransactionId := spammer.tipSelector.GetTips()
 		spammer.transactionParser.Parse(
-			transaction.New(trunkTransactionId, branchTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM"))).Bytes(),
+			message.New(trunkTransactionId, branchTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM"))).Bytes(),
 			nil,
 		)
 
@@ -75,7 +75,7 @@ func (spammer *Spammer) run(tps int, processId int64) {
 func (spammer *Spammer) sendBurst(transactions int, processId int64) {
 	spammingIdentity := ed25119.GenerateKeyPair()
 
-	previousTransactionId := transaction.EmptyId
+	previousTransactionId := message.EmptyId
 
 	burstBuffer := make([][]byte, transactions)
 	for i := 0; i < transactions; i++ {
@@ -83,7 +83,7 @@ func (spammer *Spammer) sendBurst(transactions int, processId int64) {
 			return
 		}
 
-		spamTransaction := transaction.New(previousTransactionId, previousTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM")))
+		spamTransaction := message.New(previousTransactionId, previousTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM")))
 		previousTransactionId = spamTransaction.GetId()
 		burstBuffer[i] = spamTransaction.Bytes()
 	}
diff --git a/packages/binary/tangle/events.go b/packages/binary/tangle/events.go
index ebf537c9..ec056169 100644
--- a/packages/binary/tangle/events.go
+++ b/packages/binary/tangle/events.go
@@ -3,7 +3,7 @@ package tangle
 import (
 	"github.com/iotaledger/hive.go/events"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 )
 
@@ -29,12 +29,12 @@ func newEvents() *Events {
 }
 
 func transactionIdEvent(handler interface{}, params ...interface{}) {
-	handler.(func(transaction.Id))(params[0].(transaction.Id))
+	handler.(func(message.Id))(params[0].(message.Id))
 }
 
 func cachedTransactionEvent(handler interface{}, params ...interface{}) {
-	handler.(func(*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))(
-		params[0].(*transaction.CachedTransaction).Retain(),
+	handler.(func(*message.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))(
+		params[0].(*message.CachedTransaction).Retain(),
 		params[1].(*transactionmetadata.CachedTransactionMetadata).Retain(),
 	)
 }
diff --git a/packages/binary/tangle/model/approver/approver.go b/packages/binary/tangle/model/approver/approver.go
index b08bf342..23c0842b 100644
--- a/packages/binary/tangle/model/approver/approver.go
+++ b/packages/binary/tangle/model/approver/approver.go
@@ -3,36 +3,36 @@ package approver
 import (
 	"github.com/iotaledger/hive.go/objectstorage"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 )
 
 type Approver struct {
 	objectstorage.StorableObjectFlags
 
 	storageKey            []byte
-	referencedTransaction transaction.Id
-	approvingTransaction  transaction.Id
+	referencedTransaction message.Id
+	approvingTransaction  message.Id
 }
 
-func New(referencedTransaction transaction.Id, approvingTransaction transaction.Id) *Approver {
+func New(referencedTransaction message.Id, approvingTransaction message.Id) *Approver {
 	approver := &Approver{
-		storageKey:            make([]byte, transaction.IdLength+transaction.IdLength),
+		storageKey:            make([]byte, message.IdLength+message.IdLength),
 		referencedTransaction: referencedTransaction,
 		approvingTransaction:  approvingTransaction,
 	}
 
-	copy(approver.storageKey[:transaction.IdLength], referencedTransaction[:])
-	copy(approver.storageKey[transaction.IdLength:], approvingTransaction[:])
+	copy(approver.storageKey[:message.IdLength], referencedTransaction[:])
+	copy(approver.storageKey[message.IdLength:], approvingTransaction[:])
 
 	return approver
 }
 
 func FromStorage(id []byte) (result objectstorage.StorableObject) {
 	approver := &Approver{
-		storageKey: make([]byte, transaction.IdLength+transaction.IdLength),
+		storageKey: make([]byte, message.IdLength+message.IdLength),
 	}
-	copy(approver.referencedTransaction[:], id[:transaction.IdLength])
-	copy(approver.approvingTransaction[:], id[transaction.IdLength:])
+	copy(approver.referencedTransaction[:], id[:message.IdLength])
+	copy(approver.approvingTransaction[:], id[message.IdLength:])
 	copy(approver.storageKey, id)
 
 	return approver
@@ -42,7 +42,7 @@ func (approver *Approver) GetStorageKey() []byte {
 	return approver.storageKey
 }
 
-func (approver *Approver) GetApprovingTransactionId() transaction.Id {
+func (approver *Approver) GetApprovingTransactionId() message.Id {
 	return approver.approvingTransaction
 }
 
diff --git a/packages/binary/tangle/model/transaction/cached_transaction.go b/packages/binary/tangle/model/message/cached_transaction.go
similarity index 97%
rename from packages/binary/tangle/model/transaction/cached_transaction.go
rename to packages/binary/tangle/model/message/cached_transaction.go
index d95494ed..8657ecb8 100644
--- a/packages/binary/tangle/model/transaction/cached_transaction.go
+++ b/packages/binary/tangle/model/message/cached_transaction.go
@@ -1,4 +1,4 @@
-package transaction
+package message
 
 import (
 	"github.com/iotaledger/hive.go/objectstorage"
diff --git a/packages/binary/tangle/model/transaction/id.go b/packages/binary/tangle/model/message/id.go
similarity index 98%
rename from packages/binary/tangle/model/transaction/id.go
rename to packages/binary/tangle/model/message/id.go
index c7a93190..40600315 100644
--- a/packages/binary/tangle/model/transaction/id.go
+++ b/packages/binary/tangle/model/message/id.go
@@ -1,4 +1,4 @@
-package transaction
+package message
 
 import (
 	"fmt"
diff --git a/packages/binary/tangle/model/message/init.go b/packages/binary/tangle/model/message/init.go
new file mode 100644
index 00000000..15999765
--- /dev/null
+++ b/packages/binary/tangle/model/message/init.go
@@ -0,0 +1,10 @@
+package message
+
+import (
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
+)
+
+func init() {
+	payload.SetGenericUnmarshalerFactory(data.GenericPayloadUnmarshalerFactory)
+}
diff --git a/packages/binary/tangle/model/transaction/payload/data/data.go b/packages/binary/tangle/model/message/payload/data/data.go
similarity index 96%
rename from packages/binary/tangle/model/transaction/payload/data/data.go
rename to packages/binary/tangle/model/message/payload/data/data.go
index 3c971c20..70efdcfc 100644
--- a/packages/binary/tangle/model/transaction/payload/data/data.go
+++ b/packages/binary/tangle/model/message/payload/data/data.go
@@ -4,7 +4,7 @@ import (
 	"github.com/iotaledger/hive.go/stringify"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
 )
 
 type Data struct {
diff --git a/packages/binary/tangle/model/transaction/payload/data/init.go b/packages/binary/tangle/model/message/payload/data/init.go
similarity index 56%
rename from packages/binary/tangle/model/transaction/payload/data/init.go
rename to packages/binary/tangle/model/message/payload/data/init.go
index 547bca2c..f1ae7c9d 100644
--- a/packages/binary/tangle/model/transaction/payload/data/init.go
+++ b/packages/binary/tangle/model/message/payload/data/init.go
@@ -1,7 +1,7 @@
 package data
 
 import (
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
 )
 
 func init() {
diff --git a/packages/binary/tangle/model/transaction/payload/id.go b/packages/binary/tangle/model/message/payload/id.go
similarity index 100%
rename from packages/binary/tangle/model/transaction/payload/id.go
rename to packages/binary/tangle/model/message/payload/id.go
diff --git a/packages/binary/tangle/model/transaction/payload/payload.go b/packages/binary/tangle/model/message/payload/payload.go
similarity index 100%
rename from packages/binary/tangle/model/transaction/payload/payload.go
rename to packages/binary/tangle/model/message/payload/payload.go
diff --git a/packages/binary/tangle/model/transaction/payload/type.go b/packages/binary/tangle/model/message/payload/type.go
similarity index 100%
rename from packages/binary/tangle/model/transaction/payload/type.go
rename to packages/binary/tangle/model/message/payload/type.go
diff --git a/packages/binary/tangle/model/transaction/payload/type_register.go b/packages/binary/tangle/model/message/payload/type_register.go
similarity index 100%
rename from packages/binary/tangle/model/transaction/payload/type_register.go
rename to packages/binary/tangle/model/message/payload/type_register.go
diff --git a/packages/binary/tangle/model/transaction/test/transaction_test.go b/packages/binary/tangle/model/message/test/transaction_test.go
similarity index 65%
rename from packages/binary/tangle/model/transaction/test/transaction_test.go
rename to packages/binary/tangle/model/message/test/transaction_test.go
index 172e6509..357ec1ba 100644
--- a/packages/binary/tangle/model/transaction/test/transaction_test.go
+++ b/packages/binary/tangle/model/message/test/transaction_test.go
@@ -11,8 +11,8 @@ import (
 	"github.com/panjf2000/ants/v2"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
 )
 
 func BenchmarkVerifyDataTransactions(b *testing.B) {
@@ -21,7 +21,7 @@ func BenchmarkVerifyDataTransactions(b *testing.B) {
 
 	transactions := make([][]byte, b.N)
 	for i := 0; i < b.N; i++ {
-		tx := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data")))
+		tx := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data")))
 
 		if marshaledTransaction, err := tx.MarshalBinary(); err != nil {
 			b.Error(err)
@@ -35,7 +35,7 @@ func BenchmarkVerifyDataTransactions(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		currentIndex := i
 		pool.Submit(func() {
-			if tx, err, _ := transaction.FromBytes(transactions[currentIndex]); err != nil {
+			if tx, err, _ := message.FromBytes(transactions[currentIndex]); err != nil {
 				b.Error(err)
 			} else {
 				tx.VerifySignature()
@@ -49,9 +49,9 @@ func BenchmarkVerifyDataTransactions(b *testing.B) {
 func BenchmarkVerifySignature(b *testing.B) {
 	pool, _ := ants.NewPool(80, ants.WithNonblocking(false))
 
-	transactions := make([]*transaction.Transaction, b.N)
+	transactions := make([]*message.Transaction, b.N)
 	for i := 0; i < b.N; i++ {
-		transactions[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("test")))
+		transactions[i] = message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("test")))
 		transactions[i].Bytes()
 	}
 
diff --git a/packages/binary/tangle/model/transaction/transaction.go b/packages/binary/tangle/model/message/transaction.go
similarity index 98%
rename from packages/binary/tangle/model/transaction/transaction.go
rename to packages/binary/tangle/model/message/transaction.go
index d2420224..ff3c9c36 100644
--- a/packages/binary/tangle/model/transaction/transaction.go
+++ b/packages/binary/tangle/model/message/transaction.go
@@ -1,4 +1,4 @@
-package transaction
+package message
 
 import (
 	"sync"
@@ -8,7 +8,7 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
 
 	"github.com/iotaledger/hive.go/objectstorage"
 
diff --git a/packages/binary/tangle/model/missingtransaction/missingtransaction.go b/packages/binary/tangle/model/missingtransaction/missingtransaction.go
index ea1715c8..64baa95c 100644
--- a/packages/binary/tangle/model/missingtransaction/missingtransaction.go
+++ b/packages/binary/tangle/model/missingtransaction/missingtransaction.go
@@ -3,18 +3,18 @@ package missingtransaction
 import (
 	"time"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/hive.go/objectstorage"
 )
 
 type MissingTransaction struct {
 	objectstorage.StorableObjectFlags
 
-	transactionId transaction.Id
+	transactionId message.Id
 	missingSince  time.Time
 }
 
-func New(transactionId transaction.Id) *MissingTransaction {
+func New(transactionId message.Id) *MissingTransaction {
 	return &MissingTransaction{
 		transactionId: transactionId,
 		missingSince:  time.Now(),
@@ -28,7 +28,7 @@ func FromStorage(key []byte) objectstorage.StorableObject {
 	return result
 }
 
-func (missingTransaction *MissingTransaction) GetTransactionId() transaction.Id {
+func (missingTransaction *MissingTransaction) GetTransactionId() message.Id {
 	return missingTransaction.transactionId
 }
 
diff --git a/packages/binary/tangle/model/transaction/init.go b/packages/binary/tangle/model/transaction/init.go
deleted file mode 100644
index 5805588c..00000000
--- a/packages/binary/tangle/model/transaction/init.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package transaction
-
-import (
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
-)
-
-func init() {
-	payload.SetGenericUnmarshalerFactory(data.GenericPayloadUnmarshalerFactory)
-}
diff --git a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go b/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go
index a7730e62..1fcf1c25 100644
--- a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go
+++ b/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go
@@ -6,13 +6,13 @@ import (
 
 	"github.com/iotaledger/hive.go/objectstorage"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 )
 
 type TransactionMetadata struct {
 	objectstorage.StorableObjectFlags
 
-	transactionId      transaction.Id
+	transactionId      message.Id
 	receivedTime       time.Time
 	solid              bool
 	solidificationTime time.Time
@@ -21,7 +21,7 @@ type TransactionMetadata struct {
 	solidificationTimeMutex sync.RWMutex
 }
 
-func New(transactionId transaction.Id) *TransactionMetadata {
+func New(transactionId message.Id) *TransactionMetadata {
 	return &TransactionMetadata{
 		transactionId: transactionId,
 		receivedTime:  time.Now(),
diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go
index 0e86da66..ac2418da 100644
--- a/packages/binary/tangle/tangle.go
+++ b/packages/binary/tangle/tangle.go
@@ -9,8 +9,8 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/approver"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/missingtransaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 
 	"github.com/iotaledger/hive.go/async"
@@ -41,9 +41,9 @@ type Tangle struct {
 func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
 	result = &Tangle{
 		storageId:                  storageId,
-		transactionStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransaction...), transaction.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
+		transactionStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransaction...), message.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
 		transactionMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransactionMetadata...), transactionmetadata.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
-		approverStorage:            objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), approver.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(transaction.IdLength, transaction.IdLength), objectstorage.LeakDetectionEnabled(false)),
+		approverStorage:            objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), approver.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(message.IdLength, message.IdLength), objectstorage.LeakDetectionEnabled(false)),
 		missingTransactionsStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleMissingTransaction...), missingtransaction.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
 
 		Events: *newEvents(),
@@ -60,22 +60,22 @@ func (tangle *Tangle) GetStorageId() []byte {
 }
 
 // Attaches a new transaction to the tangle.
-func (tangle *Tangle) AttachTransaction(transaction *transaction.Transaction) {
+func (tangle *Tangle) AttachTransaction(transaction *message.Transaction) {
 	tangle.storeTransactionsWorkerPool.Submit(func() { tangle.storeTransactionWorker(transaction) })
 }
 
 // Retrieves a transaction from the tangle.
-func (tangle *Tangle) GetTransaction(transactionId transaction.Id) *transaction.CachedTransaction {
-	return &transaction.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])}
+func (tangle *Tangle) GetTransaction(transactionId message.Id) *message.CachedTransaction {
+	return &message.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])}
 }
 
 // Retrieves the metadata of a transaction from the tangle.
-func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *transactionmetadata.CachedTransactionMetadata {
+func (tangle *Tangle) GetTransactionMetadata(transactionId message.Id) *transactionmetadata.CachedTransactionMetadata {
 	return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(transactionId[:])}
 }
 
 // GetApprovers retrieves the approvers of a transaction from the tangle.
-func (tangle *Tangle) GetApprovers(transactionId transaction.Id) approver.CachedApprovers {
+func (tangle *Tangle) GetApprovers(transactionId message.Id) approver.CachedApprovers {
 	approvers := make(approver.CachedApprovers, 0)
 	tangle.approverStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool {
 		approvers = append(approvers, &approver.CachedApprover{CachedObject: cachedObject})
@@ -87,8 +87,8 @@ func (tangle *Tangle) GetApprovers(transactionId transaction.Id) approver.Cached
 }
 
 // Deletes a transaction from the tangle (i.e. for local snapshots)
-func (tangle *Tangle) DeleteTransaction(transactionId transaction.Id) {
-	tangle.GetTransaction(transactionId).Consume(func(currentTransaction *transaction.Transaction) {
+func (tangle *Tangle) DeleteTransaction(transactionId message.Id) {
+	tangle.GetTransaction(transactionId).Consume(func(currentTransaction *message.Transaction) {
 		trunkTransactionId := currentTransaction.GetTrunkTransactionId()
 		tangle.deleteApprover(trunkTransactionId, transactionId)
 
@@ -135,13 +135,13 @@ func (tangle *Tangle) Prune() error {
 }
 
 // Worker that stores the transactions and calls the corresponding storage events"
-func (tangle *Tangle) storeTransactionWorker(tx *transaction.Transaction) {
+func (tangle *Tangle) storeTransactionWorker(tx *message.Transaction) {
 	// store transaction
-	var cachedTransaction *transaction.CachedTransaction
+	var cachedTransaction *message.CachedTransaction
 	if _tmp, transactionIsNew := tangle.transactionStorage.StoreIfAbsent(tx); !transactionIsNew {
 		return
 	} else {
-		cachedTransaction = &transaction.CachedTransaction{CachedObject: _tmp}
+		cachedTransaction = &message.CachedTransaction{CachedObject: _tmp}
 	}
 
 	// store transaction metadata
@@ -170,9 +170,9 @@ func (tangle *Tangle) storeTransactionWorker(tx *transaction.Transaction) {
 }
 
 // Worker that solidifies the transactions (recursively from past to present).
-func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
-	isTransactionMarkedAsSolid := func(transactionId transaction.Id) bool {
-		if transactionId == transaction.EmptyId {
+func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	isTransactionMarkedAsSolid := func(transactionId message.Id) bool {
+		if transactionId == message.EmptyId {
 			return true
 		}
 
@@ -198,7 +198,7 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *transaction.C
 		return true
 	}
 
-	isTransactionSolid := func(transaction *transaction.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool {
+	isTransactionSolid := func(transaction *message.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool {
 		if transaction == nil || transaction.IsDeleted() {
 			return false
 		}
@@ -214,13 +214,13 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *transaction.C
 		return isTransactionMarkedAsSolid(transaction.GetTrunkTransactionId()) && isTransactionMarkedAsSolid(transaction.GetBranchTransactionId())
 	}
 
-	popElementsFromStack := func(stack *list.List) (*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) {
+	popElementsFromStack := func(stack *list.List) (*message.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) {
 		currentSolidificationEntry := stack.Front()
 		currentCachedTransaction := currentSolidificationEntry.Value.([2]interface{})[0]
 		currentCachedTransactionMetadata := currentSolidificationEntry.Value.([2]interface{})[1]
 		stack.Remove(currentSolidificationEntry)
 
-		return currentCachedTransaction.(*transaction.CachedTransaction), currentCachedTransactionMetadata.(*transactionmetadata.CachedTransactionMetadata)
+		return currentCachedTransaction.(*message.CachedTransaction), currentCachedTransactionMetadata.(*transactionmetadata.CachedTransactionMetadata)
 	}
 
 	// initialize the stack
@@ -261,9 +261,9 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *transaction.C
 }
 
 // Worker that Monitors the missing transactions (by scheduling regular checks).
-func (tangle *Tangle) monitorMissingTransactionWorker(transactionId transaction.Id) {
-	var scheduleNextMissingCheck func(transactionId transaction.Id)
-	scheduleNextMissingCheck = func(transactionId transaction.Id) {
+func (tangle *Tangle) monitorMissingTransactionWorker(transactionId message.Id) {
+	var scheduleNextMissingCheck func(transactionId message.Id)
+	scheduleNextMissingCheck = func(transactionId message.Id) {
 		time.AfterFunc(MISSING_CHECK_INTERVAL, func() {
 			tangle.missingTransactionsStorage.Load(transactionId[:]).Consume(func(object objectstorage.StorableObject) {
 				missingTransaction := object.(*missingtransaction.MissingTransaction)
@@ -288,24 +288,24 @@ func (tangle *Tangle) monitorMissingTransactionWorker(transactionId transaction.
 	scheduleNextMissingCheck(transactionId)
 }
 
-func (tangle *Tangle) deleteApprover(approvedTransaction transaction.Id, approvingTransaction transaction.Id) {
-	idToDelete := make([]byte, transaction.IdLength+transaction.IdLength)
-	copy(idToDelete[:transaction.IdLength], approvedTransaction[:])
-	copy(idToDelete[transaction.IdLength:], approvingTransaction[:])
+func (tangle *Tangle) deleteApprover(approvedTransaction message.Id, approvingTransaction message.Id) {
+	idToDelete := make([]byte, message.IdLength+message.IdLength)
+	copy(idToDelete[:message.IdLength], approvedTransaction[:])
+	copy(idToDelete[message.IdLength:], approvingTransaction[:])
 	tangle.approverStorage.Delete(idToDelete)
 }
 
 // Deletes a transaction and all of its approvers (recursively).
-func (tangle *Tangle) deleteSubtangle(transactionId transaction.Id) {
+func (tangle *Tangle) deleteSubtangle(transactionId message.Id) {
 	cleanupStack := list.New()
 	cleanupStack.PushBack(transactionId)
 
-	processedTransactions := make(map[transaction.Id]types.Empty)
+	processedTransactions := make(map[message.Id]types.Empty)
 	processedTransactions[transactionId] = types.Void
 
 	for cleanupStack.Len() >= 1 {
 		currentStackEntry := cleanupStack.Front()
-		currentTransactionId := currentStackEntry.Value.(transaction.Id)
+		currentTransactionId := currentStackEntry.Value.(message.Id)
 		cleanupStack.Remove(currentStackEntry)
 
 		tangle.DeleteTransaction(currentTransactionId)
diff --git a/packages/binary/tangle/tangle_test.go b/packages/binary/tangle/tangle_test.go
index 6ba20cc7..9cf607e6 100644
--- a/packages/binary/tangle/tangle_test.go
+++ b/packages/binary/tangle/tangle_test.go
@@ -11,8 +11,8 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/database"
 	"github.com/iotaledger/goshimmer/plugins/config"
@@ -34,9 +34,9 @@ func BenchmarkTangle_AttachTransaction(b *testing.B) {
 
 	testIdentity := ed25119.GenerateKeyPair()
 
-	transactionBytes := make([]*transaction.Transaction, b.N)
+	transactionBytes := make([]*message.Transaction, b.N)
 	for i := 0; i < b.N; i++ {
-		transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, testIdentity, time.Now(), 0, data.New([]byte("some data")))
+		transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, testIdentity, time.Now(), 0, data.New([]byte("some data")))
 		transactionBytes[i].Bytes()
 	}
 
@@ -63,36 +63,36 @@ func TestTangle_AttachTransaction(t *testing.T) {
 		return
 	}
 
-	tangle.Events.TransactionAttached.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	tangle.Events.TransactionAttached.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
 		cachedTransactionMetadata.Release()
 
-		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
+		cachedTransaction.Consume(func(transaction *message.Transaction) {
 			fmt.Println("ATTACHED:", transaction.GetId())
 		})
 	}))
 
-	tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
 		cachedTransactionMetadata.Release()
 
-		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
+		cachedTransaction.Consume(func(transaction *message.Transaction) {
 			fmt.Println("SOLID:", transaction.GetId())
 		})
 	}))
 
-	tangle.Events.TransactionUnsolidifiable.Attach(events.NewClosure(func(transactionId transaction.Id) {
+	tangle.Events.TransactionUnsolidifiable.Attach(events.NewClosure(func(transactionId message.Id) {
 		fmt.Println("UNSOLIDIFIABLE:", transactionId)
 	}))
 
-	tangle.Events.TransactionMissing.Attach(events.NewClosure(func(transactionId transaction.Id) {
+	tangle.Events.TransactionMissing.Attach(events.NewClosure(func(transactionId message.Id) {
 		fmt.Println("MISSING:", transactionId)
 	}))
 
-	tangle.Events.TransactionRemoved.Attach(events.NewClosure(func(transactionId transaction.Id) {
+	tangle.Events.TransactionRemoved.Attach(events.NewClosure(func(transactionId message.Id) {
 		fmt.Println("REMOVED:", transactionId)
 	}))
 
-	newTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data")))
-	newTransaction2 := transaction.New(newTransaction1.GetId(), newTransaction1.GetId(), ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some other data")))
+	newTransaction1 := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data")))
+	newTransaction2 := message.New(newTransaction1.GetId(), newTransaction1.GetId(), ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some other data")))
 
 	tangle.AttachTransaction(newTransaction2)
 
diff --git a/packages/binary/tangle/tipselector/events.go b/packages/binary/tangle/tipselector/events.go
index 563e4a70..21f6276e 100644
--- a/packages/binary/tangle/tipselector/events.go
+++ b/packages/binary/tangle/tipselector/events.go
@@ -1,7 +1,7 @@
 package tipselector
 
 import (
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/hive.go/events"
 )
 
@@ -11,5 +11,5 @@ type Events struct {
 }
 
 func transactionIdEvent(handler interface{}, params ...interface{}) {
-	handler.(func(transaction.Id))(params[0].(transaction.Id))
+	handler.(func(message.Id))(params[0].(message.Id))
 }
diff --git a/packages/binary/tangle/tipselector/tipselector.go b/packages/binary/tangle/tipselector/tipselector.go
index 1b03500c..f98d1f50 100644
--- a/packages/binary/tangle/tipselector/tipselector.go
+++ b/packages/binary/tangle/tipselector/tipselector.go
@@ -2,7 +2,7 @@ package tipselector
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/hive.go/events"
 )
 
@@ -21,7 +21,7 @@ func New() *TipSelector {
 	}
 }
 
-func (tipSelector *TipSelector) AddTip(transaction *transaction.Transaction) {
+func (tipSelector *TipSelector) AddTip(transaction *message.Transaction) {
 	transactionId := transaction.GetId()
 	if tipSelector.tips.Set(transactionId, transactionId) {
 		tipSelector.Events.TipAdded.Trigger(transactionId)
@@ -38,16 +38,16 @@ func (tipSelector *TipSelector) AddTip(transaction *transaction.Transaction) {
 	}
 }
 
-func (tipSelector *TipSelector) GetTips() (trunkTransaction, branchTransaction transaction.Id) {
+func (tipSelector *TipSelector) GetTips() (trunkTransaction, branchTransaction message.Id) {
 	tip := tipSelector.tips.RandomEntry()
 	if tip == nil {
-		trunkTransaction = transaction.EmptyId
-		branchTransaction = transaction.EmptyId
+		trunkTransaction = message.EmptyId
+		branchTransaction = message.EmptyId
 
 		return
 	}
 
-	branchTransaction = tip.(transaction.Id)
+	branchTransaction = tip.(message.Id)
 
 	if tipSelector.tips.Size() == 1 {
 		trunkTransaction = branchTransaction
@@ -55,9 +55,9 @@ func (tipSelector *TipSelector) GetTips() (trunkTransaction, branchTransaction t
 		return
 	}
 
-	trunkTransaction = tipSelector.tips.RandomEntry().(transaction.Id)
+	trunkTransaction = tipSelector.tips.RandomEntry().(message.Id)
 	for trunkTransaction == branchTransaction && tipSelector.tips.Size() > 1 {
-		trunkTransaction = tipSelector.tips.RandomEntry().(transaction.Id)
+		trunkTransaction = tipSelector.tips.RandomEntry().(message.Id)
 	}
 
 	return
diff --git a/packages/binary/tangle/tipselector/tipselector_test.go b/packages/binary/tangle/tipselector/tipselector_test.go
index fe565bd1..f089f29d 100644
--- a/packages/binary/tangle/tipselector/tipselector_test.go
+++ b/packages/binary/tangle/tipselector/tipselector_test.go
@@ -7,8 +7,8 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
 )
 
 func Test(t *testing.T) {
@@ -17,11 +17,11 @@ func Test(t *testing.T) {
 
 	// check if first tips point to genesis
 	trunk1, branch1 := tipSelector.GetTips()
-	assert.Equal(t, transaction.EmptyId, trunk1)
-	assert.Equal(t, transaction.EmptyId, branch1)
+	assert.Equal(t, message.EmptyId, trunk1)
+	assert.Equal(t, message.EmptyId, branch1)
 
 	// create a transaction and attach it
-	transaction1 := transaction.New(trunk1, branch1, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
+	transaction1 := message.New(trunk1, branch1, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction1)
 
 	// check if the tip shows up in the tip count
@@ -33,7 +33,7 @@ func Test(t *testing.T) {
 	assert.Equal(t, transaction1.GetId(), branch2)
 
 	// create a 2nd transaction and attach it
-	transaction2 := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
+	transaction2 := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction2)
 
 	// check if the tip shows up in the tip count
@@ -41,7 +41,7 @@ func Test(t *testing.T) {
 
 	// attach a transaction to our two tips
 	trunk3, branch3 := tipSelector.GetTips()
-	transaction3 := transaction.New(trunk3, branch3, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
+	transaction3 := message.New(trunk3, branch3, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction3)
 
 	// check if the tip shows replaces the current tips
diff --git a/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go b/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go
index 132ca46e..f6d64f3e 100644
--- a/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go
+++ b/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go
@@ -7,14 +7,14 @@ import (
 	"github.com/iotaledger/hive.go/async"
 	"github.com/iotaledger/hive.go/autopeering/peer"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 )
 
 var ErrInvalidSignature = fmt.Errorf("invalid signature")
 
 type TransactionSignatureFilter struct {
-	onAcceptCallback func(tx *transaction.Transaction, peer *peer.Peer)
-	onRejectCallback func(tx *transaction.Transaction, err error, peer *peer.Peer)
+	onAcceptCallback func(tx *message.Transaction, peer *peer.Peer)
+	onRejectCallback func(tx *message.Transaction, err error, peer *peer.Peer)
 	workerPool       async.WorkerPool
 
 	onAcceptCallbackMutex sync.RWMutex
@@ -27,7 +27,7 @@ func NewTransactionSignatureFilter() (result *TransactionSignatureFilter) {
 	return
 }
 
-func (filter *TransactionSignatureFilter) Filter(tx *transaction.Transaction, peer *peer.Peer) {
+func (filter *TransactionSignatureFilter) Filter(tx *message.Transaction, peer *peer.Peer) {
 	filter.workerPool.Submit(func() {
 		if tx.VerifySignature() {
 			filter.getAcceptCallback()(tx, peer)
@@ -37,13 +37,13 @@ func (filter *TransactionSignatureFilter) Filter(tx *transaction.Transaction, pe
 	})
 }
 
-func (filter *TransactionSignatureFilter) OnAccept(callback func(tx *transaction.Transaction, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) OnAccept(callback func(tx *message.Transaction, peer *peer.Peer)) {
 	filter.onAcceptCallbackMutex.Lock()
 	filter.onAcceptCallback = callback
 	filter.onAcceptCallbackMutex.Unlock()
 }
 
-func (filter *TransactionSignatureFilter) OnReject(callback func(tx *transaction.Transaction, err error, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) OnReject(callback func(tx *message.Transaction, err error, peer *peer.Peer)) {
 	filter.onRejectCallbackMutex.Lock()
 	filter.onRejectCallback = callback
 	filter.onRejectCallbackMutex.Unlock()
@@ -53,7 +53,7 @@ func (filter *TransactionSignatureFilter) Shutdown() {
 	filter.workerPool.ShutdownGracefully()
 }
 
-func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *transaction.Transaction, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *message.Transaction, peer *peer.Peer)) {
 	filter.onAcceptCallbackMutex.RLock()
 	result = filter.onAcceptCallback
 	filter.onAcceptCallbackMutex.RUnlock()
@@ -61,7 +61,7 @@ func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *t
 	return
 }
 
-func (filter *TransactionSignatureFilter) getRejectCallback() (result func(tx *transaction.Transaction, err error, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) getRejectCallback() (result func(tx *message.Transaction, err error, peer *peer.Peer)) {
 	filter.onRejectCallbackMutex.RLock()
 	result = filter.onRejectCallback
 	filter.onRejectCallbackMutex.RUnlock()
diff --git a/packages/binary/tangle/transactionparser/transaction_filter.go b/packages/binary/tangle/transactionparser/transaction_filter.go
index ac63d23c..c13d94ec 100644
--- a/packages/binary/tangle/transactionparser/transaction_filter.go
+++ b/packages/binary/tangle/transactionparser/transaction_filter.go
@@ -3,12 +3,12 @@ package transactionparser
 import (
 	"github.com/iotaledger/hive.go/autopeering/peer"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 )
 
 type TransactionFilter interface {
-	Filter(tx *transaction.Transaction, peer *peer.Peer)
-	OnAccept(callback func(tx *transaction.Transaction, peer *peer.Peer))
-	OnReject(callback func(tx *transaction.Transaction, err error, peer *peer.Peer))
+	Filter(tx *message.Transaction, peer *peer.Peer)
+	OnAccept(callback func(tx *message.Transaction, peer *peer.Peer))
+	OnReject(callback func(tx *message.Transaction, err error, peer *peer.Peer))
 	Shutdown()
 }
diff --git a/packages/binary/tangle/transactionparser/transactionparser.go b/packages/binary/tangle/transactionparser/transactionparser.go
index 8aa1c56d..d684efc3 100644
--- a/packages/binary/tangle/transactionparser/transactionparser.go
+++ b/packages/binary/tangle/transactionparser/transactionparser.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/iotaledger/hive.go/autopeering/peer"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser/builtinfilters"
 
 	"github.com/iotaledger/hive.go/events"
@@ -33,10 +33,10 @@ func New() (result *TransactionParser) {
 				handler.(func([]byte, error, *peer.Peer))(params[0].([]byte), params[1].(error), params[2].(*peer.Peer))
 			}),
 			TransactionParsed: events.NewEvent(func(handler interface{}, params ...interface{}) {
-				handler.(func(*transaction.Transaction, *peer.Peer))(params[0].(*transaction.Transaction), params[1].(*peer.Peer))
+				handler.(func(*message.Transaction, *peer.Peer))(params[0].(*message.Transaction), params[1].(*peer.Peer))
 			}),
 			TransactionRejected: events.NewEvent(func(handler interface{}, params ...interface{}) {
-				handler.(func(*transaction.Transaction, error, *peer.Peer))(params[0].(*transaction.Transaction), params[1].(error), params[2].(*peer.Peer))
+				handler.(func(*message.Transaction, error, *peer.Peer))(params[0].(*message.Transaction), params[1].(error), params[2].(*peer.Peer))
 			}),
 		},
 	}
@@ -121,13 +121,13 @@ func (transactionParser *TransactionParser) setupTransactionsFilterDataFlow() {
 		numberOfTransactionFilters := len(transactionParser.transactionFilters)
 		for i := 0; i < numberOfTransactionFilters; i++ {
 			if i == numberOfTransactionFilters-1 {
-				transactionParser.transactionFilters[i].OnAccept(func(tx *transaction.Transaction, peer *peer.Peer) {
+				transactionParser.transactionFilters[i].OnAccept(func(tx *message.Transaction, peer *peer.Peer) {
 					transactionParser.Events.TransactionParsed.Trigger(tx, peer)
 				})
 			} else {
 				transactionParser.transactionFilters[i].OnAccept(transactionParser.transactionFilters[i+1].Filter)
 			}
-			transactionParser.transactionFilters[i].OnReject(func(tx *transaction.Transaction, err error, peer *peer.Peer) {
+			transactionParser.transactionFilters[i].OnReject(func(tx *message.Transaction, err error, peer *peer.Peer) {
 				transactionParser.Events.TransactionRejected.Trigger(tx, err, peer)
 			})
 		}
@@ -136,7 +136,7 @@ func (transactionParser *TransactionParser) setupTransactionsFilterDataFlow() {
 }
 
 func (transactionParser *TransactionParser) parseTransaction(bytes []byte, peer *peer.Peer) {
-	if parsedTransaction, err, _ := transaction.FromBytes(bytes); err != nil {
+	if parsedTransaction, err, _ := message.FromBytes(bytes); err != nil {
 		transactionParser.Events.BytesRejected.Trigger(bytes, err, peer)
 	} else {
 		transactionParser.transactionFilters[0].Filter(parsedTransaction, peer)
diff --git a/packages/binary/tangle/transactionparser/transactionparser_test.go b/packages/binary/tangle/transactionparser/transactionparser_test.go
index 85309414..9a5f6413 100644
--- a/packages/binary/tangle/transactionparser/transactionparser_test.go
+++ b/packages/binary/tangle/transactionparser/transactionparser_test.go
@@ -9,12 +9,12 @@ import (
 	"github.com/iotaledger/hive.go/events"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
 )
 
 func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) {
-	txBytes := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"))).Bytes()
+	txBytes := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"))).Bytes()
 	txParser := New()
 
 	b.ResetTimer()
@@ -29,7 +29,7 @@ func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) {
 func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) {
 	transactionBytes := make([][]byte, b.N)
 	for i := 0; i < b.N; i++ {
-		transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"+strconv.Itoa(i)))).Bytes()
+		transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"+strconv.Itoa(i)))).Bytes()
 	}
 
 	txParser := New()
@@ -44,12 +44,12 @@ func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) {
 }
 
 func TestTransactionParser_ParseTransaction(t *testing.T) {
-	tx := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test")))
+	tx := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test")))
 
 	txParser := New()
 	txParser.Parse(tx.Bytes(), nil)
 
-	txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *transaction.Transaction) {
+	txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *message.Transaction) {
 		fmt.Println("PARSED!!!")
 	}))
 
diff --git a/packages/binary/tangle/transactionrequester/transactionrequester.go b/packages/binary/tangle/transactionrequester/transactionrequester.go
index 6f036e81..2016298c 100644
--- a/packages/binary/tangle/transactionrequester/transactionrequester.go
+++ b/packages/binary/tangle/transactionrequester/transactionrequester.go
@@ -7,11 +7,11 @@ import (
 	"github.com/iotaledger/hive.go/async"
 	"github.com/iotaledger/hive.go/events"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 )
 
 type TransactionRequester struct {
-	scheduledRequests map[transaction.Id]*time.Timer
+	scheduledRequests map[message.Id]*time.Timer
 	requestWorker     async.NonBlockingWorkerPool
 	options           *Options
 	Events            Events
@@ -21,11 +21,11 @@ type TransactionRequester struct {
 
 func New(optionalOptions ...Option) *TransactionRequester {
 	requester := &TransactionRequester{
-		scheduledRequests: make(map[transaction.Id]*time.Timer),
+		scheduledRequests: make(map[message.Id]*time.Timer),
 		options:           newOptions(optionalOptions),
 		Events: Events{
 			SendRequest: events.NewEvent(func(handler interface{}, params ...interface{}) {
-				handler.(func(transaction.Id))(params[0].(transaction.Id))
+				handler.(func(message.Id))(params[0].(message.Id))
 			}),
 		},
 	}
@@ -35,7 +35,7 @@ func New(optionalOptions ...Option) *TransactionRequester {
 	return requester
 }
 
-func (requester *TransactionRequester) ScheduleRequest(transactionId transaction.Id) {
+func (requester *TransactionRequester) ScheduleRequest(transactionId message.Id) {
 	var retryRequest func(bool)
 	retryRequest = func(initialRequest bool) {
 		requester.requestWorker.Submit(func() {
@@ -58,7 +58,7 @@ func (requester *TransactionRequester) ScheduleRequest(transactionId transaction
 	retryRequest(true)
 }
 
-func (requester *TransactionRequester) StopRequest(transactionId transaction.Id) {
+func (requester *TransactionRequester) StopRequest(transactionId message.Id) {
 	requester.scheduledRequestsMutex.RLock()
 	if timer, timerExists := requester.scheduledRequests[transactionId]; timerExists {
 		requester.scheduledRequestsMutex.RUnlock()
diff --git a/packages/binary/valuetransfer/address/address.go b/packages/binary/valuetransfer/address/address.go
index 13e89793..265fbf0c 100644
--- a/packages/binary/valuetransfer/address/address.go
+++ b/packages/binary/valuetransfer/address/address.go
@@ -11,13 +11,13 @@ import (
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 )
 
-type AddressVersion = byte
+type Version = byte
 
-type AddressDigest = []byte
+type Digest = []byte
 
 type Address [Length]byte
 
-// Random create a random address, which can for example be used in unit tests.
+// Random creates a random address, which can for example be used in unit tests.
 func Random() (address Address) {
 	// generate a random sequence of bytes
 	addressBytes := make([]byte, Length)
@@ -31,7 +31,7 @@ func Random() (address Address) {
 	return
 }
 
-// FromBase58 creates an address from base58 encoded string.
+// FromBase58 creates an address from a base58 encoded string.
 func FromBase58(base58String string) (address Address, err error) {
 	// decode string
 	bytes, err := base58.Decode(base58String)
@@ -52,6 +52,7 @@ func FromBase58(base58String string) (address Address, err error) {
 	return
 }
 
+// FromED25519PubKey creates an address from an ed25519 public key.
 func FromED25519PubKey(key ed25119.PublicKey) (address Address) {
 	digest := blake2b.Sum256(key[:])
 
@@ -75,7 +76,7 @@ func FromBytes(bytes []byte) (result Address, err error, consumedBytes int) {
 	return
 }
 
-// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+// Parse is a wrapper for simplified unmarshaling of a byte stream using the marshalUtil package.
 func Parse(marshalUtil *marshalutil.MarshalUtil) (Address, error) {
 	if address, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
 		return Address{}, err
@@ -84,11 +85,13 @@ func Parse(marshalUtil *marshalutil.MarshalUtil) (Address, error) {
 	}
 }
 
-func (address *Address) GetVersion() AddressVersion {
+// Version returns the version of the address, which corresponds to the signature scheme that is used.
+func (address *Address) Version() Version {
 	return address[0]
 }
 
-func (address *Address) GetDigest() AddressDigest {
+// Digest returns the digest part of an address (i.e. the hashed version of the ed25519 public key)-
+func (address *Address) Digest() Digest {
 	return address[1:]
 }
 
@@ -102,4 +105,5 @@ func (address Address) String() string {
 	return base58.Encode(address.Bytes())
 }
 
+// Length contains the length of an address (digest length = 32 + version byte length = 1).
 const Length = 33
diff --git a/packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go b/packages/binary/valuetransfer/address/signaturescheme/ed25519.go
similarity index 95%
rename from packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go
rename to packages/binary/valuetransfer/address/signaturescheme/ed25519.go
index e7423f55..997de93a 100644
--- a/packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go
+++ b/packages/binary/valuetransfer/address/signaturescheme/ed25519.go
@@ -1,4 +1,4 @@
-package signatures
+package signaturescheme
 
 import (
 	"fmt"
@@ -64,7 +64,7 @@ type ed25519Signature struct {
 
 // ed25519SignatureFromBytes unmarshals an ed25519 signatures from a sequence of bytes.
 // It either creates a new signature or fills the optionally provided object with the parsed information.
-func ed25519SignatureFromBytes(bytes []byte, optionalTargetObject ...*ed25519Signature) (result *ed25519Signature, err error, consumedBytes int) {
+func Ed25519SignatureFromBytes(bytes []byte, optionalTargetObject ...*ed25519Signature) (result *ed25519Signature, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -128,7 +128,4 @@ func (signature *ed25519Signature) Address() address.Address {
 	return address.FromED25519PubKey(signature.publicKey)
 }
 
-// interface contract (allow the compiler to check if the implementation has all of the required methods).
-var _ Signature = &ed25519Signature{}
-
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetransfer/address/signaturescheme/signature.go b/packages/binary/valuetransfer/address/signaturescheme/signature.go
new file mode 100644
index 00000000..1e89e733
--- /dev/null
+++ b/packages/binary/valuetransfer/address/signaturescheme/signature.go
@@ -0,0 +1,15 @@
+package signaturescheme
+
+import "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+
+// Signature defines an interface for an address signature generated by the corresponding signature scheme.
+type Signature interface {
+	// IsValid returns true if the signature is valid for the given data.
+	IsValid(signedData []byte) bool
+
+	// Bytes returns a marshaled version of the signature.
+	Bytes() []byte
+
+	// Address returns the address, that this signature signs.
+	Address() address.Address
+}
diff --git a/packages/binary/valuetransfer/address/signaturescheme/signaturescheme.go b/packages/binary/valuetransfer/address/signaturescheme/signaturescheme.go
new file mode 100644
index 00000000..2abe2f6a
--- /dev/null
+++ b/packages/binary/valuetransfer/address/signaturescheme/signaturescheme.go
@@ -0,0 +1,17 @@
+package signaturescheme
+
+import (
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+)
+
+// SignatureScheme defines an interface for different signature generation methods (i.e. ED25519, WOTS, and so on ...).
+type SignatureScheme interface {
+	// Version returns the version byte that is associated to this signature scheme.
+	Version() byte
+
+	// Address returns the address that this signature scheme instance is securing.
+	Address() address.Address
+
+	// Sign creates a valid signature for the given data according to the signature scheme implementation.
+	Sign(data []byte) Signature
+}
diff --git a/packages/binary/valuetransfer/balance/balance.go b/packages/binary/valuetransfer/balance/balance.go
new file mode 100644
index 00000000..e9e3acb2
--- /dev/null
+++ b/packages/binary/valuetransfer/balance/balance.go
@@ -0,0 +1,79 @@
+package balance
+
+import (
+	"strconv"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+type Balance struct {
+	value int64
+	color Color
+}
+
+func New(color Color, balance int64) (result *Balance) {
+	result = &Balance{
+		color: color,
+		value: balance,
+	}
+
+	return
+}
+
+func FromBytes(bytes []byte) (result *Balance, err error, consumedBytes int) {
+	result = &Balance{}
+
+	marshalUtil := marshalutil.New(bytes)
+
+	result.value, err = marshalUtil.ReadInt64()
+	if err != nil {
+		return
+	}
+
+	if coinColor, colorErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) {
+		return ColorFromBytes(data)
+	}); colorErr != nil {
+		return nil, colorErr, marshalUtil.ReadOffset()
+	} else {
+		result.color = coinColor.(Color)
+	}
+
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func Parse(marshalUtil *marshalutil.MarshalUtil) (*Balance, error) {
+	if address, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return address.(*Balance), nil
+	}
+}
+
+// Value returns the numeric value of the balance.
+func (balance *Balance) Value() int64 {
+	return balance.value
+}
+
+// Color returns the Color of the balance.
+func (balance *Balance) Color() Color {
+	return balance.color
+}
+
+func (balance *Balance) Bytes() []byte {
+	marshalUtil := marshalutil.New(Length)
+
+	marshalUtil.WriteInt64(balance.value)
+	marshalUtil.WriteBytes(balance.color.Bytes())
+
+	return marshalUtil.Bytes()
+}
+
+func (balance *Balance) String() string {
+	return strconv.FormatInt(balance.value, 10) + " " + balance.color.String()
+}
+
+// Length encodes the length of a marshaled Balance (the length of the color + 8 bytes for the balance).
+const Length = 8 + ColorLength
diff --git a/packages/binary/valuetransfer/balance/balance_test.go b/packages/binary/valuetransfer/balance/balance_test.go
new file mode 100644
index 00000000..98fbd6bb
--- /dev/null
+++ b/packages/binary/valuetransfer/balance/balance_test.go
@@ -0,0 +1,24 @@
+package balance
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMarshalUnmarshal(t *testing.T) {
+	balance := New(COLOR_IOTA, 1337)
+	assert.Equal(t, int64(1337), balance.Value())
+	assert.Equal(t, COLOR_IOTA, balance.Color())
+
+	marshaledBalance := balance.Bytes()
+	assert.Equal(t, Length, len(marshaledBalance))
+
+	restoredBalance, err, consumedBytes := FromBytes(marshaledBalance)
+	if err != nil {
+		panic(err)
+	}
+	assert.Equal(t, Length, consumedBytes)
+	assert.Equal(t, balance.value, restoredBalance.Value())
+	assert.Equal(t, balance.color, restoredBalance.Color())
+}
diff --git a/packages/binary/valuetransfer/coloredbalance/color/color.go b/packages/binary/valuetransfer/balance/color.go
similarity index 51%
rename from packages/binary/valuetransfer/coloredbalance/color/color.go
rename to packages/binary/valuetransfer/balance/color.go
index 5afcc691..a3794188 100644
--- a/packages/binary/valuetransfer/coloredbalance/color/color.go
+++ b/packages/binary/valuetransfer/balance/color.go
@@ -1,4 +1,4 @@
-package color
+package balance
 
 import (
 	"github.com/mr-tron/base58"
@@ -6,32 +6,32 @@ import (
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
-type Color [Length]byte
+type Color [ColorLength]byte
 
-func FromBytes(bytes []byte) (result Color, err error, consumedBytes int) {
-	colorBytes, err := marshalutil.New(bytes).ReadBytes(Length)
+func ColorFromBytes(bytes []byte) (result Color, err error, consumedBytes int) {
+	colorBytes, err := marshalutil.New(bytes).ReadBytes(ColorLength)
 	if err != nil {
 		return
 	}
 	copy(result[:], colorBytes)
 
-	consumedBytes = Length
+	consumedBytes = ColorLength
 
 	return
 }
 
-const Length = 32
+const ColorLength = 32
 
 func (color Color) Bytes() []byte {
 	return color[:]
 }
 
 func (color Color) String() string {
-	if color == IOTA {
+	if color == COLOR_IOTA {
 		return "IOTA"
 	}
 
 	return base58.Encode(color[:])
 }
 
-var IOTA Color = [32]byte{}
+var COLOR_IOTA Color = [32]byte{}
diff --git a/packages/binary/valuetransfer/coloredbalance/coloredbalance.go b/packages/binary/valuetransfer/coloredbalance/coloredbalance.go
deleted file mode 100644
index e17e8d68..00000000
--- a/packages/binary/valuetransfer/coloredbalance/coloredbalance.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package coloredbalance
-
-import (
-	"strconv"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance/color"
-)
-
-type ColoredBalance struct {
-	color   color.Color
-	balance int64
-}
-
-func New(color color.Color, balance int64) (result *ColoredBalance) {
-	result = &ColoredBalance{
-		color:   color,
-		balance: balance,
-	}
-
-	return
-}
-
-func FromBytes(bytes []byte) (result *ColoredBalance, err error, consumedBytes int) {
-	result = &ColoredBalance{}
-
-	marshalUtil := marshalutil.New(bytes)
-
-	if coinColor, colorErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) {
-		return color.FromBytes(data)
-	}); colorErr != nil {
-		return nil, colorErr, marshalUtil.ReadOffset()
-	} else {
-		result.color = coinColor.(color.Color)
-	}
-
-	result.balance, err = marshalUtil.ReadInt64()
-	if err != nil {
-		return
-	}
-
-	consumedBytes = marshalUtil.ReadOffset()
-
-	return
-}
-
-// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
-func Parse(marshalUtil *marshalutil.MarshalUtil) (*ColoredBalance, error) {
-	if address, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
-		return nil, err
-	} else {
-		return address.(*ColoredBalance), nil
-	}
-}
-
-func (balance *ColoredBalance) Bytes() []byte {
-	marshalUtil := marshalutil.New(color.Length + marshalutil.UINT32_SIZE)
-
-	marshalUtil.WriteBytes(balance.color.Bytes())
-	marshalUtil.WriteInt64(balance.balance)
-
-	return marshalUtil.Bytes()
-}
-
-func (balance *ColoredBalance) String() string {
-	return strconv.FormatInt(balance.balance, 10) + " " + balance.color.String()
-}
-
-// Length encodes the length of a marshaled ColoredBalance (the length of the color + 8 bytes for the balance).
-const Length = color.Length + 8
diff --git a/packages/binary/valuetransfer/payload/cached_object.go b/packages/binary/valuetransfer/payload/cached_object.go
deleted file mode 100644
index 5c1639b1..00000000
--- a/packages/binary/valuetransfer/payload/cached_object.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package payload
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-// CachedObject is a wrapper for the object storage, that takes care of type casting the managed objects.
-// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
-// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
-// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
-// specialized types of CachedObjects, without having to manually type cast over and over again.
-type CachedObject struct {
-	objectstorage.CachedObject
-}
-
-// Retain wraps the underlying method to return a new "wrapped object".
-func (cachedPayload *CachedObject) Retain() *CachedObject {
-	return &CachedObject{cachedPayload.CachedObject.Retain()}
-}
-
-// Consume wraps the underlying method to return the correctly typed objects in the callback.
-func (cachedPayload *CachedObject) Consume(consumer func(payload *Payload)) bool {
-	return cachedPayload.CachedObject.Consume(func(object objectstorage.StorableObject) {
-		consumer(object.(*Payload))
-	})
-}
-
-// Unwrap provides a way to "Get" a type casted version of the underlying object.
-func (cachedPayload *CachedObject) Unwrap() *Payload {
-	if untypedTransaction := cachedPayload.Get(); untypedTransaction == nil {
-		return nil
-	} else {
-		if typeCastedTransaction := untypedTransaction.(*Payload); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
-			return nil
-		} else {
-			return typeCastedTransaction
-		}
-	}
-}
diff --git a/packages/binary/valuetransfer/payload/id/id.go b/packages/binary/valuetransfer/payload/id.go
similarity index 63%
rename from packages/binary/valuetransfer/payload/id/id.go
rename to packages/binary/valuetransfer/payload/id.go
index 8a8f24f0..d35da661 100644
--- a/packages/binary/valuetransfer/payload/id/id.go
+++ b/packages/binary/valuetransfer/payload/id.go
@@ -1,4 +1,4 @@
-package id
+package payload
 
 import (
 	"fmt"
@@ -9,16 +9,16 @@ import (
 )
 
 // Id represents the hash of a payload that is used to identify the given payload.
-type Id [Length]byte
+type Id [IdLength]byte
 
-// New creates a payload id from a base58 encoded string.
-func New(base58EncodedString string) (result Id, err error) {
+// NewId creates a payload id from a base58 encoded string.
+func NewId(base58EncodedString string) (result Id, err error) {
 	bytes, err := base58.Decode(base58EncodedString)
 	if err != nil {
 		return
 	}
 
-	if len(bytes) != Length {
+	if len(bytes) != IdLength {
 		err = fmt.Errorf("length of base58 formatted payload id is wrong")
 
 		return
@@ -29,18 +29,18 @@ func New(base58EncodedString string) (result Id, err error) {
 	return
 }
 
-// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
-func Parse(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
-	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
+// ParseId is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func ParseId(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
+	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) }); err != nil {
 		return Id{}, err
 	} else {
 		return id.(Id), nil
 	}
 }
 
-// FromBytes unmarshals a payload id from a sequence of bytes.
+// IdFromBytes unmarshals a payload id from a sequence of bytes.
 // It either creates a new payload id or fills the optionally provided object with the parsed information.
-func FromBytes(bytes []byte, optionalTargetObject ...*Id) (result Id, err error, consumedBytes int) {
+func IdFromBytes(bytes []byte, optionalTargetObject ...*Id) (result Id, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	var targetObject *Id
 	switch len(optionalTargetObject) {
@@ -49,14 +49,14 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Id) (result Id, err error,
 	case 1:
 		targetObject = optionalTargetObject[0]
 	default:
-		panic("too many arguments in call to FromBytes")
+		panic("too many arguments in call to IdFromBytes")
 	}
 
 	// initialize helper
 	marshalUtil := marshalutil.New(bytes)
 
 	// read id from bytes
-	idBytes, err := marshalUtil.ReadBytes(Length)
+	idBytes, err := marshalUtil.ReadBytes(IdLength)
 	if err != nil {
 		return
 	}
@@ -81,7 +81,7 @@ func (id Id) Bytes() []byte {
 }
 
 // Empty represents the id encoding the genesis.
-var Genesis Id
+var GenesisId Id
 
-// Length defined the amount of bytes in a payload id (32 bytes hash value).
-const Length = 32
+// IdLength defined the amount of bytes in a payload id (32 bytes hash value).
+const IdLength = 32
diff --git a/packages/binary/valuetransfer/payload/id/id_test.go b/packages/binary/valuetransfer/payload/id_test.go
similarity index 70%
rename from packages/binary/valuetransfer/payload/id/id_test.go
rename to packages/binary/valuetransfer/payload/id_test.go
index 86fd25ab..22ff3b66 100644
--- a/packages/binary/valuetransfer/payload/id/id_test.go
+++ b/packages/binary/valuetransfer/payload/id_test.go
@@ -1,4 +1,4 @@
-package id
+package payload
 
 import (
 	"testing"
@@ -8,14 +8,14 @@ import (
 
 func Test(t *testing.T) {
 	// create variable for id
-	sourceId, err := New("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM")
+	sourceId, err := NewId("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM")
 	if err != nil {
 		panic(err)
 	}
 
 	// read serialized id into both variables
 	var restoredIdPointer Id
-	restoredIdValue, err, _ := FromBytes(sourceId.Bytes(), &restoredIdPointer)
+	restoredIdValue, err, _ := IdFromBytes(sourceId.Bytes(), &restoredIdPointer)
 	if err != nil {
 		panic(err)
 	}
diff --git a/packages/binary/valuetransfer/payload/payload.go b/packages/binary/valuetransfer/payload/payload.go
index ee248f7d..c97b2f12 100644
--- a/packages/binary/valuetransfer/payload/payload.go
+++ b/packages/binary/valuetransfer/payload/payload.go
@@ -8,36 +8,34 @@ import (
 	"golang.org/x/crypto/blake2b"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
 
 type Payload struct {
 	objectstorage.StorableObjectFlags
 
-	trunkPayloadId  payloadid.Id
-	branchPayloadId payloadid.Id
-	transfer        *transfer.Transfer
+	trunkPayloadId  Id
+	branchPayloadId Id
+	transaction     *transaction.Transaction
 
-	id      *payloadid.Id
+	id      *Id
 	idMutex sync.RWMutex
 
 	bytes      []byte
 	bytesMutex sync.RWMutex
 }
 
-func New(trunkPayloadId, branchPayloadId payloadid.Id, valueTransfer *transfer.Transfer) *Payload {
+func New(trunkPayloadId, branchPayloadId Id, valueTransfer *transaction.Transaction) *Payload {
 	return &Payload{
 		trunkPayloadId:  trunkPayloadId,
 		branchPayloadId: branchPayloadId,
-		transfer:        valueTransfer,
+		transaction:     valueTransfer,
 	}
 }
 
 func FromStorage(key []byte) objectstorage.StorableObject {
-	id, err, _ := payloadid.FromBytes(key)
+	id, err, _ := IdFromBytes(key)
 	if err != nil {
 		panic(err)
 	}
@@ -74,25 +72,25 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload,
 	}
 
 	// parse trunk payload id
-	parsedTrunkPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return payloadid.FromBytes(data) })
+	parsedTrunkPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.trunkPayloadId = parsedTrunkPayloadId.(payloadid.Id)
+	result.trunkPayloadId = parsedTrunkPayloadId.(Id)
 
 	// parse branch payload id
-	parsedBranchPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return payloadid.FromBytes(data) })
+	parsedBranchPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.branchPayloadId = parsedBranchPayloadId.(payloadid.Id)
+	result.branchPayloadId = parsedBranchPayloadId.(Id)
 
 	// parse transfer
-	parsedTransfer, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return transfer.FromBytes(data) })
+	parsedTransfer, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return transaction.FromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.transfer = parsedTransfer.(*transfer.Transfer)
+	result.transaction = parsedTransfer.(*transaction.Transaction)
 
 	// return the number of bytes we processed
 	consumedBytes = marshalUtil.ReadOffset()
@@ -103,7 +101,7 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload,
 	return
 }
 
-func (payload *Payload) GetId() payloadid.Id {
+func (payload *Payload) Id() Id {
 	// acquire lock for reading id
 	payload.idMutex.RLock()
 
@@ -125,27 +123,27 @@ func (payload *Payload) GetId() payloadid.Id {
 	}
 
 	// otherwise calculate the id
-	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length + transferid.Length)
+	marshalUtil := marshalutil.New(IdLength + IdLength + transaction.IdLength)
 	marshalUtil.WriteBytes(payload.trunkPayloadId.Bytes())
 	marshalUtil.WriteBytes(payload.branchPayloadId.Bytes())
-	marshalUtil.WriteBytes(payload.GetTransfer().GetId().Bytes())
+	marshalUtil.WriteBytes(payload.Transaction().Id().Bytes())
 
-	var id payloadid.Id = blake2b.Sum256(marshalUtil.Bytes())
+	var id Id = blake2b.Sum256(marshalUtil.Bytes())
 	payload.id = &id
 
 	return id
 }
 
-func (payload *Payload) GetTrunkId() payloadid.Id {
+func (payload *Payload) TrunkId() Id {
 	return payload.trunkPayloadId
 }
 
-func (payload *Payload) GetBranchId() payloadid.Id {
+func (payload *Payload) BranchId() Id {
 	return payload.branchPayloadId
 }
 
-func (payload *Payload) GetTransfer() *transfer.Transfer {
-	return payload.transfer
+func (payload *Payload) Transaction() *transaction.Transaction {
+	return payload.transaction
 }
 
 func (payload *Payload) Bytes() (bytes []byte) {
@@ -170,13 +168,13 @@ func (payload *Payload) Bytes() (bytes []byte) {
 	}
 
 	// retrieve bytes of transfer
-	transferBytes, err := payload.GetTransfer().MarshalBinary()
+	transferBytes, err := payload.Transaction().MarshalBinary()
 	if err != nil {
 		return
 	}
 
 	// marshal fields
-	payloadLength := payloadid.Length + payloadid.Length + len(transferBytes)
+	payloadLength := IdLength + IdLength + len(transferBytes)
 	marshalUtil := marshalutil.New(marshalutil.UINT32_SIZE + marshalutil.UINT32_SIZE + payloadLength)
 	marshalUtil.WriteUint32(Type)
 	marshalUtil.WriteUint32(uint32(payloadLength))
@@ -193,10 +191,10 @@ func (payload *Payload) Bytes() (bytes []byte) {
 
 func (payload *Payload) String() string {
 	return stringify.Struct("Payload",
-		stringify.StructField("id", payload.GetId()),
-		stringify.StructField("trunk", payload.GetTrunkId()),
-		stringify.StructField("branch", payload.GetBranchId()),
-		stringify.StructField("transfer", payload.GetTransfer()),
+		stringify.StructField("id", payload.Id()),
+		stringify.StructField("trunk", payload.TrunkId()),
+		stringify.StructField("branch", payload.BranchId()),
+		stringify.StructField("transfer", payload.Transaction()),
 	)
 }
 
@@ -239,7 +237,7 @@ var _ payload.Payload = &Payload{}
 // UnmarshalBinary(data []byte) (err error) already implemented by Payload
 
 func (payload *Payload) GetStorageKey() []byte {
-	id := payload.GetId()
+	id := payload.Id()
 
 	return id[:]
 }
@@ -252,3 +250,37 @@ func (payload *Payload) Update(other objectstorage.StorableObject) {
 var _ objectstorage.StorableObject = &Payload{}
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CachedPayload is a wrapper for the object storage, that takes care of type casting the managed objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of CachedObjects, without having to manually type cast over and over again.
+type CachedPayload struct {
+	objectstorage.CachedObject
+}
+
+// Retain wraps the underlying method to return a new "wrapped object".
+func (cachedPayload *CachedPayload) Retain() *CachedPayload {
+	return &CachedPayload{cachedPayload.CachedObject.Retain()}
+}
+
+// Consume wraps the underlying method to return the correctly typed objects in the callback.
+func (cachedPayload *CachedPayload) Consume(consumer func(payload *Payload)) bool {
+	return cachedPayload.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*Payload))
+	})
+}
+
+// Unwrap provides a way to "Get" a type casted version of the underlying object.
+func (cachedPayload *CachedPayload) Unwrap() *Payload {
+	if untypedTransaction := cachedPayload.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*Payload); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/payload/payload_test.go b/packages/binary/valuetransfer/payload/payload_test.go
new file mode 100644
index 00000000..0dc21937
--- /dev/null
+++ b/packages/binary/valuetransfer/payload/payload_test.go
@@ -0,0 +1,120 @@
+package payload
+
+import (
+	"fmt"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+func ExamplePayload() {
+	// 1. create value transfer (user provides this)
+	valueTransfer := transaction.New(
+		// inputs
+		transaction.NewInputs(
+			transaction.NewOutputId(address.Random(), transaction.RandomId()),
+			transaction.NewOutputId(address.Random(), transaction.RandomId()),
+		),
+
+		// outputs
+		transaction.NewOutputs(map[address.Address][]*balance.Balance{
+			address.Random(): {
+				balance.New(balance.COLOR_IOTA, 1337),
+			},
+		}),
+	)
+
+	// 2. create value payload (the ontology creates this and wraps the user provided transfer accordingly)
+	valuePayload := New(
+		// trunk in "value transfer ontology" (filled by ontology tipSelector)
+		GenesisId,
+
+		// branch in "value transfer ontology"  (filled by ontology tipSelector)
+		GenesisId,
+
+		// value transfer
+		valueTransfer,
+	)
+
+	// 3. build actual transaction (the base layer creates this and wraps the ontology provided payload)
+	tx := message.New(
+		// trunk in "network tangle" ontology (filled by tipSelector)
+		message.EmptyId,
+
+		// branch in "network tangle" ontology (filled by tipSelector)
+		message.EmptyId,
+
+		// issuer of the transaction (signs automatically)
+		ed25119.GenerateKeyPair(),
+
+		// the time when the transaction was created
+		time.Now(),
+
+		// the ever increasing sequence number of this transaction
+		0,
+
+		// payload
+		valuePayload,
+	)
+
+	fmt.Println(tx)
+}
+
+func TestPayload(t *testing.T) {
+	addressKeyPair1 := ed25119.GenerateKeyPair()
+	addressKeyPair2 := ed25119.GenerateKeyPair()
+
+	originalPayload := New(
+		GenesisId,
+		GenesisId,
+		transaction.New(
+			transaction.NewInputs(
+				transaction.NewOutputId(address.FromED25519PubKey(addressKeyPair1.PublicKey), transaction.RandomId()),
+				transaction.NewOutputId(address.FromED25519PubKey(addressKeyPair2.PublicKey), transaction.RandomId()),
+			),
+
+			transaction.NewOutputs(map[address.Address][]*balance.Balance{
+				address.Random(): {
+					balance.New(balance.COLOR_IOTA, 1337),
+				},
+			}),
+		).Sign(
+			signaturescheme.ED25519(addressKeyPair1),
+		),
+	)
+
+	assert.Equal(t, false, originalPayload.Transaction().SignaturesValid())
+
+	originalPayload.Transaction().Sign(
+		signaturescheme.ED25519(addressKeyPair2),
+	)
+
+	assert.Equal(t, true, originalPayload.Transaction().SignaturesValid())
+
+	clonedPayload1, err, _ := FromBytes(originalPayload.Bytes())
+	if err != nil {
+		panic(err)
+	}
+
+	assert.Equal(t, originalPayload.BranchId(), clonedPayload1.BranchId())
+	assert.Equal(t, originalPayload.TrunkId(), clonedPayload1.TrunkId())
+	assert.Equal(t, originalPayload.Transaction().Bytes(), clonedPayload1.Transaction().Bytes())
+	assert.Equal(t, originalPayload.Id(), clonedPayload1.Id())
+	assert.Equal(t, true, clonedPayload1.Transaction().SignaturesValid())
+
+	clonedPayload2, err, _ := FromBytes(clonedPayload1.Bytes())
+	if err != nil {
+		panic(err)
+	}
+
+	assert.Equal(t, originalPayload.Id(), clonedPayload2.Id())
+	assert.Equal(t, true, clonedPayload2.Transaction().SignaturesValid())
+}
diff --git a/packages/binary/valuetransfer/payload/transfer/id/id.go b/packages/binary/valuetransfer/payload/transfer/id/id.go
deleted file mode 100644
index 22baa614..00000000
--- a/packages/binary/valuetransfer/payload/transfer/id/id.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package id
-
-import (
-	"github.com/mr-tron/base58"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-)
-
-type Id [Length]byte
-
-func New(idBytes []byte) (result Id) {
-	copy(result[:], idBytes)
-
-	return
-}
-
-// FromBytes unmarshals a transfer id from a sequence of bytes.
-func FromBytes(bytes []byte) (result Id, err error, consumedBytes int) {
-	// parse the bytes
-	marshalUtil := marshalutil.New(bytes)
-	idBytes, err := marshalUtil.ReadBytes(Length)
-	if err != nil {
-		return
-	}
-	copy(result[:], idBytes)
-	consumedBytes = marshalUtil.ReadOffset()
-
-	return
-}
-
-// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
-func Parse(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
-	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
-		return Id{}, err
-	} else {
-		return id.(Id), nil
-	}
-}
-
-func (id Id) Bytes() []byte {
-	return id[:]
-}
-
-func (id Id) String() string {
-	return base58.Encode(id[:])
-}
-
-const Length = 32
diff --git a/packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go b/packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
deleted file mode 100644
index 81fe7247..00000000
--- a/packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package signatures
-
-import "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-
-// SignatureScheme defines an interface for different signature generation methods (i.e. ED25519, WOTS, and so on ...).
-type SignatureScheme interface {
-	// Version returns the version byte that is associated to this signature scheme.
-	Version() byte
-
-	// Address returns the address that this signature scheme instance is securing.
-	Address() address.Address
-
-	// Sign creates a valid signature for the given data according to the signature scheme implementation.
-	Sign(data []byte) Signature
-}
-
-// Signature defines an interface for an address signature generated by the corresponding signature scheme.
-type Signature interface {
-	// IsValid returns true if the signature is valid for the given data.
-	IsValid(signedData []byte) bool
-
-	// Bytes returns a marshaled version of the signature.
-	Bytes() []byte
-
-	// Address returns the address, that this signature signs.
-	Address() address.Address
-}
diff --git a/packages/binary/valuetransfer/payload/transfer/transfer.go b/packages/binary/valuetransfer/payload/transfer/transfer.go
deleted file mode 100644
index 0703fcba..00000000
--- a/packages/binary/valuetransfer/payload/transfer/transfer.go
+++ /dev/null
@@ -1,296 +0,0 @@
-package transfer
-
-import (
-	"fmt"
-	"sync"
-
-	"github.com/iotaledger/hive.go/objectstorage"
-	"github.com/iotaledger/hive.go/stringify"
-	"github.com/mr-tron/base58"
-	"golang.org/x/crypto/blake2b"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/inputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/outputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/signatures"
-)
-
-// region IMPLEMENT Transfer ///////////////////////////////////////////////////////////////////////////////////////////
-
-type Transfer struct {
-	objectstorage.StorableObjectFlags
-
-	inputs     *inputs.Inputs
-	outputs    *outputs.Outputs
-	signatures *signatures.Signatures
-
-	id      *id.Id
-	idMutex sync.RWMutex
-
-	essenceBytes      []byte
-	essenceBytesMutex sync.RWMutex
-
-	signatureBytes      []byte
-	signatureBytesMutex sync.RWMutex
-
-	bytes      []byte
-	bytesMutex sync.RWMutex
-}
-
-func New(inputs *inputs.Inputs, outputs *outputs.Outputs) *Transfer {
-	return &Transfer{
-		inputs:     inputs,
-		outputs:    outputs,
-		signatures: signatures.New(),
-	}
-}
-
-func FromBytes(bytes []byte, optionalTargetObject ...*Transfer) (result *Transfer, err error, consumedBytes int) {
-	// determine the target object that will hold the unmarshaled information
-	switch len(optionalTargetObject) {
-	case 0:
-		result = &Transfer{}
-	case 1:
-		result = optionalTargetObject[0]
-	default:
-		panic("too many arguments in call to OutputFromBytes")
-	}
-
-	// initialize helper
-	marshalUtil := marshalutil.New(bytes)
-
-	// unmarshal inputs
-	parsedInputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return inputs.FromBytes(data) })
-	if err != nil {
-		return
-	}
-	result.inputs = parsedInputs.(*inputs.Inputs)
-
-	// unmarshal outputs
-	parsedOutputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return outputs.FromBytes(data) })
-	if err != nil {
-		return
-	}
-	result.outputs = parsedOutputs.(*outputs.Outputs)
-
-	// store essence bytes
-	essenceBytesCount := marshalUtil.ReadOffset()
-	result.essenceBytes = make([]byte, essenceBytesCount)
-	copy(result.essenceBytes, bytes[:essenceBytesCount])
-
-	// unmarshal outputs
-	parsedSignatures, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return signatures.FromBytes(data) })
-	if err != nil {
-		return
-	}
-	result.signatures = parsedSignatures.(*signatures.Signatures)
-
-	// store signature bytes
-	signatureBytesCount := marshalUtil.ReadOffset() - essenceBytesCount
-	result.signatureBytes = make([]byte, signatureBytesCount)
-	copy(result.signatureBytes, bytes[essenceBytesCount:essenceBytesCount+signatureBytesCount])
-
-	// return the number of bytes we processed
-	consumedBytes = essenceBytesCount + signatureBytesCount
-
-	// store bytes, so we don't have to marshal manually
-	result.bytes = bytes[:consumedBytes]
-
-	return
-}
-
-func FromStorage(key []byte) *Transfer {
-	transferId := id.New(key)
-
-	return &Transfer{
-		id: &transferId,
-	}
-}
-
-func (transfer *Transfer) GetId() id.Id {
-	// acquire lock for reading id
-	transfer.idMutex.RLock()
-
-	// return if id has been calculated already
-	if transfer.id != nil {
-		defer transfer.idMutex.RUnlock()
-
-		return *transfer.id
-	}
-
-	// switch to write lock
-	transfer.idMutex.RUnlock()
-	transfer.idMutex.Lock()
-	defer transfer.idMutex.Unlock()
-
-	// return if id has been calculated in the mean time
-	if transfer.id != nil {
-		return *transfer.id
-	}
-
-	// otherwise calculate the id
-	idBytes := blake2b.Sum256(transfer.Bytes())
-	transferId := id.New(idBytes[:])
-
-	// cache result for later calls
-	transfer.id = &transferId
-
-	return transferId
-}
-
-func (transfer *Transfer) SignaturesValid() bool {
-	signaturesValid := true
-	transfer.inputs.ForEachAddress(func(address address.Address) bool {
-		if signature, exists := transfer.signatures.Get(address); !exists || !signature.IsValid(transfer.EssenceBytes()) {
-			signaturesValid = false
-
-			return false
-		}
-
-		return true
-	})
-
-	return signaturesValid
-}
-
-func (transfer *Transfer) EssenceBytes() []byte {
-	// acquire read lock on essenceBytes
-	transfer.essenceBytesMutex.RLock()
-
-	// return essenceBytes if the object has been marshaled already
-	if transfer.essenceBytes != nil {
-		defer transfer.essenceBytesMutex.RUnlock()
-
-		return transfer.essenceBytes
-	}
-
-	// switch to write lock
-	transfer.essenceBytesMutex.RUnlock()
-	transfer.essenceBytesMutex.Lock()
-	defer transfer.essenceBytesMutex.Unlock()
-
-	// return essenceBytes if the object has been marshaled in the mean time
-	if essenceBytes := transfer.essenceBytes; essenceBytes != nil {
-		return essenceBytes
-	}
-
-	// create marshal helper
-	marshalUtil := marshalutil.New()
-
-	// marshal inputs
-	marshalUtil.WriteBytes(transfer.inputs.Bytes())
-
-	// marshal outputs
-	marshalUtil.WriteBytes(transfer.outputs.Bytes())
-
-	// store marshaled result
-	transfer.essenceBytes = marshalUtil.Bytes()
-
-	return transfer.essenceBytes
-}
-
-func (transfer *Transfer) SignatureBytes() []byte {
-	transfer.signatureBytesMutex.RLock()
-	if transfer.signatureBytes != nil {
-		defer transfer.signatureBytesMutex.RUnlock()
-
-		return transfer.signatureBytes
-	}
-
-	transfer.signatureBytesMutex.RUnlock()
-	transfer.signatureBytesMutex.Lock()
-	defer transfer.signatureBytesMutex.Unlock()
-
-	if transfer.signatureBytes != nil {
-		return transfer.signatureBytes
-	}
-
-	// generate signatures
-	transfer.signatureBytes = transfer.signatures.Bytes()
-
-	return transfer.signatureBytes
-}
-
-func (transfer *Transfer) Bytes() []byte {
-	// acquire read lock on bytes
-	transfer.bytesMutex.RLock()
-
-	// return bytes if the object has been marshaled already
-	if transfer.bytes != nil {
-		defer transfer.bytesMutex.RUnlock()
-
-		return transfer.bytes
-	}
-
-	// switch to write lock
-	transfer.bytesMutex.RUnlock()
-	transfer.bytesMutex.Lock()
-	defer transfer.bytesMutex.Unlock()
-
-	// return bytes if the object has been marshaled in the mean time
-	if bytes := transfer.bytes; bytes != nil {
-		return bytes
-	}
-
-	// create marshal helper
-	marshalUtil := marshalutil.New()
-
-	// marshal essence bytes
-	marshalUtil.WriteBytes(transfer.EssenceBytes())
-
-	// marshal signature bytes
-	marshalUtil.WriteBytes(transfer.SignatureBytes())
-
-	// store marshaled result
-	transfer.bytes = marshalUtil.Bytes()
-
-	return transfer.bytes
-}
-
-func (transfer *Transfer) Sign(signature signatures.SignatureScheme) *Transfer {
-	transfer.signatures.Add(signature.Address(), signature.Sign(transfer.EssenceBytes()))
-
-	return transfer
-}
-
-func (transfer *Transfer) String() string {
-	id := transfer.GetId()
-
-	return stringify.Struct("Transfer"+fmt.Sprintf("(%p)", transfer),
-		stringify.StructField("id", base58.Encode(id[:])),
-		stringify.StructField("inputs", transfer.inputs),
-		stringify.StructField("outputs", transfer.outputs),
-	)
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region IMPLEMENT StorableObject interface ///////////////////////////////////////////////////////////////////////////
-
-// define contract (ensure that the struct fulfills the given interface)
-var _ objectstorage.StorableObject = &Transfer{}
-
-func (transfer *Transfer) GetStorageKey() []byte {
-	id := transfer.GetId()
-
-	return id[:]
-}
-
-func (transfer *Transfer) Update(other objectstorage.StorableObject) {
-	panic("update forbidden")
-}
-
-// MarshalBinary returns a bytes representation of the transfer by implementing the encoding.BinaryMarshaler interface.
-func (transfer *Transfer) MarshalBinary() ([]byte, error) {
-	return transfer.Bytes(), nil
-}
-
-func (transfer *Transfer) UnmarshalBinary(bytes []byte) (err error) {
-	_, err, _ = FromBytes(bytes, transfer)
-
-	return
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetransfer/tangle/attachment.go b/packages/binary/valuetransfer/tangle/attachment.go
new file mode 100644
index 00000000..033e3f2b
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/attachment.go
@@ -0,0 +1,23 @@
+package tangle
+
+import (
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+// Attachment stores the information which transaction was attached by which transaction. We need this to perform
+// reverse lookups for which Payloads contain which Transactions.
+type Attachment struct {
+	transactionId transaction.Id
+	payloadId     payload.Id
+}
+
+// TransactionId returns the transaction id of this Attachment.
+func (attachment *Attachment) TransactionId() transaction.Id {
+	return attachment.transactionId
+}
+
+// PayloadId returns the payload id of this Attachment.
+func (attachment *Attachment) PayloadId() payload.Id {
+	return attachment.payloadId
+}
diff --git a/packages/binary/valuetransfer/tangle/events.go b/packages/binary/valuetransfer/tangle/events.go
index 653ae828..15e31ddd 100644
--- a/packages/binary/valuetransfer/tangle/events.go
+++ b/packages/binary/valuetransfer/tangle/events.go
@@ -4,8 +4,7 @@ import (
 	"github.com/iotaledger/hive.go/events"
 
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadmetadata"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
 
 type Events struct {
@@ -16,6 +15,7 @@ type Events struct {
 	PayloadMissing         *events.Event
 	PayloadUnsolidifiable  *events.Event
 	TransactionRemoved     *events.Event
+	OutputMissing          *events.Event
 }
 
 func newEvents() *Events {
@@ -26,16 +26,21 @@ func newEvents() *Events {
 		PayloadMissing:         events.NewEvent(payloadIdEvent),
 		PayloadUnsolidifiable:  events.NewEvent(payloadIdEvent),
 		TransactionRemoved:     events.NewEvent(payloadIdEvent),
+		OutputMissing:          events.NewEvent(outputIdEvent),
 	}
 }
 
 func payloadIdEvent(handler interface{}, params ...interface{}) {
-	handler.(func(payloadid.Id))(params[0].(payloadid.Id))
+	handler.(func(payload.Id))(params[0].(payload.Id))
 }
 
 func cachedPayloadEvent(handler interface{}, params ...interface{}) {
-	handler.(func(*payload.CachedObject, *payloadmetadata.CachedObject))(
-		params[0].(*payload.CachedObject).Retain(),
-		params[1].(*payloadmetadata.CachedObject).Retain(),
+	handler.(func(*payload.CachedPayload, *CachedPayloadMetadata))(
+		params[0].(*payload.CachedPayload).Retain(),
+		params[1].(*CachedPayloadMetadata).Retain(),
 	)
 }
+
+func outputIdEvent(handler interface{}, params ...interface{}) {
+	handler.(func(transaction.OutputId))(params[0].(transaction.OutputId))
+}
diff --git a/packages/binary/valuetransfer/tangle/missingoutput.go b/packages/binary/valuetransfer/tangle/missingoutput.go
new file mode 100644
index 00000000..039bc60c
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/missingoutput.go
@@ -0,0 +1,109 @@
+package tangle
+
+import (
+	"time"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+// MissingPayload represents an Output that was referenced by a Transaction, but that is missing in our object storage.
+type MissingOutput struct {
+	objectstorage.StorableObjectFlags
+
+	outputId     transaction.OutputId
+	missingSince time.Time
+}
+
+// NewMissingOutput creates a new MissingOutput object, that .
+func NewMissingOutput(outputId transaction.OutputId) *MissingOutput {
+	return &MissingOutput{
+		outputId:     outputId,
+		missingSince: time.Now(),
+	}
+}
+
+// MissingOutput unmarshals a MissingOutput from a sequence of bytes - it either creates a new object or fills the
+// optionally provided one with the parsed information.
+func MissingOutputFromBytes(bytes []byte, optionalTargetObject ...*MissingOutput) (result *MissingOutput, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &MissingOutput{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to MissingOutputFromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.outputId, err = transaction.ParseOutputId(marshalUtil); err != nil {
+		return
+	}
+	if result.missingSince, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// MissingOutputFromStorage gets called when we restore a MissingOutput from the storage. The content will be
+// unmarshaled by an external caller using the binary.MarshalBinary interface.
+func MissingOutputFromStorage(keyBytes []byte) objectstorage.StorableObject {
+	outputId, err, _ := transaction.OutputIdFromBytes(keyBytes)
+	if err != nil {
+		panic(err)
+	}
+
+	return &MissingOutput{
+		outputId: outputId,
+	}
+}
+
+// Id returns the id of the Output that is missing.
+func (missingOutput *MissingOutput) Id() transaction.OutputId {
+	return missingOutput.outputId
+}
+
+// MissingSince returns the Time since the transaction was first reported as being missing.
+func (missingOutput *MissingOutput) MissingSince() time.Time {
+	return missingOutput.missingSince
+}
+
+// Bytes marshals the MissingOutput into a sequence of bytes.
+func (missingOutput *MissingOutput) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteBytes(missingOutput.outputId.Bytes())
+	marshalUtil.WriteTime(missingOutput.missingSince)
+
+	return marshalUtil.Bytes()
+}
+
+// GetStorageKey returns the key that is used to store the object in the object storage.
+func (missingOutput *MissingOutput) GetStorageKey() []byte {
+	return missingOutput.outputId.Bytes()
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+func (missingOutput *MissingOutput) Update(other objectstorage.StorableObject) {
+	panic("implement me")
+}
+
+// MarshalBinary returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler
+// interface.
+func (missingOutput *MissingOutput) MarshalBinary() (data []byte, err error) {
+	return missingOutput.Bytes(), nil
+}
+
+// UnmarshalBinary restores the values of a MissingOutput from a sequence of bytes using the  encoding.BinaryUnmarshaler
+// interface.
+func (missingOutput *MissingOutput) UnmarshalBinary(data []byte) (err error) {
+	_, err, _ = MissingOutputFromBytes(data, missingOutput)
+
+	return
+}
diff --git a/packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go b/packages/binary/valuetransfer/tangle/missingpayload.go
similarity index 72%
rename from packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go
rename to packages/binary/valuetransfer/tangle/missingpayload.go
index 38afa809..c4eb6532 100644
--- a/packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go
+++ b/packages/binary/valuetransfer/tangle/missingpayload.go
@@ -1,4 +1,4 @@
-package missingpayload
+package tangle
 
 import (
 	"time"
@@ -6,7 +6,7 @@ import (
 	"github.com/iotaledger/hive.go/objectstorage"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
 )
 
 // MissingPayload represents a payload that was referenced through branch or trunk but that is missing in our object
@@ -14,21 +14,21 @@ import (
 type MissingPayload struct {
 	objectstorage.StorableObjectFlags
 
-	payloadId    payloadid.Id
+	payloadId    payload.Id
 	missingSince time.Time
 }
 
-// New creates an entry for a missing value transfer payload.
-func New(payloadId payloadid.Id) *MissingPayload {
+// NewMissingPayload creates an entry for a missing value transfer payload.
+func NewMissingPayload(payloadId payload.Id) *MissingPayload {
 	return &MissingPayload{
 		payloadId:    payloadId,
 		missingSince: time.Now(),
 	}
 }
 
-// FromBytes unmarshals an entry for a missing value transfer payload from a sequence of bytes.
+// MissingPayloadFromBytes unmarshals an entry for a missing value transfer payload from a sequence of bytes.
 // It either creates a new entry or fills the optionally provided one with the parsed information.
-func FromBytes(bytes []byte, optionalTargetObject ...*MissingPayload) (result *MissingPayload, err error, consumedBytes int) {
+func MissingPayloadFromBytes(bytes []byte, optionalTargetObject ...*MissingPayload) (result *MissingPayload, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -36,12 +36,12 @@ func FromBytes(bytes []byte, optionalTargetObject ...*MissingPayload) (result *M
 	case 1:
 		result = optionalTargetObject[0]
 	default:
-		panic("too many arguments in call to FromBytes")
+		panic("too many arguments in call to MissingPayloadFromBytes")
 	}
 
 	// parse the bytes
 	marshalUtil := marshalutil.New(bytes)
-	if result.payloadId, err = payloadid.Parse(marshalUtil); err != nil {
+	if result.payloadId, err = payload.ParseId(marshalUtil); err != nil {
 		return
 	}
 	if result.missingSince, err = marshalUtil.ReadTime(); err != nil {
@@ -52,14 +52,14 @@ func FromBytes(bytes []byte, optionalTargetObject ...*MissingPayload) (result *M
 	return
 }
 
-// FromStorage gets called when we restore an entry for a missing value transfer payload from the storage. The bytes and
+// MissingPayloadFromStorage gets called when we restore an entry for a missing value transfer payload from the storage. The bytes and
 // the content will be unmarshaled by an external caller using the binary.MarshalBinary interface.
-func FromStorage([]byte) objectstorage.StorableObject {
+func MissingPayloadFromStorage([]byte) objectstorage.StorableObject {
 	return &MissingPayload{}
 }
 
 // GetId returns the payload id, that is missing.
-func (missingPayload *MissingPayload) GetId() payloadid.Id {
+func (missingPayload *MissingPayload) GetId() payload.Id {
 	return missingPayload.payloadId
 }
 
@@ -97,7 +97,7 @@ func (missingPayload *MissingPayload) MarshalBinary() (data []byte, err error) {
 
 // UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface.
 func (missingPayload *MissingPayload) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = FromBytes(data, missingPayload)
+	_, err, _ = MissingPayloadFromBytes(data, missingPayload)
 
 	return
 }
diff --git a/packages/binary/valuetransfer/tangle/payloadapprover.go b/packages/binary/valuetransfer/tangle/payloadapprover.go
new file mode 100644
index 00000000..e8637925
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadapprover.go
@@ -0,0 +1,129 @@
+package tangle
+
+import (
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
+)
+
+// PayloadApprover is a database entity, that allows us to keep track of the "tangle structure" by encoding which
+// payload approves which other payload. It allows us to traverse the tangle in the opposite direction of the referenced
+// trunk and branch payloads.
+type PayloadApprover struct {
+	objectstorage.StorableObjectFlags
+
+	storageKey          []byte
+	referencedPayloadId payload.Id
+	approvingPayloadId  payload.Id
+}
+
+// NewPayloadApprover creates an approver object that encodes a single relation between an approved and an approving payload.
+func NewPayloadApprover(referencedPayload payload.Id, approvingPayload payload.Id) *PayloadApprover {
+	marshalUtil := marshalutil.New(payload.IdLength + payload.IdLength)
+	marshalUtil.WriteBytes(referencedPayload.Bytes())
+	marshalUtil.WriteBytes(approvingPayload.Bytes())
+
+	return &PayloadApprover{
+		referencedPayloadId: referencedPayload,
+		approvingPayloadId:  approvingPayload,
+		storageKey:          marshalUtil.Bytes(),
+	}
+}
+
+// PayloadApproverFromStorage get's called when we restore transaction metadata from the storage.
+// In contrast to other database models, it unmarshals the information from the key and does not use the UnmarshalBinary
+// method.
+func PayloadApproverFromStorage(idBytes []byte) objectstorage.StorableObject {
+	marshalUtil := marshalutil.New(idBytes)
+
+	referencedPayloadId, err := payload.ParseId(marshalUtil)
+	if err != nil {
+		panic(err)
+	}
+	approvingPayloadId, err := payload.ParseId(marshalUtil)
+	if err != nil {
+		panic(err)
+	}
+
+	result := &PayloadApprover{
+		referencedPayloadId: referencedPayloadId,
+		approvingPayloadId:  approvingPayloadId,
+		storageKey:          marshalUtil.Bytes(true),
+	}
+
+	return result
+}
+
+// GetApprovingPayloadId returns the id of the approving payload.
+func (payloadApprover *PayloadApprover) GetApprovingPayloadId() payload.Id {
+	return payloadApprover.approvingPayloadId
+}
+
+// GetStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (payloadApprover *PayloadApprover) GetStorageKey() []byte {
+	return payloadApprover.storageKey
+}
+
+// MarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
+// since all of the information about an approver are stored in the "key".
+func (payloadApprover *PayloadApprover) MarshalBinary() (data []byte, err error) {
+	return
+}
+
+// UnmarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
+// since all of the information about an approver are stored in the "key".
+func (payloadApprover *PayloadApprover) UnmarshalBinary(data []byte) error {
+	return nil
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+// It is required to match StorableObject interface.
+func (payloadApprover *PayloadApprover) Update(other objectstorage.StorableObject) {
+	panic("implement me")
+}
+
+// CachedPayloadApprover is a wrapper for the object storage, that takes care of type casting the managed objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of CachedApprovers, without having to manually type cast over and over again.
+type CachedPayloadApprover struct {
+	objectstorage.CachedObject
+}
+
+// Retain wraps the underlying method to return a new "wrapped object".
+func (cachedPayloadApprover *CachedPayloadApprover) Retain() *CachedPayloadApprover {
+	return &CachedPayloadApprover{cachedPayloadApprover.CachedObject.Retain()}
+}
+
+// Consume wraps the underlying method to return the correctly typed objects in the callback.
+func (cachedPayloadApprover *CachedPayloadApprover) Consume(consumer func(payload *PayloadApprover)) bool {
+	return cachedPayloadApprover.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*PayloadApprover))
+	})
+}
+
+// Unwrap provides a way to "Get" a type casted version of the underlying object.
+func (cachedPayloadApprover *CachedPayloadApprover) Unwrap() *PayloadApprover {
+	if untypedTransaction := cachedPayloadApprover.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*PayloadApprover); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
+
+type CachedApprovers []*CachedPayloadApprover
+
+func (cachedApprovers CachedApprovers) Consume(consumer func(approver *PayloadApprover)) (consumed bool) {
+	for _, cachedApprover := range cachedApprovers {
+		consumed = cachedApprover.Consume(consumer) || consumed
+	}
+
+	return
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go b/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go
deleted file mode 100644
index b90c7042..00000000
--- a/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package payloadapprover
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-// CachedObject is a wrapper for the object storage, that takes care of type casting the managed objects.
-// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
-// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
-// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
-// specialized types of CachedObjects, without having to manually type cast over and over again.
-type CachedObject struct {
-	objectstorage.CachedObject
-}
-
-// Retain wraps the underlying method to return a new "wrapped object".
-func (cachedApprover *CachedObject) Retain() *CachedObject {
-	return &CachedObject{cachedApprover.CachedObject.Retain()}
-}
-
-// Consume wraps the underlying method to return the correctly typed objects in the callback.
-func (cachedApprover *CachedObject) Consume(consumer func(payload *PayloadApprover)) bool {
-	return cachedApprover.CachedObject.Consume(func(object objectstorage.StorableObject) {
-		consumer(object.(*PayloadApprover))
-	})
-}
-
-// Unwrap provides a way to "Get" a type casted version of the underlying object.
-func (cachedApprover *CachedObject) Unwrap() *PayloadApprover {
-	if untypedTransaction := cachedApprover.Get(); untypedTransaction == nil {
-		return nil
-	} else {
-		if typeCastedTransaction := untypedTransaction.(*PayloadApprover); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
-			return nil
-		} else {
-			return typeCastedTransaction
-		}
-	}
-}
-
-type CachedObjects []*CachedObject
-
-func (cachedApprovers CachedObjects) Consume(consumer func(approver *PayloadApprover)) (consumed bool) {
-	for _, cachedApprover := range cachedApprovers {
-		consumed = cachedApprover.Consume(consumer) || consumed
-	}
-
-	return
-}
diff --git a/packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go b/packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go
deleted file mode 100644
index 7c92ae68..00000000
--- a/packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package payloadapprover
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-)
-
-// PayloadApprover is a database entity, that allows us to keep track of the "tangle structure" by encoding which
-// payload approves which other payload. It allows us to traverse the tangle in the opposite direction of the referenced
-// trunk and branch payloads.
-type PayloadApprover struct {
-	objectstorage.StorableObjectFlags
-
-	storageKey          []byte
-	referencedPayloadId payloadid.Id
-	approvingPayloadId  payloadid.Id
-}
-
-// New creates an approver object that encodes a single relation between an approved and an approving payload.
-func New(referencedPayload payloadid.Id, approvingPayload payloadid.Id) *PayloadApprover {
-	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length)
-	marshalUtil.WriteBytes(referencedPayload.Bytes())
-	marshalUtil.WriteBytes(approvingPayload.Bytes())
-
-	return &PayloadApprover{
-		referencedPayloadId: referencedPayload,
-		approvingPayloadId:  approvingPayload,
-		storageKey:          marshalUtil.Bytes(),
-	}
-}
-
-// FromStorage get's called when we restore transaction metadata from the storage.
-// In contrast to other database models, it unmarshals the information from the key and does not use the UnmarshalBinary
-// method.
-func FromStorage(idBytes []byte) objectstorage.StorableObject {
-	marshalUtil := marshalutil.New(idBytes)
-
-	referencedPayloadId, err := payloadid.Parse(marshalUtil)
-	if err != nil {
-		panic(err)
-	}
-	approvingPayloadId, err := payloadid.Parse(marshalUtil)
-	if err != nil {
-		panic(err)
-	}
-
-	result := &PayloadApprover{
-		referencedPayloadId: referencedPayloadId,
-		approvingPayloadId:  approvingPayloadId,
-		storageKey:          marshalUtil.Bytes(true),
-	}
-
-	return result
-}
-
-// GetApprovingPayloadId returns the id of the approving payload.
-func (approver *PayloadApprover) GetApprovingPayloadId() payloadid.Id {
-	return approver.approvingPayloadId
-}
-
-// GetStorageKey returns the key that is used to store the object in the database.
-// It is required to match StorableObject interface.
-func (approver *PayloadApprover) GetStorageKey() []byte {
-	return approver.storageKey
-}
-
-// MarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
-// since all of the information about an approver are stored in the "key".
-func (approver *PayloadApprover) MarshalBinary() (data []byte, err error) {
-	return
-}
-
-// UnmarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
-// since all of the information about an approver are stored in the "key".
-func (approver *PayloadApprover) UnmarshalBinary(data []byte) error {
-	return nil
-}
-
-// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
-// It is required to match StorableObject interface.
-func (approver *PayloadApprover) Update(other objectstorage.StorableObject) {
-	panic("implement me")
-}
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go b/packages/binary/valuetransfer/tangle/payloadmetadata.go
similarity index 59%
rename from packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go
rename to packages/binary/valuetransfer/tangle/payloadmetadata.go
index b6eb53fb..d693c86e 100644
--- a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata.go
@@ -1,4 +1,4 @@
-package payloadmetadata
+package tangle
 
 import (
 	"sync"
@@ -8,7 +8,7 @@ import (
 	"github.com/iotaledger/hive.go/stringify"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
 )
 
 // PayloadMetadata is a container for the metadata of a value transfer payload.
@@ -16,7 +16,7 @@ import (
 type PayloadMetadata struct {
 	objectstorage.StorableObjectFlags
 
-	payloadId          payloadid.Id
+	payloadId          payload.Id
 	solid              bool
 	solidificationTime time.Time
 
@@ -24,16 +24,16 @@ type PayloadMetadata struct {
 	solidificationTimeMutex sync.RWMutex
 }
 
-// New creates an empty container for the metadata of a value transfer payload.
-func New(payloadId payloadid.Id) *PayloadMetadata {
+// NewPayloadMetadata creates an empty container for the metadata of a value transfer payload.
+func NewPayloadMetadata(payloadId payload.Id) *PayloadMetadata {
 	return &PayloadMetadata{
 		payloadId: payloadId,
 	}
 }
 
-// FromBytes unmarshals a container with the metadata of a value transfer payload from a sequence of bytes.
+// PayloadMetadataFromBytes unmarshals a container with the metadata of a value transfer payload from a sequence of bytes.
 // It either creates a new container or fills the optionally provided container with the parsed information.
-func FromBytes(bytes []byte, optionalTargetObject ...*PayloadMetadata) (result *PayloadMetadata, err error, consumedBytes int) {
+func PayloadMetadataFromBytes(bytes []byte, optionalTargetObject ...*PayloadMetadata) (result *PayloadMetadata, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -41,12 +41,12 @@ func FromBytes(bytes []byte, optionalTargetObject ...*PayloadMetadata) (result *
 	case 1:
 		result = optionalTargetObject[0]
 	default:
-		panic("too many arguments in call to FromBytes")
+		panic("too many arguments in call to PayloadMetadataFromBytes")
 	}
 
 	// parse the bytes
 	marshalUtil := marshalutil.New(bytes)
-	if result.payloadId, err = payloadid.Parse(marshalUtil); err != nil {
+	if result.payloadId, err = payload.ParseId(marshalUtil); err != nil {
 		return
 	}
 	if result.solidificationTime, err = marshalUtil.ReadTime(); err != nil {
@@ -60,22 +60,22 @@ func FromBytes(bytes []byte, optionalTargetObject ...*PayloadMetadata) (result *
 	return
 }
 
-// FromStorage gets called when we restore transaction metadata from the storage. The bytes and the content will be
+// PayloadMetadataFromStorage gets called when we restore transaction metadata from the storage. The bytes and the content will be
 // unmarshaled by an external caller using the binary.MarshalBinary interface.
-func FromStorage(id []byte) objectstorage.StorableObject {
+func PayloadMetadataFromStorage(id []byte) objectstorage.StorableObject {
 	result := &PayloadMetadata{}
 
 	var err error
-	if result.payloadId, err = payloadid.Parse(marshalutil.New(id)); err != nil {
+	if result.payloadId, err = payload.ParseId(marshalutil.New(id)); err != nil {
 		panic(err)
 	}
 
 	return result
 }
 
-// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
-func Parse(marshalUtil *marshalutil.MarshalUtil) (*PayloadMetadata, error) {
-	if payloadMetadata, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
+// ParsePayloadMetadata is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func ParsePayloadMetadata(marshalUtil *marshalutil.MarshalUtil) (*PayloadMetadata, error) {
+	if payloadMetadata, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return PayloadMetadataFromBytes(data) }); err != nil {
 		return nil, err
 	} else {
 		return payloadMetadata.(*PayloadMetadata), nil
@@ -83,11 +83,11 @@ func Parse(marshalUtil *marshalutil.MarshalUtil) (*PayloadMetadata, error) {
 }
 
 // GetPayloadId return the id of the payload that this metadata is associated to.
-func (payloadMetadata *PayloadMetadata) GetPayloadId() payloadid.Id {
+func (payloadMetadata *PayloadMetadata) GetPayloadId() payload.Id {
 	return payloadMetadata.payloadId
 }
 
-// IsSolid returns true of the payload has been marked as solid.
+// IsSolid returns true if the payload has been marked as solid.
 func (payloadMetadata *PayloadMetadata) IsSolid() (result bool) {
 	payloadMetadata.solidMutex.RLock()
 	result = payloadMetadata.solid
@@ -172,7 +172,41 @@ func (payloadMetadata *PayloadMetadata) MarshalBinary() ([]byte, error) {
 
 // UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface.
 func (payloadMetadata *PayloadMetadata) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = FromBytes(data, payloadMetadata)
+	_, err, _ = PayloadMetadataFromBytes(data, payloadMetadata)
 
 	return
 }
+
+// CachedPayloadMetadata is a wrapper for the object storage, that takes care of type casting the managed objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of CachedObjects, without having to manually type cast over and over again.
+type CachedPayloadMetadata struct {
+	objectstorage.CachedObject
+}
+
+// Retain wraps the underlying method to return a new "wrapped object".
+func (cachedPayloadMetadata *CachedPayloadMetadata) Retain() *CachedPayloadMetadata {
+	return &CachedPayloadMetadata{cachedPayloadMetadata.CachedObject.Retain()}
+}
+
+// Consume wraps the underlying method to return the correctly typed objects in the callback.
+func (cachedPayloadMetadata *CachedPayloadMetadata) Consume(consumer func(payload *PayloadMetadata)) bool {
+	return cachedPayloadMetadata.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*PayloadMetadata))
+	})
+}
+
+// Unwrap provides a way to "Get" a type casted version of the underlying object.
+func (cachedPayloadMetadata *CachedPayloadMetadata) Unwrap() *PayloadMetadata {
+	if untypedTransaction := cachedPayloadMetadata.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*PayloadMetadata); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go b/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go
deleted file mode 100644
index bde06d7c..00000000
--- a/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package payloadmetadata
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-// CachedObject is a wrapper for the object storage, that takes care of type casting the managed objects.
-// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
-// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
-// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
-// specialized types of CachedObjects, without having to manually type cast over and over again.
-type CachedObject struct {
-	objectstorage.CachedObject
-}
-
-// Retain wraps the underlying method to return a new "wrapped object".
-func (cachedPayload *CachedObject) Retain() *CachedObject {
-	return &CachedObject{cachedPayload.CachedObject.Retain()}
-}
-
-// Consume wraps the underlying method to return the correctly typed objects in the callback.
-func (cachedPayload *CachedObject) Consume(consumer func(payload *PayloadMetadata)) bool {
-	return cachedPayload.CachedObject.Consume(func(object objectstorage.StorableObject) {
-		consumer(object.(*PayloadMetadata))
-	})
-}
-
-// Unwrap provides a way to "Get" a type casted version of the underlying object.
-func (cachedPayload *CachedObject) Unwrap() *PayloadMetadata {
-	if untypedTransaction := cachedPayload.Get(); untypedTransaction == nil {
-		return nil
-	} else {
-		if typeCastedTransaction := untypedTransaction.(*PayloadMetadata); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
-			return nil
-		} else {
-			return typeCastedTransaction
-		}
-	}
-}
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go b/packages/binary/valuetransfer/tangle/payloadmetadata_test.go
similarity index 80%
rename from packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go
rename to packages/binary/valuetransfer/tangle/payloadmetadata_test.go
index fd85c6f5..cd50258b 100644
--- a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata_test.go
@@ -1,4 +1,4 @@
-package payloadmetadata
+package tangle
 
 import (
 	"testing"
@@ -6,13 +6,13 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
 )
 
 func TestMarshalUnmarshal(t *testing.T) {
-	originalMetadata := New(id.Genesis)
+	originalMetadata := NewPayloadMetadata(payload.GenesisId)
 
-	clonedMetadata, err, _ := FromBytes(originalMetadata.Bytes())
+	clonedMetadata, err, _ := PayloadMetadataFromBytes(originalMetadata.Bytes())
 	if err != nil {
 		panic(err)
 	}
@@ -23,7 +23,7 @@ func TestMarshalUnmarshal(t *testing.T) {
 
 	originalMetadata.SetSolid(true)
 
-	clonedMetadata, err, _ = FromBytes(originalMetadata.Bytes())
+	clonedMetadata, err, _ = PayloadMetadataFromBytes(originalMetadata.Bytes())
 	if err != nil {
 		panic(err)
 	}
@@ -34,7 +34,7 @@ func TestMarshalUnmarshal(t *testing.T) {
 }
 
 func TestPayloadMetadata_SetSolid(t *testing.T) {
-	originalMetadata := New(id.Genesis)
+	originalMetadata := NewPayloadMetadata(payload.GenesisId)
 
 	assert.Equal(t, false, originalMetadata.IsSolid())
 	assert.Equal(t, time.Time{}, originalMetadata.GetSoldificationTime())
diff --git a/packages/binary/valuetransfer/tangle/tangle.go b/packages/binary/valuetransfer/tangle/tangle.go
index 39c97d71..5c61b2b7 100644
--- a/packages/binary/valuetransfer/tangle/tangle.go
+++ b/packages/binary/valuetransfer/tangle/tangle.go
@@ -2,6 +2,7 @@ package tangle
 
 import (
 	"container/list"
+	"fmt"
 	"time"
 
 	"github.com/dgraph-io/badger/v2"
@@ -9,11 +10,8 @@ import (
 	"github.com/iotaledger/hive.go/objectstorage"
 
 	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
-	valuepayload "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/missingpayload"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadapprover"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadmetadata"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
 
 // Tangle represents the value tangle that consists out of value payloads.
@@ -26,6 +24,9 @@ type Tangle struct {
 	approverStorage        *objectstorage.ObjectStorage
 	missingPayloadStorage  *objectstorage.ObjectStorage
 
+	transactionOutputMetadataStorage *objectstorage.ObjectStorage
+	missingOutputStorage             *objectstorage.ObjectStorage
+
 	Events Events
 
 	storePayloadWorkerPool async.WorkerPool
@@ -37,10 +38,13 @@ func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
 	result = &Tangle{
 		storageId: storageId,
 
-		payloadStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayload...), valuepayload.FromStorage, objectstorage.CacheTime(time.Second)),
-		payloadMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayloadMetadata...), payloadmetadata.FromStorage, objectstorage.CacheTime(time.Second)),
-		approverStorage:        objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferApprover...), payloadapprover.FromStorage, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(payloadid.Length, payloadid.Length), objectstorage.KeysOnly(true)),
-		missingPayloadStorage:  objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), missingpayload.FromStorage, objectstorage.CacheTime(time.Second)),
+		payloadStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayload...), payload.FromStorage, objectstorage.CacheTime(time.Second)),
+		payloadMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayloadMetadata...), PayloadMetadataFromStorage, objectstorage.CacheTime(time.Second)),
+		approverStorage:        objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferApprover...), PayloadApproverFromStorage, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(payload.IdLength, payload.IdLength), objectstorage.KeysOnly(true)),
+		missingPayloadStorage:  objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingPayloadFromStorage, objectstorage.CacheTime(time.Second)),
+
+		transactionOutputMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), transaction.OutputFromStorage, objectstorage.CacheTime(time.Second)),
+		missingOutputStorage:             objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingOutputFromStorage, objectstorage.CacheTime(time.Second)),
 
 		Events: *newEvents(),
 	}
@@ -49,25 +53,25 @@ func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
 }
 
 // AttachPayload adds a new payload to the value tangle.
-func (tangle *Tangle) AttachPayload(payload *valuepayload.Payload) {
+func (tangle *Tangle) AttachPayload(payload *payload.Payload) {
 	tangle.storePayloadWorkerPool.Submit(func() { tangle.storePayloadWorker(payload) })
 }
 
 // GetPayload retrieves a payload from the object storage.
-func (tangle *Tangle) GetPayload(payloadId payloadid.Id) *valuepayload.CachedObject {
-	return &valuepayload.CachedObject{CachedObject: tangle.payloadStorage.Load(payloadId.Bytes())}
+func (tangle *Tangle) GetPayload(payloadId payload.Id) *payload.CachedPayload {
+	return &payload.CachedPayload{CachedObject: tangle.payloadStorage.Load(payloadId.Bytes())}
 }
 
 // GetPayloadMetadata retrieves the metadata of a value payload from the object storage.
-func (tangle *Tangle) GetPayloadMetadata(payloadId payloadid.Id) *payloadmetadata.CachedObject {
-	return &payloadmetadata.CachedObject{CachedObject: tangle.payloadMetadataStorage.Load(payloadId.Bytes())}
+func (tangle *Tangle) GetPayloadMetadata(payloadId payload.Id) *CachedPayloadMetadata {
+	return &CachedPayloadMetadata{CachedObject: tangle.payloadMetadataStorage.Load(payloadId.Bytes())}
 }
 
 // GetApprovers retrieves the approvers of a payload from the object storage.
-func (tangle *Tangle) GetApprovers(transactionId payloadid.Id) payloadapprover.CachedObjects {
-	approvers := make(payloadapprover.CachedObjects, 0)
+func (tangle *Tangle) GetApprovers(transactionId payload.Id) CachedApprovers {
+	approvers := make(CachedApprovers, 0)
 	tangle.approverStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool {
-		approvers = append(approvers, &payloadapprover.CachedObject{CachedObject: cachedObject})
+		approvers = append(approvers, &CachedPayloadApprover{CachedObject: cachedObject})
 
 		return true
 	}, transactionId[:])
@@ -106,26 +110,26 @@ func (tangle *Tangle) Prune() error {
 }
 
 // storePayloadWorker is the worker function that stores the payload and calls the corresponding storage events.
-func (tangle *Tangle) storePayloadWorker(payload *valuepayload.Payload) {
+func (tangle *Tangle) storePayloadWorker(payloadToStore *payload.Payload) {
 	// store payload
-	var cachedPayload *valuepayload.CachedObject
-	if _tmp, transactionIsNew := tangle.payloadStorage.StoreIfAbsent(payload); !transactionIsNew {
+	var cachedPayload *payload.CachedPayload
+	if _tmp, transactionIsNew := tangle.payloadStorage.StoreIfAbsent(payloadToStore); !transactionIsNew {
 		return
 	} else {
-		cachedPayload = &valuepayload.CachedObject{CachedObject: _tmp}
+		cachedPayload = &payload.CachedPayload{CachedObject: _tmp}
 	}
 
 	// store payload metadata
-	payloadId := payload.GetId()
-	cachedMetadata := &payloadmetadata.CachedObject{CachedObject: tangle.payloadMetadataStorage.Store(payloadmetadata.New(payloadId))}
+	payloadId := payloadToStore.Id()
+	cachedMetadata := &CachedPayloadMetadata{CachedObject: tangle.payloadMetadataStorage.Store(NewPayloadMetadata(payloadId))}
 
 	// store trunk approver
-	trunkId := payload.GetTrunkId()
-	tangle.approverStorage.Store(payloadapprover.New(trunkId, payloadId)).Release()
+	trunkId := payloadToStore.TrunkId()
+	tangle.approverStorage.Store(NewPayloadApprover(trunkId, payloadId)).Release()
 
 	// store branch approver
-	if branchId := payload.GetBranchId(); branchId != trunkId {
-		tangle.approverStorage.Store(payloadapprover.New(branchId, trunkId)).Release()
+	if branchId := payloadToStore.BranchId(); branchId != trunkId {
+		tangle.approverStorage.Store(NewPayloadApprover(branchId, trunkId)).Release()
 	}
 
 	// trigger events
@@ -134,21 +138,39 @@ func (tangle *Tangle) storePayloadWorker(payload *valuepayload.Payload) {
 	}
 	tangle.Events.PayloadAttached.Trigger(cachedPayload, cachedMetadata)
 
+	// retrieve or store TransactionMetadata
+	newTransaction := false
+	transactionId := cachedPayload.Unwrap().Transaction().Id()
+	cachedTransactionMetadata := &CachedTransactionMetadata{CachedObject: tangle.payloadMetadataStorage.ComputeIfAbsent(transactionId.Bytes(), func(key []byte) objectstorage.StorableObject {
+		newTransaction = true
+
+		result := NewTransactionMetadata(transactionId)
+		result.Persist()
+		result.SetModified()
+
+		return result
+	})}
+
+	// if the transaction is new, store the Consumers.
+	if newTransaction {
+		fmt.Println("git aWADD")
+	}
+
 	// check solidity
 	tangle.solidifierWorkerPool.Submit(func() {
-		tangle.solidifyTransactionWorker(cachedPayload, cachedMetadata)
+		tangle.solidifyTransactionWorker(cachedPayload, cachedMetadata, cachedTransactionMetadata)
 	})
 }
 
 // solidifyTransactionWorker is the worker function that solidifies the payloads (recursively from past to present).
-func (tangle *Tangle) solidifyTransactionWorker(cachedPayload *valuepayload.CachedObject, cachedMetadata *payloadmetadata.CachedObject) {
-	popElementsFromStack := func(stack *list.List) (*valuepayload.CachedObject, *payloadmetadata.CachedObject) {
+func (tangle *Tangle) solidifyTransactionWorker(cachedPayload *payload.CachedPayload, cachedMetadata *CachedPayloadMetadata, cachedTransactionMetadata *CachedTransactionMetadata) {
+	popElementsFromStack := func(stack *list.List) (*payload.CachedPayload, *CachedPayloadMetadata) {
 		currentSolidificationEntry := stack.Front()
 		currentCachedPayload := currentSolidificationEntry.Value.([2]interface{})[0]
 		currentCachedMetadata := currentSolidificationEntry.Value.([2]interface{})[1]
 		stack.Remove(currentSolidificationEntry)
 
-		return currentCachedPayload.(*valuepayload.CachedObject), currentCachedMetadata.(*payloadmetadata.CachedObject)
+		return currentCachedPayload.(*payload.CachedPayload), currentCachedMetadata.(*CachedPayloadMetadata)
 	}
 
 	// initialize the stack
@@ -164,33 +186,74 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedPayload *valuepayload.Cach
 		if currentPayload == nil || currentMetadata == nil {
 			currentCachedPayload.Release()
 			currentCachedMetadata.Release()
+			cachedTransactionMetadata.Release()
 
 			continue
 		}
 
 		// if current transaction is solid and was not marked as solid before: mark as solid and propagate
-		if tangle.isPayloadSolid(currentPayload, currentMetadata) && currentMetadata.SetSolid(true) {
-			tangle.Events.PayloadSolid.Trigger(currentCachedPayload, currentCachedMetadata)
+		if tangle.isPayloadSolid(currentPayload, currentMetadata) && tangle.isTransactionSolid(currentPayload.Transaction(), cachedTransactionMetadata.Unwrap()) {
+			if currentMetadata.SetSolid(true) {
+				tangle.Events.PayloadSolid.Trigger(currentCachedPayload, currentCachedMetadata)
 
-			tangle.GetApprovers(currentPayload.GetId()).Consume(func(approver *payloadapprover.PayloadApprover) {
-				approverTransactionId := approver.GetApprovingPayloadId()
+				tangle.GetApprovers(currentPayload.Id()).Consume(func(approver *PayloadApprover) {
+					approverTransactionId := approver.GetApprovingPayloadId()
 
-				solidificationStack.PushBack([2]interface{}{
-					tangle.GetPayload(approverTransactionId),
-					tangle.GetPayloadMetadata(approverTransactionId),
+					solidificationStack.PushBack([2]interface{}{
+						tangle.GetPayload(approverTransactionId),
+						tangle.GetPayloadMetadata(approverTransactionId),
+					})
 				})
-			})
+			}
 		}
 
 		// release cached results
 		currentCachedPayload.Release()
 		currentCachedMetadata.Release()
+		cachedTransactionMetadata.Release()
+	}
+}
+
+func (tangle *Tangle) isTransactionSolid(transaction *transaction.Transaction, metadata *TransactionMetadata) bool {
+	if transaction == nil || transaction.IsDeleted() {
+		return false
+	}
+
+	if metadata == nil || metadata.IsDeleted() {
+		return false
 	}
+
+	if metadata.Solid() {
+		return true
+	}
+
+	// iterate through all transfers and check if they are solid
+	return transaction.Inputs().ForEach(tangle.isOutputMarkedAsSolid)
+}
+
+func (tangle *Tangle) GetTransferOutputMetadata(transactionOutputId transaction.OutputId) *CachedTransactionOutputMetadata {
+	return &CachedTransactionOutputMetadata{CachedObject: tangle.transactionOutputMetadataStorage.Load(transactionOutputId.Bytes())}
+}
+
+func (tangle *Tangle) isOutputMarkedAsSolid(transferOutputId transaction.OutputId) (result bool) {
+	objectConsumed := tangle.GetTransferOutputMetadata(transferOutputId).Consume(func(transferOutputMetadata *TransactionOutputMetadata) {
+		result = transferOutputMetadata.Solid()
+	})
+
+	if !objectConsumed {
+		if cachedMissingOutput, missingOutputStored := tangle.missingOutputStorage.StoreIfAbsent(NewMissingOutput(transferOutputId)); missingOutputStored {
+			cachedMissingOutput.Consume(func(object objectstorage.StorableObject) {
+				tangle.Events.OutputMissing.Trigger(object.(*MissingOutput).Id())
+			})
+		}
+	}
+
+	return
 }
 
 // isPayloadSolid returns true if the given payload is solid. A payload is considered to be solid solid, if it is either
 // already marked as solid or if its referenced payloads are marked as solid.
-func (tangle *Tangle) isPayloadSolid(payload *valuepayload.Payload, metadata *payloadmetadata.PayloadMetadata) bool {
+func (tangle *Tangle) isPayloadSolid(payload *payload.Payload, metadata *PayloadMetadata) bool {
 	if payload == nil || payload.IsDeleted() {
 		return false
 	}
@@ -203,13 +266,13 @@ func (tangle *Tangle) isPayloadSolid(payload *valuepayload.Payload, metadata *pa
 		return true
 	}
 
-	return tangle.isPayloadMarkedAsSolid(payload.GetTrunkId()) && tangle.isPayloadMarkedAsSolid(payload.GetBranchId())
+	return tangle.isPayloadMarkedAsSolid(payload.TrunkId()) && tangle.isPayloadMarkedAsSolid(payload.BranchId())
 }
 
 // isPayloadMarkedAsSolid returns true if the payload was marked as solid already (by setting the corresponding flags
 // in its metadata.
-func (tangle *Tangle) isPayloadMarkedAsSolid(payloadId payloadid.Id) bool {
-	if payloadId == payloadid.Genesis {
+func (tangle *Tangle) isPayloadMarkedAsSolid(payloadId payload.Id) bool {
+	if payloadId == payload.GenesisId {
 		return true
 	}
 
@@ -218,9 +281,9 @@ func (tangle *Tangle) isPayloadMarkedAsSolid(payloadId payloadid.Id) bool {
 		transactionMetadataCached.Release()
 
 		// if transaction is missing and was not reported as missing, yet
-		if cachedMissingPayload, missingPayloadStored := tangle.missingPayloadStorage.StoreIfAbsent(missingpayload.New(payloadId)); missingPayloadStored {
+		if cachedMissingPayload, missingPayloadStored := tangle.missingPayloadStorage.StoreIfAbsent(NewMissingPayload(payloadId)); missingPayloadStored {
 			cachedMissingPayload.Consume(func(object objectstorage.StorableObject) {
-				tangle.Events.PayloadMissing.Trigger(object.(*missingpayload.MissingPayload).GetId())
+				tangle.Events.PayloadMissing.Trigger(object.(*MissingPayload).GetId())
 			})
 		}
 
diff --git a/packages/binary/valuetransfer/tangle/tangle_test.go b/packages/binary/valuetransfer/tangle/tangle_test.go
index ccbbea0a..cd83c779 100644
--- a/packages/binary/valuetransfer/tangle/tangle_test.go
+++ b/packages/binary/valuetransfer/tangle/tangle_test.go
@@ -11,16 +11,9 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance/color"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/inputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/outputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadmetadata"
-	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transferoutput/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 	"github.com/iotaledger/goshimmer/packages/database"
 	"github.com/iotaledger/goshimmer/plugins/config"
 )
@@ -39,7 +32,7 @@ func TestTangle_AttachPayload(t *testing.T) {
 		return
 	}
 
-	tangle.Events.PayloadSolid.Attach(events.NewClosure(func(payload *payload.CachedObject, metadata *payloadmetadata.CachedObject) {
+	tangle.Events.PayloadSolid.Attach(events.NewClosure(func(payload *payload.CachedPayload, metadata *CachedPayloadMetadata) {
 		fmt.Println(payload.Unwrap())
 
 		payload.Release()
@@ -49,15 +42,18 @@ func TestTangle_AttachPayload(t *testing.T) {
 	addressKeyPair1 := ed25119.GenerateKeyPair()
 	addressKeyPair2 := ed25119.GenerateKeyPair()
 
-	tangle.AttachPayload(payload.New(id.Genesis, id.Genesis, transfer.New(
-		inputs.New(
-			transferoutputid.New(address.FromED25519PubKey(addressKeyPair1.PublicKey), transferid.New([]byte("transfer1"))),
-			transferoutputid.New(address.FromED25519PubKey(addressKeyPair2.PublicKey), transferid.New([]byte("transfer2"))),
+	transferId1, _ := transaction.IdFromBase58("8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh")
+	transferId2, _ := transaction.IdFromBase58("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM")
+
+	tangle.AttachPayload(payload.New(payload.GenesisId, payload.GenesisId, transaction.New(
+		transaction.NewInputs(
+			transaction.NewOutputId(address.FromED25519PubKey(addressKeyPair1.PublicKey), transferId1),
+			transaction.NewOutputId(address.FromED25519PubKey(addressKeyPair2.PublicKey), transferId2),
 		),
 
-		outputs.New(map[address.Address][]*coloredbalance.ColoredBalance{
+		transaction.NewOutputs(map[address.Address][]*balance.Balance{
 			address.Random(): {
-				coloredbalance.New(color.IOTA, 1337),
+				balance.New(balance.COLOR_IOTA, 1337),
 			},
 		}),
 	)))
diff --git a/packages/binary/valuetransfer/tangle/transactionmetadata.go b/packages/binary/valuetransfer/tangle/transactionmetadata.go
new file mode 100644
index 00000000..898c57a7
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/transactionmetadata.go
@@ -0,0 +1,213 @@
+package tangle
+
+import (
+	"sync"
+	"time"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+// TransactionMetadata contains the information of a Transaction, that are based on our local perception of things (i.e. if it is
+// solid, or when we it became solid).
+type TransactionMetadata struct {
+	objectstorage.StorableObjectFlags
+
+	id                 transaction.Id
+	solid              bool
+	solidificationTime time.Time
+
+	solidMutex              sync.RWMutex
+	solidificationTimeMutex sync.RWMutex
+}
+
+// NewTransactionMetadata is the constructor for the TransactionMetadata type.
+func NewTransactionMetadata(id transaction.Id) *TransactionMetadata {
+	return &TransactionMetadata{
+		id: id,
+	}
+}
+
+// TransactionMetadataFromBytes unmarshals a TransactionMetadata object from a sequence of bytes.
+// It either creates a new object or fills the optionally provided object with the parsed information.
+func TransactionMetadataFromBytes(bytes []byte, optionalTargetObject ...*TransactionMetadata) (result *TransactionMetadata, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &TransactionMetadata{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to TransactionMetadataFromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.id, err = transaction.ParseId(marshalUtil); err != nil {
+		return
+	}
+	if result.solidificationTime, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	if result.solid, err = marshalUtil.ReadBool(); err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// TransactionMetadataFromStorage is the factory method for TransactionMetadata objects stored in the objectstorage. The bytes and the content
+// will be filled by the objectstorage, by subsequently calling MarshalBinary.
+func TransactionMetadataFromStorage(storageKey []byte) objectstorage.StorableObject {
+	result := &TransactionMetadata{}
+
+	var err error
+	if result.id, err = transaction.ParseId(marshalutil.New(storageKey)); err != nil {
+		panic(err)
+	}
+
+	return result
+}
+
+// Parse is a wrapper for simplified unmarshaling of TransactionMetadata objects from a byte stream using the marshalUtil package.
+func ParseTransactionMetadata(marshalUtil *marshalutil.MarshalUtil) (*TransactionMetadata, error) {
+	if metadata, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return TransactionMetadataFromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return metadata.(*TransactionMetadata), nil
+	}
+}
+
+// Id return the id of the Transaction that this TransactionMetadata is associated to.
+func (transactionMetadata *TransactionMetadata) Id() transaction.Id {
+	return transactionMetadata.id
+}
+
+// Solid returns true if the Transaction has been marked as solid.
+func (transactionMetadata *TransactionMetadata) Solid() (result bool) {
+	transactionMetadata.solidMutex.RLock()
+	result = transactionMetadata.solid
+	transactionMetadata.solidMutex.RUnlock()
+
+	return
+}
+
+// SetSolid marks a Transaction as either solid or not solid.
+// It returns true if the solid flag was changes and automatically updates the solidificationTime as well.
+func (transactionMetadata *TransactionMetadata) SetSolid(solid bool) (modified bool) {
+	transactionMetadata.solidMutex.RLock()
+	if transactionMetadata.solid != solid {
+		transactionMetadata.solidMutex.RUnlock()
+
+		transactionMetadata.solidMutex.Lock()
+		if transactionMetadata.solid != solid {
+			transactionMetadata.solid = solid
+			if solid {
+				transactionMetadata.solidificationTimeMutex.Lock()
+				transactionMetadata.solidificationTime = time.Now()
+				transactionMetadata.solidificationTimeMutex.Unlock()
+			}
+
+			transactionMetadata.SetModified()
+
+			modified = true
+		}
+		transactionMetadata.solidMutex.Unlock()
+
+	} else {
+		transactionMetadata.solidMutex.RUnlock()
+	}
+
+	return
+}
+
+// SoldificationTime returns the time when the Transaction was marked to be solid.
+func (transactionMetadata *TransactionMetadata) SoldificationTime() time.Time {
+	transactionMetadata.solidificationTimeMutex.RLock()
+	defer transactionMetadata.solidificationTimeMutex.RUnlock()
+
+	return transactionMetadata.solidificationTime
+}
+
+// Bytes marshals the TransactionMetadata object into a sequence of bytes.
+func (transactionMetadata *TransactionMetadata) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteBytes(transactionMetadata.id.Bytes())
+	marshalUtil.WriteTime(transactionMetadata.solidificationTime)
+	marshalUtil.WriteBool(transactionMetadata.solid)
+
+	return marshalUtil.Bytes()
+}
+
+// String creates a human readable version of the metadata (for debug purposes).
+func (transactionMetadata *TransactionMetadata) String() string {
+	return stringify.Struct("transaction.TransactionMetadata",
+		stringify.StructField("payloadId", transactionMetadata.Id()),
+		stringify.StructField("solid", transactionMetadata.Solid()),
+		stringify.StructField("solidificationTime", transactionMetadata.SoldificationTime()),
+	)
+}
+
+// GetStorageKey returns the key that is used to identify the TransactionMetadata in the objectstorage.
+func (transactionMetadata *TransactionMetadata) GetStorageKey() []byte {
+	return transactionMetadata.id.Bytes()
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+func (transactionMetadata *TransactionMetadata) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// MarshalBinary marshals the TransactionMetadata object into a sequence of bytes and matches the encoding.BinaryMarshaler
+// interface.
+func (transactionMetadata *TransactionMetadata) MarshalBinary() ([]byte, error) {
+	return transactionMetadata.Bytes(), nil
+}
+
+// UnmarshalBinary restores the values of a TransactionMetadata object from a sequence of bytes and matches the
+// encoding.BinaryUnmarshaler interface.
+func (transactionMetadata *TransactionMetadata) UnmarshalBinary(data []byte) (err error) {
+	_, err, _ = TransactionMetadataFromBytes(data, transactionMetadata)
+
+	return
+}
+
+// CachedTransactionMetadata is a wrapper for the object storage, that takes care of type casting the TransactionMetadata objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of TransactionMetadata, without having to manually type cast over and over again.
+type CachedTransactionMetadata struct {
+	objectstorage.CachedObject
+}
+
+// Retain overrides the underlying method to return a new CachedTransactionMetadata instead of a generic CachedObject.
+func (cachedTransactionMetadata *CachedTransactionMetadata) Retain() *CachedTransactionMetadata {
+	return &CachedTransactionMetadata{cachedTransactionMetadata.CachedObject.Retain()}
+}
+
+// Consume  overrides the underlying method to use a CachedTransactionMetadata object instead of a generic CachedObject in the
+// consumer).
+func (cachedTransactionMetadata *CachedTransactionMetadata) Consume(consumer func(metadata *TransactionMetadata)) bool {
+	return cachedTransactionMetadata.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*TransactionMetadata))
+	})
+}
+
+// Unwrap provides a way to retrieve a type casted version of the underlying object.
+func (cachedTransactionMetadata *CachedTransactionMetadata) Unwrap() *TransactionMetadata {
+	if untypedTransaction := cachedTransactionMetadata.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*TransactionMetadata); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go b/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go
new file mode 100644
index 00000000..9e0c15fe
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go
@@ -0,0 +1,213 @@
+package tangle
+
+import (
+	"sync"
+	"time"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+// TransactionOutputMetadata contains the information of a transaction output, that are based on our local perception of things (i.e. if it
+// is solid, or when we it became solid).
+type TransactionOutputMetadata struct {
+	objectstorage.StorableObjectFlags
+
+	id                 transaction.OutputId
+	solid              bool
+	solidificationTime time.Time
+
+	solidMutex              sync.RWMutex
+	solidificationTimeMutex sync.RWMutex
+}
+
+// NewOutputMetadata is the constructor for the TransactionOutputMetadata type.
+func NewTransactionOutputMetadata(outputId transaction.OutputId) *TransactionOutputMetadata {
+	return &TransactionOutputMetadata{
+		id: outputId,
+	}
+}
+
+// TransactionOutputMetadataFromBytes unmarshals a TransactionOutputMetadata object from a sequence of bytes.
+// It either creates a new object or fills the optionally provided object with the parsed information.
+func TransactionOutputMetadataFromBytes(bytes []byte, optionalTargetObject ...*TransactionOutputMetadata) (result *TransactionOutputMetadata, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &TransactionOutputMetadata{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to TransactionOutputMetadataFromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.id, err = transaction.ParseOutputId(marshalUtil); err != nil {
+		return
+	}
+	if result.solidificationTime, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	if result.solid, err = marshalUtil.ReadBool(); err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// TransactionOutputMetadataFromStorage is the factory method for TransactionOutputMetadata objects stored in the objectstorage. The bytes and the content
+// will be filled by the objectstorage, by subsequently calling MarshalBinary.
+func TransactionOutputMetadataFromStorage(storageKey []byte) objectstorage.StorableObject {
+	result := &TransactionOutputMetadata{}
+
+	var err error
+	if result.id, err = transaction.ParseOutputId(marshalutil.New(storageKey)); err != nil {
+		panic(err)
+	}
+
+	return result
+}
+
+// Parse is a wrapper for simplified unmarshaling of TransactionOutputMetadata objects from a byte stream using the marshalUtil package.
+func ParseTransactionOutputMetadata(marshalUtil *marshalutil.MarshalUtil) (*TransactionOutputMetadata, error) {
+	if outputMetadata, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return TransactionOutputMetadataFromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return outputMetadata.(*TransactionOutputMetadata), nil
+	}
+}
+
+// OutputId returns the id of the Output that this TransactionOutputMetadata is associated to.
+func (transactionOutputMetadata *TransactionOutputMetadata) Id() transaction.OutputId {
+	return transactionOutputMetadata.id
+}
+
+// Solid returns true if the Output has been marked as solid.
+func (transactionOutputMetadata *TransactionOutputMetadata) Solid() (result bool) {
+	transactionOutputMetadata.solidMutex.RLock()
+	result = transactionOutputMetadata.solid
+	transactionOutputMetadata.solidMutex.RUnlock()
+
+	return
+}
+
+// SetSolid marks a Output as either solid or not solid.
+// It returns true if the solid flag was changes and automatically updates the solidificationTime as well.
+func (transactionOutputMetadata *TransactionOutputMetadata) SetSolid(solid bool) (modified bool) {
+	transactionOutputMetadata.solidMutex.RLock()
+	if transactionOutputMetadata.solid != solid {
+		transactionOutputMetadata.solidMutex.RUnlock()
+
+		transactionOutputMetadata.solidMutex.Lock()
+		if transactionOutputMetadata.solid != solid {
+			transactionOutputMetadata.solid = solid
+			if solid {
+				transactionOutputMetadata.solidificationTimeMutex.Lock()
+				transactionOutputMetadata.solidificationTime = time.Now()
+				transactionOutputMetadata.solidificationTimeMutex.Unlock()
+			}
+
+			transactionOutputMetadata.SetModified()
+
+			modified = true
+		}
+		transactionOutputMetadata.solidMutex.Unlock()
+
+	} else {
+		transactionOutputMetadata.solidMutex.RUnlock()
+	}
+
+	return
+}
+
+// SoldificationTime returns the time when the Output was marked to be solid.
+func (transactionOutputMetadata *TransactionOutputMetadata) SoldificationTime() time.Time {
+	transactionOutputMetadata.solidificationTimeMutex.RLock()
+	defer transactionOutputMetadata.solidificationTimeMutex.RUnlock()
+
+	return transactionOutputMetadata.solidificationTime
+}
+
+// Bytes marshals the TransactionOutputMetadata object into a sequence of bytes.
+func (transactionOutputMetadata *TransactionOutputMetadata) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteBytes(transactionOutputMetadata.id.Bytes())
+	marshalUtil.WriteTime(transactionOutputMetadata.solidificationTime)
+	marshalUtil.WriteBool(transactionOutputMetadata.solid)
+
+	return marshalUtil.Bytes()
+}
+
+// String creates a human readable version of the metadata (for debug purposes).
+func (transactionOutputMetadata *TransactionOutputMetadata) String() string {
+	return stringify.Struct("transaction.TransactionOutputMetadata",
+		stringify.StructField("payloadId", transactionOutputMetadata.Id()),
+		stringify.StructField("solid", transactionOutputMetadata.Solid()),
+		stringify.StructField("solidificationTime", transactionOutputMetadata.SoldificationTime()),
+	)
+}
+
+// GetStorageKey returns the key that is used to identify the TransactionOutputMetadata in the objectstorage.
+func (transactionOutputMetadata *TransactionOutputMetadata) GetStorageKey() []byte {
+	return transactionOutputMetadata.id.Bytes()
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+func (transactionOutputMetadata *TransactionOutputMetadata) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// MarshalBinary marshals the TransactionOutputMetadata object into a sequence of bytes and matches the encoding.BinaryMarshaler
+// interface.
+func (transactionOutputMetadata *TransactionOutputMetadata) MarshalBinary() ([]byte, error) {
+	return transactionOutputMetadata.Bytes(), nil
+}
+
+// UnmarshalBinary restores the values of a TransactionOutputMetadata object from a sequence of bytes and matches the
+// encoding.BinaryUnmarshaler interface.
+func (transactionOutputMetadata *TransactionOutputMetadata) UnmarshalBinary(data []byte) (err error) {
+	_, err, _ = TransactionOutputMetadataFromBytes(data, transactionOutputMetadata)
+
+	return
+}
+
+// CachedTransactionOutputMetadata is a wrapper for the object storage, that takes care of type casting the TransactionOutputMetadata objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of TransactionOutputMetadata, without having to manually type cast over and over again.
+type CachedTransactionOutputMetadata struct {
+	objectstorage.CachedObject
+}
+
+// Retain overrides the underlying method to return a new CachedTransactionOutputMetadata instead of a generic CachedObject.
+func (cachedOutputMetadata *CachedTransactionOutputMetadata) Retain() *CachedTransactionOutputMetadata {
+	return &CachedTransactionOutputMetadata{cachedOutputMetadata.CachedObject.Retain()}
+}
+
+// Consume  overrides the underlying method to use a CachedTransactionOutputMetadata object instead of a generic CachedObject in the
+// consumer).
+func (cachedOutputMetadata *CachedTransactionOutputMetadata) Consume(consumer func(outputMetadata *TransactionOutputMetadata)) bool {
+	return cachedOutputMetadata.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*TransactionOutputMetadata))
+	})
+}
+
+// Unwrap provides a way to retrieve a type casted version of the underlying object.
+func (cachedOutputMetadata *CachedTransactionOutputMetadata) Unwrap() *TransactionOutputMetadata {
+	if untypedTransaction := cachedOutputMetadata.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*TransactionOutputMetadata); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/test/payload_test.go b/packages/binary/valuetransfer/test/payload_test.go
deleted file mode 100644
index dcee4c24..00000000
--- a/packages/binary/valuetransfer/test/payload_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package test
-
-import (
-	"fmt"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance/color"
-	valuepayload "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/inputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/outputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/signatures"
-	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transferoutput/id"
-)
-
-func ExamplePayload() {
-	// 1. create value transfer (user provides this)
-	valueTransfer := transfer.New(
-		// inputs
-		inputs.New(
-			transferoutputid.New(address.Random(), transferid.New([]byte("transfer1"))),
-			transferoutputid.New(address.Random(), transferid.New([]byte("transfer2"))),
-		),
-
-		// outputs
-		outputs.New(map[address.Address][]*coloredbalance.ColoredBalance{
-			address.Random(): {
-				coloredbalance.New(color.IOTA, 1337),
-			},
-		}),
-	)
-
-	// 2. create value payload (the ontology creates this and wraps the user provided transfer accordingly)
-	valuePayload := valuepayload.New(
-		// trunk in "value transfer ontology" (filled by ontology tipSelector)
-		payloadid.Genesis,
-
-		// branch in "value transfer ontology"  (filled by ontology tipSelector)
-		payloadid.Genesis,
-
-		// value transfer
-		valueTransfer,
-	)
-
-	// 3. build actual transaction (the base layer creates this and wraps the ontology provided payload)
-	tx := transaction.New(
-		// trunk in "network tangle" ontology (filled by tipSelector)
-		transaction.EmptyId,
-
-		// branch in "network tangle" ontology (filled by tipSelector)
-		transaction.EmptyId,
-
-		// issuer of the transaction (signs automatically)
-		ed25119.GenerateKeyPair(),
-
-		// the time when the transaction was created
-		time.Now(),
-
-		// the ever increasing sequence number of this transaction
-		0,
-
-		// payload
-		valuePayload,
-	)
-
-	fmt.Println(tx)
-}
-
-func TestPayload(t *testing.T) {
-	addressKeyPair1 := ed25119.GenerateKeyPair()
-	addressKeyPair2 := ed25119.GenerateKeyPair()
-
-	originalPayload := valuepayload.New(
-		payloadid.Genesis,
-		payloadid.Genesis,
-		transfer.New(
-			inputs.New(
-				transferoutputid.New(address.FromED25519PubKey(addressKeyPair1.PublicKey), transferid.New([]byte("transfer1"))),
-				transferoutputid.New(address.FromED25519PubKey(addressKeyPair2.PublicKey), transferid.New([]byte("transfer2"))),
-			),
-
-			outputs.New(map[address.Address][]*coloredbalance.ColoredBalance{
-				address.Random(): {
-					coloredbalance.New(color.IOTA, 1337),
-				},
-			}),
-		).Sign(
-			signatures.ED25519(addressKeyPair1),
-		),
-	)
-
-	assert.Equal(t, false, originalPayload.GetTransfer().SignaturesValid())
-
-	originalPayload.GetTransfer().Sign(
-		signatures.ED25519(addressKeyPair2),
-	)
-
-	assert.Equal(t, true, originalPayload.GetTransfer().SignaturesValid())
-
-	clonedPayload1, err, _ := valuepayload.FromBytes(originalPayload.Bytes())
-	if err != nil {
-		panic(err)
-	}
-
-	assert.Equal(t, originalPayload.GetId(), clonedPayload1.GetId())
-	assert.Equal(t, true, clonedPayload1.GetTransfer().SignaturesValid())
-
-	clonedPayload2, err, _ := valuepayload.FromBytes(clonedPayload1.Bytes())
-	if err != nil {
-		panic(err)
-	}
-
-	assert.Equal(t, originalPayload.GetId(), clonedPayload2.GetId())
-	assert.Equal(t, true, clonedPayload2.GetTransfer().SignaturesValid())
-}
diff --git a/packages/binary/valuetransfer/transaction/id.go b/packages/binary/valuetransfer/transaction/id.go
new file mode 100644
index 00000000..396b5d28
--- /dev/null
+++ b/packages/binary/valuetransfer/transaction/id.go
@@ -0,0 +1,86 @@
+package transaction
+
+import (
+	"crypto/rand"
+	"fmt"
+
+	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+// Id is the data type that represents the identifier for a Transaction.
+type Id [IdLength]byte
+
+// IdFromBase58 creates an id from a base58 encoded string.
+func IdFromBase58(base58String string) (id Id, err error) {
+	// decode string
+	bytes, err := base58.Decode(base58String)
+	if err != nil {
+		return
+	}
+
+	// sanitize input
+	if len(bytes) != IdLength {
+		err = fmt.Errorf("base58 encoded string does not match the length of a transaction id")
+
+		return
+	}
+
+	// copy bytes to result
+	copy(id[:], bytes)
+
+	return
+}
+
+// IdFromBytes unmarshals an Id from a sequence of bytes.
+func IdFromBytes(bytes []byte) (result Id, err error, consumedBytes int) {
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if idBytes, idErr := marshalUtil.ReadBytes(IdLength); idErr != nil {
+		err = idErr
+
+		return
+	} else {
+		copy(result[:], idBytes)
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// Parse is a wrapper for simplified unmarshaling of Ids from a byte stream using the marshalUtil package.
+func ParseId(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
+	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) }); err != nil {
+		return Id{}, err
+	} else {
+		return id.(Id), nil
+	}
+}
+
+// Random creates a random address, which can for example be used in unit tests.
+func RandomId() (id Id) {
+	// generate a random sequence of bytes
+	idBytes := make([]byte, IdLength)
+	if _, err := rand.Read(idBytes); err != nil {
+		panic(err)
+	}
+
+	// copy the generated bytes into the result
+	copy(id[:], idBytes)
+
+	return
+}
+
+// Bytes marshals the Id into a sequence of bytes.
+func (id Id) Bytes() []byte {
+	return id[:]
+}
+
+// String creates a human readable version of the Id (for debug purposes).
+func (id Id) String() string {
+	return base58.Encode(id[:])
+}
+
+// IdLength contains the amount of bytes that a marshaled version of the Id contains.
+const IdLength = 32
diff --git a/packages/binary/valuetransfer/payload/transfer/inputs/inputs.go b/packages/binary/valuetransfer/transaction/inputs.go
similarity index 50%
rename from packages/binary/valuetransfer/payload/transfer/inputs/inputs.go
rename to packages/binary/valuetransfer/transaction/inputs.go
index 1d107dc7..49f07e96 100644
--- a/packages/binary/valuetransfer/payload/transfer/inputs/inputs.go
+++ b/packages/binary/valuetransfer/transaction/inputs.go
@@ -1,29 +1,26 @@
-package inputs
+package transaction
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transferoutput/id"
 )
 
 type Inputs struct {
 	*orderedmap.OrderedMap
 }
 
-func New(transferOutputIds ...transferoutputid.Id) (inputs *Inputs) {
+func NewInputs(outputIds ...OutputId) (inputs *Inputs) {
 	inputs = &Inputs{orderedmap.New()}
-	for _, transferOutputId := range transferOutputIds {
-		inputs.Add(transferOutputId)
+	for _, outputId := range outputIds {
+		inputs.Add(outputId)
 	}
 
 	return
 }
 
-func FromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
-	inputs = New()
+func InputsFromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
+	inputs = NewInputs()
 
 	marshalUtil := marshalutil.New(bytes)
 	inputCount, err := marshalUtil.ReadUint32()
@@ -39,13 +36,18 @@ func FromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
 			return
 		}
 
-		transferIdBytes, readErr := marshalUtil.ReadBytes(transferid.Length)
+		idBytes, readErr := marshalUtil.ReadBytes(IdLength)
 		if readErr != nil {
 			err = readErr
 
 			return
 		}
-		transferId := id.New(transferIdBytes)
+		id, idErr, _ := IdFromBytes(idBytes)
+		if idErr != nil {
+			err = idErr
+
+			return
+		}
 
 		addressMap, addressExists := inputs.Get(readAddress)
 		if !addressExists {
@@ -53,7 +55,7 @@ func FromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
 
 			inputs.Set(readAddress, addressMap)
 		}
-		addressMap.(*orderedmap.OrderedMap).Set(transferId, transferoutputid.New(readAddress, transferId))
+		addressMap.(*orderedmap.OrderedMap).Set(id, NewOutputId(readAddress, id))
 	}
 
 	consumedBytes = marshalUtil.ReadOffset()
@@ -61,9 +63,9 @@ func FromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
 	return
 }
 
-func (inputs *Inputs) Add(input transferoutputid.Id) *Inputs {
-	inputAddress := input.GetAddress()
-	transferId := input.GetTransferId()
+func (inputs *Inputs) Add(input OutputId) *Inputs {
+	inputAddress := input.Address()
+	transactionId := input.TransactionId()
 
 	addressMap, addressExists := inputs.Get(inputAddress)
 	if !addressExists {
@@ -72,7 +74,7 @@ func (inputs *Inputs) Add(input transferoutputid.Id) *Inputs {
 		inputs.Set(inputAddress, addressMap)
 	}
 
-	addressMap.(*orderedmap.OrderedMap).Set(transferId, input)
+	addressMap.(*orderedmap.OrderedMap).Set(transactionId, input)
 
 	return inputs
 }
@@ -82,10 +84,12 @@ func (inputs *Inputs) Bytes() (bytes []byte) {
 
 	marshalUtil.WriteSeek(4)
 	var inputCounter uint32
-	inputs.ForEach(func(transferOutputId transferoutputid.Id) {
-		marshalUtil.WriteBytes(transferOutputId.ToBytes())
+	inputs.ForEach(func(outputId OutputId) bool {
+		marshalUtil.WriteBytes(outputId.Bytes())
 
 		inputCounter++
+
+		return true
 	})
 	marshalUtil.WriteSeek(0)
 	marshalUtil.WriteUint32(inputCounter)
@@ -93,24 +97,34 @@ func (inputs *Inputs) Bytes() (bytes []byte) {
 	return marshalUtil.Bytes()
 }
 
-func (inputs *Inputs) ForEach(consumer func(transferOutputId transferoutputid.Id)) {
-	inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
-		value.(*orderedmap.OrderedMap).ForEach(func(key, value interface{}) bool {
-			consumer(value.(transferoutputid.Id))
-
-			return true
+func (inputs *Inputs) ForEach(consumer func(outputId OutputId) bool) bool {
+	return inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
+		return value.(*orderedmap.OrderedMap).ForEach(func(key, value interface{}) bool {
+			return consumer(value.(OutputId))
 		})
-
-		return true
 	})
 }
 
-func (inputs *Inputs) ForEachAddress(consumer func(currentAddress address.Address) bool) {
-	inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
+func (inputs *Inputs) ForEachAddress(consumer func(currentAddress address.Address) bool) bool {
+	return inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
 		return consumer(key.(address.Address))
 	})
 }
 
+func (inputs *Inputs) ForEachTransaction(consumer func(transactionId Id) bool) bool {
+	seenTransactions := make(map[Id]bool)
+
+	return inputs.ForEach(func(outputId OutputId) bool {
+		if currentTransactionId := outputId.TransactionId(); !seenTransactions[currentTransactionId] {
+			seenTransactions[currentTransactionId] = true
+
+			return consumer(currentTransactionId)
+		}
+
+		return true
+	})
+}
+
 func (inputs *Inputs) String() string {
 	if inputs == nil {
 		return "<nil>"
@@ -119,10 +133,12 @@ func (inputs *Inputs) String() string {
 	result := "[\n"
 
 	empty := true
-	inputs.ForEach(func(transferOutputId transferoutputid.Id) {
+	inputs.ForEach(func(outputId OutputId) bool {
 		empty = false
 
-		result += "    " + transferOutputId.String() + ",\n"
+		result += "    " + outputId.String() + ",\n"
+
+		return true
 	})
 
 	if empty {
diff --git a/packages/binary/valuetransfer/transaction/output.go b/packages/binary/valuetransfer/transaction/output.go
new file mode 100644
index 00000000..949434dc
--- /dev/null
+++ b/packages/binary/valuetransfer/transaction/output.go
@@ -0,0 +1,126 @@
+package transaction
+
+import (
+	"fmt"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
+)
+
+// Output represents the output of a Transaction and contains the balances and the identifiers for this output.
+type Output struct {
+	address       address.Address
+	transactionId Id
+	balances      []*balance.Balance
+
+	objectstorage.StorableObjectFlags
+	storageKey []byte
+}
+
+// NewOutput creates an Output that contains the balances and identifiers of a Transaction.
+func NewOutput(address address.Address, transactionId Id, balances []*balance.Balance) *Output {
+	return &Output{
+		address:       address,
+		transactionId: transactionId,
+		balances:      balances,
+
+		storageKey: marshalutil.New().WriteBytes(address.Bytes()).WriteBytes(transactionId.Bytes()).Bytes(),
+	}
+}
+
+// OutputFromStorage get's called when we restore a Output from the storage.
+// In contrast to other database models, it unmarshals some information from the key so we simply store the key before
+// it gets handed over to UnmarshalBinary (by the ObjectStorage).
+func OutputFromStorage(keyBytes []byte) objectstorage.StorableObject {
+	return &Output{
+		storageKey: marshalutil.New(keyBytes).Bytes(true),
+	}
+}
+
+// Address returns the address that this output belongs to.
+func (output *Output) Address() address.Address {
+	return output.address
+}
+
+// TransactionId returns the id of the Transaction, that created this output.
+func (output *Output) TransactionId() Id {
+	return output.transactionId
+}
+
+// Balances returns the colored balances (color + balance) that this output contains.
+func (output *Output) Balances() []*balance.Balance {
+	return output.balances
+}
+
+// MarshalBinary marshals the balances into a sequence of bytes - the address and transaction id are stored inside the key
+// and are ignored here.
+func (output *Output) MarshalBinary() (data []byte, err error) {
+	// determine amount of balances in the output
+	balanceCount := len(output.balances)
+
+	// initialize helper
+	marshalUtil := marshalutil.New(4 + balanceCount*balance.Length)
+
+	// marshal the amount of balances
+	marshalUtil.WriteUint32(uint32(balanceCount))
+
+	// marshal balances
+	for _, balance := range output.balances {
+		marshalUtil.WriteBytes(balance.Bytes())
+	}
+
+	return
+}
+
+// UnmarshalBinary restores a Output from a serialized version in the ObjectStorage with parts of the object
+// being stored in its key rather than the content of the database to reduce storage requirements.
+func (output *Output) UnmarshalBinary(data []byte) (err error) {
+	// check if the storageKey has been set
+	if output.storageKey == nil {
+		return fmt.Errorf("missing storageKey when trying to unmarshal Output (it contains part of the information)")
+	}
+
+	// parse information from storageKey
+	storageKeyUnmarshaler := marshalutil.New(output.storageKey)
+	output.address, err = address.Parse(storageKeyUnmarshaler)
+	if err != nil {
+		return
+	}
+	output.transactionId, err = ParseId(storageKeyUnmarshaler)
+	if err != nil {
+		return
+	}
+
+	// parse information from content bytes
+	contentUnmarshaler := marshalutil.New(data)
+	balanceCount, err := contentUnmarshaler.ReadUint32()
+	if err != nil {
+		return
+	}
+	output.balances = make([]*balance.Balance, balanceCount)
+	for i := uint32(0); i < balanceCount; i++ {
+		output.balances[i], err = balance.Parse(contentUnmarshaler)
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// Update is disabled and panics if it ever gets called - it is required to match StorableObject interface.
+func (output *Output) Update(other objectstorage.StorableObject) {
+	panic("this object should never be updated")
+}
+
+// GetStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (output *Output) GetStorageKey() []byte {
+	return output.storageKey
+}
+
+// define contract (ensure that the struct fulfills the given interface)
+var _ objectstorage.StorableObject = &Output{}
diff --git a/packages/binary/valuetransfer/transferoutput/transferoutput_test.go b/packages/binary/valuetransfer/transaction/output_test.go
similarity index 70%
rename from packages/binary/valuetransfer/transferoutput/transferoutput_test.go
rename to packages/binary/valuetransfer/transaction/output_test.go
index c7614b3b..305409e8 100644
--- a/packages/binary/valuetransfer/transferoutput/transferoutput_test.go
+++ b/packages/binary/valuetransfer/transaction/output_test.go
@@ -1,4 +1,4 @@
-package transferoutput
+package transaction
 
 import (
 	"testing"
diff --git a/packages/binary/valuetransfer/transaction/outputid.go b/packages/binary/valuetransfer/transaction/outputid.go
new file mode 100644
index 00000000..891cd5c0
--- /dev/null
+++ b/packages/binary/valuetransfer/transaction/outputid.go
@@ -0,0 +1,71 @@
+package transaction
+
+import (
+	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+)
+
+// OutputId is the data type that represents the identifier for a Output.
+type OutputId [OutputIdLength]byte
+
+// NewOutputId is the constructor for the OutputId type.
+func NewOutputId(outputAddress address.Address, transactionId Id) (outputId OutputId) {
+	copy(outputId[:address.Length], outputAddress.Bytes())
+	copy(outputId[address.Length:], transactionId[:])
+
+	return
+}
+
+// OutputIdFromBytes unmarshals an OutputId from a sequence of bytes.
+func OutputIdFromBytes(bytes []byte) (result OutputId, err error, consumedBytes int) {
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if idBytes, idErr := marshalUtil.ReadBytes(OutputIdLength); idErr != nil {
+		err = idErr
+
+		return
+	} else {
+		copy(result[:], idBytes)
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// Parse is a wrapper for simplified unmarshaling of Ids from a byte stream using the marshalUtil package.
+func ParseOutputId(marshalUtil *marshalutil.MarshalUtil) (OutputId, error) {
+	if outputId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return OutputIdFromBytes(data) }); err != nil {
+		return OutputId{}, err
+	} else {
+		return outputId.(OutputId), nil
+	}
+}
+
+// Address returns the address part of an OutputId.
+func (outputId OutputId) Address() (address address.Address) {
+	copy(address[:], outputId[:])
+
+	return
+}
+
+// TransactionId returns the transaction id part of an OutputId.
+func (outputId OutputId) TransactionId() (transactionId Id) {
+	copy(transactionId[:], outputId[address.Length:])
+
+	return
+}
+
+// Bytes marshals the OutputId into a sequence of bytes.
+func (outputId OutputId) Bytes() []byte {
+	return outputId[:]
+}
+
+// String creates a human readable version of the OutputId (for debug purposes).
+func (outputId OutputId) String() string {
+	return base58.Encode(outputId[:])
+}
+
+// IdLength contains the amount of bytes that a marshaled version of the OutputId contains.
+const OutputIdLength = address.Length + IdLength
diff --git a/packages/binary/valuetransfer/payload/transfer/outputs/outputs.go b/packages/binary/valuetransfer/transaction/outputs.go
similarity index 79%
rename from packages/binary/valuetransfer/payload/transfer/outputs/outputs.go
rename to packages/binary/valuetransfer/transaction/outputs.go
index d32956f4..3166ac93 100644
--- a/packages/binary/valuetransfer/payload/transfer/outputs/outputs.go
+++ b/packages/binary/valuetransfer/transaction/outputs.go
@@ -1,17 +1,17 @@
-package outputs
+package transaction
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
 )
 
 type Outputs struct {
 	*orderedmap.OrderedMap
 }
 
-func New(outputs map[address.Address][]*coloredbalance.ColoredBalance) (result *Outputs) {
+func NewOutputs(outputs map[address.Address][]*balance.Balance) (result *Outputs) {
 	result = &Outputs{orderedmap.New()}
 	for address, balances := range outputs {
 		result.Add(address, balances)
@@ -22,7 +22,7 @@ func New(outputs map[address.Address][]*coloredbalance.ColoredBalance) (result *
 
 // FromBytes reads the bytes and unmarshals the given information into an *Outputs object. It either creates a
 // new object, or uses the optional object provided in the arguments.
-func FromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs, err error, consumedBytes int) {
+func OutputsFromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -61,16 +61,16 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs,
 		}
 
 		// iterate the corresponding times and collect balances
-		coloredBalances := make([]*coloredbalance.ColoredBalance, balanceCount)
+		coloredBalances := make([]*balance.Balance, balanceCount)
 		for j := uint32(0); j < balanceCount; j++ {
-			coloredBalance, coloredBalanceErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return coloredbalance.FromBytes(data) })
+			coloredBalance, coloredBalanceErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return balance.FromBytes(data) })
 			if coloredBalanceErr != nil {
 				err = coloredBalanceErr
 
 				return
 			}
 
-			coloredBalances[j] = coloredBalance.(*coloredbalance.ColoredBalance)
+			coloredBalances[j] = coloredBalance.(*balance.Balance)
 		}
 
 		// add the gathered information as an output
@@ -83,15 +83,15 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs,
 	return
 }
 
-func (outputs *Outputs) Add(address address.Address, balances []*coloredbalance.ColoredBalance) *Outputs {
+func (outputs *Outputs) Add(address address.Address, balances []*balance.Balance) *Outputs {
 	outputs.Set(address, balances)
 
 	return outputs
 }
 
-func (outputs *Outputs) ForEach(consumer func(address address.Address, balances []*coloredbalance.ColoredBalance)) {
+func (outputs *Outputs) ForEach(consumer func(address address.Address, balances []*balance.Balance)) {
 	outputs.OrderedMap.ForEach(func(key, value interface{}) bool {
-		consumer(key.(address.Address), value.([]*coloredbalance.ColoredBalance))
+		consumer(key.(address.Address), value.([]*balance.Balance))
 
 		return true
 	})
@@ -107,7 +107,7 @@ func (outputs *Outputs) Bytes() []byte {
 	}
 
 	marshalUtil.WriteUint32(uint32(outputs.Size()))
-	outputs.ForEach(func(address address.Address, balances []*coloredbalance.ColoredBalance) {
+	outputs.ForEach(func(address address.Address, balances []*balance.Balance) {
 		marshalUtil.WriteBytes(address.Bytes())
 		marshalUtil.WriteUint32(uint32(len(balances)))
 
@@ -126,7 +126,7 @@ func (outputs *Outputs) String() string {
 
 	result := "[\n"
 	empty := true
-	outputs.ForEach(func(address address.Address, balances []*coloredbalance.ColoredBalance) {
+	outputs.ForEach(func(address address.Address, balances []*balance.Balance) {
 		empty = false
 
 		result += "    " + address.String() + ": [\n"
diff --git a/packages/binary/valuetransfer/payload/transfer/signatures/signatures.go b/packages/binary/valuetransfer/transaction/signatures.go
similarity index 66%
rename from packages/binary/valuetransfer/payload/transfer/signatures/signatures.go
rename to packages/binary/valuetransfer/transaction/signatures.go
index d07d1a43..26f3f45f 100644
--- a/packages/binary/valuetransfer/payload/transfer/signatures/signatures.go
+++ b/packages/binary/valuetransfer/transaction/signatures.go
@@ -1,9 +1,10 @@
-package signatures
+package transaction
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
 )
 
 // Signatures represents a container for the address signatures of a value transfer.
@@ -14,7 +15,7 @@ type Signatures struct {
 }
 
 // New creates an empty container for the address signatures of a value transfer.
-func New() *Signatures {
+func NewSignatures() *Signatures {
 	return &Signatures{
 		orderedMap: orderedmap.New(),
 	}
@@ -22,7 +23,7 @@ func New() *Signatures {
 
 // FromBytes unmarshals a container with signatures from a sequence of bytes.
 // It either creates a new container or fills the optionally provided container with the parsed information.
-func FromBytes(bytes []byte, optionalTargetObject ...*Signatures) (result *Signatures, err error, consumedBytes int) {
+func SignaturesFromBytes(bytes []byte, optionalTargetObject ...*Signatures) (result *Signatures, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -46,15 +47,15 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Signatures) (result *Signa
 	for versionByte != 0 {
 		// perform signature scheme specific decoding
 		switch versionByte {
-		case VERSION_ED25519:
+		case signaturescheme.VERSION_ED25519:
 			marshalUtil.ReadSeek(-1)
-			signature, signatureErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ed25519SignatureFromBytes(data) })
+			signature, signatureErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return signaturescheme.Ed25519SignatureFromBytes(data) })
 			if signatureErr != nil {
 				err = signatureErr
 
 				return
 			}
-			typeCastedSignature := signature.(Signature)
+			typeCastedSignature := signature.(signaturescheme.Signature)
 
 			result.orderedMap.Set(typeCastedSignature.Address(), typeCastedSignature)
 		}
@@ -71,17 +72,17 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Signatures) (result *Signa
 	return
 }
 
-func (signatures *Signatures) Add(address address.Address, signature Signature) {
+func (signatures *Signatures) Add(address address.Address, signature signaturescheme.Signature) {
 	signatures.orderedMap.Set(address, signature)
 }
 
-func (signatures *Signatures) Get(address address.Address) (Signature, bool) {
+func (signatures *Signatures) Get(address address.Address) (signaturescheme.Signature, bool) {
 	signature, exists := signatures.orderedMap.Get(address)
 	if !exists {
 		return nil, false
 	}
 
-	return signature.(Signature), exists
+	return signature.(signaturescheme.Signature), exists
 }
 
 // Size returns the amount of signatures in this container.
@@ -91,9 +92,9 @@ func (signatures *Signatures) Size() int {
 
 // ForEach iterates through all signatures, calling the consumer for every found entry.
 // The iteration can be aborted by the consumer returning false
-func (signatures *Signatures) ForEach(consumer func(address address.Address, signature Signature) bool) {
+func (signatures *Signatures) ForEach(consumer func(address address.Address, signature signaturescheme.Signature) bool) {
 	signatures.orderedMap.ForEach(func(key, value interface{}) bool {
-		return consumer(key.(address.Address), value.(Signature))
+		return consumer(key.(address.Address), value.(signaturescheme.Signature))
 	})
 }
 
@@ -103,7 +104,7 @@ func (signatures *Signatures) Bytes() []byte {
 	marshalUtil := marshalutil.New()
 
 	// iterate through signatures and dump them
-	signatures.ForEach(func(address address.Address, signature Signature) bool {
+	signatures.ForEach(func(address address.Address, signature signaturescheme.Signature) bool {
 		marshalUtil.WriteBytes(signature.Bytes())
 
 		return true
@@ -115,3 +116,40 @@ func (signatures *Signatures) Bytes() []byte {
 	// return result
 	return marshalUtil.Bytes()
 }
+
+func (signatures *Signatures) String() string {
+	if signatures == nil {
+		return "<nil>"
+	}
+
+	result := "[\n"
+	empty := true
+	signatures.ForEach(func(address address.Address, signature signaturescheme.Signature) bool {
+		empty = false
+
+		result += "    " + address.String() + ": [\n"
+
+		/*
+			balancesEmpty := true
+			for _, balance := range balances {
+				balancesEmpty = false
+
+				result += "        " + balance.String() + ",\n"
+			}
+
+			if balancesEmpty {
+				result += "        <empty>\n"
+			}
+		*/
+
+		result += "    ]\n"
+
+		return true
+	})
+
+	if empty {
+		result += "    <empty>\n"
+	}
+
+	return result + "]"
+}
diff --git a/packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go b/packages/binary/valuetransfer/transaction/signatures_test.go
similarity index 65%
rename from packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go
rename to packages/binary/valuetransfer/transaction/signatures_test.go
index 214092e8..88589852 100644
--- a/packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go
+++ b/packages/binary/valuetransfer/transaction/signatures_test.go
@@ -1,4 +1,4 @@
-package signatures
+package transaction
 
 import (
 	"testing"
@@ -7,15 +7,16 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
 )
 
 func TestSignatures(t *testing.T) {
 	dataToSign := []byte("test")
 
-	address1SigScheme := ED25519(ed25119.GenerateKeyPair())
-	address2SigScheme := ED25519(ed25119.GenerateKeyPair())
+	address1SigScheme := signaturescheme.ED25519(ed25119.GenerateKeyPair())
+	address2SigScheme := signaturescheme.ED25519(ed25119.GenerateKeyPair())
 
-	signatures := New()
+	signatures := NewSignatures()
 	signatures.Add(address1SigScheme.Address(), address1SigScheme.Sign(dataToSign))
 	signatures.Add(address2SigScheme.Address(), address2SigScheme.Sign(dataToSign))
 
@@ -25,13 +26,13 @@ func TestSignatures(t *testing.T) {
 
 	assert.Equal(t, 2, signatures.Size())
 
-	signatures.ForEach(func(address address.Address, signature Signature) bool {
+	signatures.ForEach(func(address address.Address, signature signaturescheme.Signature) bool {
 		assert.Equal(t, true, signature.IsValid(dataToSign))
 
 		return true
 	})
 
-	clonedSignatures, err, _ := FromBytes(signatures.Bytes())
+	clonedSignatures, err, _ := SignaturesFromBytes(signatures.Bytes())
 	if err != nil {
 		t.Error(err)
 
@@ -40,7 +41,7 @@ func TestSignatures(t *testing.T) {
 
 	assert.Equal(t, 2, clonedSignatures.Size())
 
-	clonedSignatures.ForEach(func(address address.Address, signature Signature) bool {
+	clonedSignatures.ForEach(func(address address.Address, signature signaturescheme.Signature) bool {
 		assert.Equal(t, true, signature.IsValid(dataToSign))
 
 		return true
diff --git a/packages/binary/valuetransfer/transaction/transaction.go b/packages/binary/valuetransfer/transaction/transaction.go
new file mode 100644
index 00000000..705a62ab
--- /dev/null
+++ b/packages/binary/valuetransfer/transaction/transaction.go
@@ -0,0 +1,339 @@
+package transaction
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+	"github.com/mr-tron/base58"
+	"golang.org/x/crypto/blake2b"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
+)
+
+// region IMPLEMENT Transaction ///////////////////////////////////////////////////////////////////////////////////////////
+
+type Transaction struct {
+	objectstorage.StorableObjectFlags
+
+	inputs     *Inputs
+	outputs    *Outputs
+	signatures *Signatures
+
+	id      *Id
+	idMutex sync.RWMutex
+
+	essenceBytes      []byte
+	essenceBytesMutex sync.RWMutex
+
+	signatureBytes      []byte
+	signatureBytesMutex sync.RWMutex
+
+	bytes      []byte
+	bytesMutex sync.RWMutex
+}
+
+func New(inputs *Inputs, outputs *Outputs) *Transaction {
+	return &Transaction{
+		inputs:     inputs,
+		outputs:    outputs,
+		signatures: NewSignatures(),
+	}
+}
+
+func FromBytes(bytes []byte, optionalTargetObject ...*Transaction) (result *Transaction, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Transaction{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to OutputFromBytes")
+	}
+
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// unmarshal inputs
+	parsedInputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return InputsFromBytes(data) })
+	if err != nil {
+		return
+	}
+	result.inputs = parsedInputs.(*Inputs)
+
+	// unmarshal outputs
+	parsedOutputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return OutputsFromBytes(data) })
+	if err != nil {
+		return
+	}
+	result.outputs = parsedOutputs.(*Outputs)
+
+	// store essence bytes
+	essenceBytesCount := marshalUtil.ReadOffset()
+	result.essenceBytes = make([]byte, essenceBytesCount)
+	copy(result.essenceBytes, bytes[:essenceBytesCount])
+
+	// unmarshal outputs
+	parsedSignatures, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return SignaturesFromBytes(data) })
+	if err != nil {
+		return
+	}
+	result.signatures = parsedSignatures.(*Signatures)
+
+	// store signature bytes
+	signatureBytesCount := marshalUtil.ReadOffset() - essenceBytesCount
+	result.signatureBytes = make([]byte, signatureBytesCount)
+	copy(result.signatureBytes, bytes[essenceBytesCount:essenceBytesCount+signatureBytesCount])
+
+	// return the number of bytes we processed
+	consumedBytes = essenceBytesCount + signatureBytesCount
+
+	// store bytes, so we don't have to marshal manually
+	result.bytes = bytes[:consumedBytes]
+
+	return
+}
+
+func FromStorage(key []byte) *Transaction {
+	id, err, _ := IdFromBytes(key)
+	if err != nil {
+		panic(err)
+	}
+
+	return &Transaction{
+		id: &id,
+	}
+}
+
+func (transaction *Transaction) Id() Id {
+	// acquire lock for reading id
+	transaction.idMutex.RLock()
+
+	// return if id has been calculated already
+	if transaction.id != nil {
+		defer transaction.idMutex.RUnlock()
+
+		return *transaction.id
+	}
+
+	// switch to write lock
+	transaction.idMutex.RUnlock()
+	transaction.idMutex.Lock()
+	defer transaction.idMutex.Unlock()
+
+	// return if id has been calculated in the mean time
+	if transaction.id != nil {
+		return *transaction.id
+	}
+
+	// otherwise calculate the id
+	idBytes := blake2b.Sum256(transaction.Bytes())
+	id, err, _ := IdFromBytes(idBytes[:])
+	if err != nil {
+		panic(err)
+	}
+
+	// cache result for later calls
+	transaction.id = &id
+
+	return id
+}
+
+func (transaction *Transaction) Inputs() *Inputs {
+	return transaction.inputs
+}
+
+func (transaction *Transaction) SignaturesValid() bool {
+	signaturesValid := true
+	transaction.inputs.ForEachAddress(func(address address.Address) bool {
+		if signature, exists := transaction.signatures.Get(address); !exists || !signature.IsValid(transaction.EssenceBytes()) {
+			signaturesValid = false
+
+			return false
+		}
+
+		return true
+	})
+
+	return signaturesValid
+}
+
+func (transaction *Transaction) EssenceBytes() []byte {
+	// acquire read lock on essenceBytes
+	transaction.essenceBytesMutex.RLock()
+
+	// return essenceBytes if the object has been marshaled already
+	if transaction.essenceBytes != nil {
+		defer transaction.essenceBytesMutex.RUnlock()
+
+		return transaction.essenceBytes
+	}
+
+	// switch to write lock
+	transaction.essenceBytesMutex.RUnlock()
+	transaction.essenceBytesMutex.Lock()
+	defer transaction.essenceBytesMutex.Unlock()
+
+	// return essenceBytes if the object has been marshaled in the mean time
+	if essenceBytes := transaction.essenceBytes; essenceBytes != nil {
+		return essenceBytes
+	}
+
+	// create marshal helper
+	marshalUtil := marshalutil.New()
+
+	// marshal inputs
+	marshalUtil.WriteBytes(transaction.inputs.Bytes())
+
+	// marshal outputs
+	marshalUtil.WriteBytes(transaction.outputs.Bytes())
+
+	// store marshaled result
+	transaction.essenceBytes = marshalUtil.Bytes()
+
+	return transaction.essenceBytes
+}
+
+func (transaction *Transaction) SignatureBytes() []byte {
+	transaction.signatureBytesMutex.RLock()
+	if transaction.signatureBytes != nil {
+		defer transaction.signatureBytesMutex.RUnlock()
+
+		return transaction.signatureBytes
+	}
+
+	transaction.signatureBytesMutex.RUnlock()
+	transaction.signatureBytesMutex.Lock()
+	defer transaction.signatureBytesMutex.Unlock()
+
+	if transaction.signatureBytes != nil {
+		return transaction.signatureBytes
+	}
+
+	// generate signatures
+	transaction.signatureBytes = transaction.signatures.Bytes()
+
+	return transaction.signatureBytes
+}
+
+func (transaction *Transaction) Bytes() []byte {
+	// acquire read lock on bytes
+	transaction.bytesMutex.RLock()
+
+	// return bytes if the object has been marshaled already
+	if transaction.bytes != nil {
+		defer transaction.bytesMutex.RUnlock()
+
+		return transaction.bytes
+	}
+
+	// switch to write lock
+	transaction.bytesMutex.RUnlock()
+	transaction.bytesMutex.Lock()
+	defer transaction.bytesMutex.Unlock()
+
+	// return bytes if the object has been marshaled in the mean time
+	if bytes := transaction.bytes; bytes != nil {
+		return bytes
+	}
+
+	// create marshal helper
+	marshalUtil := marshalutil.New()
+
+	// marshal essence bytes
+	marshalUtil.WriteBytes(transaction.EssenceBytes())
+
+	// marshal signature bytes
+	marshalUtil.WriteBytes(transaction.SignatureBytes())
+
+	// store marshaled result
+	transaction.bytes = marshalUtil.Bytes()
+
+	return transaction.bytes
+}
+
+func (transaction *Transaction) Sign(signature signaturescheme.SignatureScheme) *Transaction {
+	transaction.signatures.Add(signature.Address(), signature.Sign(transaction.EssenceBytes()))
+
+	return transaction
+}
+
+func (transaction *Transaction) String() string {
+	id := transaction.Id()
+
+	return stringify.Struct("Transaction"+fmt.Sprintf("(%p)", transaction),
+		stringify.StructField("id", base58.Encode(id[:])),
+		stringify.StructField("inputs", transaction.inputs),
+		stringify.StructField("outputs", transaction.outputs),
+		stringify.StructField("signatures", transaction.signatures),
+	)
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// region IMPLEMENT StorableObject interface ///////////////////////////////////////////////////////////////////////////
+
+// define contract (ensure that the struct fulfills the given interface)
+var _ objectstorage.StorableObject = &Transaction{}
+
+func (transaction *Transaction) GetStorageKey() []byte {
+	id := transaction.Id()
+
+	return id[:]
+}
+
+func (transaction *Transaction) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// MarshalBinary returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler interface.
+func (transaction *Transaction) MarshalBinary() ([]byte, error) {
+	return transaction.Bytes(), nil
+}
+
+func (transaction *Transaction) UnmarshalBinary(bytes []byte) (err error) {
+	_, err, _ = FromBytes(bytes, transaction)
+
+	return
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CachedTransaction is a wrapper for the object storage, that takes care of type casting the Transaction objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of Transaction, without having to manually type cast over and over again.
+type CachedTransaction struct {
+	objectstorage.CachedObject
+}
+
+// Retain overrides the underlying method to return a new CachedTransaction instead of a generic CachedObject.
+func (cachedTransaction *CachedTransaction) Retain() *CachedTransaction {
+	return &CachedTransaction{cachedTransaction.CachedObject.Retain()}
+}
+
+// Consume  overrides the underlying method to use a CachedTransaction object instead of a generic CachedObject in the
+// consumer).
+func (cachedTransaction *CachedTransaction) Consume(consumer func(metadata *Transaction)) bool {
+	return cachedTransaction.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*Transaction))
+	})
+}
+
+// Unwrap provides a way to retrieve a type casted version of the underlying object.
+func (cachedTransaction *CachedTransaction) Unwrap() *Transaction {
+	if untypedTransaction := cachedTransaction.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*Transaction); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/transferoutput/id/id.go b/packages/binary/valuetransfer/transferoutput/id/id.go
deleted file mode 100644
index b8d9252e..00000000
--- a/packages/binary/valuetransfer/transferoutput/id/id.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package id
-
-import (
-	"github.com/mr-tron/base58"
-
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-)
-
-type Id [Length]byte
-
-func New(outputAddress address.Address, transferId id.Id) (transferOutputId Id) {
-	copy(transferOutputId[:address.Length], outputAddress.Bytes())
-	copy(transferOutputId[address.Length:], transferId[:])
-
-	return
-}
-
-func FromBytes(bytes []byte) (transferOutputId Id) {
-	copy(transferOutputId[:], bytes)
-
-	return
-}
-
-func (transferOutputId Id) GetAddress() (address address.Address) {
-	copy(address[:], transferOutputId[:])
-
-	return
-}
-
-func (transferOutputId Id) GetTransferId() id.Id {
-	return id.New(transferOutputId[address.Length:])
-}
-
-func (transferOutputId Id) ToBytes() []byte {
-	return transferOutputId[:]
-}
-
-func (transferOutputId Id) String() string {
-	return "Id(" + base58.Encode(transferOutputId[:]) + ")"
-}
-
-const Length = address.Length + id.Length
diff --git a/packages/binary/valuetransfer/transferoutput/transferoutput.go b/packages/binary/valuetransfer/transferoutput/transferoutput.go
deleted file mode 100644
index b2808491..00000000
--- a/packages/binary/valuetransfer/transferoutput/transferoutput.go
+++ /dev/null
@@ -1,127 +0,0 @@
-package transferoutput
-
-import (
-	"fmt"
-
-	"github.com/iotaledger/hive.go/objectstorage"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
-	transferId "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-)
-
-// TransferOutput represents the output of a transfer and it contains the balances and the identifiers for this output.
-type TransferOutput struct {
-	address    address.Address
-	transferId transferId.Id
-	balances   []*coloredbalance.ColoredBalance
-
-	objectstorage.StorableObjectFlags
-	storageKey []byte
-}
-
-// New creates a transfer output that contains the balances and identifiers of a successful transfer.
-func New(address address.Address, transferId transferId.Id, balances []*coloredbalance.ColoredBalance) *TransferOutput {
-	return &TransferOutput{
-		address:    address,
-		transferId: transferId,
-		balances:   balances,
-
-		storageKey: marshalutil.New().WriteBytes(address.Bytes()).WriteBytes(transferId.Bytes()).Bytes(),
-	}
-}
-
-// FromStorage get's called when we restore a TransferOutput from the storage.
-// In contrast to other database models, it unmarshals some information from the key so we simply store the key before
-// it gets handed over to UnmarshalBinary (by the ObjectStorage).
-func FromStorage(keyBytes []byte) objectstorage.StorableObject {
-	return &TransferOutput{
-		storageKey: marshalutil.New(keyBytes).Bytes(true),
-	}
-}
-
-// Address returns the address that this output belongs to.
-func (transferOutput *TransferOutput) Address() address.Address {
-	return transferOutput.address
-}
-
-// TransferId returns the transfer id, that created this output.
-func (transferOutput *TransferOutput) TransferId() transferId.Id {
-	return transferOutput.transferId
-}
-
-// Balances returns the colored balances (color + balance) that this output contains.
-func (transferOutput *TransferOutput) Balances() []*coloredbalance.ColoredBalance {
-	return transferOutput.balances
-}
-
-// MarshalBinary marshals the balances into a sequence of bytes - the address and transferId are stored inside the key
-// and are ignored here.
-func (transferOutput *TransferOutput) MarshalBinary() (data []byte, err error) {
-	// determine amount of balances in the output
-	balanceCount := len(transferOutput.balances)
-
-	// initialize helper
-	marshalUtil := marshalutil.New(4 + balanceCount*coloredbalance.Length)
-
-	// marshal the amount of balances
-	marshalUtil.WriteUint32(uint32(balanceCount))
-
-	// marshal balances
-	for _, balance := range transferOutput.balances {
-		marshalUtil.WriteBytes(balance.Bytes())
-	}
-
-	return
-}
-
-// UnmarshalBinary restores a TransferOutput from a serialized version in the ObjectStorage with parts of the object
-// being stored in its key rather than the content of the database to reduce storage requirements.
-func (transferOutput *TransferOutput) UnmarshalBinary(data []byte) (err error) {
-	// check if the storageKey has been set
-	if transferOutput.storageKey == nil {
-		return fmt.Errorf("missing storageKey when trying to unmarshal TransferOutput (it contains part of the information)")
-	}
-
-	// parse information from storageKey
-	storageKeyUnmarshaler := marshalutil.New(transferOutput.storageKey)
-	transferOutput.address, err = address.Parse(storageKeyUnmarshaler)
-	if err != nil {
-		return
-	}
-	transferOutput.transferId, err = transferId.Parse(storageKeyUnmarshaler)
-	if err != nil {
-		return
-	}
-
-	// parse information from content bytes
-	contentUnmarshaler := marshalutil.New(data)
-	balanceCount, err := contentUnmarshaler.ReadUint32()
-	if err != nil {
-		return
-	}
-	transferOutput.balances = make([]*coloredbalance.ColoredBalance, balanceCount)
-	for i := uint32(0); i < balanceCount; i++ {
-		transferOutput.balances[i], err = coloredbalance.Parse(contentUnmarshaler)
-		if err != nil {
-			return
-		}
-	}
-
-	return
-}
-
-// Update is disabled and panics if it ever gets called - it is required to match StorableObject interface.
-func (transferOutput *TransferOutput) Update(other objectstorage.StorableObject) {
-	panic("this object should never be updated")
-}
-
-// GetStorageKey returns the key that is used to store the object in the database.
-// It is required to match StorableObject interface.
-func (transferOutput *TransferOutput) GetStorageKey() []byte {
-	return transferOutput.storageKey
-}
-
-// define contract (ensure that the struct fulfills the given interface)
-var _ objectstorage.StorableObject = &TransferOutput{}
diff --git a/packages/gossip/manager.go b/packages/gossip/manager.go
index 24b72a25..d3d6ccba 100644
--- a/packages/gossip/manager.go
+++ b/packages/gossip/manager.go
@@ -6,7 +6,7 @@ import (
 	"net"
 	"sync"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 
 	"github.com/golang/protobuf/proto"
 	pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
@@ -26,7 +26,7 @@ var (
 )
 
 // GetTransaction defines a function that returns the transaction data with the given hash.
-type GetTransaction func(transactionId transaction.Id) ([]byte, error)
+type GetTransaction func(transactionId message.Id) ([]byte, error)
 
 type Manager struct {
 	local          *peer.Local
@@ -256,7 +256,7 @@ func (m *Manager) handlePacket(data []byte, p *peer.Peer) error {
 		}
 		m.log.Debugw("received message", "type", "TRANSACTION_REQUEST", "id", p.ID())
 		// do something
-		txId, err, _ := transaction.IdFromBytes(msg.GetHash())
+		txId, err, _ := message.IdFromBytes(msg.GetHash())
 		if err != nil {
 			m.log.Debugw("error getting transaction", "hash", msg.GetHash(), "err", err)
 		}
diff --git a/packages/gossip/manager_test.go b/packages/gossip/manager_test.go
index afb6ee59..f91adc10 100644
--- a/packages/gossip/manager_test.go
+++ b/packages/gossip/manager_test.go
@@ -7,7 +7,7 @@ import (
 	"time"
 
 	"github.com/golang/protobuf/proto"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
 	"github.com/iotaledger/goshimmer/packages/gossip/server"
 	"github.com/iotaledger/hive.go/autopeering/peer"
@@ -27,7 +27,7 @@ var (
 	testTxData = []byte("testTx")
 )
 
-func getTestTransaction(transaction.Id) ([]byte, error) { return testTxData, nil }
+func getTestTransaction(message.Id) ([]byte, error) { return testTxData, nil }
 
 func TestClose(t *testing.T) {
 	_, detach := newEventMock(t)
diff --git a/plugins/gossip/gossip.go b/plugins/gossip/gossip.go
index 7eaf015f..c32d37d8 100644
--- a/plugins/gossip/gossip.go
+++ b/plugins/gossip/gossip.go
@@ -8,7 +8,7 @@ import (
 	"github.com/iotaledger/hive.go/autopeering/peer/service"
 	"github.com/iotaledger/hive.go/logger"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	gp "github.com/iotaledger/goshimmer/packages/gossip"
 	"github.com/iotaledger/goshimmer/packages/gossip/server"
 	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
@@ -76,10 +76,10 @@ func start(shutdownSignal <-chan struct{}) {
 	log.Info("Stopping " + name + " ...")
 }
 
-func getTransaction(transactionId transaction.Id) (bytes []byte, err error) {
+func getTransaction(transactionId message.Id) (bytes []byte, err error) {
 	log.Debugw("get tx from db", "id", transactionId.String())
 
-	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *transaction.Transaction) {
+	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *message.Transaction) {
 		bytes = transaction.Bytes()
 	}) {
 		err = fmt.Errorf("transaction not found: hash=%s", transactionId)
diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go
index 3d917c98..ceca12f9 100644
--- a/plugins/gossip/plugin.go
+++ b/plugins/gossip/plugin.go
@@ -8,7 +8,7 @@ import (
 	"github.com/iotaledger/hive.go/logger"
 	"github.com/iotaledger/hive.go/node"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/gossip"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
@@ -77,16 +77,16 @@ func configureEvents() {
 	}))
 
 	// configure flow of outgoing transactions (gossip on solidification)
-	tangle.Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, transactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	tangle.Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, transactionMetadata *transactionmetadata.CachedTransactionMetadata) {
 		transactionMetadata.Release()
 
-		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
+		cachedTransaction.Consume(func(transaction *message.Transaction) {
 			mgr.SendTransaction(transaction.Bytes())
 		})
 	}))
 
 	// request missing transactions
-	tangle.TransactionRequester.Events.SendRequest.Attach(events.NewClosure(func(transactionId transaction.Id) {
+	tangle.TransactionRequester.Events.SendRequest.Attach(events.NewClosure(func(transactionId message.Id) {
 		mgr.RequestTransaction(transactionId[:])
 	}))
 }
diff --git a/plugins/metrics/plugin.go b/plugins/metrics/plugin.go
index 28f3013c..501e05f9 100644
--- a/plugins/metrics/plugin.go
+++ b/plugins/metrics/plugin.go
@@ -8,7 +8,7 @@ import (
 	"github.com/iotaledger/hive.go/node"
 	"github.com/iotaledger/hive.go/timeutil"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
@@ -18,7 +18,7 @@ var PLUGIN = node.NewPlugin("Metrics", node.Enabled, configure, run)
 
 func configure(plugin *node.Plugin) {
 	// increase received TPS counter whenever we receive a new transaction
-	tangle.Instance.Events.TransactionAttached.Attach(events.NewClosure(func(transaction *transaction.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
+	tangle.Instance.Events.TransactionAttached.Attach(events.NewClosure(func(transaction *message.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
 		transaction.Release()
 		metadata.Release()
 
diff --git a/plugins/spa/explorer_routes.go b/plugins/spa/explorer_routes.go
index e98cccbb..7be33868 100644
--- a/plugins/spa/explorer_routes.go
+++ b/plugins/spa/explorer_routes.go
@@ -4,7 +4,7 @@ import (
 	"net/http"
 	"sync"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
 
 	"github.com/labstack/echo"
@@ -23,7 +23,7 @@ type ExplorerTx struct {
 	MWM                      int    `json:"mwm"`
 }
 
-func createExplorerTx(tx *transaction.Transaction) (*ExplorerTx, error) {
+func createExplorerTx(tx *message.Transaction) (*ExplorerTx, error) {
 	transactionId := tx.GetId()
 
 	txMetadata := tangle.Instance.GetTransactionMetadata(transactionId)
@@ -57,7 +57,7 @@ type SearchResult struct {
 
 func setupExplorerRoutes(routeGroup *echo.Group) {
 	routeGroup.GET("/tx/:hash", func(c echo.Context) (err error) {
-		transactionId, err := transaction.NewId(c.Param("hash"))
+		transactionId, err := message.NewId(c.Param("hash"))
 		if err != nil {
 			return
 		}
@@ -91,7 +91,7 @@ func setupExplorerRoutes(routeGroup *echo.Group) {
 		go func() {
 			defer wg.Done()
 
-			transactionId, err := transaction.NewId(search)
+			transactionId, err := message.NewId(search)
 			if err != nil {
 				return
 			}
@@ -115,8 +115,8 @@ func setupExplorerRoutes(routeGroup *echo.Group) {
 	})
 }
 
-func findTransaction(transactionId transaction.Id) (explorerTx *ExplorerTx, err error) {
-	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *transaction.Transaction) {
+func findTransaction(transactionId message.Id) (explorerTx *ExplorerTx, err error) {
+	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *message.Transaction) {
 		explorerTx, err = createExplorerTx(transaction)
 	}) {
 		err = errors.Wrapf(ErrNotFound, "tx hash: %s", transactionId.String())
diff --git a/plugins/spa/livefeed.go b/plugins/spa/livefeed.go
index 7a6674c9..adea985e 100644
--- a/plugins/spa/livefeed.go
+++ b/plugins/spa/livefeed.go
@@ -7,7 +7,7 @@ import (
 	"github.com/iotaledger/hive.go/events"
 	"github.com/iotaledger/hive.go/workerpool"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
@@ -19,7 +19,7 @@ var liveFeedWorkerPool *workerpool.WorkerPool
 
 func configureLiveFeed() {
 	liveFeedWorkerPool = workerpool.New(func(task workerpool.Task) {
-		task.Param(0).(*transaction.CachedTransaction).Consume(func(transaction *transaction.Transaction) {
+		task.Param(0).(*message.CachedTransaction).Consume(func(transaction *message.Transaction) {
 			sendToAllWSClient(&msg{MsgTypeTx, &tx{transaction.GetId().String(), 0}})
 		})
 
@@ -29,7 +29,7 @@ func configureLiveFeed() {
 
 func runLiveFeed() {
 	newTxRateLimiter := time.NewTicker(time.Second / 10)
-	notifyNewTx := events.NewClosure(func(tx *transaction.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
+	notifyNewTx := events.NewClosure(func(tx *message.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
 		metadata.Release()
 
 		select {
diff --git a/plugins/tangle/plugin.go b/plugins/tangle/plugin.go
index acc998fe..0b3f156d 100644
--- a/plugins/tangle/plugin.go
+++ b/plugins/tangle/plugin.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/tipselector"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser"
@@ -41,7 +41,7 @@ func configure(*node.Plugin) {
 	Instance = tangle.New(database.GetBadgerInstance(), storageprefix.MainNet)
 
 	// setup TransactionParser
-	TransactionParser.Events.TransactionParsed.Attach(events.NewClosure(func(transaction *transaction.Transaction, peer *peer.Peer) {
+	TransactionParser.Events.TransactionParsed.Attach(events.NewClosure(func(transaction *message.Transaction, peer *peer.Peer) {
 		// TODO: ADD PEER
 
 		Instance.AttachTransaction(transaction)
@@ -49,16 +49,16 @@ func configure(*node.Plugin) {
 
 	// setup TransactionRequester
 	Instance.Events.TransactionMissing.Attach(events.NewClosure(TransactionRequester.ScheduleRequest))
-	Instance.Events.MissingTransactionReceived.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	Instance.Events.MissingTransactionReceived.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
 		cachedTransactionMetadata.Release()
 
-		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
+		cachedTransaction.Consume(func(transaction *message.Transaction) {
 			TransactionRequester.StopRequest(transaction.GetId())
 		})
 	}))
 
 	// setup TipSelector
-	Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
 		cachedTransactionMetadata.Release()
 
 		cachedTransaction.Consume(TipSelector.AddTip)
diff --git a/plugins/webapi/gtta/plugin.go b/plugins/webapi/gtta/plugin.go
index 4e05e8e3..ebebea7b 100644
--- a/plugins/webapi/gtta/plugin.go
+++ b/plugins/webapi/gtta/plugin.go
@@ -3,7 +3,7 @@ package gtta
 import (
 	"net/http"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
 	"github.com/iotaledger/goshimmer/plugins/webapi"
 	"github.com/iotaledger/hive.go/node"
@@ -24,6 +24,6 @@ func Handler(c echo.Context) error {
 }
 
 type Response struct {
-	BranchTransaction transaction.Id `json:"branchTransaction"`
-	TrunkTransaction  transaction.Id `json:"trunkTransaction"`
+	BranchTransaction message.Id `json:"branchTransaction"`
+	TrunkTransaction  message.Id `json:"trunkTransaction"`
 }
-- 
GitLab