diff --git a/go.sum b/go.sum
index cde30b4b760f323146f0a3998bf9693ef5c89032..c7afddc9654d282ad4946272d093c7b783843df3 100644
--- a/go.sum
+++ b/go.sum
@@ -137,6 +137,8 @@ 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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/iotaledger/hive.go v0.0.0-20200225104639-95ee10a0e675 h1:rlN7zGXPTms6tg4OoD4rTbca71X/wffneMvScmrwMp8=
+github.com/iotaledger/hive.go v0.0.0-20200225104639-95ee10a0e675/go.mod h1:wj3bFHlcX0NiEOWu5+WOg/MI/5N7PKCFnyaziaylB64=
 github.com/iotaledger/hive.go v0.0.0-20200227215953-967611cd154b h1:fmFIWBJt73KvQpUNuhTWpdseHrKIgb1Hz7g0a6K4S0M=
 github.com/iotaledger/hive.go v0.0.0-20200227215953-967611cd154b/go.mod h1:wj3bFHlcX0NiEOWu5+WOg/MI/5N7PKCFnyaziaylB64=
 github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8=
diff --git a/packages/binary/valuetangle/test/payload_test.go b/packages/binary/valuetangle/test/payload_test.go
deleted file mode 100644
index ffee05e890a65972cc842298627f35a3ba12cb8a..0000000000000000000000000000000000000000
--- a/packages/binary/valuetangle/test/payload_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package test
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/iotaledger/goshimmer/packages/binary/valuetangle"
-)
-
-func TestPayload(t *testing.T) {
-	inputs := valuetangle.NewTransferInputs().Add(
-		valuetangle.NewTransferOutputId(valuetangle.NewAddress([]byte("test")), valuetangle.NewTransferId([]byte("test"))),
-	).Add(
-		valuetangle.NewTransferOutputId(valuetangle.NewAddress([]byte("test")), valuetangle.NewTransferId([]byte("test1"))),
-	)
-
-	transfer := valuetangle.NewTransfer(inputs)
-	bytes, err := transfer.MarshalBinary()
-	if err != nil {
-		t.Error(err)
-
-		return
-	}
-
-	var restoredTransfer valuetangle.Transfer
-	fmt.Println(restoredTransfer.UnmarshalBinary(bytes))
-	fmt.Println(bytes, nil)
-	fmt.Println(restoredTransfer.MarshalBinary())
-}
diff --git a/packages/binary/valuetangle/transfer.go b/packages/binary/valuetangle/transfer.go
deleted file mode 100644
index b7fb56a6de06b43e8d56fee81d35d042f4eca024..0000000000000000000000000000000000000000
--- a/packages/binary/valuetangle/transfer.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package valuetangle
-
-import (
-	"sync"
-
-	"github.com/iotaledger/hive.go/objectstorage"
-	"golang.org/x/crypto/blake2b"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-)
-
-type Transfer struct {
-	objectstorage.StorableObjectFlags
-
-	id     *TransferId
-	inputs *TransferInputs
-	bytes  []byte
-
-	idMutex    sync.RWMutex
-	bytesMutex sync.RWMutex
-}
-
-func NewTransfer(inputs *TransferInputs) *Transfer {
-	return &Transfer{
-		inputs: inputs,
-	}
-}
-
-func FromStorage(key []byte) *Transfer {
-	id := NewTransferId(key)
-
-	return &Transfer{
-		id: &id,
-	}
-}
-
-func (transfer *Transfer) GetId() TransferId {
-	// acquire lock for reading id
-	transfer.idMutex.RLock()
-
-	// return if id has been calculated already
-	if transfer.id != nil {
-		defer transfer.idMutex.RUnlock()
-
-		return *transfer.id
-	}
-
-	// switch to write lock
-	transfer.idMutex.RUnlock()
-	transfer.idMutex.Lock()
-	defer transfer.idMutex.Unlock()
-
-	// return if id has been calculated in the mean time
-	if transfer.id != nil {
-		return *transfer.id
-	}
-
-	// otherwise calculate the id
-	bytes, err := transfer.MarshalBinary()
-	if err != nil {
-		panic(err)
-	}
-	idBytes := blake2b.Sum256(bytes)
-	transferId := NewTransferId(idBytes[:])
-
-	// cache result for later calls
-	transfer.id = &transferId
-
-	return transferId
-}
-
-func (transfer *Transfer) Update(other objectstorage.StorableObject) {
-	panic("update forbidden")
-}
-
-func (transfer *Transfer) GetStorageKey() []byte {
-	id := transfer.GetId()
-
-	return id[:]
-}
-
-// MarshalBinary returns a bytes representation of the transfer by implementing the encoding.BinaryMarshaler interface.
-func (transfer *Transfer) MarshalBinary() ([]byte, error) {
-	// acquired read lock on bytes
-	transfer.bytesMutex.RLock()
-
-	// return bytes if the object has been marshaled already
-	if transfer.bytes != nil {
-		defer transfer.bytesMutex.RUnlock()
-
-		return transfer.bytes, nil
-	}
-
-	// switch to write lock
-	transfer.bytesMutex.RUnlock()
-	transfer.bytesMutex.Lock()
-	defer transfer.bytesMutex.Unlock()
-
-	// return bytes if the object has been marshaled in the mean time
-	if bytes := transfer.bytes; bytes != nil {
-		return bytes, nil
-	}
-
-	// create marshal helper
-	marshalUtil := marshalutil.New()
-
-	// marshal inputs
-	marshalUtil.WriteBytes(transfer.inputs.ToBytes())
-
-	// store marshaled result
-	transfer.bytes = marshalUtil.Bytes()
-
-	return transfer.bytes, nil
-}
-
-func (transfer *Transfer) UnmarshalBinary(data []byte) error {
-	marshalUtil := marshalutil.New(data)
-
-	// unmarshal inputs
-	if parseResult, err := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) {
-		return TransferInputsFromBytes(data)
-	}); err != nil {
-		return err
-	} else {
-		transfer.inputs = parseResult.(*TransferInputs)
-	}
-
-	return nil
-}
-
-// define contracts (ensure that the struct fulfills the corresponding interfaces
-var _ objectstorage.StorableObject = &Transfer{}
diff --git a/packages/binary/valuetangle/address.go b/packages/binary/valuetransfers/address.go
similarity index 60%
rename from packages/binary/valuetangle/address.go
rename to packages/binary/valuetransfers/address.go
index 0d1119b324a20ff0edfc03564a01dead879c7695..ecf5f38739da463d601b2682eb13d38710f3490f 100644
--- a/packages/binary/valuetangle/address.go
+++ b/packages/binary/valuetransfers/address.go
@@ -1,4 +1,8 @@
-package valuetangle
+package valuetransfers
+
+import (
+	"github.com/mr-tron/base58"
+)
 
 type AddressVersion = byte
 
@@ -20,4 +24,12 @@ func (address *Address) GetDigest() AddressDigest {
 	return address[1:]
 }
 
+func (address Address) ToBytes() []byte {
+	return address[:]
+}
+
+func (address Address) String() string {
+	return "Address(" + base58.Encode(address.ToBytes()) + ")"
+}
+
 const AddressLength = 33
diff --git a/packages/binary/valuetransfers/color.go b/packages/binary/valuetransfers/color.go
new file mode 100644
index 0000000000000000000000000000000000000000..29150eb215d1b8a6a00467944cf9038969c0eb7c
--- /dev/null
+++ b/packages/binary/valuetransfers/color.go
@@ -0,0 +1,37 @@
+package valuetransfers
+
+import (
+	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+type Color [ColorLength]byte
+
+func ColorFromBytes(bytes []byte) (result Color, err error, consumedBytes int) {
+	colorBytes, err := marshalutil.New(bytes).ReadBytes(ColorLength)
+	if err != nil {
+		return
+	}
+	copy(result[:], colorBytes)
+
+	consumedBytes = ColorLength
+
+	return
+}
+
+const ColorLength = 32
+
+func (color Color) Bytes() []byte {
+	return color[:]
+}
+
+func (color Color) String() string {
+	if color == COLOR_IOTA {
+		return "IOTA"
+	}
+
+	return base58.Encode(color[:])
+}
+
+var COLOR_IOTA Color = [32]byte{}
diff --git a/packages/binary/valuetransfers/coloredbalance.go b/packages/binary/valuetransfers/coloredbalance.go
new file mode 100644
index 0000000000000000000000000000000000000000..2a9a5f6620fac323c784e7d4c4f26e9a1ecd640a
--- /dev/null
+++ b/packages/binary/valuetransfers/coloredbalance.go
@@ -0,0 +1,57 @@
+package valuetransfers
+
+import (
+	"strconv"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+type ColoredBalance struct {
+	color   Color
+	balance int64
+}
+
+func NewColoredBalance(color Color, balance int64) (result *ColoredBalance) {
+	result = &ColoredBalance{
+		color:   color,
+		balance: balance,
+	}
+
+	return
+}
+
+func ColoredBalanceFromBytes(bytes []byte) (result *ColoredBalance, err error, consumedBytes int) {
+	result = &ColoredBalance{}
+
+	marshalUtil := marshalutil.New(bytes)
+
+	if color, colorErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) {
+		return ColorFromBytes(data)
+	}); colorErr != nil {
+		return nil, colorErr, marshalUtil.ReadOffset()
+	} else {
+		result.color = color.(Color)
+	}
+
+	result.balance, err = marshalUtil.ReadInt64()
+	if err != nil {
+		return
+	}
+
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func (balance *ColoredBalance) ToBytes() []byte {
+	marshalUtil := marshalutil.New(ColorLength + marshalutil.UINT32_SIZE)
+
+	marshalUtil.WriteBytes(balance.color.Bytes())
+	marshalUtil.WriteInt64(balance.balance)
+
+	return marshalUtil.Bytes()
+}
+
+func (balance *ColoredBalance) String() string {
+	return strconv.FormatInt(balance.balance, 10) + " " + balance.color.String()
+}
diff --git a/packages/binary/valuetransfers/outputs.go b/packages/binary/valuetransfers/outputs.go
new file mode 100644
index 0000000000000000000000000000000000000000..a672469734932ad92b33edb285845054bdd98444
--- /dev/null
+++ b/packages/binary/valuetransfers/outputs.go
@@ -0,0 +1,154 @@
+package valuetransfers
+
+import (
+	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+type Outputs struct {
+	*orderedmap.OrderedMap
+}
+
+func NewOutputs(outputs map[Address][]*ColoredBalance) (result *Outputs) {
+	result = &Outputs{orderedmap.New()}
+	for address, balances := range outputs {
+		result.Add(address, balances)
+	}
+
+	return
+}
+
+// OutputsFromBytes reads the bytes and unmarshals the given information into an *Outputs object. It either creates a
+// new object, or uses the optional object provided in the arguments.
+func OutputsFromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Outputs{orderedmap.New()}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to OutputFromBytes")
+	}
+
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// read number of addresses in the outputs
+	addressCount, addressCountErr := marshalUtil.ReadUint32()
+	if addressCountErr != nil {
+		err = addressCountErr
+
+		return
+	}
+
+	// iterate the corresponding times and collect addresses + their details
+	for i := uint32(0); i < addressCount; i++ {
+		// read address
+		addressBytes, addressErr := marshalUtil.ReadBytes(AddressLength)
+		if addressErr != nil {
+			err = addressErr
+
+			return
+		}
+		address := NewAddress(addressBytes)
+
+		// read number of balances in the outputs
+		balanceCount, balanceCountErr := marshalUtil.ReadUint32()
+		if balanceCountErr != nil {
+			err = balanceCountErr
+
+			return
+		}
+
+		// iterate the corresponding times and collect balances
+		coloredBalances := make([]*ColoredBalance, balanceCount)
+		for j := uint32(0); j < balanceCount; j++ {
+			coloredBalance, coloredBalanceErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ColoredBalanceFromBytes(data) })
+			if coloredBalanceErr != nil {
+				err = coloredBalanceErr
+
+				return
+			}
+
+			coloredBalances[j] = coloredBalance.(*ColoredBalance)
+		}
+
+		// add the gathered information as an output
+		result.Add(address, coloredBalances)
+	}
+
+	// return the number of bytes we processed
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func (outputs *Outputs) Add(address Address, balances []*ColoredBalance) *Outputs {
+	outputs.Set(address, balances)
+
+	return outputs
+}
+
+func (outputs *Outputs) ForEach(consumer func(address Address, balances []*ColoredBalance)) {
+	outputs.OrderedMap.ForEach(func(key, value interface{}) bool {
+		consumer(key.(Address), value.([]*ColoredBalance))
+
+		return true
+	})
+}
+
+func (outputs *Outputs) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	if outputs == nil {
+		marshalUtil.WriteUint32(0)
+
+		return marshalUtil.Bytes()
+	}
+
+	marshalUtil.WriteUint32(uint32(outputs.Size()))
+	outputs.ForEach(func(address Address, balances []*ColoredBalance) {
+		marshalUtil.WriteBytes(address.ToBytes())
+		marshalUtil.WriteUint32(uint32(len(balances)))
+
+		for _, balance := range balances {
+			marshalUtil.WriteBytes(balance.ToBytes())
+		}
+	})
+
+	return marshalUtil.Bytes()
+}
+
+func (outputs *Outputs) String() string {
+	if outputs == nil {
+		return "<nil>"
+	}
+
+	result := "[\n"
+	empty := true
+	outputs.ForEach(func(address Address, balances []*ColoredBalance) {
+		empty = false
+
+		result += "    " + address.String() + ": [\n"
+
+		balancesEmpty := true
+		for _, balance := range balances {
+			balancesEmpty = false
+
+			result += "        " + balance.String() + ",\n"
+		}
+
+		if balancesEmpty {
+			result += "        <empty>\n"
+		}
+
+		result += "    ]\n"
+	})
+
+	if empty {
+		result += "    <empty>\n"
+	}
+
+	return result + "]"
+}
diff --git a/packages/binary/valuetangle/payload.go b/packages/binary/valuetransfers/payload.go
similarity index 99%
rename from packages/binary/valuetangle/payload.go
rename to packages/binary/valuetransfers/payload.go
index 659bbd09554e77bc3c7132510a6f2d4738b2ae28..f613b37d0997b46a27d144dab962bfdea9df7d39 100644
--- a/packages/binary/valuetangle/payload.go
+++ b/packages/binary/valuetransfers/payload.go
@@ -1,4 +1,4 @@
-package valuetangle
+package valuetransfers
 
 import (
 	"sync"
diff --git a/packages/binary/valuetransfers/payload/id/id.go b/packages/binary/valuetransfers/payload/id/id.go
new file mode 100644
index 0000000000000000000000000000000000000000..8f9847f54fd82e1d017095934152e26f6005478a
--- /dev/null
+++ b/packages/binary/valuetransfers/payload/id/id.go
@@ -0,0 +1,21 @@
+package id
+
+import (
+	"github.com/mr-tron/base58"
+)
+
+type Id [Length]byte
+
+func New(idBytes []byte) (result Id) {
+	copy(result[:], idBytes)
+
+	return
+}
+
+func (id Id) String() string {
+	return base58.Encode(id[:])
+}
+
+var Empty Id
+
+const Length = 32
diff --git a/packages/binary/valuetransfers/payload/payload.go b/packages/binary/valuetransfers/payload/payload.go
new file mode 100644
index 0000000000000000000000000000000000000000..1b45a4eca4b447a6382d7a89dcdde68214fdea33
--- /dev/null
+++ b/packages/binary/valuetransfers/payload/payload.go
@@ -0,0 +1,203 @@
+package payload
+
+import (
+	"sync"
+
+	"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/transaction/payload"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer"
+	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
+)
+
+type Payload struct {
+	objectstorage.StorableObjectFlags
+
+	id              *payloadid.Id
+	trunkPayloadId  payloadid.Id
+	branchPayloadId payloadid.Id
+	transfer        *transfer.Transfer
+	bytes           []byte
+
+	idMutex    sync.RWMutex
+	bytesMutex sync.RWMutex
+}
+
+func NewPayload(trunkPayloadId, branchPayloadId payloadid.Id, valueTransfer *transfer.Transfer) *Payload {
+	return &Payload{
+		trunkPayloadId:  trunkPayloadId,
+		branchPayloadId: branchPayloadId,
+		transfer:        valueTransfer,
+	}
+}
+
+func (payload *Payload) GetId() payloadid.Id {
+	// acquire lock for reading id
+	payload.idMutex.RLock()
+
+	// return if id has been calculated already
+	if payload.id != nil {
+		defer payload.idMutex.RUnlock()
+
+		return *payload.id
+	}
+
+	// switch to write lock
+	payload.idMutex.RUnlock()
+	payload.idMutex.Lock()
+	defer payload.idMutex.Unlock()
+
+	// return if id has been calculated in the mean time
+	if payload.id != nil {
+		return *payload.id
+	}
+
+	// otherwise calculate the id
+	transferId := payload.GetTransfer().GetId()
+	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length + transferid.Length)
+	marshalUtil.WriteBytes(payload.trunkPayloadId[:])
+	marshalUtil.WriteBytes(payload.branchPayloadId[:])
+	marshalUtil.WriteBytes(transferId[:])
+	var id payloadid.Id = blake2b.Sum256(marshalUtil.Bytes())
+	payload.id = &id
+
+	return id
+}
+
+func (payload *Payload) GetTrunkPayloadId() payloadid.Id {
+	return payload.trunkPayloadId
+}
+
+func (payload *Payload) GetBranchPayloadId() payloadid.Id {
+	return payload.branchPayloadId
+}
+
+func (payload *Payload) GetTransfer() *transfer.Transfer {
+	return payload.transfer
+}
+
+func (payload *Payload) Bytes() (bytes []byte) {
+	// acquire lock for reading bytes
+	payload.bytesMutex.RLock()
+
+	// return if bytes have been determined already
+	if bytes = payload.bytes; bytes != nil {
+		defer payload.bytesMutex.RUnlock()
+
+		return
+	}
+
+	// switch to write lock
+	payload.bytesMutex.RUnlock()
+	payload.bytesMutex.Lock()
+	defer payload.bytesMutex.Unlock()
+
+	// return if bytes have been determined in the mean time
+	if bytes = payload.bytes; bytes != nil {
+		return
+	}
+
+	// retrieve bytes of transfer
+	transferBytes, err := payload.GetTransfer().MarshalBinary()
+	if err != nil {
+		return
+	}
+
+	// marshal fields
+	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length + transferid.Length)
+	marshalUtil.WriteBytes(payload.trunkPayloadId[:])
+	marshalUtil.WriteBytes(payload.branchPayloadId[:])
+	marshalUtil.WriteBytes(transferBytes)
+	bytes = marshalUtil.Bytes()
+
+	// store result
+	payload.bytes = bytes
+
+	return
+}
+
+func (payload *Payload) String() string {
+	return stringify.Struct("Payload",
+		stringify.StructField("id", payload.GetId()),
+		stringify.StructField("trunk", payload.GetTrunkPayloadId()),
+		stringify.StructField("branch", payload.GetBranchPayloadId()),
+		stringify.StructField("transfer", payload.GetTransfer()),
+	)
+}
+
+// region Payload implementation ///////////////////////////////////////////////////////////////////////////////////////
+
+var Type = payload.Type(1)
+
+func (payload *Payload) GetType() payload.Type {
+	return Type
+}
+
+func (payload *Payload) MarshalBinary() (bytes []byte, err error) {
+	return payload.Bytes(), nil
+}
+
+func (payload *Payload) UnmarshalBinary(data []byte) (err error) {
+	marshalUtil := marshalutil.New(data)
+
+	trunkTransactionIdBytes, err := marshalUtil.ReadBytes(payloadid.Length)
+	if err != nil {
+		return
+	}
+
+	branchTransactionIdBytes, err := marshalUtil.ReadBytes(payloadid.Length)
+	if err != nil {
+		return
+	}
+
+	valueTransfer := &transfer.Transfer{}
+	if err = valueTransfer.UnmarshalBinary(marshalUtil.ReadRemainingBytes()); err != nil {
+		return
+	}
+
+	payload.trunkPayloadId = payloadid.New(trunkTransactionIdBytes)
+	payload.branchPayloadId = payloadid.New(branchTransactionIdBytes)
+	payload.transfer = valueTransfer
+	payload.bytes = data
+
+	return
+}
+
+func init() {
+	payload.RegisterType(Type, func(data []byte) (payload payload.Payload, err error) {
+		payload = &Payload{}
+		err = payload.UnmarshalBinary(data)
+
+		return
+	})
+}
+
+// define contract (ensure that the struct fulfills the corresponding interface)
+var _ payload.Payload = &Payload{}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// region StorableObject implementation ////////////////////////////////////////////////////////////////////////////////
+
+// MarshalBinary() (bytes []byte, err error) already implemented by Payload
+
+// UnmarshalBinary(data []byte) (err error) already implemented by Payload
+
+func (payload *Payload) GetStorageKey() []byte {
+	id := payload.GetId()
+
+	return id[:]
+}
+
+func (payload *Payload) Update(other objectstorage.StorableObject) {
+	panic("a Payload should never be updated")
+}
+
+// define contract (ensure that the struct fulfills the corresponding interface)
+var _ objectstorage.StorableObject = &Payload{}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetransfers/payload/transfer/address/address.go b/packages/binary/valuetransfers/payload/transfer/address/address.go
new file mode 100644
index 0000000000000000000000000000000000000000..22947afa4936af755cd8e9a0f1b204dd2cd4cb5e
--- /dev/null
+++ b/packages/binary/valuetransfers/payload/transfer/address/address.go
@@ -0,0 +1,35 @@
+package address
+
+import (
+	"github.com/mr-tron/base58"
+)
+
+type AddressVersion = byte
+
+type AddressDigest = []byte
+
+type Address [Length]byte
+
+func New(bytes []byte) (address Address) {
+	copy(address[:], bytes)
+
+	return
+}
+
+func (address *Address) GetVersion() AddressVersion {
+	return address[0]
+}
+
+func (address *Address) GetDigest() AddressDigest {
+	return address[1:]
+}
+
+func (address Address) ToBytes() []byte {
+	return address[:]
+}
+
+func (address Address) String() string {
+	return "Address(" + base58.Encode(address.ToBytes()) + ")"
+}
+
+const Length = 33
diff --git a/packages/binary/valuetransfers/payload/transfer/coloredbalance/color/color.go b/packages/binary/valuetransfers/payload/transfer/coloredbalance/color/color.go
new file mode 100644
index 0000000000000000000000000000000000000000..a06aa5536462519ec0c33236854fb99fbd5cd3cc
--- /dev/null
+++ b/packages/binary/valuetransfers/payload/transfer/coloredbalance/color/color.go
@@ -0,0 +1,37 @@
+package color
+
+import (
+	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+type Color [Length]byte
+
+func FromBytes(bytes []byte) (result Color, err error, consumedBytes int) {
+	colorBytes, err := marshalutil.New(bytes).ReadBytes(Length)
+	if err != nil {
+		return
+	}
+	copy(result[:], colorBytes)
+
+	consumedBytes = Length
+
+	return
+}
+
+const Length = 32
+
+func (color Color) Bytes() []byte {
+	return color[:]
+}
+
+func (color Color) String() string {
+	if color == COLOR_IOTA {
+		return "IOTA"
+	}
+
+	return base58.Encode(color[:])
+}
+
+var COLOR_IOTA Color = [32]byte{}
diff --git a/packages/binary/valuetransfers/payload/transfer/coloredbalance/coloredbalance.go b/packages/binary/valuetransfers/payload/transfer/coloredbalance/coloredbalance.go
new file mode 100644
index 0000000000000000000000000000000000000000..1cae2c86ef6178b58c18b5b0df0453375c81f8cb
--- /dev/null
+++ b/packages/binary/valuetransfers/payload/transfer/coloredbalance/coloredbalance.go
@@ -0,0 +1,58 @@
+package coloredbalance
+
+import (
+	"strconv"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	color2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/coloredbalance/color"
+)
+
+type ColoredBalance struct {
+	color   color2.Color
+	balance int64
+}
+
+func New(color color2.Color, balance int64) (result *ColoredBalance) {
+	result = &ColoredBalance{
+		color:   color,
+		balance: balance,
+	}
+
+	return
+}
+
+func FromBytes(bytes []byte) (result *ColoredBalance, err error, consumedBytes int) {
+	result = &ColoredBalance{}
+
+	marshalUtil := marshalutil.New(bytes)
+
+	if coinColor, colorErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) {
+		return color2.FromBytes(data)
+	}); colorErr != nil {
+		return nil, colorErr, marshalUtil.ReadOffset()
+	} else {
+		result.color = coinColor.(color2.Color)
+	}
+
+	result.balance, err = marshalUtil.ReadInt64()
+	if err != nil {
+		return
+	}
+
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func (balance *ColoredBalance) ToBytes() []byte {
+	marshalUtil := marshalutil.New(color2.Length + marshalutil.UINT32_SIZE)
+
+	marshalUtil.WriteBytes(balance.color.Bytes())
+	marshalUtil.WriteInt64(balance.balance)
+
+	return marshalUtil.Bytes()
+}
+
+func (balance *ColoredBalance) String() string {
+	return strconv.FormatInt(balance.balance, 10) + " " + balance.color.String()
+}
diff --git a/packages/binary/valuetransfers/payload/transfer/id/id.go b/packages/binary/valuetransfers/payload/transfer/id/id.go
new file mode 100644
index 0000000000000000000000000000000000000000..3f25a8790726f6240cadae26e566ddd68941b430
--- /dev/null
+++ b/packages/binary/valuetransfers/payload/transfer/id/id.go
@@ -0,0 +1,19 @@
+package id
+
+import (
+	"github.com/mr-tron/base58"
+)
+
+type Id [Length]byte
+
+func New(idBytes []byte) (result Id) {
+	copy(result[:], idBytes)
+
+	return
+}
+
+func (id Id) String() string {
+	return base58.Encode(id[:])
+}
+
+const Length = 32
diff --git a/packages/binary/valuetransfers/payload/transfer/inputs/inputs.go b/packages/binary/valuetransfers/payload/transfer/inputs/inputs.go
new file mode 100644
index 0000000000000000000000000000000000000000..ad13389f764064252d3d62dce88634ae819211a0
--- /dev/null
+++ b/packages/binary/valuetransfers/payload/transfer/inputs/inputs.go
@@ -0,0 +1,135 @@
+package inputs
+
+import (
+	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	address2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
+	id2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/output/id"
+)
+
+type Inputs struct {
+	*orderedmap.OrderedMap
+}
+
+func New(transferOutputIds ...id2.Id) (inputs *Inputs) {
+	inputs = &Inputs{orderedmap.New()}
+	for _, transferOutputId := range transferOutputIds {
+		inputs.Add(transferOutputId)
+	}
+
+	return
+}
+
+func FromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
+	inputs = New()
+
+	marshalUtil := marshalutil.New(bytes)
+	inputCount, err := marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+
+	for i := uint32(0); i < inputCount; i++ {
+		addressBytes, readErr := marshalUtil.ReadBytes(address2.Length)
+		if readErr != nil {
+			err = readErr
+
+			return
+		}
+		readAddress := address2.New(addressBytes)
+
+		transferIdBytes, readErr := marshalUtil.ReadBytes(id2.Length)
+		if readErr != nil {
+			err = readErr
+
+			return
+		}
+		transferId := id.New(transferIdBytes)
+
+		addressMap, addressExists := inputs.Get(readAddress)
+		if !addressExists {
+			addressMap = orderedmap.New()
+
+			inputs.Set(readAddress, addressMap)
+		}
+		addressMap.(*orderedmap.OrderedMap).Set(transferId, id2.New(readAddress, transferId))
+	}
+
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func (inputs *Inputs) Add(input id2.Id) *Inputs {
+	inputAddress := input.GetAddress()
+	transferId := input.GetTransferId()
+
+	addressMap, addressExists := inputs.Get(inputAddress)
+	if !addressExists {
+		addressMap = orderedmap.New()
+
+		inputs.Set(inputAddress, addressMap)
+	}
+
+	addressMap.(*orderedmap.OrderedMap).Set(transferId, input)
+
+	return inputs
+}
+
+func (inputs *Inputs) ToBytes() (bytes []byte) {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteSeek(4)
+	var inputCounter uint32
+	inputs.ForEach(func(transferOutputId id2.Id) {
+		marshalUtil.WriteBytes(transferOutputId.ToBytes())
+
+		inputCounter++
+	})
+	marshalUtil.WriteSeek(0)
+	marshalUtil.WriteUint32(inputCounter)
+
+	return marshalUtil.Bytes()
+}
+
+func (inputs *Inputs) ForEach(consumer func(transferOutputId id2.Id)) {
+	inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
+		value.(*orderedmap.OrderedMap).ForEach(func(key, value interface{}) bool {
+			consumer(value.(id2.Id))
+
+			return true
+		})
+
+		return true
+	})
+}
+
+func (inputs *Inputs) ForEachAddress(consumer func(currentAddress address2.Address)) {
+	inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
+		consumer(key.(address2.Address))
+
+		return true
+	})
+}
+
+func (inputs *Inputs) String() string {
+	if inputs == nil {
+		return "<nil>"
+	}
+
+	result := "[\n"
+
+	empty := true
+	inputs.ForEach(func(transferOutputId id2.Id) {
+		empty = false
+
+		result += "    " + transferOutputId.String() + ",\n"
+	})
+
+	if empty {
+		result += "    <empty>\n"
+	}
+
+	return result + "]"
+}
diff --git a/packages/binary/valuetransfers/payload/transfer/output/id/id.go b/packages/binary/valuetransfers/payload/transfer/output/id/id.go
new file mode 100644
index 0000000000000000000000000000000000000000..159dd3d9d7ee0913bbbd8661ce2338e98072cab8
--- /dev/null
+++ b/packages/binary/valuetransfers/payload/transfer/output/id/id.go
@@ -0,0 +1,41 @@
+package id
+
+import (
+	"github.com/mr-tron/base58"
+
+	address2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/address"
+	id2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
+)
+
+type Id [Length]byte
+
+func New(outputAddress address2.Address, transferId id2.Id) (transferOutputId Id) {
+	copy(transferOutputId[:address2.Length], outputAddress.ToBytes())
+	copy(transferOutputId[address2.Length:], transferId[:])
+
+	return
+}
+
+func FromBytes(bytes []byte) (transferOutputId Id) {
+	copy(transferOutputId[:], bytes)
+
+	return
+}
+
+func (transferOutputId Id) GetAddress() address2.Address {
+	return address2.New(transferOutputId[:address2.Length])
+}
+
+func (transferOutputId Id) GetTransferId() id2.Id {
+	return id2.New(transferOutputId[address2.Length:])
+}
+
+func (transferOutputId Id) ToBytes() []byte {
+	return transferOutputId[:]
+}
+
+func (transferOutputId Id) String() string {
+	return "Id(" + base58.Encode(transferOutputId[:]) + ")"
+}
+
+const Length = address2.Length + id2.Length
diff --git a/packages/binary/valuetransfers/payload/transfer/outputs/outputs.go b/packages/binary/valuetransfers/payload/transfer/outputs/outputs.go
new file mode 100644
index 0000000000000000000000000000000000000000..f89c40ec1c84d4e2c9d9e5cb9a364358ea860b7d
--- /dev/null
+++ b/packages/binary/valuetransfers/payload/transfer/outputs/outputs.go
@@ -0,0 +1,156 @@
+package outputs
+
+import (
+	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	address2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/address"
+	coloredbalance2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/coloredbalance"
+)
+
+type Outputs struct {
+	*orderedmap.OrderedMap
+}
+
+func New(outputs map[address2.Address][]*coloredbalance2.ColoredBalance) (result *Outputs) {
+	result = &Outputs{orderedmap.New()}
+	for address, balances := range outputs {
+		result.Add(address, balances)
+	}
+
+	return
+}
+
+// FromBytes reads the bytes and unmarshals the given information into an *Outputs object. It either creates a
+// new object, or uses the optional object provided in the arguments.
+func FromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Outputs{orderedmap.New()}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to OutputFromBytes")
+	}
+
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// read number of addresses in the outputs
+	addressCount, addressCountErr := marshalUtil.ReadUint32()
+	if addressCountErr != nil {
+		err = addressCountErr
+
+		return
+	}
+
+	// iterate the corresponding times and collect addresses + their details
+	for i := uint32(0); i < addressCount; i++ {
+		// read address
+		addressBytes, addressErr := marshalUtil.ReadBytes(address2.Length)
+		if addressErr != nil {
+			err = addressErr
+
+			return
+		}
+		address := address2.New(addressBytes)
+
+		// read number of balances in the outputs
+		balanceCount, balanceCountErr := marshalUtil.ReadUint32()
+		if balanceCountErr != nil {
+			err = balanceCountErr
+
+			return
+		}
+
+		// iterate the corresponding times and collect balances
+		coloredBalances := make([]*coloredbalance2.ColoredBalance, balanceCount)
+		for j := uint32(0); j < balanceCount; j++ {
+			coloredBalance, coloredBalanceErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return coloredbalance2.FromBytes(data) })
+			if coloredBalanceErr != nil {
+				err = coloredBalanceErr
+
+				return
+			}
+
+			coloredBalances[j] = coloredBalance.(*coloredbalance2.ColoredBalance)
+		}
+
+		// add the gathered information as an output
+		result.Add(address, coloredBalances)
+	}
+
+	// return the number of bytes we processed
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+func (outputs *Outputs) Add(address address2.Address, balances []*coloredbalance2.ColoredBalance) *Outputs {
+	outputs.Set(address, balances)
+
+	return outputs
+}
+
+func (outputs *Outputs) ForEach(consumer func(address address2.Address, balances []*coloredbalance2.ColoredBalance)) {
+	outputs.OrderedMap.ForEach(func(key, value interface{}) bool {
+		consumer(key.(address2.Address), value.([]*coloredbalance2.ColoredBalance))
+
+		return true
+	})
+}
+
+func (outputs *Outputs) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	if outputs == nil {
+		marshalUtil.WriteUint32(0)
+
+		return marshalUtil.Bytes()
+	}
+
+	marshalUtil.WriteUint32(uint32(outputs.Size()))
+	outputs.ForEach(func(address address2.Address, balances []*coloredbalance2.ColoredBalance) {
+		marshalUtil.WriteBytes(address.ToBytes())
+		marshalUtil.WriteUint32(uint32(len(balances)))
+
+		for _, balance := range balances {
+			marshalUtil.WriteBytes(balance.ToBytes())
+		}
+	})
+
+	return marshalUtil.Bytes()
+}
+
+func (outputs *Outputs) String() string {
+	if outputs == nil {
+		return "<nil>"
+	}
+
+	result := "[\n"
+	empty := true
+	outputs.ForEach(func(address address2.Address, balances []*coloredbalance2.ColoredBalance) {
+		empty = false
+
+		result += "    " + address.String() + ": [\n"
+
+		balancesEmpty := true
+		for _, balance := range balances {
+			balancesEmpty = false
+
+			result += "        " + balance.String() + ",\n"
+		}
+
+		if balancesEmpty {
+			result += "        <empty>\n"
+		}
+
+		result += "    ]\n"
+	})
+
+	if empty {
+		result += "    <empty>\n"
+	}
+
+	return result + "]"
+}
diff --git a/packages/binary/valuetransfers/payload/transfer/transfer.go b/packages/binary/valuetransfers/payload/transfer/transfer.go
new file mode 100644
index 0000000000000000000000000000000000000000..1047eeb0e575e6ab96291408915e484495d1f87f
--- /dev/null
+++ b/packages/binary/valuetransfers/payload/transfer/transfer.go
@@ -0,0 +1,197 @@
+package transfer
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+	"github.com/mr-tron/base58"
+	"golang.org/x/crypto/blake2b"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/inputs"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/outputs"
+)
+
+// region IMPLEMENT Transfer ///////////////////////////////////////////////////////////////////////////////////////////
+
+type Transfer struct {
+	objectstorage.StorableObjectFlags
+
+	id      *id.Id
+	inputs  *inputs.Inputs
+	outputs *outputs.Outputs
+	bytes   []byte
+
+	idMutex    sync.RWMutex
+	bytesMutex sync.RWMutex
+}
+
+func New(inputs *inputs.Inputs, outputs *outputs.Outputs) *Transfer {
+	return &Transfer{
+		inputs:  inputs,
+		outputs: outputs,
+	}
+}
+
+func TransferFromBytes(bytes []byte, optionalTargetObject ...*Transfer) (result *Transfer, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Transfer{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to OutputFromBytes")
+	}
+
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// unmarshal inputs
+	if parseResult, inputsErr := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) {
+		return inputs.FromBytes(data)
+	}); inputsErr != nil {
+		err = inputsErr
+
+		return
+	} else {
+		result.inputs = parseResult.(*inputs.Inputs)
+	}
+
+	// unmarshal outputs
+	if parseResult, outputsErr := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) {
+		return outputs.FromBytes(data)
+	}); outputsErr != nil {
+		err = outputsErr
+
+		return
+	} else {
+		result.outputs = parseResult.(*outputs.Outputs)
+	}
+
+	// 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
+}
+
+func TransferFromStorage(key []byte) *Transfer {
+	id := id.New(key)
+
+	return &Transfer{
+		id: &id,
+	}
+}
+
+func (transfer *Transfer) GetId() id.Id {
+	// acquire lock for reading id
+	transfer.idMutex.RLock()
+
+	// return if id has been calculated already
+	if transfer.id != nil {
+		defer transfer.idMutex.RUnlock()
+
+		return *transfer.id
+	}
+
+	// switch to write lock
+	transfer.idMutex.RUnlock()
+	transfer.idMutex.Lock()
+	defer transfer.idMutex.Unlock()
+
+	// return if id has been calculated in the mean time
+	if transfer.id != nil {
+		return *transfer.id
+	}
+
+	// otherwise calculate the id
+	idBytes := blake2b.Sum256(transfer.Bytes())
+	transferId := id.New(idBytes[:])
+
+	// cache result for later calls
+	transfer.id = &transferId
+
+	return transferId
+}
+
+func (transfer *Transfer) Bytes() []byte {
+	// acquired read lock on bytes
+	transfer.bytesMutex.RLock()
+
+	// return bytes if the object has been marshaled already
+	if transfer.bytes != nil {
+		defer transfer.bytesMutex.RUnlock()
+
+		return transfer.bytes
+	}
+
+	// switch to write lock
+	transfer.bytesMutex.RUnlock()
+	transfer.bytesMutex.Lock()
+	defer transfer.bytesMutex.Unlock()
+
+	// return bytes if the object has been marshaled in the mean time
+	if bytes := transfer.bytes; bytes != nil {
+		return bytes
+	}
+
+	// create marshal helper
+	marshalUtil := marshalutil.New()
+
+	// marshal inputs
+	marshalUtil.WriteBytes(transfer.inputs.ToBytes())
+
+	// marshal outputs
+	marshalUtil.WriteBytes(transfer.outputs.Bytes())
+
+	// store marshaled result
+	transfer.bytes = marshalUtil.Bytes()
+
+	return transfer.bytes
+}
+
+func (transfer *Transfer) String() string {
+	id := transfer.GetId()
+
+	return stringify.Struct("Transfer"+fmt.Sprintf("(%p)", transfer),
+		stringify.StructField("id", base58.Encode(id[:])),
+		stringify.StructField("inputs", transfer.inputs),
+		stringify.StructField("outputs", transfer.outputs),
+	)
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// region IMPLEMENT StorableObject interface ///////////////////////////////////////////////////////////////////////////
+
+// define contract (ensure that the struct fulfills the given interface)
+var _ objectstorage.StorableObject = &Transfer{}
+
+func (transfer *Transfer) GetStorageKey() []byte {
+	id := transfer.GetId()
+
+	return id[:]
+}
+
+func (transfer *Transfer) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// MarshalBinary returns a bytes representation of the transfer by implementing the encoding.BinaryMarshaler interface.
+func (transfer *Transfer) MarshalBinary() ([]byte, error) {
+	return transfer.Bytes(), nil
+}
+
+func (transfer *Transfer) UnmarshalBinary(bytes []byte) (err error) {
+	_, err, _ = TransferFromBytes(bytes, transfer)
+
+	return
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetangle/payload_id.go b/packages/binary/valuetransfers/payload_id.go
similarity index 92%
rename from packages/binary/valuetangle/payload_id.go
rename to packages/binary/valuetransfers/payload_id.go
index 70f5b7c5b3c713d5cb331b276487f2a632bf9341..c596cfb0a794c0857abe340005b3015ccc7cf3f8 100644
--- a/packages/binary/valuetangle/payload_id.go
+++ b/packages/binary/valuetransfers/payload_id.go
@@ -1,4 +1,4 @@
-package valuetangle
+package valuetransfers
 
 import (
 	"github.com/mr-tron/base58"
diff --git a/packages/binary/valuetransfers/test/payload_test.go b/packages/binary/valuetransfers/test/payload_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..18d5be717f28d98a0d6b3b772aaf3fdd70e4001c
--- /dev/null
+++ b/packages/binary/valuetransfers/test/payload_test.go
@@ -0,0 +1,52 @@
+package test
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/coloredbalance"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/coloredbalance/color"
+	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/inputs"
+	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/output/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/outputs"
+)
+
+func TestPayload(t *testing.T) {
+	createdPayload := payload.NewPayload(
+		payloadid.Empty,
+		payloadid.Empty,
+		transfer.New(
+			inputs.New(
+				transferoutputid.New(address.New([]byte("test")), transferid.New([]byte("test"))),
+				transferoutputid.New(address.New([]byte("test")), transferid.New([]byte("test1"))),
+			),
+
+			outputs.New(map[address.Address][]*coloredbalance.ColoredBalance{
+				address.New([]byte("output_address")): {
+					coloredbalance.New(color.COLOR_IOTA, 1337),
+				},
+			}),
+		),
+	)
+
+	fmt.Println(createdPayload.Bytes())
+
+	restoredTransfer, err, _ := valuetransfers.TransferFromBytes(sourceTransfer.Bytes())
+	if err != nil {
+		t.Error(err)
+
+		return
+	}
+
+	fmt.Println(sourceTransfer)
+	fmt.Println(restoredTransfer)
+
+	fmt.Println(restoredTransfer.GetId())
+	fmt.Println(sourceTransfer.GetId())
+}
diff --git a/packages/binary/valuetransfers/transfer.go b/packages/binary/valuetransfers/transfer.go
new file mode 100644
index 0000000000000000000000000000000000000000..91eb05834ec12f93254cd16a3004d3bc838d16bc
--- /dev/null
+++ b/packages/binary/valuetransfers/transfer.go
@@ -0,0 +1,194 @@
+package valuetransfers
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+	"github.com/mr-tron/base58"
+	"golang.org/x/crypto/blake2b"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+// region IMPLEMENT Transfer ///////////////////////////////////////////////////////////////////////////////////////////
+
+type Transfer struct {
+	objectstorage.StorableObjectFlags
+
+	id      *TransferId
+	inputs  *TransferInputs
+	outputs *Outputs
+	bytes   []byte
+
+	idMutex    sync.RWMutex
+	bytesMutex sync.RWMutex
+}
+
+func NewTransfer(inputs *TransferInputs, outputs *Outputs) *Transfer {
+	return &Transfer{
+		inputs:  inputs,
+		outputs: outputs,
+	}
+}
+
+func TransferFromBytes(bytes []byte, optionalTargetObject ...*Transfer) (result *Transfer, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Transfer{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to OutputFromBytes")
+	}
+
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// unmarshal inputs
+	if parseResult, inputsErr := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) {
+		return TransferInputsFromBytes(data)
+	}); inputsErr != nil {
+		err = inputsErr
+
+		return
+	} else {
+		result.inputs = parseResult.(*TransferInputs)
+	}
+
+	// unmarshal outputs
+	if parseResult, outputsErr := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) {
+		return OutputsFromBytes(data)
+	}); outputsErr != nil {
+		err = outputsErr
+
+		return
+	} else {
+		result.outputs = parseResult.(*Outputs)
+	}
+
+	// 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
+}
+
+func TransferFromStorage(key []byte) *Transfer {
+	id := NewTransferId(key)
+
+	return &Transfer{
+		id: &id,
+	}
+}
+
+func (transfer *Transfer) GetId() TransferId {
+	// acquire lock for reading id
+	transfer.idMutex.RLock()
+
+	// return if id has been calculated already
+	if transfer.id != nil {
+		defer transfer.idMutex.RUnlock()
+
+		return *transfer.id
+	}
+
+	// switch to write lock
+	transfer.idMutex.RUnlock()
+	transfer.idMutex.Lock()
+	defer transfer.idMutex.Unlock()
+
+	// return if id has been calculated in the mean time
+	if transfer.id != nil {
+		return *transfer.id
+	}
+
+	// otherwise calculate the id
+	idBytes := blake2b.Sum256(transfer.Bytes())
+	transferId := NewTransferId(idBytes[:])
+
+	// cache result for later calls
+	transfer.id = &transferId
+
+	return transferId
+}
+
+func (transfer *Transfer) Bytes() []byte {
+	// acquired read lock on bytes
+	transfer.bytesMutex.RLock()
+
+	// return bytes if the object has been marshaled already
+	if transfer.bytes != nil {
+		defer transfer.bytesMutex.RUnlock()
+
+		return transfer.bytes
+	}
+
+	// switch to write lock
+	transfer.bytesMutex.RUnlock()
+	transfer.bytesMutex.Lock()
+	defer transfer.bytesMutex.Unlock()
+
+	// return bytes if the object has been marshaled in the mean time
+	if bytes := transfer.bytes; bytes != nil {
+		return bytes
+	}
+
+	// create marshal helper
+	marshalUtil := marshalutil.New()
+
+	// marshal inputs
+	marshalUtil.WriteBytes(transfer.inputs.ToBytes())
+
+	// marshal outputs
+	marshalUtil.WriteBytes(transfer.outputs.Bytes())
+
+	// store marshaled result
+	transfer.bytes = marshalUtil.Bytes()
+
+	return transfer.bytes
+}
+
+func (transfer *Transfer) String() string {
+	id := transfer.GetId()
+
+	return stringify.Struct("Transfer"+fmt.Sprintf("(%p)", transfer),
+		stringify.StructField("id", base58.Encode(id[:])),
+		stringify.StructField("inputs", transfer.inputs),
+		stringify.StructField("outputs", transfer.outputs),
+	)
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// region IMPLEMENT StorableObject interface ///////////////////////////////////////////////////////////////////////////
+
+// define contract (ensure that the struct fulfills the given interface)
+var _ objectstorage.StorableObject = &Transfer{}
+
+func (transfer *Transfer) GetStorageKey() []byte {
+	id := transfer.GetId()
+
+	return id[:]
+}
+
+func (transfer *Transfer) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// MarshalBinary returns a bytes representation of the transfer by implementing the encoding.BinaryMarshaler interface.
+func (transfer *Transfer) MarshalBinary() ([]byte, error) {
+	return transfer.Bytes(), nil
+}
+
+func (transfer *Transfer) UnmarshalBinary(bytes []byte) (err error) {
+	_, err, _ = TransferFromBytes(bytes, transfer)
+
+	return
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetangle/transfer_id.go b/packages/binary/valuetransfers/transfer_id.go
similarity index 92%
rename from packages/binary/valuetangle/transfer_id.go
rename to packages/binary/valuetransfers/transfer_id.go
index 147ac73ffed7865743b33ec95d6f64513c87880b..ed2c198b418a4041c5abb81f39ea9674366e7a48 100644
--- a/packages/binary/valuetangle/transfer_id.go
+++ b/packages/binary/valuetransfers/transfer_id.go
@@ -1,4 +1,4 @@
-package valuetangle
+package valuetransfers
 
 import (
 	"github.com/mr-tron/base58"
diff --git a/packages/binary/valuetangle/transfer_inputs.go b/packages/binary/valuetransfers/transfer_inputs.go
similarity index 88%
rename from packages/binary/valuetangle/transfer_inputs.go
rename to packages/binary/valuetransfers/transfer_inputs.go
index f83e22d152914ea7fd17f6817e67f47d6d4763e9..aaffde448d5dcc3dcb5895000c18d064b90ac6ff 100644
--- a/packages/binary/valuetangle/transfer_inputs.go
+++ b/packages/binary/valuetransfers/transfer_inputs.go
@@ -1,4 +1,4 @@
-package valuetangle
+package valuetransfers
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
@@ -11,7 +11,6 @@ type TransferInputs struct {
 
 func NewTransferInputs(transferOutputIds ...TransferOutputId) (inputs *TransferInputs) {
 	inputs = &TransferInputs{orderedmap.New()}
-
 	for _, transferOutputId := range transferOutputIds {
 		inputs.Add(transferOutputId)
 	}
@@ -110,3 +109,24 @@ func (inputs *TransferInputs) ForEachAddress(consumer func(address Address)) {
 		return true
 	})
 }
+
+func (inputs *TransferInputs) String() string {
+	if inputs == nil {
+		return "<nil>"
+	}
+
+	result := "[\n"
+
+	empty := true
+	inputs.ForEach(func(transferOutputId TransferOutputId) {
+		empty = false
+
+		result += "    " + transferOutputId.String() + ",\n"
+	})
+
+	if empty {
+		result += "    <empty>\n"
+	}
+
+	return result + "]"
+}
diff --git a/packages/binary/valuetangle/transferoutput_id.go b/packages/binary/valuetransfers/transferoutput_id.go
similarity index 84%
rename from packages/binary/valuetangle/transferoutput_id.go
rename to packages/binary/valuetransfers/transferoutput_id.go
index f39c12b60db79cdb60b91032adde136a7f9c6c92..cbb9c76d619888179404ac28e82a5e2ce592d635 100644
--- a/packages/binary/valuetangle/transferoutput_id.go
+++ b/packages/binary/valuetransfers/transferoutput_id.go
@@ -1,4 +1,4 @@
-package valuetangle
+package valuetransfers
 
 import (
 	"github.com/mr-tron/base58"
@@ -7,7 +7,7 @@ import (
 type TransferOutputId [TransferOutputIdLength]byte
 
 func NewTransferOutputId(address Address, transferId TransferId) (transferOutputId TransferOutputId) {
-	copy(transferOutputId[:AddressLength], address[:])
+	copy(transferOutputId[:AddressLength], address.ToBytes())
 	copy(transferOutputId[AddressLength:], transferId[:])
 
 	return
@@ -32,7 +32,7 @@ func (transferOutputId TransferOutputId) ToBytes() []byte {
 }
 
 func (transferOutputId TransferOutputId) String() string {
-	return base58.Encode(transferOutputId[:])
+	return "TransferOutputId(" + base58.Encode(transferOutputId[:]) + ")"
 }
 
 const TransferOutputIdLength = AddressLength + TransferIdLength