diff --git a/packages/mana/balance.go b/packages/mana/balance.go
new file mode 100644
index 0000000000000000000000000000000000000000..85ed7b4fc84f05a8a5b1c1bc64c74b9c3cd7b2a0
--- /dev/null
+++ b/packages/mana/balance.go
@@ -0,0 +1,49 @@
+package mana
+
+import (
+	"fmt"
+)
+
+type Balance struct {
+	calculator                 *Calculator
+	currentBalance             uint64
+	lastErosion                uint64
+	roundingErrorInLastErosion float64
+}
+
+func NewBalance(calculator *Calculator) *Balance {
+	return &Balance{
+		calculator:                 calculator,
+		currentBalance:             0,
+		lastErosion:                0,
+		roundingErrorInLastErosion: 0,
+	}
+}
+
+func (balance *Balance) GetValue() uint64 {
+	return balance.currentBalance
+}
+
+func (balance *Balance) AddTransfer(movedCoins uint64, receivedTime uint64, spentTime uint64) {
+	gainedMana, lastErosion, _ := balance.calculator.ManaOfTransferDiscrete(movedCoins, receivedTime, spentTime)
+
+	if lastErosion >= balance.lastErosion {
+		balance.Erode(lastErosion)
+	} else {
+		fmt.Println("empty")
+		// revert old actions
+		// apply new
+		// replay old
+	}
+
+	balance.currentBalance += gainedMana
+}
+
+func (balance *Balance) Erode(erosionTime uint64) {
+	if balance.lastErosion <= erosionTime {
+		balance.currentBalance, balance.lastErosion, _ = balance.calculator.ErodedManaDiscrete(balance.currentBalance, balance.lastErosion, erosionTime)
+	} else {
+		fmt.Println("empty")
+		// revert old erosions
+	}
+}
diff --git a/packages/mana/calculator.go b/packages/mana/calculator.go
new file mode 100644
index 0000000000000000000000000000000000000000..7ee676f890085b8067529f608229f978bd64d416
--- /dev/null
+++ b/packages/mana/calculator.go
@@ -0,0 +1,138 @@
+package mana
+
+import (
+	"math"
+)
+
+type Calculator struct {
+	decayInterval   uint64
+	decayRate       float64
+	coinsPerMana    uint64
+	decayFactor     float64
+	manaScaleFactor float64
+}
+
+func NewCalculator(decayInterval uint64, decayRate float64, coinsPerMana uint64) (result *Calculator) {
+	result = &Calculator{
+		decayInterval: decayInterval,
+		decayRate:     decayRate,
+		coinsPerMana:  coinsPerMana,
+		decayFactor:   1 - decayRate,
+	}
+
+	// make mana reach exactly the token supply
+	result.manaScaleFactor = result.decayRate / result.decayFactor
+
+	return
+}
+
+func (calculator *Calculator) ManaOfTransferDiscrete(movedCoins uint64, receivedTime uint64, spentTime uint64) (result uint64, lastErosion uint64, roundingError float64) {
+	if spentTime <= receivedTime {
+		return 0, 0, 0
+	}
+
+	baseMana := movedCoins / calculator.coinsPerMana
+	scaleFactor := 1 - calculator.decayRate
+	erosionIntervals := spentTime/calculator.decayInterval - receivedTime/calculator.decayInterval
+
+	var totalManaReceived float64
+
+	switch true {
+	// no decay intervals
+	case erosionIntervals == 0:
+		lastIntervalDuration := spentTime - receivedTime
+		if lastIntervalDuration != 0 {
+			totalManaReceived += float64(CoinTimeDestroyed(baseMana, lastIntervalDuration))
+		}
+
+		lastErosion = 0
+
+	// only 1 decay interval
+	case erosionIntervals == 1:
+		firstIntervalDuration := calculator.decayInterval - receivedTime%calculator.decayInterval
+		gainsInFirstInterval := float64(CoinTimeDestroyed(baseMana, firstIntervalDuration)) * math.Pow(scaleFactor, float64(erosionIntervals))
+		totalManaReceived += gainsInFirstInterval
+
+		lastIntervalDuration := spentTime % calculator.decayInterval
+		if lastIntervalDuration != 0 {
+			totalManaReceived += float64(CoinTimeDestroyed(baseMana, lastIntervalDuration))
+		}
+
+		lastErosion = spentTime - lastIntervalDuration
+
+	// multiple decay intervals
+	default:
+		firstIntervalDuration := calculator.decayInterval - receivedTime%calculator.decayInterval
+		gainsInFirstInterval := float64(CoinTimeDestroyed(baseMana, firstIntervalDuration)) * math.Pow(scaleFactor, float64(erosionIntervals))
+		totalManaReceived += gainsInFirstInterval
+
+		gainsInConsecutiveIntervals := float64(CoinTimeDestroyed(baseMana, calculator.decayInterval)) * scaleFactor * (1 - math.Pow(scaleFactor, float64(erosionIntervals-1))) / (1 - scaleFactor)
+		totalManaReceived += gainsInConsecutiveIntervals
+
+		lastIntervalDuration := spentTime % calculator.decayInterval
+		if lastIntervalDuration != 0 {
+			totalManaReceived += float64(CoinTimeDestroyed(baseMana, lastIntervalDuration))
+		}
+
+		lastErosion = spentTime - lastIntervalDuration
+	}
+
+	result = uint64(totalManaReceived)
+	roundingError = totalManaReceived - float64(result)
+
+	return
+}
+
+func (calculator *Calculator) ManaOfTransferContinuous(movedCoins uint64, heldTime uint64) (result uint64, roundingError float64) {
+	relativeDecayTime := float64(heldTime) / float64(calculator.decayInterval)
+
+	erosionFactor := (1-math.Pow(calculator.decayFactor, float64(relativeDecayTime+1)))/calculator.decayRate - 1
+
+	gainedMana := float64(movedCoins) * calculator.manaScaleFactor * erosionFactor
+
+	result = uint64(math.Round(gainedMana))
+	roundingError = gainedMana - float64(result)
+
+	return
+}
+
+func (calculator *Calculator) ManaOfTransferContinuous1(movedCoins uint64, heldTime uint64) (result uint64, roundingError float64) {
+	gainsInConsecutiveIntervals := (float64(movedCoins) / calculator.decayRate) * (1 - math.Pow(math.E, -calculator.decayRate*float64(heldTime)))
+
+	result = uint64(gainsInConsecutiveIntervals)
+	roundingError = gainsInConsecutiveIntervals - float64(result)
+
+	return
+}
+
+func (calculator *Calculator) ErodedManaContinuous(mana uint64, erosionTime uint64) (result uint64, roundingError float64) {
+	if erosionTime == 0 {
+		result = mana
+		roundingError = 0
+
+		return
+	}
+
+	growthFactor := math.Log(1-calculator.decayRate) / float64(calculator.decayInterval)
+	erodedMana := float64(mana) * math.Pow(math.E, growthFactor*float64(erosionTime))
+
+	result = uint64(erodedMana)
+	roundingError = erodedMana - float64(result)
+
+	return
+}
+
+func (calculator *Calculator) ErodedManaDiscrete(mana uint64, erosionStartTime uint64, erosionEndTime uint64) (result uint64, lastErosion uint64, roundingError float64) {
+	if erosionStartTime > erosionEndTime {
+		panic("negative erosion duration")
+	}
+
+	erosionIntervals := erosionEndTime/calculator.decayInterval - erosionStartTime/calculator.decayInterval
+	erodedValue := math.Pow(float64(1-calculator.decayRate), float64(erosionIntervals)) * float64(mana)
+
+	result = uint64(erodedValue)
+	lastErosion = erosionEndTime
+	roundingError = erodedValue - float64(result)
+
+	return
+}
diff --git a/packages/mana/mana.go b/packages/mana/mana.go
index d8f6b10a7ca4a9094cda378176772d17a2a0baed..4855d809ad887fc908c8886fccbe6dfc1563aed2 100644
--- a/packages/mana/mana.go
+++ b/packages/mana/mana.go
@@ -1,39 +1,11 @@
 package mana
 
-import (
-	"math"
-)
-
 func CoinTimeDestroyed(transferredValue uint64, parkedTime uint64) uint64 {
 	return transferredValue * parkedTime
 }
 
-func ManaOfTransfer(value uint64, receivedTime uint64, spentTime uint64) (result uint64) {
-	firstIntervalDuration := DECAY_INTERVAL - receivedTime%DECAY_INTERVAL
-	lastIntervalDuration := spentTime % DECAY_INTERVAL
-	erosionCount := (spentTime - receivedTime) / DECAY_INTERVAL
-
-	gainsInFirstInterval := CoinTimeDestroyed(value, firstIntervalDuration)
-	gainsInLastInterval := CoinTimeDestroyed(value, lastIntervalDuration)
-	gainsPerConsecutiveInterval := CoinTimeDestroyed(value, DECAY_INTERVAL)
-
-	scaleFactor := 1 - DECAY_RATE
-
-	erodedGainsOfFirstInterval := uint64(float64(gainsInFirstInterval) * math.Pow(scaleFactor, float64(erosionCount)))
-
-	var erodedGainsOfConsecutiveIntervals uint64
-	if erosionCount >= 1 {
-		erodedGainsOfConsecutiveIntervals = uint64(float64(gainsPerConsecutiveInterval) * scaleFactor * (1 - math.Pow(scaleFactor, float64(erosionCount-1))) / (1 - scaleFactor))
-	}
-
-	result += erodedGainsOfFirstInterval
-	result += erodedGainsOfConsecutiveIntervals
-	result += gainsInLastInterval
-
-	return
-}
-
 const (
-	DECAY_INTERVAL = 100
-	DECAY_RATE     = 0.5
+	DECAY_INTERVAL = 10
+	DECAY_RATE     = 0.01
+	COINS_PER_MANA = 10
 )
diff --git a/packages/mana/mana_test.go b/packages/mana/mana_test.go
index 5e1eee80eb502b35851df8336e355f40055b5452..bfb0589ee2d750c132c6833528428f94e273a0b3 100644
--- a/packages/mana/mana_test.go
+++ b/packages/mana/mana_test.go
@@ -2,15 +2,82 @@ package mana
 
 import (
 	"fmt"
+	"math"
 	"testing"
+
+	"github.com/magiconair/properties/assert"
 )
 
-func TestManaOfTransfer(t *testing.T) {
-	fmt.Println(ManaOfTransfer(50, 10, 110))
-	fmt.Println(ManaOfTransfer(50, 0, 190))
-	fmt.Println(ManaOfTransfer(50, 0, 200))
-	fmt.Println(ManaOfTransfer(50, 0, 290))
-	fmt.Println(ManaOfTransfer(50, 0, 300))
-	fmt.Println(ManaOfTransfer(50, 0, 390))
-	fmt.Println(ManaOfTransfer(50, 0, 400))
+func BenchmarkCalculator_ManaOfTransferContinuous(b *testing.B) {
+	calculator := NewCalculator(10, 0.1, 1)
+
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		calculator.ManaOfTransferContinuous(10000000, 100000000000)
+	}
+}
+
+func TestBalance_Erode(t *testing.T) {
+	calculator := NewCalculator(50, 0.1, 9)
+	balance := NewBalance(calculator)
+
+	calculator1 := NewCalculator(5000, 0.2, 1)
+
+	fmt.Println("===")
+	fmt.Println(calculator1.ManaOfTransferContinuous(1000, 5000000))
+	fmt.Println(calculator.ManaOfTransferDiscrete(1000, 0, 50))
+
+	balance.AddTransfer(500, 0, 10)
+
+	assert.Equal(t, balance.GetValue(), uint64(450))
+
+	balance.Erode(20)
+
+	assert.Equal(t, balance.GetValue(), uint64(405))
+
+	balance.Erode(40)
+
+	assert.Equal(t, balance.GetValue(), uint64(328))
+}
+
+func calcManaContinuous(calculator *Calculator, coins uint64, timeHeld uint64) (result uint64, roundingError float64) {
+	scaleFactor := 1 - math.Pow(1-calculator.decayRate, float64(timeHeld)/float64(calculator.decayInterval))
+	fmt.Println(scaleFactor)
+	erodedGains := float64(coins) * float64(calculator.decayInterval) * scaleFactor
+
+	result = uint64(erodedGains)
+	roundingError = erodedGains - float64(result)
+
+	return
+}
+
+func TestCalculator_ManaOfTransfer(t *testing.T) {
+	manaCalculator := NewCalculator(10, 0.1, 10)
+
+	var mana, lastErosion uint64
+	var roundingError float64
+
+	mana, lastErosion, _ = manaCalculator.ManaOfTransferDiscrete(49, 0, 0)
+	assert.Equal(t, mana, uint64(0))
+	assert.Equal(t, lastErosion, uint64(0))
+
+	fmt.Println(calcManaContinuous(manaCalculator, 50, 10))
+	fmt.Println(manaCalculator.ManaOfTransferDiscrete(50, 0, 10))
+	fmt.Println(calcManaContinuous(manaCalculator, 50, 20))
+	fmt.Println(manaCalculator.ManaOfTransferDiscrete(50, 0, 20))
+
+	mana, lastErosion, _ = manaCalculator.ManaOfTransferDiscrete(49, 0, 1)
+	assert.Equal(t, mana, uint64(4))
+	assert.Equal(t, lastErosion, uint64(0))
+
+	mana, lastErosion, _ = manaCalculator.ManaOfTransferDiscrete(49, 0, 10)
+	assert.Equal(t, mana, uint64(36))
+	assert.Equal(t, lastErosion, uint64(10))
+
+	mana, lastErosion, _ = manaCalculator.ManaOfTransferDiscrete(50, 0, 31)
+	assert.Equal(t, mana, uint64(101))
+	assert.Equal(t, lastErosion, uint64(30))
+
+	fmt.Println(roundingError)
 }