diff --git a/go.mod b/go.mod
index 98fab30bc27e2ea7add0b2f062232c8f2191f4fc..dc7473f91ded16226ddffa67e57279919cf436d4 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,7 @@ require (
 	github.com/ethereum/go-ethereum v1.8.27
 	github.com/gdamore/tcell v1.1.2
 	github.com/go-zeromq/zmq4 v0.4.0
-	github.com/golang/protobuf v1.3.1 // indirect
+	github.com/golang/protobuf v1.3.1
 	github.com/google/open-location-code/go v0.0.0-20190603181809-cf814bded323
 	github.com/iotaledger/iota.go v1.0.0-beta.6
 	github.com/labstack/echo v3.3.10+incompatible
diff --git a/packages/mana/balance.go b/packages/mana/balance.go
index 49b8a313e79e5c4debd7a9de72a052bb75b82601..b3fc2c5abadcfa94e32ea328540b6d9a7c0dadd0 100644
--- a/packages/mana/balance.go
+++ b/packages/mana/balance.go
@@ -163,7 +163,7 @@ func (balance *Balance) applyTransfer(transfer *Transfer) {
 	// calculate mana gains
 	var gainedMana uint64
 	for _, input := range transfer.inputs {
-		generatedMana, _ := balance.calculator.GenerateMana(input.coinAmount, transfer.spentTime-input.receivedTime)
+		generatedMana, _ := balance.calculator.GenerateMana(input.GetCoinAmount(), transfer.spentTime-input.GetReceivedTime())
 
 		gainedMana += generatedMana
 	}
diff --git a/packages/mana/balance_test.go b/packages/mana/balance_test.go
index 65e5a3d3f2d135d0cbdd17c5c775c9a816cd0d75..f7c384b04f1f018a7f8f284757f9f71cd00f9213 100644
--- a/packages/mana/balance_test.go
+++ b/packages/mana/balance_test.go
@@ -13,26 +13,17 @@ func TestBalance_CleanupTransferHistory(t *testing.T) {
 	// fill transfer history
 	balance1 := NewBalance(calculator)
 	balance1.BookTransfer(&Transfer{
-		inputs: []*Input{{
-			coinAmount:   1000,
-			receivedTime: 1000,
-		}},
+		inputs: []*Input{NewInput(1000, 1000)},
 		spentTime:  1700,
 		burnedMana: 10,
 	})
 	balance1.BookTransfer(&Transfer{
-		inputs: []*Input{{
-			coinAmount:   1000,
-			receivedTime: 700,
-		}},
+		inputs: []*Input{NewInput(1000, 700)},
 		spentTime:  1000,
 		burnedMana: 0,
 	})
 	balance1.BookTransfer(&Transfer{
-		inputs: []*Input{{
-			coinAmount:   1000,
-			receivedTime: 0,
-		}},
+		inputs: []*Input{NewInput(1000, 0)},
 		spentTime:  700,
 		burnedMana: 0,
 	})
@@ -62,26 +53,17 @@ func TestBalance_AddTransfer(t *testing.T) {
 	// spend coins multiple times
 	balance1 := NewBalance(calculator)
 	balance1.BookTransfer(&Transfer{
-		inputs: []*Input{{
-			coinAmount:   1000,
-			receivedTime: 1000,
-		}},
+		inputs: []*Input{NewInput(1000, 1000)},
 		spentTime:  1700,
 		burnedMana: 10,
 	})
 	balance1.BookTransfer(&Transfer{
-		inputs: []*Input{{
-			coinAmount:   1000,
-			receivedTime: 700,
-		}},
+		inputs: []*Input{NewInput(1000, 700)},
 		spentTime:  1000,
 		burnedMana: 0,
 	})
 	balance1.BookTransfer(&Transfer{
-		inputs: []*Input{{
-			coinAmount:   1000,
-			receivedTime: 0,
-		}},
+		inputs: []*Input{NewInput(1000, 0)},
 		spentTime:  700,
 		burnedMana: 0,
 	})
@@ -89,10 +71,7 @@ func TestBalance_AddTransfer(t *testing.T) {
 	// hold coins for the full time
 	balance2 := NewBalance(calculator)
 	balance2.BookTransfer(&Transfer{
-		inputs: []*Input{{
-			coinAmount:   1000,
-			receivedTime: 0,
-		}},
+		inputs: []*Input{NewInput(1000, 0)},
 		spentTime:  1700,
 		burnedMana: 10,
 	})
diff --git a/packages/mana/errors.go b/packages/mana/errors.go
index 258f8407ab2c2c82426834bb698968581dbebb46..b049eeaef8dc9c3f2de9e32f3ef50aa9f8a4241d 100644
--- a/packages/mana/errors.go
+++ b/packages/mana/errors.go
@@ -5,5 +5,6 @@ import (
 )
 
 var (
-	ErrUnmarshalFailed = errors.New("unmarshal failed")
+	ErrUnmarshalFailed = errors.Wrap(errors.New("unmarshal failed"), "unmarshal failed")
+	ErrMarshalFailed = errors.Wrap(errors.New("marshal failed"), "marshal failed")
 )
diff --git a/packages/mana/input.go b/packages/mana/input.go
index c28916606ff75e80a0a338b31389870c4baf7c0e..320702c4eff35314be7f8da50f4cda9fa23e370e 100644
--- a/packages/mana/input.go
+++ b/packages/mana/input.go
@@ -1,76 +1,95 @@
 package mana
 
 import (
-	"encoding/binary"
-	"fmt"
+	"sync"
 
-	"github.com/iotaledger/goshimmer/packages/marshal"
+	"github.com/golang/protobuf/proto"
 
 	"github.com/iotaledger/goshimmer/packages/errors"
+	manaproto "github.com/iotaledger/goshimmer/packages/mana/proto"
 )
 
 type Input struct {
-	coinAmount   uint64
-	receivedTime uint64
+	coinAmount        uint64
+	coinAmountMutex   sync.RWMutex
+	receivedTime      uint64
+	receivedTimeMutex sync.RWMutex
 }
 
-func (input *Input) MarshalBinary() (data []byte, err errors.IdentifiableError) {
-	data = inputSchema.Marshal(input)
-
-	return
-}
-
-func (input *Input) UnmarshalBinary(data []byte) (err errors.IdentifiableError) {
-	if len(data) < INPUT_TOTAL_MARSHALED_SIZE {
-		err = ErrUnmarshalFailed.Derive("byte sequence of marshaled input is not long enough")
+func NewInput(coinAmount uint64, receivedTime uint64) *Input {
+	return &Input{
+		coinAmount:   coinAmount,
+		receivedTime: receivedTime,
 	}
-
-	input.coinAmount = binary.BigEndian.Uint64(data[INPUT_COIN_AMOUNT_BYTE_OFFSET_START:INPUT_COIN_AMOUNT_BYTE_OFFSET_END])
-	input.receivedTime = binary.BigEndian.Uint64(data[INPUT_RECEIVED_TIME_BYTE_OFFSET_START:INPUT_RECEIVED_TIME_BYTE_OFFSET_END])
-
-	return
 }
 
 func (input *Input) GetCoinAmount() uint64 {
-	return input_getCoinAmount(input)
+	input.coinAmountMutex.RLock()
+	defer input.coinAmountMutex.RUnlock()
+
+	return input.coinAmount
 }
 
-func input_getCoinAmount(input interface{}) uint64 {
-	return input.(*Input).coinAmount
+func (input *Input) SetCoinAmount(coinAmount uint64) {
+	input.coinAmountMutex.Lock()
+	defer input.coinAmountMutex.Unlock()
+
+	input.coinAmount = coinAmount
 }
 
-func input_setCoinAmount(input interface{}, coinAmount uint64) {
-	input.(*Input).coinAmount = coinAmount
+func (input *Input) GetReceivedTime() uint64 {
+	input.receivedTimeMutex.RLock()
+	defer input.receivedTimeMutex.RUnlock()
+
+	return input.receivedTime
 }
 
-func input_getReceivedTime(input interface{}) uint64 {
-	return input.(*Input).receivedTime
+func (input *Input) SetReceivedTime(receivedTime uint64) {
+	input.receivedTimeMutex.Lock()
+	defer input.receivedTimeMutex.Unlock()
+
+	input.receivedTime = receivedTime
 }
 
-func input_setReceivedTime(input interface{}, receivedTime uint64) {
-	input.(*Input).coinAmount = receivedTime
+func (input *Input) ToProto() (result *manaproto.Input) {
+	input.receivedTimeMutex.RLock()
+	input.coinAmountMutex.RLock()
+	defer input.receivedTimeMutex.RUnlock()
+	defer input.coinAmountMutex.RUnlock()
+
+	return &manaproto.Input{
+		CoinAmount:   input.coinAmount,
+		ReceivedTime: input.receivedTime,
+	}
 }
 
-var inputSchema = marshal.Schema(
-	marshal.Uint64(input_getCoinAmount, input_setCoinAmount),
-	marshal.Uint64(input_getReceivedTime, input_setReceivedTime),
-)
+func (input *Input) FromProto(proto *manaproto.Input) {
+	input.receivedTimeMutex.Lock()
+	input.coinAmountMutex.Lock()
+	defer input.receivedTimeMutex.Unlock()
+	defer input.coinAmountMutex.Unlock()
 
-func init() {
-	fmt.Println((&Input{
-		coinAmount:   10,
-		receivedTime: 20,
-	}).MarshalBinary())
+	input.coinAmount = proto.CoinAmount
+	input.receivedTime = proto.ReceivedTime
 }
 
-const (
-	INPUT_COIN_AMOUNT_BYTE_OFFSET_START  = 0
-	INPUT_COIN_AMOUNT_BYTE_OFFSET_LENGTH = 8
-	INPUT_COIN_AMOUNT_BYTE_OFFSET_END    = INPUT_COIN_AMOUNT_BYTE_OFFSET_START + INPUT_COIN_AMOUNT_BYTE_OFFSET_LENGTH
+func (input *Input) MarshalBinary() (result []byte, err errors.IdentifiableError) {
+	if marshaledData, marshalErr := proto.Marshal(input.ToProto()); marshalErr != nil {
+		err = ErrMarshalFailed.Derive(marshalErr, "marshal failed")
+	} else {
+		result = marshaledData
+	}
+
+	return
+}
 
-	INPUT_RECEIVED_TIME_BYTE_OFFSET_START  = INPUT_COIN_AMOUNT_BYTE_OFFSET_END
-	INPUT_RECEIVED_TIME_BYTE_OFFSET_LENGTH = 8
-	INPUT_RECEIVED_TIME_BYTE_OFFSET_END    = INPUT_RECEIVED_TIME_BYTE_OFFSET_START + INPUT_RECEIVED_TIME_BYTE_OFFSET_LENGTH
+func (input *Input) UnmarshalBinary(data []byte) (err errors.IdentifiableError) {
+	var unmarshaledProto manaproto.Input
+	if unmarshalError := proto.Unmarshal(data, &unmarshaledProto); unmarshalError != nil {
+		err = ErrUnmarshalFailed.Derive(unmarshalError, "unmarshal failed")
+	} else {
+		input.FromProto(&unmarshaledProto)
+	}
 
-	INPUT_TOTAL_MARSHALED_SIZE = INPUT_RECEIVED_TIME_BYTE_OFFSET_END
-)
+	return
+}
diff --git a/packages/mana/input_test.go b/packages/mana/input_test.go
index 997f0223121cdf557e5e1bb89243e1cb3c9d0bc1..b78ea6f03b00430deb65f859d67befdf68abc98f 100644
--- a/packages/mana/input_test.go
+++ b/packages/mana/input_test.go
@@ -1,27 +1,31 @@
 package mana
 
 import (
+	"github.com/magiconair/properties/assert"
 	"testing"
 )
 
-func BenchmarkDirectReceiver(b *testing.B) {
-	input := &Input{
-		coinAmount:   10,
-		receivedTime: 0,
-	}
+func TestInput_MarshalUnmarshalBinary(t *testing.T) {
+	// create original input
+	originalInput := NewInput(1337, 1338)
 
-	for i := 0; i < b.N; i++ {
-		input.GetCoinAmount()
-	}
-}
+	// marshal
+	marshaledInput, err := originalInput.MarshalBinary()
+	if err != nil {
+		t.Error(err)
 
-func BenchmarkManualReceiver(b *testing.B) {
-	input := &Input{
-		coinAmount:   10,
-		receivedTime: 0,
+		return
 	}
 
-	for i := 0; i < b.N; i++ {
-		input_getCoinAmount(input)
+	// unmarshal
+	var unmarshaledInput Input
+	if err := unmarshaledInput.UnmarshalBinary(marshaledInput); err != nil {
+		t.Error(err)
+
+		return
 	}
+
+	// compare result
+	assert.Equal(t, unmarshaledInput.GetCoinAmount(), originalInput.GetCoinAmount())
+	assert.Equal(t, unmarshaledInput.GetReceivedTime(), originalInput.GetReceivedTime())
 }
diff --git a/packages/mana/proto/input.pb.go b/packages/mana/proto/input.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..4c1e004d7f0004ae9d06e09da6197dd61c70a4f1
--- /dev/null
+++ b/packages/mana/proto/input.pb.go
@@ -0,0 +1,85 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: input.proto
+
+package proto
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type Input struct {
+	CoinAmount           uint64   `protobuf:"varint,1,opt,name=coinAmount,proto3" json:"coinAmount,omitempty"`
+	ReceivedTime         uint64   `protobuf:"varint,2,opt,name=receivedTime,proto3" json:"receivedTime,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Input) Reset()         { *m = Input{} }
+func (m *Input) String() string { return proto.CompactTextString(m) }
+func (*Input) ProtoMessage()    {}
+func (*Input) Descriptor() ([]byte, []int) {
+	return fileDescriptor_db6f7669dced820e, []int{0}
+}
+
+func (m *Input) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Input.Unmarshal(m, b)
+}
+func (m *Input) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Input.Marshal(b, m, deterministic)
+}
+func (m *Input) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Input.Merge(m, src)
+}
+func (m *Input) XXX_Size() int {
+	return xxx_messageInfo_Input.Size(m)
+}
+func (m *Input) XXX_DiscardUnknown() {
+	xxx_messageInfo_Input.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Input proto.InternalMessageInfo
+
+func (m *Input) GetCoinAmount() uint64 {
+	if m != nil {
+		return m.CoinAmount
+	}
+	return 0
+}
+
+func (m *Input) GetReceivedTime() uint64 {
+	if m != nil {
+		return m.ReceivedTime
+	}
+	return 0
+}
+
+func init() {
+	proto.RegisterType((*Input)(nil), "proto.Input")
+}
+
+func init() { proto.RegisterFile("input.proto", fileDescriptor_db6f7669dced820e) }
+
+var fileDescriptor_db6f7669dced820e = []byte{
+	// 98 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0xcc, 0x2b, 0x28,
+	0x2d, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x4a, 0xde, 0x5c, 0xac, 0x9e,
+	0x20, 0x51, 0x21, 0x39, 0x2e, 0xae, 0xe4, 0xfc, 0xcc, 0x3c, 0xc7, 0xdc, 0xfc, 0xd2, 0xbc, 0x12,
+	0x09, 0x46, 0x05, 0x46, 0x0d, 0x96, 0x20, 0x24, 0x11, 0x21, 0x25, 0x2e, 0x9e, 0xa2, 0xd4, 0xe4,
+	0xd4, 0xcc, 0xb2, 0xd4, 0x94, 0x90, 0xcc, 0xdc, 0x54, 0x09, 0x26, 0xb0, 0x0a, 0x14, 0xb1, 0x24,
+	0x36, 0xb0, 0x99, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf7, 0x69, 0x89, 0xbd, 0x69, 0x00,
+	0x00, 0x00,
+}
diff --git a/packages/mana/proto/input.proto b/packages/mana/proto/input.proto
new file mode 100644
index 0000000000000000000000000000000000000000..0b4f943d0825645c40961597f7f28862fe4682f8
--- /dev/null
+++ b/packages/mana/proto/input.proto
@@ -0,0 +1,8 @@
+syntax = "proto3";
+
+package proto;
+
+message Input {
+    uint64 coinAmount = 1;
+    uint64 receivedTime = 2;
+};
\ No newline at end of file
diff --git a/packages/mana/proto/transfer.pb.go b/packages/mana/proto/transfer.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..8cc0fa7677de8230d1e3991449096fc425ba117f
--- /dev/null
+++ b/packages/mana/proto/transfer.pb.go
@@ -0,0 +1,95 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: transfer.proto
+
+package proto
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type Transfer struct {
+	Inputs               []*Input `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs,omitempty"`
+	SpentTime            uint64   `protobuf:"varint,2,opt,name=spentTime,proto3" json:"spentTime,omitempty"`
+	BurnedMana           uint64   `protobuf:"varint,3,opt,name=burnedMana,proto3" json:"burnedMana,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Transfer) Reset()         { *m = Transfer{} }
+func (m *Transfer) String() string { return proto.CompactTextString(m) }
+func (*Transfer) ProtoMessage()    {}
+func (*Transfer) Descriptor() ([]byte, []int) {
+	return fileDescriptor_96c3e6bcafb460d3, []int{0}
+}
+
+func (m *Transfer) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Transfer.Unmarshal(m, b)
+}
+func (m *Transfer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Transfer.Marshal(b, m, deterministic)
+}
+func (m *Transfer) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Transfer.Merge(m, src)
+}
+func (m *Transfer) XXX_Size() int {
+	return xxx_messageInfo_Transfer.Size(m)
+}
+func (m *Transfer) XXX_DiscardUnknown() {
+	xxx_messageInfo_Transfer.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Transfer proto.InternalMessageInfo
+
+func (m *Transfer) GetInputs() []*Input {
+	if m != nil {
+		return m.Inputs
+	}
+	return nil
+}
+
+func (m *Transfer) GetSpentTime() uint64 {
+	if m != nil {
+		return m.SpentTime
+	}
+	return 0
+}
+
+func (m *Transfer) GetBurnedMana() uint64 {
+	if m != nil {
+		return m.BurnedMana
+	}
+	return 0
+}
+
+func init() {
+	proto.RegisterType((*Transfer)(nil), "proto.Transfer")
+}
+
+func init() { proto.RegisterFile("transfer.proto", fileDescriptor_96c3e6bcafb460d3) }
+
+var fileDescriptor_96c3e6bcafb460d3 = []byte{
+	// 130 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2b, 0x29, 0x4a, 0xcc,
+	0x2b, 0x4e, 0x4b, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x52, 0xdc,
+	0x99, 0x79, 0x05, 0xa5, 0x25, 0x10, 0x31, 0xa5, 0x3c, 0x2e, 0x8e, 0x10, 0xa8, 0x2a, 0x21, 0x15,
+	0x2e, 0x36, 0xb0, 0x54, 0xb1, 0x04, 0xa3, 0x02, 0xb3, 0x06, 0xb7, 0x11, 0x0f, 0x44, 0x8d, 0x9e,
+	0x27, 0x48, 0x30, 0x08, 0x2a, 0x27, 0x24, 0xc3, 0xc5, 0x59, 0x5c, 0x90, 0x9a, 0x57, 0x12, 0x92,
+	0x99, 0x9b, 0x2a, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x12, 0x84, 0x10, 0x10, 0x92, 0xe3, 0xe2, 0x4a,
+	0x2a, 0x2d, 0xca, 0x4b, 0x4d, 0xf1, 0x4d, 0xcc, 0x4b, 0x94, 0x60, 0x06, 0x4b, 0x23, 0x89, 0x24,
+	0xb1, 0x81, 0x8d, 0x34, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x61, 0xd4, 0xdc, 0x02, 0x9c, 0x00,
+	0x00, 0x00,
+}
diff --git a/packages/mana/proto/transfer.proto b/packages/mana/proto/transfer.proto
new file mode 100644
index 0000000000000000000000000000000000000000..951014d04d104381cd5e1a50d43f79ba46f6e67a
--- /dev/null
+++ b/packages/mana/proto/transfer.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+import "input.proto";
+
+package proto;
+
+message Transfer {
+    repeated Input inputs = 1;
+    uint64 spentTime = 2;
+    uint64 burnedMana = 3;
+};
\ No newline at end of file
diff --git a/packages/mana/transfer.go b/packages/mana/transfer.go
index 212235a6a0030b65bded2bc141dc27d163beb4a2..1597a076e325dcaf31b1bf280ed82a14d4ee709a 100644
--- a/packages/mana/transfer.go
+++ b/packages/mana/transfer.go
@@ -1,31 +1,81 @@
 package mana
 
 import (
+	"sync"
+
+	"github.com/iotaledger/goshimmer/packages/marshaling"
+
+	"github.com/golang/protobuf/proto"
 	"github.com/iotaledger/goshimmer/packages/errors"
+	manaproto "github.com/iotaledger/goshimmer/packages/mana/proto"
 )
 
 type Transfer struct {
-	inputs     []*Input
-	spentTime  uint64
-	burnedMana uint64
+	inputs          []*Input
+	inputsMutex     sync.RWMutex
+	spentTime       uint64
+	spentTimeMutex  sync.RWMutex
+	burnedMana      uint64
+	burnedManaMutex sync.RWMutex
+}
+
+func NewTransfer(inputs []*Input, spentTime uint64, burnedMana uint64) *Transfer {
+	return &Transfer{
+		inputs:     inputs,
+		spentTime:  spentTime,
+		burnedMana: burnedMana,
+	}
 }
 
-func (transfer *Transfer) MarshalBinary() (data []byte, err errors.IdentifiableError) {
-	data = make([]byte, INPUT_TOTAL_MARSHALED_SIZE)
+// Returns a protobuf representation of this transfer.
+func (transfer *Transfer) ToProto() (result proto.Message) {
+	transfer.inputsMutex.RLock()
+	transfer.spentTimeMutex.RLock()
+	transfer.burnedManaMutex.RLock()
+	defer transfer.inputsMutex.RUnlock()
+	defer transfer.spentTimeMutex.RUnlock()
+	defer transfer.burnedManaMutex.RUnlock()
 
-	return
+	protoTransfer := &manaproto.Transfer{
+		Inputs:     make([]*manaproto.Input, len(transfer.inputs)),
+		SpentTime:  transfer.spentTime,
+		BurnedMana: transfer.burnedMana,
+	}
+
+	for i, input := range transfer.inputs {
+		protoTransfer.Inputs[i] = input.ToProto()
+	}
+
+	return protoTransfer
 }
 
-func (transfer *Transfer) UnmarshalBinary(data []byte) (err errors.IdentifiableError) {
-	return
+// Restores the values from a protobuf representation of a transfer.
+func (transfer *Transfer) FromProto(proto proto.Message) {
+	transfer.inputsMutex.Lock()
+	transfer.spentTimeMutex.Lock()
+	transfer.burnedManaMutex.Lock()
+	defer transfer.inputsMutex.Unlock()
+	defer transfer.spentTimeMutex.Unlock()
+	defer transfer.burnedManaMutex.Unlock()
+
+	protoTransfer := proto.(*manaproto.Transfer)
+
+	transfer.inputs = make([]*Input, len(protoTransfer.Inputs))
+	transfer.spentTime = protoTransfer.SpentTime
+	transfer.burnedMana = protoTransfer.BurnedMana
+
+	for i, protoInput := range protoTransfer.Inputs {
+		var input Input
+		input.FromProto(protoInput)
+
+		transfer.inputs[i] = &input
+	}
 }
 
-const (
-	TRANSFER_SPENT_TIME_BYTE_OFFSET_START  = 0
-	TRANSFER_SPENT_TIME_BYTE_OFFSET_LENGTH = 8
-	TRANSFER_SPENT_TIME_BYTE_OFFSET_END    = TRANSFER_SPENT_TIME_BYTE_OFFSET_START + TRANSFER_SPENT_TIME_BYTE_OFFSET_LENGTH
+func (transfer *Transfer) MarshalBinary() ([]byte, errors.IdentifiableError) {
+	return marshaling.Marshal(transfer)
+}
 
-	TRANSFER_BURNED_MANA_BYTE_OFFSET_START  = INPUT_COIN_AMOUNT_BYTE_OFFSET_END
-	TRANSFER_BURNED_MANA_BYTE_OFFSET_LENGTH = 8
-	TRANSFER_BURNED_MANA_BYTE_OFFSET_END    = INPUT_RECEIVED_TIME_BYTE_OFFSET_START + INPUT_RECEIVED_TIME_BYTE_OFFSET_LENGTH
-)
+func (transfer *Transfer) UnmarshalBinary(data []byte) (err errors.IdentifiableError) {
+	return marshaling.Unmarshal(transfer, &manaproto.Transfer{}, data)
+}
diff --git a/packages/mana/transfer_test.go b/packages/mana/transfer_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f8b9616b5bb5feea79416cff6b299d81204c4ac3
--- /dev/null
+++ b/packages/mana/transfer_test.go
@@ -0,0 +1,32 @@
+package mana
+
+import (
+	"testing"
+
+	"github.com/magiconair/properties/assert"
+)
+
+func TestTransfer_MarshalUnmarshalBinary(t *testing.T) {
+	// create original input
+	originalTransfer := NewTransfer([]*Input{}, 1337, 1338)
+
+	// marshal
+	marshaledTransfer, err := originalTransfer.MarshalBinary()
+	if err != nil {
+		t.Error(err)
+
+		return
+	}
+
+	// unmarshal
+	var unmarshaledTransfer Transfer
+	if err := unmarshaledTransfer.UnmarshalBinary(marshaledTransfer); err != nil {
+		t.Error(err)
+
+		return
+	}
+
+	// compare result
+	assert.Equal(t, unmarshaledTransfer.spentTime, originalTransfer.spentTime)
+	assert.Equal(t, unmarshaledTransfer.burnedMana, originalTransfer.burnedMana)
+}
diff --git a/packages/marshal/encoder.go b/packages/marshal/encoder.go
deleted file mode 100644
index 86990dc67a1d18927a26d95eafaf326b9e092c93..0000000000000000000000000000000000000000
--- a/packages/marshal/encoder.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package marshal
-
-type Field interface {
-	Get(obj interface{}) interface{}
-	Marshal(obj interface{}, data []byte) []byte
-}
-
-type encoder struct {
-	fields []Field
-}
-
-func Schema(fields ...Field) *encoder {
-	return &encoder{
-		fields: fields,
-	}
-}
-
-func (encoder *encoder) Marshal(obj interface{}) []byte {
-	result := make([]byte, 0)
-
-	for _, field := range encoder.fields {
-		result = append(result, field.Marshal(field.Get(obj), result)...)
-	}
-
-	return result
-}
diff --git a/packages/marshal/uint64.go b/packages/marshal/uint64.go
deleted file mode 100644
index 5bdd83254b5e39f27ea028581a638c3cab0f1b7a..0000000000000000000000000000000000000000
--- a/packages/marshal/uint64.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package marshal
-
-import (
-	"encoding/binary"
-)
-
-func Uint64(getter uint64Getter, setter uint64Setter) *uint64Field {
-	return &uint64Field{
-		getter: getter,
-		setter: setter,
-	}
-}
-
-type uint64Getter func(receiver interface{}) uint64
-type uint64Setter func(receiver interface{}, val uint64)
-
-type uint64Field struct {
-	getter uint64Getter
-	setter uint64Setter
-}
-
-func (uint64Field *uint64Field) Marshal(obj interface{}, data []byte) (result []byte) {
-	result = make([]byte, 8)
-
-	binary.BigEndian.PutUint64(result, obj.(uint64))
-
-	return
-}
-
-func (uint64Field *uint64Field) Get(obj interface{}) interface{} {
-	return uint64Field.getter(obj)
-}
diff --git a/packages/marshaling/errors.go b/packages/marshaling/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..73b27016e283cb154bfd11cdab942e75243dd241
--- /dev/null
+++ b/packages/marshaling/errors.go
@@ -0,0 +1,8 @@
+package marshaling
+
+import "github.com/iotaledger/goshimmer/packages/errors"
+
+var (
+	ErrUnmarshalFailed = errors.Wrap(errors.New("unmarshal failed"), "unmarshal failed")
+	ErrMarshalFailed   = errors.Wrap(errors.New("marshal failed"), "marshal failed")
+)
diff --git a/packages/marshaling/marshal.go b/packages/marshaling/marshal.go
new file mode 100644
index 0000000000000000000000000000000000000000..807a4f43bca376bcf5ad2da51b51c14ad1c8a049
--- /dev/null
+++ b/packages/marshaling/marshal.go
@@ -0,0 +1,16 @@
+package marshaling
+
+import (
+	"github.com/golang/protobuf/proto"
+	"github.com/iotaledger/goshimmer/packages/errors"
+)
+
+func Marshal(serializable Serializable) (result []byte, err errors.IdentifiableError) {
+	if marshaledData, marshalErr := proto.Marshal(serializable.ToProto()); marshalErr != nil {
+		err = ErrMarshalFailed.Derive(marshalErr, "marshal failed")
+	} else {
+		result = marshaledData
+	}
+
+	return
+}
diff --git a/packages/marshaling/types.go b/packages/marshaling/types.go
new file mode 100644
index 0000000000000000000000000000000000000000..2a56555c2fbf7a68c8580443365dba70e8930721
--- /dev/null
+++ b/packages/marshaling/types.go
@@ -0,0 +1,8 @@
+package marshaling
+
+import "github.com/golang/protobuf/proto"
+
+type Serializable interface {
+	ToProto() (result proto.Message)
+	FromProto(proto proto.Message)
+}
diff --git a/packages/marshaling/unmarshal.go b/packages/marshaling/unmarshal.go
new file mode 100644
index 0000000000000000000000000000000000000000..e03f1757489bde2fccc8c0232101a6696d6c9d4e
--- /dev/null
+++ b/packages/marshaling/unmarshal.go
@@ -0,0 +1,16 @@
+package marshaling
+
+import (
+	"github.com/golang/protobuf/proto"
+	"github.com/iotaledger/goshimmer/packages/errors"
+)
+
+func Unmarshal(serializable Serializable, protobuf proto.Message, data []byte) (err errors.IdentifiableError) {
+	if unmarshalError := proto.Unmarshal(data, protobuf); unmarshalError != nil {
+		err = ErrUnmarshalFailed.Derive(unmarshalError, "unmarshal failed")
+	} else {
+		serializable.FromProto(protobuf)
+	}
+
+	return
+}