diff --git a/go.mod b/go.mod
index 5cbc7fa9b22bb6fd8c9595d570bf2091c9e45d08..bbe4a07eade49b1b9dda5c04e1e2d3e919beafdb 100644
--- a/go.mod
+++ b/go.mod
@@ -10,13 +10,12 @@ 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-20200323150914-f38e680ea7d1
+	github.com/iotaledger/hive.go v0.0.0-20200328153852-4d06dcf6bf29
 	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
 	github.com/magiconair/properties v1.8.1
 	github.com/mr-tron/base58 v1.1.3
-	github.com/oasislabs/ed25519 v0.0.0-20200302143042-29f6767a7c3e
 	github.com/panjf2000/ants/v2 v2.2.2
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/pflag v1.0.5
@@ -30,5 +29,3 @@ require (
 	golang.org/x/net v0.0.0-20200301022130-244492dfa37a
 	gopkg.in/src-d/go-git.v4 v4.13.1
 )
-
-replace github.com/iotaledger/hive.go v0.0.0 => ../hive.go
diff --git a/go.sum b/go.sum
index af601bdf46103e7eb133b073fe777dc340cddce7..06874594975f900add465032d03412e5b4bb0618 100644
--- a/go.sum
+++ b/go.sum
@@ -130,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-20200319204115-e052cf07c81d h1:bwrSVMHIVROkg6wkdg2MmMRrzRwGORjNBl7LHJzp3+0=
-github.com/iotaledger/hive.go v0.0.0-20200319204115-e052cf07c81d/go.mod h1:EfH+ZcYGFJzzoFpO7NHGi2k7+Xc84ASyp1EwjhI3eJc=
-github.com/iotaledger/hive.go v0.0.0-20200323150914-f38e680ea7d1 h1:ActziRcYMPDnccHya7NKtwsPCIL5B9WcSE/g6ZFcAew=
-github.com/iotaledger/hive.go v0.0.0-20200323150914-f38e680ea7d1/go.mod h1:EfH+ZcYGFJzzoFpO7NHGi2k7+Xc84ASyp1EwjhI3eJc=
+github.com/iotaledger/hive.go v0.0.0-20200328153852-4d06dcf6bf29 h1:rKw/dp9FnM15e4lzEYwcpxX1lHE0Q06XeIgPskzF9vA=
+github.com/iotaledger/hive.go v0.0.0-20200328153852-4d06dcf6bf29/go.mod h1:4sloxRutRhCuXgAgtOu1ZxVM95Na+ovK9MRDEQGZlzw=
 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=
@@ -186,8 +184,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
 github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
 github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
 github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
-github.com/oasislabs/ed25519 v0.0.0-20200302143042-29f6767a7c3e h1:85L+lUTJHx4O7UP9y/65XV8iq7oaA2Uqe5WiUSB8XE4=
-github.com/oasislabs/ed25519 v0.0.0-20200302143042-29f6767a7c3e/go.mod h1:xIpCyrK2ouGA4QBGbiNbkoONrvJ00u9P3QOkXSOAC0c=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
@@ -319,6 +315,7 @@ go.dedis.ch/kyber/v3 v3.0.12 h1:15d61EyBcBoFIS97kS2c/Vz4o3FR8ALnZ2ck9J/ebYM=
 go.dedis.ch/kyber/v3 v3.0.12/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1U=
 go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo=
 go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4=
+go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo=
 go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.mongodb.org/mongo-driver v1.0.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
diff --git a/packages/binary/marshalutil/marshalutil.bool.go b/packages/binary/marshalutil/marshalutil.bool.go
deleted file mode 100644
index ab1467f13cd19330fa985206d6b5dc6daa175da9..0000000000000000000000000000000000000000
--- a/packages/binary/marshalutil/marshalutil.bool.go
+++ /dev/null
@@ -1,24 +0,0 @@
-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.byte.go b/packages/binary/marshalutil/marshalutil.byte.go
deleted file mode 100644
index 37e15bf71d31107e46f59e2e0c52fed7566d14e5..0000000000000000000000000000000000000000
--- a/packages/binary/marshalutil/marshalutil.byte.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package marshalutil
-
-func (util *MarshalUtil) WriteByte(byte byte) {
-	writeEndOffset := util.expandWriteCapacity(1)
-
-	util.bytes[util.writeOffset] = byte
-
-	util.WriteSeek(writeEndOffset)
-}
-
-func (util *MarshalUtil) ReadByte() (byte, error) {
-	readEndOffset, err := util.checkReadCapacity(1)
-	if err != nil {
-		return 0, err
-	}
-
-	defer util.ReadSeek(readEndOffset)
-
-	return util.bytes[util.readOffset], nil
-}
diff --git a/packages/binary/marshalutil/marshalutil.bytes.go b/packages/binary/marshalutil/marshalutil.bytes.go
deleted file mode 100644
index 9e58affdf422462de2011713f3b31e93fb588d53..0000000000000000000000000000000000000000
--- a/packages/binary/marshalutil/marshalutil.bytes.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package marshalutil
-
-// WriteBytes appends the given bytes to the internal buffer.
-// It returns the same MarshalUtil so calls can be chained.
-func (util *MarshalUtil) WriteBytes(bytes []byte) *MarshalUtil {
-	if bytes == nil {
-		return util
-	}
-
-	writeEndOffset := util.expandWriteCapacity(len(bytes))
-
-	copy(util.bytes[util.writeOffset:writeEndOffset], bytes)
-
-	util.WriteSeek(writeEndOffset)
-
-	return util
-}
-
-// ReadBytes unmarshals the given amount of bytes from the internal read buffer.
-func (util *MarshalUtil) ReadBytes(length int) ([]byte, error) {
-	if length < 0 {
-		length = len(util.bytes) - util.readOffset + length
-	}
-
-	readEndOffset, err := util.checkReadCapacity(length)
-	if err != nil {
-		return nil, err
-	}
-
-	defer util.ReadSeek(readEndOffset)
-
-	return util.bytes[util.readOffset:readEndOffset], nil
-}
-
-func (util *MarshalUtil) ReadRemainingBytes() []byte {
-	defer util.ReadSeek(util.size)
-
-	return util.bytes[util.readOffset:]
-}
diff --git a/packages/binary/marshalutil/marshalutil.go b/packages/binary/marshalutil/marshalutil.go
deleted file mode 100644
index 87837b836e27401d660a2cd0db70364244d75a1b..0000000000000000000000000000000000000000
--- a/packages/binary/marshalutil/marshalutil.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package marshalutil
-
-import (
-	"fmt"
-)
-
-type MarshalUtil struct {
-	bytes       []byte
-	readOffset  int
-	writeOffset int
-	size        int
-}
-
-func New(args ...interface{}) *MarshalUtil {
-	switch argsCount := len(args); argsCount {
-	case 0:
-		return &MarshalUtil{
-			bytes: make([]byte, 1024),
-			size:  0,
-		}
-	case 1:
-		switch param := args[0].(type) {
-		case int:
-			return &MarshalUtil{
-				bytes: make([]byte, param),
-				size:  param,
-			}
-		case []byte:
-			return &MarshalUtil{
-				bytes: param,
-				size:  len(param),
-			}
-		default:
-			panic(fmt.Errorf("illegal argument type %T in marshalutil.New(...)", param))
-		}
-	default:
-		panic(fmt.Errorf("illegal argument count %d in marshalutil.New(...)", argsCount))
-	}
-}
-
-func (util *MarshalUtil) Parse(parser func(data []byte) (interface{}, error, int)) (result interface{}, err error) {
-	result, err, readBytes := parser(util.bytes[util.readOffset:])
-	if err == nil {
-		util.ReadSeek(util.readOffset + readBytes)
-	}
-
-	return
-}
-
-func (util *MarshalUtil) ReadOffset() int {
-	return util.readOffset
-}
-
-func (util *MarshalUtil) WriteOffset() int {
-	return util.writeOffset
-}
-
-func (util *MarshalUtil) WriteSeek(offset int) {
-	if offset < 0 {
-		util.writeOffset += offset
-	} else {
-		util.writeOffset = offset
-	}
-}
-
-func (util *MarshalUtil) ReadSeek(offset int) {
-	if offset < 0 {
-		util.readOffset += offset
-	} else {
-		util.readOffset = offset
-	}
-}
-
-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]
-}
-
-func (util *MarshalUtil) checkReadCapacity(length int) (readEndOffset int, err error) {
-	readEndOffset = util.readOffset + length
-
-	if readEndOffset > util.size {
-		err = fmt.Errorf("tried to read %d bytes from %d bytes input", readEndOffset, util.size)
-	}
-
-	return
-}
-
-func (util *MarshalUtil) expandWriteCapacity(length int) (writeEndOffset int) {
-	writeEndOffset = util.writeOffset + length
-
-	if writeEndOffset > util.size {
-		extendedBytes := make([]byte, writeEndOffset-util.size)
-		util.bytes = append(util.bytes, extendedBytes...)
-		util.size = writeEndOffset
-	}
-
-	return
-}
diff --git a/packages/binary/marshalutil/marshalutil.int64.go b/packages/binary/marshalutil/marshalutil.int64.go
deleted file mode 100644
index 5f1e55754abe159ab0df532234f5b8535eb77eb6..0000000000000000000000000000000000000000
--- a/packages/binary/marshalutil/marshalutil.int64.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package marshalutil
-
-import (
-	"encoding/binary"
-)
-
-const INT64_SIZE = 8
-
-func (util *MarshalUtil) WriteInt64(value int64) {
-	writeEndOffset := util.expandWriteCapacity(INT64_SIZE)
-
-	binary.LittleEndian.PutUint64(util.bytes[util.writeOffset:writeEndOffset], uint64(value))
-
-	util.WriteSeek(writeEndOffset)
-}
-
-func (util *MarshalUtil) ReadInt64() (int64, error) {
-	readEndOffset, err := util.checkReadCapacity(INT64_SIZE)
-	if err != nil {
-		return 0, err
-	}
-
-	defer util.ReadSeek(readEndOffset)
-
-	return int64(binary.LittleEndian.Uint64(util.bytes[util.readOffset:readEndOffset])), nil
-}
diff --git a/packages/binary/marshalutil/marshalutil.time.go b/packages/binary/marshalutil/marshalutil.time.go
deleted file mode 100644
index 36633dd9d878dcf9d065dbe1b2a3272fe578a1d6..0000000000000000000000000000000000000000
--- a/packages/binary/marshalutil/marshalutil.time.go
+++ /dev/null
@@ -1,33 +0,0 @@
-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.uint32.go b/packages/binary/marshalutil/marshalutil.uint32.go
deleted file mode 100644
index 473e1d505100c96a5f156b0a42ce033bd6924313..0000000000000000000000000000000000000000
--- a/packages/binary/marshalutil/marshalutil.uint32.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package marshalutil
-
-import (
-	"encoding/binary"
-)
-
-const UINT32_SIZE = 4
-
-func (util *MarshalUtil) WriteUint32(value uint32) {
-	writeEndOffset := util.expandWriteCapacity(UINT32_SIZE)
-
-	binary.LittleEndian.PutUint32(util.bytes[util.writeOffset:writeEndOffset], value)
-
-	util.WriteSeek(writeEndOffset)
-}
-
-func (util *MarshalUtil) ReadUint32() (uint32, error) {
-	readEndOffset, err := util.checkReadCapacity(UINT32_SIZE)
-	if err != nil {
-		return 0, err
-	}
-
-	defer util.ReadSeek(readEndOffset)
-
-	return binary.LittleEndian.Uint32(util.bytes[util.readOffset:readEndOffset]), nil
-}
diff --git a/packages/binary/marshalutil/marshalutil.uint64.go b/packages/binary/marshalutil/marshalutil.uint64.go
deleted file mode 100644
index 0bb33119de4cf8b337998ea119f2bd138ef977e3..0000000000000000000000000000000000000000
--- a/packages/binary/marshalutil/marshalutil.uint64.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package marshalutil
-
-import "encoding/binary"
-
-const UINT64_SIZE = 8
-
-func (util *MarshalUtil) WriteUint64(value uint64) {
-	writeEndOffset := util.expandWriteCapacity(UINT64_SIZE)
-
-	binary.LittleEndian.PutUint64(util.bytes[util.writeOffset:writeEndOffset], value)
-
-	util.WriteSeek(writeEndOffset)
-}
-
-func (util *MarshalUtil) ReadUint64() (uint64, error) {
-	readEndOffset, err := util.checkReadCapacity(UINT64_SIZE)
-	if err != nil {
-		return 0, err
-	}
-
-	defer util.ReadSeek(readEndOffset)
-
-	return binary.LittleEndian.Uint64(util.bytes[util.readOffset:readEndOffset]), nil
-}
diff --git a/packages/binary/marshalutil/marshalutil_test.go b/packages/binary/marshalutil/marshalutil_test.go
deleted file mode 100644
index 17605b2140af0de9c4673c264f9b7787f7c926ac..0000000000000000000000000000000000000000
--- a/packages/binary/marshalutil/marshalutil_test.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package marshalutil
-
-import (
-	"fmt"
-	"testing"
-)
-
-func Test(t *testing.T) {
-	util := New(1)
-
-	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())
-	fmt.Println(util.ReadUint64())
-	fmt.Println(util.ReadUint64())
-
-	fmt.Println(util)
-}
diff --git a/packages/binary/tangle/model/message/id.go b/packages/binary/messagelayer/message/id.go
similarity index 95%
rename from packages/binary/tangle/model/message/id.go
rename to packages/binary/messagelayer/message/id.go
index 4060031583911101ef10ab67c45932a86a56e530..c268adbf146c231318a38cd1339d09326d26db2e 100644
--- a/packages/binary/tangle/model/message/id.go
+++ b/packages/binary/messagelayer/message/id.go
@@ -3,9 +3,8 @@ package message
 import (
 	"fmt"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/mr-tron/base58"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 type Id [IdLength]byte
diff --git a/packages/binary/messagelayer/message/message.go b/packages/binary/messagelayer/message/message.go
new file mode 100644
index 0000000000000000000000000000000000000000..87f0fc8b1a8ceb32c3c061d5c830f2dd2098f7bf
--- /dev/null
+++ b/packages/binary/messagelayer/message/message.go
@@ -0,0 +1,344 @@
+package message
+
+import (
+	"sync"
+	"time"
+
+	"github.com/iotaledger/hive.go/crypto/ed25519"
+	"github.com/iotaledger/hive.go/identity"
+	"github.com/iotaledger/hive.go/marshalutil"
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+	"github.com/mr-tron/base58"
+	"golang.org/x/crypto/blake2b"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
+)
+
+// region Message //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+type Message struct {
+	// base functionality of StorableObject
+	objectstorage.StorableObjectFlags
+
+	// core properties (they are part of the transaction when being sent)
+	trunkTransactionId  Id
+	branchTransactionId Id
+	issuerPublicKey     ed25519.PublicKey
+	issuingTime         time.Time
+	sequenceNumber      uint64
+	payload             payload.Payload
+	bytes               []byte
+	bytesMutex          sync.RWMutex
+	signature           ed25519.Signature
+	signatureMutex      sync.RWMutex
+
+	// derived properties
+	id             *Id
+	idMutex        sync.RWMutex
+	payloadId      *payload.Id
+	payloadIdMutex sync.RWMutex
+
+	// only stored on the machine of the signer
+	issuerLocalIdentity *identity.LocalIdentity
+}
+
+// New creates a new transaction with the details provided by the issuer.
+func New(trunkTransactionId Id, branchTransactionId Id, issuerPublicKey ed25519.PublicKey, issuingTime time.Time, sequenceNumber uint64, payload payload.Payload, localIdentity *identity.LocalIdentity) (result *Message) {
+	return &Message{
+		trunkTransactionId:  trunkTransactionId,
+		branchTransactionId: branchTransactionId,
+		issuerPublicKey:     issuerPublicKey,
+		issuingTime:         issuingTime,
+		sequenceNumber:      sequenceNumber,
+		payload:             payload,
+
+		issuerLocalIdentity: localIdentity,
+	}
+}
+
+func FromBytes(bytes []byte, optionalTargetObject ...*Message) (result *Message, err error, consumedBytes int) {
+	marshalUtil := marshalutil.New(bytes)
+	result, err = Parse(marshalUtil, optionalTargetObject...)
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func Parse(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Message) (result *Message, err error) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Message{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to Parse")
+	}
+
+	if parsedObject, parseErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) {
+		return StorableObjectFromKey(data)
+	}); parseErr != nil {
+		err = parseErr
+
+		return
+	} else {
+		result = parsedObject.(*Message)
+	}
+	if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parseErr error, parsedBytes int) {
+		parseErr, parsedBytes = result.UnmarshalObjectStorageValue(data)
+
+		return
+	}); err != nil {
+		return
+	}
+
+	return
+}
+
+// Get's called when we restore a transaction from storage. The bytes and the content will be unmarshaled by an external
+// caller (the objectStorage factory).
+func StorableObjectFromKey(key []byte, optionalTargetObject ...*Message) (result objectstorage.StorableObject, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Message{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to StorableObjectFromKey")
+	}
+
+	marshalUtil := marshalutil.New(key)
+	*result.(*Message).id, err = ParseId(marshalUtil)
+	if err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func (transaction *Message) VerifySignature() (result bool) {
+	transactionBytes := transaction.Bytes()
+
+	transaction.signatureMutex.RLock()
+	result = transaction.issuerPublicKey.VerifySignature(transactionBytes[:len(transactionBytes)-ed25519.SignatureSize], transaction.signature)
+	transaction.signatureMutex.RUnlock()
+
+	return
+}
+
+func (transaction *Message) GetId() (result Id) {
+	transaction.idMutex.RLock()
+	if transaction.id == nil {
+		transaction.idMutex.RUnlock()
+
+		transaction.idMutex.Lock()
+		if transaction.id == nil {
+			result = transaction.calculateTransactionId()
+
+			transaction.id = &result
+		} else {
+			result = *transaction.id
+		}
+		transaction.idMutex.Unlock()
+	} else {
+		result = *transaction.id
+
+		transaction.idMutex.RUnlock()
+	}
+
+	return
+}
+
+func (transaction *Message) GetTrunkTransactionId() Id {
+	return transaction.trunkTransactionId
+}
+
+func (transaction *Message) GetBranchTransactionId() Id {
+	return transaction.branchTransactionId
+}
+
+// IssuingTime returns the time when the transaction was created.
+func (transaction *Message) IssuingTime() time.Time {
+	return transaction.issuingTime
+}
+
+// SequenceNumber returns the sequence number of this transaction.
+func (transaction *Message) SequenceNumber() uint64 {
+	return transaction.sequenceNumber
+}
+
+func (transaction *Message) GetPayload() payload.Payload {
+	return transaction.payload
+}
+
+func (transaction *Message) GetPayloadId() (result payload.Id) {
+	transaction.payloadIdMutex.RLock()
+	if transaction.payloadId == nil {
+		transaction.payloadIdMutex.RUnlock()
+
+		transaction.payloadIdMutex.Lock()
+		if transaction.payloadId == nil {
+			result = transaction.calculatePayloadId()
+
+			transaction.payloadId = &result
+		} else {
+			result = *transaction.payloadId
+		}
+		transaction.payloadIdMutex.Unlock()
+	} else {
+		result = *transaction.payloadId
+
+		transaction.payloadIdMutex.RUnlock()
+	}
+
+	return
+}
+
+func (transaction *Message) calculateTransactionId() Id {
+	payloadId := transaction.GetPayloadId()
+
+	hashBase := make([]byte, IdLength+IdLength+payload.IdLength)
+	offset := 0
+
+	copy(hashBase[offset:], transaction.trunkTransactionId[:])
+	offset += IdLength
+
+	copy(hashBase[offset:], transaction.branchTransactionId[:])
+	offset += IdLength
+
+	copy(hashBase[offset:], payloadId[:])
+	// offset += payloadIdLength
+
+	return blake2b.Sum512(hashBase)
+}
+
+func (transaction *Message) calculatePayloadId() payload.Id {
+	bytes := transaction.Bytes()
+
+	return blake2b.Sum512(bytes[2*IdLength:])
+}
+
+func (transaction *Message) Bytes() []byte {
+	transaction.bytesMutex.RLock()
+	if transaction.bytes != nil {
+		defer transaction.bytesMutex.RUnlock()
+
+		return transaction.bytes
+	}
+
+	transaction.bytesMutex.RUnlock()
+	transaction.bytesMutex.RLock()
+	defer transaction.bytesMutex.RUnlock()
+
+	if transaction.bytes != nil {
+		return transaction.bytes
+	}
+
+	// marshal result
+	marshalUtil := marshalutil.New()
+	marshalUtil.WriteBytes(transaction.trunkTransactionId.Bytes())
+	marshalUtil.WriteBytes(transaction.branchTransactionId.Bytes())
+	marshalUtil.WriteBytes(transaction.issuerPublicKey.Bytes())
+	marshalUtil.WriteTime(transaction.issuingTime)
+	marshalUtil.WriteUint64(transaction.sequenceNumber)
+	marshalUtil.WriteBytes(transaction.payload.Bytes())
+	marshalUtil.WriteBytes(transaction.issuerLocalIdentity.Sign(marshalUtil.Bytes()).Bytes())
+
+	return marshalUtil.Bytes()
+}
+
+func (transaction *Message) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	// initialize helper
+	marshalUtil := marshalutil.New(data)
+
+	// parse information
+	if transaction.trunkTransactionId, err = ParseId(marshalUtil); err != nil {
+		return
+	}
+	if transaction.branchTransactionId, err = ParseId(marshalUtil); err != nil {
+		return
+	}
+	if transaction.issuerPublicKey, err = ed25519.ParsePublicKey(marshalUtil); err != nil {
+		return
+	}
+	if transaction.issuingTime, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	if transaction.sequenceNumber, err = marshalUtil.ReadUint64(); err != nil {
+		return
+	}
+	if transaction.payload, err = payload.Parse(marshalUtil); err != nil {
+		return
+	}
+	if transaction.signature, err = ed25519.ParseSignature(marshalUtil); err != nil {
+		return
+	}
+
+	// return the number of bytes we processed
+	consumedBytes = marshalUtil.ReadOffset()
+
+	// store marshaled version
+	transaction.bytes = make([]byte, consumedBytes)
+	copy(transaction.bytes, data)
+
+	return
+}
+
+func (transaction *Message) ObjectStorageKey() []byte {
+	return transaction.GetId().Bytes()
+}
+
+// Since transactions are immutable and do not get changed after being created, we cache the result of the marshaling.
+func (transaction *Message) ObjectStorageValue() []byte {
+	return transaction.Bytes()
+}
+
+func (transaction *Message) Update(other objectstorage.StorableObject) {
+	panic("transactions should never be overwritten and only stored once to optimize IO")
+}
+
+func (transaction *Message) String() string {
+	transactionId := transaction.GetId()
+
+	return stringify.Struct("Message",
+		stringify.StructField("id", base58.Encode(transactionId[:])),
+		stringify.StructField("trunkTransactionId", base58.Encode(transaction.trunkTransactionId[:])),
+		stringify.StructField("trunkTransactionId", base58.Encode(transaction.branchTransactionId[:])),
+		stringify.StructField("payload", transaction.payload),
+	)
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// region CachedMessage ////////////////////////////////////////////////////////////////////////////////////////////////
+
+type CachedMessage struct {
+	objectstorage.CachedObject
+}
+
+func (cachedMessage *CachedMessage) Retain() *CachedMessage {
+	return &CachedMessage{cachedMessage.CachedObject.Retain()}
+}
+
+func (cachedMessage *CachedMessage) Consume(consumer func(transaction *Message)) bool {
+	return cachedMessage.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*Message))
+	})
+}
+
+func (cachedMessage *CachedMessage) Unwrap() *Message {
+	if untypedTransaction := cachedMessage.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*Message); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/messagelayer/messagefactory/events.go b/packages/binary/messagelayer/messagefactory/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..8916433f1b209183090470a7395634758d74389d
--- /dev/null
+++ b/packages/binary/messagelayer/messagefactory/events.go
@@ -0,0 +1,26 @@
+package messagefactory
+
+import (
+	"github.com/iotaledger/hive.go/events"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+)
+
+type Events struct {
+	// A MessageConstructed event is triggered when a message is built including tips, sequence number and other metadata.
+	MessageConstructed *events.Event
+
+	// Error gets triggered when an error occurred.
+	Error *events.Event
+}
+
+func newEvents() *Events {
+	return &Events{
+		MessageConstructed: events.NewEvent(messageConstructedEvent),
+		Error:              events.NewEvent(events.ErrorCaller),
+	}
+}
+
+func messageConstructedEvent(handler interface{}, params ...interface{}) {
+	handler.(func(*message.Message))(params[0].(*message.Message))
+}
diff --git a/packages/binary/messagelayer/messagefactory/messagefactory.go b/packages/binary/messagelayer/messagefactory/messagefactory.go
new file mode 100644
index 0000000000000000000000000000000000000000..25529c4607622de53b424f5689c8242e174c807a
--- /dev/null
+++ b/packages/binary/messagelayer/messagefactory/messagefactory.go
@@ -0,0 +1,71 @@
+package messagefactory
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/dgraph-io/badger/v2"
+	"github.com/pkg/errors"
+
+	"github.com/iotaledger/hive.go/identity"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector"
+)
+
+type MessageFactory struct {
+	Events        *Events
+	sequence      *badger.Sequence
+	localIdentity *identity.LocalIdentity
+	tipSelector   *tipselector.TipSelector
+}
+
+func New(db *badger.DB, localIdentity *identity.LocalIdentity, tipSelector *tipselector.TipSelector, sequenceKey []byte) *MessageFactory {
+	sequence, err := db.GetSequence(sequenceKey, 100)
+	if err != nil {
+		panic(fmt.Errorf("Could not create transaction sequence number. %v", err))
+	}
+
+	return &MessageFactory{
+		Events:        newEvents(),
+		sequence:      sequence,
+		localIdentity: localIdentity,
+		tipSelector:   tipSelector,
+	}
+}
+
+// IssuePayload creates a new message including sequence number and tip selection and returns it.
+// It also triggers the MessageConstructed event once it's done, which is for example used by the plugins to listen for
+// messages that shall be attached to the tangle.
+func (m *MessageFactory) IssuePayload(payload payload.Payload) *message.Message {
+	sequenceNumber, err := m.sequence.Next()
+	if err != nil {
+		m.Events.Error.Trigger(errors.Wrap(err, "Could not create sequence number"))
+
+		return nil
+	}
+
+	trunkTransaction, branchTransaction := m.tipSelector.GetTips()
+
+	tx := message.New(
+		trunkTransaction,
+		branchTransaction,
+		m.localIdentity.PublicKey(),
+		time.Now(),
+		sequenceNumber,
+		payload,
+		m.localIdentity,
+	)
+
+	m.Events.MessageConstructed.Trigger(tx)
+
+	return tx
+}
+
+// Shutdown closes the  messageFactory and persists the sequence number
+func (m *MessageFactory) Shutdown() {
+	if err := m.sequence.Release(); err != nil {
+		m.Events.Error.Trigger(errors.Wrap(err, "Could not release transaction sequence number."))
+	}
+}
diff --git a/packages/binary/messagelayer/messagefactory/messagefactory_test.go b/packages/binary/messagelayer/messagefactory/messagefactory_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..8e2d3cbe13f2056edb0a154057f1a991323f4b97
--- /dev/null
+++ b/packages/binary/messagelayer/messagefactory/messagefactory_test.go
@@ -0,0 +1,147 @@
+package messagefactory
+
+import (
+	"encoding"
+	"io/ioutil"
+	"os"
+	"sync"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector"
+	"github.com/iotaledger/goshimmer/packages/database"
+	"github.com/iotaledger/goshimmer/plugins/config"
+
+	"github.com/iotaledger/hive.go/events"
+	"github.com/iotaledger/hive.go/identity"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+const (
+	sequenceKey   = "seq"
+	totalMessages = 2000
+)
+
+func TestMessageFactory_BuildMessage(t *testing.T) {
+	// Set up DB for testing
+	dir, err := ioutil.TempDir("", t.Name())
+	require.NoError(t, err)
+	defer os.Remove(dir)
+	// use the tempdir for the database
+	config.Node.Set(database.CFG_DIRECTORY, dir)
+	db := database.GetBadgerInstance()
+
+	localIdentity := identity.GenerateLocalIdentity()
+	tipSelector := tipselector.New()
+
+	// keep track of sequence numbers
+	sequenceNumbers := sync.Map{}
+
+	msgFactory := New(db, localIdentity, tipSelector, []byte(sequenceKey))
+
+	// attach to event and count
+	countEvents := uint64(0)
+	msgFactory.Events.MessageConstructed.Attach(events.NewClosure(func(msg *message.Message) {
+		atomic.AddUint64(&countEvents, 1)
+	}))
+
+	t.Run("CheckProperties", func(t *testing.T) {
+		data := []byte("TestCheckProperties")
+		var p payload.Payload = NewMockPayload(data)
+		msg := msgFactory.IssuePayload(p)
+
+		assert.NotNil(t, msg.GetTrunkTransactionId())
+		assert.NotNil(t, msg.GetBranchTransactionId())
+
+		// time in range of 0.1 seconds
+		assert.InDelta(t, time.Now().UnixNano(), msg.IssuingTime().UnixNano(), 100000000)
+
+		// check payload
+		assert.Same(t, p, msg.GetPayload())
+		assert.Equal(t, data, msg.GetPayload().Bytes())
+
+		// check total events and sequence number
+		assert.EqualValues(t, 1, countEvents)
+		assert.EqualValues(t, 0, msg.SequenceNumber())
+
+		sequenceNumbers.Store(msg.SequenceNumber(), true)
+	})
+
+	// create messages in parallel
+	t.Run("ParallelCreation", func(t *testing.T) {
+		for i := 1; i < totalMessages; i++ {
+			t.Run("test", func(t *testing.T) {
+				t.Parallel()
+				data := []byte("TestCheckProperties")
+				var p payload.Payload = NewMockPayload(data)
+				msg := msgFactory.IssuePayload(p)
+
+				assert.NotNil(t, msg.GetTrunkTransactionId())
+				assert.NotNil(t, msg.GetBranchTransactionId())
+
+				// time in range of 0.1 seconds
+				assert.InDelta(t, time.Now().UnixNano(), msg.IssuingTime().UnixNano(), 100000000)
+
+				// check payload
+				assert.Same(t, p, msg.GetPayload())
+				assert.Equal(t, data, msg.GetPayload().Bytes())
+
+				sequenceNumbers.Store(msg.SequenceNumber(), true)
+			})
+		}
+	})
+
+	// check total events and sequence number
+	assert.EqualValues(t, totalMessages, countEvents)
+
+	max := uint64(0)
+	countSequence := 0
+	sequenceNumbers.Range(func(key, value interface{}) bool {
+		seq := key.(uint64)
+		val := value.(bool)
+		if val != true {
+			return false
+		}
+
+		// check for max sequence number
+		if seq > max {
+			max = seq
+		}
+		countSequence++
+		return true
+	})
+	assert.EqualValues(t, totalMessages-1, max)
+	assert.EqualValues(t, totalMessages, countSequence)
+
+	_ = db.Close()
+}
+
+type MockPayload struct {
+	data []byte
+	encoding.BinaryMarshaler
+	encoding.BinaryUnmarshaler
+}
+
+func NewMockPayload(data []byte) *MockPayload {
+	return &MockPayload{data: data}
+}
+
+func (m *MockPayload) Bytes() []byte {
+	return m.data
+}
+
+func (m *MockPayload) Type() payload.Type {
+	return payload.Type(0)
+}
+
+func (m *MockPayload) String() string {
+	return string(m.data)
+}
+
+func (m *MockPayload) Unmarshal(bytes []byte) error {
+	panic("implement me")
+}
diff --git a/packages/binary/tangle/model/message/payload/data/data.go b/packages/binary/messagelayer/payload/data.go
similarity index 53%
rename from packages/binary/tangle/model/message/payload/data/data.go
rename to packages/binary/messagelayer/payload/data.go
index 70efdcfcdaa7b98f2171108ba85cab469851d018..1325ed35a21d69784b4a134dac6d0dec92370b2f 100644
--- a/packages/binary/tangle/model/message/payload/data/data.go
+++ b/packages/binary/messagelayer/payload/data.go
@@ -1,27 +1,33 @@
-package data
+package payload
 
 import (
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/stringify"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
 )
 
+var DataType = Type(0)
+
 type Data struct {
-	payloadType payload.Type
+	payloadType Type
 	data        []byte
 }
 
-var Type = payload.Type(0)
-
-func New(data []byte) *Data {
+func NewData(data []byte) *Data {
 	return &Data{
-		payloadType: Type,
+		payloadType: DataType,
 		data:        data,
 	}
 }
 
-func FromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err error, consumedBytes int) {
+func DataFromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err error, consumedBytes int) {
+	marshalUtil := marshalutil.New(bytes)
+	result, err = ParseData(marshalUtil, optionalTargetObject...)
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func ParseData(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Data) (result *Data, err error) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -29,13 +35,10 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err e
 	case 1:
 		result = optionalTargetObject[0]
 	default:
-		panic("too many arguments in call to FromBytes")
+		panic("too many arguments in call to ParseData")
 	}
 
-	// initialize helper
-	marshalUtil := marshalutil.New(bytes)
-
-	// read data
+	// parse information
 	result.payloadType, err = marshalUtil.ReadUint32()
 	if err != nil {
 		return
@@ -49,17 +52,14 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err e
 		return
 	}
 
-	// return the number of bytes we processed
-	consumedBytes = marshalUtil.ReadOffset()
-
 	return
 }
 
-func (dataPayload *Data) GetType() payload.Type {
+func (dataPayload *Data) Type() Type {
 	return dataPayload.payloadType
 }
 
-func (dataPayload *Data) GetData() []byte {
+func (dataPayload *Data) Data() []byte {
 	return dataPayload.data
 }
 
@@ -69,7 +69,7 @@ func (dataPayload *Data) Bytes() []byte {
 	marshalUtil := marshalutil.New()
 
 	// marshal the payload specific information
-	marshalUtil.WriteUint32(dataPayload.GetType())
+	marshalUtil.WriteUint32(dataPayload.Type())
 	marshalUtil.WriteUint32(uint32(len(dataPayload.data)))
 	marshalUtil.WriteBytes(dataPayload.data[:])
 
@@ -77,28 +77,24 @@ func (dataPayload *Data) Bytes() []byte {
 	return marshalUtil.Bytes()
 }
 
-func (dataPayload *Data) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = FromBytes(data, dataPayload)
+func (dataPayload *Data) Unmarshal(data []byte) (err error) {
+	_, err, _ = DataFromBytes(data, dataPayload)
 
 	return
 }
 
-func (dataPayload *Data) MarshalBinary() (data []byte, err error) {
-	return dataPayload.Bytes(), nil
-}
-
 func (dataPayload *Data) String() string {
 	return stringify.Struct("Data",
-		stringify.StructField("data", string(dataPayload.GetData())),
+		stringify.StructField("data", string(dataPayload.Data())),
 	)
 }
 
-func GenericPayloadUnmarshalerFactory(payloadType payload.Type) payload.Unmarshaler {
-	return func(data []byte) (payload payload.Payload, err error) {
+func GenericPayloadUnmarshalerFactory(payloadType Type) Unmarshaler {
+	return func(data []byte) (payload Payload, err error) {
 		payload = &Data{
 			payloadType: payloadType,
 		}
-		err = payload.UnmarshalBinary(data)
+		err = payload.Unmarshal(data)
 
 		return
 	}
diff --git a/packages/binary/tangle/model/message/payload/id.go b/packages/binary/messagelayer/payload/id.go
similarity index 100%
rename from packages/binary/tangle/model/message/payload/id.go
rename to packages/binary/messagelayer/payload/id.go
diff --git a/packages/binary/tangle/model/message/payload/payload.go b/packages/binary/messagelayer/payload/payload.go
similarity index 83%
rename from packages/binary/tangle/model/message/payload/payload.go
rename to packages/binary/messagelayer/payload/payload.go
index 06d0d43912b9889493f0a8ba1d72cf7adaed5fec..97faf1711fc77b6e82babb5d9aebb5ac5578c739 100644
--- a/packages/binary/tangle/model/message/payload/payload.go
+++ b/packages/binary/messagelayer/payload/payload.go
@@ -1,17 +1,19 @@
 package payload
 
 import (
-	"encoding"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/hive.go/marshalutil"
 )
 
-type Payload interface {
-	encoding.BinaryMarshaler
-	encoding.BinaryUnmarshaler
+func init() {
+	SetGenericUnmarshalerFactory(GenericPayloadUnmarshalerFactory)
 
+	RegisterType(DataType, GenericPayloadUnmarshalerFactory(DataType))
+}
+
+type Payload interface {
+	Type() Type
 	Bytes() []byte
-	GetType() Type
+	Unmarshal(bytes []byte) error
 	String() string
 }
 
diff --git a/packages/binary/tangle/model/message/payload/type.go b/packages/binary/messagelayer/payload/type.go
similarity index 100%
rename from packages/binary/tangle/model/message/payload/type.go
rename to packages/binary/messagelayer/payload/type.go
diff --git a/packages/binary/tangle/model/message/payload/type_register.go b/packages/binary/messagelayer/payload/type_register.go
similarity index 100%
rename from packages/binary/tangle/model/message/payload/type_register.go
rename to packages/binary/messagelayer/payload/type_register.go
diff --git a/packages/binary/messagelayer/tangle/approver.go b/packages/binary/messagelayer/tangle/approver.go
new file mode 100644
index 0000000000000000000000000000000000000000..9c638ed1a6da843b5cc4f6f1d2bb077d0f0452ae
--- /dev/null
+++ b/packages/binary/messagelayer/tangle/approver.go
@@ -0,0 +1,164 @@
+package tangle
+
+import (
+	"github.com/iotaledger/hive.go/marshalutil"
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+)
+
+// region Approver /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+type Approver struct {
+	objectstorage.StorableObjectFlags
+
+	referencedMessageId message.Id
+	approvingMessageId  message.Id
+}
+
+func NewApprover(referencedTransaction message.Id, approvingTransaction message.Id) *Approver {
+	approver := &Approver{
+		referencedMessageId: referencedTransaction,
+		approvingMessageId:  approvingTransaction,
+	}
+
+	return approver
+}
+
+func ApproverFromBytes(bytes []byte, optionalTargetObject ...*Approver) (result *Approver, err error, consumedBytes int) {
+	marshalUtil := marshalutil.New(bytes)
+	result, err = ParseApprover(marshalUtil, optionalTargetObject...)
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func ParseApprover(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Approver) (result *Approver, err error) {
+	if parsedObject, parseErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) {
+		return ApproverFromStorageKey(data, optionalTargetObject...)
+	}); parseErr != nil {
+		err = parseErr
+
+		return
+	} else {
+		result = parsedObject.(*Approver)
+	}
+
+	if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parseErr error, parsedBytes int) {
+		parseErr, parsedBytes = result.UnmarshalObjectStorageValue(data)
+
+		return
+	}); err != nil {
+		return
+	}
+
+	return
+}
+
+func ApproverFromStorageKey(key []byte, optionalTargetObject ...*Approver) (result objectstorage.StorableObject, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Approver{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to ApproverFromStorageKey")
+	}
+
+	// parse the properties that are stored in the key
+	marshalUtil := marshalutil.New(key)
+	result.(*Approver).referencedMessageId, err = message.ParseId(marshalUtil)
+	if err != nil {
+		return
+	}
+	result.(*Approver).approvingMessageId, err = message.ParseId(marshalUtil)
+	if err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func (approver *Approver) ReferencedMessageId() message.Id {
+	return approver.approvingMessageId
+}
+
+func (approver *Approver) ApprovingMessageId() message.Id {
+	return approver.approvingMessageId
+}
+
+func (approver *Approver) Bytes() []byte {
+	return approver.ObjectStorageKey()
+}
+
+func (approver *Approver) String() string {
+	return stringify.Struct("Approver",
+		stringify.StructField("referencedMessageId", approver.ReferencedMessageId()),
+		stringify.StructField("approvingMessageId", approver.ApprovingMessageId()),
+	)
+}
+
+func (approver *Approver) ObjectStorageKey() []byte {
+	return marshalutil.New().
+		WriteBytes(approver.referencedMessageId.Bytes()).
+		WriteBytes(approver.approvingMessageId.Bytes()).
+		Bytes()
+}
+
+func (approver *Approver) ObjectStorageValue() (result []byte) {
+	return
+}
+
+func (approver *Approver) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	return
+}
+
+func (approver *Approver) Update(other objectstorage.StorableObject) {
+	panic("approvers should never be overwritten and only stored once to optimize IO")
+}
+
+// interface contract (allow the compiler to check if the implementation has all of the required methods).
+var _ objectstorage.StorableObject = &Approver{}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// region CachedApprover ///////////////////////////////////////////////////////////////////////////////////////////////
+
+type CachedApprover struct {
+	objectstorage.CachedObject
+}
+
+func (cachedApprover *CachedApprover) Unwrap() *Approver {
+	if untypedObject := cachedApprover.Get(); untypedObject == nil {
+		return nil
+	} else {
+		if typedObject := untypedObject.(*Approver); typedObject == nil || typedObject.IsDeleted() {
+			return nil
+		} else {
+			return typedObject
+		}
+	}
+}
+
+func (cachedApprover *CachedApprover) Consume(consumer func(approver *Approver)) (consumed bool) {
+	return cachedApprover.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*Approver))
+	})
+}
+
+type CachedApprovers []*CachedApprover
+
+func (cachedApprovers CachedApprovers) Consume(consumer func(approver *Approver)) (consumed bool) {
+	for _, cachedApprover := range cachedApprovers {
+		consumed = cachedApprover.Consume(func(approver *Approver) {
+			consumer(approver)
+		}) || consumed
+	}
+
+	return
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/tangle/events.go b/packages/binary/messagelayer/tangle/events.go
similarity index 74%
rename from packages/binary/tangle/events.go
rename to packages/binary/messagelayer/tangle/events.go
index ec056169f8f0ceddaaf3a3d4b344218973da00cc..4c0a6c5490fa3bf3209c0b9522e701d8efd545ed 100644
--- a/packages/binary/tangle/events.go
+++ b/packages/binary/messagelayer/tangle/events.go
@@ -3,8 +3,7 @@ package tangle
 import (
 	"github.com/iotaledger/hive.go/events"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 )
 
 type Events struct {
@@ -33,8 +32,8 @@ func transactionIdEvent(handler interface{}, params ...interface{}) {
 }
 
 func cachedTransactionEvent(handler interface{}, params ...interface{}) {
-	handler.(func(*message.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))(
-		params[0].(*message.CachedTransaction).Retain(),
-		params[1].(*transactionmetadata.CachedTransactionMetadata).Retain(),
+	handler.(func(*message.CachedMessage, *CachedMessageMetadata))(
+		params[0].(*message.CachedMessage).Retain(),
+		params[1].(*CachedMessageMetadata).Retain(),
 	)
 }
diff --git a/packages/binary/messagelayer/tangle/messagemetadata.go b/packages/binary/messagelayer/tangle/messagemetadata.go
new file mode 100644
index 0000000000000000000000000000000000000000..4fd028f1ed18709b38f7f2e807e89c47ffcee879
--- /dev/null
+++ b/packages/binary/messagelayer/tangle/messagemetadata.go
@@ -0,0 +1,171 @@
+package tangle
+
+import (
+	"sync"
+	"time"
+
+	"github.com/iotaledger/hive.go/marshalutil"
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+)
+
+type MessageMetadata struct {
+	objectstorage.StorableObjectFlags
+
+	transactionId      message.Id
+	receivedTime       time.Time
+	solid              bool
+	solidificationTime time.Time
+
+	solidMutex              sync.RWMutex
+	solidificationTimeMutex sync.RWMutex
+}
+
+func NewMessageMetadata(transactionId message.Id) *MessageMetadata {
+	return &MessageMetadata{
+		transactionId: transactionId,
+		receivedTime:  time.Now(),
+	}
+}
+
+func MessageMetadataFromBytes(bytes []byte) (result *MessageMetadata, err error, consumedBytes int) {
+	marshalUtil := marshalutil.New(bytes)
+	result, err = ParseMessageMetadata(marshalUtil)
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func ParseMessageMetadata(marshalUtil *marshalutil.MarshalUtil) (result *MessageMetadata, err error) {
+	if parsedObject, parseErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) {
+		return MessageMetadataFromStorageKey(data)
+	}); parseErr != nil {
+		err = parseErr
+
+		return
+	} else {
+		result = parsedObject.(*MessageMetadata)
+	}
+
+	if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parseErr error, parsedBytes int) {
+		parseErr, parsedBytes = result.UnmarshalObjectStorageValue(data)
+
+		return
+	}); err != nil {
+		return
+	}
+
+	return
+}
+
+func MessageMetadataFromStorageKey(key []byte) (result objectstorage.StorableObject, err error, consumedBytes int) {
+	result = &MessageMetadata{}
+
+	marshalUtil := marshalutil.New(key)
+	result.(*MessageMetadata).transactionId, err = message.ParseId(marshalUtil)
+	if err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func (messageMetadata *MessageMetadata) IsSolid() (result bool) {
+	messageMetadata.solidMutex.RLock()
+	result = messageMetadata.solid
+	messageMetadata.solidMutex.RUnlock()
+
+	return
+}
+
+func (messageMetadata *MessageMetadata) SetSolid(solid bool) (modified bool) {
+	messageMetadata.solidMutex.RLock()
+	if messageMetadata.solid != solid {
+		messageMetadata.solidMutex.RUnlock()
+
+		messageMetadata.solidMutex.Lock()
+		if messageMetadata.solid != solid {
+			messageMetadata.solid = solid
+			if solid {
+				messageMetadata.solidificationTimeMutex.Lock()
+				messageMetadata.solidificationTime = time.Now()
+				messageMetadata.solidificationTimeMutex.Unlock()
+			}
+
+			messageMetadata.SetModified()
+
+			modified = true
+		}
+		messageMetadata.solidMutex.Unlock()
+
+	} else {
+		messageMetadata.solidMutex.RUnlock()
+	}
+
+	return
+}
+
+func (messageMetadata *MessageMetadata) SoldificationTime() time.Time {
+	messageMetadata.solidificationTimeMutex.RLock()
+	defer messageMetadata.solidificationTimeMutex.RUnlock()
+
+	return messageMetadata.solidificationTime
+}
+
+func (messageMetadata *MessageMetadata) ObjectStorageKey() []byte {
+	return messageMetadata.transactionId.Bytes()
+}
+
+func (messageMetadata *MessageMetadata) ObjectStorageValue() []byte {
+	return marshalutil.New().
+		WriteTime(messageMetadata.receivedTime).
+		WriteTime(messageMetadata.solidificationTime).
+		WriteBool(messageMetadata.solid).
+		Bytes()
+}
+
+func (messageMetadata *MessageMetadata) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	marshalUtil := marshalutil.New(data)
+
+	if messageMetadata.receivedTime, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	if messageMetadata.solidificationTime, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	if messageMetadata.solid, err = marshalUtil.ReadBool(); err != nil {
+		return
+	}
+
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func (messageMetadata *MessageMetadata) Update(other objectstorage.StorableObject) {
+	panic("updates disabled")
+}
+
+var _ objectstorage.StorableObject = &MessageMetadata{}
+
+type CachedMessageMetadata struct {
+	objectstorage.CachedObject
+}
+
+func (cachedMessageMetadata *CachedMessageMetadata) Retain() *CachedMessageMetadata {
+	return &CachedMessageMetadata{cachedMessageMetadata.CachedObject.Retain()}
+}
+
+func (cachedMessageMetadata *CachedMessageMetadata) Unwrap() *MessageMetadata {
+	if untypedObject := cachedMessageMetadata.Get(); untypedObject == nil {
+		return nil
+	} else {
+		if typedObject := untypedObject.(*MessageMetadata); typedObject == nil || typedObject.IsDeleted() {
+			return nil
+		} else {
+			return typedObject
+		}
+	}
+}
diff --git a/packages/binary/messagelayer/tangle/missingmessage.go b/packages/binary/messagelayer/tangle/missingmessage.go
new file mode 100644
index 0000000000000000000000000000000000000000..e05d11f047ff21258bea8a55ebdee012dcc6b633
--- /dev/null
+++ b/packages/binary/messagelayer/tangle/missingmessage.go
@@ -0,0 +1,82 @@
+package tangle
+
+import (
+	"time"
+
+	"github.com/iotaledger/hive.go/marshalutil"
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+)
+
+type MissingMessage struct {
+	objectstorage.StorableObjectFlags
+
+	transactionId message.Id
+	missingSince  time.Time
+}
+
+func NewMissingMessage(transactionId message.Id) *MissingMessage {
+	return &MissingMessage{
+		transactionId: transactionId,
+		missingSince:  time.Now(),
+	}
+}
+
+func MissingMessageFromStorageKey(key []byte, optionalTargetObject ...*MissingMessage) (result objectstorage.StorableObject, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Approver{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to MissingMessageFromStorageKey")
+	}
+
+	// parse the properties that are stored in the key
+	marshalUtil := marshalutil.New(key)
+	result.(*MissingMessage).transactionId, err = message.ParseId(marshalUtil)
+	if err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func (missingMessage *MissingMessage) GetTransactionId() message.Id {
+	return missingMessage.transactionId
+}
+
+func (missingMessage *MissingMessage) GetMissingSince() time.Time {
+	return missingMessage.missingSince
+}
+
+func (missingMessage *MissingMessage) Update(other objectstorage.StorableObject) {
+	panic("missing transactions should never be overwritten and only stored once to optimize IO")
+}
+
+func (missingMessage *MissingMessage) ObjectStorageKey() []byte {
+	return missingMessage.transactionId[:]
+}
+
+func (missingMessage *MissingMessage) ObjectStorageValue() (result []byte) {
+	result, err := missingMessage.missingSince.MarshalBinary()
+	if err != nil {
+		panic(err)
+	}
+
+	return
+}
+
+func (missingMessage *MissingMessage) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	marshalUtil := marshalutil.New(data)
+	missingMessage.missingSince, err = marshalUtil.ReadTime()
+	if err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
diff --git a/packages/binary/tangle/tangle.go b/packages/binary/messagelayer/tangle/tangle.go
similarity index 55%
rename from packages/binary/tangle/tangle.go
rename to packages/binary/messagelayer/tangle/tangle.go
index ac2418daf5b7619ff920a2029a840f01af177b67..8dc9477f6362012cd685baff7b7a497be17dd234 100644
--- a/packages/binary/tangle/tangle.go
+++ b/packages/binary/messagelayer/tangle/tangle.go
@@ -7,11 +7,8 @@ import (
 	"github.com/dgraph-io/badger/v2"
 	"github.com/iotaledger/hive.go/types"
 
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/approver"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/missingtransaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 
 	"github.com/iotaledger/hive.go/async"
 	"github.com/iotaledger/hive.go/objectstorage"
@@ -25,26 +22,38 @@ const (
 type Tangle struct {
 	storageId []byte
 
-	transactionStorage         *objectstorage.ObjectStorage
-	transactionMetadataStorage *objectstorage.ObjectStorage
-	approverStorage            *objectstorage.ObjectStorage
-	missingTransactionsStorage *objectstorage.ObjectStorage
+	messageStorage         *objectstorage.ObjectStorage
+	messageMetadataStorage *objectstorage.ObjectStorage
+	approverStorage        *objectstorage.ObjectStorage
+	missingMessageStorage  *objectstorage.ObjectStorage
 
 	Events Events
 
-	storeTransactionsWorkerPool async.WorkerPool
-	solidifierWorkerPool        async.WorkerPool
-	cleanupWorkerPool           async.WorkerPool
+	storeMessageWorkerPool async.WorkerPool
+	solidifierWorkerPool   async.WorkerPool
+	cleanupWorkerPool      async.WorkerPool
+}
+
+func messageFactory(key []byte) (objectstorage.StorableObject, error, int) {
+	return message.StorableObjectFromKey(key)
+}
+
+func approverFactory(key []byte) (objectstorage.StorableObject, error, int) {
+	return ApproverFromStorageKey(key)
+}
+
+func missingMessageFactory(key []byte) (objectstorage.StorableObject, error, int) {
+	return MissingMessageFromStorageKey(key)
 }
 
 // Constructor for the tangle.
 func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
 	result = &Tangle{
-		storageId:                  storageId,
-		transactionStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransaction...), message.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
-		transactionMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransactionMetadata...), transactionmetadata.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
-		approverStorage:            objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), approver.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(message.IdLength, message.IdLength), objectstorage.LeakDetectionEnabled(false)),
-		missingTransactionsStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleMissingTransaction...), missingtransaction.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
+		storageId:              storageId,
+		messageStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.Layer0Message...), messageFactory, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
+		messageMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.Layer0MessageMetadata...), MessageMetadataFromStorageKey, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
+		approverStorage:        objectstorage.New(badgerInstance, append(storageId, storageprefix.Layer0Approvers...), approverFactory, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(message.IdLength, message.IdLength), objectstorage.LeakDetectionEnabled(false)),
+		missingMessageStorage:  objectstorage.New(badgerInstance, append(storageId, storageprefix.Layer0MissingMessage...), missingMessageFactory, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
 
 		Events: *newEvents(),
 	}
@@ -55,30 +64,30 @@ func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
 }
 
 // Returns the storage id of this tangle (can be used to create ontologies that follow the storage of the main tangle).
-func (tangle *Tangle) GetStorageId() []byte {
+func (tangle *Tangle) StorageId() []byte {
 	return tangle.storageId
 }
 
 // Attaches a new transaction to the tangle.
-func (tangle *Tangle) AttachTransaction(transaction *message.Transaction) {
-	tangle.storeTransactionsWorkerPool.Submit(func() { tangle.storeTransactionWorker(transaction) })
+func (tangle *Tangle) AttachMessage(transaction *message.Message) {
+	tangle.storeMessageWorkerPool.Submit(func() { tangle.storeMessageWorker(transaction) })
 }
 
 // Retrieves a transaction from the tangle.
-func (tangle *Tangle) GetTransaction(transactionId message.Id) *message.CachedTransaction {
-	return &message.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])}
+func (tangle *Tangle) Message(transactionId message.Id) *message.CachedMessage {
+	return &message.CachedMessage{CachedObject: tangle.messageStorage.Load(transactionId[:])}
 }
 
 // Retrieves the metadata of a transaction from the tangle.
-func (tangle *Tangle) GetTransactionMetadata(transactionId message.Id) *transactionmetadata.CachedTransactionMetadata {
-	return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(transactionId[:])}
+func (tangle *Tangle) MessageMetadata(transactionId message.Id) *CachedMessageMetadata {
+	return &CachedMessageMetadata{CachedObject: tangle.messageMetadataStorage.Load(transactionId[:])}
 }
 
-// GetApprovers retrieves the approvers of a transaction from the tangle.
-func (tangle *Tangle) GetApprovers(transactionId message.Id) approver.CachedApprovers {
-	approvers := make(approver.CachedApprovers, 0)
+// Approvers retrieves the approvers of a transaction from the tangle.
+func (tangle *Tangle) Approvers(transactionId message.Id) CachedApprovers {
+	approvers := make(CachedApprovers, 0)
 	tangle.approverStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool {
-		approvers = append(approvers, &approver.CachedApprover{CachedObject: cachedObject})
+		approvers = append(approvers, &CachedApprover{CachedObject: cachedObject})
 
 		return true
 	}, transactionId[:])
@@ -87,33 +96,33 @@ func (tangle *Tangle) GetApprovers(transactionId message.Id) approver.CachedAppr
 }
 
 // Deletes a transaction from the tangle (i.e. for local snapshots)
-func (tangle *Tangle) DeleteTransaction(transactionId message.Id) {
-	tangle.GetTransaction(transactionId).Consume(func(currentTransaction *message.Transaction) {
+func (tangle *Tangle) DeleteMessage(messageId message.Id) {
+	tangle.Message(messageId).Consume(func(currentTransaction *message.Message) {
 		trunkTransactionId := currentTransaction.GetTrunkTransactionId()
-		tangle.deleteApprover(trunkTransactionId, transactionId)
+		tangle.deleteApprover(trunkTransactionId, messageId)
 
 		branchTransactionId := currentTransaction.GetBranchTransactionId()
 		if branchTransactionId != trunkTransactionId {
-			tangle.deleteApprover(branchTransactionId, transactionId)
+			tangle.deleteApprover(branchTransactionId, messageId)
 		}
 
-		tangle.transactionMetadataStorage.Delete(transactionId[:])
-		tangle.transactionStorage.Delete(transactionId[:])
+		tangle.messageMetadataStorage.Delete(messageId[:])
+		tangle.messageStorage.Delete(messageId[:])
 
-		tangle.Events.TransactionRemoved.Trigger(transactionId)
+		tangle.Events.TransactionRemoved.Trigger(messageId)
 	})
 }
 
 // Marks the tangle as stopped, so it will not accept any new transactions (waits for all backgroundTasks to finish.
 func (tangle *Tangle) Shutdown() *Tangle {
-	tangle.storeTransactionsWorkerPool.ShutdownGracefully()
+	tangle.storeMessageWorkerPool.ShutdownGracefully()
 	tangle.solidifierWorkerPool.ShutdownGracefully()
 	tangle.cleanupWorkerPool.ShutdownGracefully()
 
-	tangle.transactionStorage.Shutdown()
-	tangle.transactionMetadataStorage.Shutdown()
+	tangle.messageStorage.Shutdown()
+	tangle.messageMetadataStorage.Shutdown()
 	tangle.approverStorage.Shutdown()
-	tangle.missingTransactionsStorage.Shutdown()
+	tangle.missingMessageStorage.Shutdown()
 
 	return tangle
 }
@@ -121,10 +130,10 @@ func (tangle *Tangle) Shutdown() *Tangle {
 // Resets the database and deletes all objects (good for testing or "node resets").
 func (tangle *Tangle) Prune() error {
 	for _, storage := range []*objectstorage.ObjectStorage{
-		tangle.transactionStorage,
-		tangle.transactionMetadataStorage,
+		tangle.messageStorage,
+		tangle.messageMetadataStorage,
 		tangle.approverStorage,
-		tangle.missingTransactionsStorage,
+		tangle.missingMessageStorage,
 	} {
 		if err := storage.Prune(); err != nil {
 			return err
@@ -135,55 +144,55 @@ func (tangle *Tangle) Prune() error {
 }
 
 // Worker that stores the transactions and calls the corresponding storage events"
-func (tangle *Tangle) storeTransactionWorker(tx *message.Transaction) {
+func (tangle *Tangle) storeMessageWorker(tx *message.Message) {
 	// store transaction
-	var cachedTransaction *message.CachedTransaction
-	if _tmp, transactionIsNew := tangle.transactionStorage.StoreIfAbsent(tx); !transactionIsNew {
+	var cachedTransaction *message.CachedMessage
+	if _tmp, transactionIsNew := tangle.messageStorage.StoreIfAbsent(tx); !transactionIsNew {
 		return
 	} else {
-		cachedTransaction = &message.CachedTransaction{CachedObject: _tmp}
+		cachedTransaction = &message.CachedMessage{CachedObject: _tmp}
 	}
 
 	// store transaction metadata
 	transactionId := tx.GetId()
-	cachedTransactionMetadata := &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Store(transactionmetadata.New(transactionId))}
+	cachedTransactionMetadata := &CachedMessageMetadata{CachedObject: tangle.messageMetadataStorage.Store(NewMessageMetadata(transactionId))}
 
 	// store trunk approver
 	trunkTransactionID := tx.GetTrunkTransactionId()
-	tangle.approverStorage.Store(approver.New(trunkTransactionID, transactionId)).Release()
+	tangle.approverStorage.Store(NewApprover(trunkTransactionID, transactionId)).Release()
 
 	// store branch approver
 	if branchTransactionID := tx.GetBranchTransactionId(); branchTransactionID != trunkTransactionID {
-		tangle.approverStorage.Store(approver.New(branchTransactionID, transactionId)).Release()
+		tangle.approverStorage.Store(NewApprover(branchTransactionID, transactionId)).Release()
 	}
 
 	// trigger events
-	if tangle.missingTransactionsStorage.DeleteIfPresent(transactionId[:]) {
+	if tangle.missingMessageStorage.DeleteIfPresent(transactionId[:]) {
 		tangle.Events.MissingTransactionReceived.Trigger(cachedTransaction, cachedTransactionMetadata)
 	}
 	tangle.Events.TransactionAttached.Trigger(cachedTransaction, cachedTransactionMetadata)
 
 	// check solidity
 	tangle.solidifierWorkerPool.Submit(func() {
-		tangle.solidifyTransactionWorker(cachedTransaction, cachedTransactionMetadata)
+		tangle.solidifyMessageWorker(cachedTransaction, cachedTransactionMetadata)
 	})
 }
 
 // Worker that solidifies the transactions (recursively from past to present).
-func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+func (tangle *Tangle) solidifyMessageWorker(cachedTransaction *message.CachedMessage, cachedTransactionMetadata *CachedMessageMetadata) {
 	isTransactionMarkedAsSolid := func(transactionId message.Id) bool {
 		if transactionId == message.EmptyId {
 			return true
 		}
 
-		transactionMetadataCached := tangle.GetTransactionMetadata(transactionId)
+		transactionMetadataCached := tangle.MessageMetadata(transactionId)
 		if transactionMetadata := transactionMetadataCached.Unwrap(); transactionMetadata == nil {
 			transactionMetadataCached.Release()
 
 			// if transaction is missing and was not reported as missing, yet
-			if cachedMissingTransaction, missingTransactionStored := tangle.missingTransactionsStorage.StoreIfAbsent(missingtransaction.New(transactionId)); missingTransactionStored {
+			if cachedMissingTransaction, missingTransactionStored := tangle.missingMessageStorage.StoreIfAbsent(NewMissingMessage(transactionId)); missingTransactionStored {
 				cachedMissingTransaction.Consume(func(object objectstorage.StorableObject) {
-					tangle.monitorMissingTransactionWorker(object.(*missingtransaction.MissingTransaction).GetTransactionId())
+					tangle.monitorMissingMessageWorker(object.(*MissingMessage).GetTransactionId())
 				})
 			}
 
@@ -198,7 +207,7 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.Cache
 		return true
 	}
 
-	isTransactionSolid := func(transaction *message.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool {
+	isTransactionSolid := func(transaction *message.Message, transactionMetadata *MessageMetadata) bool {
 		if transaction == nil || transaction.IsDeleted() {
 			return false
 		}
@@ -214,13 +223,13 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.Cache
 		return isTransactionMarkedAsSolid(transaction.GetTrunkTransactionId()) && isTransactionMarkedAsSolid(transaction.GetBranchTransactionId())
 	}
 
-	popElementsFromStack := func(stack *list.List) (*message.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) {
+	popElementsFromStack := func(stack *list.List) (*message.CachedMessage, *CachedMessageMetadata) {
 		currentSolidificationEntry := stack.Front()
 		currentCachedTransaction := currentSolidificationEntry.Value.([2]interface{})[0]
 		currentCachedTransactionMetadata := currentSolidificationEntry.Value.([2]interface{})[1]
 		stack.Remove(currentSolidificationEntry)
 
-		return currentCachedTransaction.(*message.CachedTransaction), currentCachedTransactionMetadata.(*transactionmetadata.CachedTransactionMetadata)
+		return currentCachedTransaction.(*message.CachedMessage), currentCachedTransactionMetadata.(*CachedMessageMetadata)
 	}
 
 	// initialize the stack
@@ -244,12 +253,12 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.Cache
 		if isTransactionSolid(currentTransaction, currentTransactionMetadata) && currentTransactionMetadata.SetSolid(true) {
 			tangle.Events.TransactionSolid.Trigger(currentCachedTransaction, currentCachedTransactionMetadata)
 
-			tangle.GetApprovers(currentTransaction.GetId()).Consume(func(approver *approver.Approver) {
-				approverTransactionId := approver.GetApprovingTransactionId()
+			tangle.Approvers(currentTransaction.GetId()).Consume(func(approver *Approver) {
+				approverTransactionId := approver.ReferencedMessageId()
 
 				solidificationStack.PushBack([2]interface{}{
-					tangle.GetTransaction(approverTransactionId),
-					tangle.GetTransactionMetadata(approverTransactionId),
+					tangle.Message(approverTransactionId),
+					tangle.MessageMetadata(approverTransactionId),
 				})
 			})
 		}
@@ -261,12 +270,12 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.Cache
 }
 
 // Worker that Monitors the missing transactions (by scheduling regular checks).
-func (tangle *Tangle) monitorMissingTransactionWorker(transactionId message.Id) {
+func (tangle *Tangle) monitorMissingMessageWorker(transactionId message.Id) {
 	var scheduleNextMissingCheck func(transactionId message.Id)
 	scheduleNextMissingCheck = func(transactionId message.Id) {
 		time.AfterFunc(MISSING_CHECK_INTERVAL, func() {
-			tangle.missingTransactionsStorage.Load(transactionId[:]).Consume(func(object objectstorage.StorableObject) {
-				missingTransaction := object.(*missingtransaction.MissingTransaction)
+			tangle.missingMessageStorage.Load(transactionId[:]).Consume(func(object objectstorage.StorableObject) {
+				missingTransaction := object.(*MissingMessage)
 
 				if time.Since(missingTransaction.GetMissingSince()) >= MAX_MISSING_TIME_BEFORE_CLEANUP {
 					tangle.cleanupWorkerPool.Submit(func() {
@@ -308,10 +317,10 @@ func (tangle *Tangle) deleteSubtangle(transactionId message.Id) {
 		currentTransactionId := currentStackEntry.Value.(message.Id)
 		cleanupStack.Remove(currentStackEntry)
 
-		tangle.DeleteTransaction(currentTransactionId)
+		tangle.DeleteMessage(currentTransactionId)
 
-		tangle.GetApprovers(currentTransactionId).Consume(func(approver *approver.Approver) {
-			approverId := approver.GetApprovingTransactionId()
+		tangle.Approvers(currentTransactionId).Consume(func(approver *Approver) {
+			approverId := approver.ReferencedMessageId()
 
 			if _, transactionProcessed := processedTransactions[approverId]; !transactionProcessed {
 				cleanupStack.PushBack(approverId)
diff --git a/packages/binary/messagelayer/tangle/tangle_test.go b/packages/binary/messagelayer/tangle/tangle_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b8ca052137454bd9c5c11d3d0f0b2c9295e793db
--- /dev/null
+++ b/packages/binary/messagelayer/tangle/tangle_test.go
@@ -0,0 +1,105 @@
+package tangle
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"testing"
+	"time"
+
+	"github.com/iotaledger/hive.go/events"
+	"github.com/iotaledger/hive.go/identity"
+	"github.com/stretchr/testify/require"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
+	"github.com/iotaledger/goshimmer/packages/database"
+	"github.com/iotaledger/goshimmer/plugins/config"
+)
+
+func BenchmarkTangle_AttachTransaction(b *testing.B) {
+	dir, err := ioutil.TempDir("", b.Name())
+	require.NoError(b, err)
+	defer os.Remove(dir)
+	// use the tempdir for the database
+	config.Node.Set(database.CFG_DIRECTORY, dir)
+
+	tangle := New(database.GetBadgerInstance(), []byte("TEST_BINARY_TANGLE"))
+	if err := tangle.Prune(); err != nil {
+		b.Error(err)
+
+		return
+	}
+
+	testIdentity := identity.GenerateLocalIdentity()
+
+	transactionBytes := make([]*message.Message, b.N)
+	for i := 0; i < b.N; i++ {
+		transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, testIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("some data")), testIdentity)
+		transactionBytes[i].Bytes()
+	}
+
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		tangle.AttachMessage(transactionBytes[i])
+	}
+
+	tangle.Shutdown()
+}
+
+func TestTangle_AttachTransaction(t *testing.T) {
+	dir, err := ioutil.TempDir("", t.Name())
+	require.NoError(t, err)
+	defer os.Remove(dir)
+	// use the tempdir for the database
+	config.Node.Set(database.CFG_DIRECTORY, dir)
+
+	messageTangle := New(database.GetBadgerInstance(), []byte("TEST_BINARY_TANGLE"))
+	if err := messageTangle.Prune(); err != nil {
+		t.Error(err)
+
+		return
+	}
+
+	messageTangle.Events.TransactionAttached.Attach(events.NewClosure(func(cachedTransaction *message.CachedMessage, cachedTransactionMetadata *CachedMessageMetadata) {
+		cachedTransactionMetadata.Release()
+
+		cachedTransaction.Consume(func(transaction *message.Message) {
+			fmt.Println("ATTACHED:", transaction.GetId())
+		})
+	}))
+
+	messageTangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedMessage, cachedTransactionMetadata *CachedMessageMetadata) {
+		cachedTransactionMetadata.Release()
+
+		cachedTransaction.Consume(func(transaction *message.Message) {
+			fmt.Println("SOLID:", transaction.GetId())
+		})
+	}))
+
+	messageTangle.Events.TransactionUnsolidifiable.Attach(events.NewClosure(func(transactionId message.Id) {
+		fmt.Println("UNSOLIDIFIABLE:", transactionId)
+	}))
+
+	messageTangle.Events.TransactionMissing.Attach(events.NewClosure(func(transactionId message.Id) {
+		fmt.Println("MISSING:", transactionId)
+	}))
+
+	messageTangle.Events.TransactionRemoved.Attach(events.NewClosure(func(transactionId message.Id) {
+		fmt.Println("REMOVED:", transactionId)
+	}))
+
+	localIdentity1 := identity.GenerateLocalIdentity()
+	localIdentity2 := identity.GenerateLocalIdentity()
+	newTransaction1 := message.New(message.EmptyId, message.EmptyId, localIdentity1.PublicKey(), time.Now(), 0, payload.NewData([]byte("some data")), localIdentity1)
+	newTransaction2 := message.New(newTransaction1.GetId(), newTransaction1.GetId(), localIdentity2.PublicKey(), time.Now(), 0, payload.NewData([]byte("some other data")), localIdentity2)
+
+	messageTangle.AttachMessage(newTransaction2)
+
+	time.Sleep(7 * time.Second)
+
+	messageTangle.AttachMessage(newTransaction1)
+
+	messageTangle.Shutdown()
+}
diff --git a/packages/binary/tangle/model/message/test/transaction_test.go b/packages/binary/messagelayer/test/transaction_test.go
similarity index 63%
rename from packages/binary/tangle/model/message/test/transaction_test.go
rename to packages/binary/messagelayer/test/transaction_test.go
index 357ec1ba1978454ed0581d9835c55e1f79972d00..9153245835a19fc56fbc11ebf2887efb17d27791 100644
--- a/packages/binary/tangle/model/message/test/transaction_test.go
+++ b/packages/binary/messagelayer/test/transaction_test.go
@@ -7,27 +7,25 @@ import (
 	"time"
 
 	"github.com/iotaledger/hive.go/async"
+	"github.com/iotaledger/hive.go/identity"
 
 	"github.com/panjf2000/ants/v2"
 
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
 )
 
 func BenchmarkVerifyDataTransactions(b *testing.B) {
 	var pool async.WorkerPool
 	pool.Tune(runtime.NumCPU() * 2)
 
+	localIdentity := identity.GenerateLocalIdentity()
+
 	transactions := make([][]byte, b.N)
 	for i := 0; i < b.N; i++ {
-		tx := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data")))
+		tx := message.New(message.EmptyId, message.EmptyId, localIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("some data")), localIdentity)
 
-		if marshaledTransaction, err := tx.MarshalBinary(); err != nil {
-			b.Error(err)
-		} else {
-			transactions[i] = marshaledTransaction
-		}
+		transactions[i] = tx.Bytes()
 	}
 
 	b.ResetTimer()
@@ -49,9 +47,11 @@ func BenchmarkVerifyDataTransactions(b *testing.B) {
 func BenchmarkVerifySignature(b *testing.B) {
 	pool, _ := ants.NewPool(80, ants.WithNonblocking(false))
 
-	transactions := make([]*message.Transaction, b.N)
+	localIdentity := identity.GenerateLocalIdentity()
+
+	transactions := make([]*message.Message, b.N)
 	for i := 0; i < b.N; i++ {
-		transactions[i] = message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("test")))
+		transactions[i] = message.New(message.EmptyId, message.EmptyId, localIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("test")), localIdentity)
 		transactions[i].Bytes()
 	}
 
diff --git a/packages/binary/tangle/tipselector/events.go b/packages/binary/messagelayer/tipselector/events.go
similarity index 81%
rename from packages/binary/tangle/tipselector/events.go
rename to packages/binary/messagelayer/tipselector/events.go
index 21f6276eda365408bae0bb38f0b71d7a891fcb0a..432f2914018bbdfdeec89238c2195b4d31c7ee9f 100644
--- a/packages/binary/tangle/tipselector/events.go
+++ b/packages/binary/messagelayer/tipselector/events.go
@@ -1,8 +1,9 @@
 package tipselector
 
 import (
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/hive.go/events"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 )
 
 type Events struct {
diff --git a/packages/binary/tangle/tipselector/tipselector.go b/packages/binary/messagelayer/tipselector/tipselector.go
similarity index 92%
rename from packages/binary/tangle/tipselector/tipselector.go
rename to packages/binary/messagelayer/tipselector/tipselector.go
index f98d1f50eb3b09e739333c03dc434a084ab36a21..84e447d8661d165f0a0b390937e44ae9274d65fe 100644
--- a/packages/binary/tangle/tipselector/tipselector.go
+++ b/packages/binary/messagelayer/tipselector/tipselector.go
@@ -1,9 +1,10 @@
 package tipselector
 
 import (
-	"github.com/iotaledger/goshimmer/packages/binary/datastructure"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/hive.go/events"
+
+	"github.com/iotaledger/goshimmer/packages/binary/datastructure"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 )
 
 type TipSelector struct {
@@ -21,7 +22,7 @@ func New() *TipSelector {
 	}
 }
 
-func (tipSelector *TipSelector) AddTip(transaction *message.Transaction) {
+func (tipSelector *TipSelector) AddTip(transaction *message.Message) {
 	transactionId := transaction.GetId()
 	if tipSelector.tips.Set(transactionId, transactionId) {
 		tipSelector.Events.TipAdded.Trigger(transactionId)
diff --git a/packages/binary/tangle/tipselector/tipselector_test.go b/packages/binary/messagelayer/tipselector/tipselector_test.go
similarity index 60%
rename from packages/binary/tangle/tipselector/tipselector_test.go
rename to packages/binary/messagelayer/tipselector/tipselector_test.go
index f089f29d993401ab1d3d98b906b7e74e853671f2..929d515c4555b5f08c4b5bf713f13f64292cdcde 100644
--- a/packages/binary/tangle/tipselector/tipselector_test.go
+++ b/packages/binary/messagelayer/tipselector/tipselector_test.go
@@ -4,11 +4,11 @@ import (
 	"testing"
 	"time"
 
+	"github.com/iotaledger/hive.go/identity"
 	"github.com/stretchr/testify/assert"
 
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
 )
 
 func Test(t *testing.T) {
@@ -21,7 +21,8 @@ func Test(t *testing.T) {
 	assert.Equal(t, message.EmptyId, branch1)
 
 	// create a transaction and attach it
-	transaction1 := message.New(trunk1, branch1, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
+	localIdentity1 := identity.GenerateLocalIdentity()
+	transaction1 := message.New(trunk1, branch1, localIdentity1.PublicKey(), time.Now(), 0, payload.NewData([]byte("testtransaction")), localIdentity1)
 	tipSelector.AddTip(transaction1)
 
 	// check if the tip shows up in the tip count
@@ -33,15 +34,17 @@ func Test(t *testing.T) {
 	assert.Equal(t, transaction1.GetId(), branch2)
 
 	// create a 2nd transaction and attach it
-	transaction2 := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
+	localIdentity2 := identity.GenerateLocalIdentity()
+	transaction2 := message.New(message.EmptyId, message.EmptyId, localIdentity2.PublicKey(), time.Now(), 0, payload.NewData([]byte("testtransaction")), localIdentity2)
 	tipSelector.AddTip(transaction2)
 
 	// check if the tip shows up in the tip count
 	assert.Equal(t, 2, tipSelector.GetTipCount())
 
 	// attach a transaction to our two tips
+	localIdentity3 := identity.GenerateLocalIdentity()
 	trunk3, branch3 := tipSelector.GetTips()
-	transaction3 := message.New(trunk3, branch3, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
+	transaction3 := message.New(trunk3, branch3, localIdentity3.PublicKey(), time.Now(), 0, payload.NewData([]byte("testtransaction")), localIdentity3)
 	tipSelector.AddTip(transaction3)
 
 	// check if the tip shows replaces the current tips
diff --git a/packages/binary/tangle/transactionparser/builtinfilters/recently_seen_bytes_filter.go b/packages/binary/messagelayer/transactionparser/builtinfilters/recently_seen_bytes_filter.go
similarity index 100%
rename from packages/binary/tangle/transactionparser/builtinfilters/recently_seen_bytes_filter.go
rename to packages/binary/messagelayer/transactionparser/builtinfilters/recently_seen_bytes_filter.go
diff --git a/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go b/packages/binary/messagelayer/transactionparser/builtinfilters/transaction_signature_filter.go
similarity index 74%
rename from packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go
rename to packages/binary/messagelayer/transactionparser/builtinfilters/transaction_signature_filter.go
index f6d64f3eb25ae0b828b2195540f5235912111295..347c7c31dd34a0df8baf169bac65f3f6af03669b 100644
--- a/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go
+++ b/packages/binary/messagelayer/transactionparser/builtinfilters/transaction_signature_filter.go
@@ -7,14 +7,14 @@ import (
 	"github.com/iotaledger/hive.go/async"
 	"github.com/iotaledger/hive.go/autopeering/peer"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 )
 
 var ErrInvalidSignature = fmt.Errorf("invalid signature")
 
 type TransactionSignatureFilter struct {
-	onAcceptCallback func(tx *message.Transaction, peer *peer.Peer)
-	onRejectCallback func(tx *message.Transaction, err error, peer *peer.Peer)
+	onAcceptCallback func(tx *message.Message, peer *peer.Peer)
+	onRejectCallback func(tx *message.Message, err error, peer *peer.Peer)
 	workerPool       async.WorkerPool
 
 	onAcceptCallbackMutex sync.RWMutex
@@ -27,7 +27,7 @@ func NewTransactionSignatureFilter() (result *TransactionSignatureFilter) {
 	return
 }
 
-func (filter *TransactionSignatureFilter) Filter(tx *message.Transaction, peer *peer.Peer) {
+func (filter *TransactionSignatureFilter) Filter(tx *message.Message, peer *peer.Peer) {
 	filter.workerPool.Submit(func() {
 		if tx.VerifySignature() {
 			filter.getAcceptCallback()(tx, peer)
@@ -37,13 +37,13 @@ func (filter *TransactionSignatureFilter) Filter(tx *message.Transaction, peer *
 	})
 }
 
-func (filter *TransactionSignatureFilter) OnAccept(callback func(tx *message.Transaction, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) OnAccept(callback func(tx *message.Message, peer *peer.Peer)) {
 	filter.onAcceptCallbackMutex.Lock()
 	filter.onAcceptCallback = callback
 	filter.onAcceptCallbackMutex.Unlock()
 }
 
-func (filter *TransactionSignatureFilter) OnReject(callback func(tx *message.Transaction, err error, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) OnReject(callback func(tx *message.Message, err error, peer *peer.Peer)) {
 	filter.onRejectCallbackMutex.Lock()
 	filter.onRejectCallback = callback
 	filter.onRejectCallbackMutex.Unlock()
@@ -53,7 +53,7 @@ func (filter *TransactionSignatureFilter) Shutdown() {
 	filter.workerPool.ShutdownGracefully()
 }
 
-func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *message.Transaction, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *message.Message, peer *peer.Peer)) {
 	filter.onAcceptCallbackMutex.RLock()
 	result = filter.onAcceptCallback
 	filter.onAcceptCallbackMutex.RUnlock()
@@ -61,7 +61,7 @@ func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *m
 	return
 }
 
-func (filter *TransactionSignatureFilter) getRejectCallback() (result func(tx *message.Transaction, err error, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) getRejectCallback() (result func(tx *message.Message, err error, peer *peer.Peer)) {
 	filter.onRejectCallbackMutex.RLock()
 	result = filter.onRejectCallback
 	filter.onRejectCallbackMutex.RUnlock()
diff --git a/packages/binary/tangle/transactionparser/bytes_filter.go b/packages/binary/messagelayer/transactionparser/bytes_filter.go
similarity index 100%
rename from packages/binary/tangle/transactionparser/bytes_filter.go
rename to packages/binary/messagelayer/transactionparser/bytes_filter.go
diff --git a/packages/binary/tangle/transactionparser/events.go b/packages/binary/messagelayer/transactionparser/events.go
similarity index 100%
rename from packages/binary/tangle/transactionparser/events.go
rename to packages/binary/messagelayer/transactionparser/events.go
diff --git a/packages/binary/messagelayer/transactionparser/transaction_filter.go b/packages/binary/messagelayer/transactionparser/transaction_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..0cb631ae596c4d41ef44d5252bc47a265974f599
--- /dev/null
+++ b/packages/binary/messagelayer/transactionparser/transaction_filter.go
@@ -0,0 +1,14 @@
+package transactionparser
+
+import (
+	"github.com/iotaledger/hive.go/autopeering/peer"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+)
+
+type TransactionFilter interface {
+	Filter(tx *message.Message, peer *peer.Peer)
+	OnAccept(callback func(tx *message.Message, peer *peer.Peer))
+	OnReject(callback func(tx *message.Message, err error, peer *peer.Peer))
+	Shutdown()
+}
diff --git a/packages/binary/tangle/transactionparser/transactionparser.go b/packages/binary/messagelayer/transactionparser/transactionparser.go
similarity index 90%
rename from packages/binary/tangle/transactionparser/transactionparser.go
rename to packages/binary/messagelayer/transactionparser/transactionparser.go
index d684efc3283a5c58a168c11cc0be8dbe1c42fdda..6e54add7ea0c3dcafa1474550b7fc2a66aef3a3e 100644
--- a/packages/binary/tangle/transactionparser/transactionparser.go
+++ b/packages/binary/messagelayer/transactionparser/transactionparser.go
@@ -3,11 +3,10 @@ package transactionparser
 import (
 	"sync"
 
-	"github.com/iotaledger/hive.go/autopeering/peer"
-
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser/builtinfilters"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/transactionparser/builtinfilters"
 
+	"github.com/iotaledger/hive.go/autopeering/peer"
 	"github.com/iotaledger/hive.go/events"
 	"github.com/iotaledger/hive.go/typeutils"
 )
@@ -33,10 +32,10 @@ func New() (result *TransactionParser) {
 				handler.(func([]byte, error, *peer.Peer))(params[0].([]byte), params[1].(error), params[2].(*peer.Peer))
 			}),
 			TransactionParsed: events.NewEvent(func(handler interface{}, params ...interface{}) {
-				handler.(func(*message.Transaction, *peer.Peer))(params[0].(*message.Transaction), params[1].(*peer.Peer))
+				handler.(func(*message.Message, *peer.Peer))(params[0].(*message.Message), params[1].(*peer.Peer))
 			}),
 			TransactionRejected: events.NewEvent(func(handler interface{}, params ...interface{}) {
-				handler.(func(*message.Transaction, error, *peer.Peer))(params[0].(*message.Transaction), params[1].(error), params[2].(*peer.Peer))
+				handler.(func(*message.Message, error, *peer.Peer))(params[0].(*message.Message), params[1].(error), params[2].(*peer.Peer))
 			}),
 		},
 	}
@@ -121,13 +120,13 @@ func (transactionParser *TransactionParser) setupTransactionsFilterDataFlow() {
 		numberOfTransactionFilters := len(transactionParser.transactionFilters)
 		for i := 0; i < numberOfTransactionFilters; i++ {
 			if i == numberOfTransactionFilters-1 {
-				transactionParser.transactionFilters[i].OnAccept(func(tx *message.Transaction, peer *peer.Peer) {
+				transactionParser.transactionFilters[i].OnAccept(func(tx *message.Message, peer *peer.Peer) {
 					transactionParser.Events.TransactionParsed.Trigger(tx, peer)
 				})
 			} else {
 				transactionParser.transactionFilters[i].OnAccept(transactionParser.transactionFilters[i+1].Filter)
 			}
-			transactionParser.transactionFilters[i].OnReject(func(tx *message.Transaction, err error, peer *peer.Peer) {
+			transactionParser.transactionFilters[i].OnReject(func(tx *message.Message, err error, peer *peer.Peer) {
 				transactionParser.Events.TransactionRejected.Trigger(tx, err, peer)
 			})
 		}
diff --git a/packages/binary/tangle/transactionparser/transactionparser_test.go b/packages/binary/messagelayer/transactionparser/transactionparser_test.go
similarity index 53%
rename from packages/binary/tangle/transactionparser/transactionparser_test.go
rename to packages/binary/messagelayer/transactionparser/transactionparser_test.go
index 9a5f64138ab321ef06f4c9844e1f137d0b0d6bc0..b1493d5cc7690d8ffe121873d352f268f84722e0 100644
--- a/packages/binary/tangle/transactionparser/transactionparser_test.go
+++ b/packages/binary/messagelayer/transactionparser/transactionparser_test.go
@@ -7,14 +7,15 @@ import (
 	"time"
 
 	"github.com/iotaledger/hive.go/events"
+	"github.com/iotaledger/hive.go/identity"
 
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
 )
 
 func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) {
-	txBytes := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"))).Bytes()
+	localIdentity := identity.GenerateLocalIdentity()
+	txBytes := message.New(message.EmptyId, message.EmptyId, localIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("Test")), localIdentity).Bytes()
 	txParser := New()
 
 	b.ResetTimer()
@@ -28,8 +29,9 @@ func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) {
 
 func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) {
 	transactionBytes := make([][]byte, b.N)
+	localIdentity := identity.GenerateLocalIdentity()
 	for i := 0; i < b.N; i++ {
-		transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"+strconv.Itoa(i)))).Bytes()
+		transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, localIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("Test"+strconv.Itoa(i))), localIdentity).Bytes()
 	}
 
 	txParser := New()
@@ -44,12 +46,13 @@ func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) {
 }
 
 func TestTransactionParser_ParseTransaction(t *testing.T) {
-	tx := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test")))
+	localIdentity := identity.GenerateLocalIdentity()
+	tx := message.New(message.EmptyId, message.EmptyId, localIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("Test")), localIdentity)
 
 	txParser := New()
 	txParser.Parse(tx.Bytes(), nil)
 
-	txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *message.Transaction) {
+	txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *message.Message) {
 		fmt.Println("PARSED!!!")
 	}))
 
diff --git a/packages/binary/tangle/transactionrequester/constants.go b/packages/binary/messagelayer/transactionrequester/constants.go
similarity index 100%
rename from packages/binary/tangle/transactionrequester/constants.go
rename to packages/binary/messagelayer/transactionrequester/constants.go
diff --git a/packages/binary/tangle/transactionrequester/events.go b/packages/binary/messagelayer/transactionrequester/events.go
similarity index 100%
rename from packages/binary/tangle/transactionrequester/events.go
rename to packages/binary/messagelayer/transactionrequester/events.go
diff --git a/packages/binary/tangle/transactionrequester/options.go b/packages/binary/messagelayer/transactionrequester/options.go
similarity index 100%
rename from packages/binary/tangle/transactionrequester/options.go
rename to packages/binary/messagelayer/transactionrequester/options.go
diff --git a/packages/binary/tangle/transactionrequester/transactionrequester.go b/packages/binary/messagelayer/transactionrequester/transactionrequester.go
similarity index 96%
rename from packages/binary/tangle/transactionrequester/transactionrequester.go
rename to packages/binary/messagelayer/transactionrequester/transactionrequester.go
index 2016298c71b42658d3b832c2040285118de61175..a81993a83c048ac90cefd8a107e77bffca06cf11 100644
--- a/packages/binary/tangle/transactionrequester/transactionrequester.go
+++ b/packages/binary/messagelayer/transactionrequester/transactionrequester.go
@@ -7,7 +7,7 @@ import (
 	"github.com/iotaledger/hive.go/async"
 	"github.com/iotaledger/hive.go/events"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 )
 
 type TransactionRequester struct {
diff --git a/packages/binary/signature/ed25119/ed25119.go b/packages/binary/signature/ed25119/ed25119.go
deleted file mode 100644
index d4b65997bf4657ceeaa43427a8f0b43a58fc2dfe..0000000000000000000000000000000000000000
--- a/packages/binary/signature/ed25119/ed25119.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package ed25119
-
-import (
-	"crypto/rand"
-
-	"github.com/oasislabs/ed25519"
-)
-
-func GenerateKeyPair() (keyPair KeyPair) {
-	if public, private, err := ed25519.GenerateKey(rand.Reader); err != nil {
-		panic(err)
-	} else {
-		copy(keyPair.PrivateKey[:], private)
-		copy(keyPair.PublicKey[:], public)
-
-		return
-	}
-}
diff --git a/packages/binary/signature/ed25119/key_pair.go b/packages/binary/signature/ed25119/key_pair.go
deleted file mode 100644
index ad0c31bbe6ef5e06df08138dd66282e843322af4..0000000000000000000000000000000000000000
--- a/packages/binary/signature/ed25119/key_pair.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package ed25119
-
-type KeyPair struct {
-	PrivateKey PrivateKey
-	PublicKey  PublicKey
-}
diff --git a/packages/binary/signature/ed25119/private_key.go b/packages/binary/signature/ed25119/private_key.go
deleted file mode 100644
index 9885a64325cab2c1dccbb84ece58d1b7302856d2..0000000000000000000000000000000000000000
--- a/packages/binary/signature/ed25119/private_key.go
+++ /dev/null
@@ -1,31 +0,0 @@
-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))
-
-	return
-}
-
-const PrivateKeySize = 64
diff --git a/packages/binary/signature/ed25119/public_key.go b/packages/binary/signature/ed25119/public_key.go
deleted file mode 100644
index f050f6ff274fc6bbb19f6a9dd1c105465fea551c..0000000000000000000000000000000000000000
--- a/packages/binary/signature/ed25119/public_key.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package ed25119
-
-import (
-	"errors"
-	"fmt"
-
-	"github.com/oasislabs/ed25519"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-)
-
-type PublicKey [PublicKeySize]byte
-
-func PublicKeyFromBytes(bytes []byte) (result PublicKey, err error, consumedBytes int) {
-	if len(bytes) < PublicKeySize {
-		err = fmt.Errorf("bytes too short")
-
-		return
-	}
-
-	copy(result[:], bytes)
-
-	consumedBytes = PublicKeySize
-
-	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")
-	}
-
-	copy(publicKey[:], bytes[:])
-
-	return
-}
-
-const PublicKeySize = 32
diff --git a/packages/binary/signature/ed25119/signature.go b/packages/binary/signature/ed25119/signature.go
deleted file mode 100644
index 01a6750f7b62cb1c9b5653ef272bd9d4fee3c1b9..0000000000000000000000000000000000000000
--- a/packages/binary/signature/ed25119/signature.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package ed25119
-
-import (
-	"errors"
-	"fmt"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-)
-
-type Signature [SignatureSize]byte
-
-func SignatureFromBytes(bytes []byte) (result Signature, err error, consumedBytes int) {
-	if len(bytes) < SignatureSize {
-		err = fmt.Errorf("bytes too short")
-
-		return
-	}
-
-	copy(result[:SignatureSize], bytes)
-
-	consumedBytes = SignatureSize
-
-	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")
-	}
-
-	copy(signature[:], bytes[:])
-
-	return
-}
-
-const SignatureSize = 64
diff --git a/packages/binary/spammer/spammer.go b/packages/binary/spammer/spammer.go
index ea0efc5ac16b3f4ca84dcf3f4740ca7cec0a3f26..34d1b98b5b85bd0efa2972862854043c889e1c78 100644
--- a/packages/binary/spammer/spammer.go
+++ b/packages/binary/spammer/spammer.go
@@ -6,26 +6,22 @@ import (
 
 	"github.com/iotaledger/hive.go/types"
 
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/tipselector"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/messagefactory"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
 )
 
 type Spammer struct {
-	transactionParser *transactionparser.TransactionParser
-	tipSelector       *tipselector.TipSelector
+	messageFactory *messagefactory.MessageFactory
 
 	processId      int64
 	shutdownSignal chan types.Empty
 }
 
-func New(transactionParser *transactionparser.TransactionParser, tipSelector *tipselector.TipSelector) *Spammer {
+func New(messageFactory *messagefactory.MessageFactory) *Spammer {
 	return &Spammer{
-		shutdownSignal:    make(chan types.Empty),
-		transactionParser: transactionParser,
-		tipSelector:       tipSelector,
+		messageFactory: messageFactory,
+
+		shutdownSignal: make(chan types.Empty),
 	}
 }
 
@@ -33,16 +29,11 @@ func (spammer *Spammer) Start(tps int) {
 	go spammer.run(tps, atomic.AddInt64(&spammer.processId, 1))
 }
 
-func (spammer *Spammer) Burst(transactions int) {
-	go spammer.sendBurst(transactions, atomic.AddInt64(&spammer.processId, 1))
-}
-
 func (spammer *Spammer) Shutdown() {
 	atomic.AddInt64(&spammer.processId, 1)
 }
 
 func (spammer *Spammer) run(tps int, processId int64) {
-	spammingIdentity := ed25119.GenerateKeyPair()
 	currentSentCounter := 0
 	start := time.Now()
 
@@ -51,11 +42,7 @@ func (spammer *Spammer) run(tps int, processId int64) {
 			return
 		}
 
-		trunkTransactionId, branchTransactionId := spammer.tipSelector.GetTips()
-		spammer.transactionParser.Parse(
-			message.New(trunkTransactionId, branchTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM"))).Bytes(),
-			nil,
-		)
+		spammer.messageFactory.IssuePayload(payload.NewData([]byte("SPAM")))
 
 		currentSentCounter++
 
@@ -71,28 +58,3 @@ func (spammer *Spammer) run(tps int, processId int64) {
 		}
 	}
 }
-
-func (spammer *Spammer) sendBurst(transactions int, processId int64) {
-	spammingIdentity := ed25119.GenerateKeyPair()
-
-	previousTransactionId := message.EmptyId
-
-	burstBuffer := make([][]byte, transactions)
-	for i := 0; i < transactions; i++ {
-		if atomic.LoadInt64(&spammer.processId) != processId {
-			return
-		}
-
-		spamTransaction := message.New(previousTransactionId, previousTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM")))
-		previousTransactionId = spamTransaction.GetId()
-		burstBuffer[i] = spamTransaction.Bytes()
-	}
-
-	for i := 0; i < transactions; i++ {
-		if atomic.LoadInt64(&spammer.processId) != processId {
-			return
-		}
-
-		spammer.transactionParser.Parse(burstBuffer[i], nil)
-	}
-}
diff --git a/packages/binary/storageprefix/storageprefix.go b/packages/binary/storageprefix/storageprefix.go
index d288fb2db1a15ff0d9245d8c2f8748d401534acc..efafc56ee9dd4aeefa34fe8db69ee583f8170ec3 100644
--- a/packages/binary/storageprefix/storageprefix.go
+++ b/packages/binary/storageprefix/storageprefix.go
@@ -3,18 +3,20 @@ package storageprefix
 var (
 	MainNet = []byte{0}
 
-	TangleTransaction         = []byte{1}
-	TangleTransactionMetadata = []byte{2}
-	TangleApprovers           = []byte{3}
-	TangleMissingTransaction  = []byte{4}
+	Layer0Message         = []byte{1}
+	Layer0MessageMetadata = []byte{2}
+	Layer0Approvers       = []byte{3}
+	Layer0MissingMessage  = []byte{4}
 
 	ValueTransferPayload         = []byte{5}
 	ValueTransferPayloadMetadata = []byte{6}
 	ValueTransferApprover        = []byte{7}
 	ValueTransferMissingPayload  = []byte{8}
+	ValueTransferAttachment      = []byte{9}
+	ValueTransferConsumer        = []byte{10}
 
-	LedgerStateTransferOutput        = []byte{9}
-	LedgerStateTransferOutputBooking = []byte{10}
-	LedgerStateReality               = []byte{11}
-	LedgerStateConflictSet           = []byte{12}
+	LedgerStateTransferOutput        = []byte{11}
+	LedgerStateTransferOutputBooking = []byte{12}
+	LedgerStateReality               = []byte{13}
+	LedgerStateConflictSet           = []byte{14}
 )
diff --git a/packages/binary/tangle/model/approver/approver.go b/packages/binary/tangle/model/approver/approver.go
deleted file mode 100644
index 23c0842be18b328762caafb230a0025868b4ca8b..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/approver/approver.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package approver
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-)
-
-type Approver struct {
-	objectstorage.StorableObjectFlags
-
-	storageKey            []byte
-	referencedTransaction message.Id
-	approvingTransaction  message.Id
-}
-
-func New(referencedTransaction message.Id, approvingTransaction message.Id) *Approver {
-	approver := &Approver{
-		storageKey:            make([]byte, message.IdLength+message.IdLength),
-		referencedTransaction: referencedTransaction,
-		approvingTransaction:  approvingTransaction,
-	}
-
-	copy(approver.storageKey[:message.IdLength], referencedTransaction[:])
-	copy(approver.storageKey[message.IdLength:], approvingTransaction[:])
-
-	return approver
-}
-
-func FromStorage(id []byte) (result objectstorage.StorableObject) {
-	approver := &Approver{
-		storageKey: make([]byte, message.IdLength+message.IdLength),
-	}
-	copy(approver.referencedTransaction[:], id[:message.IdLength])
-	copy(approver.approvingTransaction[:], id[message.IdLength:])
-	copy(approver.storageKey, id)
-
-	return approver
-}
-
-func (approver *Approver) GetStorageKey() []byte {
-	return approver.storageKey
-}
-
-func (approver *Approver) GetApprovingTransactionId() message.Id {
-	return approver.approvingTransaction
-}
-
-func (approver *Approver) Update(other objectstorage.StorableObject) {
-	panic("approvers should never be overwritten and only stored once to optimize IO")
-}
-
-func (approver *Approver) MarshalBinary() (result []byte, err error) {
-	return
-}
-
-func (approver *Approver) UnmarshalBinary(data []byte) (err error) {
-	return
-}
diff --git a/packages/binary/tangle/model/approver/cached_approver.go b/packages/binary/tangle/model/approver/cached_approver.go
deleted file mode 100644
index 68fe1bcba8559b730f5fad4e2d2d7fbdbea405a4..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/approver/cached_approver.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package approver
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-type CachedApprover struct {
-	objectstorage.CachedObject
-}
-
-func (cachedApprover *CachedApprover) Unwrap() *Approver {
-	if untypedObject := cachedApprover.Get(); untypedObject == nil {
-		return nil
-	} else {
-		if typedObject := untypedObject.(*Approver); typedObject == nil || typedObject.IsDeleted() {
-			return nil
-		} else {
-			return typedObject
-		}
-	}
-}
-
-type CachedApprovers []*CachedApprover
-
-func (cachedApprovers CachedApprovers) Consume(consumer func(approver *Approver)) (consumed bool) {
-	for _, cachedApprover := range cachedApprovers {
-		consumed = cachedApprover.Consume(func(object objectstorage.StorableObject) {
-			consumer(object.(*Approver))
-		}) || consumed
-	}
-
-	return
-}
diff --git a/packages/binary/tangle/model/message/cached_transaction.go b/packages/binary/tangle/model/message/cached_transaction.go
deleted file mode 100644
index 8657ecb87618a106654fbba2acb861594406eb35..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/message/cached_transaction.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package message
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-type CachedTransaction struct {
-	objectstorage.CachedObject
-}
-
-func (cachedTransaction *CachedTransaction) Retain() *CachedTransaction {
-	return &CachedTransaction{cachedTransaction.CachedObject.Retain()}
-}
-
-func (cachedTransaction *CachedTransaction) Consume(consumer func(transaction *Transaction)) bool {
-	return cachedTransaction.CachedObject.Consume(func(object objectstorage.StorableObject) {
-		consumer(object.(*Transaction))
-	})
-}
-
-func (cachedTransaction *CachedTransaction) Unwrap() *Transaction {
-	if untypedTransaction := cachedTransaction.Get(); untypedTransaction == nil {
-		return nil
-	} else {
-		if typeCastedTransaction := untypedTransaction.(*Transaction); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
-			return nil
-		} else {
-			return typeCastedTransaction
-		}
-	}
-}
diff --git a/packages/binary/tangle/model/message/init.go b/packages/binary/tangle/model/message/init.go
deleted file mode 100644
index 15999765139b3e31a3828cb69e25b93d9c2c24eb..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/message/init.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package message
-
-import (
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
-)
-
-func init() {
-	payload.SetGenericUnmarshalerFactory(data.GenericPayloadUnmarshalerFactory)
-}
diff --git a/packages/binary/tangle/model/message/payload/data/init.go b/packages/binary/tangle/model/message/payload/data/init.go
deleted file mode 100644
index f1ae7c9d143c2115e60d3d082c22584d56b170da..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/message/payload/data/init.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package data
-
-import (
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
-)
-
-func init() {
-	payload.RegisterType(Type, GenericPayloadUnmarshalerFactory(Type))
-}
diff --git a/packages/binary/tangle/model/message/transaction.go b/packages/binary/tangle/model/message/transaction.go
deleted file mode 100644
index ff3c9c362cdcc4f42e6fb9c6a00d40e9454cd84b..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/message/transaction.go
+++ /dev/null
@@ -1,281 +0,0 @@
-package message
-
-import (
-	"sync"
-	"time"
-
-	"github.com/iotaledger/hive.go/stringify"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
-
-	"github.com/iotaledger/hive.go/objectstorage"
-
-	"github.com/mr-tron/base58"
-
-	"golang.org/x/crypto/blake2b"
-)
-
-type Transaction struct {
-	// base functionality of StorableObject
-	objectstorage.StorableObjectFlags
-
-	// core properties (they are part of the transaction when being sent)
-	trunkTransactionId  Id
-	branchTransactionId Id
-	issuerPublicKey     ed25119.PublicKey
-	issuingTime         time.Time
-	sequenceNumber      uint64
-	payload             payload.Payload
-	bytes               []byte
-	bytesMutex          sync.RWMutex
-	signature           ed25119.Signature
-	signatureMutex      sync.RWMutex
-
-	// derived properties
-	id             *Id
-	idMutex        sync.RWMutex
-	payloadId      *payload.Id
-	payloadIdMutex sync.RWMutex
-
-	// only stored on the machine of the signer
-	issuerPrivateKey ed25119.PrivateKey
-}
-
-// New creates a new transaction with the details provided by the issuer.
-func New(trunkTransactionId Id, branchTransactionId Id, issuerKeyPair ed25119.KeyPair, issuingTime time.Time, sequenceNumber uint64, payload payload.Payload) (result *Transaction) {
-	return &Transaction{
-		trunkTransactionId:  trunkTransactionId,
-		branchTransactionId: branchTransactionId,
-		issuerPublicKey:     issuerKeyPair.PublicKey,
-		issuingTime:         issuingTime,
-		sequenceNumber:      sequenceNumber,
-		payload:             payload,
-
-		issuerPrivateKey: issuerKeyPair.PrivateKey,
-	}
-}
-
-// Get's called when we restore a transaction from storage. The bytes and the content will be unmarshaled by an external
-// caller (the objectStorage factory).
-func FromStorage(id []byte) (result objectstorage.StorableObject) {
-	var transactionId Id
-	copy(transactionId[:], id)
-
-	result = &Transaction{
-		id: &transactionId,
-	}
-
-	return
-}
-
-func FromBytes(bytes []byte, optionalTargetObject ...*Transaction) (result *Transaction, err error, consumedBytes int) {
-	// determine the target object that will hold the unmarshaled information
-	switch len(optionalTargetObject) {
-	case 0:
-		result = &Transaction{}
-	case 1:
-		result = optionalTargetObject[0]
-	default:
-		panic("too many arguments in call to FromBytes")
-	}
-
-	// initialize helper
-	marshalUtil := marshalutil.New(bytes)
-
-	// parse information
-	if result.trunkTransactionId, err = ParseId(marshalUtil); err != nil {
-		return
-	}
-	if result.branchTransactionId, err = ParseId(marshalUtil); err != nil {
-		return
-	}
-	if result.issuerPublicKey, err = ed25119.ParsePublicKey(marshalUtil); err != nil {
-		return
-	}
-	if result.issuingTime, err = marshalUtil.ReadTime(); err != nil {
-		return
-	}
-	if result.sequenceNumber, err = marshalUtil.ReadUint64(); err != nil {
-		return
-	}
-	if result.payload, err = payload.Parse(marshalUtil); err != nil {
-		return
-	}
-	if result.signature, err = ed25119.ParseSignature(marshalUtil); err != nil {
-		return
-	}
-
-	// return the number of bytes we processed
-	consumedBytes = marshalUtil.ReadOffset()
-
-	// store marshaled version
-	result.bytes = make([]byte, consumedBytes)
-	copy(result.bytes, bytes)
-
-	return
-}
-
-func (transaction *Transaction) VerifySignature() (result bool) {
-	transactionBytes := transaction.Bytes()
-
-	transaction.signatureMutex.RLock()
-	result = transaction.issuerPublicKey.VerifySignature(transactionBytes[:len(transactionBytes)-ed25119.SignatureSize], transaction.signature)
-	transaction.signatureMutex.RUnlock()
-
-	return
-}
-
-func (transaction *Transaction) GetId() (result Id) {
-	transaction.idMutex.RLock()
-	if transaction.id == nil {
-		transaction.idMutex.RUnlock()
-
-		transaction.idMutex.Lock()
-		if transaction.id == nil {
-			result = transaction.calculateTransactionId()
-
-			transaction.id = &result
-		} else {
-			result = *transaction.id
-		}
-		transaction.idMutex.Unlock()
-	} else {
-		result = *transaction.id
-
-		transaction.idMutex.RUnlock()
-	}
-
-	return
-}
-
-func (transaction *Transaction) GetTrunkTransactionId() Id {
-	return transaction.trunkTransactionId
-}
-
-func (transaction *Transaction) GetBranchTransactionId() Id {
-	return transaction.branchTransactionId
-}
-
-// IssuingTime returns the time when the transaction was created.
-func (transaction *Transaction) IssuingTime() time.Time {
-	return transaction.issuingTime
-}
-
-// SequenceNumber returns the sequence number of this transaction.
-func (transaction *Transaction) SequenceNumber() uint64 {
-	return transaction.sequenceNumber
-}
-
-func (transaction *Transaction) GetPayload() payload.Payload {
-	return transaction.payload
-}
-
-func (transaction *Transaction) GetPayloadId() (result payload.Id) {
-	transaction.payloadIdMutex.RLock()
-	if transaction.payloadId == nil {
-		transaction.payloadIdMutex.RUnlock()
-
-		transaction.payloadIdMutex.Lock()
-		if transaction.payloadId == nil {
-			result = transaction.calculatePayloadId()
-
-			transaction.payloadId = &result
-		} else {
-			result = *transaction.payloadId
-		}
-		transaction.payloadIdMutex.Unlock()
-	} else {
-		result = *transaction.payloadId
-
-		transaction.payloadIdMutex.RUnlock()
-	}
-
-	return
-}
-
-func (transaction *Transaction) calculateTransactionId() Id {
-	payloadId := transaction.GetPayloadId()
-
-	hashBase := make([]byte, IdLength+IdLength+payload.IdLength)
-	offset := 0
-
-	copy(hashBase[offset:], transaction.trunkTransactionId[:])
-	offset += IdLength
-
-	copy(hashBase[offset:], transaction.branchTransactionId[:])
-	offset += IdLength
-
-	copy(hashBase[offset:], payloadId[:])
-	// offset += payloadIdLength
-
-	return blake2b.Sum512(hashBase)
-}
-
-func (transaction *Transaction) calculatePayloadId() payload.Id {
-	bytes := transaction.Bytes()
-
-	return blake2b.Sum512(bytes[2*IdLength:])
-}
-
-func (transaction *Transaction) Bytes() []byte {
-	transaction.bytesMutex.RLock()
-	if transaction.bytes != nil {
-		defer transaction.bytesMutex.RUnlock()
-
-		return transaction.bytes
-	}
-
-	transaction.bytesMutex.RUnlock()
-	transaction.bytesMutex.RLock()
-	defer transaction.bytesMutex.RUnlock()
-
-	if transaction.bytes != nil {
-		return transaction.bytes
-	}
-
-	// marshal result
-	marshalUtil := marshalutil.New()
-	marshalUtil.WriteBytes(transaction.trunkTransactionId.Bytes())
-	marshalUtil.WriteBytes(transaction.branchTransactionId.Bytes())
-	marshalUtil.WriteBytes(transaction.issuerPublicKey.Bytes())
-	marshalUtil.WriteTime(transaction.issuingTime)
-	marshalUtil.WriteUint64(transaction.sequenceNumber)
-	marshalUtil.WriteBytes(transaction.payload.Bytes())
-	marshalUtil.WriteBytes(transaction.issuerPrivateKey.Sign(marshalUtil.Bytes()).Bytes())
-
-	return marshalUtil.Bytes()
-}
-
-// Since transactions are immutable and do not get changed after being created, we cache the result of the marshaling.
-func (transaction *Transaction) MarshalBinary() (result []byte, err error) {
-	return transaction.Bytes(), nil
-}
-
-func (transaction *Transaction) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = FromBytes(data, transaction)
-
-	return
-}
-
-func (transaction *Transaction) GetStorageKey() []byte {
-	transactionId := transaction.GetId()
-
-	return transactionId[:]
-}
-
-func (transaction *Transaction) Update(other objectstorage.StorableObject) {
-	panic("transactions should never be overwritten and only stored once to optimize IO")
-}
-
-func (transaction *Transaction) String() string {
-	transactionId := transaction.GetId()
-
-	return stringify.Struct("Transaction",
-		stringify.StructField("id", base58.Encode(transactionId[:])),
-		stringify.StructField("trunkTransactionId", base58.Encode(transaction.trunkTransactionId[:])),
-		stringify.StructField("trunkTransactionId", base58.Encode(transaction.branchTransactionId[:])),
-		stringify.StructField("payload", transaction.payload),
-	)
-}
diff --git a/packages/binary/tangle/model/missingtransaction/cached_missingtransaction.go b/packages/binary/tangle/model/missingtransaction/cached_missingtransaction.go
deleted file mode 100644
index f50f532127ba98e5f9baaaa5bd1095ba0c9d4216..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/missingtransaction/cached_missingtransaction.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package missingtransaction
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-type CachedMissingTransaction struct {
-	objectstorage.CachedObject
-}
-
-func (cachedObject *CachedMissingTransaction) Unwrap() *MissingTransaction {
-	if untypedObject := cachedObject.Get(); untypedObject == nil {
-		return nil
-	} else {
-		if typedObject := untypedObject.(*MissingTransaction); typedObject == nil || typedObject.IsDeleted() {
-			return nil
-		} else {
-			return typedObject
-		}
-	}
-}
diff --git a/packages/binary/tangle/model/missingtransaction/missingtransaction.go b/packages/binary/tangle/model/missingtransaction/missingtransaction.go
deleted file mode 100644
index 64baa95c546c79f158ae50f4ec14fd4dcfc6b722..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/missingtransaction/missingtransaction.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package missingtransaction
-
-import (
-	"time"
-
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-type MissingTransaction struct {
-	objectstorage.StorableObjectFlags
-
-	transactionId message.Id
-	missingSince  time.Time
-}
-
-func New(transactionId message.Id) *MissingTransaction {
-	return &MissingTransaction{
-		transactionId: transactionId,
-		missingSince:  time.Now(),
-	}
-}
-
-func FromStorage(key []byte) objectstorage.StorableObject {
-	result := &MissingTransaction{}
-	copy(result.transactionId[:], key)
-
-	return result
-}
-
-func (missingTransaction *MissingTransaction) GetTransactionId() message.Id {
-	return missingTransaction.transactionId
-}
-
-func (missingTransaction *MissingTransaction) GetMissingSince() time.Time {
-	return missingTransaction.missingSince
-}
-
-func (missingTransaction *MissingTransaction) GetStorageKey() []byte {
-	return missingTransaction.transactionId[:]
-}
-
-func (missingTransaction *MissingTransaction) Update(other objectstorage.StorableObject) {
-	panic("missing transactions should never be overwritten and only stored once to optimize IO")
-}
-
-func (missingTransaction *MissingTransaction) MarshalBinary() (result []byte, err error) {
-	return missingTransaction.missingSince.MarshalBinary()
-}
-
-func (missingTransaction *MissingTransaction) UnmarshalBinary(data []byte) (err error) {
-	return missingTransaction.missingSince.UnmarshalBinary(data)
-}
diff --git a/packages/binary/tangle/model/transactionmetadata/cached_transactionmetadata.go b/packages/binary/tangle/model/transactionmetadata/cached_transactionmetadata.go
deleted file mode 100644
index c8db28e1b0f455adb506ff40b5996fbab8801bcb..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/transactionmetadata/cached_transactionmetadata.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package transactionmetadata
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-type CachedTransactionMetadata struct {
-	objectstorage.CachedObject
-}
-
-func (cachedObject *CachedTransactionMetadata) Retain() *CachedTransactionMetadata {
-	return &CachedTransactionMetadata{cachedObject.CachedObject.Retain()}
-}
-
-func (cachedObject *CachedTransactionMetadata) Unwrap() *TransactionMetadata {
-	if untypedObject := cachedObject.Get(); untypedObject == nil {
-		return nil
-	} else {
-		if typedObject := untypedObject.(*TransactionMetadata); typedObject == nil || typedObject.IsDeleted() {
-			return nil
-		} else {
-			return typedObject
-		}
-	}
-}
diff --git a/packages/binary/tangle/model/transactionmetadata/proto.go b/packages/binary/tangle/model/transactionmetadata/proto.go
deleted file mode 100644
index 652cb7d48f6a027ca9104ade946c2483f7626510..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/transactionmetadata/proto.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package transactionmetadata
-
-import (
-	"fmt"
-	"reflect"
-	"runtime"
-	"time"
-	"unsafe"
-)
-
-type Proto struct {
-	receivedTime       time.Time
-	solidificationTime time.Time
-	solid              bool
-}
-
-// region GENERIC SERIALIZATION CODE ///////////////////////////////////////////////////////////////////////////////////
-
-var sizeOfProto = int(unsafe.Sizeof(Proto{}))
-
-func ProtoFromBytes(bytes []byte) (result *Proto, err error) {
-	if bytesLength := len(bytes); bytesLength != sizeOfProto {
-		return nil, fmt.Errorf("bytes are not long enough (%d instead of %d)", bytesLength, sizeOfProto)
-	}
-
-	copiedBytes := make([]byte, sizeOfProto)
-	copy(copiedBytes, bytes)
-
-	result = (*Proto)(unsafe.Pointer(
-		(*reflect.SliceHeader)(unsafe.Pointer(&copiedBytes)).Data,
-	))
-
-	runtime.KeepAlive(copiedBytes)
-
-	return
-}
-
-func (proto *Proto) ToBytes() (result []byte) {
-	result = make([]byte, sizeOfProto)
-	copy(result, *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
-		Data: uintptr(unsafe.Pointer(proto)),
-		Len:  sizeOfProto,
-		Cap:  sizeOfProto,
-	})))
-
-	runtime.KeepAlive(proto)
-
-	return
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go b/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go
deleted file mode 100644
index 1fcf1c255093cb2e9f7dec98fae7426e05ed33ec..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package transactionmetadata
-
-import (
-	"sync"
-	"time"
-
-	"github.com/iotaledger/hive.go/objectstorage"
-
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-)
-
-type TransactionMetadata struct {
-	objectstorage.StorableObjectFlags
-
-	transactionId      message.Id
-	receivedTime       time.Time
-	solid              bool
-	solidificationTime time.Time
-
-	solidMutex              sync.RWMutex
-	solidificationTimeMutex sync.RWMutex
-}
-
-func New(transactionId message.Id) *TransactionMetadata {
-	return &TransactionMetadata{
-		transactionId: transactionId,
-		receivedTime:  time.Now(),
-	}
-}
-
-func FromStorage(id []byte) objectstorage.StorableObject {
-	result := &TransactionMetadata{}
-	copy(result.transactionId[:], id)
-
-	return result
-}
-
-func (transactionMetadata *TransactionMetadata) IsSolid() (result bool) {
-	transactionMetadata.solidMutex.RLock()
-	result = transactionMetadata.solid
-	transactionMetadata.solidMutex.RUnlock()
-
-	return
-}
-
-func (transactionMetadata *TransactionMetadata) SetSolid(solid bool) (modified bool) {
-	transactionMetadata.solidMutex.RLock()
-	if transactionMetadata.solid != solid {
-		transactionMetadata.solidMutex.RUnlock()
-
-		transactionMetadata.solidMutex.Lock()
-		if transactionMetadata.solid != solid {
-			transactionMetadata.solid = solid
-			if solid {
-				transactionMetadata.solidificationTimeMutex.Lock()
-				transactionMetadata.solidificationTime = time.Now()
-				transactionMetadata.solidificationTimeMutex.Unlock()
-			}
-
-			transactionMetadata.SetModified()
-
-			modified = true
-		}
-		transactionMetadata.solidMutex.Unlock()
-
-	} else {
-		transactionMetadata.solidMutex.RUnlock()
-	}
-
-	return
-}
-
-func (transactionMetadata *TransactionMetadata) GetSoldificationTime() time.Time {
-	transactionMetadata.solidificationTimeMutex.RLock()
-	defer transactionMetadata.solidificationTimeMutex.RUnlock()
-
-	return transactionMetadata.solidificationTime
-}
-
-func (transactionMetadata *TransactionMetadata) GetStorageKey() []byte {
-	return transactionMetadata.transactionId[:]
-}
-
-func (transactionMetadata *TransactionMetadata) Update(other objectstorage.StorableObject) {
-
-}
-
-func (transactionMetadata *TransactionMetadata) MarshalBinary() ([]byte, error) {
-	return (&Proto{
-		receivedTime:       transactionMetadata.receivedTime,
-		solidificationTime: transactionMetadata.solidificationTime,
-		solid:              transactionMetadata.solid,
-	}).ToBytes(), nil
-}
-
-func (transactionMetadata *TransactionMetadata) UnmarshalBinary(data []byte) (err error) {
-	proto, err := ProtoFromBytes(data)
-	if err != nil {
-		return
-	}
-
-	transactionMetadata.receivedTime = proto.receivedTime
-	transactionMetadata.solidificationTime = proto.solidificationTime
-	transactionMetadata.solid = proto.solid
-
-	return
-}
diff --git a/packages/binary/tangle/tangle_test.go b/packages/binary/tangle/tangle_test.go
deleted file mode 100644
index 9cf607e6dcec5c78cf853c5f4ccaf0865b9cbabf..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/tangle_test.go
+++ /dev/null
@@ -1,104 +0,0 @@
-package tangle
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-	"testing"
-	"time"
-
-	"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/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
-	"github.com/iotaledger/goshimmer/packages/database"
-	"github.com/iotaledger/goshimmer/plugins/config"
-)
-
-func BenchmarkTangle_AttachTransaction(b *testing.B) {
-	dir, err := ioutil.TempDir("", b.Name())
-	require.NoError(b, err)
-	defer os.Remove(dir)
-	// use the tempdir for the database
-	config.Node.Set(database.CFG_DIRECTORY, dir)
-
-	tangle := New(database.GetBadgerInstance(), []byte("TEST_BINARY_TANGLE"))
-	if err := tangle.Prune(); err != nil {
-		b.Error(err)
-
-		return
-	}
-
-	testIdentity := ed25119.GenerateKeyPair()
-
-	transactionBytes := make([]*message.Transaction, b.N)
-	for i := 0; i < b.N; i++ {
-		transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, testIdentity, time.Now(), 0, data.New([]byte("some data")))
-		transactionBytes[i].Bytes()
-	}
-
-	b.ResetTimer()
-
-	for i := 0; i < b.N; i++ {
-		tangle.AttachTransaction(transactionBytes[i])
-	}
-
-	tangle.Shutdown()
-}
-
-func TestTangle_AttachTransaction(t *testing.T) {
-	dir, err := ioutil.TempDir("", t.Name())
-	require.NoError(t, err)
-	defer os.Remove(dir)
-	// use the tempdir for the database
-	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.TransactionAttached.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
-		cachedTransactionMetadata.Release()
-
-		cachedTransaction.Consume(func(transaction *message.Transaction) {
-			fmt.Println("ATTACHED:", transaction.GetId())
-		})
-	}))
-
-	tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
-		cachedTransactionMetadata.Release()
-
-		cachedTransaction.Consume(func(transaction *message.Transaction) {
-			fmt.Println("SOLID:", transaction.GetId())
-		})
-	}))
-
-	tangle.Events.TransactionUnsolidifiable.Attach(events.NewClosure(func(transactionId message.Id) {
-		fmt.Println("UNSOLIDIFIABLE:", transactionId)
-	}))
-
-	tangle.Events.TransactionMissing.Attach(events.NewClosure(func(transactionId message.Id) {
-		fmt.Println("MISSING:", transactionId)
-	}))
-
-	tangle.Events.TransactionRemoved.Attach(events.NewClosure(func(transactionId message.Id) {
-		fmt.Println("REMOVED:", transactionId)
-	}))
-
-	newTransaction1 := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data")))
-	newTransaction2 := message.New(newTransaction1.GetId(), newTransaction1.GetId(), ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some other data")))
-
-	tangle.AttachTransaction(newTransaction2)
-
-	time.Sleep(7 * time.Second)
-
-	tangle.AttachTransaction(newTransaction1)
-
-	tangle.Shutdown()
-}
diff --git a/packages/binary/tangle/transactionparser/transaction_filter.go b/packages/binary/tangle/transactionparser/transaction_filter.go
deleted file mode 100644
index c13d94ec4d85c133e7967efd00bad62e19a21faa..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/transactionparser/transaction_filter.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package transactionparser
-
-import (
-	"github.com/iotaledger/hive.go/autopeering/peer"
-
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-)
-
-type TransactionFilter interface {
-	Filter(tx *message.Transaction, peer *peer.Peer)
-	OnAccept(callback func(tx *message.Transaction, peer *peer.Peer))
-	OnReject(callback func(tx *message.Transaction, err error, peer *peer.Peer))
-	Shutdown()
-}
diff --git a/packages/binary/valuetransfer/address/address.go b/packages/binary/valuetransfer/address/address.go
index b73092fd6fd8d9c522be45fd802fda80824088fe..26bfa6f28daa4361897509849fc0ef4a58b3fb76 100644
--- a/packages/binary/valuetransfer/address/address.go
+++ b/packages/binary/valuetransfer/address/address.go
@@ -3,11 +3,12 @@ package address
 import (
 	"crypto/rand"
 	"fmt"
+
+	"github.com/iotaledger/hive.go/crypto/ed25519"
 	"github.com/mr-tron/base58"
 	"golang.org/x/crypto/blake2b"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
+	"github.com/iotaledger/hive.go/marshalutil"
 )
 
 type Version = byte
@@ -55,7 +56,7 @@ func FromBase58(base58String string) (address Address, err error) {
 }
 
 // FromED25519PubKey creates an address from an ed25519 public key.
-func FromED25519PubKey(key ed25119.PublicKey) (address Address) {
+func FromED25519PubKey(key ed25519.PublicKey) (address Address) {
 	digest := blake2b.Sum256(key[:])
 
 	address[0] = VERSION_ED25519
diff --git a/packages/binary/valuetransfer/address/signaturescheme/bls.go b/packages/binary/valuetransfer/address/signaturescheme/bls.go
index 83e403d98383d46952fc22cd4e2d7020773346bc..d6aeec56fd0334eea47329ecad54237832116ed8 100644
--- a/packages/binary/valuetransfer/address/signaturescheme/bls.go
+++ b/packages/binary/valuetransfer/address/signaturescheme/bls.go
@@ -2,14 +2,16 @@ package signaturescheme
 
 import (
 	"fmt"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"math/rand"
+
 	"github.com/mr-tron/base58"
 	"go.dedis.ch/kyber/v3"
 	"go.dedis.ch/kyber/v3/pairing/bn256"
 	"go.dedis.ch/kyber/v3/sign"
 	"go.dedis.ch/kyber/v3/sign/bdn"
 	"go.dedis.ch/kyber/v3/util/random"
-	"math/rand"
+
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 )
 
 // BLS implements BLS signature scheme which is robust against rogue public key attacks, or BDN
@@ -19,7 +21,6 @@ import (
 // This package doesn't implement any threshold signature related primitives.
 // it only contains what is needed for the node to check validity of the BLS signatures against addresses
 // and also minimum signing required for testing
-
 var suite = bn256.NewSuite()
 
 const (
@@ -31,7 +32,6 @@ const (
 
 // ---------------- implements SignatureScheme interface
 // blsSignatureScheme defines an interface for the key pairs of BLS signatures.
-
 type blsSignatureScheme struct {
 	priKey kyber.Scalar
 	pubKey kyber.Point
@@ -43,7 +43,6 @@ var rnd = random.New(rand.New(rand.NewSource(42)))
 // RandBLS creates a RANDOM instance of a signature scheme, that is used to sign the corresponding address.
 // mostly intended for testing.
 // only for testing: each time same sequence!
-
 func RandBLS() SignatureScheme {
 	ret := &blsSignatureScheme{}
 	ret.priKey, ret.pubKey = bdn.NewKeyPair(suite, rnd)
@@ -52,7 +51,6 @@ func RandBLS() SignatureScheme {
 
 // BLS creates an instance of BLS signature scheme
 // from given private and public keys in marshaled binary form
-
 func BLS(priKey, pubKey []byte) (SignatureScheme, error) {
 	if len(priKey) != BLS_PRIVATE_KEY_SIZE || len(pubKey) != BLS_PUBLIC_KEY_SIZE {
 		return nil, fmt.Errorf("wrong key size")
@@ -117,7 +115,7 @@ func BLSSignatureFromBytes(data []byte) (result *blsSignature, err error, consum
 	consumedBytes = 0
 	err = nil
 	if len(data) < BLS_FULL_SIGNATURE_SIZE {
-		err = fmt.Errorf("marshalled BLS signature size must be %d", BLS_FULL_SIGNATURE_SIZE)
+		err = fmt.Errorf("marshaled BLS signature size must be %d", BLS_FULL_SIGNATURE_SIZE)
 		return
 	}
 	if data[0] != address.VERSION_BLS {
diff --git a/packages/binary/valuetransfer/address/signaturescheme/bls_test.go b/packages/binary/valuetransfer/address/signaturescheme/bls_test.go
index d1376109a4025fceefd2c1f4d8de327505cf8d01..9e1a38aa85f917454ee5e2c84389c723884c021a 100644
--- a/packages/binary/valuetransfer/address/signaturescheme/bls_test.go
+++ b/packages/binary/valuetransfer/address/signaturescheme/bls_test.go
@@ -1,41 +1,21 @@
 package signaturescheme
 
 import (
-	"github.com/magiconair/properties/assert"
-	"github.com/mr-tron/base58"
 	"testing"
-)
-
-var dataToSign = []byte("Hello Boneh-Lynn-Shacham (BLS) --> Boneh-Drijvers-Neven (BDN)")
-
-func TestBLS_rndSigSheme(t *testing.T) {
 
-	sigScheme := RandBLS()
-	t.Logf("generating random BLS signature scheme: %s\n", sigScheme.(*blsSignatureScheme).String())
-	signature := sigScheme.Sign(dataToSign)
-
-	assert.Equal(t, sigScheme.Address(), signature.Address())
-	assert.Equal(t, signature.IsValid(dataToSign), true)
-}
-
-const (
-	priKeyTest = "Cjsu52qf28G4oLiUDcimEY7SPbWJQA9zoKCNi4ywMxg"
-	pubKeyTest = "28LgNCDp52gTotmd21hcEXKar5tTyxuJKqQdGHCJnZ5Z1M7Rdh4Qo2BYC3s3NicLD99tZ3yX9mZvRmsnQLMRcHnzqgq2CQp7CYWCKfTUT9yzJKUTQ4JmN2DhSkSNc5kau4KE8PRGByQxpiYQq4DRF4Qb3Dn4cHmhTrDi9xQiYTxoAYW"
+	"github.com/magiconair/properties/assert"
 )
 
-func TestBLS_sigScheme(t *testing.T) {
-	priKeyBin, err := base58.Decode(priKeyTest)
-	assert.Equal(t, err, nil)
-
-	pubKeyBin, err := base58.Decode(pubKeyTest)
-	assert.Equal(t, err, nil)
+var dataToSign = []byte("Hello Boneh-Lynn-Shacham (BLS) --> Boneh-Drijvers-Neven (BDN)")
 
-	sigScheme, err := BLS(priKeyBin, pubKeyBin)
-	assert.Equal(t, err, nil)
+func TestBLS_base(t *testing.T) {
+	blsSigScheme := RandBLS()
+	t.Logf("generating random BLS signature scheme: %s\n", blsSigScheme.(*blsSignatureScheme).String())
+	signature := blsSigScheme.Sign(dataToSign)
 
-	signature := sigScheme.Sign(dataToSign)
-	assert.Equal(t, sigScheme.Address(), signature.Address())
-	assert.Equal(t, signature.IsValid(dataToSign), true)
+	assert.Equal(t, blsSigScheme.Address(), signature.Address())
+	res := signature.IsValid(dataToSign)
+	assert.Equal(t, res, true)
 }
 
 // number of signatures to aggregate
@@ -49,13 +29,16 @@ func TestBLS_aggregation(t *testing.T) {
 		sigSchemes[i] = RandBLS()
 		sigs[i] = sigSchemes[i].Sign(dataToSign)
 	}
-	// aggregate 2 signatures
-	a01, err := AggregateBLSSignatures(sigs[0], sigs[1])
+	aggregatedSig1, err := AggregateBLSSignatures(sigs...)
 	assert.Equal(t, err, nil)
-	assert.Equal(t, a01.IsValid(dataToSign), true)
 
-	// aggregate N signatures
-	aN, err := AggregateBLSSignatures(sigs...)
+	assert.Equal(t, aggregatedSig1.IsValid(dataToSign), true)
+
+	aggregatedScheme, err := AggregateBLSSignatureSchemes(sigSchemes...)
 	assert.Equal(t, err, nil)
-	assert.Equal(t, aN.IsValid(dataToSign), true)
+
+	if err == nil {
+		aggregatedSig2 := aggregatedScheme.Sign(dataToSign)
+		assert.Equal(t, aggregatedSig2, aggregatedSig2)
+	}
 }
diff --git a/packages/binary/valuetransfer/address/signaturescheme/ed25519.go b/packages/binary/valuetransfer/address/signaturescheme/ed25519.go
index 4a3db62f31091ec1ab9d092a1c66049f586c6ca8..54349efecb0118b7342f039b6123152fe015d499 100644
--- a/packages/binary/valuetransfer/address/signaturescheme/ed25519.go
+++ b/packages/binary/valuetransfer/address/signaturescheme/ed25519.go
@@ -3,15 +3,16 @@ package signaturescheme
 import (
 	"fmt"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
+	"github.com/iotaledger/hive.go/crypto/ed25519"
+	"github.com/iotaledger/hive.go/marshalutil"
+
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 )
 
 // region PUBLIC API ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 // ED25519 creates an instance of a signature scheme, that is used to sign the corresponding address.
-func ED25519(keyPair ed25119.KeyPair) SignatureScheme {
+func ED25519(keyPair ed25519.KeyPair) SignatureScheme {
 	return &ed25519SignatureScheme{
 		keyPair: keyPair,
 	}
@@ -23,7 +24,7 @@ func ED25519(keyPair ed25119.KeyPair) SignatureScheme {
 
 // ed25519SignatureScheme defines an interface for ED25519 elliptic curve signatures.
 type ed25519SignatureScheme struct {
-	keyPair ed25119.KeyPair
+	keyPair ed25519.KeyPair
 }
 
 // Version returns the version byte that is associated to this signature scheme.
@@ -53,8 +54,8 @@ var _ SignatureScheme = &ed25519SignatureScheme{}
 
 // ed25519Signature represents a signature for an addresses that uses elliptic curve cryptography.
 type ed25519Signature struct {
-	publicKey ed25119.PublicKey
-	signature ed25119.Signature
+	publicKey ed25519.PublicKey
+	signature ed25519.Signature
 }
 
 // ed25519SignatureFromBytes unmarshals an ed25519 signatures from a sequence of bytes.
@@ -84,18 +85,18 @@ func Ed25519SignatureFromBytes(bytes []byte, optionalTargetObject ...*ed25519Sig
 	}
 
 	// read public key
-	publicKey, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ed25119.PublicKeyFromBytes(data) })
+	publicKey, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ed25519.PublicKeyFromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.publicKey = publicKey.(ed25119.PublicKey)
+	result.publicKey = publicKey.(ed25519.PublicKey)
 
 	// read signature
-	signature, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ed25119.SignatureFromBytes(data) })
+	signature, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ed25519.SignatureFromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.signature = signature.(ed25119.Signature)
+	result.signature = signature.(ed25519.Signature)
 
 	// return the number of bytes we processed
 	consumedBytes = marshalUtil.ReadOffset()
@@ -110,7 +111,7 @@ func (signature *ed25519Signature) IsValid(signedData []byte) bool {
 
 // Bytes returns a marshaled version of the signature.
 func (signature *ed25519Signature) Bytes() []byte {
-	marshalUtil := marshalutil.New(1 + ed25119.PublicKeySize + ed25119.SignatureSize)
+	marshalUtil := marshalutil.New(1 + ed25519.PublicKeySize + ed25519.SignatureSize)
 	marshalUtil.WriteByte(address.VERSION_ED25519)
 	marshalUtil.WriteBytes(signature.publicKey[:])
 	marshalUtil.WriteBytes(signature.signature[:])
diff --git a/packages/binary/valuetransfer/balance/balance.go b/packages/binary/valuetransfer/balance/balance.go
index e9e3acb2747efa07138ec022c85747c98d64c6fa..d969817c1ef7f4317d9f7fe2e1710c1fc24c8fc4 100644
--- a/packages/binary/valuetransfer/balance/balance.go
+++ b/packages/binary/valuetransfer/balance/balance.go
@@ -3,7 +3,7 @@ package balance
 import (
 	"strconv"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/hive.go/marshalutil"
 )
 
 type Balance struct {
diff --git a/packages/binary/valuetransfer/balance/color.go b/packages/binary/valuetransfer/balance/color.go
index a37941884dfc6b14402df0112d698715a4f7dde0..b50ccacfd5458773a0167a3adbdf26a279485a6c 100644
--- a/packages/binary/valuetransfer/balance/color.go
+++ b/packages/binary/valuetransfer/balance/color.go
@@ -1,9 +1,8 @@
 package balance
 
 import (
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/mr-tron/base58"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 type Color [ColorLength]byte
diff --git a/packages/binary/valuetransfer/payload/id.go b/packages/binary/valuetransfer/payload/id.go
index 313af04e90758de12b082d06967639819845d9f8..43241a6c059da3ad23a2b68712f709ad87bfa21f 100644
--- a/packages/binary/valuetransfer/payload/id.go
+++ b/packages/binary/valuetransfer/payload/id.go
@@ -4,9 +4,8 @@ import (
 	"crypto/rand"
 	"fmt"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"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.
diff --git a/packages/binary/valuetransfer/payload/payload.go b/packages/binary/valuetransfer/payload/payload.go
index c97b2f128cc97537eedb61568b56f4a4ce1e3596..d41e143931d8d5d1ee98b578b7413f124e4a8315 100644
--- a/packages/binary/valuetransfer/payload/payload.go
+++ b/packages/binary/valuetransfer/payload/payload.go
@@ -3,27 +3,26 @@ package payload
 import (
 	"sync"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 	"github.com/iotaledger/hive.go/stringify"
 	"golang.org/x/crypto/blake2b"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
 
 type Payload struct {
 	objectstorage.StorableObjectFlags
 
-	trunkPayloadId  Id
-	branchPayloadId Id
-	transaction     *transaction.Transaction
-
 	id      *Id
 	idMutex sync.RWMutex
 
-	bytes      []byte
-	bytesMutex sync.RWMutex
+	trunkPayloadId  Id
+	branchPayloadId Id
+	transaction     *transaction.Transaction
+	bytes           []byte
+	bytesMutex      sync.RWMutex
 }
 
 func New(trunkPayloadId, branchPayloadId Id, valueTransfer *transaction.Transaction) *Payload {
@@ -34,20 +33,34 @@ func New(trunkPayloadId, branchPayloadId Id, valueTransfer *transaction.Transact
 	}
 }
 
-func FromStorage(key []byte) objectstorage.StorableObject {
-	id, err, _ := IdFromBytes(key)
-	if err != nil {
-		panic(err)
-	}
+// 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) {
+	marshalUtil := marshalutil.New(bytes)
+	result, err = Parse(marshalUtil, optionalTargetObject...)
+	consumedBytes = marshalUtil.ReadOffset()
 
-	return &Payload{
-		id: &id,
+	return
+}
+
+func StorableObjectFromKey(key []byte) (result objectstorage.StorableObject, err error, consumedBytes int) {
+	result = &Payload{}
+
+	// parse the properties that are stored in the key
+	marshalUtil := marshalutil.New(key)
+	if payloadId, idErr := ParseId(marshalUtil); idErr != nil {
+		err = idErr
+
+		return
+	} else {
+		result.(*Payload).id = &payloadId
 	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
 }
 
-// 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) {
+func Parse(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Payload) (result *Payload, err error) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -55,48 +68,16 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload,
 	case 1:
 		result = optionalTargetObject[0]
 	default:
-		panic("too many arguments in call to OutputFromBytes")
+		panic("too many arguments in call to Parse")
 	}
 
-	// initialize helper
-	marshalUtil := marshalutil.New(bytes)
+	if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parseErr error, parsedBytes int) {
+		parseErr, parsedBytes = result.UnmarshalObjectStorageValue(data)
 
-	// 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.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) })
-	if err != nil {
+	}); err != nil {
 		return
 	}
-	result.trunkPayloadId = parsedTrunkPayloadId.(Id)
-
-	// parse branch payload id
-	parsedBranchPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) })
-	if err != nil {
-		return
-	}
-	result.branchPayloadId = parsedBranchPayloadId.(Id)
-
-	// parse transfer
-	parsedTransfer, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return transaction.FromBytes(data) })
-	if err != nil {
-		return
-	}
-	result.transaction = parsedTransfer.(*transaction.Transaction)
-
-	// return the number of bytes we processed
-	consumedBytes = marshalUtil.ReadOffset()
-
-	// store bytes, so we don't have to marshal manually
-	result.bytes = bytes[:consumedBytes]
 
 	return
 }
@@ -168,10 +149,7 @@ func (payload *Payload) Bytes() (bytes []byte) {
 	}
 
 	// retrieve bytes of transfer
-	transferBytes, err := payload.Transaction().MarshalBinary()
-	if err != nil {
-		return
-	}
+	transferBytes := payload.Transaction().ObjectStorageValue()
 
 	// marshal fields
 	payloadLength := IdLength + IdLength + len(transferBytes)
@@ -202,24 +180,57 @@ func (payload *Payload) String() string {
 
 var Type = payload.Type(1)
 
-func (payload *Payload) GetType() payload.Type {
+func (payload *Payload) Type() payload.Type {
 	return Type
 }
 
-func (payload *Payload) MarshalBinary() (bytes []byte, err error) {
-	return payload.Bytes(), nil
+func (payload *Payload) ObjectStorageValue() []byte {
+	return payload.Bytes()
+}
+
+func (payload *Payload) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	marshalUtil := marshalutil.New(data)
+
+	// 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
+	if payload.trunkPayloadId, err = ParseId(marshalUtil); err != nil {
+		return
+	}
+	if payload.branchPayloadId, err = ParseId(marshalUtil); err != nil {
+		return
+	}
+	if payload.transaction, err = transaction.Parse(marshalUtil); err != nil {
+		return
+	}
+
+	// return the number of bytes we processed
+	consumedBytes = marshalUtil.ReadOffset()
+
+	// store bytes, so we don't have to marshal manually
+	payload.bytes = make([]byte, consumedBytes)
+	copy(payload.bytes, data[:consumedBytes])
+
+	return
 }
 
-func (payload *Payload) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = FromBytes(data, payload)
+func (payload *Payload) Unmarshal(data []byte) (err error) {
+	_, err, _ = FromBytes(data)
 
 	return
 }
 
 func init() {
 	payload.RegisterType(Type, func(data []byte) (payload payload.Payload, err error) {
-		payload = &Payload{}
-		err = payload.UnmarshalBinary(data)
+		payload, err, _ = FromBytes(data)
 
 		return
 	})
@@ -232,11 +243,11 @@ var _ payload.Payload = &Payload{}
 
 // region StorableObject implementation ////////////////////////////////////////////////////////////////////////////////
 
-// MarshalBinary() (bytes []byte, err error) already implemented by Payload
+// ObjectStorageValue() (bytes []byte, err error) already implemented by Payload
 
-// UnmarshalBinary(data []byte) (err error) already implemented by Payload
+// UnmarshalObjectStorageValue(data []byte) (err error) already implemented by Payload
 
-func (payload *Payload) GetStorageKey() []byte {
+func (payload *Payload) ObjectStorageKey() []byte {
 	id := payload.Id()
 
 	return id[:]
diff --git a/packages/binary/valuetransfer/payload/payload_test.go b/packages/binary/valuetransfer/payload/payload_test.go
index 0dc219378c233b2cc8daeec57eebca4e7fa313b8..46d445cfe6b5fd2552488404263d85b1cfdd8857 100644
--- a/packages/binary/valuetransfer/payload/payload_test.go
+++ b/packages/binary/valuetransfer/payload/payload_test.go
@@ -5,10 +5,11 @@ import (
 	"testing"
 	"time"
 
+	"github.com/iotaledger/hive.go/crypto/ed25519"
+	"github.com/iotaledger/hive.go/identity"
 	"github.com/stretchr/testify/assert"
 
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
@@ -45,6 +46,7 @@ func ExamplePayload() {
 	)
 
 	// 3. build actual transaction (the base layer creates this and wraps the ontology provided payload)
+	localIdentity := identity.GenerateLocalIdentity()
 	tx := message.New(
 		// trunk in "network tangle" ontology (filled by tipSelector)
 		message.EmptyId,
@@ -53,7 +55,7 @@ func ExamplePayload() {
 		message.EmptyId,
 
 		// issuer of the transaction (signs automatically)
-		ed25119.GenerateKeyPair(),
+		localIdentity.PublicKey(),
 
 		// the time when the transaction was created
 		time.Now(),
@@ -63,14 +65,16 @@ func ExamplePayload() {
 
 		// payload
 		valuePayload,
+
+		localIdentity,
 	)
 
 	fmt.Println(tx)
 }
 
 func TestPayload(t *testing.T) {
-	addressKeyPair1 := ed25119.GenerateKeyPair()
-	addressKeyPair2 := ed25119.GenerateKeyPair()
+	addressKeyPair1 := ed25519.GenerateKeyPair()
+	addressKeyPair2 := ed25519.GenerateKeyPair()
 
 	originalPayload := New(
 		GenesisId,
diff --git a/packages/binary/valuetransfer/tangle/attachment.go b/packages/binary/valuetransfer/tangle/attachment.go
index 9dcfadb462596bd48d54d50977f88ed8faf9f2ca..c7dcd5527dd54681e18f64806059aa3263bf4d43 100644
--- a/packages/binary/valuetransfer/tangle/attachment.go
+++ b/packages/binary/valuetransfer/tangle/attachment.go
@@ -1,10 +1,10 @@
 package tangle
 
 import (
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 	"github.com/iotaledger/hive.go/stringify"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
@@ -33,7 +33,7 @@ func NewAttachment(transactionId transaction.Id, payloadId payload.Id) *Attachme
 	}
 }
 
-// AttachmentFromBytes unmarshals a MissingOutput from a sequence of bytes - it either creates a new object or fills the
+// AttachmentFromBytes unmarshals an Attachment from a sequence of bytes - it either creates a new object or fills the
 // optionally provided one with the parsed information.
 func AttachmentFromBytes(bytes []byte, optionalTargetObject ...*Attachment) (result *Attachment, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
@@ -54,20 +54,30 @@ func AttachmentFromBytes(bytes []byte, optionalTargetObject ...*Attachment) (res
 	if result.payloadId, err = payload.ParseId(marshalUtil); err != nil {
 		return
 	}
+	result.storageKey = marshalutil.New(bytes[:AttachmentLength]).Bytes(true)
 	consumedBytes = marshalUtil.ReadOffset()
 
 	return
 }
 
-// AttachmentFromStorage gets called when we restore an Attachment from the storage - it parses the key bytes and
+// Parse is a wrapper for simplified unmarshaling of Attachments from a byte stream using the marshalUtil package.
+func ParseAttachment(marshalUtil *marshalutil.MarshalUtil) (*Attachment, error) {
+	if attachment, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return AttachmentFromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return attachment.(*Attachment), nil
+	}
+}
+
+// AttachmentFromStorageKey gets called when we restore an Attachment from the storage - it parses the key bytes and
 // returns the new object.
-func AttachmentFromStorage(keyBytes []byte) objectstorage.StorableObject {
+func AttachmentFromStorageKey(keyBytes []byte) (objectstorage.StorableObject, error, int) {
 	result, err, _ := AttachmentFromBytes(keyBytes)
 	if err != nil {
-		panic(err)
+		return nil, err, 0
 	}
 
-	return result
+	return result, nil, 0
 }
 
 // TransactionId returns the transaction id of this Attachment.
@@ -80,9 +90,9 @@ func (attachment *Attachment) PayloadId() payload.Id {
 	return attachment.payloadId
 }
 
-// Bytes marshals an Attachment into a sequence of bytes.
+// Bytes marshals the Attachment into a sequence of bytes.
 func (attachment *Attachment) Bytes() []byte {
-	return attachment.GetStorageKey()
+	return attachment.ObjectStorageKey()
 }
 
 // String returns a human readable version of the Attachment.
@@ -93,20 +103,20 @@ func (attachment *Attachment) String() string {
 	)
 }
 
-// GetStorageKey returns the key that is used to store the object in the database.
-func (attachment *Attachment) GetStorageKey() []byte {
+// ObjectStorageKey returns the key that is used to store the object in the database.
+func (attachment *Attachment) ObjectStorageKey() []byte {
 	return attachment.storageKey
 }
 
-// MarshalBinary marshals the "content part" of an Attachment to a sequence of bytes. Since all of the information for
-// this object are stored in its key, this method does nothing and is only required to conform with the interface.
-func (attachment *Attachment) MarshalBinary() (data []byte, err error) {
+// ObjectStorageValue marshals the "content part" of an Attachment to a sequence of bytes. Since all of the information
+// for this object are stored in its key, this method does nothing and is only required to conform with the interface.
+func (attachment *Attachment) ObjectStorageValue() (data []byte) {
 	return
 }
 
-// UnmarshalBinary unmarshals the "content part" of an Attachment from a sequence of bytes. Since all of the information
+// UnmarshalObjectStorageValue unmarshals the "content part" of an Attachment from a sequence of bytes. Since all of the information
 // for this object are stored in its key, this method does nothing and is only required to conform with the interface.
-func (attachment *Attachment) UnmarshalBinary(data []byte) (err error) {
+func (attachment *Attachment) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
 	return
 }
 
diff --git a/packages/binary/valuetransfer/tangle/consumer.go b/packages/binary/valuetransfer/tangle/consumer.go
new file mode 100644
index 0000000000000000000000000000000000000000..9dadca65db17677ba70df2aed239817319aa32e5
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/consumer.go
@@ -0,0 +1,131 @@
+package tangle
+
+import (
+	"github.com/iotaledger/hive.go/marshalutil"
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+// Consumer stores the information which transaction output was consumed by which transaction. We need this to be able
+// to perform reverse lookups from transaction outputs to their corresponding consuming transactions.
+type Consumer struct {
+	objectstorage.StorableObjectFlags
+
+	consumedInput transaction.OutputId
+	transactionId transaction.Id
+
+	storageKey []byte
+}
+
+// NewConsumer creates a Consumer object with the given information.
+func NewConsumer(consumedInput transaction.OutputId, transactionId transaction.Id) *Consumer {
+	return &Consumer{
+		consumedInput: consumedInput,
+		transactionId: transactionId,
+
+		storageKey: marshalutil.New(ConsumerLength).
+			WriteBytes(consumedInput.Bytes()).
+			WriteBytes(transactionId.Bytes()).
+			Bytes(),
+	}
+}
+
+// ConsumerFromBytes unmarshals a Consumer from a sequence of bytes - it either creates a new object or fills the
+// optionally provided one with the parsed information.
+func ConsumerFromBytes(bytes []byte, optionalTargetObject ...*Consumer) (result *Consumer, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Consumer{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to ConsumerFromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.consumedInput, err = transaction.ParseOutputId(marshalUtil); err != nil {
+		return
+	}
+	if result.transactionId, err = transaction.ParseId(marshalUtil); err != nil {
+		return
+	}
+	result.storageKey = marshalutil.New(bytes[:ConsumerLength]).Bytes(true)
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// Parse is a wrapper for simplified unmarshaling of Consumers from a byte stream using the marshalUtil package.
+func ParseConsumer(marshalUtil *marshalutil.MarshalUtil) (*Consumer, error) {
+	if consumer, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ConsumerFromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return consumer.(*Consumer), nil
+	}
+}
+
+// ConsumerFromStorage gets called when we restore an Consumer from the storage - it parses the key bytes and
+// returns the new object.
+func ConsumerFromStorage(keyBytes []byte) objectstorage.StorableObject {
+	result, err, _ := ConsumerFromBytes(keyBytes)
+	if err != nil {
+		panic(err)
+	}
+
+	return result
+}
+
+// ConsumedInput returns the OutputId of the Consumer.
+func (consumer *Consumer) ConsumedInput() transaction.OutputId {
+	return consumer.consumedInput
+}
+
+// TransactionId returns the transaction Id of this Consumer.
+func (consumer *Consumer) TransactionId() transaction.Id {
+	return consumer.transactionId
+}
+
+// Bytes marshals the Consumer into a sequence of bytes.
+func (consumer *Consumer) Bytes() []byte {
+	return consumer.ObjectStorageKey()
+}
+
+// String returns a human readable version of the Consumer.
+func (consumer *Consumer) String() string {
+	return stringify.Struct("Consumer",
+		stringify.StructField("consumedInput", consumer.ConsumedInput()),
+		stringify.StructField("transactionId", consumer.TransactionId()),
+	)
+}
+
+// ObjectStorageKey returns the key that is used to store the object in the database.
+func (consumer *Consumer) ObjectStorageKey() []byte {
+	return consumer.storageKey
+}
+
+// ObjectStorageValue marshals the "content part" of an Consumer to a sequence of bytes. Since all of the information for
+// this object are stored in its key, this method does nothing and is only required to conform with the interface.
+func (consumer *Consumer) ObjectStorageValue() (data []byte) {
+	return
+}
+
+// UnmarshalObjectStorageValue unmarshals the "content part" of a Consumer from a sequence of bytes. Since all of the information
+// for this object are stored in its key, this method does nothing and is only required to conform with the interface.
+func (consumer *Consumer) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	return
+}
+
+// Update is disabled - updates are supposed to happen through the setters (if existing).
+func (consumer *Consumer) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// Interface contract: make compiler warn if the interface is not implemented correctly.
+var _ objectstorage.StorableObject = &Consumer{}
+
+// ConsumerLength holds the length of a marshaled Consumer in bytes.
+const ConsumerLength = transaction.OutputIdLength + transaction.IdLength
diff --git a/packages/binary/valuetransfer/tangle/events.go b/packages/binary/valuetransfer/tangle/events.go
index 15e31ddd8348f6af855e3d711fd1a924e46c2797..f36280cb9dca449d2b6ad47163741d60064ad766 100644
--- a/packages/binary/valuetransfer/tangle/events.go
+++ b/packages/binary/valuetransfer/tangle/events.go
@@ -16,6 +16,8 @@ type Events struct {
 	PayloadUnsolidifiable  *events.Event
 	TransactionRemoved     *events.Event
 	OutputMissing          *events.Event
+
+	TransactionSolid *events.Event
 }
 
 func newEvents() *Events {
@@ -25,8 +27,9 @@ func newEvents() *Events {
 		MissingPayloadReceived: events.NewEvent(cachedPayloadEvent),
 		PayloadMissing:         events.NewEvent(payloadIdEvent),
 		PayloadUnsolidifiable:  events.NewEvent(payloadIdEvent),
-		TransactionRemoved:     events.NewEvent(payloadIdEvent),
 		OutputMissing:          events.NewEvent(outputIdEvent),
+
+		TransactionSolid: events.NewEvent(transactionEvent),
 	}
 }
 
@@ -41,6 +44,13 @@ func cachedPayloadEvent(handler interface{}, params ...interface{}) {
 	)
 }
 
+func transactionEvent(handler interface{}, params ...interface{}) {
+	handler.(func(*transaction.Transaction, *CachedTransactionMetadata))(
+		params[0].(*transaction.Transaction),
+		params[1].(*CachedTransactionMetadata).Retain(),
+	)
+}
+
 func outputIdEvent(handler interface{}, params ...interface{}) {
 	handler.(func(transaction.OutputId))(params[0].(transaction.OutputId))
 }
diff --git a/packages/binary/valuetransfer/tangle/missingoutput.go b/packages/binary/valuetransfer/tangle/missingoutput.go
index 92b34c383995a0127d5b19f2775630102f4d4633..6e5e05d5a67aa6724cf52a2d96b0a3a1f6aa1c6f 100644
--- a/packages/binary/valuetransfer/tangle/missingoutput.go
+++ b/packages/binary/valuetransfer/tangle/missingoutput.go
@@ -3,9 +3,9 @@ package tangle
 import (
 	"time"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
 
@@ -51,9 +51,9 @@ func MissingOutputFromBytes(bytes []byte, optionalTargetObject ...*MissingOutput
 	return
 }
 
-// MissingOutputFromStorage gets called when we restore a MissingOutput from the storage. The content will be
-// unmarshaled by an external caller using the binary.MarshalBinary interface.
-func MissingOutputFromStorage(keyBytes []byte) objectstorage.StorableObject {
+// MissingOutputFromStorageKey gets called when we restore a MissingOutput from the storage. The content will be
+// unmarshaled by an external caller using the binary.ObjectStorageValue interface.
+func MissingOutputFromStorageKey(keyBytes []byte) (objectstorage.StorableObject, error, int) {
 	outputId, err, _ := transaction.OutputIdFromBytes(keyBytes)
 	if err != nil {
 		panic(err)
@@ -61,7 +61,7 @@ func MissingOutputFromStorage(keyBytes []byte) objectstorage.StorableObject {
 
 	return &MissingOutput{
 		outputId: outputId,
-	}
+	}, nil, transaction.OutputIdLength
 }
 
 // Id returns the id of the Output that is missing.
@@ -84,26 +84,29 @@ func (missingOutput *MissingOutput) Bytes() []byte {
 	return marshalUtil.Bytes()
 }
 
-// GetStorageKey returns the key that is used to store the object in the object storage.
-func (missingOutput *MissingOutput) GetStorageKey() []byte {
+// ObjectStorageKey returns the key that is used to store the object in the object storage.
+func (missingOutput *MissingOutput) ObjectStorageKey() []byte {
 	return missingOutput.outputId.Bytes()
 }
 
-// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
-func (missingOutput *MissingOutput) Update(other objectstorage.StorableObject) {
-	panic("implement me")
-}
-
-// MarshalBinary returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler
+// ObjectStorageValue returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler
 // interface.
-func (missingOutput *MissingOutput) MarshalBinary() (data []byte, err error) {
-	return missingOutput.Bytes(), nil
+func (missingOutput *MissingOutput) ObjectStorageValue() []byte {
+	return missingOutput.Bytes()
 }
 
-// UnmarshalBinary restores the values of a MissingOutput from a sequence of bytes using the  encoding.BinaryUnmarshaler
+// UnmarshalObjectStorageValue restores the values of a MissingOutput from a sequence of bytes using the  encoding.BinaryUnmarshaler
 // interface.
-func (missingOutput *MissingOutput) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = MissingOutputFromBytes(data, missingOutput)
+func (missingOutput *MissingOutput) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	_, err, consumedBytes = MissingOutputFromBytes(data, missingOutput)
 
 	return
 }
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+func (missingOutput *MissingOutput) Update(other objectstorage.StorableObject) {
+	panic("implement me")
+}
+
+// Interface contract: make compiler warn if the interface is not implemented correctly.
+var _ objectstorage.StorableObject = &MissingOutput{}
diff --git a/packages/binary/valuetransfer/tangle/missingpayload.go b/packages/binary/valuetransfer/tangle/missingpayload.go
index c4eb653276ad39e4697adfca578e3d73a6d97496..395365a2c6d4705bf670707001b5d23c3c659fb7 100644
--- a/packages/binary/valuetransfer/tangle/missingpayload.go
+++ b/packages/binary/valuetransfer/tangle/missingpayload.go
@@ -3,9 +3,9 @@ package tangle
 import (
 	"time"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
 )
 
@@ -52,10 +52,10 @@ func MissingPayloadFromBytes(bytes []byte, optionalTargetObject ...*MissingPaylo
 	return
 }
 
-// MissingPayloadFromStorage gets called when we restore an entry for a missing value transfer payload from the storage. The bytes and
-// the content will be unmarshaled by an external caller using the binary.MarshalBinary interface.
-func MissingPayloadFromStorage([]byte) objectstorage.StorableObject {
-	return &MissingPayload{}
+// MissingPayloadFromStorageKey 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.ObjectStorageValue interface.
+func MissingPayloadFromStorageKey([]byte) (result objectstorage.StorableObject, err error, consumedBytes int) {
+	return
 }
 
 // GetId returns the payload id, that is missing.
@@ -78,26 +78,29 @@ func (missingPayload *MissingPayload) Bytes() []byte {
 	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
+// ObjectStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (missingPayload *MissingPayload) ObjectStorageKey() []byte {
+	return missingPayload.payloadId.Bytes()
 }
 
-// UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface.
-func (missingPayload *MissingPayload) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = MissingPayloadFromBytes(data, missingPayload)
+// ObjectStorageValue is required to match the encoding.BinaryMarshaler interface.
+func (missingPayload *MissingPayload) ObjectStorageValue() (data []byte) {
+	return missingPayload.Bytes()
+}
+
+// UnmarshalObjectStorageValue is required to match the encoding.BinaryUnmarshaler interface.
+func (missingPayload *MissingPayload) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	_, err, consumedBytes = MissingPayloadFromBytes(data, missingPayload)
 
 	return
 }
+
+// Interface contract: make compiler warn if the interface is not implemented correctly.
+var _ objectstorage.StorableObject = &MissingPayload{}
diff --git a/packages/binary/valuetransfer/tangle/payloadapprover.go b/packages/binary/valuetransfer/tangle/payloadapprover.go
index e86379258ed67159df15b2e40d10313d5b2ec9a4..4664cb16a3aa51a8d600be49b73b03d7490401a0 100644
--- a/packages/binary/valuetransfer/tangle/payloadapprover.go
+++ b/packages/binary/valuetransfer/tangle/payloadapprover.go
@@ -1,9 +1,9 @@
 package tangle
 
 import (
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
 )
 
@@ -31,28 +31,30 @@ func NewPayloadApprover(referencedPayload payload.Id, approvingPayload payload.I
 	}
 }
 
-// PayloadApproverFromStorage get's called when we restore transaction metadata from the storage.
-// In contrast to other database models, it unmarshals the information from the key and does not use the UnmarshalBinary
+// PayloadApproverFromStorageKey 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 UnmarshalObjectStorageValue
 // method.
-func PayloadApproverFromStorage(idBytes []byte) objectstorage.StorableObject {
+func PayloadApproverFromStorageKey(idBytes []byte) (result objectstorage.StorableObject, err error, consumedBytes int) {
 	marshalUtil := marshalutil.New(idBytes)
 
 	referencedPayloadId, err := payload.ParseId(marshalUtil)
 	if err != nil {
-		panic(err)
+		return
 	}
 	approvingPayloadId, err := payload.ParseId(marshalUtil)
 	if err != nil {
-		panic(err)
+		return
 	}
 
-	result := &PayloadApprover{
+	result = &PayloadApprover{
 		referencedPayloadId: referencedPayloadId,
 		approvingPayloadId:  approvingPayloadId,
 		storageKey:          marshalUtil.Bytes(true),
 	}
 
-	return result
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
 }
 
 // GetApprovingPayloadId returns the id of the approving payload.
@@ -60,22 +62,22 @@ func (payloadApprover *PayloadApprover) GetApprovingPayloadId() payload.Id {
 	return payloadApprover.approvingPayloadId
 }
 
-// GetStorageKey returns the key that is used to store the object in the database.
+// ObjectStorageKey returns the key that is used to store the object in the database.
 // It is required to match StorableObject interface.
-func (payloadApprover *PayloadApprover) GetStorageKey() []byte {
+func (payloadApprover *PayloadApprover) ObjectStorageKey() []byte {
 	return payloadApprover.storageKey
 }
 
-// MarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
+// ObjectStorageValue is implemented to conform with the StorableObject interface, but it does not really do anything,
 // since all of the information about an approver are stored in the "key".
-func (payloadApprover *PayloadApprover) MarshalBinary() (data []byte, err error) {
+func (payloadApprover *PayloadApprover) ObjectStorageValue() (data []byte) {
 	return
 }
 
-// UnmarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
-// since all of the information about an approver are stored in the "key".
-func (payloadApprover *PayloadApprover) UnmarshalBinary(data []byte) error {
-	return nil
+// UnmarshalObjectStorageValue is implemented to conform with the StorableObject interface, but it does not really do
+// anything, since all of the information about an approver are stored in the "key".
+func (payloadApprover *PayloadApprover) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	return
 }
 
 // Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata.go b/packages/binary/valuetransfer/tangle/payloadmetadata.go
index d693c86ef09f99f746aff937840be6c690709219..4308e595612350142b745f365c96441ccc710bd3 100644
--- a/packages/binary/valuetransfer/tangle/payloadmetadata.go
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata.go
@@ -4,10 +4,10 @@ import (
 	"sync"
 	"time"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 	"github.com/iotaledger/hive.go/stringify"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
 )
 
@@ -60,17 +60,18 @@ func PayloadMetadataFromBytes(bytes []byte, optionalTargetObject ...*PayloadMeta
 	return
 }
 
-// PayloadMetadataFromStorage gets called when we restore transaction metadata from the storage. The bytes and the content will be
-// unmarshaled by an external caller using the binary.MarshalBinary interface.
-func PayloadMetadataFromStorage(id []byte) objectstorage.StorableObject {
-	result := &PayloadMetadata{}
+// PayloadMetadataFromStorageKey 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.ObjectStorageValue interface.
+func PayloadMetadataFromStorageKey(id []byte) (result objectstorage.StorableObject, err error, consumedBytes int) {
+	result = &PayloadMetadata{}
 
-	var err error
-	if result.payloadId, err = payload.ParseId(marshalutil.New(id)); err != nil {
-		panic(err)
+	marshalUtil := marshalutil.New(id)
+	if result.(*PayloadMetadata).payloadId, err = payload.ParseId(marshalUtil); err != nil {
+		return
 	}
+	consumedBytes = marshalUtil.ReadOffset()
 
-	return result
+	return
 }
 
 // ParsePayloadMetadata is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
@@ -125,7 +126,7 @@ func (payloadMetadata *PayloadMetadata) SetSolid(solid bool) (modified bool) {
 	return
 }
 
-// GetSoldificationTime returns the time when the payload was marked to be solid.
+// SoldificationTime returns the time when the payload was marked to be solid.
 func (payloadMetadata *PayloadMetadata) GetSoldificationTime() time.Time {
 	payloadMetadata.solidificationTimeMutex.RLock()
 	defer payloadMetadata.solidificationTimeMutex.RUnlock()
@@ -153,9 +154,9 @@ func (payloadMetadata *PayloadMetadata) String() string {
 	)
 }
 
-// GetStorageKey returns the key that is used to store the object in the database.
+// ObjectStorageKey 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 {
+func (payloadMetadata *PayloadMetadata) ObjectStorageKey() []byte {
 	return payloadMetadata.payloadId.Bytes()
 }
 
@@ -165,14 +166,14 @@ func (payloadMetadata *PayloadMetadata) Update(other objectstorage.StorableObjec
 	panic("update forbidden")
 }
 
-// MarshalBinary is required to match the encoding.BinaryMarshaler interface.
-func (payloadMetadata *PayloadMetadata) MarshalBinary() ([]byte, error) {
-	return payloadMetadata.Bytes(), nil
+// ObjectStorageValue is required to match the encoding.BinaryMarshaler interface.
+func (payloadMetadata *PayloadMetadata) ObjectStorageValue() []byte {
+	return payloadMetadata.Bytes()
 }
 
-// UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface.
-func (payloadMetadata *PayloadMetadata) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = PayloadMetadataFromBytes(data, payloadMetadata)
+// UnmarshalObjectStorageValue is required to match the encoding.BinaryUnmarshaler interface.
+func (payloadMetadata *PayloadMetadata) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	_, err, consumedBytes = PayloadMetadataFromBytes(data, payloadMetadata)
 
 	return
 }
diff --git a/packages/binary/valuetransfer/tangle/tangle.go b/packages/binary/valuetransfer/tangle/tangle.go
index 5c61b2b713d39b43e8e6d1b9de471e3bcd7de928..01b00b5050ab4c3a8561f7b626a4f636b3d19282 100644
--- a/packages/binary/valuetransfer/tangle/tangle.go
+++ b/packages/binary/valuetransfer/tangle/tangle.go
@@ -2,7 +2,6 @@ package tangle
 
 import (
 	"container/list"
-	"fmt"
 	"time"
 
 	"github.com/dgraph-io/badger/v2"
@@ -23,7 +22,9 @@ type Tangle struct {
 	payloadMetadataStorage *objectstorage.ObjectStorage
 	approverStorage        *objectstorage.ObjectStorage
 	missingPayloadStorage  *objectstorage.ObjectStorage
+	attachmentStorage      *objectstorage.ObjectStorage
 
+	consumerStorage                  *objectstorage.ObjectStorage
 	transactionOutputMetadataStorage *objectstorage.ObjectStorage
 	missingOutputStorage             *objectstorage.ObjectStorage
 
@@ -38,13 +39,20 @@ func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
 	result = &Tangle{
 		storageId: storageId,
 
-		payloadStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayload...), payload.FromStorage, objectstorage.CacheTime(time.Second)),
-		payloadMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayloadMetadata...), PayloadMetadataFromStorage, objectstorage.CacheTime(time.Second)),
-		approverStorage:        objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferApprover...), PayloadApproverFromStorage, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(payload.IdLength, payload.IdLength), objectstorage.KeysOnly(true)),
-		missingPayloadStorage:  objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingPayloadFromStorage, objectstorage.CacheTime(time.Second)),
+		// payload related storage
+		payloadStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayload...), payload.StorableObjectFromKey, objectstorage.CacheTime(time.Second)),
+		payloadMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayloadMetadata...), PayloadMetadataFromStorageKey, objectstorage.CacheTime(time.Second)),
+		missingPayloadStorage:  objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingPayloadFromStorageKey, objectstorage.CacheTime(time.Second)),
+		approverStorage:        objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferApprover...), PayloadApproverFromStorageKey, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(payload.IdLength, payload.IdLength), objectstorage.KeysOnly(true)),
 
-		transactionOutputMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), transaction.OutputFromStorage, objectstorage.CacheTime(time.Second)),
-		missingOutputStorage:             objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingOutputFromStorage, objectstorage.CacheTime(time.Second)),
+		// transaction related storage
+		transactionOutputMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.Layer0Approvers...), transaction.OutputFromStorageKey, objectstorage.CacheTime(time.Second)),
+		missingOutputStorage:             objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingOutputFromStorageKey, objectstorage.CacheTime(time.Second)),
+		consumerStorage:                  objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferConsumer...), transaction.OutputFromStorageKey, objectstorage.CacheTime(time.Second)),
+
+		attachmentStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferAttachment...), AttachmentFromStorageKey, objectstorage.CacheTime(time.Second)),
+
+		// transaction related storage
 
 		Events: *newEvents(),
 	}
@@ -67,6 +75,11 @@ func (tangle *Tangle) GetPayloadMetadata(payloadId payload.Id) *CachedPayloadMet
 	return &CachedPayloadMetadata{CachedObject: tangle.payloadMetadataStorage.Load(payloadId.Bytes())}
 }
 
+// GetPayloadMetadata retrieves the metadata of a value payload from the object storage.
+func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *CachedTransactionMetadata {
+	return &CachedTransactionMetadata{CachedObject: tangle.missingOutputStorage.Load(transactionId.Bytes())}
+}
+
 // GetApprovers retrieves the approvers of a payload from the object storage.
 func (tangle *Tangle) GetApprovers(transactionId payload.Id) CachedApprovers {
 	approvers := make(CachedApprovers, 0)
@@ -123,21 +136,6 @@ func (tangle *Tangle) storePayloadWorker(payloadToStore *payload.Payload) {
 	payloadId := payloadToStore.Id()
 	cachedMetadata := &CachedPayloadMetadata{CachedObject: tangle.payloadMetadataStorage.Store(NewPayloadMetadata(payloadId))}
 
-	// store trunk approver
-	trunkId := payloadToStore.TrunkId()
-	tangle.approverStorage.Store(NewPayloadApprover(trunkId, payloadId)).Release()
-
-	// store branch approver
-	if branchId := payloadToStore.BranchId(); branchId != trunkId {
-		tangle.approverStorage.Store(NewPayloadApprover(branchId, trunkId)).Release()
-	}
-
-	// trigger events
-	if tangle.missingPayloadStorage.DeleteIfPresent(payloadId.Bytes()) {
-		tangle.Events.MissingPayloadReceived.Trigger(cachedPayload, cachedMetadata)
-	}
-	tangle.Events.PayloadAttached.Trigger(cachedPayload, cachedMetadata)
-
 	// retrieve or store TransactionMetadata
 	newTransaction := false
 	transactionId := cachedPayload.Unwrap().Transaction().Id()
@@ -151,11 +149,33 @@ func (tangle *Tangle) storePayloadWorker(payloadToStore *payload.Payload) {
 		return result
 	})}
 
-	// if the transaction is new, store the Consumers.
+	// store trunk approver
+	trunkId := payloadToStore.TrunkId()
+	tangle.approverStorage.Store(NewPayloadApprover(trunkId, payloadId)).Release()
+
+	// store branch approver
+	if branchId := payloadToStore.BranchId(); branchId != trunkId {
+		tangle.approverStorage.Store(NewPayloadApprover(branchId, trunkId)).Release()
+	}
+
+	// store the consumers, the first time we see a Transaction
 	if newTransaction {
-		fmt.Println("git aWADD")
+		payloadToStore.Transaction().Inputs().ForEach(func(outputId transaction.OutputId) bool {
+			tangle.consumerStorage.Store(NewConsumer(outputId, transactionId))
+
+			return true
+		})
 	}
 
+	// store attachment
+	tangle.attachmentStorage.StoreIfAbsent(NewAttachment(payloadToStore.Transaction().Id(), payloadToStore.Id()))
+
+	// 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, cachedTransactionMetadata)
@@ -164,53 +184,73 @@ func (tangle *Tangle) storePayloadWorker(payloadToStore *payload.Payload) {
 
 // solidifyTransactionWorker is the worker function that solidifies the payloads (recursively from past to present).
 func (tangle *Tangle) solidifyTransactionWorker(cachedPayload *payload.CachedPayload, cachedMetadata *CachedPayloadMetadata, cachedTransactionMetadata *CachedTransactionMetadata) {
-	popElementsFromStack := func(stack *list.List) (*payload.CachedPayload, *CachedPayloadMetadata) {
+	popElementsFromStack := func(stack *list.List) (*payload.CachedPayload, *CachedPayloadMetadata, *CachedTransactionMetadata) {
 		currentSolidificationEntry := stack.Front()
-		currentCachedPayload := currentSolidificationEntry.Value.([2]interface{})[0]
-		currentCachedMetadata := currentSolidificationEntry.Value.([2]interface{})[1]
+		currentCachedPayload := currentSolidificationEntry.Value.([3]interface{})[0]
+		currentCachedMetadata := currentSolidificationEntry.Value.([3]interface{})[1]
+		currentCachedTransactionMetadata := currentSolidificationEntry.Value.([3]interface{})[2]
 		stack.Remove(currentSolidificationEntry)
 
-		return currentCachedPayload.(*payload.CachedPayload), currentCachedMetadata.(*CachedPayloadMetadata)
+		return currentCachedPayload.(*payload.CachedPayload), currentCachedMetadata.(*CachedPayloadMetadata), currentCachedTransactionMetadata.(*CachedTransactionMetadata)
 	}
 
 	// initialize the stack
 	solidificationStack := list.New()
-	solidificationStack.PushBack([2]interface{}{cachedPayload, cachedMetadata})
+	solidificationStack.PushBack([3]interface{}{cachedPayload, cachedMetadata, cachedTransactionMetadata})
 
 	// process payloads that are supposed to be checked for solidity recursively
 	for solidificationStack.Len() > 0 {
-		currentCachedPayload, currentCachedMetadata := popElementsFromStack(solidificationStack)
+		currentCachedPayload, currentCachedMetadata, currentCachedTransactionMetadata := popElementsFromStack(solidificationStack)
 
 		currentPayload := currentCachedPayload.Unwrap()
-		currentMetadata := currentCachedMetadata.Unwrap()
-		if currentPayload == nil || currentMetadata == nil {
+		currentPayloadMetadata := currentCachedMetadata.Unwrap()
+		currentTransaction := currentPayload.Transaction()
+		currentTransactionMetadata := currentCachedTransactionMetadata.Unwrap()
+		if currentPayload == nil || currentPayloadMetadata == nil || currentTransactionMetadata == nil {
 			currentCachedPayload.Release()
 			currentCachedMetadata.Release()
-			cachedTransactionMetadata.Release()
+			currentCachedTransactionMetadata.Release()
 
 			continue
 		}
 
-		// if current transaction is solid and was not marked as solid before: mark as solid and propagate
-		if tangle.isPayloadSolid(currentPayload, currentMetadata) && tangle.isTransactionSolid(currentPayload.Transaction(), cachedTransactionMetadata.Unwrap()) {
-			if currentMetadata.SetSolid(true) {
+		// if current transaction and payload are solid ...
+		if tangle.isPayloadSolid(currentPayload, currentPayloadMetadata) && tangle.isTransactionSolid(currentTransaction, currentTransactionMetadata) {
+			payloadBecameSolid := currentPayloadMetadata.SetSolid(true)
+			transactionBecameSolid := currentTransactionMetadata.SetSolid(true)
+
+			// if payload was marked as solid the first time ...
+			if payloadBecameSolid {
 				tangle.Events.PayloadSolid.Trigger(currentCachedPayload, currentCachedMetadata)
 
 				tangle.GetApprovers(currentPayload.Id()).Consume(func(approver *PayloadApprover) {
-					approverTransactionId := approver.GetApprovingPayloadId()
-
-					solidificationStack.PushBack([2]interface{}{
-						tangle.GetPayload(approverTransactionId),
-						tangle.GetPayloadMetadata(approverTransactionId),
+					approvingPayloadId := approver.GetApprovingPayloadId()
+					approvingCachedPayload := tangle.GetPayload(approvingPayloadId)
+
+					approvingCachedPayload.Consume(func(payload *payload.Payload) {
+						solidificationStack.PushBack([3]interface{}{
+							approvingCachedPayload,
+							tangle.GetPayloadMetadata(approvingPayloadId),
+							tangle.GetTransactionMetadata(payload.Transaction().Id()),
+						})
 					})
 				})
 			}
+
+			if transactionBecameSolid {
+				tangle.Events.TransactionSolid.Trigger(currentTransaction, currentTransactionMetadata)
+
+				currentTransaction.Inputs().ForEach(func(outputId transaction.OutputId) bool {
+					return true
+				})
+				//tangle.GetConsumers(outputId)
+			}
 		}
 
-		// release cached results
+		// release cached objects
 		currentCachedPayload.Release()
 		currentCachedMetadata.Release()
-		cachedTransactionMetadata.Release()
+		currentCachedTransactionMetadata.Release()
 	}
 }
 
diff --git a/packages/binary/valuetransfer/tangle/tangle_test.go b/packages/binary/valuetransfer/tangle/tangle_test.go
index 93e35e7d176b6acbff1b41055cb2b8c6267d8727..f2f371ecfa423785c3258768f6012c384a79dced 100644
--- a/packages/binary/valuetransfer/tangle/tangle_test.go
+++ b/packages/binary/valuetransfer/tangle/tangle_test.go
@@ -6,11 +6,11 @@ import (
 	"os"
 	"testing"
 
+	"github.com/iotaledger/hive.go/crypto/ed25519"
 	"github.com/iotaledger/hive.go/events"
 	"github.com/stretchr/testify/assert"
 	"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/balance"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
@@ -59,8 +59,8 @@ func TestTangle_AttachPayload(t *testing.T) {
 		metadata.Release()
 	}))
 
-	addressKeyPair1 := ed25119.GenerateKeyPair()
-	addressKeyPair2 := ed25119.GenerateKeyPair()
+	addressKeyPair1 := ed25519.GenerateKeyPair()
+	addressKeyPair2 := ed25519.GenerateKeyPair()
 
 	transferId1, _ := transaction.IdFromBase58("8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh")
 	transferId2, _ := transaction.IdFromBase58("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM")
diff --git a/packages/binary/valuetransfer/tangle/transactionmetadata.go b/packages/binary/valuetransfer/tangle/transactionmetadata.go
index 898c57a7002705de783894a1cd0a2b915d2c4ef8..98a36a7420195b7625f21ce93a3594c6b438eb6e 100644
--- a/packages/binary/valuetransfer/tangle/transactionmetadata.go
+++ b/packages/binary/valuetransfer/tangle/transactionmetadata.go
@@ -4,10 +4,10 @@ import (
 	"sync"
 	"time"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 	"github.com/iotaledger/hive.go/stringify"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
 
@@ -61,7 +61,7 @@ func TransactionMetadataFromBytes(bytes []byte, optionalTargetObject ...*Transac
 }
 
 // TransactionMetadataFromStorage is the factory method for TransactionMetadata objects stored in the objectstorage. The bytes and the content
-// will be filled by the objectstorage, by subsequently calling MarshalBinary.
+// will be filled by the objectstorage, by subsequently calling ObjectStorageValue.
 func TransactionMetadataFromStorage(storageKey []byte) objectstorage.StorableObject {
 	result := &TransactionMetadata{}
 
@@ -153,8 +153,8 @@ func (transactionMetadata *TransactionMetadata) String() string {
 	)
 }
 
-// GetStorageKey returns the key that is used to identify the TransactionMetadata in the objectstorage.
-func (transactionMetadata *TransactionMetadata) GetStorageKey() []byte {
+// ObjectStorageKey returns the key that is used to identify the TransactionMetadata in the objectstorage.
+func (transactionMetadata *TransactionMetadata) ObjectStorageKey() []byte {
 	return transactionMetadata.id.Bytes()
 }
 
@@ -163,16 +163,16 @@ func (transactionMetadata *TransactionMetadata) Update(other objectstorage.Stora
 	panic("update forbidden")
 }
 
-// MarshalBinary marshals the TransactionMetadata object into a sequence of bytes and matches the encoding.BinaryMarshaler
+// ObjectStorageValue marshals the TransactionMetadata object into a sequence of bytes and matches the encoding.BinaryMarshaler
 // interface.
-func (transactionMetadata *TransactionMetadata) MarshalBinary() ([]byte, error) {
-	return transactionMetadata.Bytes(), nil
+func (transactionMetadata *TransactionMetadata) ObjectStorageValue() []byte {
+	return transactionMetadata.Bytes()
 }
 
-// UnmarshalBinary restores the values of a TransactionMetadata object from a sequence of bytes and matches the
+// UnmarshalObjectStorageValue restores the values of a TransactionMetadata object from a sequence of bytes and matches the
 // encoding.BinaryUnmarshaler interface.
-func (transactionMetadata *TransactionMetadata) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = TransactionMetadataFromBytes(data, transactionMetadata)
+func (transactionMetadata *TransactionMetadata) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	_, err, consumedBytes = TransactionMetadataFromBytes(data, transactionMetadata)
 
 	return
 }
@@ -211,3 +211,6 @@ func (cachedTransactionMetadata *CachedTransactionMetadata) Unwrap() *Transactio
 		}
 	}
 }
+
+// Interface contract: make compiler warn if the interface is not implemented correctly.
+var _ objectstorage.StorableObject = &TransactionMetadata{}
diff --git a/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go b/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go
index 9e0c15feea79b708abdc6db89d573444d98da3ea..a4c7e97684cdb1cd3defa5fcf04622f059869f5d 100644
--- a/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go
+++ b/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go
@@ -4,10 +4,10 @@ import (
 	"sync"
 	"time"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 	"github.com/iotaledger/hive.go/stringify"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
 
@@ -61,7 +61,7 @@ func TransactionOutputMetadataFromBytes(bytes []byte, optionalTargetObject ...*T
 }
 
 // TransactionOutputMetadataFromStorage is the factory method for TransactionOutputMetadata objects stored in the objectstorage. The bytes and the content
-// will be filled by the objectstorage, by subsequently calling MarshalBinary.
+// will be filled by the objectstorage, by subsequently calling ObjectStorageValue.
 func TransactionOutputMetadataFromStorage(storageKey []byte) objectstorage.StorableObject {
 	result := &TransactionOutputMetadata{}
 
@@ -153,30 +153,32 @@ func (transactionOutputMetadata *TransactionOutputMetadata) String() string {
 	)
 }
 
-// GetStorageKey returns the key that is used to identify the TransactionOutputMetadata in the objectstorage.
-func (transactionOutputMetadata *TransactionOutputMetadata) GetStorageKey() []byte {
+// ObjectStorageKey returns the key that is used to identify the TransactionOutputMetadata in the objectstorage.
+func (transactionOutputMetadata *TransactionOutputMetadata) ObjectStorageKey() []byte {
 	return transactionOutputMetadata.id.Bytes()
 }
 
-// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
-func (transactionOutputMetadata *TransactionOutputMetadata) Update(other objectstorage.StorableObject) {
-	panic("update forbidden")
+// ObjectStorageValue returns the bytes, that are stored in the value part of the k/v store.
+func (transactionOutputMetadata *TransactionOutputMetadata) ObjectStorageValue() []byte {
+	return transactionOutputMetadata.Bytes()
 }
 
-// MarshalBinary marshals the TransactionOutputMetadata object into a sequence of bytes and matches the encoding.BinaryMarshaler
-// interface.
-func (transactionOutputMetadata *TransactionOutputMetadata) MarshalBinary() ([]byte, error) {
-	return transactionOutputMetadata.Bytes(), nil
-}
-
-// UnmarshalBinary restores the values of a TransactionOutputMetadata object from a sequence of bytes and matches the
+// UnmarshalObjectStorageValue restores the values of a TransactionOutputMetadata object from a sequence of bytes and matches the
 // encoding.BinaryUnmarshaler interface.
-func (transactionOutputMetadata *TransactionOutputMetadata) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = TransactionOutputMetadataFromBytes(data, transactionOutputMetadata)
+func (transactionOutputMetadata *TransactionOutputMetadata) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	_, err, consumedBytes = TransactionOutputMetadataFromBytes(data, transactionOutputMetadata)
 
 	return
 }
 
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+func (transactionOutputMetadata *TransactionOutputMetadata) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// Interface contract: make compiler warn if the interface is not implemented correctly.
+var _ objectstorage.StorableObject = &TransactionOutputMetadata{}
+
 // CachedTransactionOutputMetadata is a wrapper for the object storage, that takes care of type casting the TransactionOutputMetadata objects.
 // Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
 // that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
diff --git a/packages/binary/valuetransfer/transaction/id.go b/packages/binary/valuetransfer/transaction/id.go
index 99b954bb847782596fc0593a78c146e0a3b412e7..3352d007cedf0082fe16cf08bf31acebd8396483 100644
--- a/packages/binary/valuetransfer/transaction/id.go
+++ b/packages/binary/valuetransfer/transaction/id.go
@@ -4,9 +4,8 @@ import (
 	"crypto/rand"
 	"fmt"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/mr-tron/base58"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 // Id is the data type that represents the identifier for a Transaction.
diff --git a/packages/binary/valuetransfer/transaction/inputs.go b/packages/binary/valuetransfer/transaction/inputs.go
index 49f07e965add261dd8518f7f125e0489895e17ad..255a485027831cfde98e30845de50d6f2302d7f9 100644
--- a/packages/binary/valuetransfer/transaction/inputs.go
+++ b/packages/binary/valuetransfer/transaction/inputs.go
@@ -2,8 +2,9 @@ package transaction
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+
+	"github.com/iotaledger/hive.go/marshalutil"
 )
 
 type Inputs struct {
diff --git a/packages/binary/valuetransfer/transaction/output.go b/packages/binary/valuetransfer/transaction/output.go
index 949434dcb5c97c5749ccde643e0a0c0fb43db4a7..a5d0aab1908952bc29b8634f666c083579504fce 100644
--- a/packages/binary/valuetransfer/transaction/output.go
+++ b/packages/binary/valuetransfer/transaction/output.go
@@ -1,11 +1,11 @@
 package transaction
 
 import (
-	"fmt"
+	"time"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
 )
@@ -14,6 +14,8 @@ import (
 type Output struct {
 	address       address.Address
 	transactionId Id
+	solid         bool
+	solidSince    time.Time
 	balances      []*balance.Balance
 
 	objectstorage.StorableObjectFlags
@@ -25,19 +27,66 @@ func NewOutput(address address.Address, transactionId Id, balances []*balance.Ba
 	return &Output{
 		address:       address,
 		transactionId: transactionId,
+		solid:         false,
+		solidSince:    time.Time{},
 		balances:      balances,
 
 		storageKey: marshalutil.New().WriteBytes(address.Bytes()).WriteBytes(transactionId.Bytes()).Bytes(),
 	}
 }
 
-// OutputFromStorage get's called when we restore a Output from the storage.
+// OutputFromBytes unmarshals an Output object from a sequence of bytes.
+// It either creates a new object or fills the optionally provided object with the parsed information.
+func OutputFromBytes(bytes []byte, optionalTargetObject ...*Output) (result *Output, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Output{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to OutputFromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.address, err = address.Parse(marshalUtil); err != nil {
+		return
+	}
+	if result.transactionId, err = ParseId(marshalUtil); err != nil {
+		return
+	}
+	if result.solid, err = marshalUtil.ReadBool(); err != nil {
+		return
+	}
+	if result.solidSince, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	var balanceCount uint32
+	if balanceCount, err = marshalUtil.ReadUint32(); err != nil {
+		return
+	} else {
+		result.balances = make([]*balance.Balance, balanceCount)
+		for i := uint32(0); i < balanceCount; i++ {
+			result.balances[i], err = balance.Parse(marshalUtil)
+			if err != nil {
+				return
+			}
+		}
+	}
+	result.storageKey = marshalutil.New().WriteBytes(result.address.Bytes()).WriteBytes(result.transactionId.Bytes()).Bytes()
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// OutputFromStorageKey get's called when we restore a Output from the storage.
 // In contrast to other database models, it unmarshals some information from the key so we simply store the key before
-// it gets handed over to UnmarshalBinary (by the ObjectStorage).
-func OutputFromStorage(keyBytes []byte) objectstorage.StorableObject {
+// it gets handed over to UnmarshalObjectStorageValue (by the ObjectStorage).
+func OutputFromStorageKey(keyBytes []byte) (result objectstorage.StorableObject, err error, consumedBytes int) {
 	return &Output{
-		storageKey: marshalutil.New(keyBytes).Bytes(true),
-	}
+		storageKey: keyBytes[:OutputIdLength],
+	}, nil, OutputIdLength
 }
 
 // Address returns the address that this output belongs to.
@@ -55,58 +104,37 @@ func (output *Output) Balances() []*balance.Balance {
 	return output.balances
 }
 
-// MarshalBinary marshals the balances into a sequence of bytes - the address and transaction id are stored inside the key
+// ObjectStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (output *Output) ObjectStorageKey() []byte {
+	return marshalutil.New(OutputIdLength).
+		WriteBytes(output.address.Bytes()).
+		WriteBytes(output.transactionId.Bytes()).
+		Bytes()
+}
+
+// ObjectStorageValue marshals the balances into a sequence of bytes - the address and transaction id are stored inside the key
 // and are ignored here.
-func (output *Output) MarshalBinary() (data []byte, err error) {
+func (output *Output) ObjectStorageValue() (data []byte) {
 	// determine amount of balances in the output
 	balanceCount := len(output.balances)
 
 	// initialize helper
-	marshalUtil := marshalutil.New(4 + balanceCount*balance.Length)
-
-	// marshal the amount of balances
+	marshalUtil := marshalutil.New(marshalutil.BOOL_SIZE + marshalutil.TIME_SIZE + marshalutil.UINT32_SIZE + balanceCount*balance.Length)
+	marshalUtil.WriteBool(output.solid)
+	marshalUtil.WriteTime(output.solidSince)
 	marshalUtil.WriteUint32(uint32(balanceCount))
-
-	// marshal balances
-	for _, balance := range output.balances {
-		marshalUtil.WriteBytes(balance.Bytes())
+	for _, balanceToMarshal := range output.balances {
+		marshalUtil.WriteBytes(balanceToMarshal.Bytes())
 	}
 
 	return
 }
 
-// UnmarshalBinary restores a Output from a serialized version in the ObjectStorage with parts of the object
+// UnmarshalObjectStorageValue restores a Output from a serialized version in the ObjectStorage with parts of the object
 // being stored in its key rather than the content of the database to reduce storage requirements.
-func (output *Output) UnmarshalBinary(data []byte) (err error) {
-	// check if the storageKey has been set
-	if output.storageKey == nil {
-		return fmt.Errorf("missing storageKey when trying to unmarshal Output (it contains part of the information)")
-	}
-
-	// parse information from storageKey
-	storageKeyUnmarshaler := marshalutil.New(output.storageKey)
-	output.address, err = address.Parse(storageKeyUnmarshaler)
-	if err != nil {
-		return
-	}
-	output.transactionId, err = ParseId(storageKeyUnmarshaler)
-	if err != nil {
-		return
-	}
-
-	// parse information from content bytes
-	contentUnmarshaler := marshalutil.New(data)
-	balanceCount, err := contentUnmarshaler.ReadUint32()
-	if err != nil {
-		return
-	}
-	output.balances = make([]*balance.Balance, balanceCount)
-	for i := uint32(0); i < balanceCount; i++ {
-		output.balances[i], err = balance.Parse(contentUnmarshaler)
-		if err != nil {
-			return
-		}
-	}
+func (output *Output) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) {
+	_, err, consumedBytes = OutputFromBytes(marshalutil.New(output.storageKey).WriteBytes(data).Bytes(), output)
 
 	return
 }
@@ -116,11 +144,5 @@ func (output *Output) Update(other objectstorage.StorableObject) {
 	panic("this object should never be updated")
 }
 
-// GetStorageKey returns the key that is used to store the object in the database.
-// It is required to match StorableObject interface.
-func (output *Output) GetStorageKey() []byte {
-	return output.storageKey
-}
-
 // define contract (ensure that the struct fulfills the given interface)
 var _ objectstorage.StorableObject = &Output{}
diff --git a/packages/binary/valuetransfer/transaction/outputid.go b/packages/binary/valuetransfer/transaction/outputid.go
index 891cd5c09b678dfd7f097e179a25ce3fb6460c3f..6f0640c2f314f424f4711167a6869cbf60dcade9 100644
--- a/packages/binary/valuetransfer/transaction/outputid.go
+++ b/packages/binary/valuetransfer/transaction/outputid.go
@@ -3,7 +3,8 @@ package transaction
 import (
 	"github.com/mr-tron/base58"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/hive.go/marshalutil"
+
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 )
 
diff --git a/packages/binary/valuetransfer/transaction/outputs.go b/packages/binary/valuetransfer/transaction/outputs.go
index 3166ac938e9e355218f9a8632c926229dac6a50f..daf995bbea3799e65868699dad447f0f79ec9fd0 100644
--- a/packages/binary/valuetransfer/transaction/outputs.go
+++ b/packages/binary/valuetransfer/transaction/outputs.go
@@ -2,8 +2,9 @@ package transaction
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/hive.go/marshalutil"
+
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
 )
 
diff --git a/packages/binary/valuetransfer/transaction/signatures.go b/packages/binary/valuetransfer/transaction/signatures.go
index 843389e3cb50b025fe7895de56a1258e004bcb52..fd9a14240d1716fcf28254a2ad5337afde1379bf 100644
--- a/packages/binary/valuetransfer/transaction/signatures.go
+++ b/packages/binary/valuetransfer/transaction/signatures.go
@@ -1,8 +1,9 @@
 package transaction
 
 import (
+	"github.com/iotaledger/hive.go/marshalutil"
+
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
 )
diff --git a/packages/binary/valuetransfer/transaction/signatures_test.go b/packages/binary/valuetransfer/transaction/signatures_test.go
index a27426c1b16b22d03f41db1caa1b9dc94ab09111..a344780c8d4ba489e0149423e2c015a1e10179ea 100644
--- a/packages/binary/valuetransfer/transaction/signatures_test.go
+++ b/packages/binary/valuetransfer/transaction/signatures_test.go
@@ -3,9 +3,9 @@ package transaction
 import (
 	"testing"
 
+	"github.com/iotaledger/hive.go/crypto/ed25519"
 	"github.com/stretchr/testify/assert"
 
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
 )
@@ -13,8 +13,8 @@ import (
 func TestSignatures(t *testing.T) {
 	dataToSign := []byte("test")
 
-	address1SigScheme := signaturescheme.ED25519(ed25119.GenerateKeyPair())
-	address2SigScheme := signaturescheme.ED25519(ed25119.GenerateKeyPair())
+	address1SigScheme := signaturescheme.ED25519(ed25519.GenerateKeyPair())
+	address2SigScheme := signaturescheme.ED25519(ed25519.GenerateKeyPair())
 	address3SigScheme := signaturescheme.RandBLS()
 
 	signatures := NewSignatures()
diff --git a/packages/binary/valuetransfer/transaction/transaction.go b/packages/binary/valuetransfer/transaction/transaction.go
index 705a62ab8278e0bda05a23b95914aa91188853f3..0d685c6055cddad72ce21c3897e35c4302744a0a 100644
--- a/packages/binary/valuetransfer/transaction/transaction.go
+++ b/packages/binary/valuetransfer/transaction/transaction.go
@@ -4,12 +4,12 @@ import (
 	"fmt"
 	"sync"
 
+	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 	"github.com/iotaledger/hive.go/stringify"
 	"github.com/mr-tron/base58"
 	"golang.org/x/crypto/blake2b"
 
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
 )
@@ -45,6 +45,14 @@ func New(inputs *Inputs, outputs *Outputs) *Transaction {
 }
 
 func FromBytes(bytes []byte, optionalTargetObject ...*Transaction) (result *Transaction, err error, consumedBytes int) {
+	marshalUtil := marshalutil.New(bytes)
+	result, err = Parse(marshalUtil, optionalTargetObject...)
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func FromStorageKey(key []byte, optionalTargetObject ...*Transaction) (result objectstorage.StorableObject, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -52,63 +60,41 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Transaction) (result *Tran
 	case 1:
 		result = optionalTargetObject[0]
 	default:
-		panic("too many arguments in call to OutputFromBytes")
+		panic("too many arguments in call to FromStorageKey")
 	}
 
-	// initialize helper
-	marshalUtil := marshalutil.New(bytes)
-
-	// unmarshal inputs
-	parsedInputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return InputsFromBytes(data) })
+	marshalUtil := marshalutil.New(key)
+	id, err := ParseId(marshalUtil)
 	if err != nil {
 		return
 	}
-	result.inputs = parsedInputs.(*Inputs)
+	result.(*Transaction).id = &id
 
-	// unmarshal outputs
-	parsedOutputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return OutputsFromBytes(data) })
-	if err != nil {
-		return
+	return
+}
+
+func Parse(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Transaction) (result *Transaction, err error) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Transaction{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to Parse")
 	}
-	result.outputs = parsedOutputs.(*Outputs)
 
-	// store essence bytes
-	essenceBytesCount := marshalUtil.ReadOffset()
-	result.essenceBytes = make([]byte, essenceBytesCount)
-	copy(result.essenceBytes, bytes[:essenceBytesCount])
+	if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parseErr error, parsedBytes int) {
+		parseErr, parsedBytes = result.UnmarshalObjectStorageValue(data)
 
-	// unmarshal outputs
-	parsedSignatures, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return SignaturesFromBytes(data) })
-	if err != nil {
+		return
+	}); err != nil {
 		return
 	}
-	result.signatures = parsedSignatures.(*Signatures)
-
-	// store signature bytes
-	signatureBytesCount := marshalUtil.ReadOffset() - essenceBytesCount
-	result.signatureBytes = make([]byte, signatureBytesCount)
-	copy(result.signatureBytes, bytes[essenceBytesCount:essenceBytesCount+signatureBytesCount])
-
-	// return the number of bytes we processed
-	consumedBytes = essenceBytesCount + signatureBytesCount
-
-	// store bytes, so we don't have to marshal manually
-	result.bytes = bytes[:consumedBytes]
 
 	return
 }
 
-func FromStorage(key []byte) *Transaction {
-	id, err, _ := IdFromBytes(key)
-	if err != nil {
-		panic(err)
-	}
-
-	return &Transaction{
-		id: &id,
-	}
-}
-
 func (transaction *Transaction) Id() Id {
 	// acquire lock for reading id
 	transaction.idMutex.RLock()
@@ -280,23 +266,59 @@ func (transaction *Transaction) String() string {
 // define contract (ensure that the struct fulfills the given interface)
 var _ objectstorage.StorableObject = &Transaction{}
 
-func (transaction *Transaction) GetStorageKey() []byte {
-	id := transaction.Id()
-
-	return id[:]
+func (transaction *Transaction) ObjectStorageKey() []byte {
+	return transaction.Id().Bytes()
 }
 
 func (transaction *Transaction) Update(other objectstorage.StorableObject) {
 	panic("update forbidden")
 }
 
-// MarshalBinary returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler interface.
-func (transaction *Transaction) MarshalBinary() ([]byte, error) {
-	return transaction.Bytes(), nil
+// ObjectStorageValue returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler interface.
+func (transaction *Transaction) ObjectStorageValue() []byte {
+	return transaction.Bytes()
 }
 
-func (transaction *Transaction) UnmarshalBinary(bytes []byte) (err error) {
-	_, err, _ = FromBytes(bytes, transaction)
+func (transaction *Transaction) UnmarshalObjectStorageValue(bytes []byte) (err error, consumedBytes int) {
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// unmarshal inputs
+	parsedInputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return InputsFromBytes(data) })
+	if err != nil {
+		return
+	}
+	transaction.inputs = parsedInputs.(*Inputs)
+
+	// unmarshal outputs
+	parsedOutputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return OutputsFromBytes(data) })
+	if err != nil {
+		return
+	}
+	transaction.outputs = parsedOutputs.(*Outputs)
+
+	// store essence bytes
+	essenceBytesCount := marshalUtil.ReadOffset()
+	transaction.essenceBytes = make([]byte, essenceBytesCount)
+	copy(transaction.essenceBytes, bytes[:essenceBytesCount])
+
+	// unmarshal outputs
+	parsedSignatures, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return SignaturesFromBytes(data) })
+	if err != nil {
+		return
+	}
+	transaction.signatures = parsedSignatures.(*Signatures)
+
+	// store signature bytes
+	signatureBytesCount := marshalUtil.ReadOffset() - essenceBytesCount
+	transaction.signatureBytes = make([]byte, signatureBytesCount)
+	copy(transaction.signatureBytes, bytes[essenceBytesCount:essenceBytesCount+signatureBytesCount])
+
+	// return the number of bytes we processed
+	consumedBytes = essenceBytesCount + signatureBytesCount
+
+	// store bytes, so we don't have to marshal manually
+	transaction.bytes = bytes[:consumedBytes]
 
 	return
 }
diff --git a/packages/gossip/manager.go b/packages/gossip/manager.go
index d3d6ccba7be2b5275b2bfde6b7f8250c0b0f6ba0..00846158806c22b4bee306b82d6d5b81951ae98c 100644
--- a/packages/gossip/manager.go
+++ b/packages/gossip/manager.go
@@ -6,14 +6,16 @@ import (
 	"net"
 	"sync"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-
 	"github.com/golang/protobuf/proto"
-	pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
-	"github.com/iotaledger/goshimmer/packages/gossip/server"
+	"go.uber.org/zap"
+
 	"github.com/iotaledger/hive.go/autopeering/peer"
 	"github.com/iotaledger/hive.go/events"
-	"go.uber.org/zap"
+	"github.com/iotaledger/hive.go/identity"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
+	"github.com/iotaledger/goshimmer/packages/gossip/server"
 )
 
 const (
@@ -37,7 +39,7 @@ type Manager struct {
 
 	mu        sync.RWMutex
 	srv       *server.TCP
-	neighbors map[peer.ID]*Neighbor
+	neighbors map[identity.ID]*Neighbor
 	running   bool
 }
 
@@ -47,7 +49,7 @@ func NewManager(local *peer.Local, f GetTransaction, log *zap.SugaredLogger) *Ma
 		getTransaction: f,
 		log:            log,
 		srv:            nil,
-		neighbors:      make(map[peer.ID]*Neighbor),
+		neighbors:      make(map[identity.ID]*Neighbor),
 		running:        false,
 	}
 }
@@ -113,7 +115,7 @@ func (m *Manager) AddInbound(p *peer.Peer) error {
 }
 
 // NeighborRemoved disconnects the neighbor with the given ID.
-func (m *Manager) DropNeighbor(id peer.ID) error {
+func (m *Manager) DropNeighbor(id identity.ID) error {
 	n, err := m.removeNeighbor(id)
 	if err != nil {
 		return err
@@ -125,7 +127,7 @@ func (m *Manager) DropNeighbor(id peer.ID) error {
 
 // RequestTransaction requests the transaction with the given hash from the neighbors.
 // If no peer is provided, all neighbors are queried.
-func (m *Manager) RequestTransaction(txHash []byte, to ...peer.ID) {
+func (m *Manager) RequestTransaction(txHash []byte, to ...identity.ID) {
 	req := &pb.TransactionRequest{
 		Hash: txHash,
 	}
@@ -135,7 +137,7 @@ func (m *Manager) RequestTransaction(txHash []byte, to ...peer.ID) {
 
 // SendTransaction adds the given transaction data to the send queue of the neighbors.
 // The actual send then happens asynchronously. If no peer is provided, it is send to all neighbors.
-func (m *Manager) SendTransaction(txData []byte, to ...peer.ID) {
+func (m *Manager) SendTransaction(txData []byte, to ...identity.ID) {
 	tx := &pb.Transaction{
 		Data: txData,
 	}
@@ -153,14 +155,14 @@ func (m *Manager) GetAllNeighbors() []*Neighbor {
 	return result
 }
 
-func (m *Manager) getNeighbors(ids ...peer.ID) []*Neighbor {
+func (m *Manager) getNeighbors(ids ...identity.ID) []*Neighbor {
 	if len(ids) > 0 {
 		return m.getNeighborsById(ids)
 	}
 	return m.GetAllNeighbors()
 }
 
-func (m *Manager) getNeighborsById(ids []peer.ID) []*Neighbor {
+func (m *Manager) getNeighborsById(ids []identity.ID) []*Neighbor {
 	result := make([]*Neighbor, 0, len(ids))
 
 	m.mu.RLock()
@@ -173,7 +175,7 @@ func (m *Manager) getNeighborsById(ids []peer.ID) []*Neighbor {
 	return result
 }
 
-func (m *Manager) send(b []byte, to ...peer.ID) {
+func (m *Manager) send(b []byte, to ...identity.ID) {
 	neighbors := m.getNeighbors(to...)
 
 	for _, nbr := range neighbors {
@@ -219,7 +221,7 @@ func (m *Manager) addNeighbor(peer *peer.Peer, connectorFunc func(*peer.Peer) (n
 	return nil
 }
 
-func (m *Manager) removeNeighbor(id peer.ID) (*Neighbor, error) {
+func (m *Manager) removeNeighbor(id identity.ID) (*Neighbor, error) {
 	m.mu.Lock()
 	defer m.mu.Unlock()
 	if _, ok := m.neighbors[id]; !ok {
@@ -238,7 +240,7 @@ func (m *Manager) handlePacket(data []byte, p *peer.Peer) error {
 
 	switch pb.MType(data[0]) {
 
-	// Incoming Transaction
+	// Incoming Message
 	case pb.MTransaction:
 		msg := new(pb.Transaction)
 		if err := proto.Unmarshal(data[1:], msg); err != nil {
@@ -247,7 +249,7 @@ func (m *Manager) handlePacket(data []byte, p *peer.Peer) error {
 		m.log.Debugw("received message", "type", "TRANSACTION", "id", p.ID())
 		Events.TransactionReceived.Trigger(&TransactionReceivedEvent{Data: msg.GetData(), Peer: p})
 
-	// Incoming Transaction request
+	// Incoming Message request
 	case pb.MTransactionRequest:
 
 		msg := new(pb.TransactionRequest)
diff --git a/packages/gossip/manager_test.go b/packages/gossip/manager_test.go
index 220f55134afb7dffb6c5d7eff0a5ac800af5ccca..ab0e5b8288c01fe4ac2348abfdfdcb9ea1d09c32 100644
--- a/packages/gossip/manager_test.go
+++ b/packages/gossip/manager_test.go
@@ -7,9 +7,6 @@ import (
 	"time"
 
 	"github.com/golang/protobuf/proto"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
-	"github.com/iotaledger/goshimmer/packages/gossip/server"
 	"github.com/iotaledger/hive.go/autopeering/peer"
 	"github.com/iotaledger/hive.go/autopeering/peer/service"
 	"github.com/iotaledger/hive.go/database/mapdb"
@@ -18,6 +15,10 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/require"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
+	"github.com/iotaledger/goshimmer/packages/gossip/server"
 )
 
 const graceTime = 10 * time.Millisecond
diff --git a/packages/gossip/neighbor_test.go b/packages/gossip/neighbor_test.go
index 2ec1a2be9349dae2367d831b4367ae58b1baec00..841ed0dd9ca35478abd3fb55074fd2f17f6ce62e 100644
--- a/packages/gossip/neighbor_test.go
+++ b/packages/gossip/neighbor_test.go
@@ -1,7 +1,6 @@
 package gossip
 
 import (
-	"crypto/ed25519"
 	"net"
 	"sync"
 	"sync/atomic"
@@ -10,7 +9,9 @@ import (
 
 	"github.com/iotaledger/hive.go/autopeering/peer"
 	"github.com/iotaledger/hive.go/autopeering/peer/service"
+	"github.com/iotaledger/hive.go/crypto/ed25519"
 	"github.com/iotaledger/hive.go/events"
+	"github.com/iotaledger/hive.go/identity"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -126,9 +127,11 @@ func newTestPeer(name string, conn net.Conn) *peer.Peer {
 	services := service.New()
 	services.Update(service.PeeringKey, conn.LocalAddr().Network(), 0)
 	services.Update(service.GossipKey, conn.LocalAddr().Network(), 0)
-	key := make([]byte, ed25519.PublicKeySize)
-	copy(key, name)
-	return peer.NewPeer(key, net.IPv4zero, services)
+
+	var publicKey ed25519.PublicKey
+	copy(publicKey[:], name)
+
+	return peer.NewPeer(identity.New(publicKey), net.IPv4zero, services)
 }
 
 func newPipe() (net.Conn, net.Conn, func()) {
diff --git a/packages/gossip/server/server.go b/packages/gossip/server/server.go
index 80a1d183b1258a7f1a28434790a0d13759b6300b..133e92efce596ff0cf49e380059579825d59932e 100644
--- a/packages/gossip/server/server.go
+++ b/packages/gossip/server/server.go
@@ -17,6 +17,8 @@ import (
 	"github.com/iotaledger/hive.go/autopeering/peer/service"
 	pb "github.com/iotaledger/hive.go/autopeering/server/proto"
 	"github.com/iotaledger/hive.go/backoff"
+	"github.com/iotaledger/hive.go/crypto/ed25519"
+	"github.com/iotaledger/hive.go/identity"
 	"github.com/iotaledger/hive.go/netutil"
 	"go.uber.org/zap"
 )
@@ -72,9 +74,9 @@ type acceptMatcher struct {
 }
 
 type accept struct {
-	fromID peer.ID  // ID of the connecting peer
-	req    []byte   // raw data of the handshake request
-	conn   net.Conn // the actual network connection
+	fromID identity.ID // ID of the connecting peer
+	req    []byte      // raw data of the handshake request
+	conn   net.Conn    // the actual network connection
 }
 
 // ServeTCP creates the object and starts listening for incoming connections.
@@ -297,7 +299,7 @@ func (t *TCP) listenLoop() {
 
 		select {
 		case t.acceptReceived <- accept{
-			fromID: key.ID(),
+			fromID: identity.NewID(key),
 			req:    req,
 			conn:   conn,
 		}:
@@ -308,15 +310,15 @@ func (t *TCP) listenLoop() {
 	}
 }
 
-func (t *TCP) doHandshake(key peer.PublicKey, remoteAddr string, conn net.Conn) error {
+func (t *TCP) doHandshake(key ed25519.PublicKey, remoteAddr string, conn net.Conn) error {
 	reqData, err := newHandshakeRequest(remoteAddr)
 	if err != nil {
 		return err
 	}
 
 	pkt := &pb.Packet{
-		PublicKey: t.local.PublicKey(),
-		Signature: t.local.Sign(reqData),
+		PublicKey: t.local.PublicKey().Bytes(),
+		Signature: t.local.Sign(reqData).Bytes(),
 		Data:      reqData,
 	}
 	b, err := proto.Marshal(pkt)
@@ -353,7 +355,7 @@ func (t *TCP) doHandshake(key peer.PublicKey, remoteAddr string, conn net.Conn)
 	}
 
 	signer, err := peer.RecoverKeyFromSignedData(pkt)
-	if err != nil || !bytes.Equal(key, signer) {
+	if err != nil || !bytes.Equal(key.Bytes(), signer.Bytes()) {
 		return ErrInvalidHandshake
 	}
 	if !t.validateHandshakeResponse(pkt.GetData(), reqData) {
@@ -363,29 +365,29 @@ func (t *TCP) doHandshake(key peer.PublicKey, remoteAddr string, conn net.Conn)
 	return nil
 }
 
-func (t *TCP) readHandshakeRequest(conn net.Conn) (peer.PublicKey, []byte, error) {
+func (t *TCP) readHandshakeRequest(conn net.Conn) (ed25519.PublicKey, []byte, error) {
 	if err := conn.SetReadDeadline(time.Now().Add(handshakeTimeout)); err != nil {
-		return nil, nil, err
+		return ed25519.PublicKey{}, nil, err
 	}
 	b := make([]byte, maxHandshakePacketSize)
 	n, err := conn.Read(b)
 	if err != nil {
-		return nil, nil, fmt.Errorf("%w: %s", ErrInvalidHandshake, err.Error())
+		return ed25519.PublicKey{}, nil, fmt.Errorf("%w: %s", ErrInvalidHandshake, err.Error())
 	}
 
 	pkt := &pb.Packet{}
 	err = proto.Unmarshal(b[:n], pkt)
 	if err != nil {
-		return nil, nil, err
+		return ed25519.PublicKey{}, nil, err
 	}
 
 	key, err := peer.RecoverKeyFromSignedData(pkt)
 	if err != nil {
-		return nil, nil, err
+		return ed25519.PublicKey{}, nil, err
 	}
 
 	if !t.validateHandshakeRequest(pkt.GetData()) {
-		return nil, nil, ErrInvalidHandshake
+		return ed25519.PublicKey{}, nil, ErrInvalidHandshake
 	}
 
 	return key, pkt.GetData(), nil
@@ -398,8 +400,8 @@ func (t *TCP) writeHandshakeResponse(reqData []byte, conn net.Conn) error {
 	}
 
 	pkt := &pb.Packet{
-		PublicKey: t.local.PublicKey(),
-		Signature: t.local.Sign(data),
+		PublicKey: t.local.PublicKey().Bytes(),
+		Signature: t.local.Sign(data).Bytes(),
 		Data:      data,
 	}
 	b, err := proto.Marshal(pkt)
diff --git a/packages/gossip/server/server_test.go b/packages/gossip/server/server_test.go
index 983856704860aaa927a444461ada7838f09fc8da..5c62ef5ed25f72618b4fc87599b6321422768e62 100644
--- a/packages/gossip/server/server_test.go
+++ b/packages/gossip/server/server_test.go
@@ -58,7 +58,7 @@ func TestUnansweredDial(t *testing.T) {
 	// create peer with invalid gossip address
 	services := getPeer(transA).Services().CreateRecord()
 	services.Update(service.GossipKey, "tcp", 0)
-	unreachablePeer := peer.NewPeer(getPeer(transA).PublicKey(), net.ParseIP("127.0.0.1"), services)
+	unreachablePeer := peer.NewPeer(getPeer(transA).Identity, net.ParseIP("127.0.0.1"), services)
 
 	_, err := transA.DialPeer(unreachablePeer)
 	assert.Error(t, err)
@@ -82,7 +82,7 @@ func TestNoHandshakeResponse(t *testing.T) {
 	// create peer for the listener
 	services := getPeer(transA).Services().CreateRecord()
 	services.Update(service.GossipKey, lis.Addr().Network(), lis.Addr().(*net.TCPAddr).Port)
-	p := peer.NewPeer(getPeer(transA).PublicKey(), lis.Addr().(*net.TCPAddr).IP, services)
+	p := peer.NewPeer(getPeer(transA).Identity, lis.Addr().(*net.TCPAddr).IP, services)
 
 	_, err = transA.DialPeer(p)
 	assert.Error(t, err)
diff --git a/pluginmgr/core/plugins.go b/pluginmgr/core/plugins.go
index c7faaaf75c525471eb3df8a5847cba45c66aa455..a210f3d1b50a562b9711667c4dee1817a9afa649 100644
--- a/pluginmgr/core/plugins.go
+++ b/pluginmgr/core/plugins.go
@@ -9,9 +9,10 @@ import (
 	"github.com/iotaledger/goshimmer/plugins/gossip"
 	"github.com/iotaledger/goshimmer/plugins/gracefulshutdown"
 	"github.com/iotaledger/goshimmer/plugins/logger"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 	"github.com/iotaledger/goshimmer/plugins/metrics"
 	"github.com/iotaledger/goshimmer/plugins/portcheck"
-	"github.com/iotaledger/goshimmer/plugins/tangle"
+
 	"github.com/iotaledger/hive.go/node"
 )
 
@@ -23,7 +24,7 @@ var PLUGINS = node.Plugins(
 	portcheck.PLUGIN,
 	database.PLUGIN,
 	autopeering.PLUGIN,
-	tangle.PLUGIN,
+	messagelayer.PLUGIN,
 	gossip.PLUGIN,
 	gracefulshutdown.PLUGIN,
 	metrics.PLUGIN,
diff --git a/plugins/autopeering/autopeering.go b/plugins/autopeering/autopeering.go
index 658c39be6c7063fcf39041ac4ce660bf5c2bb41b..dcbeefd3350162221a24217e0c593ec63811bf78 100644
--- a/plugins/autopeering/autopeering.go
+++ b/plugins/autopeering/autopeering.go
@@ -9,17 +9,20 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
-	"github.com/iotaledger/goshimmer/plugins/banner"
-	"github.com/iotaledger/goshimmer/plugins/config"
-	"github.com/iotaledger/goshimmer/plugins/gossip"
 	"github.com/iotaledger/hive.go/autopeering/discover"
 	"github.com/iotaledger/hive.go/autopeering/peer"
 	"github.com/iotaledger/hive.go/autopeering/peer/service"
 	"github.com/iotaledger/hive.go/autopeering/selection"
 	"github.com/iotaledger/hive.go/autopeering/server"
+	"github.com/iotaledger/hive.go/crypto/ed25519"
+	"github.com/iotaledger/hive.go/identity"
 	"github.com/iotaledger/hive.go/logger"
 	"github.com/iotaledger/hive.go/node"
+
+	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
+	"github.com/iotaledger/goshimmer/plugins/banner"
+	"github.com/iotaledger/goshimmer/plugins/config"
+	"github.com/iotaledger/goshimmer/plugins/gossip"
 )
 
 // autopeering constants
@@ -160,11 +163,15 @@ func parseEntryNodes() (result []*peer.Peer, err error) {
 		if err != nil {
 			return nil, fmt.Errorf("%w: host cannot be resolved: %s", ErrParsingMasterNode, err)
 		}
+		publicKey, err, _ := ed25519.PublicKeyFromBytes(pubKey)
+		if err != nil {
+			return nil, err
+		}
 
 		services := service.New()
 		services.Update(service.PeeringKey, addr.Network(), addr.Port)
 
-		result = append(result, peer.NewPeer(pubKey, addr.IP, services))
+		result = append(result, peer.NewPeer(identity.New(publicKey), addr.IP, services))
 	}
 
 	return result, nil
diff --git a/plugins/gossip/gossip.go b/plugins/gossip/gossip.go
index d611032d64f0dc8890189f819f27cefb9ab0c330..4d71f3a459fc7260bfd0b44dc97d086e9acc28dd 100644
--- a/plugins/gossip/gossip.go
+++ b/plugins/gossip/gossip.go
@@ -9,12 +9,12 @@ import (
 	"github.com/iotaledger/hive.go/logger"
 	"github.com/iotaledger/hive.go/netutil"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 	gp "github.com/iotaledger/goshimmer/packages/gossip"
 	"github.com/iotaledger/goshimmer/packages/gossip/server"
 	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
 	"github.com/iotaledger/goshimmer/plugins/config"
-	"github.com/iotaledger/goshimmer/plugins/tangle"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 )
 
 var (
@@ -71,13 +71,13 @@ func start(shutdownSignal <-chan struct{}) {
 	log.Info("Stopping " + name + " ...")
 }
 
-func getTransaction(transactionId message.Id) (bytes []byte, err error) {
-	log.Debugw("get tx from db", "id", transactionId.String())
+func getTransaction(messageId message.Id) (bytes []byte, err error) {
+	log.Debugw("get tx from db", "id", messageId.String())
 
-	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *message.Transaction) {
-		bytes = transaction.Bytes()
+	if !messagelayer.Tangle.Message(messageId).Consume(func(message *message.Message) {
+		bytes = message.Bytes()
 	}) {
-		err = fmt.Errorf("transaction not found: hash=%s", transactionId)
+		err = fmt.Errorf("transaction not found: hash=%s", messageId)
 	}
 
 	return
diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go
index ceca12f9e3a1e12300f408d8e2595b25aaa8eaf2..37c4f064fcbe33abfe1d33c6ef194f60dc8ef1ca 100644
--- a/plugins/gossip/plugin.go
+++ b/plugins/gossip/plugin.go
@@ -8,11 +8,11 @@ import (
 	"github.com/iotaledger/hive.go/logger"
 	"github.com/iotaledger/hive.go/node"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle"
 	"github.com/iotaledger/goshimmer/packages/gossip"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
-	"github.com/iotaledger/goshimmer/plugins/tangle"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 )
 
 const name = "Gossip" // name of the plugin
@@ -73,20 +73,20 @@ func configureEvents() {
 
 	// configure flow of incoming transactions
 	gossip.Events.TransactionReceived.Attach(events.NewClosure(func(event *gossip.TransactionReceivedEvent) {
-		tangle.TransactionParser.Parse(event.Data, event.Peer)
+		messagelayer.TransactionParser.Parse(event.Data, event.Peer)
 	}))
 
 	// configure flow of outgoing transactions (gossip on solidification)
-	tangle.Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, transactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	messagelayer.Tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedMessage, transactionMetadata *tangle.CachedMessageMetadata) {
 		transactionMetadata.Release()
 
-		cachedTransaction.Consume(func(transaction *message.Transaction) {
+		cachedTransaction.Consume(func(transaction *message.Message) {
 			mgr.SendTransaction(transaction.Bytes())
 		})
 	}))
 
 	// request missing transactions
-	tangle.TransactionRequester.Events.SendRequest.Attach(events.NewClosure(func(transactionId message.Id) {
+	messagelayer.TransactionRequester.Events.SendRequest.Attach(events.NewClosure(func(transactionId message.Id) {
 		mgr.RequestTransaction(transactionId[:])
 	}))
 }
diff --git a/plugins/graph/plugin.go b/plugins/graph/plugin.go
index c1aedcb1988e963fb74c31d51fc944632f2ba2a9..f71c4ad692e396df2b610e87d1ddbdab51a0c3ee 100644
--- a/plugins/graph/plugin.go
+++ b/plugins/graph/plugin.go
@@ -6,7 +6,7 @@ import (
 	"time"
 
 	"github.com/iotaledger/goshimmer/plugins/config"
-	"github.com/iotaledger/goshimmer/plugins/tangle"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 
 	"golang.org/x/net/context"
 
@@ -102,10 +102,10 @@ func run(*node.Plugin) {
 
 	daemon.BackgroundWorker("Graph[NewTxWorker]", func(shutdownSignal <-chan struct{}) {
 		log.Info("Starting Graph[NewTxWorker] ... done")
-		tangle.Instance.Events.TransactionAttached.Attach(notifyNewTx)
+		messagelayer.Tangle.Events.TransactionAttached.Attach(notifyNewTx)
 		newTxWorkerPool.Start()
 		<-shutdownSignal
-		tangle.Instance.Events.TransactionAttached.Detach(notifyNewTx)
+		messagelayer.Tangle.Events.TransactionAttached.Detach(notifyNewTx)
 		newTxWorkerPool.Stop()
 		log.Info("Stopping Graph[NewTxWorker] ... done")
 	}, shutdown.ShutdownPriorityGraph)
diff --git a/plugins/messagelayer/plugin.go b/plugins/messagelayer/plugin.go
new file mode 100644
index 0000000000000000000000000000000000000000..d9faebe1c6859d2622ab2e8fd4fb59ee06e72d81
--- /dev/null
+++ b/plugins/messagelayer/plugin.go
@@ -0,0 +1,91 @@
+package messagelayer
+
+import (
+	"github.com/iotaledger/hive.go/autopeering/peer"
+
+	"github.com/iotaledger/hive.go/daemon"
+	"github.com/iotaledger/hive.go/events"
+	"github.com/iotaledger/hive.go/logger"
+	"github.com/iotaledger/hive.go/node"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/messagefactory"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/transactionparser"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/transactionrequester"
+	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
+	"github.com/iotaledger/goshimmer/packages/database"
+	"github.com/iotaledger/goshimmer/packages/shutdown"
+	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
+)
+
+const (
+	PLUGIN_NAME        = "MessageLayer"
+	DB_SEQUENCE_NUMBER = "seq"
+)
+
+var PLUGIN = node.NewPlugin(PLUGIN_NAME, node.Enabled, configure, run)
+
+var TransactionParser *transactionparser.TransactionParser
+
+var TransactionRequester *transactionrequester.TransactionRequester
+
+var TipSelector *tipselector.TipSelector
+
+var Tangle *tangle.Tangle
+
+var MessageFactory *messagefactory.MessageFactory
+
+var log *logger.Logger
+
+func configure(*node.Plugin) {
+	log = logger.NewLogger(PLUGIN_NAME)
+
+	// create instances
+	TransactionParser = transactionparser.New()
+	TransactionRequester = transactionrequester.New()
+	TipSelector = tipselector.New()
+	Tangle = tangle.New(database.GetBadgerInstance(), storageprefix.MainNet)
+
+	// Setup MessageFactory (behavior + logging))
+	MessageFactory = messagefactory.New(database.GetBadgerInstance(), local.GetInstance().LocalIdentity(), TipSelector, []byte(DB_SEQUENCE_NUMBER))
+	MessageFactory.Events.MessageConstructed.Attach(events.NewClosure(Tangle.AttachMessage))
+	MessageFactory.Events.Error.Attach(events.NewClosure(func(err error) {
+		log.Errorf("Error in MessageFactory: %v", err)
+	}))
+
+	// setup TransactionParser
+	TransactionParser.Events.TransactionParsed.Attach(events.NewClosure(func(transaction *message.Message, peer *peer.Peer) {
+		// TODO: ADD PEER
+
+		Tangle.AttachMessage(transaction)
+	}))
+
+	// setup TransactionRequester
+	Tangle.Events.TransactionMissing.Attach(events.NewClosure(TransactionRequester.ScheduleRequest))
+	Tangle.Events.MissingTransactionReceived.Attach(events.NewClosure(func(cachedTransaction *message.CachedMessage, cachedTransactionMetadata *tangle.CachedMessageMetadata) {
+		cachedTransactionMetadata.Release()
+
+		cachedTransaction.Consume(func(transaction *message.Message) {
+			TransactionRequester.StopRequest(transaction.GetId())
+		})
+	}))
+
+	// setup TipSelector
+	Tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedMessage, cachedTransactionMetadata *tangle.CachedMessageMetadata) {
+		cachedTransactionMetadata.Release()
+
+		cachedTransaction.Consume(TipSelector.AddTip)
+	}))
+}
+
+func run(*node.Plugin) {
+	_ = daemon.BackgroundWorker("Tangle", func(shutdownSignal <-chan struct{}) {
+		<-shutdownSignal
+
+		MessageFactory.Shutdown()
+		TransactionParser.Shutdown()
+		Tangle.Shutdown()
+	}, shutdown.ShutdownPriorityTangle)
+}
diff --git a/plugins/metrics/plugin.go b/plugins/metrics/plugin.go
index 501e05f9a2c41e36ac6838321afc629205dd0e4d..a07160af3d5e560cbfe12139151ed87decad0512 100644
--- a/plugins/metrics/plugin.go
+++ b/plugins/metrics/plugin.go
@@ -8,17 +8,17 @@ import (
 	"github.com/iotaledger/hive.go/node"
 	"github.com/iotaledger/hive.go/timeutil"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
-	"github.com/iotaledger/goshimmer/plugins/tangle"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 )
 
 var PLUGIN = node.NewPlugin("Metrics", node.Enabled, configure, run)
 
 func configure(plugin *node.Plugin) {
 	// increase received TPS counter whenever we receive a new transaction
-	tangle.Instance.Events.TransactionAttached.Attach(events.NewClosure(func(transaction *message.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
+	messagelayer.Tangle.Events.TransactionAttached.Attach(events.NewClosure(func(transaction *message.CachedMessage, metadata *tangle.CachedMessageMetadata) {
 		transaction.Release()
 		metadata.Release()
 
diff --git a/plugins/spa/explorer_routes.go b/plugins/spa/explorer_routes.go
index 7be338682180c3a68c11ce67f55b7b995c312537..e42e3f2673e1969f7de4fad4c461d727f200a9c9 100644
--- a/plugins/spa/explorer_routes.go
+++ b/plugins/spa/explorer_routes.go
@@ -4,8 +4,8 @@ import (
 	"net/http"
 	"sync"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/plugins/tangle"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 
 	"github.com/labstack/echo"
 	"github.com/pkg/errors"
@@ -23,10 +23,10 @@ type ExplorerTx struct {
 	MWM                      int    `json:"mwm"`
 }
 
-func createExplorerTx(tx *message.Transaction) (*ExplorerTx, error) {
+func createExplorerTx(tx *message.Message) (*ExplorerTx, error) {
 	transactionId := tx.GetId()
 
-	txMetadata := tangle.Instance.GetTransactionMetadata(transactionId)
+	txMetadata := messagelayer.Tangle.MessageMetadata(transactionId)
 
 	t := &ExplorerTx{
 		Hash:                     transactionId.String(),
@@ -116,7 +116,7 @@ func setupExplorerRoutes(routeGroup *echo.Group) {
 }
 
 func findTransaction(transactionId message.Id) (explorerTx *ExplorerTx, err error) {
-	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *message.Transaction) {
+	if !messagelayer.Tangle.Message(transactionId).Consume(func(transaction *message.Message) {
 		explorerTx, err = createExplorerTx(transaction)
 	}) {
 		err = errors.Wrapf(ErrNotFound, "tx hash: %s", transactionId.String())
diff --git a/plugins/spa/livefeed.go b/plugins/spa/livefeed.go
index adea985e1823779f4956d5ff7b08227cdd2dee7c..34851c6f03a583705c3cb6bd82e4fd12ea444a65 100644
--- a/plugins/spa/livefeed.go
+++ b/plugins/spa/livefeed.go
@@ -7,10 +7,10 @@ import (
 	"github.com/iotaledger/hive.go/events"
 	"github.com/iotaledger/hive.go/workerpool"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
-	"github.com/iotaledger/goshimmer/plugins/tangle"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 )
 
 var liveFeedWorkerCount = 1
@@ -19,7 +19,7 @@ var liveFeedWorkerPool *workerpool.WorkerPool
 
 func configureLiveFeed() {
 	liveFeedWorkerPool = workerpool.New(func(task workerpool.Task) {
-		task.Param(0).(*message.CachedTransaction).Consume(func(transaction *message.Transaction) {
+		task.Param(0).(*message.CachedMessage).Consume(func(transaction *message.Message) {
 			sendToAllWSClient(&msg{MsgTypeTx, &tx{transaction.GetId().String(), 0}})
 		})
 
@@ -29,7 +29,7 @@ func configureLiveFeed() {
 
 func runLiveFeed() {
 	newTxRateLimiter := time.NewTicker(time.Second / 10)
-	notifyNewTx := events.NewClosure(func(tx *message.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
+	notifyNewTx := events.NewClosure(func(tx *message.CachedMessage, metadata *tangle.CachedMessageMetadata) {
 		metadata.Release()
 
 		select {
@@ -41,11 +41,11 @@ func runLiveFeed() {
 	})
 
 	daemon.BackgroundWorker("SPA[TxUpdater]", func(shutdownSignal <-chan struct{}) {
-		tangle.Instance.Events.TransactionAttached.Attach(notifyNewTx)
+		messagelayer.Tangle.Events.TransactionAttached.Attach(notifyNewTx)
 		liveFeedWorkerPool.Start()
 		<-shutdownSignal
 		log.Info("Stopping SPA[TxUpdater] ...")
-		tangle.Instance.Events.TransactionAttached.Detach(notifyNewTx)
+		messagelayer.Tangle.Events.TransactionAttached.Detach(notifyNewTx)
 		newTxRateLimiter.Stop()
 		liveFeedWorkerPool.Stop()
 		log.Info("Stopping SPA[TxUpdater] ... done")
diff --git a/plugins/tangle/plugin.go b/plugins/tangle/plugin.go
deleted file mode 100644
index 0b3f156dc0d154a602a032cd5ddd4c1e89b41449..0000000000000000000000000000000000000000
--- a/plugins/tangle/plugin.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package tangle
-
-import (
-	"github.com/iotaledger/hive.go/autopeering/peer"
-
-	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/tipselector"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionrequester"
-	"github.com/iotaledger/goshimmer/packages/database"
-	"github.com/iotaledger/goshimmer/packages/shutdown"
-
-	"github.com/iotaledger/hive.go/daemon"
-	"github.com/iotaledger/hive.go/events"
-	"github.com/iotaledger/hive.go/logger"
-	"github.com/iotaledger/hive.go/node"
-)
-
-var PLUGIN = node.NewPlugin("Tangle", node.Enabled, configure, run)
-
-var TransactionParser *transactionparser.TransactionParser
-
-var TransactionRequester *transactionrequester.TransactionRequester
-
-var TipSelector *tipselector.TipSelector
-
-var Instance *tangle.Tangle
-
-var log *logger.Logger
-
-func configure(*node.Plugin) {
-	log = logger.NewLogger("Tangle")
-
-	// create instances
-	TransactionParser = transactionparser.New()
-	TransactionRequester = transactionrequester.New()
-	TipSelector = tipselector.New()
-	Instance = tangle.New(database.GetBadgerInstance(), storageprefix.MainNet)
-
-	// setup TransactionParser
-	TransactionParser.Events.TransactionParsed.Attach(events.NewClosure(func(transaction *message.Transaction, peer *peer.Peer) {
-		// TODO: ADD PEER
-
-		Instance.AttachTransaction(transaction)
-	}))
-
-	// setup TransactionRequester
-	Instance.Events.TransactionMissing.Attach(events.NewClosure(TransactionRequester.ScheduleRequest))
-	Instance.Events.MissingTransactionReceived.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
-		cachedTransactionMetadata.Release()
-
-		cachedTransaction.Consume(func(transaction *message.Transaction) {
-			TransactionRequester.StopRequest(transaction.GetId())
-		})
-	}))
-
-	// setup TipSelector
-	Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
-		cachedTransactionMetadata.Release()
-
-		cachedTransaction.Consume(TipSelector.AddTip)
-	}))
-}
-
-func run(*node.Plugin) {
-	_ = daemon.BackgroundWorker("Tangle", func(shutdownSignal <-chan struct{}) {
-		<-shutdownSignal
-
-		TransactionParser.Shutdown()
-		Instance.Shutdown()
-	}, shutdown.ShutdownPriorityTangle)
-}
diff --git a/plugins/webapi/broadcastData/plugin.go b/plugins/webapi/broadcastData/plugin.go
index 1139efbd272def8819273bf6c8e0582c4908e694..c0cb7a5042e0461bbbca47cb1e47b67bef7b3191 100644
--- a/plugins/webapi/broadcastData/plugin.go
+++ b/plugins/webapi/broadcastData/plugin.go
@@ -3,11 +3,11 @@ package broadcastData
 import (
 	"net/http"
 
-	"github.com/iotaledger/goshimmer/packages/model/value_transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 	"github.com/iotaledger/goshimmer/plugins/webapi"
 	"github.com/iotaledger/hive.go/logger"
 	"github.com/iotaledger/hive.go/node"
-	"github.com/iotaledger/hive.go/typeutils"
 	"github.com/labstack/echo"
 )
 
@@ -28,49 +28,9 @@ func broadcastData(c echo.Context) error {
 		return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
 	}
 
-	log.Debug("Received - address:", request.Address, " data:", request.Data)
+	tx := messagelayer.MessageFactory.IssuePayload(payload.NewData([]byte(request.Data)))
 
-	tx := value_transaction.New()
-	tx.SetHead(true)
-	tx.SetTail(true)
-
-	buffer := make([]byte, 2187)
-	if len(request.Data) > 2187 {
-		log.Warnf("data exceeds 2187 byte limit - (payload data size: %d)", len(request.Data))
-		return c.JSON(http.StatusBadRequest, Response{Error: "data exceeds 2187 byte limit"})
-	}
-
-	copy(buffer, typeutils.StringToBytes(request.Data))
-
-	// TODO: FIX FOR NEW TX LAYOUT
-
-	/*
-		trytes, err := trinary.BytesToTrytes(buffer)
-		if err != nil {
-			log.Warnf("trytes conversion failed: %s", err.Error())
-			return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
-		}
-
-		err = address.ValidAddress(request.Address)
-		if err != nil {
-			log.Warnf("invalid Address: %s", request.Address)
-			return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
-		}
-
-		tx.SetAddress(request.Address)
-		tx.SetSignatureMessageFragment(trytes)
-		tx.SetValue(0)
-		tx.SetBranchTransactionHash(tipselectionn.GetRandomTip())
-		tx.SetTrunkTransactionHash(tipselectionn.GetRandomTip(tx.GetBranchTransactionHash()))
-		tx.SetTimestamp(uint(time.Now().Unix()))
-		if err := tx.DoProofOfWork(meta_transaction.MIN_WEIGHT_MAGNITUDE); err != nil {
-			log.Warnf("PoW failed: %s", err)
-			return c.JSON(http.StatusInternalServerError, Response{Error: err.Error()})
-		}
-
-		gossip.Events.TransactionReceived.Trigger(&gossip.TransactionReceivedEvent{Data: tx.GetBytes(), Peer: &local.GetInstance().Peer})
-	*/
-	return c.JSON(http.StatusOK, Response{Hash: tx.GetHash()})
+	return c.JSON(http.StatusOK, Response{Hash: tx.GetId().String()})
 }
 
 type Response struct {
diff --git a/plugins/webapi/getNeighbors/plugin.go b/plugins/webapi/getNeighbors/plugin.go
index 5f4f45677c361007c3d080bfe19b67552f05c00d..acedd3f3f92bf5a705511f02099fbc51283b0c4d 100644
--- a/plugins/webapi/getNeighbors/plugin.go
+++ b/plugins/webapi/getNeighbors/plugin.go
@@ -39,7 +39,7 @@ func getNeighbors(c echo.Context) error {
 		for _, peer := range autopeering.Discovery.GetVerifiedPeers() {
 			n := Neighbor{
 				ID:        peer.ID().String(),
-				PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey()),
+				PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey().Bytes()),
 			}
 			n.Services = getServices(peer)
 			knownPeers = append(knownPeers, n)
@@ -49,7 +49,7 @@ func getNeighbors(c echo.Context) error {
 	for _, peer := range autopeering.Selection.GetOutgoingNeighbors() {
 		n := Neighbor{
 			ID:        peer.ID().String(),
-			PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey()),
+			PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey().Bytes()),
 		}
 		n.Services = getServices(peer)
 		chosen = append(chosen, n)
@@ -57,7 +57,7 @@ func getNeighbors(c echo.Context) error {
 	for _, peer := range autopeering.Selection.GetIncomingNeighbors() {
 		n := Neighbor{
 			ID:        peer.ID().String(),
-			PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey()),
+			PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey().Bytes()),
 		}
 		n.Services = getServices(peer)
 		accepted = append(accepted, n)
diff --git a/plugins/webapi/gtta/plugin.go b/plugins/webapi/gtta/plugin.go
index ebebea7b8acf07d9f3d1af755c218c2c55e57644..6ecd48a8f756956a22dbdb6028917c7271b34e5d 100644
--- a/plugins/webapi/gtta/plugin.go
+++ b/plugins/webapi/gtta/plugin.go
@@ -3,11 +3,12 @@ package gtta
 import (
 	"net/http"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
-	"github.com/iotaledger/goshimmer/plugins/tangle"
-	"github.com/iotaledger/goshimmer/plugins/webapi"
 	"github.com/iotaledger/hive.go/node"
 	"github.com/labstack/echo"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
+	"github.com/iotaledger/goshimmer/plugins/webapi"
 )
 
 var PLUGIN = node.NewPlugin("WebAPI GTTA Endpoint", node.Disabled, func(plugin *node.Plugin) {
@@ -15,7 +16,7 @@ var PLUGIN = node.NewPlugin("WebAPI GTTA Endpoint", node.Disabled, func(plugin *
 })
 
 func Handler(c echo.Context) error {
-	trunkTransactionId, branchTransactionId := tangle.TipSelector.GetTips()
+	trunkTransactionId, branchTransactionId := messagelayer.TipSelector.GetTips()
 
 	return c.JSON(http.StatusOK, Response{
 		TrunkTransaction:  trunkTransactionId,
diff --git a/plugins/webapi/spammer/plugin.go b/plugins/webapi/spammer/plugin.go
index 0b827a85c9b82b59053b043948dff098bb1d8300..b83f0b6e390439b81c4c3c1f4c46edb6ca7ff5f6 100644
--- a/plugins/webapi/spammer/plugin.go
+++ b/plugins/webapi/spammer/plugin.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/spammer"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
-	"github.com/iotaledger/goshimmer/plugins/tangle"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 	"github.com/iotaledger/goshimmer/plugins/webapi"
 
 	"github.com/iotaledger/hive.go/node"
@@ -16,7 +16,8 @@ var transactionSpammer *spammer.Spammer
 var PLUGIN = node.NewPlugin("Spammer", node.Disabled, configure, run)
 
 func configure(plugin *node.Plugin) {
-	transactionSpammer = spammer.New(tangle.TransactionParser, tangle.TipSelector)
+	transactionSpammer = spammer.New(messagelayer.MessageFactory)
+
 	webapi.Server.GET("spammer", handleRequest)
 }
 
diff --git a/plugins/webapi/spammer/webapi.go b/plugins/webapi/spammer/webapi.go
index f020f8478abbc0edd89f13550c15a88ee416f7be..f85f6e623d9723911ed5e731b64d8d0f707214fb 100644
--- a/plugins/webapi/spammer/webapi.go
+++ b/plugins/webapi/spammer/webapi.go
@@ -2,7 +2,6 @@ package spammer
 
 import (
 	"net/http"
-	"strconv"
 
 	"github.com/labstack/echo"
 )
@@ -22,15 +21,6 @@ func handleRequest(c echo.Context) error {
 		transactionSpammer.Shutdown()
 		transactionSpammer.Start(request.Tps)
 		return c.JSON(http.StatusOK, Response{Message: "started spamming transactions"})
-	case "burst":
-		if request.Tps == 0 {
-			return c.JSON(http.StatusBadRequest, Response{Error: "burst requires the tps to be set"})
-		}
-
-		transactionSpammer.Shutdown()
-		transactionSpammer.Burst(request.Tps)
-
-		return c.JSON(http.StatusOK, Response{Message: "sent a burst of " + strconv.Itoa(request.Tps) + " transactions"})
 	case "stop":
 		transactionSpammer.Shutdown()
 		return c.JSON(http.StatusOK, Response{Message: "stopped spamming transactions"})