diff --git a/packages/mana/balance.go b/packages/mana/balance.go
index ebf746872358ab76fb954099f266174feb291e73..49b8a313e79e5c4debd7a9de72a052bb75b82601 100644
--- a/packages/mana/balance.go
+++ b/packages/mana/balance.go
@@ -161,12 +161,17 @@ func (balance *Balance) applyTransfer(transfer *Transfer) {
 	}
 
 	// calculate mana gains
-	gainedMana, roundingError := balance.calculator.GenerateMana(transfer.movedCoins, transfer.spentTime-transfer.receivedTime)
+	var gainedMana uint64
+	for _, input := range transfer.inputs {
+		generatedMana, _ := balance.calculator.GenerateMana(input.coinAmount, transfer.spentTime-input.receivedTime)
+
+		gainedMana += generatedMana
+	}
 
 	// store results
 	balance.transferHistory.AddLast(&BalanceHistoryEntry{
 		transfer:                 transfer,
 		balance:                  currentBalance + gainedMana - transfer.burnedMana,
-		accumulatedRoundingError: roundingError,
+		accumulatedRoundingError: 0, // TODO: remove
 	})
 }
diff --git a/packages/mana/balance_test.go b/packages/mana/balance_test.go
index 0a35b1e6b862d41bf21bd9ce5db4811175dc83e1..65e5a3d3f2d135d0cbdd17c5c775c9a816cd0d75 100644
--- a/packages/mana/balance_test.go
+++ b/packages/mana/balance_test.go
@@ -13,22 +13,28 @@ func TestBalance_CleanupTransferHistory(t *testing.T) {
 	// fill transfer history
 	balance1 := NewBalance(calculator)
 	balance1.BookTransfer(&Transfer{
-		movedCoins:   1000,
-		burnedMana:   10,
-		receivedTime: 1000,
-		spentTime:    1700,
+		inputs: []*Input{{
+			coinAmount:   1000,
+			receivedTime: 1000,
+		}},
+		spentTime:  1700,
+		burnedMana: 10,
 	})
 	balance1.BookTransfer(&Transfer{
-		movedCoins:   1000,
-		burnedMana:   0,
-		receivedTime: 700,
-		spentTime:    1000,
+		inputs: []*Input{{
+			coinAmount:   1000,
+			receivedTime: 700,
+		}},
+		spentTime:  1000,
+		burnedMana: 0,
 	})
 	balance1.BookTransfer(&Transfer{
-		movedCoins:   1000,
-		burnedMana:   0,
-		receivedTime: 0,
-		spentTime:    700,
+		inputs: []*Input{{
+			coinAmount:   1000,
+			receivedTime: 0,
+		}},
+		spentTime:  700,
+		burnedMana: 0,
 	})
 
 	// cleanup transfer history
@@ -56,31 +62,39 @@ func TestBalance_AddTransfer(t *testing.T) {
 	// spend coins multiple times
 	balance1 := NewBalance(calculator)
 	balance1.BookTransfer(&Transfer{
-		movedCoins:   1000,
-		burnedMana:   10,
-		receivedTime: 1000,
-		spentTime:    1700,
+		inputs: []*Input{{
+			coinAmount:   1000,
+			receivedTime: 1000,
+		}},
+		spentTime:  1700,
+		burnedMana: 10,
 	})
 	balance1.BookTransfer(&Transfer{
-		movedCoins:   1000,
-		burnedMana:   0,
-		receivedTime: 700,
-		spentTime:    1000,
+		inputs: []*Input{{
+			coinAmount:   1000,
+			receivedTime: 700,
+		}},
+		spentTime:  1000,
+		burnedMana: 0,
 	})
 	balance1.BookTransfer(&Transfer{
-		movedCoins:   1000,
-		burnedMana:   0,
-		receivedTime: 0,
-		spentTime:    700,
+		inputs: []*Input{{
+			coinAmount:   1000,
+			receivedTime: 0,
+		}},
+		spentTime:  700,
+		burnedMana: 0,
 	})
 
 	// hold coins for the full time
 	balance2 := NewBalance(calculator)
 	balance2.BookTransfer(&Transfer{
-		movedCoins:   1000,
-		burnedMana:   10,
-		receivedTime: 0,
-		spentTime:    1700,
+		inputs: []*Input{{
+			coinAmount:   1000,
+			receivedTime: 0,
+		}},
+		spentTime:  1700,
+		burnedMana: 10,
 	})
 
 	// check result
diff --git a/packages/mana/errors.go b/packages/mana/errors.go
index e462aad43529f4c59f902abf4c35b79e75f1a54f..258f8407ab2c2c82426834bb698968581dbebb46 100644
--- a/packages/mana/errors.go
+++ b/packages/mana/errors.go
@@ -1 +1,9 @@
 package mana
+
+import (
+	"github.com/iotaledger/goshimmer/packages/errors"
+)
+
+var (
+	ErrUnmarshalFailed = errors.New("unmarshal failed")
+)
diff --git a/packages/mana/input.go b/packages/mana/input.go
new file mode 100644
index 0000000000000000000000000000000000000000..c28916606ff75e80a0a338b31389870c4baf7c0e
--- /dev/null
+++ b/packages/mana/input.go
@@ -0,0 +1,76 @@
+package mana
+
+import (
+	"encoding/binary"
+	"fmt"
+
+	"github.com/iotaledger/goshimmer/packages/marshal"
+
+	"github.com/iotaledger/goshimmer/packages/errors"
+)
+
+type Input struct {
+	coinAmount   uint64
+	receivedTime uint64
+}
+
+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")
+	}
+
+	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)
+}
+
+func input_getCoinAmount(input interface{}) uint64 {
+	return input.(*Input).coinAmount
+}
+
+func input_setCoinAmount(input interface{}, coinAmount uint64) {
+	input.(*Input).coinAmount = coinAmount
+}
+
+func input_getReceivedTime(input interface{}) uint64 {
+	return input.(*Input).receivedTime
+}
+
+func input_setReceivedTime(input interface{}, receivedTime uint64) {
+	input.(*Input).coinAmount = receivedTime
+}
+
+var inputSchema = marshal.Schema(
+	marshal.Uint64(input_getCoinAmount, input_setCoinAmount),
+	marshal.Uint64(input_getReceivedTime, input_setReceivedTime),
+)
+
+func init() {
+	fmt.Println((&Input{
+		coinAmount:   10,
+		receivedTime: 20,
+	}).MarshalBinary())
+}
+
+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
+
+	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
+
+	INPUT_TOTAL_MARSHALED_SIZE = INPUT_RECEIVED_TIME_BYTE_OFFSET_END
+)
diff --git a/packages/mana/input_test.go b/packages/mana/input_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..997f0223121cdf557e5e1bb89243e1cb3c9d0bc1
--- /dev/null
+++ b/packages/mana/input_test.go
@@ -0,0 +1,27 @@
+package mana
+
+import (
+	"testing"
+)
+
+func BenchmarkDirectReceiver(b *testing.B) {
+	input := &Input{
+		coinAmount:   10,
+		receivedTime: 0,
+	}
+
+	for i := 0; i < b.N; i++ {
+		input.GetCoinAmount()
+	}
+}
+
+func BenchmarkManualReceiver(b *testing.B) {
+	input := &Input{
+		coinAmount:   10,
+		receivedTime: 0,
+	}
+
+	for i := 0; i < b.N; i++ {
+		input_getCoinAmount(input)
+	}
+}
diff --git a/packages/mana/transfer.go b/packages/mana/transfer.go
index bcf42d744083af21c330547b26040a0e0ad9b567..212235a6a0030b65bded2bc141dc27d163beb4a2 100644
--- a/packages/mana/transfer.go
+++ b/packages/mana/transfer.go
@@ -1,8 +1,31 @@
 package mana
 
+import (
+	"github.com/iotaledger/goshimmer/packages/errors"
+)
+
 type Transfer struct {
-	movedCoins   uint64
-	burnedMana   uint64
-	receivedTime uint64
-	spentTime    uint64
+	inputs     []*Input
+	spentTime  uint64
+	burnedMana uint64
+}
+
+func (transfer *Transfer) MarshalBinary() (data []byte, err errors.IdentifiableError) {
+	data = make([]byte, INPUT_TOTAL_MARSHALED_SIZE)
+
+	return
 }
+
+func (transfer *Transfer) UnmarshalBinary(data []byte) (err errors.IdentifiableError) {
+	return
+}
+
+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
+
+	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
+)
diff --git a/packages/marshal/encoder.go b/packages/marshal/encoder.go
new file mode 100644
index 0000000000000000000000000000000000000000..86990dc67a1d18927a26d95eafaf326b9e092c93
--- /dev/null
+++ b/packages/marshal/encoder.go
@@ -0,0 +1,26 @@
+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
new file mode 100644
index 0000000000000000000000000000000000000000..5bdd83254b5e39f27ea028581a638c3cab0f1b7a
--- /dev/null
+++ b/packages/marshal/uint64.go
@@ -0,0 +1,32 @@
+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)
+}