diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index e2d2185afacfaa7f99517ee36d36344b6447dada..14e1619cfbcb7c123f37b8c72e90ac18ad91ceda 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -212,3 +212,38 @@ jobs:
         with:
           name: ${{ env.TEST_NAME }}
           path: tools/integration-tests/logs
+
+
+  value:
+    name: value
+    env:
+      TEST_NAME: value
+    runs-on: ubuntu-latest
+    steps:
+
+      - name: Check out code
+        uses: actions/checkout@v2
+
+      - name: Build GoShimmer image
+        run: docker build -t iotaledger/goshimmer .
+
+      - name: Pull additional Docker images
+        run: |
+          docker pull angelocapossele/drand:latest
+          docker pull gaiaadm/pumba:latest
+          docker pull gaiadocker/iproute2:latest
+
+      - name: Run integration tests
+        run: docker-compose -f tools/integration-tests/tester/docker-compose.yml up --abort-on-container-exit --exit-code-from tester --build
+
+      - name: Create logs from tester
+        if: always()
+        run: |
+          docker logs tester &> tools/integration-tests/logs/tester.log
+
+      - name: Save logs as artifacts
+        if: always()
+        uses: actions/upload-artifact@v1
+        with:
+          name: ${{ env.TEST_NAME }}
+          path: tools/integration-tests/logs
diff --git a/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go b/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go
index e5a148f6efaa36c55bf160bb5e8d2a11a5f2702b..268499b7b446b9536a2784f0b7ba902b05b7eebe 100644
--- a/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go
+++ b/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go
@@ -1,10 +1,13 @@
 package branchmanager
 
 import (
+	"reflect"
 	"testing"
 
+	"github.com/iotaledger/hive.go/events"
 	"github.com/iotaledger/hive.go/kvstore/mapdb"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 )
 
 type sampleTree struct {
@@ -22,6 +25,92 @@ func (st *sampleTree) Release() {
 	}
 }
 
+// eventMock is a wrapper around mock.Mock used to test the triggered events.
+type eventMock struct {
+	mock.Mock
+
+	attached []struct {
+		*events.Event
+		*events.Closure
+	}
+}
+
+func newEventMock(t *testing.T, mgr *BranchManager) *eventMock {
+	e := &eventMock{}
+	e.Test(t)
+
+	// attach all events
+	e.attach(mgr.Events.BranchConfirmed, e.BranchConfirmed)
+	e.attach(mgr.Events.BranchDisliked, e.BranchDisliked)
+	e.attach(mgr.Events.BranchFinalized, e.BranchFinalized)
+	e.attach(mgr.Events.BranchLiked, e.BranchLiked)
+	e.attach(mgr.Events.BranchPreferred, e.BranchPreferred)
+	e.attach(mgr.Events.BranchRejected, e.BranchRejected)
+	e.attach(mgr.Events.BranchUnpreferred, e.BranchUnpreferred)
+
+	// assure that all available events are mocked
+	numEvents := reflect.ValueOf(mgr.Events).Elem().NumField()
+	assert.Equalf(t, len(e.attached), numEvents, "not all events in BranchManager.Events have been attached")
+
+	return e
+}
+
+// DetachAll detaches all attached event mocks.
+func (e *eventMock) DetachAll() {
+	for _, a := range e.attached {
+		a.Event.Detach(a.Closure)
+	}
+}
+
+// Expect starts a description of an expectation of the specified event being triggered.
+func (e *eventMock) Expect(eventName string, arguments ...interface{}) {
+	e.On(eventName, arguments...)
+}
+
+func (e *eventMock) attach(event *events.Event, f interface{}) {
+	closure := events.NewClosure(f)
+	event.Attach(closure)
+	e.attached = append(e.attached, struct {
+		*events.Event
+		*events.Closure
+	}{event, closure})
+}
+
+func (e *eventMock) BranchConfirmed(cachedBranch *CachedBranch) {
+	defer cachedBranch.Release()
+	e.Called(cachedBranch.Unwrap())
+}
+
+func (e *eventMock) BranchDisliked(cachedBranch *CachedBranch) {
+	defer cachedBranch.Release()
+	e.Called(cachedBranch.Unwrap())
+}
+
+func (e *eventMock) BranchFinalized(cachedBranch *CachedBranch) {
+	defer cachedBranch.Release()
+	e.Called(cachedBranch.Unwrap())
+}
+
+func (e *eventMock) BranchLiked(cachedBranch *CachedBranch) {
+	defer cachedBranch.Release()
+	e.Called(cachedBranch.Unwrap())
+}
+
+func (e *eventMock) BranchPreferred(cachedBranch *CachedBranch) {
+	defer cachedBranch.Release()
+	e.Called(cachedBranch.Unwrap())
+}
+
+func (e *eventMock) BranchRejected(cachedBranch *CachedBranch) {
+	defer cachedBranch.Release()
+	e.Called(cachedBranch.Unwrap())
+}
+
+func (e *eventMock) BranchUnpreferred(cachedBranch *CachedBranch) {
+	defer cachedBranch.Release()
+	e.Called(cachedBranch.Unwrap())
+}
+
 // ./img/sample_tree.png
 func createSampleTree(branchManager *BranchManager) *sampleTree {
 	st := &sampleTree{
@@ -857,6 +946,8 @@ func TestBranchManager_BranchesConflicting(t *testing.T) {
 
 func TestBranchManager_SetBranchPreferred(t *testing.T) {
 	branchManager := New(mapdb.NewMapDB())
+	event := newEventMock(t, branchManager)
+	defer event.DetachAll()
 
 	cachedBranch2, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{{0}})
 	defer cachedBranch2.Release()
@@ -882,6 +973,9 @@ func TestBranchManager_SetBranchPreferred(t *testing.T) {
 	// lets assume branch 4 is preferred since its underlying transaction was longer
 	// solid than the avg. network delay before the conflicting transaction which created
 	// the conflict set was received
+
+	event.Expect("BranchPreferred", branch4)
+
 	modified, err := branchManager.SetBranchPreferred(branch4.ID(), true)
 	assert.NoError(t, err)
 	assert.True(t, modified)
@@ -894,6 +988,11 @@ func TestBranchManager_SetBranchPreferred(t *testing.T) {
 
 	// now branch 2 becomes preferred via FPC, this causes branch 2 to be liked (since
 	// the master branch is liked) and its liked state propagates to branch 4 (but not branch 5)
+
+	event.Expect("BranchPreferred", branch2)
+	event.Expect("BranchLiked", branch2)
+	event.Expect("BranchLiked", branch4)
+
 	modified, err = branchManager.SetBranchPreferred(branch2.ID(), true)
 	assert.NoError(t, err)
 	assert.True(t, modified)
@@ -907,6 +1006,12 @@ func TestBranchManager_SetBranchPreferred(t *testing.T) {
 
 	// now the network decides that branch 5 is preferred (via FPC), thus branch 4 should lose its
 	// preferred and liked state and branch 5 should instead become preferred and liked
+
+	event.Expect("BranchPreferred", branch5)
+	event.Expect("BranchLiked", branch5)
+	event.Expect("BranchUnpreferred", branch4)
+	event.Expect("BranchDisliked", branch4)
+
 	modified, err = branchManager.SetBranchPreferred(branch5.ID(), true)
 	assert.NoError(t, err)
 	assert.True(t, modified)
@@ -920,10 +1025,15 @@ func TestBranchManager_SetBranchPreferred(t *testing.T) {
 	assert.False(t, branch4.Preferred(), "branch 4 should not be preferred")
 	assert.True(t, branch5.Liked(), "branch 5 should be liked")
 	assert.True(t, branch5.Preferred(), "branch 5 should be preferred")
+
+	// check that all event have been triggered
+	event.AssertExpectations(t)
 }
 
 func TestBranchManager_SetBranchPreferred2(t *testing.T) {
 	branchManager := New(mapdb.NewMapDB())
+	event := newEventMock(t, branchManager)
+	defer event.DetachAll()
 
 	cachedBranch2, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{{0}})
 	defer cachedBranch2.Release()
@@ -949,6 +1059,11 @@ func TestBranchManager_SetBranchPreferred2(t *testing.T) {
 	defer cachedBranch7.Release()
 	branch7 := cachedBranch7.Unwrap()
 
+	event.Expect("BranchPreferred", branch2)
+	event.Expect("BranchLiked", branch2)
+	event.Expect("BranchPreferred", branch6)
+	event.Expect("BranchLiked", branch6)
+
 	// assume branch 2 preferred since solid longer than avg. network delay
 	modified, err := branchManager.SetBranchPreferred(branch2.ID(), true)
 	assert.NoError(t, err)
@@ -1031,13 +1146,21 @@ func TestBranchManager_SetBranchPreferred2(t *testing.T) {
 	// should also become liked, since branch 2 of which it spawns off is liked too.
 
 	// simulate branch 3 being not preferred from FPC vote
+	// this does not trigger any events as branch 3 was never preferred
 	modified, err = branchManager.SetBranchPreferred(branch3.ID(), false)
 	assert.NoError(t, err)
 	assert.False(t, modified)
 	// simulate branch 7 being not preferred from FPC vote
+	// this does not trigger any events as branch 7 was never preferred
 	modified, err = branchManager.SetBranchPreferred(branch7.ID(), false)
 	assert.NoError(t, err)
 	assert.False(t, modified)
+
+	event.Expect("BranchPreferred", branch4)
+	event.Expect("BranchLiked", branch4)
+	event.Expect("BranchPreferred", aggrBranch8)
+	event.Expect("BranchLiked", aggrBranch8)
+
 	// simulate branch 4 being preferred by FPC vote
 	modified, err = branchManager.SetBranchPreferred(branch4.ID(), true)
 	assert.NoError(t, err)
@@ -1049,4 +1172,7 @@ func TestBranchManager_SetBranchPreferred2(t *testing.T) {
 	// of which it spawns off are.
 	assert.True(t, aggrBranch8.Liked(), "aggr. branch 8 should be liked")
 	assert.True(t, aggrBranch8.Preferred(), "aggr. branch 8 should be preferred")
+
+	// check that all event have been triggered
+	event.AssertExpectations(t)
 }
diff --git a/dapps/valuetransfers/packages/tangle/errors.go b/dapps/valuetransfers/packages/tangle/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..3ada3b8ae0f57de974ed9bcc4a11012a6db3b5c4
--- /dev/null
+++ b/dapps/valuetransfers/packages/tangle/errors.go
@@ -0,0 +1,14 @@
+package tangle
+
+import "errors"
+
+var (
+	// ErrFatal represents an error that is not "expected".
+	ErrFatal = errors.New("fatal error")
+
+	// ErrTransactionInvalid represents an error type that is triggered when an invalid transaction is detected.
+	ErrTransactionInvalid = errors.New("transaction invalid")
+
+	// ErrPayloadInvalid represents an error type that is triggered when an invalid payload is detected.
+	ErrPayloadInvalid = errors.New("payload invalid")
+)
diff --git a/dapps/valuetransfers/packages/tangle/events.go b/dapps/valuetransfers/packages/tangle/events.go
index 8194f08f1e9d8e2fe71bc17b0627e41cd276cf84..17207555c01f1fd714c7fdef352e46fbab7cd994 100644
--- a/dapps/valuetransfers/packages/tangle/events.go
+++ b/dapps/valuetransfers/packages/tangle/events.go
@@ -19,7 +19,6 @@ type Events struct {
 	MissingPayloadReceived *events.Event
 	PayloadMissing         *events.Event
 	PayloadInvalid         *events.Event
-	PayloadUnsolidifiable  *events.Event
 
 	// TransactionReceived gets triggered whenever a transaction was received for the first time (not solid yet).
 	TransactionReceived *events.Event
@@ -81,7 +80,6 @@ func newEvents() *Events {
 		MissingPayloadReceived: events.NewEvent(cachedPayloadEvent),
 		PayloadMissing:         events.NewEvent(payloadIDEvent),
 		PayloadInvalid:         events.NewEvent(cachedPayloadErrorEvent),
-		PayloadUnsolidifiable:  events.NewEvent(payloadIDEvent),
 		TransactionReceived:    events.NewEvent(cachedTransactionAttachmentEvent),
 		TransactionInvalid:     events.NewEvent(cachedTransactionErrorEvent),
 		TransactionSolid:       events.NewEvent(cachedTransactionEvent),
diff --git a/dapps/valuetransfers/packages/tangle/output.go b/dapps/valuetransfers/packages/tangle/output.go
index 5d19ec51f12cd50a29abc7de98e279a8b8d5cb8b..4711420a72eff4425e62d19b0d6ebea762fbcb1f 100644
--- a/dapps/valuetransfers/packages/tangle/output.go
+++ b/dapps/valuetransfers/packages/tangle/output.go
@@ -162,6 +162,7 @@ func (output *Output) setBranchID(branchID branchmanager.BranchID) (modified boo
 	}
 
 	output.branchID = branchID
+	output.SetModified()
 	modified = true
 
 	return
diff --git a/dapps/valuetransfers/packages/tangle/tangle.go b/dapps/valuetransfers/packages/tangle/tangle.go
index 4b18130df03a1554604381b652c1ee2c49b2e2bc..7c91dec1711e6dd251325a46b6987a493af1cd99 100644
--- a/dapps/valuetransfers/packages/tangle/tangle.go
+++ b/dapps/valuetransfers/packages/tangle/tangle.go
@@ -36,10 +36,9 @@ type Tangle struct {
 	outputStorage              *objectstorage.ObjectStorage
 	consumerStorage            *objectstorage.ObjectStorage
 
-	Events Events
+	Events *Events
 
-	workerPool        async.WorkerPool
-	cleanupWorkerPool async.WorkerPool
+	workerPool async.WorkerPool
 }
 
 // New is the constructor of a Tangle and creates a new Tangle object from the given details.
@@ -59,10 +58,13 @@ func New(store kvstore.KVStore) (tangle *Tangle) {
 		outputStorage:              osFactory.New(osOutput, osOutputFactory, OutputKeyPartitions, objectstorage.CacheTime(cacheTime), osLeakDetectionOption),
 		consumerStorage:            osFactory.New(osConsumer, osConsumerFactory, ConsumerPartitionKeys, objectstorage.CacheTime(cacheTime), osLeakDetectionOption),
 
-		Events: *newEvents(),
+		Events: newEvents(),
 	}
 	tangle.setupDAGSynchronization()
 
+	// TODO: CHANGE BACK TO MULTI THREADING ONCE WE FIXED LOGICAL RACE CONDITIONS
+	tangle.workerPool.Tune(1)
+
 	return
 }
 
@@ -192,13 +194,13 @@ func (tangle *Tangle) Fork(transactionID transaction.ID, conflictingInputs []tra
 
 	tx := cachedTransaction.Unwrap()
 	if tx == nil {
-		err = fmt.Errorf("failed to load transaction '%s'", transactionID)
+		err = fmt.Errorf("failed to load transaction '%s': %w", transactionID, ErrFatal)
 
 		return
 	}
 	txMetadata := cachedTransactionMetadata.Unwrap()
 	if txMetadata == nil {
-		err = fmt.Errorf("failed to load metadata of transaction '%s'", transactionID)
+		err = fmt.Errorf("failed to load metadata of transaction '%s': %w", transactionID, ErrFatal)
 
 		return
 	}
@@ -267,7 +269,6 @@ func (tangle *Tangle) Prune() (err error) {
 // Shutdown stops the worker pools and shuts down the object storage instances.
 func (tangle *Tangle) Shutdown() *Tangle {
 	tangle.workerPool.ShutdownGracefully()
-	tangle.cleanupWorkerPool.ShutdownGracefully()
 
 	for _, storage := range []*objectstorage.ObjectStorage{
 		tangle.payloadStorage,
@@ -1032,7 +1033,7 @@ func (tangle *Tangle) solidifyPayload(cachedPayload *payload.CachedPayload, cach
 
 // deleteTransactionFutureCone removes a transaction and its whole future cone from the database (including all of the
 // reference models).
-func (tangle *Tangle) deleteTransactionFutureCone(transactionID transaction.ID) {
+func (tangle *Tangle) deleteTransactionFutureCone(transactionID transaction.ID, cause error) {
 	// initialize stack with current transaction
 	deleteStack := list.New()
 	deleteStack.PushBack(transactionID)
@@ -1045,7 +1046,7 @@ func (tangle *Tangle) deleteTransactionFutureCone(transactionID transaction.ID)
 		currentTransactionID := currentTransactionIDEntry.Value.(transaction.ID)
 
 		// delete the transaction
-		consumers, attachments := tangle.deleteTransaction(currentTransactionID)
+		consumers, attachments := tangle.deleteTransaction(currentTransactionID, cause)
 
 		// queue consumers to also be deleted
 		for _, consumer := range consumers {
@@ -1054,7 +1055,7 @@ func (tangle *Tangle) deleteTransactionFutureCone(transactionID transaction.ID)
 
 		// remove payload future cone
 		for _, attachingPayloadID := range attachments {
-			tangle.deletePayloadFutureCone(attachingPayloadID)
+			tangle.deletePayloadFutureCone(attachingPayloadID, cause)
 		}
 	}
 }
@@ -1062,13 +1063,21 @@ func (tangle *Tangle) deleteTransactionFutureCone(transactionID transaction.ID)
 // deleteTransaction deletes a single transaction and all of its related models from the database.
 // Note: We do not immediately remove the attachments as this is related to the Payloads and is therefore left to the
 //       caller to clean this up.
-func (tangle *Tangle) deleteTransaction(transactionID transaction.ID) (consumers []transaction.ID, attachments []payload.ID) {
+func (tangle *Tangle) deleteTransaction(transactionID transaction.ID, cause error) (consumers []transaction.ID, attachments []payload.ID) {
 	// create result
 	consumers = make([]transaction.ID, 0)
 	attachments = make([]payload.ID, 0)
 
+	cachedTransaction := tangle.Transaction(transactionID)
+	cachedTransactionMetadata := tangle.TransactionMetadata(transactionID)
+
 	// process transaction and its models
-	tangle.Transaction(transactionID).Consume(func(tx *transaction.Transaction) {
+	cachedTransaction.Consume(func(tx *transaction.Transaction) {
+		// if the removal was triggered by an invalid Transaction
+		if errors.Is(cause, ErrTransactionInvalid) {
+			tangle.Events.TransactionInvalid.Trigger(cachedTransaction, cachedTransactionMetadata, cause)
+		}
+
 		// mark transaction as deleted
 		tx.Delete()
 
@@ -1106,7 +1115,9 @@ func (tangle *Tangle) deleteTransaction(transactionID transaction.ID) (consumers
 	})
 
 	// delete transaction metadata
-	tangle.transactionMetadataStorage.Delete(transactionID.Bytes())
+	cachedTransactionMetadata.Consume(func(metadata *TransactionMetadata) {
+		metadata.Delete()
+	})
 
 	// process attachments
 	tangle.Attachments(transactionID).Consume(func(attachment *Attachment) {
@@ -1118,7 +1129,7 @@ func (tangle *Tangle) deleteTransaction(transactionID transaction.ID) (consumers
 
 // deletePayloadFutureCone removes a payload and its whole future cone from the database (including all of the reference
 // models).
-func (tangle *Tangle) deletePayloadFutureCone(payloadID payload.ID) {
+func (tangle *Tangle) deletePayloadFutureCone(payloadID payload.ID, cause error) {
 	// initialize stack with current transaction
 	deleteStack := list.New()
 	deleteStack.PushBack(payloadID)
@@ -1130,8 +1141,16 @@ func (tangle *Tangle) deletePayloadFutureCone(payloadID payload.ID) {
 		deleteStack.Remove(currentTransactionIDEntry)
 		currentPayloadID := currentTransactionIDEntry.Value.(payload.ID)
 
+		cachedPayload := tangle.Payload(currentPayloadID)
+		cachedPayloadMetadata := tangle.PayloadMetadata(currentPayloadID)
+
 		// process payload
-		tangle.Payload(currentPayloadID).Consume(func(currentPayload *payload.Payload) {
+		cachedPayload.Consume(func(currentPayload *payload.Payload) {
+			// trigger payload invalid if it was called with an "invalid cause"
+			if errors.Is(cause, ErrPayloadInvalid) || errors.Is(cause, ErrTransactionInvalid) {
+				tangle.Events.PayloadInvalid.Trigger(cachedPayload, cachedPayloadMetadata, cause)
+			}
+
 			// delete payload
 			currentPayload.Delete()
 
@@ -1146,12 +1165,14 @@ func (tangle *Tangle) deletePayloadFutureCone(payloadID payload.ID) {
 
 			// if this was the last attachment of the transaction then we also delete the transaction
 			if !tangle.Attachments(currentPayload.Transaction().ID()).Consume(func(attachment *Attachment) {}) {
-				tangle.deleteTransaction(currentPayload.Transaction().ID())
+				tangle.deleteTransaction(currentPayload.Transaction().ID(), nil)
 			}
 		})
 
 		// delete payload metadata
-		tangle.payloadMetadataStorage.Delete(currentPayloadID.Bytes())
+		cachedPayloadMetadata.Consume(func(payloadMetadata *PayloadMetadata) {
+			payloadMetadata.Delete()
+		})
 
 		// queue approvers
 		tangle.Approvers(currentPayloadID).Consume(func(approver *PayloadApprover) {
@@ -1175,9 +1196,7 @@ func (tangle *Tangle) processSolidificationStackEntry(solidificationStack *list.
 	// abort if the transaction is not solid or invalid
 	transactionSolid, consumedBranches, transactionSolidityErr := tangle.checkTransactionSolidity(currentTransaction, currentTransactionMetadata)
 	if transactionSolidityErr != nil {
-		tangle.Events.TransactionInvalid.Trigger(solidificationStackEntry.CachedTransaction, solidificationStackEntry.CachedTransactionMetadata, transactionSolidityErr)
-
-		tangle.deleteTransactionFutureCone(currentTransaction.ID())
+		tangle.deleteTransactionFutureCone(currentTransaction.ID(), transactionSolidityErr)
 
 		return
 	}
@@ -1188,9 +1207,7 @@ func (tangle *Tangle) processSolidificationStackEntry(solidificationStack *list.
 	// abort if the payload is not solid or invalid
 	payloadSolid, payloadSolidityErr := tangle.payloadBecameNewlySolid(currentPayload, currentPayloadMetadata, consumedBranches)
 	if payloadSolidityErr != nil {
-		tangle.Events.PayloadInvalid.Trigger(solidificationStackEntry.CachedPayload, solidificationStackEntry.CachedPayloadMetadata, payloadSolidityErr)
-
-		tangle.deletePayloadFutureCone(currentPayload.ID())
+		tangle.deletePayloadFutureCone(currentPayload.ID(), payloadSolidityErr)
 
 		return
 	}
@@ -1274,7 +1291,7 @@ func (tangle *Tangle) bookTransaction(cachedTransaction *transaction.CachedTrans
 		// abort if the output could not be found
 		output := cachedOutput.Unwrap()
 		if output == nil {
-			err = fmt.Errorf("could not load output '%s'", outputID)
+			err = fmt.Errorf("could not load output '%s': %w", outputID, ErrFatal)
 
 			return false
 		}
@@ -1492,7 +1509,7 @@ func (tangle *Tangle) payloadBecameNewlySolid(p *payload.Payload, payloadMetadat
 		return
 	}
 	if branchesConflicting {
-		err = fmt.Errorf("the payload '%s' combines conflicting versions of the ledger state", p.ID())
+		err = fmt.Errorf("the payload '%s' combines conflicting versions of the ledger state: %w", p.ID(), ErrPayloadInvalid)
 
 		return false, err
 	}
@@ -1526,7 +1543,7 @@ func (tangle *Tangle) checkTransactionSolidity(tx *transaction.Transaction, meta
 
 	// abort if the outputs are not matching the inputs
 	if !tangle.checkTransactionOutputs(consumedBalances, tx.Outputs()) {
-		err = fmt.Errorf("the outputs do not match the inputs in transaction with id '%s'", tx.ID())
+		err = fmt.Errorf("the outputs do not match the inputs in transaction with id '%s': %w", tx.ID(), ErrTransactionInvalid)
 
 		return
 	}
@@ -1538,7 +1555,7 @@ func (tangle *Tangle) checkTransactionSolidity(tx *transaction.Transaction, meta
 		return
 	}
 	if branchesConflicting {
-		err = fmt.Errorf("the transaction '%s' spends conflicting inputs", tx.ID())
+		err = fmt.Errorf("the transaction '%s' spends conflicting inputs: %w", tx.ID(), ErrTransactionInvalid)
 
 		return
 	}
@@ -1581,7 +1598,7 @@ func (tangle *Tangle) retrieveConsumedInputDetails(tx *transaction.Transaction)
 				// check overflows in the numbers
 				if inputBalance.Value > math.MaxInt64-currentBalance {
 					// TODO: make it an explicit error var
-					err = fmt.Errorf("buffer overflow in balances of inputs")
+					err = fmt.Errorf("buffer overflow in balances of inputs: %w", ErrTransactionInvalid)
 
 					cachedInputs.Release()
 
@@ -1782,7 +1799,7 @@ func (tangle *Tangle) moveTransactionToBranch(cachedTransaction *transaction.Cac
 						// unwrap output
 						output := cachedOutput.Unwrap()
 						if output == nil {
-							err = fmt.Errorf("failed to load output '%s'", outputID)
+							err = fmt.Errorf("failed to load output '%s': %w", outputID, ErrFatal)
 
 							return false
 						}
@@ -1904,7 +1921,7 @@ func (tangle *Tangle) calculateBranchOfTransaction(currentTransaction *transacti
 
 		transactionOutput := cachedTransactionOutput.Unwrap()
 		if transactionOutput == nil {
-			err = fmt.Errorf("failed to load output '%s'", outputId)
+			err = fmt.Errorf("failed to load output '%s': %w", outputId, ErrFatal)
 
 			return false
 		}
diff --git a/dapps/valuetransfers/packages/tangle/tangle_event_test.go b/dapps/valuetransfers/packages/tangle/tangle_event_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..bdd3f1dfc42386262c342e644c8d298852563958
--- /dev/null
+++ b/dapps/valuetransfers/packages/tangle/tangle_event_test.go
@@ -0,0 +1,211 @@
+package tangle
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
+	"github.com/iotaledger/hive.go/events"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+// eventTangle is a wrapper around Tangle used to test the triggered events.
+type eventTangle struct {
+	mock.Mock
+	*Tangle
+
+	attached []struct {
+		*events.Event
+		*events.Closure
+	}
+}
+
+func newEventTangle(t *testing.T, tangle *Tangle) *eventTangle {
+	e := &eventTangle{Tangle: tangle}
+	e.Test(t)
+
+	// attach all events
+	e.attach(tangle.Events.PayloadAttached, e.PayloadAttached)
+	e.attach(tangle.Events.PayloadSolid, e.PayloadSolid)
+	e.attach(tangle.Events.PayloadLiked, e.PayloadLiked)
+	e.attach(tangle.Events.PayloadConfirmed, e.PayloadConfirmed)
+	e.attach(tangle.Events.PayloadRejected, e.PayloadRejected)
+	e.attach(tangle.Events.PayloadDisliked, e.PayloadDisliked)
+	e.attach(tangle.Events.MissingPayloadReceived, e.MissingPayloadReceived)
+	e.attach(tangle.Events.PayloadMissing, e.PayloadMissing)
+	e.attach(tangle.Events.PayloadInvalid, e.PayloadInvalid)
+	e.attach(tangle.Events.TransactionReceived, e.TransactionReceived)
+	e.attach(tangle.Events.TransactionInvalid, e.TransactionInvalid)
+	e.attach(tangle.Events.TransactionSolid, e.TransactionSolid)
+	e.attach(tangle.Events.TransactionBooked, e.TransactionBooked)
+	e.attach(tangle.Events.TransactionPreferred, e.TransactionPreferred)
+	e.attach(tangle.Events.TransactionUnpreferred, e.TransactionUnpreferred)
+	e.attach(tangle.Events.TransactionLiked, e.TransactionLiked)
+	e.attach(tangle.Events.TransactionDisliked, e.TransactionDisliked)
+	e.attach(tangle.Events.TransactionFinalized, e.TransactionFinalized)
+	e.attach(tangle.Events.TransactionConfirmed, e.TransactionConfirmed)
+	e.attach(tangle.Events.TransactionRejected, e.TransactionRejected)
+	e.attach(tangle.Events.Fork, e.Fork)
+	e.attach(tangle.Events.Error, e.Error)
+
+	// assure that all available events are mocked
+	numEvents := reflect.ValueOf(tangle.Events).Elem().NumField()
+	assert.Equalf(t, len(e.attached), numEvents, "not all events in Tangle.Events have been attached")
+
+	return e
+}
+
+// DetachAll detaches all attached event mocks.
+func (e *eventTangle) DetachAll() {
+	for _, a := range e.attached {
+		a.Event.Detach(a.Closure)
+	}
+}
+
+func (e *eventTangle) attach(event *events.Event, f interface{}) {
+	closure := events.NewClosure(f)
+	event.Attach(closure)
+	e.attached = append(e.attached, struct {
+		*events.Event
+		*events.Closure
+	}{event, closure})
+}
+
+// Expect starts a description of an expectation of the specified event being triggered.
+func (e *eventTangle) Expect(eventName string, arguments ...interface{}) {
+	e.On(eventName, arguments...).Once()
+}
+
+func (e *eventTangle) PayloadAttached(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) {
+	defer payload.Release()
+	defer payloadMetadata.Release()
+	e.Called(payload.Unwrap(), payloadMetadata.Unwrap())
+}
+
+func (e *eventTangle) PayloadSolid(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) {
+	defer payload.Release()
+	defer payloadMetadata.Release()
+	e.Called(payload.Unwrap(), payloadMetadata.Unwrap())
+}
+
+func (e *eventTangle) PayloadLiked(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) {
+	defer payload.Release()
+	defer payloadMetadata.Release()
+	e.Called(payload.Unwrap(), payloadMetadata.Unwrap())
+}
+
+func (e *eventTangle) PayloadConfirmed(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) {
+	defer payload.Release()
+	defer payloadMetadata.Release()
+	e.Called(payload.Unwrap(), payloadMetadata.Unwrap())
+}
+
+func (e *eventTangle) PayloadRejected(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) {
+	defer payload.Release()
+	defer payloadMetadata.Release()
+	e.Called(payload.Unwrap(), payloadMetadata.Unwrap())
+}
+
+func (e *eventTangle) PayloadDisliked(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) {
+	defer payload.Release()
+	defer payloadMetadata.Release()
+	e.Called(payload.Unwrap(), payloadMetadata.Unwrap())
+}
+
+func (e *eventTangle) MissingPayloadReceived(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) {
+	defer payload.Release()
+	defer payloadMetadata.Release()
+	e.Called(payload.Unwrap(), payloadMetadata.Unwrap())
+}
+
+func (e *eventTangle) PayloadMissing(id payload.ID) {
+	e.Called(id)
+}
+
+func (e *eventTangle) PayloadInvalid(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata, err error) {
+	defer payload.Release()
+	defer payloadMetadata.Release()
+	e.Called(payload.Unwrap(), payloadMetadata.Unwrap(), err)
+}
+
+func (e *eventTangle) TransactionReceived(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata, attachment *CachedAttachment) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	defer attachment.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap(), attachment.Unwrap())
+}
+
+func (e *eventTangle) TransactionInvalid(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata, err error) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap(), err)
+}
+
+func (e *eventTangle) TransactionSolid(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap())
+}
+
+func (e *eventTangle) TransactionBooked(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata, decisionPending bool) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap(), decisionPending)
+}
+
+func (e *eventTangle) TransactionPreferred(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap())
+}
+
+func (e *eventTangle) TransactionUnpreferred(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap())
+}
+
+func (e *eventTangle) TransactionLiked(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap())
+}
+
+func (e *eventTangle) TransactionDisliked(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap())
+}
+
+func (e *eventTangle) TransactionFinalized(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap())
+}
+
+func (e *eventTangle) TransactionConfirmed(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap())
+}
+
+func (e *eventTangle) TransactionRejected(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap())
+}
+
+func (e *eventTangle) Fork(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata, branch *branchmanager.CachedBranch, outputIDs []transaction.OutputID) {
+	defer transaction.Release()
+	defer transactionMetadata.Release()
+	defer branch.Release()
+	e.Called(transaction.Unwrap(), transactionMetadata.Unwrap(), branch.Unwrap(), outputIDs)
+}
+
+// TODO: Error is never tested
+func (e *eventTangle) Error(err error) {
+	e.Called(err)
+}
diff --git a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go
index 63a8889e3eb4bd02e0a2a7de51cf75599643f3a3..5d185f9637fef1a7bef59f8e8b69138936f2ac37 100644
--- a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go
+++ b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go
@@ -12,6 +12,7 @@ import (
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
 	"github.com/iotaledger/hive.go/kvstore/mapdb"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/require"
 )
 
@@ -33,9 +34,9 @@ const (
 // TODO: clean up create scenario with some helper functions: DRY!
 
 // preparePropagationScenario1 creates a tangle according to `img/scenario1.png`.
-func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) {
+func preparePropagationScenario1(t *testing.T) (*eventTangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) {
 	// create tangle
-	tangle := New(mapdb.NewMapDB())
+	tangle := newEventTangle(t, New(mapdb.NewMapDB()))
 
 	// create seed for testing
 	seed := wallet.NewSeed()
@@ -80,6 +81,12 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction
 		// check if signatures are valid
 		assert.True(t, transactions["[-GENESIS, A+, B+, C+]"].SignaturesValid())
 
+		tangle.Expect("PayloadAttached", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("PayloadSolid", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionReceived", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything, mock.Anything)
+		tangle.Expect("TransactionSolid", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionBooked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything, true)
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-GENESIS, A+, B+, C+]"])
 
@@ -148,6 +155,12 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction
 		// check if signatures are valid
 		assert.True(t, transactions["[-A, D+]"].SignaturesValid())
 
+		tangle.Expect("PayloadAttached", valueObjects["[-A, D+]"], mock.Anything)
+		tangle.Expect("PayloadSolid", valueObjects["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionReceived", transactions["[-A, D+]"], mock.Anything, mock.Anything)
+		tangle.Expect("TransactionSolid", transactions["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionBooked", transactions["[-A, D+]"], mock.Anything, true)
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-A, D+]"])
 
@@ -202,6 +215,12 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction
 		// check if signatures are valid
 		assert.True(t, transactions["[-B, -C, E+]"].SignaturesValid())
 
+		tangle.Expect("PayloadAttached", valueObjects["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadSolid", valueObjects["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionReceived", transactions["[-B, -C, E+]"], mock.Anything, mock.Anything)
+		tangle.Expect("TransactionSolid", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionBooked", transactions["[-B, -C, E+]"], mock.Anything, true)
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-B, -C, E+]"])
 
@@ -247,6 +266,9 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction
 		// create payload
 		valueObjects["[-B, -C, E+] (Reattachment)"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-B, -C, E+]"])
 
+		tangle.Expect("PayloadAttached", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+		tangle.Expect("PayloadSolid", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-B, -C, E+] (Reattachment)"])
 
@@ -290,9 +312,10 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction
 	// [-A, F+]
 	{
 		// create transaction + payload
+		outputA := transaction.NewOutputID(seed.Address(A), transactions["[-GENESIS, A+, B+, C+]"].ID())
 		transactions["[-A, F+]"] = transaction.New(
 			transaction.NewInputs(
-				transaction.NewOutputID(seed.Address(A), transactions["[-GENESIS, A+, B+, C+]"].ID()),
+				outputA,
 			),
 
 			transaction.NewOutputs(map[address.Address][]*balance.Balance{
@@ -307,6 +330,13 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction
 		// check if signatures are valid
 		assert.True(t, transactions["[-A, F+]"].SignaturesValid())
 
+		tangle.Expect("PayloadAttached", valueObjects["[-A, F+]"], mock.Anything)
+		tangle.Expect("PayloadSolid", valueObjects["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionReceived", transactions["[-A, F+]"], mock.Anything, mock.Anything)
+		tangle.Expect("TransactionSolid", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionBooked", transactions["[-A, F+]"], mock.Anything, true)
+		tangle.Expect("Fork", transactions["[-A, D+]"], mock.Anything, mock.Anything, []transaction.OutputID{outputA})
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-A, F+]"])
 
@@ -384,6 +414,12 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction
 		// check if signatures are valid
 		assert.True(t, transactions["[-E, -F, G+]"].SignaturesValid())
 
+		tangle.Expect("PayloadAttached", valueObjects["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("PayloadSolid", valueObjects["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionReceived", transactions["[-E, -F, G+]"], mock.Anything, mock.Anything)
+		tangle.Expect("TransactionSolid", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionBooked", transactions["[-E, -F, G+]"], mock.Anything, true)
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-E, -F, G+]"])
 
@@ -446,6 +482,11 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction
 		// check if signatures are valid
 		assert.True(t, transactions["[-F, -D, Y+]"].SignaturesValid())
 
+		tangle.Expect("PayloadAttached", valueObjects["[-F, -D, Y+]"], mock.Anything)
+		tangle.Expect("PayloadInvalid", valueObjects["[-F, -D, Y+]"], mock.Anything, mock.MatchedBy(func(err error) bool { return assert.Error(t, err) }))
+		tangle.Expect("TransactionReceived", transactions["[-F, -D, Y+]"], mock.Anything, mock.Anything)
+		tangle.Expect("TransactionInvalid", transactions["[-F, -D, Y+]"], mock.Anything, mock.MatchedBy(func(err error) bool { return assert.Error(t, err) }))
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-F, -D, Y+]"])
 
@@ -469,6 +510,9 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction
 	{
 		valueObjects["[-B, -C, E+] (2nd Reattachment)"] = payload.New(valueObjects["[-A, F+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-B, -C, E+]"])
 
+		tangle.Expect("PayloadAttached", valueObjects["[-B, -C, E+] (2nd Reattachment)"], mock.Anything)
+		tangle.Expect("PayloadInvalid", valueObjects["[-B, -C, E+] (2nd Reattachment)"], mock.Anything, mock.MatchedBy(func(err error) bool { return assert.Error(t, err) }))
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-B, -C, E+] (2nd Reattachment)"])
 
@@ -498,15 +542,16 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction
 }
 
 // preparePropagationScenario1 creates a tangle according to `img/scenario2.png`.
-func preparePropagationScenario2(t *testing.T) (*Tangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) {
+func preparePropagationScenario2(t *testing.T) (*eventTangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) {
 	tangle, transactions, valueObjects, branches, seed := preparePropagationScenario1(t)
 
 	// [-C, H+]
 	{
 		// create transaction + payload
+		outputC := transaction.NewOutputID(seed.Address(C), transactions["[-GENESIS, A+, B+, C+]"].ID())
 		transactions["[-C, H+]"] = transaction.New(
 			transaction.NewInputs(
-				transaction.NewOutputID(seed.Address(C), transactions["[-GENESIS, A+, B+, C+]"].ID()),
+				outputC,
 			),
 
 			transaction.NewOutputs(map[address.Address][]*balance.Balance{
@@ -521,6 +566,13 @@ func preparePropagationScenario2(t *testing.T) (*Tangle, map[string]*transaction
 		// check if signatures are valid
 		assert.True(t, transactions["[-C, H+]"].SignaturesValid())
 
+		tangle.Expect("PayloadAttached", valueObjects["[-C, H+]"], mock.Anything)
+		tangle.Expect("PayloadSolid", valueObjects["[-C, H+]"], mock.Anything)
+		tangle.Expect("TransactionReceived", transactions["[-C, H+]"], mock.Anything, mock.Anything)
+		tangle.Expect("TransactionSolid", transactions["[-C, H+]"], mock.Anything)
+		tangle.Expect("TransactionBooked", transactions["[-C, H+]"], mock.Anything, true)
+		tangle.Expect("Fork", transactions["[-B, -C, E+]"], mock.Anything, mock.Anything, []transaction.OutputID{outputC})
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-C, H+]"])
 
@@ -620,6 +672,12 @@ func preparePropagationScenario2(t *testing.T) (*Tangle, map[string]*transaction
 		// check if signatures are valid
 		assert.True(t, transactions["[-H, -D, I+]"].SignaturesValid())
 
+		tangle.Expect("PayloadAttached", valueObjects["[-H, -D, I+]"], mock.Anything)
+		tangle.Expect("PayloadSolid", valueObjects["[-H, -D, I+]"], mock.Anything)
+		tangle.Expect("TransactionReceived", transactions["[-H, -D, I+]"], mock.Anything, mock.Anything)
+		tangle.Expect("TransactionSolid", transactions["[-H, -D, I+]"], mock.Anything)
+		tangle.Expect("TransactionBooked", transactions["[-H, -D, I+]"], mock.Anything, true)
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-H, -D, I+]"])
 
@@ -683,6 +741,12 @@ func preparePropagationScenario2(t *testing.T) (*Tangle, map[string]*transaction
 		// check if signatures are valid
 		assert.True(t, transactions["[-B, J+]"].SignaturesValid())
 
+		tangle.Expect("PayloadAttached", valueObjects["[-B, J+]"], mock.Anything)
+		tangle.Expect("PayloadSolid", valueObjects["[-B, J+]"], mock.Anything)
+		tangle.Expect("TransactionReceived", transactions["[-B, J+]"], mock.Anything, mock.Anything)
+		tangle.Expect("TransactionSolid", transactions["[-B, J+]"], mock.Anything)
+		tangle.Expect("TransactionBooked", transactions["[-B, J+]"], mock.Anything, true)
+
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-B, J+]"])
 
@@ -736,6 +800,7 @@ func TestPropagationScenario1(t *testing.T) {
 	// test past cone monotonicity - all value objects MUST be confirmed
 	{
 		tangle, transactions, valueObjects, _, _ := preparePropagationScenario1(t)
+		defer tangle.DetachAll()
 
 		// initialize debugger for this test
 		debugger.ResetAliases()
@@ -746,42 +811,79 @@ func TestPropagationScenario1(t *testing.T) {
 			debugger.RegisterAlias(tx.ID(), "TransactionID"+name)
 		}
 
+		// preferring [-GENESIS, A+, B+, C+] will get it liked
+		tangle.Expect("TransactionPreferred", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("PayloadLiked", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
 		verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, false, true, false, false)
 
-		// should not be confirmed because [-GENESIS, A+, B+, C+] is not confirmed
+		// finalizing [-B, -C, E+] will not get it confirmed, as [-GENESIS, A+, B+, C+] is not yet confirmed
+		tangle.Expect("TransactionPreferred", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
 		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, false, false)
 
-		// now finalize [-GENESIS, A+, B+, C+]
+		// finalize [-GENESIS, A+, B+, C+] to also get [-B, -C, E+] as well as [-B, -C, E+] (Reattachment) confirmed
+		tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionConfirmed", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionConfirmed", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
 		verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)
-
-		// and [-B, -C, E+] should be confirmed now too
 		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, true, false)
-		// as well as the reattachment
 		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], true, true, true, true, false)
+
+		tangle.AssertExpectations(t)
 	}
 
 	// test future cone monotonicity simple - everything MUST be rejected and finalized if spending funds from rejected tx
 	{
 		tangle, transactions, valueObjects, _, _ := preparePropagationScenario1(t)
+		defer tangle.DetachAll()
+
+		// finalizing [-GENESIS, A+, B+, C+] will get the entire future cone finalized and rejected
+		tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionRejected", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionRejected", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionRejected", transactions["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionRejected", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionRejected", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-A, D+]"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-A, F+]"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-E, -F, G+]"], mock.Anything)
 
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
 		verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], false, true, false, false, true)
-
-		// check future cone to be rejected
 		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, true, false, false, true)
 		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, true, false, false, true)
 		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, true, false, false, true)
 		verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, true, false, false, true)
 		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true)
+
+		tangle.AssertExpectations(t)
 	}
 
 	// test future cone monotonicity more complex - everything MUST be rejected and finalized if spending funds from rejected tx
 	{
 		tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t)
+		defer tangle.DetachAll()
 
 		// initialize debugger for this test
 		debugger.ResetAliases()
@@ -792,10 +894,26 @@ func TestPropagationScenario1(t *testing.T) {
 			debugger.RegisterAlias(tx.ID(), "TransactionID"+name)
 		}
 
+		tangle.Expect("TransactionPreferred", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionConfirmed", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("PayloadLiked", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
 		verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)
 
+		tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionRejected", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-A, F+]"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionRejected", transactions["[-E, -F, G+]"], mock.Anything)
+
 		// finalize & reject
 		//debugger.Enable()
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
@@ -816,11 +934,21 @@ func TestPropagationScenario1(t *testing.T) {
 		// [-A, D+] should be unchanged
 		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false, false)
 		verifyBranchState(t, tangle, branches["A"], false, false, false, false)
+
+		tangle.AssertExpectations(t)
 	}
 
 	// simulate vote on [-A, F+] -> Branch A becomes rejected, Branch B confirmed
 	{
 		tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t)
+		defer tangle.DetachAll()
+
+		tangle.Expect("PayloadLiked", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionPreferred", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionConfirmed", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
 
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
@@ -833,55 +961,135 @@ func TestPropagationScenario1(t *testing.T) {
 		verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, false, false, false, false)
 		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, false, false, false, false)
 
+		tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+		tangle.Expect("TransactionPreferred", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionConfirmed", transactions["[-B, -C, E+]"], mock.Anything)
+
 		// confirm [-B, -C, E+]
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
 		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, true, false)
 		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], true, true, true, true, false)
 
+		tangle.Expect("PayloadLiked", valueObjects["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionPreferred", transactions["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-A, D+]"], mock.Anything)
+
 		// prefer [-A, D+]
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true)
 		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], true, false, true, false, false)
 		verifyBranchState(t, tangle, branches["A"], false, true, false, false)
 
+		tangle.Expect("PayloadLiked", valueObjects["[-A, F+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionPreferred", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionConfirmed", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-A, D+]"], mock.Anything)
+		tangle.Expect("PayloadDisliked", valueObjects["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionUnpreferred", transactions["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionDisliked", transactions["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionRejected", transactions["[-A, D+]"], mock.Anything)
+
 		// simulate vote result to like [-A, F+] -> [-A, F+] becomes confirmed and [-A, D+] rejected
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-A, F+]"], true)
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-A, F+]"])
 		verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], true, true, true, true, false)
 		verifyBranchState(t, tangle, branches["B"], true, true, true, false)
+		// [-A, D+] should be rejected
+		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, true, false, false, true)
+		verifyBranchState(t, tangle, branches["A"], true, false, false, true)
+
+		tangle.Expect("PayloadLiked", valueObjects["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionPreferred", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionConfirmed", transactions["[-E, -F, G+]"], mock.Anything)
 
 		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, false, false, false, false)
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true)
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-E, -F, G+]"])
 		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, true, true, true, false)
 
-		// [-A, D+] should be rejected
-		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, true, false, false, true)
-		verifyBranchState(t, tangle, branches["A"], true, false, false, true)
+		tangle.AssertExpectations(t)
 	}
 
 	// simulate vote on [-A, D+] -> Branch B becomes rejected, Branch A confirmed
 	{
 		tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t)
+		defer tangle.DetachAll()
+
+		tangle.Expect("PayloadLiked", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionPreferred", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+		tangle.Expect("TransactionConfirmed", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
 
 		// confirm [-GENESIS, A+, B+, C+]
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
 		verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)
 
+		tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+		tangle.Expect("TransactionPreferred", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything)
+		tangle.Expect("TransactionConfirmed", transactions["[-B, -C, E+]"], mock.Anything)
+
 		// confirm [-B, -C, E+]
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
 		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, true, false)
 
+		tangle.Expect("PayloadLiked", valueObjects["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionPreferred", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-A, F+]"], mock.Anything)
+
 		// prefer [-A, F+] and thus Branch B
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-A, F+]"], true)
 		verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], true, false, true, false, false)
 		verifyBranchState(t, tangle, branches["B"], false, true, false, false)
+
+		tangle.Expect("PayloadLiked", valueObjects["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionPreferred", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-E, -F, G+]"], mock.Anything)
+
 		// prefer [-E, -F, G+]
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true)
 		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, false, true, false, false)
 
+		tangle.Expect("PayloadLiked", valueObjects["[-A, D+]"], mock.Anything)
+		tangle.Expect("PayloadConfirmed", valueObjects["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionPreferred", transactions["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionLiked", transactions["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-A, D+]"], mock.Anything)
+		tangle.Expect("TransactionConfirmed", transactions["[-A, D+]"], mock.Anything)
+
+		tangle.Expect("PayloadRejected", valueObjects["[-A, F+]"], mock.Anything)
+		tangle.Expect("PayloadDisliked", valueObjects["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionUnpreferred", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionDisliked", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("TransactionRejected", transactions["[-A, F+]"], mock.Anything)
+		tangle.Expect("PayloadRejected", valueObjects["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("PayloadDisliked", valueObjects["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionUnpreferred", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionDisliked", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionFinalized", transactions["[-E, -F, G+]"], mock.Anything)
+		tangle.Expect("TransactionRejected", transactions["[-E, -F, G+]"], mock.Anything)
+
 		// simulate vote result to like [-A, D+] -> [-A, D+] becomes confirmed and [-A, F+], [-E, -F, G+] rejected
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true)
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-A, D+]"])
@@ -892,12 +1100,15 @@ func TestPropagationScenario1(t *testing.T) {
 		verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, true, false, false, true)
 		verifyBranchState(t, tangle, branches["B"], true, false, false, true)
 		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true)
+
+		tangle.AssertExpectations(t)
 	}
 }
 
 func TestPropagationScenario2(t *testing.T) {
 	// img/scenario2.png
 	tangle, transactions, valueObjects, branches, _ := preparePropagationScenario2(t)
+	defer tangle.DetachAll()
 
 	// initialize debugger for this test
 	debugger.ResetAliases()
@@ -908,22 +1119,42 @@ func TestPropagationScenario2(t *testing.T) {
 		debugger.RegisterAlias(tx.ID(), "TransactionID"+name)
 	}
 
+	tangle.Expect("PayloadLiked", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+	tangle.Expect("PayloadConfirmed", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything)
+	tangle.Expect("TransactionPreferred", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+	tangle.Expect("TransactionLiked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+	tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+	tangle.Expect("TransactionConfirmed", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything)
+
 	// confirm [-GENESIS, A+, B+, C+]
 	setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
 	setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
 	verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)
 
+	tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+]"], mock.Anything)
+	tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+	tangle.Expect("TransactionPreferred", transactions["[-B, -C, E+]"], mock.Anything)
+	tangle.Expect("TransactionLiked", transactions["[-B, -C, E+]"], mock.Anything)
+
 	// prefer [-B, -C, E+] and thus Branch D
 	setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
 	verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, false, true, false, false)
 	verifyBranchState(t, tangle, branches["D"], false, true, false, false)
 	verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], true, false, true, false, false)
 
+	tangle.Expect("PayloadLiked", valueObjects["[-A, F+]"], mock.Anything)
+	tangle.Expect("TransactionPreferred", transactions["[-A, F+]"], mock.Anything)
+	tangle.Expect("TransactionLiked", transactions["[-A, F+]"], mock.Anything)
+
 	// prefer [-A, F+] and thus Branch B
 	setTransactionPreferredWithCheck(t, tangle, transactions["[-A, F+]"], true)
 	verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], true, false, true, false, false)
 	verifyBranchState(t, tangle, branches["B"], false, true, false, false)
 
+	tangle.Expect("PayloadLiked", valueObjects["[-E, -F, G+]"], mock.Anything)
+	tangle.Expect("TransactionPreferred", transactions["[-E, -F, G+]"], mock.Anything)
+	tangle.Expect("TransactionLiked", transactions["[-E, -F, G+]"], mock.Anything)
+
 	// prefer [-E, -F, G+]
 	setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true)
 	verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, false, true, false, false)
@@ -945,10 +1176,34 @@ func TestPropagationScenario2(t *testing.T) {
 	verifyBranchState(t, tangle, branches["E"], false, false, false, false)
 	verifyBranchState(t, tangle, branches["ACE"], false, false, false, false)
 
+	tangle.Expect("PayloadLiked", valueObjects["[-H, -D, I+]"], mock.Anything)
+	tangle.Expect("TransactionPreferred", transactions["[-H, -D, I+]"], mock.Anything)
+	tangle.Expect("TransactionLiked", transactions["[-H, -D, I+]"], mock.Anything)
+
 	// prefer [-H, -D, I+] - should be liked after votes on [-A, D+] and [-C, H+]
 	setTransactionPreferredWithCheck(t, tangle, transactions["[-H, -D, I+]"], true)
 	verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], true, false, false, false, false)
 
+	tangle.Expect("PayloadLiked", valueObjects["[-A, D+]"], mock.Anything)
+	tangle.Expect("PayloadConfirmed", valueObjects["[-A, D+]"], mock.Anything)
+	tangle.Expect("TransactionPreferred", transactions["[-A, D+]"], mock.Anything)
+	tangle.Expect("TransactionLiked", transactions["[-A, D+]"], mock.Anything)
+	tangle.Expect("TransactionFinalized", transactions["[-A, D+]"], mock.Anything)
+	tangle.Expect("TransactionConfirmed", transactions["[-A, D+]"], mock.Anything)
+
+	tangle.Expect("PayloadRejected", valueObjects["[-A, F+]"], mock.Anything)
+	tangle.Expect("PayloadDisliked", valueObjects["[-A, F+]"], mock.Anything)
+	tangle.Expect("TransactionUnpreferred", transactions["[-A, F+]"], mock.Anything)
+	tangle.Expect("TransactionDisliked", transactions["[-A, F+]"], mock.Anything)
+	tangle.Expect("TransactionFinalized", transactions["[-A, F+]"], mock.Anything)
+	tangle.Expect("TransactionRejected", transactions["[-A, F+]"], mock.Anything)
+	tangle.Expect("PayloadRejected", valueObjects["[-E, -F, G+]"], mock.Anything)
+	tangle.Expect("PayloadDisliked", valueObjects["[-E, -F, G+]"], mock.Anything)
+	tangle.Expect("TransactionUnpreferred", transactions["[-E, -F, G+]"], mock.Anything)
+	tangle.Expect("TransactionDisliked", transactions["[-E, -F, G+]"], mock.Anything)
+	tangle.Expect("TransactionFinalized", transactions["[-E, -F, G+]"], mock.Anything)
+	tangle.Expect("TransactionRejected", transactions["[-E, -F, G+]"], mock.Anything)
+
 	// simulate vote result to like [-A, D+] -> [-A, D+] becomes confirmed and [-A, F+], [-E, -F, G+] rejected
 	setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true)
 	setTransactionFinalizedWithCheck(t, tangle, transactions["[-A, D+]"])
@@ -959,6 +1214,22 @@ func TestPropagationScenario2(t *testing.T) {
 	verifyBranchState(t, tangle, branches["B"], true, false, false, true)
 	verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true)
 
+	tangle.Expect("PayloadLiked", valueObjects["[-C, H+]"], mock.Anything)
+	tangle.Expect("PayloadConfirmed", valueObjects["[-C, H+]"], mock.Anything)
+	tangle.Expect("TransactionPreferred", transactions["[-C, H+]"], mock.Anything)
+	tangle.Expect("TransactionLiked", transactions["[-C, H+]"], mock.Anything)
+	tangle.Expect("TransactionFinalized", transactions["[-C, H+]"], mock.Anything)
+	tangle.Expect("TransactionConfirmed", transactions["[-C, H+]"], mock.Anything)
+
+	tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+]"], mock.Anything)
+	tangle.Expect("PayloadDisliked", valueObjects["[-B, -C, E+]"], mock.Anything)
+	tangle.Expect("TransactionUnpreferred", transactions["[-B, -C, E+]"], mock.Anything)
+	tangle.Expect("TransactionDisliked", transactions["[-B, -C, E+]"], mock.Anything)
+	tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything)
+	tangle.Expect("TransactionRejected", transactions["[-B, -C, E+]"], mock.Anything)
+	tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+	tangle.Expect("PayloadDisliked", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything)
+
 	// simulate vote result to like [-C, H+] -> [-C, H+] becomes confirmed and [-B, -C, E+], [-B, -C, E+] (Reattachment) rejected
 	setTransactionPreferredWithCheck(t, tangle, transactions["[-C, H+]"], true)
 	setTransactionFinalizedWithCheck(t, tangle, transactions["[-C, H+]"])
@@ -973,23 +1244,37 @@ func TestPropagationScenario2(t *testing.T) {
 	verifyBranchState(t, tangle, branches["BD"], true, false, false, true)
 	// TODO: BD is not finalized
 
+	// [-H, -D, I+] is already preferred
+	tangle.Expect("PayloadConfirmed", valueObjects["[-H, -D, I+]"], mock.Anything)
+	tangle.Expect("TransactionFinalized", transactions["[-H, -D, I+]"], mock.Anything)
+	tangle.Expect("TransactionConfirmed", transactions["[-H, -D, I+]"], mock.Anything)
+
 	// [-H, -D, I+] should now be liked
 	verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], true, false, true, false, false)
 	setTransactionFinalizedWithCheck(t, tangle, transactions["[-H, -D, I+]"])
 	verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], true, true, true, true, false)
-
 	// [-B, J+] should be unchanged
 	verifyInclusionState(t, tangle, valueObjects["[-B, J+]"], false, false, false, false, false)
+
+	tangle.Expect("PayloadLiked", valueObjects["[-B, J+]"], mock.Anything)
+	tangle.Expect("PayloadConfirmed", valueObjects["[-B, J+]"], mock.Anything)
+	tangle.Expect("TransactionPreferred", transactions["[-B, J+]"], mock.Anything)
+	tangle.Expect("TransactionLiked", transactions["[-B, J+]"], mock.Anything)
+	tangle.Expect("TransactionFinalized", transactions["[-B, J+]"], mock.Anything)
+	tangle.Expect("TransactionConfirmed", transactions["[-B, J+]"], mock.Anything)
+
 	// [-B, J+] should become confirmed after preferring and finalizing
 	setTransactionPreferredWithCheck(t, tangle, transactions["[-B, J+]"], true)
 	setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, J+]"])
 	verifyInclusionState(t, tangle, valueObjects["[-B, J+]"], true, true, true, true, false)
 	verifyBranchState(t, tangle, branches["E"], true, true, true, false)
 	verifyBranchState(t, tangle, branches["ACE"], true, true, true, false)
+
+	tangle.AssertExpectations(t)
 }
 
 // verifyBranchState verifies the the branch state according to the given parameters.
-func verifyBranchState(t *testing.T, tangle *Tangle, id branchmanager.BranchID, finalized, liked, confirmed, rejected bool) {
+func verifyBranchState(t *testing.T, tangle *eventTangle, id branchmanager.BranchID, finalized, liked, confirmed, rejected bool) {
 	assert.True(t, tangle.branchManager.Branch(id).Consume(func(branch *branchmanager.Branch) {
 		assert.Equalf(t, finalized, branch.Finalized(), "branch finalized state does not match")
 		assert.Equalf(t, liked, branch.Liked(), "branch liked state does not match")
@@ -1000,7 +1285,7 @@ func verifyBranchState(t *testing.T, tangle *Tangle, id branchmanager.BranchID,
 }
 
 // verifyInclusionState verifies the inclusion state of outputs and transaction according to the given parameters.
-func verifyTransactionInclusionState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, preferred, finalized, liked, confirmed, rejected bool) {
+func verifyTransactionInclusionState(t *testing.T, tangle *eventTangle, valueObject *payload.Payload, preferred, finalized, liked, confirmed, rejected bool) {
 	tx := valueObject.Transaction()
 
 	// check outputs
@@ -1025,7 +1310,7 @@ func verifyTransactionInclusionState(t *testing.T, tangle *Tangle, valueObject *
 }
 
 // verifyValueObjectInclusionState verifies the inclusion state of a value object according to the given parameters.
-func verifyValueObjectInclusionState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, liked, confirmed, rejected bool) {
+func verifyValueObjectInclusionState(t *testing.T, tangle *eventTangle, valueObject *payload.Payload, liked, confirmed, rejected bool) {
 	assert.True(t, tangle.PayloadMetadata(valueObject.ID()).Consume(func(payloadMetadata *PayloadMetadata) {
 		assert.Equalf(t, liked, payloadMetadata.Liked(), "value object liked state does not match")
 		assert.Equalf(t, confirmed, payloadMetadata.Confirmed(), "value object confirmed state does not match")
@@ -1034,20 +1319,20 @@ func verifyValueObjectInclusionState(t *testing.T, tangle *Tangle, valueObject *
 }
 
 // verifyInclusionState verifies the inclusion state of outputs, transaction and value object according to the given parameters.
-func verifyInclusionState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, preferred, finalized, liked, confirmed, rejected bool) {
+func verifyInclusionState(t *testing.T, tangle *eventTangle, valueObject *payload.Payload, preferred, finalized, liked, confirmed, rejected bool) {
 	verifyTransactionInclusionState(t, tangle, valueObject, preferred, finalized, liked, confirmed, rejected)
 	verifyValueObjectInclusionState(t, tangle, valueObject, liked, confirmed, rejected)
 }
 
 // setTransactionPreferredWithCheck sets the transaction to preferred and makes sure that no error occurred and it's modified.
-func setTransactionPreferredWithCheck(t *testing.T, tangle *Tangle, tx *transaction.Transaction, preferred bool) {
+func setTransactionPreferredWithCheck(t *testing.T, tangle *eventTangle, tx *transaction.Transaction, preferred bool) {
 	modified, err := tangle.SetTransactionPreferred(tx.ID(), preferred)
 	require.NoError(t, err)
 	assert.True(t, modified)
 }
 
 // setTransactionFinalizedWithCheck sets the transaction to finalized and makes sure that no error occurred and it's modified.
-func setTransactionFinalizedWithCheck(t *testing.T, tangle *Tangle, tx *transaction.Transaction) {
+func setTransactionFinalizedWithCheck(t *testing.T, tangle *eventTangle, tx *transaction.Transaction) {
 	modified, err := tangle.SetTransactionFinalized(tx.ID())
 	require.NoError(t, err)
 	assert.True(t, modified)
diff --git a/dapps/valuetransfers/packages/tangle/tangle_test.go b/dapps/valuetransfers/packages/tangle/tangle_test.go
index ea157aac8073b1365f3365b8ba4f05ca77783254..aab20994b3729d5f1179bf2ffaa1574d89e15c9b 100644
--- a/dapps/valuetransfers/packages/tangle/tangle_test.go
+++ b/dapps/valuetransfers/packages/tangle/tangle_test.go
@@ -5,30 +5,34 @@ import (
 	"math"
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
-
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-
-	"github.com/iotaledger/hive.go/events"
 	"github.com/iotaledger/hive.go/kvstore/mapdb"
 	"github.com/iotaledger/hive.go/types"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/require"
 )
 
 func TestSetTransactionPreferred(t *testing.T) {
 	tangle := New(mapdb.NewMapDB())
+	event := newEventTangle(t, tangle)
+	defer event.DetachAll()
+
 	tx := createDummyTransaction()
 	valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx)
 	tangle.storeTransactionModels(valueObject)
 
+	event.Expect("TransactionPreferred", tx, mock.Anything)
+
 	modified, err := tangle.SetTransactionPreferred(tx.ID(), true)
 	require.NoError(t, err)
 	assert.True(t, modified)
+
+	event.AssertExpectations(t)
 }
 
 // TestBookTransaction tests the following cases:
@@ -41,19 +45,32 @@ func TestBookTransaction(t *testing.T) {
 	// CASE: missing output
 	t.Run("CASE: missing output", func(t *testing.T) {
 		tangle := New(mapdb.NewMapDB())
+		event := newEventTangle(t, tangle)
+		defer event.DetachAll()
+
 		tx := createDummyTransaction()
 		valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx)
-		cachedTransaction, cachedTransactionMetadata, _, _ := tangle.storeTransactionModels(valueObject)
 
+		cachedTransaction, cachedTransactionMetadata, _, transactionIsNew := tangle.storeTransactionModels(valueObject)
+		assert.True(t, transactionIsNew)
+
+		event.Expect("TransactionSolid", tx, mock.Anything)
+
+		// manually trigger a booking: tx will be marked solid, but it cannot be book as its inputs are unavailable
 		transactionBooked, decisionPending, err := tangle.bookTransaction(cachedTransaction, cachedTransactionMetadata)
 		assert.False(t, transactionBooked)
 		assert.False(t, decisionPending)
 		assert.Error(t, err)
+
+		event.AssertExpectations(t)
 	})
 
 	// CASE: transaction already booked by another process
 	t.Run("CASE: transaction already booked by another process", func(t *testing.T) {
 		tangle := New(mapdb.NewMapDB())
+		event := newEventTangle(t, tangle)
+		defer event.DetachAll()
+
 		tx := createDummyTransaction()
 		valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx)
 		cachedTransaction, cachedTransactionMetadata, _, _ := tangle.storeTransactionModels(valueObject)
@@ -65,11 +82,15 @@ func TestBookTransaction(t *testing.T) {
 		require.NoError(t, err)
 		assert.False(t, transactionBooked)
 		assert.False(t, decisionPending)
+
+		event.AssertExpectations(t)
 	})
 
 	// CASE: booking first spend
 	t.Run("CASE: booking first spend", func(t *testing.T) {
 		tangle := New(mapdb.NewMapDB())
+		event := newEventTangle(t, tangle)
+		defer event.DetachAll()
 
 		// prepare snapshot
 		color1 := [32]byte{1}
@@ -85,7 +106,7 @@ func TestBookTransaction(t *testing.T) {
 		inputIDs := loadSnapshotFromOutputs(tangle, outputs)
 
 		// build first spending
-		tx := transaction.New(
+		tx1 := transaction.New(
 			transaction.NewInputs(inputIDs...),
 			// outputs
 			transaction.NewOutputs(map[address.Address][]*balance.Balance{
@@ -96,13 +117,16 @@ func TestBookTransaction(t *testing.T) {
 			}),
 		)
 
-		valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx)
+		valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx1)
 		cachedTransaction, cachedTransactionMetadata, _, _ := tangle.storeTransactionModels(valueObject)
 		txMetadata := cachedTransactionMetadata.Unwrap()
 
 		// assert that branchID is undefined before being booked
 		assert.Equal(t, branchmanager.UndefinedBranchID, txMetadata.BranchID())
 
+		event.Expect("TransactionSolid", tx1, mock.Anything)
+		// TransactionBooked is triggered outside of bookTransaction
+
 		transactionBooked, decisionPending, err := tangle.bookTransaction(cachedTransaction, cachedTransactionMetadata)
 		require.NoError(t, err)
 		assert.True(t, transactionBooked, "transactionBooked")
@@ -114,7 +138,7 @@ func TestBookTransaction(t *testing.T) {
 		// CASE: booking double spend
 		t.Run("CASE: booking double spend", func(t *testing.T) {
 			// build second spending
-			tx := transaction.New(
+			tx2 := transaction.New(
 				transaction.NewInputs(inputIDs...),
 				// outputs
 				transaction.NewOutputs(map[address.Address][]*balance.Balance{
@@ -125,13 +149,17 @@ func TestBookTransaction(t *testing.T) {
 				}),
 			)
 
-			valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx)
+			valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx2)
 			cachedTransaction, cachedTransactionMetadata, _, _ = tangle.storeTransactionModels(valueObject)
 			txMetadata := cachedTransactionMetadata.Unwrap()
 
 			// assert that branchID is undefined before being booked
 			assert.Equal(t, branchmanager.UndefinedBranchID, txMetadata.BranchID())
 
+			// manually book the double spending tx2, this will mark it as solid and trigger a fork
+			event.Expect("TransactionSolid", tx2, mock.Anything)
+			event.Expect("Fork", tx1, mock.Anything, mock.Anything, inputIDs)
+
 			transactionBooked, decisionPending, err := tangle.bookTransaction(cachedTransaction, cachedTransactionMetadata)
 			require.NoError(t, err)
 			assert.True(t, transactionBooked, "transactionBooked")
@@ -140,6 +168,8 @@ func TestBookTransaction(t *testing.T) {
 			// assert that first spend and double spend have different BranchIDs
 			assert.NotEqual(t, branchmanager.MasterBranchID, txMetadata.BranchID(), "BranchID")
 		})
+
+		event.AssertExpectations(t)
 	})
 }
 
@@ -230,6 +260,9 @@ func TestFork(t *testing.T) {
 	// CASE: already finalized
 	t.Run("CASE: already finalized", func(t *testing.T) {
 		tangle := New(mapdb.NewMapDB())
+		event := newEventTangle(t, tangle)
+		defer event.DetachAll()
+
 		// prepare snapshot
 		color1 := [32]byte{1}
 		outputs := map[address.Address][]*balance.Balance{
@@ -259,14 +292,20 @@ func TestFork(t *testing.T) {
 
 		txMetadata.setFinalized(true)
 
+		// no fork created so no event should be triggered
 		forked, finalized, err := tangle.Fork(tx.ID(), []transaction.OutputID{})
 		require.NoError(t, err)
 		assert.False(t, forked)
 		assert.True(t, finalized)
+
+		event.AssertExpectations(t)
 	})
 
 	t.Run("CASE: normal fork", func(t *testing.T) {
 		tangle := New(mapdb.NewMapDB())
+		event := newEventTangle(t, tangle)
+		defer event.DetachAll()
+
 		// prepare snapshot
 		color1 := [32]byte{1}
 		outputs := map[address.Address][]*balance.Balance{
@@ -293,24 +332,30 @@ func TestFork(t *testing.T) {
 		valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx)
 		tangle.storeTransactionModels(valueObject)
 
+		event.Expect("Fork", tx, mock.Anything, mock.Anything, []transaction.OutputID{})
+
 		forked, finalized, err := tangle.Fork(tx.ID(), []transaction.OutputID{})
 		require.NoError(t, err)
 		assert.True(t, forked, "forked")
 		assert.False(t, finalized, "finalized")
 
 		t.Run("CASE: branch existed already", func(t *testing.T) {
+			// no fork created so no event should be triggered
 			forked, finalized, err = tangle.Fork(tx.ID(), []transaction.OutputID{})
 			require.NoError(t, err)
 			assert.False(t, forked, "forked")
 			assert.False(t, finalized, "finalized")
 		})
-	})
 
+		event.AssertExpectations(t)
+	})
 }
 
 func TestBookPayload(t *testing.T) {
 	t.Run("CASE: undefined branchID", func(t *testing.T) {
 		tangle := New(mapdb.NewMapDB())
+		event := newEventTangle(t, tangle)
+		defer event.DetachAll()
 
 		valueObject := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
 		cachedPayload, cachedMetadata, _ := tangle.storePayload(valueObject)
@@ -325,10 +370,14 @@ func TestBookPayload(t *testing.T) {
 
 		require.NoError(t, err)
 		assert.False(t, payloadBooked, "payloadBooked")
+
+		event.AssertExpectations(t)
 	})
 
 	t.Run("CASE: successfully book", func(t *testing.T) {
 		tangle := New(mapdb.NewMapDB())
+		event := newEventTangle(t, tangle)
+		defer event.DetachAll()
 
 		valueObject := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
 		cachedPayload, cachedMetadata, _ := tangle.storePayload(valueObject)
@@ -341,6 +390,7 @@ func TestBookPayload(t *testing.T) {
 		txMetadata := cachedTransactionMetadata.Unwrap()
 		txMetadata.setBranchID(branchmanager.BranchID{1})
 
+		event.Expect("PayloadSolid", valueObject, mock.Anything)
 		payloadBooked, err := tangle.bookPayload(cachedPayload.Retain(), cachedMetadata.Retain(), cachedTransactionMetadata.Retain())
 		defer func() {
 			cachedPayload.Release()
@@ -350,10 +400,14 @@ func TestBookPayload(t *testing.T) {
 
 		require.NoError(t, err)
 		assert.True(t, payloadBooked, "payloadBooked")
+
+		event.AssertExpectations(t)
 	})
 
 	t.Run("CASE: not booked", func(t *testing.T) {
 		tangle := New(mapdb.NewMapDB())
+		event := newEventTangle(t, tangle)
+		defer event.DetachAll()
 
 		valueObject := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
 		cachedPayload, cachedMetadata, _ := tangle.storePayload(valueObject)
@@ -366,6 +420,7 @@ func TestBookPayload(t *testing.T) {
 		txMetadata := cachedTransactionMetadata.Unwrap()
 		txMetadata.setBranchID(branchmanager.BranchID{1})
 
+		event.Expect("PayloadSolid", valueObject, mock.Anything)
 		payloadBooked, err := tangle.bookPayload(cachedPayload.Retain(), cachedMetadata.Retain(), cachedTransactionMetadata.Retain())
 		defer func() {
 			cachedPayload.Release()
@@ -375,6 +430,8 @@ func TestBookPayload(t *testing.T) {
 
 		require.NoError(t, err)
 		assert.False(t, payloadBooked, "payloadBooked")
+
+		event.AssertExpectations(t)
 	})
 
 }
@@ -382,6 +439,8 @@ func TestBookPayload(t *testing.T) {
 // TestStorePayload checks whether a value object is correctly stored.
 func TestStorePayload(t *testing.T) {
 	tangle := New(mapdb.NewMapDB())
+	event := newEventTangle(t, tangle)
+	defer event.DetachAll()
 
 	tx := createDummyTransaction()
 	valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx)
@@ -415,6 +474,8 @@ func TestStorePayload(t *testing.T) {
 			assert.Equal(t, valueObject.ID(), payloadMetadata.PayloadID())
 		})
 	}
+
+	event.AssertExpectations(t)
 }
 
 // TestStoreTransactionModels checks whether all models corresponding to a transaction are correctly created.
@@ -896,7 +957,7 @@ func TestRetrieveConsumedInputDetails(t *testing.T) {
 		cachedInputs.Consume(func(input *Output) {
 			assert.ElementsMatch(t, outputs[input.Address()], input.Balances())
 		})
-		assert.True(t, cmp.Equal(sumOutputsByColor(outputs), consumedBalances))
+		assert.Equal(t, sumOutputsByColor(outputs), consumedBalances)
 		assert.Len(t, consumedBranches, 1)
 		assert.Contains(t, consumedBranches, branchmanager.MasterBranchID)
 	}
@@ -938,7 +999,7 @@ func TestRetrieveConsumedInputDetails(t *testing.T) {
 		cachedInputs.Consume(func(input *Output) {
 			assert.ElementsMatch(t, outputs[input.Address()], input.Balances())
 		})
-		assert.True(t, cmp.Equal(sumOutputsByColor(outputs), consumedBalances))
+		assert.Equal(t, sumOutputsByColor(outputs), consumedBalances)
 		assert.Len(t, consumedBranches, 1)
 		assert.Contains(t, consumedBranches, branchmanager.MasterBranchID)
 	}
@@ -1003,7 +1064,7 @@ func TestRetrieveConsumedInputDetails(t *testing.T) {
 		cachedInputs.Consume(func(input *Output) {
 			assert.ElementsMatch(t, outputs[input.Address()], input.Balances())
 		})
-		assert.True(t, cmp.Equal(sumOutputsByColor(outputs), consumedBalances))
+		assert.Equal(t, sumOutputsByColor(outputs), consumedBalances)
 		assert.Len(t, consumedBranches, 2)
 		assert.Contains(t, consumedBranches, branchmanager.MasterBranchID)
 		assert.Contains(t, consumedBranches, newBranch)
@@ -1172,6 +1233,8 @@ func TestCheckTransactionSolidity(t *testing.T) {
 
 func TestPayloadBranchID(t *testing.T) {
 	tangle := New(mapdb.NewMapDB())
+	event := newEventTangle(t, tangle)
+	defer event.DetachAll()
 
 	{
 		branchID := tangle.payloadBranchID(payload.GenesisID)
@@ -1197,19 +1260,19 @@ func TestPayloadBranchID(t *testing.T) {
 	// test missing value object
 	{
 		valueObject := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
-		missing := 0
-		tangle.Events.PayloadMissing.Attach(events.NewClosure(func(payloadID payload.ID) {
-			missing++
-		}))
 
+		event.Expect("PayloadMissing", valueObject.ID())
 		branchID := tangle.payloadBranchID(valueObject.ID())
 		assert.Equal(t, branchmanager.UndefinedBranchID, branchID)
-		assert.Equal(t, 1, missing)
 	}
+
+	event.AssertExpectations(t)
 }
 
 func TestCheckPayloadSolidity(t *testing.T) {
 	tangle := New(mapdb.NewMapDB())
+	event := newEventTangle(t, tangle)
+	defer event.DetachAll()
 
 	// check with already solid payload
 	{
@@ -1315,12 +1378,17 @@ func TestCheckPayloadSolidity(t *testing.T) {
 		assert.False(t, solid)
 		assert.Error(t, err)
 	}
+
+	event.AssertExpectations(t)
 }
 
 func TestCreateValuePayloadFutureConeIterator(t *testing.T) {
 	// check with new payload -> should be added to stack
 	{
 		tangle := New(mapdb.NewMapDB())
+		event := newEventTangle(t, tangle)
+		defer event.DetachAll()
+
 		solidificationStack := list.New()
 		processedPayloads := make(map[payload.ID]types.Empty)
 		iterator := tangle.createValuePayloadFutureConeIterator(solidificationStack, processedPayloads)
@@ -1349,11 +1417,16 @@ func TestCreateValuePayloadFutureConeIterator(t *testing.T) {
 		currentSolidificationEntry.CachedTransactionMetadata.Consume(func(metadata *TransactionMetadata) {
 			assert.Equal(t, tx.ID(), metadata.ID())
 		})
+
+		event.AssertExpectations(t)
 	}
 
 	// check with already processed payload -> should not be added to stack
 	{
 		tangle := New(mapdb.NewMapDB())
+		event := newEventTangle(t, tangle)
+		defer event.DetachAll()
+
 		solidificationStack := list.New()
 		processedPayloads := make(map[payload.ID]types.Empty)
 		iterator := tangle.createValuePayloadFutureConeIterator(solidificationStack, processedPayloads)
@@ -1371,6 +1444,8 @@ func TestCreateValuePayloadFutureConeIterator(t *testing.T) {
 
 		iterator(cachedPayload, cachedMetadata, cachedTransaction, cachedTransactionMetadata)
 		assert.Equal(t, 0, solidificationStack.Len())
+
+		event.AssertExpectations(t)
 	}
 }
 
@@ -1469,6 +1544,55 @@ func TestForeachApprovers(t *testing.T) {
 	assert.Equal(t, 2, counter)
 }
 
+func TestMissingPayloadReceived(t *testing.T) {
+	tangle := New(mapdb.NewMapDB())
+	event := newEventTangle(t, tangle)
+	defer event.DetachAll()
+
+	// prepare snapshot
+	unspentOutputs := loadSnapshotFromOutputs(
+		tangle,
+		map[address.Address][]*balance.Balance{
+			address.Random(): {
+				balance.New(balance.ColorIOTA, 3),
+			},
+		},
+	)
+
+	// create transaction spending those snapshot outputs
+	tx := transaction.New(
+		transaction.NewInputs(unspentOutputs...),
+		transaction.NewOutputs(map[address.Address][]*balance.Balance{
+			address.Random(): {
+				balance.New(balance.ColorIOTA, 3),
+			},
+		}),
+	)
+
+	// create two value objects for this transaction referencing each other
+	parent := payload.New(payload.GenesisID, payload.GenesisID, tx)
+	child := payload.New(parent.ID(), parent.ID(), tx)
+
+	event.Expect("PayloadAttached", child, mock.Anything)
+	event.Expect("PayloadMissing", parent.ID(), mock.Anything)
+	event.Expect("TransactionReceived", tx, mock.Anything, mock.Anything)
+
+	// submit the child first; it cannot be solidified
+	tangle.AttachPayloadSync(child)
+
+	event.Expect("PayloadAttached", parent, mock.Anything)
+	event.Expect("PayloadSolid", parent, mock.Anything)
+	event.Expect("MissingPayloadReceived", parent, mock.Anything)
+	event.Expect("PayloadSolid", child, mock.Anything)
+	event.Expect("TransactionSolid", tx, mock.Anything, mock.Anything)
+	event.Expect("TransactionBooked", tx, mock.Anything, true)
+
+	// submitting the parent makes everything solid
+	tangle.AttachPayloadSync(parent)
+
+	event.AssertExpectations(t)
+}
+
 func storeParentPayloadWithMetadataFunc(t *testing.T, tangle *Tangle, consume func(*PayloadMetadata)) payload.ID {
 	parent1 := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
 	cachedPayload, cachedMetadata, stored := tangle.storePayload(parent1)
diff --git a/dapps/valuetransfers/packages/tangle/transactionmetadata.go b/dapps/valuetransfers/packages/tangle/transactionmetadata.go
index 106075bafe490af586673c960e379a8b8430a4dc..22b832fef18bc4fd2638c2c15dfe1b1772069084 100644
--- a/dapps/valuetransfers/packages/tangle/transactionmetadata.go
+++ b/dapps/valuetransfers/packages/tangle/transactionmetadata.go
@@ -131,6 +131,7 @@ func (transactionMetadata *TransactionMetadata) setBranchID(branchID branchmanag
 	}
 
 	transactionMetadata.branchID = branchID
+	transactionMetadata.SetModified()
 	modified = true
 
 	return
diff --git a/dapps/valuetransfers/packages/transaction/transaction.go b/dapps/valuetransfers/packages/transaction/transaction.go
index 3ad8aa7533d362e049ece4ba2c5e19a68ac19906..3b234bdf0bec0bff067f85aa5ca4fae2b3e7bb5b 100644
--- a/dapps/valuetransfers/packages/transaction/transaction.go
+++ b/dapps/valuetransfers/packages/transaction/transaction.go
@@ -207,6 +207,7 @@ func (transaction *Transaction) EssenceBytes() []byte {
 
 	// store marshaled result
 	transaction.essenceBytes = marshalUtil.Bytes()
+	transaction.SetModified()
 
 	return transaction.essenceBytes
 }
@@ -274,7 +275,7 @@ func (transaction *Transaction) Bytes() []byte {
 // Sign adds a new signature to the Transaction.
 func (transaction *Transaction) Sign(signature signaturescheme.SignatureScheme) *Transaction {
 	transaction.signatures.Add(signature.Address(), signature.Sign(transaction.EssenceBytes()))
-
+	transaction.SetModified()
 	return transaction
 }
 
@@ -284,7 +285,7 @@ func (transaction *Transaction) PutSignature(signature signaturescheme.Signature
 		return errors.New("PutSignature: invalid signature")
 	}
 	transaction.signatures.Add(signature.Address(), signature)
-
+	transaction.SetModified()
 	return nil
 }
 
diff --git a/go.mod b/go.mod
index 07ea533ab1327f151744785a703ab5bac400eb90..49d99d9b3e46e7378ced11c9476b61808e8faaaf 100644
--- a/go.mod
+++ b/go.mod
@@ -13,7 +13,7 @@ require (
 	github.com/golang/protobuf v1.3.5
 	github.com/google/go-cmp v0.4.1
 	github.com/gorilla/websocket v1.4.1
-	github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242
+	github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814
 	github.com/iotaledger/iota.go v1.0.0-beta.14
 	github.com/labstack/echo v3.3.10+incompatible
 	github.com/labstack/gommon v0.3.0
diff --git a/go.sum b/go.sum
index 1b9f1a3f7b9ef9aa472c784ac14d6645fb5bdab3..cf80657fed306bcb108b0ea53f9b12eb32b2b8ed 100644
--- a/go.sum
+++ b/go.sum
@@ -153,8 +153,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242 h1:uHMFmfrP6O6lp1lCHT6lpFwHFWYk77V0nUlGbhneQHI=
-github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242/go.mod h1:zwZhaE4ZeglpTrbmbwdnVPMI5XdRu2RmByi3Qn0ztmU=
+github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814 h1:9Cg6q13ngg3/UxBPQjdDREYr+792HO8SN8KzI1t7xFY=
+github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814/go.mod h1:zwZhaE4ZeglpTrbmbwdnVPMI5XdRu2RmByi3Qn0ztmU=
 github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8=
 github.com/iotaledger/iota.go v1.0.0-beta.14 h1:Oeb28MfBuJEeXcGrLhTCJFtbsnc8y1u7xidsAmiOD5A=
 github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8=
diff --git a/packages/gossip/manager.go b/packages/gossip/manager.go
index 128a323d6168cc13db850f0c9b05416d1abd78f0..40d96b28410e2162aa365eb93e0d7053a5e44c01 100644
--- a/packages/gossip/manager.go
+++ b/packages/gossip/manager.go
@@ -9,6 +9,7 @@ import (
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 	pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
 	"github.com/iotaledger/goshimmer/packages/gossip/server"
+	"github.com/iotaledger/hive.go/async"
 	"github.com/iotaledger/hive.go/autopeering/peer"
 	"github.com/iotaledger/hive.go/events"
 	"github.com/iotaledger/hive.go/identity"
@@ -34,11 +35,14 @@ type Manager struct {
 	mu        sync.Mutex
 	srv       *server.TCP
 	neighbors map[identity.ID]*Neighbor
+
+	// inboxWorkerPool defines a worker pool where all incoming messages are processed.
+	inboxWorkerPool async.WorkerPool
 }
 
 // NewManager creates a new Manager.
 func NewManager(local *peer.Local, f LoadMessageFunc, log *logger.Logger) *Manager {
-	return &Manager{
+	m := &Manager{
 		local:           local,
 		loadMessageFunc: f,
 		log:             log,
@@ -51,6 +55,8 @@ func NewManager(local *peer.Local, f LoadMessageFunc, log *logger.Logger) *Manag
 		srv:       nil,
 		neighbors: make(map[identity.ID]*Neighbor),
 	}
+	m.inboxWorkerPool.Tune(2)
+	return m
 }
 
 // Start starts the manager for the given TCP server.
@@ -65,6 +71,8 @@ func (m *Manager) Start(srv *server.TCP) {
 func (m *Manager) Close() {
 	m.stop()
 	m.wg.Wait()
+
+	m.inboxWorkerPool.ShutdownGracefully()
 }
 
 // Events returns the events related to the gossip protocol.
@@ -206,9 +214,13 @@ func (m *Manager) addNeighbor(peer *peer.Peer, connectorFunc func(*peer.Peer) (n
 		m.events.NeighborRemoved.Trigger(peer)
 	}))
 	n.Events.ReceiveMessage.Attach(events.NewClosure(func(data []byte) {
-		if err := m.handlePacket(data, peer); err != nil {
-			m.log.Debugw("error handling packet", "err", err)
-		}
+		dataCopy := make([]byte, len(data))
+		copy(dataCopy, data)
+		m.inboxWorkerPool.Submit(func() {
+			if err := m.handlePacket(dataCopy, n); err != nil {
+				m.log.Debugw("error handling packet", "err", err)
+			}
+		})
 	}))
 
 	m.neighbors[peer.ID()] = n
@@ -218,7 +230,7 @@ func (m *Manager) addNeighbor(peer *peer.Peer, connectorFunc func(*peer.Peer) (n
 	return nil
 }
 
-func (m *Manager) handlePacket(data []byte, p *peer.Peer) error {
+func (m *Manager) handlePacket(data []byte, n *Neighbor) error {
 	// ignore empty packages
 	if len(data) == 0 {
 		return nil
@@ -231,8 +243,8 @@ func (m *Manager) handlePacket(data []byte, p *peer.Peer) error {
 		if err := proto.Unmarshal(data[1:], protoMsg); err != nil {
 			return fmt.Errorf("invalid packet: %w", err)
 		}
-		m.log.Debugw("received packet", "type", protoMsg.Type(), "peer-id", p.ID())
-		m.events.MessageReceived.Trigger(&MessageReceivedEvent{Data: protoMsg.GetData(), Peer: p})
+		m.log.Debugw("received packet", "type", protoMsg.Type(), "peer-id", n.Peer.ID())
+		m.events.MessageReceived.Trigger(&MessageReceivedEvent{Data: protoMsg.GetData(), Peer: n.Peer})
 
 	case pb.PacketMessageRequest:
 		protoMsgReq := new(pb.MessageRequest)
@@ -240,20 +252,20 @@ func (m *Manager) handlePacket(data []byte, p *peer.Peer) error {
 			return fmt.Errorf("invalid packet: %w", err)
 		}
 
-		m.log.Debugw("received packet", "type", protoMsgReq.Type(), "peer-id", p.ID())
+		m.log.Debugw("received packet", "type", protoMsgReq.Type(), "peer-id", n.Peer.ID())
 		msgId, _, err := message.IdFromBytes(protoMsgReq.GetId())
 		if err != nil {
-			m.log.Debugw("couldn't compute message id from bytes", "peer-id", p.ID(), "err", err)
+			m.log.Debugw("couldn't compute message id from bytes", "peer-id", n.Peer.ID(), "err", err)
 			return nil
 		}
 
 		msg, err := m.loadMessageFunc(msgId)
 		if err != nil {
-			m.log.Debugw("error getting message", "peer-id", p.ID(), "msg-id", msgId, "err", err)
+			m.log.Debugw("error getting message", "peer-id", n.Peer.ID(), "msg-id", msgId, "err", err)
 			return nil
 		}
 
-		m.SendMessage(msg, p.ID())
+		n.Write(marshal(&pb.Message{Data: msg}))
 	default:
 		return ErrInvalidPacket
 	}
diff --git a/plugins/profiling/plugin.go b/plugins/profiling/plugin.go
index 2aeb0f6439b3baddea1b679117059e16ef0315cd..388da6453312ed18e7a030241dafbe03eb7af109 100644
--- a/plugins/profiling/plugin.go
+++ b/plugins/profiling/plugin.go
@@ -2,6 +2,8 @@ package profiling
 
 import (
 	"net/http"
+	"runtime"
+
 	// import required to profile
 	_ "net/http/pprof"
 
@@ -33,6 +35,10 @@ func configure(_ *node.Plugin) {
 
 func run(_ *node.Plugin) {
 	bindAddr := config.Node.GetString(CfgProfilingBindAddress)
+
+	runtime.SetMutexProfileFraction(5)
+	runtime.SetBlockProfileRate(5)
+
 	log.Infof("%s started, bind-address=%s", PluginName, bindAddr)
 	go http.ListenAndServe(bindAddr, nil)
 }
diff --git a/plugins/testsnapshots/plugin.go b/plugins/testsnapshots/plugin.go
index 2e47c4c02938840a68a6d401ab6b745a1f97adb7..294e53378b9260a77104600485b5d513a3ec1927 100644
--- a/plugins/testsnapshots/plugin.go
+++ b/plugins/testsnapshots/plugin.go
@@ -4,19 +4,19 @@ import (
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
 	"github.com/iotaledger/hive.go/logger"
 	"github.com/iotaledger/hive.go/node"
 )
 
+// FIXME: This plugin can be removed after snapshots is implemented
 const (
-	// PluginName is the plugin name of the bootstrap plugin.
+	// PluginName is the plugin name of the TestSnapshots plugin.
 	PluginName = "TestSnapshots"
 )
 
 var (
-	// Plugin is the plugin instance of the bootstrap plugin.
+	// Plugin is the plugin instance of the TestSnapshots plugin.
 	Plugin = node.NewPlugin(PluginName, node.Disabled, configure, run)
 	log    *logger.Logger
 
@@ -27,7 +27,7 @@ var (
 func configure(_ *node.Plugin) {
 	log = logger.NewLogger(PluginName)
 
-	valuetransfers.Tangle.LoadSnapshot(tangle.Snapshot{
+	valuetransfers.Tangle.LoadSnapshot(map[transaction.ID]map[address.Address][]*balance.Balance{
 		transaction.GenesisID: {
 			address0: []*balance.Balance{
 				balance.New(balance.ColorIOTA, 10000000),
diff --git a/tools/integration-tests/runTests.sh b/tools/integration-tests/runTests.sh
index 42926b752932817fcc014824a45a490ca1f36d36..0c0339ee516234599f0ba76b00bab665e3db297a 100755
--- a/tools/integration-tests/runTests.sh
+++ b/tools/integration-tests/runTests.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-TEST_NAMES='autopeering common drng message consensus'
+TEST_NAMES='autopeering common drng message value'
 
 echo "Build GoShimmer image"
 docker build -t iotaledger/goshimmer ../../.
diff --git a/tools/integration-tests/tester/framework/docker.go b/tools/integration-tests/tester/framework/docker.go
index 7b901ce4ff033a1bc438663a9bdb8f9f99275db7..769d53d294c6463d609c21d91011559459bac294 100644
--- a/tools/integration-tests/tester/framework/docker.go
+++ b/tools/integration-tests/tester/framework/docker.go
@@ -87,12 +87,13 @@ func (d *DockerContainer) CreateGoShimmerPeer(config GoShimmerConfig) error {
 			fmt.Sprintf("--node.disablePlugins=%s", config.DisabledPlugins),
 			fmt.Sprintf("--node.enablePlugins=%s", func() string {
 				var plugins []string
+				//TODO: remove this when snapshots is implemented
+				plugins = append(plugins, "testSnapshots")
 				if config.Bootstrap {
 					plugins = append(plugins, "Bootstrap")
 				}
 				if config.Faucet {
 					plugins = append(plugins, "faucet")
-					plugins = append(plugins, "testSnapshots")
 				}
 				return strings.Join(plugins[:], ",")
 			}()),
diff --git a/tools/integration-tests/tester/framework/network.go b/tools/integration-tests/tester/framework/network.go
index b2001e554c81fc8bcfd922a6acf5b23466b32bdc..51c57cb6f7fe397f5a68206eeef868b0d6358487 100644
--- a/tools/integration-tests/tester/framework/network.go
+++ b/tools/integration-tests/tester/framework/network.go
@@ -110,6 +110,14 @@ func (n *Network) CreatePeer(c GoShimmerConfig) (*Peer, error) {
 		nodeWallet = wallet.New()
 	}
 
+	// create wallet
+	var nodeWallet *wallet.Wallet
+	if c.Faucet == true {
+		nodeWallet = wallet.New(faucetSeed)
+	} else {
+		nodeWallet = wallet.New()
+	}
+
 	// create Docker container
 	container := NewDockerContainer(n.dockerClient)
 	err = container.CreateGoShimmerPeer(config)
diff --git a/tools/integration-tests/tester/framework/parameters.go b/tools/integration-tests/tester/framework/parameters.go
index 9b88366083f9aed1e0d03198b4029efff86ec613..b0b0eb52de74d0e9fa178b4586eb51f16f49a06a 100644
--- a/tools/integration-tests/tester/framework/parameters.go
+++ b/tools/integration-tests/tester/framework/parameters.go
@@ -1,9 +1,5 @@
 package framework
 
-import (
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
-)
-
 const (
 	autopeeringMaxTries = 50
 
@@ -27,16 +23,6 @@ const (
 	exitStatusSuccessful = 0
 )
 
-// Parameters to override before calling any peer creation function.
-var (
-	// ParaFCoBAverageNetworkDelay defines the configured avg. network delay (in seconds) for the FCOB rules.
-	ParaFCoBAverageNetworkDelay = 5
-	// ParaOutboundUpdateIntervalMs the autopeering outbound update interval in milliseconds.
-	ParaOutboundUpdateIntervalMs = 100
-	// ParaBootstrapOnEveryNode whether to enable the bootstrap plugin on every node.
-	ParaBootstrapOnEveryNode = false
-)
-
 var (
 	faucetSeed = []byte{251, 163, 190, 98, 92, 82, 164, 79, 74, 48, 203, 162, 247, 119, 140, 76, 33, 100, 148, 204, 244, 248, 232, 18,
 		132, 217, 85, 31, 246, 83, 193, 193}
@@ -60,8 +46,6 @@ type GoShimmerConfig struct {
 	DRNGThreshold int
 
 	Faucet bool
-
-	Wallet *wallet.Wallet
 }
 
 // NetworkConfig defines the config of a GoShimmer Docker network.
diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod
index bc7188a199c720fd820117d96e5db574229a1034..71d5b0486458cb4d01a5a55d7b8752d442f4ce13 100644
--- a/tools/integration-tests/tester/go.mod
+++ b/tools/integration-tests/tester/go.mod
@@ -10,9 +10,10 @@ require (
 	github.com/docker/go-units v0.4.0 // indirect
 	github.com/drand/drand v0.8.1
 	github.com/iotaledger/goshimmer v0.1.3
-	github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242
 	github.com/mr-tron/base58 v1.1.3
 	github.com/opencontainers/go-digest v1.0.0 // indirect
+	github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814
+	github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
 	github.com/stretchr/testify v1.6.1
 )
 
diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum
index e26c830f613da1b5962bb4d20b1935439bcad74b..d7794210a9e9f3b986e1733599b63c037dd53f0a 100644
--- a/tools/integration-tests/tester/go.sum
+++ b/tools/integration-tests/tester/go.sum
@@ -139,8 +139,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242 h1:uHMFmfrP6O6lp1lCHT6lpFwHFWYk77V0nUlGbhneQHI=
-github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242/go.mod h1:zwZhaE4ZeglpTrbmbwdnVPMI5XdRu2RmByi3Qn0ztmU=
+github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814 h1:9Cg6q13ngg3/UxBPQjdDREYr+792HO8SN8KzI1t7xFY=
+github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814/go.mod h1:zwZhaE4ZeglpTrbmbwdnVPMI5XdRu2RmByi3Qn0ztmU=
 github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8=
 github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
diff --git a/tools/integration-tests/tester/tests/common/common_test.go b/tools/integration-tests/tester/tests/common/common_test.go
index 7cdceef806dd0823a35e9e80ccbceadf25c0e15d..32f4fea32eea5fcbe1afd1b2ba9856fe258dbbf7 100644
--- a/tools/integration-tests/tester/tests/common/common_test.go
+++ b/tools/integration-tests/tester/tests/common/common_test.go
@@ -1,4 +1,4 @@
-package autopeering
+package common
 
 import (
 	"testing"
diff --git a/tools/integration-tests/tester/tests/common/main_test.go b/tools/integration-tests/tester/tests/common/main_test.go
index bcc09c2e53590df061cbff5a2159071d567440f1..cdffe476461e98e312c15ef4dd3f001c67e1faf4 100644
--- a/tools/integration-tests/tester/tests/common/main_test.go
+++ b/tools/integration-tests/tester/tests/common/main_test.go
@@ -1,4 +1,4 @@
-package autopeering
+package common
 
 import (
 	"os"
diff --git a/tools/integration-tests/tester/tests/drng/drng_test.go b/tools/integration-tests/tester/tests/drng/drng_test.go
index a9b59a9f94f84126257e0e7892119d12300f39bf..4db5911975cc4a2b89b6ab892faccfd30bea1f50 100644
--- a/tools/integration-tests/tester/tests/drng/drng_test.go
+++ b/tools/integration-tests/tester/tests/drng/drng_test.go
@@ -1,4 +1,4 @@
-package autopeering
+package drng
 
 import (
 	"encoding/json"
diff --git a/tools/integration-tests/tester/tests/drng/main_test.go b/tools/integration-tests/tester/tests/drng/main_test.go
index bcc09c2e53590df061cbff5a2159071d567440f1..27877125211c397ac15776329b7a87ce5cb2c9f9 100644
--- a/tools/integration-tests/tester/tests/drng/main_test.go
+++ b/tools/integration-tests/tester/tests/drng/main_test.go
@@ -1,4 +1,4 @@
-package autopeering
+package drng
 
 import (
 	"os"
diff --git a/tools/integration-tests/tester/tests/message/main_test.go b/tools/integration-tests/tester/tests/message/main_test.go
index bcc09c2e53590df061cbff5a2159071d567440f1..4d3e5451fbcbed76b446721062a23f6c515d1dbf 100644
--- a/tools/integration-tests/tester/tests/message/main_test.go
+++ b/tools/integration-tests/tester/tests/message/main_test.go
@@ -1,4 +1,4 @@
-package autopeering
+package message
 
 import (
 	"os"
diff --git a/tools/integration-tests/tester/tests/message/message_test.go b/tools/integration-tests/tester/tests/message/message_test.go
index 37460d75389b960b89b302870f7bf32fa1026cde..11315ebb0804713a320a751d923445bf512b2659 100644
--- a/tools/integration-tests/tester/tests/message/message_test.go
+++ b/tools/integration-tests/tester/tests/message/message_test.go
@@ -1,4 +1,4 @@
-package autopeering
+package message
 
 import (
 	"testing"
diff --git a/tools/integration-tests/tester/tests/testutil.go b/tools/integration-tests/tester/tests/testutil.go
index 31332cffee2dc8975dce16680afb3e3cbb3a7f4e..b47c5c12b1a612eb5696bd8695d5fb9b77d9591f 100644
--- a/tools/integration-tests/tester/tests/testutil.go
+++ b/tools/integration-tests/tester/tests/testutil.go
@@ -1,11 +1,8 @@
 package tests
 
 import (
-	"errors"
 	"fmt"
 	"math/rand"
-	"sync"
-	"sync/atomic"
 	"testing"
 	"time"
 
@@ -14,17 +11,12 @@ import (
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
-	"github.com/iotaledger/goshimmer/plugins/webapi/value/utils"
 	"github.com/iotaledger/goshimmer/tools/integration-tests/tester/framework"
-	"github.com/iotaledger/hive.go/types"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
-var (
-	ErrTransactionNotAvailableInTime = errors.New("transaction was not available in time")
-	ErrTransactionStateNotSameInTime = errors.New("transaction state did not materialize in time")
-)
+const maxRetry = 50
 
 // DataMessageSent defines a struct to identify from which issuer a data message was sent.
 type DataMessageSent struct {
@@ -62,7 +54,7 @@ func SendDataMessagesOnRandomPeer(t *testing.T, peers []*framework.Peer, numMess
 // SendDataMessage sends a data message on a given peer and returns the id and a DataMessageSent struct.
 func SendDataMessage(t *testing.T, peer *framework.Peer, data []byte, number int) (string, DataMessageSent) {
 	id, err := peer.Data(data)
-	require.NoErrorf(t, err, "Could not send message on %s", peer.String())
+	require.NoErrorf(t, err, "could not send message on %s", peer.String())
 
 	sent := DataMessageSent{
 		number: number,
@@ -110,14 +102,13 @@ func CheckForMessageIds(t *testing.T, peers []*framework.Peer, ids map[string]Da
 	}
 }
 
-// SendValueMessagesOnFaucet sends funds to peers from the faucet and returns the transaction ID.
-func SendValueMessagesOnFaucet(t *testing.T, peers []*framework.Peer) (txIds []string, addrBalance map[string]map[balance.Color]int64) {
+// SendTransactionFromFaucet sends funds to peers from the faucet, sends back the remainder to faucet, and returns the transaction ID.
+func SendTransactionFromFaucet(t *testing.T, peers []*framework.Peer, sentValue int64) (txIds []string, addrBalance map[string]map[balance.Color]int64) {
 	// initiate addrBalance map
 	addrBalance = make(map[string]map[balance.Color]int64)
 	for _, p := range peers {
 		addr := p.Seed().Address(0).String()
 		addrBalance[addr] = make(map[balance.Color]int64)
-		addrBalance[addr][balance.ColorIOTA] = 0
 	}
 
 	faucetPeer := peers[0]
@@ -125,29 +116,35 @@ func SendValueMessagesOnFaucet(t *testing.T, peers []*framework.Peer) (txIds []s
 
 	// get faucet balances
 	unspentOutputs, err := faucetPeer.GetUnspentOutputs([]string{faucetAddrStr})
-	require.NoErrorf(t, err, "Could not get unspent outputs on %s", faucetPeer.String())
+	require.NoErrorf(t, err, "could not get unspent outputs on %s", faucetPeer.String())
 	addrBalance[faucetAddrStr][balance.ColorIOTA] = unspentOutputs.UnspentOutputs[0].OutputIDs[0].Balances[0].Value
 
 	// send funds to other peers
 	for i := 1; i < len(peers); i++ {
-		fail, txId := SendIotaValueMessages(t, faucetPeer, peers[i], addrBalance)
+		fail, txId := SendIotaTransaction(t, faucetPeer, peers[i], addrBalance, sentValue)
 		require.False(t, fail)
 		txIds = append(txIds, txId)
 
 		// let the transaction propagate
-		time.Sleep(1 * time.Second)
+		time.Sleep(3 * time.Second)
 	}
+
 	return
 }
 
-// SendValueMessagesOnRandomPeer sends IOTA tokens on random peer and saves the sent message token to a map.
-func SendValueMessagesOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBalance map[string]map[balance.Color]int64, numMessages int) (txIds []string) {
+// SendTransactionOnRandomPeer sends sentValue amount of IOTA tokens from/to a random peer, mutates the given balance map and returns the transaction IDs.
+func SendTransactionOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBalance map[string]map[balance.Color]int64, numMessages int, sentValue int64) (txIds []string) {
+	counter := 0
 	for i := 0; i < numMessages; i++ {
 		from := rand.Intn(len(peers))
 		to := rand.Intn(len(peers))
-		fail, txId := SendIotaValueMessages(t, peers[from], peers[to], addrBalance)
+		fail, txId := SendIotaTransaction(t, peers[from], peers[to], addrBalance, sentValue)
 		if fail {
 			i--
+			counter++
+			if counter >= maxRetry {
+				return
+			}
 			continue
 		}
 
@@ -155,23 +152,22 @@ func SendValueMessagesOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBa
 		txIds = append(txIds, txId)
 
 		// let the transaction propagate
-		time.Sleep(1 * time.Second)
+		time.Sleep(3 * time.Second)
 	}
 
 	return
 }
 
-// SendIotaValueMessages sends IOTA token from and to a given peer and returns the transaction ID.
-// The same addresses are used in each round
-func SendIotaValueMessages(t *testing.T, from *framework.Peer, to *framework.Peer, addrBalance map[string]map[balance.Color]int64) (fail bool, txId string) {
-	var sentValue int64 = 100
+// SendIotaTransaction sends sentValue amount of IOTA tokens and remainders from and to a given peer and returns the fail flag and the transaction ID.
+// Every peer sends and receives the transaction on the address of index 0.
+func SendIotaTransaction(t *testing.T, from *framework.Peer, to *framework.Peer, addrBalance map[string]map[balance.Color]int64, sentValue int64) (fail bool, txId string) {
 	sigScheme := signaturescheme.ED25519(*from.Seed().KeyPair(0))
 	inputAddr := from.Seed().Address(0)
 	outputAddr := to.Seed().Address(0)
 
 	// prepare inputs
 	resp, err := from.GetUnspentOutputs([]string{inputAddr.String()})
-	require.NoErrorf(t, err, "Could not get unspent outputs on %s", from.String())
+	require.NoErrorf(t, err, "could not get unspent outputs on %s", from.String())
 
 	// abort if no unspent outputs
 	if len(resp.UnspentOutputs[0].OutputIDs) == 0 {
@@ -179,13 +175,13 @@ func SendIotaValueMessages(t *testing.T, from *framework.Peer, to *framework.Pee
 	}
 	availableValue := resp.UnspentOutputs[0].OutputIDs[0].Balances[0].Value
 
-	//abort if the balance is not enough
+	// abort if the balance is not enough
 	if availableValue < sentValue {
 		return true, ""
 	}
 
 	out, err := transaction.OutputIDFromBase58(resp.UnspentOutputs[0].OutputIDs[0].ID)
-	require.NoErrorf(t, err, "Invalid unspent outputs ID on %s", from.String())
+	require.NoErrorf(t, err, "invalid unspent outputs ID on %s", from.String())
 	inputs := transaction.NewInputs([]transaction.OutputID{out}...)
 
 	// prepare outputs
@@ -198,7 +194,7 @@ func SendIotaValueMessages(t *testing.T, from *framework.Peer, to *framework.Pee
 	outmap[outputAddr] = []*balance.Balance{balance.New(balance.ColorIOTA, sentValue)}
 	outputs := transaction.NewOutputs(outmap)
 
-	// handle remain address
+	// handle remainder address
 	if availableValue > sentValue {
 		outputs.Add(inputAddr, []*balance.Balance{balance.New(balance.ColorIOTA, availableValue-sentValue)})
 	}
@@ -208,7 +204,7 @@ func SendIotaValueMessages(t *testing.T, from *framework.Peer, to *framework.Pee
 
 	// send transaction
 	txId, err = from.SendTransaction(txn.Bytes())
-	require.NoErrorf(t, err, "Could not send transaction on %s", from.String())
+	require.NoErrorf(t, err, "could not send transaction on %s", from.String())
 
 	addrBalance[inputAddr.String()][balance.ColorIOTA] -= sentValue
 	addrBalance[outputAddr.String()][balance.ColorIOTA] += sentValue
@@ -216,14 +212,19 @@ func SendIotaValueMessages(t *testing.T, from *framework.Peer, to *framework.Pee
 	return false, txId
 }
 
-// SendColoredValueMessagesOnRandomPeer sends colored token on a random peer and saves the sent token to a map.
-func SendColoredValueMessagesOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBalance map[string]map[balance.Color]int64, numMessages int) (txIds []string) {
+// SendColoredTransactionOnRandomPeer sends colored tokens on a random peer, saves the sent token amount to a map, and returns transaction IDs.
+func SendColoredTransactionOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBalance map[string]map[balance.Color]int64, numMessages int) (txIds []string) {
+	counter := 0
 	for i := 0; i < numMessages; i++ {
 		from := rand.Intn(len(peers))
 		to := rand.Intn(len(peers))
-		fail, txId := SendColoredValueMessage(t, peers[from], peers[to], addrBalance)
+		fail, txId := SendColoredTransaction(t, peers[from], peers[to], addrBalance)
 		if fail {
 			i--
+			counter++
+			if counter >= maxRetry {
+				return
+			}
 			continue
 		}
 
@@ -231,90 +232,92 @@ func SendColoredValueMessagesOnRandomPeer(t *testing.T, peers []*framework.Peer,
 		txIds = append(txIds, txId)
 
 		// let the transaction propagate
-		time.Sleep(1 * time.Second)
+		time.Sleep(3 * time.Second)
 	}
 
 	return
 }
 
-// SendColoredValueMessage sends a colored tokens from and to a given peer and returns the transaction ID.
-// The same addresses are used in each round
-func SendColoredValueMessage(t *testing.T, from *framework.Peer, to *framework.Peer, addrBalance map[string]map[balance.Color]int64) (fail bool, txId string) {
+// SendColoredTransaction sends IOTA and colored tokens from and to a given peer and returns the fail flag and the transaction ID.
+// 1. Get the first unspent outputs of `from`
+// 2. Accumulate the token amount of the first unspent output
+// 3. Send 50 IOTA tokens + [accumalate token amount - 50] new minted tokens to `to`
+func SendColoredTransaction(t *testing.T, from *framework.Peer, to *framework.Peer, addrBalance map[string]map[balance.Color]int64) (fail bool, txId string) {
+	var sentValue int64 = 50
+	var balanceList []*balance.Balance
 	sigScheme := signaturescheme.ED25519(*from.Seed().KeyPair(0))
 	inputAddr := from.Seed().Address(0)
 	outputAddr := to.Seed().Address(0)
 
 	// prepare inputs
 	resp, err := from.GetUnspentOutputs([]string{inputAddr.String()})
-	require.NoErrorf(t, err, "Could not get unspent outputs on %s", from.String())
+	require.NoErrorf(t, err, "could not get unspent outputs on %s", from.String())
 
 	// abort if no unspent outputs
 	if len(resp.UnspentOutputs[0].OutputIDs) == 0 {
 		return true, ""
 	}
 
+	// calculate available token in the unspent output
+	var availableValue int64 = 0
+	for _, b := range resp.UnspentOutputs[0].OutputIDs[0].Balances {
+		availableValue += b.Value
+		balanceList = append(balanceList, balance.New(getColorFromString(b.Color), (-1)*b.Value))
+	}
+
+	// abort if not enough tokens
+	if availableValue < sentValue {
+		return true, ""
+	}
+
 	out, err := transaction.OutputIDFromBase58(resp.UnspentOutputs[0].OutputIDs[0].ID)
-	require.NoErrorf(t, err, "Invalid unspent outputs ID on %s", from.String())
+	require.NoErrorf(t, err, "invalid unspent outputs ID on %s", from.String())
 	inputs := transaction.NewInputs([]transaction.OutputID{out}...)
 
 	// prepare outputs
 	outmap := map[address.Address][]*balance.Balance{}
-	bs := []*balance.Balance{}
-	var outputs *transaction.Outputs
-	var availableIOTA int64
-	availableBalances := resp.UnspentOutputs[0].OutputIDs[0].Balances
-	newColor := false
 
 	// set balances
-	if len(availableBalances) > 1 {
-		// the balances contain more than one color, send it all
-		for _, b := range availableBalances {
-			value := b.Value
-			color := getColorFromString(b.Color)
-			bs = append(bs, balance.New(color, value))
-
-			// update balance list
-			addrBalance[inputAddr.String()][color] -= value
-			if _, ok := addrBalance[outputAddr.String()][color]; ok {
-				addrBalance[outputAddr.String()][color] += value
-			} else {
-				addrBalance[outputAddr.String()][color] = value
-			}
-		}
-	} else {
-		// create new colored token if inputs only contain IOTA
-		// half of availableIota tokens remain IOTA, else get recolored
-		newColor = true
-		availableIOTA = availableBalances[0].Value
-
-		bs = append(bs, balance.New(balance.ColorIOTA, availableIOTA/2))
-		bs = append(bs, balance.New(balance.ColorNew, availableIOTA/2))
-
-		// update balance list
-		addrBalance[inputAddr.String()][balance.ColorIOTA] -= availableIOTA
-		addrBalance[outputAddr.String()][balance.ColorIOTA] += availableIOTA / 2
+	outmap[outputAddr] = []*balance.Balance{balance.New(balance.ColorIOTA, sentValue)}
+	if availableValue > sentValue {
+		outmap[outputAddr] = append(outmap[outputAddr], balance.New(balance.ColorNew, availableValue-sentValue))
 	}
-	outmap[outputAddr] = bs
-
-	outputs = transaction.NewOutputs(outmap)
+	outputs := transaction.NewOutputs(outmap)
 
 	// sign transaction
 	txn := transaction.New(inputs, outputs).Sign(sigScheme)
 
 	// send transaction
 	txId, err = from.SendTransaction(txn.Bytes())
-	require.NoErrorf(t, err, "Could not send transaction on %s", from.String())
-
-	// FIXME: the new color should be txn ID
-	if newColor {
-		if _, ok := addrBalance[outputAddr.String()][balance.ColorNew]; ok {
-			addrBalance[outputAddr.String()][balance.ColorNew] += availableIOTA / 2
-		} else {
-			addrBalance[outputAddr.String()][balance.ColorNew] = availableIOTA / 2
+	require.NoErrorf(t, err, "could not send transaction on %s", from.String())
+
+	// update balance list
+	balanceList = append(balanceList, outmap[outputAddr]...)
+	updateBalanceList(addrBalance, balanceList, inputAddr.String(), outputAddr.String(), txId)
+
+	return false, txId
+}
+
+// updateBalanceList updates the token amount map with given peers and balances.
+// If the value of balance is negative, it is the balance to be deducted from peer from, else it is deposited to peer to.
+// If the color is balance.ColorNew, it should be recolored with txId.
+func updateBalanceList(addrBalance map[string]map[balance.Color]int64, balances []*balance.Balance, from, to, txId string) {
+	for _, b := range balances {
+		color := b.Color()
+		value := b.Value()
+		if value < 0 {
+			// deduct
+			addrBalance[from][color] += value
+			continue
 		}
-		//addrBalance[outputAddr.String()][getColorFromString(txId)] = availableIOTA / 2
+		// deposit
+		if color == balance.ColorNew {
+			addrBalance[to][getColorFromString(txId)] = value
+			continue
+		}
+		addrBalance[to][color] += value
 	}
-	return false, txId
+	return
 }
 
 func getColorFromString(colorStr string) (color balance.Color) {
@@ -336,15 +339,11 @@ func CheckBalances(t *testing.T, peers []*framework.Peer, addrBalance map[string
 			require.NoError(t, err)
 			assert.Equal(t, addr, resp.UnspentOutputs[0].Address)
 
-			// calculate the balances of each color coin
+			// calculate the balances of each colored coin
 			for _, unspents := range resp.UnspentOutputs[0].OutputIDs {
-				for _, b := range unspents.Balances {
-					color := getColorFromString(b.Color)
-					if _, ok := sum[color]; ok {
-						sum[color] += b.Value
-					} else {
-						sum[color] = b.Value
-					}
+				for _, respBalance := range unspents.Balances {
+					color := getColorFromString(respBalance.Color)
+					sum[color] += respBalance.Value
 				}
 			}
 
@@ -356,65 +355,8 @@ func CheckBalances(t *testing.T, peers []*framework.Peer, addrBalance map[string
 	}
 }
 
-// CheckAddressOutputsFullyConsumed performs checks to make sure that on all given peers,
-// the given addresses have no UTXOs.
-func CheckAddressOutputsFullyConsumed(t *testing.T, peers []*framework.Peer, addrs []string) {
-	for _, peer := range peers {
-		resp, err := peer.GetUnspentOutputs(addrs)
-		assert.NoError(t, err)
-		assert.Len(t, resp.Error, 0)
-		for i, utxos := range resp.UnspentOutputs {
-			assert.Len(t, utxos.OutputIDs, 0, "address %s should not have any UTXOs", addrs[i])
-		}
-	}
-}
-
-// ExpectedInclusionState is an expected inclusion state.
-// All fields are optional.
-type ExpectedInclusionState struct {
-	// The optional confirmed state to check against.
-	Confirmed *bool
-	// The optional finalized state to check against.
-	Finalized *bool
-	// The optional conflict state to check against.
-	Conflicting *bool
-	// The optional solid state to check against.
-	Solid *bool
-	// The optional rejected state to check against.
-	Rejected *bool
-	// The optional liked state to check against.
-	Liked *bool
-	// The optional preferred state to check against.
-	Preferred *bool
-}
-
-// True returns a pointer to a true bool.
-func True() *bool {
-	x := true
-	return &x
-}
-
-// False returns a pointer to a false bool.
-func False() *bool {
-	x := false
-	return &x
-}
-
-// ExpectedTransaction defines the expected data of a transaction.
-// All fields are optional.
-type ExpectedTransaction struct {
-	// The optional input IDs to check against.
-	Inputs *[]string
-	// The optional outputs to check against.
-	Outputs *[]utils.Output
-	// The optional signature to check against.
-	Signature *[]byte
-}
-
-// CheckTransactions performs checks to make sure that all peers have received all transactions.
-// Optionally takes an expected inclusion state for all supplied transaction IDs and expected transaction
-// data per transaction ID.
-func CheckTransactions(t *testing.T, peers []*framework.Peer, transactionIDs map[string]*ExpectedTransaction, checkSynchronized bool, expectedInclusionState ExpectedInclusionState) {
+// CheckTransactions performs checks to make sure that all peers have received all transactions .
+func CheckTransactions(t *testing.T, peers []*framework.Peer, transactionIDs []string, checkSynchronized bool) {
 	for _, peer := range peers {
 		if checkSynchronized {
 			// check that the peer sees itself as synchronized
@@ -423,145 +365,15 @@ func CheckTransactions(t *testing.T, peers []*framework.Peer, transactionIDs map
 			require.True(t, info.Synced)
 		}
 
-		for txId, expectedTransaction := range transactionIDs {
+		for _, txId := range transactionIDs {
 			resp, err := peer.GetTransactionByID(txId)
 			require.NoError(t, err)
 
 			// check inclusion state
-			if expectedInclusionState.Confirmed != nil {
-				assert.Equal(t, *expectedInclusionState.Confirmed, resp.InclusionState.Confirmed, "confirmed state doesn't match - %s", txId)
-			}
-			if expectedInclusionState.Conflicting != nil {
-				assert.Equal(t, *expectedInclusionState.Conflicting, resp.InclusionState.Conflicting, "conflict state doesn't match - %s", txId)
-			}
-			if expectedInclusionState.Solid != nil {
-				assert.Equal(t, *expectedInclusionState.Solid, resp.InclusionState.Solid, "solid state doesn't match - %s", txId)
-			}
-			if expectedInclusionState.Rejected != nil {
-				assert.Equal(t, *expectedInclusionState.Rejected, resp.InclusionState.Rejected, "rejected state doesn't match - %s", txId)
-			}
-			if expectedInclusionState.Liked != nil {
-				assert.Equal(t, *expectedInclusionState.Liked, resp.InclusionState.Liked, "liked state doesn't match - %s", txId)
-			}
-			if expectedInclusionState.Preferred != nil {
-				assert.Equal(t, *expectedInclusionState.Preferred, resp.InclusionState.Preferred, "preferred state doesn't match - %s", txId)
-			}
-
-			if expectedTransaction != nil {
-				if expectedTransaction.Inputs != nil {
-					assert.Equal(t, *expectedTransaction.Inputs, resp.Transaction.Inputs, "inputs do not match - %s", txId)
-				}
-				if expectedTransaction.Outputs != nil {
-					assert.Equal(t, *expectedTransaction.Outputs, resp.Transaction.Outputs, "outputs do not match - %s", txId)
-				}
-				if expectedTransaction.Signature != nil {
-					assert.Equal(t, *expectedTransaction.Signature, resp.Transaction.Signature, "signatures do not match - %s", txId)
-				}
-			}
-		}
-	}
-}
-
-// AwaitTransactionAvailability awaits until the given transaction IDs become available on all given peers or
-// the max duration is reached. Returns a map of missing transactions per peer. An error is returned if at least
-// one peer does not have all specified transactions available.
-func AwaitTransactionAvailability(peers []*framework.Peer, transactionIDs []string, maxAwait time.Duration) (missing map[string]map[string]types.Empty, err error) {
-	s := time.Now()
-	var missingMu sync.Mutex
-	missing = map[string]map[string]types.Empty{}
-	for ; time.Since(s) < maxAwait; time.Sleep(500 * time.Millisecond) {
-		var wg sync.WaitGroup
-		wg.Add(len(peers))
-		counter := int32(len(peers) * len(transactionIDs))
-		for _, p := range peers {
-			go func(p *framework.Peer) {
-				defer wg.Done()
-				for _, txID := range transactionIDs {
-					_, err := p.GetTransactionByID(txID)
-					if err == nil {
-						missingMu.Lock()
-						m, has := missing[p.ID().String()]
-						if has {
-							delete(m, txID)
-							if len(m) == 0 {
-								delete(missing, p.ID().String())
-							}
-						}
-						missingMu.Unlock()
-						atomic.AddInt32(&counter, -1)
-						continue
-					}
-					missingMu.Lock()
-					m, has := missing[p.ID().String()]
-					if !has {
-						m = map[string]types.Empty{}
-					}
-					m[txID] = types.Empty{}
-					missing[p.ID().String()] = m
-					missingMu.Unlock()
-				}
-			}(p)
-		}
-		wg.Wait()
-		if counter == 0 {
-			// everything available
-			return missing, nil
-		}
-	}
-	return missing, ErrTransactionNotAvailableInTime
-}
-
-// AwaitTransactionInclusionState awaits on all given peers until the specified transactions
-// have the expected state or max duration is reached. This function does not gracefully
-// handle the transactions not existing on the given peers, therefore it must be ensured
-// the the transactions exist beforehand.
-func AwaitTransactionInclusionState(peers []*framework.Peer, transactionIDs map[string]ExpectedInclusionState, maxAwait time.Duration) error {
-	s := time.Now()
-	for ; time.Since(s) < maxAwait; time.Sleep(1 * time.Second) {
-		var wg sync.WaitGroup
-		wg.Add(len(peers))
-		counter := int32(len(peers) * len(transactionIDs))
-		for _, p := range peers {
-			go func(p *framework.Peer) {
-				defer wg.Done()
-				for txID := range transactionIDs {
-					tx, err := p.GetTransactionByID(txID)
-					if err != nil {
-						continue
-					}
-					expInclState := transactionIDs[txID]
-					if expInclState.Confirmed != nil && *expInclState.Confirmed != tx.InclusionState.Confirmed {
-						continue
-					}
-					if expInclState.Conflicting != nil && *expInclState.Conflicting != tx.InclusionState.Conflicting {
-						continue
-					}
-					if expInclState.Finalized != nil && *expInclState.Finalized != tx.InclusionState.Finalized {
-						continue
-					}
-					if expInclState.Liked != nil && *expInclState.Liked != tx.InclusionState.Liked {
-						continue
-					}
-					if expInclState.Preferred != nil && *expInclState.Preferred != tx.InclusionState.Preferred {
-						continue
-					}
-					if expInclState.Rejected != nil && *expInclState.Rejected != tx.InclusionState.Rejected {
-						continue
-					}
-					if expInclState.Solid != nil && *expInclState.Solid != tx.InclusionState.Solid {
-						continue
-					}
-					atomic.AddInt32(&counter, -1)
-				}
-			}(p)
-		}
-		wg.Wait()
-		if counter == 0 {
-			// everything available
-			return nil
+			assert.True(t, resp.InclusionState.Confirmed)
+			assert.False(t, resp.InclusionState.Rejected)
 		}
 	}
-	return ErrTransactionStateNotSameInTime
 }
 
 // ShutdownNetwork shuts down the network and reports errors.
diff --git a/tools/integration-tests/tester/tests/value/main_test.go b/tools/integration-tests/tester/tests/value/main_test.go
index bcc09c2e53590df061cbff5a2159071d567440f1..f5d00e21e7f402c843a19a25175355c7cead2ab2 100644
--- a/tools/integration-tests/tester/tests/value/main_test.go
+++ b/tools/integration-tests/tester/tests/value/main_test.go
@@ -1,4 +1,4 @@
-package autopeering
+package value
 
 import (
 	"os"
diff --git a/tools/integration-tests/tester/tests/value/value_test.go b/tools/integration-tests/tester/tests/value/value_test.go
index f0cb8109be17c0bea4ffaa1959042cd902070f43..a5fd1ea0492ed3a877956631ee7be8cc80fc06be 100644
--- a/tools/integration-tests/tester/tests/value/value_test.go
+++ b/tools/integration-tests/tester/tests/value/value_test.go
@@ -1,53 +1,44 @@
-package autopeering
+package value
 
 import (
 	"testing"
 	"time"
 
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers"
 	"github.com/iotaledger/goshimmer/tools/integration-tests/tester/tests"
 	"github.com/stretchr/testify/require"
 )
 
-// TestValueIotaPersistence issues messages on random peers, restarts them and checks for persistence after restart.
-func TestValueIotaPersistence(t *testing.T) {
-	n, err := f.CreateNetwork("valueIota_TestPersistence", 4, 2)
+// TestTransactionPersistence issues messages on random peers, restarts them and checks for persistence after restart.
+func TestTransactionPersistence(t *testing.T) {
+	n, err := f.CreateNetwork("transaction_TestPersistence", 4, 2)
 	require.NoError(t, err)
 	defer tests.ShutdownNetwork(t, n)
 
 	// wait for peers to change their state to synchronized
 	time.Sleep(5 * time.Second)
 
-	// master node sends funds to all peers in the network
-	txIdsSlice, addrBalance := tests.SendValueMessagesOnFaucet(t, n.Peers())
-	txIds := make(map[string]*tests.ExpectedTransaction)
-	for _, txID := range txIdsSlice {
-		txIds[txID] = nil
-	}
+	// faucet node sends 100 IOTA tokens to all peers in the network
+	txIds, addrBalance := tests.SendTransactionFromFaucet(t, n.Peers(), 100)
 
 	// wait for messages to be gossiped
-	time.Sleep(10 * time.Second)
+	time.Sleep(2 * valuetransfers.AverageNetworkDelay)
 
 	// check whether the first issued transaction is available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
-		Confirmed: tests.True(),
-	})
+	tests.CheckTransactions(t, n.Peers(), txIds, true)
 
 	// check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)
 
 	// send value message randomly
-	randomTxIds := tests.SendValueMessagesOnRandomPeer(t, n.Peers(), addrBalance, 10)
-	for _, randomTxId := range randomTxIds {
-		txIds[randomTxId] = nil
-	}
+	randomTxIds := tests.SendTransactionOnRandomPeer(t, n.Peers(), addrBalance, 10, 100)
+	txIds = append(txIds, randomTxIds...)
 
 	// wait for messages to be gossiped
-	time.Sleep(10 * time.Second)
+	time.Sleep(2 * valuetransfers.AverageNetworkDelay)
 
-	// check whether all issued transactions are persistently available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
-		Confirmed: tests.True(),
-	})
+	// check whether all issued transactions are available on all nodes and confirmed
+	tests.CheckTransactions(t, n.Peers(), txIds, true)
 
 	// check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)
@@ -65,12 +56,10 @@ func TestValueIotaPersistence(t *testing.T) {
 	}
 
 	// wait for peers to start
-	time.Sleep(10 * time.Second)
+	time.Sleep(20 * time.Second)
 
-	// check whether all issued transactions are persistently available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
-		Confirmed: tests.True(),
-	})
+	// check whether all issued transactions are available on all nodes and confirmed
+	tests.CheckTransactions(t, n.Peers(), txIds, true)
 
 	// 5. check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)
@@ -86,36 +75,26 @@ func TestValueColoredPersistence(t *testing.T) {
 	time.Sleep(5 * time.Second)
 
 	// master node sends funds to all peers in the network
-	txIdsSlice, addrBalance := tests.SendValueMessagesOnFaucet(t, n.Peers())
-	txIds := make(map[string]*tests.ExpectedTransaction)
-	for _, txID := range txIdsSlice {
-		txIds[txID] = nil
-	}
+	txIds, addrBalance := tests.SendTransactionFromFaucet(t, n.Peers(), 100)
 
 	// wait for messages to be gossiped
-	time.Sleep(10 * time.Second)
+	time.Sleep(2 * valuetransfers.AverageNetworkDelay)
 
 	// check whether the transactions are available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
-		Confirmed: tests.True(),
-	})
+	tests.CheckTransactions(t, n.Peers(), txIds, true)
 
 	// check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)
 
 	// send funds around
-	randomTxIds := tests.SendColoredValueMessagesOnRandomPeer(t, n.Peers(), addrBalance, 10)
-	for _, randomTxId := range randomTxIds {
-		txIds[randomTxId] = nil
-	}
+	randomTxIds := tests.SendColoredTransactionOnRandomPeer(t, n.Peers(), addrBalance, 10)
+	txIds = append(txIds, randomTxIds...)
 
 	// wait for value messages to be gossiped
-	time.Sleep(10 * time.Second)
+	time.Sleep(2 * valuetransfers.AverageNetworkDelay)
 
 	// check whether all issued transactions are persistently available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
-		Confirmed: tests.True(),
-	})
+	tests.CheckTransactions(t, n.Peers(), txIds, true)
 
 	// check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)
@@ -133,12 +112,10 @@ func TestValueColoredPersistence(t *testing.T) {
 	}
 
 	// wait for peers to start
-	time.Sleep(10 * time.Second)
+	time.Sleep(20 * time.Second)
 
 	// check whether all issued transactions are persistently available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
-		Confirmed: tests.True(),
-	})
+	tests.CheckTransactions(t, n.Peers(), txIds, true)
 
 	// 5. check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)