diff --git a/go.mod b/go.mod
index aa3d705bcb4ec272cf43a25dde3e24f396368d2a..0dbff93702cc3c219d93d38413c19ea26a9cf060 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2
 	github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0
 	github.com/gorilla/websocket v1.4.1
-	github.com/iotaledger/hive.go v0.0.0-20200305153009-d3408b2be05b
+	github.com/iotaledger/hive.go v0.0.0-20200316213914-76b7a4169e64
 	github.com/iotaledger/iota.go v1.0.0-beta.14
 	github.com/labstack/echo v3.3.10+incompatible
 	github.com/labstack/gommon v0.3.0 // indirect
diff --git a/go.sum b/go.sum
index d573f25b2ba5a39ed6bd477f703fe5b90044a923..18faf70e16059b5918e94f6078f178f1d63feba0 100644
--- a/go.sum
+++ b/go.sum
@@ -111,12 +111,8 @@ github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE0
 github.com/googollee/go-engine.io v1.4.1/go.mod h1:26oFqHsnuWIzNOM0T08x21eQOydBosKOCgK3tyhzPPI=
 github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2 h1:6LbNP1ft0muA4LgoPMvwbxFpVhsRAGimY0Rp+4L7Q1M=
 github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2/go.mod h1:iaugrHMOoal16IKAWvH6y6RrXXIzfOULxjNwvXBCV4o=
-github.com/googollee/go-engine.io v1.4.3-0.20200220091802-9b2ab104b298 h1:mvWmnIGGum8tzT62pTdjxSxCMOyxE7qCToNijulH+zE=
-github.com/googollee/go-engine.io v1.4.3-0.20200220091802-9b2ab104b298/go.mod h1:iaugrHMOoal16IKAWvH6y6RrXXIzfOULxjNwvXBCV4o=
 github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0 h1:7yrvhLv25w1vtVKrcg8CZAn4Pnkb6pzCAqnZ5y9O4q8=
 github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0/go.mod h1:yjlQxKcAZXZjpGwQVW/y1sgyL1ou+DdCpkswURDCRrU=
-github.com/googollee/go-socket.io v1.4.3 h1:fdfTDEnr4YyO+ZM4W9qPKJijbxklcyoxzbmHP7LkQmQ=
-github.com/googollee/go-socket.io v1.4.3/go.mod h1:MRKd5ouUDz3WpEDRnD44JEAytwK1utsJv59QJyjGCio=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
@@ -134,10 +130,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/iotaledger/hive.go v0.0.0-20200304073310-d71239623d46 h1:4v/lfoR5CiJKsG8HUuYeKbds+DPvpxxxSrKZYS7Qhrc=
-github.com/iotaledger/hive.go v0.0.0-20200304073310-d71239623d46/go.mod h1:0LQvxKmfU4bcQcjYIAq3PRfsA5584U0AioAAas6/QU8=
-github.com/iotaledger/hive.go v0.0.0-20200305153009-d3408b2be05b h1:TxHm2TTOG4Xgxdnp5qZsgXKCctnxdDeQgqT4NwPJPQ0=
-github.com/iotaledger/hive.go v0.0.0-20200305153009-d3408b2be05b/go.mod h1:0LQvxKmfU4bcQcjYIAq3PRfsA5584U0AioAAas6/QU8=
+github.com/iotaledger/hive.go v0.0.0-20200316213914-76b7a4169e64 h1:O+1g39PWKzskXCViRD07ePv6183gDbH81XoShbPTzuc=
+github.com/iotaledger/hive.go v0.0.0-20200316213914-76b7a4169e64/go.mod h1:0LQvxKmfU4bcQcjYIAq3PRfsA5584U0AioAAas6/QU8=
 github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8=
 github.com/iotaledger/iota.go v1.0.0-beta.14 h1:Oeb28MfBuJEeXcGrLhTCJFtbsnc8y1u7xidsAmiOD5A=
 github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8=
diff --git a/packages/binary/identity/constants.go b/packages/binary/identity/constants.go
deleted file mode 100644
index b8fb985060952f9cdca9bffc0a5b058f12b9d597..0000000000000000000000000000000000000000
--- a/packages/binary/identity/constants.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package identity
-
-const (
-	PublicKeySize  = 32
-	PrivateKeySize = 64
-	SignatureSize  = 64
-)
diff --git a/packages/binary/identity/identity.go b/packages/binary/identity/identity.go
deleted file mode 100644
index fd52c6f3ff73724dd4d2e9c9f17a738af25b09e3..0000000000000000000000000000000000000000
--- a/packages/binary/identity/identity.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package identity
-
-import (
-	"crypto/rand"
-
-	"github.com/oasislabs/ed25519"
-)
-
-type Identity struct {
-	Type       Type
-	PublicKey  []byte
-	PrivateKey []byte
-}
-
-func New(publicKey []byte, optionalPrivateKey ...[]byte) *Identity {
-	this := &Identity{
-		PublicKey: make([]byte, len(publicKey)),
-	}
-
-	copy(this.PublicKey, publicKey)
-
-	if len(optionalPrivateKey) == 0 {
-		this.Type = Public
-	} else {
-		this.Type = Private
-		this.PrivateKey = optionalPrivateKey[0]
-	}
-
-	return this
-}
-
-func Generate() *Identity {
-	if public, private, err := ed25519.GenerateKey(rand.Reader); err != nil {
-		panic(err)
-	} else {
-		return New(public, private)
-	}
-}
-
-func (identity *Identity) Sign(data []byte) (sig []byte) {
-	sig = ed25519.Sign(identity.PrivateKey, data)
-
-	return
-}
-
-func (identity *Identity) VerifySignature(data []byte, signature []byte) bool {
-	return ed25519.Verify(identity.PublicKey, data, signature)
-}
diff --git a/packages/binary/identity/identity_test.go b/packages/binary/identity/identity_test.go
deleted file mode 100644
index f5fc0d525240ef9d53eed0e5499b612e4398485a..0000000000000000000000000000000000000000
--- a/packages/binary/identity/identity_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package identity
-
-import (
-	"sync"
-	"testing"
-
-	"github.com/panjf2000/ants/v2"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func BenchmarkIdentity_VerifySignature(b *testing.B) {
-	identity := Generate()
-	data := []byte("TESTDATA")
-	signature := identity.Sign(data)
-
-	var wg sync.WaitGroup
-
-	b.ResetTimer()
-
-	for i := 0; i < b.N; i++ {
-		wg.Add(1)
-
-		_ = ants.Submit(func() {
-			identity.VerifySignature(data, signature)
-
-			wg.Done()
-		})
-	}
-
-	wg.Wait()
-}
-
-func Test(t *testing.T) {
-	identity := Generate()
-
-	signature := identity.Sign([]byte("TESTDATA1"))
-
-	assert.Equal(t, true, identity.VerifySignature([]byte("TESTDATA1"), signature))
-	assert.Equal(t, false, identity.VerifySignature([]byte("TESTDATA2"), signature))
-}
diff --git a/packages/binary/identity/type.go b/packages/binary/identity/type.go
deleted file mode 100644
index cc207b0279d2b24bf0b86190431a3753f0b34124..0000000000000000000000000000000000000000
--- a/packages/binary/identity/type.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package identity
-
-type Type int
-
-const (
-	Private = Type(0)
-	Public  = Type(1)
-)
diff --git a/packages/binary/marshalutil/marshalutil.bool.go b/packages/binary/marshalutil/marshalutil.bool.go
new file mode 100644
index 0000000000000000000000000000000000000000..ab1467f13cd19330fa985206d6b5dc6daa175da9
--- /dev/null
+++ b/packages/binary/marshalutil/marshalutil.bool.go
@@ -0,0 +1,24 @@
+package marshalutil
+
+func (util *MarshalUtil) WriteBool(bool bool) {
+	writeEndOffset := util.expandWriteCapacity(1)
+
+	if bool {
+		util.bytes[util.writeOffset] = 1
+	} else {
+		util.bytes[util.writeOffset] = 0
+	}
+
+	util.WriteSeek(writeEndOffset)
+}
+
+func (util *MarshalUtil) ReadBool() (bool, error) {
+	readEndOffset, err := util.checkReadCapacity(1)
+	if err != nil {
+		return false, err
+	}
+
+	defer util.ReadSeek(readEndOffset)
+
+	return util.bytes[util.readOffset] == 1, nil
+}
diff --git a/packages/binary/marshalutil/marshalutil.go b/packages/binary/marshalutil/marshalutil.go
index f6e95e704af690b4f7cdc22fc0ec5a442e1ee722..87837b836e27401d660a2cd0db70364244d75a1b 100644
--- a/packages/binary/marshalutil/marshalutil.go
+++ b/packages/binary/marshalutil/marshalutil.go
@@ -71,7 +71,14 @@ func (util *MarshalUtil) ReadSeek(offset int) {
 	}
 }
 
-func (util *MarshalUtil) Bytes() []byte {
+func (util *MarshalUtil) Bytes(clone ...bool) []byte {
+	if len(clone) >= 1 && clone[0] {
+		clone := make([]byte, util.size)
+		copy(clone, util.bytes)
+
+		return clone
+	}
+
 	return util.bytes[:util.size]
 }
 
diff --git a/packages/binary/marshalutil/marshalutil.time.go b/packages/binary/marshalutil/marshalutil.time.go
new file mode 100644
index 0000000000000000000000000000000000000000..36633dd9d878dcf9d065dbe1b2a3272fe578a1d6
--- /dev/null
+++ b/packages/binary/marshalutil/marshalutil.time.go
@@ -0,0 +1,33 @@
+package marshalutil
+
+import (
+	"time"
+)
+
+// WriteTime marshals the given time into a sequence of bytes, that get appended to the internal buffer.
+func (util *MarshalUtil) WriteTime(timeToWrite time.Time) {
+	nanoSeconds := timeToWrite.UnixNano()
+
+	// the zero value of time translates to -6795364578871345152
+	if nanoSeconds == -6795364578871345152 {
+		util.WriteInt64(0)
+	} else {
+		util.WriteInt64(timeToWrite.UnixNano())
+	}
+}
+
+// ReadTime unmarshals a time object from the internal read buffer.
+func (util *MarshalUtil) ReadTime() (result time.Time, err error) {
+	nanoSeconds, err := util.ReadInt64()
+	if err != nil {
+		return
+	}
+
+	if nanoSeconds == 0 {
+		result = time.Time{}
+	} else {
+		result = time.Unix(0, nanoSeconds)
+	}
+
+	return
+}
diff --git a/packages/binary/marshalutil/marshalutil_test.go b/packages/binary/marshalutil/marshalutil_test.go
index 4b38f69ceb12747fff314784bde35a9683f120e1..17605b2140af0de9c4673c264f9b7787f7c926ac 100644
--- a/packages/binary/marshalutil/marshalutil_test.go
+++ b/packages/binary/marshalutil/marshalutil_test.go
@@ -11,6 +11,7 @@ func Test(t *testing.T) {
 	util.WriteBytes(make([]byte, UINT64_SIZE))
 	util.WriteInt64(-12)
 	util.WriteUint64(38)
+	util.WriteUint64(38)
 
 	fmt.Println(util.ReadBytes(UINT64_SIZE))
 	fmt.Println(util.ReadInt64())
diff --git a/packages/binary/signature/ed25119/private_key.go b/packages/binary/signature/ed25119/private_key.go
index 89dc1a15ecc01f5b088a0aca871390dbbcbd101e..9885a64325cab2c1dccbb84ece58d1b7302856d2 100644
--- a/packages/binary/signature/ed25119/private_key.go
+++ b/packages/binary/signature/ed25119/private_key.go
@@ -1,11 +1,27 @@
 package ed25119
 
 import (
+	"fmt"
+
 	"github.com/oasislabs/ed25519"
 )
 
 type PrivateKey [PrivateKeySize]byte
 
+func PrivateKeyFromBytes(bytes []byte) (result PrivateKey, err error, consumedBytes int) {
+	if len(bytes) < PrivateKeySize {
+		err = fmt.Errorf("bytes too short")
+
+		return
+	}
+
+	copy(result[:], bytes)
+
+	consumedBytes = PrivateKeySize
+
+	return
+}
+
 func (privateKey PrivateKey) Sign(data []byte) (result Signature) {
 	copy(result[:], ed25519.Sign(privateKey[:], data))
 
diff --git a/packages/binary/signature/ed25119/public_key.go b/packages/binary/signature/ed25119/public_key.go
index 9afe9766d147c05f17b1ce6f4c7f256fc18bbb05..f050f6ff274fc6bbb19f6a9dd1c105465fea551c 100644
--- a/packages/binary/signature/ed25119/public_key.go
+++ b/packages/binary/signature/ed25119/public_key.go
@@ -5,6 +5,8 @@ import (
 	"fmt"
 
 	"github.com/oasislabs/ed25519"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 type PublicKey [PublicKeySize]byte
@@ -23,10 +25,22 @@ func PublicKeyFromBytes(bytes []byte) (result PublicKey, err error, consumedByte
 	return
 }
 
+func ParsePublicKey(marshalUtil *marshalutil.MarshalUtil) (PublicKey, error) {
+	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return PublicKeyFromBytes(data) }); err != nil {
+		return PublicKey{}, err
+	} else {
+		return id.(PublicKey), nil
+	}
+}
+
 func (publicKey PublicKey) VerifySignature(data []byte, signature Signature) bool {
 	return ed25519.Verify(publicKey[:], data, signature[:])
 }
 
+func (publicKey PublicKey) Bytes() []byte {
+	return publicKey[:]
+}
+
 func (publicKey *PublicKey) UnmarshalBinary(bytes []byte) (err error) {
 	if len(bytes) < PublicKeySize {
 		return errors.New("not enough bytes")
diff --git a/packages/binary/signature/ed25119/signature.go b/packages/binary/signature/ed25119/signature.go
index d8a291eec642f9714b15160dd27ea3f8b40c5b0d..01a6750f7b62cb1c9b5653ef272bd9d4fee3c1b9 100644
--- a/packages/binary/signature/ed25119/signature.go
+++ b/packages/binary/signature/ed25119/signature.go
@@ -3,6 +3,8 @@ package ed25119
 import (
 	"errors"
 	"fmt"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 type Signature [SignatureSize]byte
@@ -21,6 +23,18 @@ func SignatureFromBytes(bytes []byte) (result Signature, err error, consumedByte
 	return
 }
 
+func ParseSignature(marshalUtil *marshalutil.MarshalUtil) (Signature, error) {
+	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return SignatureFromBytes(data) }); err != nil {
+		return Signature{}, err
+	} else {
+		return id.(Signature), nil
+	}
+}
+
+func (signature Signature) Bytes() []byte {
+	return signature[:]
+}
+
 func (signature *Signature) UnmarshalBinary(bytes []byte) (err error) {
 	if len(bytes) < SignatureSize {
 		return errors.New("not enough bytes")
diff --git a/packages/binary/spammer/spammer.go b/packages/binary/spammer/spammer.go
index 945393339b7f04c57810cb224fa7d23660a81ee2..f41499e28aaae3c84f06b3249d7a0a1efe1e8fba 100644
--- a/packages/binary/spammer/spammer.go
+++ b/packages/binary/spammer/spammer.go
@@ -1,13 +1,12 @@
 package spammer
 
 import (
-	"fmt"
 	"sync/atomic"
 	"time"
 
 	"github.com/iotaledger/hive.go/types"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"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/tipselector"
@@ -43,7 +42,7 @@ func (spammer *Spammer) Shutdown() {
 }
 
 func (spammer *Spammer) run(tps int, processId int64) {
-	fmt.Println(processId)
+	spammingIdentity := ed25119.GenerateKeyPair()
 	currentSentCounter := 0
 	start := time.Now()
 
@@ -54,7 +53,7 @@ func (spammer *Spammer) run(tps int, processId int64) {
 
 		trunkTransactionId, branchTransactionId := spammer.tipSelector.GetTips()
 		spammer.transactionParser.Parse(
-			transaction.New(trunkTransactionId, branchTransactionId, identity.Generate(), data.New([]byte("SPAM"))).GetBytes(),
+			transaction.New(trunkTransactionId, branchTransactionId, spammingIdentity, data.New([]byte("SPAM"))).Bytes(),
 			nil,
 		)
 
@@ -74,7 +73,7 @@ func (spammer *Spammer) run(tps int, processId int64) {
 }
 
 func (spammer *Spammer) sendBurst(transactions int, processId int64) {
-	spammingIdentity := identity.Generate()
+	spammingIdentity := ed25119.GenerateKeyPair()
 
 	previousTransactionId := transaction.EmptyId
 
@@ -86,7 +85,7 @@ func (spammer *Spammer) sendBurst(transactions int, processId int64) {
 
 		spamTransaction := transaction.New(previousTransactionId, previousTransactionId, spammingIdentity, data.New([]byte("SPAM")))
 		previousTransactionId = spamTransaction.GetId()
-		burstBuffer[i] = spamTransaction.GetBytes()
+		burstBuffer[i] = spamTransaction.Bytes()
 	}
 
 	for i := 0; i < transactions; i++ {
diff --git a/packages/binary/storageprefix/storageprefix.go b/packages/binary/storageprefix/storageprefix.go
index fc9a8a21990a5bfaf13c435f9949b73d5a90e6c4..d288fb2db1a15ff0d9245d8c2f8748d401534acc 100644
--- a/packages/binary/storageprefix/storageprefix.go
+++ b/packages/binary/storageprefix/storageprefix.go
@@ -8,12 +8,13 @@ var (
 	TangleApprovers           = []byte{3}
 	TangleMissingTransaction  = []byte{4}
 
-	ValueTangleTransferMetadata = []byte{5}
-	ValueTangleConsumers        = []byte{6}
-	ValueTangleMissingTransfers = []byte{7}
+	ValueTransferPayload         = []byte{5}
+	ValueTransferPayloadMetadata = []byte{6}
+	ValueTransferApprover        = []byte{7}
+	ValueTransferMissingPayload  = []byte{8}
 
-	LedgerStateTransferOutput        = []byte{8}
-	LedgerStateTransferOutputBooking = []byte{9}
-	LedgerStateReality               = []byte{10}
-	LedgerStateConflictSet           = []byte{11}
+	LedgerStateTransferOutput        = []byte{9}
+	LedgerStateTransferOutputBooking = []byte{10}
+	LedgerStateReality               = []byte{11}
+	LedgerStateConflictSet           = []byte{12}
 )
diff --git a/packages/binary/tangle/model/transaction/id.go b/packages/binary/tangle/model/transaction/id.go
index 03c17fa2b4a9a03ed11dbb23d6a203a479f40f96..c7a9319090879ecaa52812d2887c72a52dca71d7 100644
--- a/packages/binary/tangle/model/transaction/id.go
+++ b/packages/binary/tangle/model/transaction/id.go
@@ -4,6 +4,8 @@ import (
 	"fmt"
 
 	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 type Id [IdLength]byte
@@ -41,6 +43,15 @@ func IdFromBytes(bytes []byte) (result Id, err error, consumedBytes int) {
 	return
 }
 
+// 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
+	}
+}
+
 func (id *Id) MarshalBinary() (result []byte, err error) {
 	return id.Bytes(), nil
 }
diff --git a/packages/binary/tangle/model/transaction/payload/data/data.go b/packages/binary/tangle/model/transaction/payload/data/data.go
index 84bc43b7da9be36c6479f5381dbab6bec64ffc79..3c971c20c30459ef8d84d1ce2c3c012309e36409 100644
--- a/packages/binary/tangle/model/transaction/payload/data/data.go
+++ b/packages/binary/tangle/model/transaction/payload/data/data.go
@@ -36,7 +36,18 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err e
 	marshalUtil := marshalutil.New(bytes)
 
 	// read data
-	result.data = marshalUtil.ReadRemainingBytes()
+	result.payloadType, err = marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+	payloadBytes, err := marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+	result.data, err = marshalUtil.ReadBytes(int(payloadBytes))
+	if err != nil {
+		return
+	}
 
 	// return the number of bytes we processed
 	consumedBytes = marshalUtil.ReadOffset()
@@ -57,7 +68,9 @@ func (dataPayload *Data) Bytes() []byte {
 	// initialize helper
 	marshalUtil := marshalutil.New()
 
-	// write the data as raw bytes
+	// marshal the payload specific information
+	marshalUtil.WriteUint32(dataPayload.GetType())
+	marshalUtil.WriteUint32(uint32(len(dataPayload.data)))
 	marshalUtil.WriteBytes(dataPayload.data[:])
 
 	// return result
diff --git a/packages/binary/tangle/model/transaction/payload/payload.go b/packages/binary/tangle/model/transaction/payload/payload.go
index 2d570a886866aa0cecc5972e420b1c76fb593c17..06d0d43912b9889493f0a8ba1d72cf7adaed5fec 100644
--- a/packages/binary/tangle/model/transaction/payload/payload.go
+++ b/packages/binary/tangle/model/transaction/payload/payload.go
@@ -2,6 +2,8 @@ package payload
 
 import (
 	"encoding"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 type Payload interface {
@@ -12,3 +14,42 @@ type Payload interface {
 	GetType() Type
 	String() string
 }
+
+// FromBytes unmarshals a public identity from a sequence of bytes.
+func FromBytes(bytes []byte) (result Payload, err error, consumedBytes int) {
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// calculate result
+	payloadType, err := marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+	payloadSize, err := marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+	marshalUtil.ReadSeek(marshalUtil.ReadOffset() - marshalutil.UINT32_SIZE*2)
+	payloadBytes, err := marshalUtil.ReadBytes(int(payloadSize) + 8)
+	if err != nil {
+		return
+	}
+	result, err = GetUnmarshaler(payloadType)(payloadBytes)
+	if err != nil {
+		return
+	}
+
+	// return the number of bytes we processed
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func Parse(marshalUtil *marshalutil.MarshalUtil) (Payload, error) {
+	if payload, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return payload.(Payload), nil
+	}
+}
diff --git a/packages/binary/tangle/model/transaction/test/transaction_test.go b/packages/binary/tangle/model/transaction/test/transaction_test.go
index fdac283ef304d95d19311ea4017a5e00aa49e999..0d88e5d6954c6b3de751016bcd89d8e927f8661c 100644
--- a/packages/binary/tangle/model/transaction/test/transaction_test.go
+++ b/packages/binary/tangle/model/transaction/test/transaction_test.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/panjf2000/ants/v2"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"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"
 )
@@ -20,7 +20,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, identity.Generate(), data.New([]byte("some data")))
+		tx := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("some data")))
 
 		if marshaledTransaction, err := tx.MarshalBinary(); err != nil {
 			b.Error(err)
@@ -50,8 +50,8 @@ func BenchmarkVerifySignature(b *testing.B) {
 
 	transactions := make([]*transaction.Transaction, b.N)
 	for i := 0; i < b.N; i++ {
-		transactions[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("test")))
-		transactions[i].GetBytes()
+		transactions[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("test")))
+		transactions[i].Bytes()
 	}
 
 	var wg sync.WaitGroup
diff --git a/packages/binary/tangle/model/transaction/transaction.go b/packages/binary/tangle/model/transaction/transaction.go
index 5e662b83c3aa2e8e6bf5a1a9e1d06a20fd94a43f..4abed904ff65a5cdea8e5bec1017ca1a69d2452a 100644
--- a/packages/binary/tangle/model/transaction/transaction.go
+++ b/packages/binary/tangle/model/transaction/transaction.go
@@ -5,8 +5,8 @@ import (
 
 	"github.com/iotaledger/hive.go/stringify"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
 	"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/hive.go/objectstorage"
@@ -23,11 +23,11 @@ type Transaction struct {
 	// core properties (they are part of the transaction when being sent)
 	trunkTransactionId  Id
 	branchTransactionId Id
-	issuer              *identity.Identity
+	issuerPublicKey     ed25119.PublicKey
 	payload             payload.Payload
 	bytes               []byte
 	bytesMutex          sync.RWMutex
-	signature           [identity.SignatureSize]byte
+	signature           ed25119.Signature
 	signatureMutex      sync.RWMutex
 
 	// derived properties
@@ -35,14 +35,18 @@ type Transaction struct {
 	idMutex        sync.RWMutex
 	payloadId      *payload.Id
 	payloadIdMutex sync.RWMutex
+
+	// only stored on the machine of the signer
+	issuerPrivateKey ed25119.PrivateKey
 }
 
 // Allows us to "issue" a transaction.
-func New(trunkTransactionId Id, branchTransactionId Id, issuer *identity.Identity, payload payload.Payload) (result *Transaction) {
+func New(trunkTransactionId Id, branchTransactionId Id, issuerKeyPair ed25119.KeyPair, payload payload.Payload) (result *Transaction) {
 	return &Transaction{
 		trunkTransactionId:  trunkTransactionId,
 		branchTransactionId: branchTransactionId,
-		issuer:              issuer,
+		issuerPublicKey:     issuerKeyPair.PublicKey,
+		issuerPrivateKey:    issuerKeyPair.PrivateKey,
 		payload:             payload,
 	}
 }
@@ -74,61 +78,38 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Transaction) (result *Tran
 	// initialize helper
 	marshalUtil := marshalutil.New(bytes)
 
-	// read trunk transaction id
-	trunkTransactionId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) })
-	if err != nil {
+	// parse information
+	if result.trunkTransactionId, err = ParseId(marshalUtil); err != nil {
 		return
 	}
-	result.trunkTransactionId = trunkTransactionId.(Id)
-
-	// read branch transaction id
-	branchTransactionId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) })
-	if err != nil {
+	if result.branchTransactionId, err = ParseId(marshalUtil); err != nil {
 		return
 	}
-	result.branchTransactionId = branchTransactionId.(Id)
-
-	// read issuer
-	publicKeyBytes, err := marshalUtil.ReadBytes(identity.PublicKeySize)
-	if err != nil {
+	if result.issuerPublicKey, err = ed25119.ParsePublicKey(marshalUtil); err != nil {
 		return
 	}
-	result.issuer = identity.New(publicKeyBytes)
-
-	// read payload type
-	payloadType, err := marshalUtil.ReadUint32()
-	if err != nil {
+	if result.payload, err = payload.Parse(marshalUtil); err != nil {
 		return
 	}
-
-	// read payload
-	payloadBytes, err := marshalUtil.ReadBytes(-identity.SignatureSize)
-	if err != nil {
-		return
-	}
-	result.payload, err = payload.GetUnmarshaler(payloadType)(payloadBytes)
-	if err != nil {
+	if result.signature, err = ed25119.ParseSignature(marshalUtil); err != nil {
 		return
 	}
 
-	// read signature
-	copy(result.signature[:], marshalUtil.ReadRemainingBytes())
+	// return the number of bytes we processed
+	consumedBytes = marshalUtil.ReadOffset()
 
 	// store marshaled version
-	result.bytes = make([]byte, len(bytes))
+	result.bytes = make([]byte, consumedBytes)
 	copy(result.bytes, bytes)
 
-	// return the number of bytes we processed
-	consumedBytes = marshalUtil.ReadOffset()
-
 	return
 }
 
 func (transaction *Transaction) VerifySignature() (result bool) {
-	transactionBytes := transaction.GetBytes()
+	transactionBytes := transaction.Bytes()
 
 	transaction.signatureMutex.RLock()
-	result = transaction.issuer.VerifySignature(transactionBytes[:len(transactionBytes)-identity.SignatureSize], transaction.signature[:])
+	result = transaction.issuerPublicKey.VerifySignature(transactionBytes[:len(transactionBytes)-ed25119.SignatureSize], transaction.signature)
 	transaction.signatureMutex.RUnlock()
 
 	return
@@ -192,14 +173,6 @@ func (transaction *Transaction) GetPayloadId() (result payload.Id) {
 	return
 }
 
-func (transaction *Transaction) GetBytes() []byte {
-	if result, err := transaction.MarshalBinary(); err != nil {
-		panic(err)
-	} else {
-		return result
-	}
-}
-
 func (transaction *Transaction) calculateTransactionId() Id {
 	payloadId := transaction.GetPayloadId()
 
@@ -219,7 +192,7 @@ func (transaction *Transaction) calculateTransactionId() Id {
 }
 
 func (transaction *Transaction) calculatePayloadId() payload.Id {
-	bytes := transaction.GetBytes()
+	bytes := transaction.Bytes()
 
 	return blake2b.Sum512(bytes[2*IdLength:])
 }
@@ -240,14 +213,13 @@ func (transaction *Transaction) Bytes() []byte {
 		return transaction.bytes
 	}
 
+	// marshal result
 	marshalUtil := marshalutil.New()
-
 	marshalUtil.WriteBytes(transaction.trunkTransactionId.Bytes())
 	marshalUtil.WriteBytes(transaction.branchTransactionId.Bytes())
-	marshalUtil.WriteBytes(transaction.issuer.PublicKey)
-	marshalUtil.WriteUint32(transaction.payload.GetType())
+	marshalUtil.WriteBytes(transaction.issuerPublicKey.Bytes())
 	marshalUtil.WriteBytes(transaction.payload.Bytes())
-	marshalUtil.WriteBytes(transaction.issuer.Sign(marshalUtil.Bytes()))
+	marshalUtil.WriteBytes(transaction.issuerPrivateKey.Sign(marshalUtil.Bytes()).Bytes())
 
 	return marshalUtil.Bytes()
 }
diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go
index 4b3653ca8c0a7402b7b1651932bf11e569136abc..0e86da66147ef245bef1599abf97c047a38a2fa9 100644
--- a/packages/binary/tangle/tangle.go
+++ b/packages/binary/tangle/tangle.go
@@ -134,7 +134,7 @@ func (tangle *Tangle) Prune() error {
 	return nil
 }
 
-// Worker that stores the transactions and calls the corresponding "Storage events"
+// Worker that stores the transactions and calls the corresponding storage events"
 func (tangle *Tangle) storeTransactionWorker(tx *transaction.Transaction) {
 	// store transaction
 	var cachedTransaction *transaction.CachedTransaction
diff --git a/packages/binary/tangle/tangle_test.go b/packages/binary/tangle/tangle_test.go
index f0f8c60ef5a7660306818fc83d7d0bfdc821fd66..be15e7e3b701e1385ba71d15399c6e9659f8c5eb 100644
--- a/packages/binary/tangle/tangle_test.go
+++ b/packages/binary/tangle/tangle_test.go
@@ -7,11 +7,10 @@ import (
 	"testing"
 	"time"
 
-	"github.com/dgraph-io/badger/v2"
 	"github.com/iotaledger/hive.go/events"
 	"github.com/stretchr/testify/require"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"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/transactionmetadata"
@@ -19,10 +18,6 @@ import (
 	"github.com/iotaledger/goshimmer/plugins/config"
 )
 
-var testDatabase *badger.DB
-
-var _ = config.PLUGIN
-
 func BenchmarkTangle_AttachTransaction(b *testing.B) {
 	dir, err := ioutil.TempDir("", b.Name())
 	require.NoError(b, err)
@@ -30,19 +25,19 @@ func BenchmarkTangle_AttachTransaction(b *testing.B) {
 	// use the tempdir for the database
 	config.Node.Set(database.CFG_DIRECTORY, dir)
 
-	tangle := New(testDatabase, []byte("TEST_BINARY_TANGLE"))
+	tangle := New(database.GetBadgerInstance(), []byte("TEST_BINARY_TANGLE"))
 	if err := tangle.Prune(); err != nil {
 		b.Error(err)
 
 		return
 	}
 
-	testIdentity := identity.Generate()
+	testIdentity := ed25119.GenerateKeyPair()
 
 	transactionBytes := make([]*transaction.Transaction, b.N)
 	for i := 0; i < b.N; i++ {
 		transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, testIdentity, data.New([]byte("some data")))
-		transactionBytes[i].GetBytes()
+		transactionBytes[i].Bytes()
 	}
 
 	b.ResetTimer()
@@ -96,8 +91,8 @@ func TestTangle_AttachTransaction(t *testing.T) {
 		fmt.Println("REMOVED:", transactionId)
 	}))
 
-	newTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("some data")))
-	newTransaction2 := transaction.New(newTransaction1.GetId(), newTransaction1.GetId(), identity.Generate(), data.New([]byte("some other data")))
+	newTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("some data")))
+	newTransaction2 := transaction.New(newTransaction1.GetId(), newTransaction1.GetId(), ed25119.GenerateKeyPair(), data.New([]byte("some other data")))
 
 	tangle.AttachTransaction(newTransaction2)
 
diff --git a/packages/binary/tangle/tipselector/tipselector_test.go b/packages/binary/tangle/tipselector/tipselector_test.go
index 0bd3d385482c852445a5d92969e4dcc990b08c05..05329cc3e71770858f8606ca19554db4d8c653ae 100644
--- a/packages/binary/tangle/tipselector/tipselector_test.go
+++ b/packages/binary/tangle/tipselector/tipselector_test.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"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"
 )
@@ -20,7 +20,7 @@ func Test(t *testing.T) {
 	assert.Equal(t, transaction.EmptyId, branch1)
 
 	// create a transaction and attach it
-	transaction1 := transaction.New(trunk1, branch1, identity.Generate(), data.New([]byte("testtransaction")))
+	transaction1 := transaction.New(trunk1, branch1, ed25119.GenerateKeyPair(), data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction1)
 
 	// check if the tip shows up in the tip count
@@ -32,7 +32,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, identity.Generate(), data.New([]byte("testtransaction")))
+	transaction2 := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction2)
 
 	// check if the tip shows up in the tip count
@@ -40,7 +40,7 @@ func Test(t *testing.T) {
 
 	// attach a transaction to our two tips
 	trunk3, branch3 := tipSelector.GetTips()
-	transaction3 := transaction.New(trunk3, branch3, identity.Generate(), data.New([]byte("testtransaction")))
+	transaction3 := transaction.New(trunk3, branch3, ed25119.GenerateKeyPair(), data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction3)
 
 	// check if the tip shows replaces the current tips
diff --git a/packages/binary/tangle/transactionparser/transactionparser_test.go b/packages/binary/tangle/transactionparser/transactionparser_test.go
index 5741252932ba5a82eb889300aa32e031f4b6ce2b..8f31c0e9d101fd2fa03e3ef821ad84de5dc2da9e 100644
--- a/packages/binary/tangle/transactionparser/transactionparser_test.go
+++ b/packages/binary/tangle/transactionparser/transactionparser_test.go
@@ -7,13 +7,13 @@ import (
 
 	"github.com/iotaledger/hive.go/events"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"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"
 )
 
 func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) {
-	txBytes := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("Test"))).GetBytes()
+	txBytes := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("Test"))).Bytes()
 	txParser := New()
 
 	b.ResetTimer()
@@ -28,7 +28,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, identity.Generate(), data.New([]byte("Test"+strconv.Itoa(i)))).GetBytes()
+		transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("Test"+strconv.Itoa(i)))).Bytes()
 	}
 
 	txParser := New()
@@ -43,10 +43,10 @@ func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) {
 }
 
 func TestTransactionParser_ParseTransaction(t *testing.T) {
-	tx := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("Test")))
+	tx := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("Test")))
 
 	txParser := New()
-	txParser.Parse(tx.GetBytes(), nil)
+	txParser.Parse(tx.Bytes(), nil)
 
 	txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *transaction.Transaction) {
 		fmt.Println("PARSED!!!")
diff --git a/packages/binary/valuetransfers/address/address.go b/packages/binary/valuetransfer/address/address.go
similarity index 100%
rename from packages/binary/valuetransfers/address/address.go
rename to packages/binary/valuetransfer/address/address.go
diff --git a/packages/binary/valuetransfers/coloredbalance/color/color.go b/packages/binary/valuetransfer/coloredbalance/color/color.go
similarity index 100%
rename from packages/binary/valuetransfers/coloredbalance/color/color.go
rename to packages/binary/valuetransfer/coloredbalance/color/color.go
diff --git a/packages/binary/valuetransfers/coloredbalance/coloredbalance.go b/packages/binary/valuetransfer/coloredbalance/coloredbalance.go
similarity index 93%
rename from packages/binary/valuetransfers/coloredbalance/coloredbalance.go
rename to packages/binary/valuetransfer/coloredbalance/coloredbalance.go
index 6d8f576bb570575d1e841549f828371c99370a32..b0f4d43a072a3130dfa3da1603b87a4e0240b080 100644
--- a/packages/binary/valuetransfers/coloredbalance/coloredbalance.go
+++ b/packages/binary/valuetransfer/coloredbalance/coloredbalance.go
@@ -4,7 +4,7 @@ import (
 	"strconv"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/coloredbalance/color"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance/color"
 )
 
 type ColoredBalance struct {
diff --git a/packages/binary/valuetransfer/payload/cached_object.go b/packages/binary/valuetransfer/payload/cached_object.go
new file mode 100644
index 0000000000000000000000000000000000000000..5c1639b1685cf5627fe8164f14c12922bc34f89c
--- /dev/null
+++ b/packages/binary/valuetransfer/payload/cached_object.go
@@ -0,0 +1,39 @@
+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/id.go
new file mode 100644
index 0000000000000000000000000000000000000000..8a8f24f0425dff74cef572d337cbf443391fcd3b
--- /dev/null
+++ b/packages/binary/valuetransfer/payload/id/id.go
@@ -0,0 +1,87 @@
+package id
+
+import (
+	"fmt"
+
+	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+// Id represents the hash of a payload that is used to identify the given payload.
+type Id [Length]byte
+
+// New creates a payload id from a base58 encoded string.
+func New(base58EncodedString string) (result Id, err error) {
+	bytes, err := base58.Decode(base58EncodedString)
+	if err != nil {
+		return
+	}
+
+	if len(bytes) != Length {
+		err = fmt.Errorf("length of base58 formatted payload id is wrong")
+
+		return
+	}
+
+	copy(result[:], bytes)
+
+	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
+	}
+}
+
+// FromBytes 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) {
+	// determine the target object that will hold the unmarshaled information
+	var targetObject *Id
+	switch len(optionalTargetObject) {
+	case 0:
+		targetObject = &result
+	case 1:
+		targetObject = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to FromBytes")
+	}
+
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// read id from bytes
+	idBytes, err := marshalUtil.ReadBytes(Length)
+	if err != nil {
+		return
+	}
+	copy(targetObject[:], idBytes)
+
+	// copy result if we have provided a target object
+	result = *targetObject
+
+	// return the number of bytes we processed
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// String returns a base58 encoded version of the payload id.
+func (id Id) String() string {
+	return base58.Encode(id[:])
+}
+
+func (id Id) Bytes() []byte {
+	return id[:]
+}
+
+// Empty represents the id encoding the genesis.
+var Genesis Id
+
+// Length defined the amount of bytes in a payload id (32 bytes hash value).
+const Length = 32
diff --git a/packages/binary/valuetransfer/payload/id/id_test.go b/packages/binary/valuetransfer/payload/id/id_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..86fd25ab18cd9c0a41dc8d8c3120fdca8939b4e2
--- /dev/null
+++ b/packages/binary/valuetransfer/payload/id/id_test.go
@@ -0,0 +1,26 @@
+package id
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func Test(t *testing.T) {
+	// create variable for id
+	sourceId, err := New("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM")
+	if err != nil {
+		panic(err)
+	}
+
+	// read serialized id into both variables
+	var restoredIdPointer Id
+	restoredIdValue, err, _ := FromBytes(sourceId.Bytes(), &restoredIdPointer)
+	if err != nil {
+		panic(err)
+	}
+
+	// check if both variables give the same result
+	assert.Equal(t, sourceId, restoredIdValue)
+	assert.Equal(t, sourceId, restoredIdPointer)
+}
diff --git a/packages/binary/valuetransfers/payload/payload.go b/packages/binary/valuetransfer/payload/payload.go
similarity index 76%
rename from packages/binary/valuetransfers/payload/payload.go
rename to packages/binary/valuetransfer/payload/payload.go
index 39430b2179a54e991c57a15d19883c112ff7f153..ee248f7dbb20167cc783f4cab6c8072bda963743 100644
--- a/packages/binary/valuetransfers/payload/payload.go
+++ b/packages/binary/valuetransfer/payload/payload.go
@@ -9,9 +9,9 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
+	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"
 )
 
 type Payload struct {
@@ -36,6 +36,17 @@ func New(trunkPayloadId, branchPayloadId payloadid.Id, valueTransfer *transfer.T
 	}
 }
 
+func FromStorage(key []byte) objectstorage.StorableObject {
+	id, err, _ := payloadid.FromBytes(key)
+	if err != nil {
+		panic(err)
+	}
+
+	return &Payload{
+		id: &id,
+	}
+}
+
 // FromBytes parses the marshaled version of a Payload into an object.
 // It either returns a new Payload or fills an optionally provided Payload with the parsed information.
 func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload, err error, consumedBytes int) {
@@ -52,19 +63,29 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload,
 	// initialize helper
 	marshalUtil := marshalutil.New(bytes)
 
+	// read information that are required to identify the payload from the outside
+	_, err = marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+	_, err = marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+
 	// parse trunk payload id
-	parsedTrunkPayloadId, err := marshalUtil.ReadBytes(payloadid.Length)
+	parsedTrunkPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return payloadid.FromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.trunkPayloadId = payloadid.New(parsedTrunkPayloadId)
+	result.trunkPayloadId = parsedTrunkPayloadId.(payloadid.Id)
 
 	// parse branch payload id
-	parsedBranchPayloadId, err := marshalUtil.ReadBytes(payloadid.Length)
+	parsedBranchPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return payloadid.FromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.branchPayloadId = payloadid.New(parsedBranchPayloadId)
+	result.branchPayloadId = parsedBranchPayloadId.(payloadid.Id)
 
 	// parse transfer
 	parsedTransfer, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return transfer.FromBytes(data) })
@@ -104,22 +125,22 @@ func (payload *Payload) GetId() payloadid.Id {
 	}
 
 	// otherwise calculate the id
-	transferId := payload.GetTransfer().GetId()
 	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length + transferid.Length)
-	marshalUtil.WriteBytes(payload.trunkPayloadId[:])
-	marshalUtil.WriteBytes(payload.branchPayloadId[:])
-	marshalUtil.WriteBytes(transferId[:])
+	marshalUtil.WriteBytes(payload.trunkPayloadId.Bytes())
+	marshalUtil.WriteBytes(payload.branchPayloadId.Bytes())
+	marshalUtil.WriteBytes(payload.GetTransfer().GetId().Bytes())
+
 	var id payloadid.Id = blake2b.Sum256(marshalUtil.Bytes())
 	payload.id = &id
 
 	return id
 }
 
-func (payload *Payload) GetTrunkPayloadId() payloadid.Id {
+func (payload *Payload) GetTrunkId() payloadid.Id {
 	return payload.trunkPayloadId
 }
 
-func (payload *Payload) GetBranchPayloadId() payloadid.Id {
+func (payload *Payload) GetBranchId() payloadid.Id {
 	return payload.branchPayloadId
 }
 
@@ -155,9 +176,12 @@ func (payload *Payload) Bytes() (bytes []byte) {
 	}
 
 	// marshal fields
-	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length + transferid.Length)
-	marshalUtil.WriteBytes(payload.trunkPayloadId[:])
-	marshalUtil.WriteBytes(payload.branchPayloadId[:])
+	payloadLength := payloadid.Length + payloadid.Length + len(transferBytes)
+	marshalUtil := marshalutil.New(marshalutil.UINT32_SIZE + marshalutil.UINT32_SIZE + payloadLength)
+	marshalUtil.WriteUint32(Type)
+	marshalUtil.WriteUint32(uint32(payloadLength))
+	marshalUtil.WriteBytes(payload.trunkPayloadId.Bytes())
+	marshalUtil.WriteBytes(payload.branchPayloadId.Bytes())
 	marshalUtil.WriteBytes(transferBytes)
 	bytes = marshalUtil.Bytes()
 
@@ -170,8 +194,8 @@ func (payload *Payload) Bytes() (bytes []byte) {
 func (payload *Payload) String() string {
 	return stringify.Struct("Payload",
 		stringify.StructField("id", payload.GetId()),
-		stringify.StructField("trunk", payload.GetTrunkPayloadId()),
-		stringify.StructField("branch", payload.GetBranchPayloadId()),
+		stringify.StructField("trunk", payload.GetTrunkId()),
+		stringify.StructField("branch", payload.GetBranchId()),
 		stringify.StructField("transfer", payload.GetTransfer()),
 	)
 }
diff --git a/packages/binary/valuetransfers/payload/id/id.go b/packages/binary/valuetransfer/payload/transfer/id/id.go
similarity index 83%
rename from packages/binary/valuetransfers/payload/id/id.go
rename to packages/binary/valuetransfer/payload/transfer/id/id.go
index 8f9847f54fd82e1d017095934152e26f6005478a..8036c466ac7ec863bcc680b59441934f9b3ab4ba 100644
--- a/packages/binary/valuetransfers/payload/id/id.go
+++ b/packages/binary/valuetransfer/payload/transfer/id/id.go
@@ -12,10 +12,12 @@ func New(idBytes []byte) (result Id) {
 	return
 }
 
+func (id Id) Bytes() []byte {
+	return id[:]
+}
+
 func (id Id) String() string {
 	return base58.Encode(id[:])
 }
 
-var Empty Id
-
 const Length = 32
diff --git a/packages/binary/valuetransfers/payload/transfer/inputs/inputs.go b/packages/binary/valuetransfer/payload/transfer/inputs/inputs.go
similarity index 92%
rename from packages/binary/valuetransfers/payload/transfer/inputs/inputs.go
rename to packages/binary/valuetransfer/payload/transfer/inputs/inputs.go
index bfb7e68840cfb082de4ac3d0269dcb2c5484223e..be2c3fa460e3fd2ceac979e31426aeaa5459bad5 100644
--- a/packages/binary/valuetransfers/payload/transfer/inputs/inputs.go
+++ b/packages/binary/valuetransfer/payload/transfer/inputs/inputs.go
@@ -3,10 +3,10 @@ package inputs
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
-	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/transferoutput/id"
+	"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 {
diff --git a/packages/binary/valuetransfers/payload/transfer/outputs/outputs.go b/packages/binary/valuetransfer/payload/transfer/outputs/outputs.go
similarity index 96%
rename from packages/binary/valuetransfers/payload/transfer/outputs/outputs.go
rename to packages/binary/valuetransfer/payload/transfer/outputs/outputs.go
index 7a6d94febd64f14aa4178c2e832d296edfb1603b..f39268bc68a6fa284b2b2cd7ffdcac0bb36fc86e 100644
--- a/packages/binary/valuetransfers/payload/transfer/outputs/outputs.go
+++ b/packages/binary/valuetransfer/payload/transfer/outputs/outputs.go
@@ -3,8 +3,8 @@ package outputs
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/coloredbalance"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
 )
 
 type Outputs struct {
diff --git a/packages/binary/valuetransfers/payload/transfer/signatures/ed25519.go b/packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go
similarity index 98%
rename from packages/binary/valuetransfers/payload/transfer/signatures/ed25519.go
rename to packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go
index 17cdc17d78cfa4f7d1ac016e5a11f231f57fe4b3..e7423f5512ee7f10ad5e889a19cccafde6246cf5 100644
--- a/packages/binary/valuetransfers/payload/transfer/signatures/ed25519.go
+++ b/packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 )
 
 // region PUBLIC API ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetransfers/payload/transfer/signatures/interfaces.go b/packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
similarity index 98%
rename from packages/binary/valuetransfers/payload/transfer/signatures/interfaces.go
rename to packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
index 9b28078ff953206e6c3aedcb12ca36e7b59d66db..81fe7247e3355d5154eae174f1ba70a948e58350 100644
--- a/packages/binary/valuetransfers/payload/transfer/signatures/interfaces.go
+++ b/packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
@@ -1,6 +1,6 @@
 package signatures
 
-import "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
+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 {
diff --git a/packages/binary/valuetransfers/payload/transfer/signatures/signatures.go b/packages/binary/valuetransfer/payload/transfer/signatures/signatures.go
similarity index 97%
rename from packages/binary/valuetransfers/payload/transfer/signatures/signatures.go
rename to packages/binary/valuetransfer/payload/transfer/signatures/signatures.go
index f5cfee2cfe03550a9282eefabcaf2ea6617eacc2..d07d1a43bdf62f827d52133792199f4250c9733e 100644
--- a/packages/binary/valuetransfers/payload/transfer/signatures/signatures.go
+++ b/packages/binary/valuetransfer/payload/transfer/signatures/signatures.go
@@ -3,7 +3,7 @@ package signatures
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 )
 
 // Signatures represents a container for the address signatures of a value transfer.
diff --git a/packages/binary/valuetransfers/payload/transfer/signatures/signatures_test.go b/packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go
similarity index 93%
rename from packages/binary/valuetransfers/payload/transfer/signatures/signatures_test.go
rename to packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go
index a4aca5092e78ebceb620719f23be35249aee5779..214092e8a9fd33b77df6cb491d7a16a08a618b38 100644
--- a/packages/binary/valuetransfers/payload/transfer/signatures/signatures_test.go
+++ b/packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go
@@ -6,7 +6,7 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 )
 
 func TestSignatures(t *testing.T) {
diff --git a/packages/binary/valuetransfers/payload/transfer/transfer.go b/packages/binary/valuetransfer/payload/transfer/transfer.go
similarity index 94%
rename from packages/binary/valuetransfers/payload/transfer/transfer.go
rename to packages/binary/valuetransfer/payload/transfer/transfer.go
index fd041ce5481d164b84964cc7efc5c073a7756803..0703fcba9f5987ce14dd917f228155e9c4b95ad9 100644
--- a/packages/binary/valuetransfers/payload/transfer/transfer.go
+++ b/packages/binary/valuetransfer/payload/transfer/transfer.go
@@ -10,11 +10,11 @@ import (
 	"golang.org/x/crypto/blake2b"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/inputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/outputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/signatures"
+	"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 ///////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetransfer/tangle/constants.go b/packages/binary/valuetransfer/tangle/constants.go
new file mode 100644
index 0000000000000000000000000000000000000000..937b67282aaa2828313d58abb363c7844df4906b
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/constants.go
@@ -0,0 +1,10 @@
+package tangle
+
+import (
+	"time"
+)
+
+const (
+	MAX_MISSING_TIME_BEFORE_CLEANUP = 30 * time.Second
+	MISSING_CHECK_INTERVAL          = 5 * time.Second
+)
diff --git a/packages/binary/valuetransfer/tangle/events.go b/packages/binary/valuetransfer/tangle/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..653ae8281c6189ab82f2346a05f1bfc6d2b124fb
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/events.go
@@ -0,0 +1,41 @@
+package tangle
+
+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"
+)
+
+type Events struct {
+	// Get's called whenever a transaction
+	PayloadAttached        *events.Event
+	PayloadSolid           *events.Event
+	MissingPayloadReceived *events.Event
+	PayloadMissing         *events.Event
+	PayloadUnsolidifiable  *events.Event
+	TransactionRemoved     *events.Event
+}
+
+func newEvents() *Events {
+	return &Events{
+		PayloadAttached:        events.NewEvent(cachedPayloadEvent),
+		PayloadSolid:           events.NewEvent(cachedPayloadEvent),
+		MissingPayloadReceived: events.NewEvent(cachedPayloadEvent),
+		PayloadMissing:         events.NewEvent(payloadIdEvent),
+		PayloadUnsolidifiable:  events.NewEvent(payloadIdEvent),
+		TransactionRemoved:     events.NewEvent(payloadIdEvent),
+	}
+}
+
+func payloadIdEvent(handler interface{}, params ...interface{}) {
+	handler.(func(payloadid.Id))(params[0].(payloadid.Id))
+}
+
+func cachedPayloadEvent(handler interface{}, params ...interface{}) {
+	handler.(func(*payload.CachedObject, *payloadmetadata.CachedObject))(
+		params[0].(*payload.CachedObject).Retain(),
+		params[1].(*payloadmetadata.CachedObject).Retain(),
+	)
+}
diff --git a/packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go b/packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go
new file mode 100644
index 0000000000000000000000000000000000000000..38afa8099f50a9c59aa62e56bed61e714911449d
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go
@@ -0,0 +1,103 @@
+package missingpayload
+
+import (
+	"time"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+)
+
+// MissingPayload represents a payload that was referenced through branch or trunk but that is missing in our object
+// storage.
+type MissingPayload struct {
+	objectstorage.StorableObjectFlags
+
+	payloadId    payloadid.Id
+	missingSince time.Time
+}
+
+// New creates an entry for a missing value transfer payload.
+func New(payloadId payloadid.Id) *MissingPayload {
+	return &MissingPayload{
+		payloadId:    payloadId,
+		missingSince: time.Now(),
+	}
+}
+
+// FromBytes 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) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &MissingPayload{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to FromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.payloadId, err = payloadid.Parse(marshalUtil); err != nil {
+		return
+	}
+	if result.missingSince, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// FromStorage 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 {
+	return &MissingPayload{}
+}
+
+// GetId returns the payload id, that is missing.
+func (missingPayload *MissingPayload) GetId() payloadid.Id {
+	return missingPayload.payloadId
+}
+
+// GetMissingSince returns the time.Time since the transaction was first reported as being missing.
+func (missingPayload *MissingPayload) GetMissingSince() time.Time {
+	return missingPayload.missingSince
+}
+
+// Bytes marshals the missing payload into a sequence of bytes.
+func (missingPayload *MissingPayload) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteBytes(missingPayload.payloadId.Bytes())
+	marshalUtil.WriteTime(missingPayload.missingSince)
+
+	return marshalUtil.Bytes()
+}
+
+// GetStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (missingPayload *MissingPayload) GetStorageKey() []byte {
+	return missingPayload.payloadId.Bytes()
+}
+
+// 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 (missingPayload *MissingPayload) Update(other objectstorage.StorableObject) {
+	panic("implement me")
+}
+
+// MarshalBinary is required to match the encoding.BinaryMarshaler interface.
+func (missingPayload *MissingPayload) MarshalBinary() (data []byte, err error) {
+	return missingPayload.Bytes(), nil
+}
+
+// UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface.
+func (missingPayload *MissingPayload) UnmarshalBinary(data []byte) (err error) {
+	_, err, _ = FromBytes(data, missingPayload)
+
+	return
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go b/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go
new file mode 100644
index 0000000000000000000000000000000000000000..b90c704272f82869a9b3069f59d1409d92650b6f
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go
@@ -0,0 +1,49 @@
+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
new file mode 100644
index 0000000000000000000000000000000000000000..7c92ae68b2b5bd01615f73532b5edb579aa5219b
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go
@@ -0,0 +1,85 @@
+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/cached_object.go b/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go
new file mode 100644
index 0000000000000000000000000000000000000000..bde06d7cc7f75d5508858a09bdd8c2b52d51c274
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go
@@ -0,0 +1,39 @@
+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.go b/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go
new file mode 100644
index 0000000000000000000000000000000000000000..b6eb53fbf71dc683f8c3b2479626b97c371e9b56
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go
@@ -0,0 +1,178 @@
+package payloadmetadata
+
+import (
+	"sync"
+	"time"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+)
+
+// PayloadMetadata is a container for the metadata of a value transfer payload.
+// It is used to store the information in the database.
+type PayloadMetadata struct {
+	objectstorage.StorableObjectFlags
+
+	payloadId          payloadid.Id
+	solid              bool
+	solidificationTime time.Time
+
+	solidMutex              sync.RWMutex
+	solidificationTimeMutex sync.RWMutex
+}
+
+// New creates an empty container for the metadata of a value transfer payload.
+func New(payloadId payloadid.Id) *PayloadMetadata {
+	return &PayloadMetadata{
+		payloadId: payloadId,
+	}
+}
+
+// FromBytes 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) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &PayloadMetadata{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to FromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.payloadId, err = payloadid.Parse(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
+}
+
+// FromStorage 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 {
+	result := &PayloadMetadata{}
+
+	var err error
+	if result.payloadId, err = payloadid.Parse(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 {
+		return nil, err
+	} else {
+		return payloadMetadata.(*PayloadMetadata), nil
+	}
+}
+
+// GetPayloadId return the id of the payload that this metadata is associated to.
+func (payloadMetadata *PayloadMetadata) GetPayloadId() payloadid.Id {
+	return payloadMetadata.payloadId
+}
+
+// IsSolid returns true of the payload has been marked as solid.
+func (payloadMetadata *PayloadMetadata) IsSolid() (result bool) {
+	payloadMetadata.solidMutex.RLock()
+	result = payloadMetadata.solid
+	payloadMetadata.solidMutex.RUnlock()
+
+	return
+}
+
+// SetSolid marks a payload as either solid or not solid.
+// It returns true if the solid flag was changes and automatically updates the solidificationTime as well.
+func (payloadMetadata *PayloadMetadata) SetSolid(solid bool) (modified bool) {
+	payloadMetadata.solidMutex.RLock()
+	if payloadMetadata.solid != solid {
+		payloadMetadata.solidMutex.RUnlock()
+
+		payloadMetadata.solidMutex.Lock()
+		if payloadMetadata.solid != solid {
+			payloadMetadata.solid = solid
+			if solid {
+				payloadMetadata.solidificationTimeMutex.Lock()
+				payloadMetadata.solidificationTime = time.Now()
+				payloadMetadata.solidificationTimeMutex.Unlock()
+			}
+
+			payloadMetadata.SetModified()
+
+			modified = true
+		}
+		payloadMetadata.solidMutex.Unlock()
+
+	} else {
+		payloadMetadata.solidMutex.RUnlock()
+	}
+
+	return
+}
+
+// GetSoldificationTime returns the time when the payload was marked to be solid.
+func (payloadMetadata *PayloadMetadata) GetSoldificationTime() time.Time {
+	payloadMetadata.solidificationTimeMutex.RLock()
+	defer payloadMetadata.solidificationTimeMutex.RUnlock()
+
+	return payloadMetadata.solidificationTime
+}
+
+// Bytes marshals the metadata into a sequence of bytes.
+func (payloadMetadata *PayloadMetadata) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteBytes(payloadMetadata.payloadId.Bytes())
+	marshalUtil.WriteTime(payloadMetadata.solidificationTime)
+	marshalUtil.WriteBool(payloadMetadata.solid)
+
+	return marshalUtil.Bytes()
+}
+
+// String creates a human readable version of the metadata (for debug purposes).
+func (payloadMetadata *PayloadMetadata) String() string {
+	return stringify.Struct("PayloadMetadata",
+		stringify.StructField("payloadId", payloadMetadata.GetPayloadId()),
+		stringify.StructField("solid", payloadMetadata.IsSolid()),
+		stringify.StructField("solidificationTime", payloadMetadata.GetSoldificationTime()),
+	)
+}
+
+// GetStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (payloadMetadata *PayloadMetadata) GetStorageKey() []byte {
+	return payloadMetadata.payloadId.Bytes()
+}
+
+// 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 (payloadMetadata *PayloadMetadata) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// MarshalBinary is required to match the encoding.BinaryMarshaler interface.
+func (payloadMetadata *PayloadMetadata) MarshalBinary() ([]byte, error) {
+	return payloadMetadata.Bytes(), nil
+}
+
+// UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface.
+func (payloadMetadata *PayloadMetadata) UnmarshalBinary(data []byte) (err error) {
+	_, err, _ = FromBytes(data, payloadMetadata)
+
+	return
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go b/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..fd85c6f5307055506ea8dc258d4bc42e8c7552bb
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go
@@ -0,0 +1,46 @@
+package payloadmetadata
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+)
+
+func TestMarshalUnmarshal(t *testing.T) {
+	originalMetadata := New(id.Genesis)
+
+	clonedMetadata, err, _ := FromBytes(originalMetadata.Bytes())
+	if err != nil {
+		panic(err)
+	}
+
+	assert.Equal(t, originalMetadata.GetPayloadId(), clonedMetadata.GetPayloadId())
+	assert.Equal(t, originalMetadata.IsSolid(), clonedMetadata.IsSolid())
+	assert.Equal(t, originalMetadata.GetSoldificationTime().Round(time.Second), clonedMetadata.GetSoldificationTime().Round(time.Second))
+
+	originalMetadata.SetSolid(true)
+
+	clonedMetadata, err, _ = FromBytes(originalMetadata.Bytes())
+	if err != nil {
+		panic(err)
+	}
+
+	assert.Equal(t, originalMetadata.GetPayloadId(), clonedMetadata.GetPayloadId())
+	assert.Equal(t, originalMetadata.IsSolid(), clonedMetadata.IsSolid())
+	assert.Equal(t, originalMetadata.GetSoldificationTime().Round(time.Second), clonedMetadata.GetSoldificationTime().Round(time.Second))
+}
+
+func TestPayloadMetadata_SetSolid(t *testing.T) {
+	originalMetadata := New(id.Genesis)
+
+	assert.Equal(t, false, originalMetadata.IsSolid())
+	assert.Equal(t, time.Time{}, originalMetadata.GetSoldificationTime())
+
+	originalMetadata.SetSolid(true)
+
+	assert.Equal(t, true, originalMetadata.IsSolid())
+	assert.Equal(t, time.Now().Round(time.Second), originalMetadata.GetSoldificationTime().Round(time.Second))
+}
diff --git a/packages/binary/valuetransfer/tangle/tangle.go b/packages/binary/valuetransfer/tangle/tangle.go
new file mode 100644
index 0000000000000000000000000000000000000000..39c97d7197cca9e091e3b386f319b965fb7ce280
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/tangle.go
@@ -0,0 +1,236 @@
+package tangle
+
+import (
+	"container/list"
+	"time"
+
+	"github.com/dgraph-io/badger/v2"
+	"github.com/iotaledger/hive.go/async"
+	"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"
+)
+
+// Tangle represents the value tangle that consists out of value payloads.
+// It is an independent ontology, that lives inside the tangle.
+type Tangle struct {
+	storageId []byte
+
+	payloadStorage         *objectstorage.ObjectStorage
+	payloadMetadataStorage *objectstorage.ObjectStorage
+	approverStorage        *objectstorage.ObjectStorage
+	missingPayloadStorage  *objectstorage.ObjectStorage
+
+	Events Events
+
+	storePayloadWorkerPool async.WorkerPool
+	solidifierWorkerPool   async.WorkerPool
+	cleanupWorkerPool      async.WorkerPool
+}
+
+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)),
+
+		Events: *newEvents(),
+	}
+
+	return
+}
+
+// AttachPayload adds a new payload to the value tangle.
+func (tangle *Tangle) AttachPayload(payload *valuepayload.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())}
+}
+
+// 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())}
+}
+
+// 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)
+	tangle.approverStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool {
+		approvers = append(approvers, &payloadapprover.CachedObject{CachedObject: cachedObject})
+
+		return true
+	}, transactionId[:])
+
+	return approvers
+}
+
+// Shutdown stops the worker pools and shuts down the object storage instances.
+func (tangle *Tangle) Shutdown() *Tangle {
+	tangle.storePayloadWorkerPool.ShutdownGracefully()
+	tangle.solidifierWorkerPool.ShutdownGracefully()
+	tangle.cleanupWorkerPool.ShutdownGracefully()
+
+	tangle.payloadStorage.Shutdown()
+	tangle.payloadMetadataStorage.Shutdown()
+	tangle.approverStorage.Shutdown()
+	tangle.missingPayloadStorage.Shutdown()
+
+	return tangle
+}
+
+// Prune resets the database and deletes all objects (for testing or "node resets").
+func (tangle *Tangle) Prune() error {
+	for _, storage := range []*objectstorage.ObjectStorage{
+		tangle.payloadStorage,
+		tangle.payloadMetadataStorage,
+		tangle.approverStorage,
+		tangle.missingPayloadStorage,
+	} {
+		if err := storage.Prune(); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// storePayloadWorker is the worker function that stores the payload and calls the corresponding storage events.
+func (tangle *Tangle) storePayloadWorker(payload *valuepayload.Payload) {
+	// store payload
+	var cachedPayload *valuepayload.CachedObject
+	if _tmp, transactionIsNew := tangle.payloadStorage.StoreIfAbsent(payload); !transactionIsNew {
+		return
+	} else {
+		cachedPayload = &valuepayload.CachedObject{CachedObject: _tmp}
+	}
+
+	// store payload metadata
+	payloadId := payload.GetId()
+	cachedMetadata := &payloadmetadata.CachedObject{CachedObject: tangle.payloadMetadataStorage.Store(payloadmetadata.New(payloadId))}
+
+	// store trunk approver
+	trunkId := payload.GetTrunkId()
+	tangle.approverStorage.Store(payloadapprover.New(trunkId, payloadId)).Release()
+
+	// store branch approver
+	if branchId := payload.GetBranchId(); branchId != trunkId {
+		tangle.approverStorage.Store(payloadapprover.New(branchId, trunkId)).Release()
+	}
+
+	// trigger events
+	if tangle.missingPayloadStorage.DeleteIfPresent(payloadId.Bytes()) {
+		tangle.Events.MissingPayloadReceived.Trigger(cachedPayload, cachedMetadata)
+	}
+	tangle.Events.PayloadAttached.Trigger(cachedPayload, cachedMetadata)
+
+	// check solidity
+	tangle.solidifierWorkerPool.Submit(func() {
+		tangle.solidifyTransactionWorker(cachedPayload, cachedMetadata)
+	})
+}
+
+// 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) {
+		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)
+	}
+
+	// initialize the stack
+	solidificationStack := list.New()
+	solidificationStack.PushBack([2]interface{}{cachedPayload, cachedMetadata})
+
+	// process payloads that are supposed to be checked for solidity recursively
+	for solidificationStack.Len() > 0 {
+		currentCachedPayload, currentCachedMetadata := popElementsFromStack(solidificationStack)
+
+		currentPayload := currentCachedPayload.Unwrap()
+		currentMetadata := currentCachedMetadata.Unwrap()
+		if currentPayload == nil || currentMetadata == nil {
+			currentCachedPayload.Release()
+			currentCachedMetadata.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)
+
+			tangle.GetApprovers(currentPayload.GetId()).Consume(func(approver *payloadapprover.PayloadApprover) {
+				approverTransactionId := approver.GetApprovingPayloadId()
+
+				solidificationStack.PushBack([2]interface{}{
+					tangle.GetPayload(approverTransactionId),
+					tangle.GetPayloadMetadata(approverTransactionId),
+				})
+			})
+		}
+
+		// release cached results
+		currentCachedPayload.Release()
+		currentCachedMetadata.Release()
+	}
+}
+
+// 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 {
+	if payload == nil || payload.IsDeleted() {
+		return false
+	}
+
+	if metadata == nil || metadata.IsDeleted() {
+		return false
+	}
+
+	if metadata.IsSolid() {
+		return true
+	}
+
+	return tangle.isPayloadMarkedAsSolid(payload.GetTrunkId()) && tangle.isPayloadMarkedAsSolid(payload.GetBranchId())
+}
+
+// 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 {
+		return true
+	}
+
+	transactionMetadataCached := tangle.GetPayloadMetadata(payloadId)
+	if transactionMetadata := transactionMetadataCached.Unwrap(); transactionMetadata == nil {
+		transactionMetadataCached.Release()
+
+		// if transaction is missing and was not reported as missing, yet
+		if cachedMissingPayload, missingPayloadStored := tangle.missingPayloadStorage.StoreIfAbsent(missingpayload.New(payloadId)); missingPayloadStored {
+			cachedMissingPayload.Consume(func(object objectstorage.StorableObject) {
+				tangle.Events.PayloadMissing.Trigger(object.(*missingpayload.MissingPayload).GetId())
+			})
+		}
+
+		return false
+	} else if !transactionMetadata.IsSolid() {
+		transactionMetadataCached.Release()
+
+		return false
+	}
+	transactionMetadataCached.Release()
+
+	return true
+}
diff --git a/packages/binary/valuetransfer/tangle/tangle_test.go b/packages/binary/valuetransfer/tangle/tangle_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b271eaa336291fe77c3c2f527365f45af7d20f5d
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/tangle_test.go
@@ -0,0 +1,66 @@
+package tangle
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/iotaledger/hive.go/events"
+	"github.com/stretchr/testify/require"
+
+	"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/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/database"
+	"github.com/iotaledger/goshimmer/plugins/config"
+)
+
+func TestTangle_AttachPayload(t *testing.T) {
+	dir, err := ioutil.TempDir("", t.Name())
+	require.NoError(t, err)
+	defer os.Remove(dir)
+
+	config.Node.Set(database.CFG_DIRECTORY, dir)
+
+	tangle := New(database.GetBadgerInstance(), []byte("TEST_BINARY_TANGLE"))
+	if err := tangle.Prune(); err != nil {
+		t.Error(err)
+
+		return
+	}
+
+	tangle.Events.PayloadSolid.Attach(events.NewClosure(func(payload *payload.CachedObject, metadata *payloadmetadata.CachedObject) {
+		fmt.Println(payload.Unwrap())
+
+		payload.Release()
+		metadata.Release()
+	}))
+
+	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"))),
+		),
+
+		outputs.New(map[address.Address][]*coloredbalance.ColoredBalance{
+			address.New([]byte("output_address")): {
+				coloredbalance.New(color.IOTA, 1337),
+			},
+		}),
+	)))
+
+	tangle.Shutdown()
+}
diff --git a/packages/binary/valuetransfers/test/payload_test.go b/packages/binary/valuetransfer/test/payload_test.go
similarity index 78%
rename from packages/binary/valuetransfers/test/payload_test.go
rename to packages/binary/valuetransfer/test/payload_test.go
index 6a91a62837450552a8e74e7a80d48dcaabaf777e..11b8e1454c34d50be8a0037aca9a3a26947fbfcf 100644
--- a/packages/binary/valuetransfers/test/payload_test.go
+++ b/packages/binary/valuetransfer/test/payload_test.go
@@ -6,20 +6,19 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/coloredbalance"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/coloredbalance/color"
-	valuepayload "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/inputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/outputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/signatures"
-	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/transferoutput/id"
+	"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() {
@@ -42,10 +41,10 @@ func ExamplePayload() {
 	// 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.Empty,
+		payloadid.Genesis,
 
 		// branch in "value transfer ontology"  (filled by ontology tipSelector)
-		payloadid.Empty,
+		payloadid.Genesis,
 
 		// value transfer
 		valueTransfer,
@@ -60,7 +59,7 @@ func ExamplePayload() {
 		transaction.EmptyId,
 
 		// issuer of the transaction (signs automatically)
-		identity.Generate(),
+		ed25119.GenerateKeyPair(),
 
 		// payload
 		valuePayload,
@@ -74,8 +73,8 @@ func TestPayload(t *testing.T) {
 	addressKeyPair2 := ed25119.GenerateKeyPair()
 
 	originalPayload := valuepayload.New(
-		payloadid.Empty,
-		payloadid.Empty,
+		payloadid.Genesis,
+		payloadid.Genesis,
 		transfer.New(
 			inputs.New(
 				transferoutputid.New(address.FromED25519PubKey(addressKeyPair1.PublicKey), transferid.New([]byte("transfer1"))),
diff --git a/packages/binary/valuetransfers/transferoutput/id/id.go b/packages/binary/valuetransfer/transferoutput/id/id.go
similarity index 95%
rename from packages/binary/valuetransfers/transferoutput/id/id.go
rename to packages/binary/valuetransfer/transferoutput/id/id.go
index 0d2c7e95c96aecb91d501886442760366d31ce69..b43b9491f95eb98c78a9e4748891d662e0f64889 100644
--- a/packages/binary/valuetransfers/transferoutput/id/id.go
+++ b/packages/binary/valuetransfer/transferoutput/id/id.go
@@ -3,8 +3,8 @@ package id
 import (
 	"github.com/mr-tron/base58"
 
-	address2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
-	id2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
+	address2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	id2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
 )
 
 type Id [Length]byte
diff --git a/packages/binary/valuetransfers/payload/transfer/id/id.go b/packages/binary/valuetransfers/payload/transfer/id/id.go
deleted file mode 100644
index 3f25a8790726f6240cadae26e566ddd68941b430..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfers/payload/transfer/id/id.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package id
-
-import (
-	"github.com/mr-tron/base58"
-)
-
-type Id [Length]byte
-
-func New(idBytes []byte) (result Id) {
-	copy(result[:], idBytes)
-
-	return
-}
-
-func (id Id) String() string {
-	return base58.Encode(id[:])
-}
-
-const Length = 32
diff --git a/plugins/gossip/gossip.go b/plugins/gossip/gossip.go
index 4995280f2ba1824f44b49d7f2817cbfd0f152b9a..7eaf015f846557a3d89f2a3c67e7e8a3c3163bf6 100644
--- a/plugins/gossip/gossip.go
+++ b/plugins/gossip/gossip.go
@@ -80,7 +80,7 @@ func getTransaction(transactionId transaction.Id) (bytes []byte, err error) {
 	log.Debugw("get tx from db", "id", transactionId.String())
 
 	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *transaction.Transaction) {
-		bytes = transaction.GetBytes()
+		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 dcf615e8af4f4220f6f58f3da88644e061a3c653..3d917c98bd06ff212ccdbc7eb72d1c8ca06bf0d0 100644
--- a/plugins/gossip/plugin.go
+++ b/plugins/gossip/plugin.go
@@ -81,7 +81,7 @@ func configureEvents() {
 		transactionMetadata.Release()
 
 		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
-			mgr.SendTransaction(transaction.GetBytes())
+			mgr.SendTransaction(transaction.Bytes())
 		})
 	}))