diff --git a/dapps/valuetransfers/packages/tipmanager/tipmanager.go b/dapps/valuetransfers/packages/tipmanager/tipmanager.go
index 52d9bedfed21b3d9b22da91c89589f07561dfd4a..6f48262a6f856d00d5e6c781a24d028c71212af7 100644
--- a/dapps/valuetransfers/packages/tipmanager/tipmanager.go
+++ b/dapps/valuetransfers/packages/tipmanager/tipmanager.go
@@ -67,10 +67,16 @@ func newBranch(branchID branchmanager.BranchID, liked bool) *Branch {
 	}
 }
 
+// LikableBrancher defines an interface for a likable branch.
+type LikableBrancher interface {
+	ID() branchmanager.BranchID
+	Liked() bool
+}
+
 // AddTip adds the given value object as a tip in the given branch.
 // If the branch is liked it is also added to t.tips.
 // Parents are handled depending on the relation (same or different branch).
-func (t *TipManager) AddTip(valueObject *payload.Payload, b *branchmanager.Branch) {
+func (t *TipManager) AddTip(valueObject *payload.Payload, b LikableBrancher) {
 	t.mutex.Lock()
 	defer t.mutex.Unlock()
 
@@ -115,17 +121,17 @@ func (t *TipManager) Tips() (parent1ObjectID, parent2ObjectID payload.ID) {
 		return
 	}
 
-	parent1ObjectID = tip.(Tip).id
+	parent1ObjectID = tip.(*Tip).id
 
 	if t.tips.Size() == 1 {
 		parent2ObjectID = parent1ObjectID
 		return
 	}
 
-	parent2ObjectID = t.tips.RandomEntry().(Tip).id
+	parent2ObjectID = t.tips.RandomEntry().(*Tip).id
 	// select 2 distinct tips if there's more than 1 tip available
 	for parent1ObjectID == parent2ObjectID && t.tips.Size() > 1 {
-		parent2ObjectID = t.tips.RandomEntry().(Tip).id
+		parent2ObjectID = t.tips.RandomEntry().(*Tip).id
 	}
 
 	return
@@ -155,7 +161,7 @@ func (t *TipManager) OnBranchLiked(branchID branchmanager.BranchID) {
 	}
 
 	// remove tips from referenced branches
-	for objectID, _ := range branch.entryPoints {
+	for objectID := range branch.entryPoints {
 		t.tips.Delete(objectID)
 	}
 }
@@ -180,7 +186,7 @@ func (t *TipManager) OnBranchDisliked(branchID branchmanager.BranchID) {
 	}
 }
 
-// OnBranchDisliked is called when a branch is merged.
+// OnBranchMerged is called when a branch is merged.
 // TODO: it should perform some cleanup logic
 func (t *TipManager) OnBranchMerged(branchID branchmanager.BranchID) {
 	// remove all tips from t.tips
@@ -198,7 +204,11 @@ func removeTipAsEntryPoint(tip *Tip) {
 // If the parent is in the same branch it is removed as a tip.
 // If the parent is not in the same branch a two-way mapping from parent to branch and branch to parent is created.
 func addTipHandleParent(parentID payload.ID, parentBranch *Branch, branch *Branch) {
-	parentTip := parentBranch.tips[parentID]
+	parentTip, ok := parentBranch.tips[parentID]
+	// parent is not a tip anymore
+	if !ok {
+		return
+	}
 
 	if parentBranch.branchID == branch.branchID {
 		// remove parents out of branch.tips
diff --git a/dapps/valuetransfers/packages/tipmanager/tipmanager_test.go b/dapps/valuetransfers/packages/tipmanager/tipmanager_test.go
index 1bf532d703a37ea7f2072389dc9ff83c8c56c146..08a7d463e211e1ed1a0a8f7afff8c00a8c56cc71 100644
--- a/dapps/valuetransfers/packages/tipmanager/tipmanager_test.go
+++ b/dapps/valuetransfers/packages/tipmanager/tipmanager_test.go
@@ -1,12 +1,17 @@
 package tipmanager
 
 import (
+	"sync"
+	"testing"
+
 	"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/stretchr/testify/assert"
 )
 
-/*
 func TestTipManagerSimple(t *testing.T) {
 	tipManager := New()
 
@@ -15,74 +20,193 @@ func TestTipManagerSimple(t *testing.T) {
 	assert.Equal(t, payload.GenesisID, parent1)
 	assert.Equal(t, payload.GenesisID, parent2)
 
-	// add few tips and check whether total tips count matches
-	totalTips := 100
-	branchesMap := make(map[branchmanager.BranchID]*payload.Payload, totalTips)
-	branches := make([]branchmanager.BranchID, totalTips)
-	for i := 0; i < totalTips; i++ {
-		branchID := branchmanager.NewBranchID(transaction.RandomID())
-		branches[i] = branchID
-		branchesMap[branchID] = payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
-		tipManager.AddTip(branchesMap[branchID], branchID)
-	}
-	assert.Equal(t, totalTips, tipManager.TipCount(branches...))
+	// add 1 transaction in liked branch -> both tips need to point there
+	tx := createDummyTransaction()
+	branch1 := newMockBranch(tx.ID(), true)
+	valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx)
+	branch1.addTip(valueObject)
+	tipManager.AddTip(valueObject, branch1)
 
-	// check if tips point to genesis when branches are not found
-	parent1, parent2 = tipManager.Tips(branchmanager.UndefinedBranchID, branchmanager.NewBranchID(transaction.RandomID()))
-	assert.Equal(t, 0, tipManager.TipCount(branchmanager.UndefinedBranchID, branchmanager.NewBranchID(transaction.RandomID())))
-	assert.Equal(t, payload.GenesisID, parent1)
-	assert.Equal(t, payload.GenesisID, parent2)
+	assert.Equal(t, 1, tipManager.TipCount())
+	parent1, parent2 = tipManager.Tips()
+	assert.Equal(t, valueObject.ID(), parent1)
+	assert.Equal(t, valueObject.ID(), parent2)
 
-	// use each branch to get the single tip
-	for b, o := range branchesMap {
-		parent1ObjectID, parent2ObjectID := tipManager.Tips(b)
-		assert.Equal(t, o.ID(), parent1ObjectID)
-		assert.Equal(t, o.ID(), parent2ObjectID)
-	}
+	// add a bunch of liked tips -> check count
+	numTips := 10
+	branch2 := newMockBranch(transaction.RandomID(), true)
+	for i := 0; i < numTips; i++ {
+		v := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
+		branch1.addTip(v)
+		tipManager.AddTip(v, branch1)
 
-	// select tips from two branches
-	parent1, parent2 = tipManager.Tips(branches[0], branches[1])
-	s := []payload.ID{
-		branchesMap[branches[0]].ID(),
-		branchesMap[branches[1]].ID(),
+		v = payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
+		branch2.addTip(v)
+		tipManager.AddTip(v, branch2)
 	}
-	assert.Contains(t, s, parent1)
-	assert.Contains(t, s, parent2)
-}
 
-func TestTipManagerParallel(t *testing.T) {
-	totalTips := 1000
-	totalThreads := 100
-	totalBranches := 10
+	assert.Equal(t, branch1.tipCount()+branch2.tipCount(), tipManager.TipCount())
+	assert.Equal(t, branch1.tipCount(), len(tipManager.branches[branch1.ID()].tips))
+	assert.Equal(t, branch2.tipCount(), len(tipManager.branches[branch2.ID()].tips))
+	parent1, parent2 = tipManager.Tips()
+	assert.NotEqual(t, parent1, parent2)
 
-	tipManager := New()
+	// add a bunch of disliked tips -> count should be unchanged
+	branch3 := newMockBranch(transaction.RandomID(), false)
+	branch4 := newMockBranch(transaction.RandomID(), false)
+	for i := 0; i < numTips; i++ {
+		v := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
+		branch3.addTip(v)
+		tipManager.AddTip(v, branch3)
 
-	branches := make([]branchmanager.BranchID, totalBranches)
-	for i := 0; i < totalBranches; i++ {
-		branchID := branchmanager.NewBranchID(transaction.RandomID())
-		branches[i] = branchID
+		v = payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
+		branch4.addTip(v)
+		tipManager.AddTip(v, branch4)
 	}
 
-	// add tips in parallel
+	assert.Equal(t, branch1.tipCount()+branch2.tipCount(), tipManager.TipCount())
+	assert.Equal(t, branch1.tipCount(), len(tipManager.branches[branch1.ID()].tips))
+	assert.Equal(t, branch2.tipCount(), len(tipManager.branches[branch2.ID()].tips))
+	assert.Equal(t, branch3.tipCount(), len(tipManager.branches[branch3.ID()].tips))
+	assert.Equal(t, branch4.tipCount(), len(tipManager.branches[branch4.ID()].tips))
+
+	// add tip to liked branch with parents from same branch -> should be in tipmanager.tips and parents removed from tipmanager.tips and branch's tips
+	removedTip1 := branch1.tip()
+	removedTip2 := branch1.tip()
+	valueObject = payload.New(removedTip1, removedTip2, createDummyTransaction())
+	branch1.addTip(valueObject)
+	tipManager.AddTip(valueObject, branch1)
+
+	assert.Equal(t, branch1.tipCount()+branch2.tipCount(), tipManager.TipCount())
+	_, ok := tipManager.tips.Get(removedTip1)
+	assert.False(t, ok)
+	_, ok = tipManager.tips.Get(removedTip2)
+	assert.False(t, ok)
+	_, ok = tipManager.tips.Get(valueObject.ID())
+	assert.True(t, ok)
+
+	_, ok = tipManager.branches[branch1.ID()].tips[removedTip1]
+	assert.False(t, ok)
+	_, ok = tipManager.branches[branch1.ID()].tips[removedTip2]
+	assert.False(t, ok)
+	_, ok = tipManager.branches[branch1.ID()].tips[valueObject.ID()]
+	assert.True(t, ok)
+
+	// add tip to disliked branch with parents from same branch -> tipmanager.tips should be unmodified, only branch's tips modified
+	removedTip1 = branch3.tip()
+	removedTip2 = branch3.tip()
+	valueObject = payload.New(removedTip1, removedTip2, createDummyTransaction())
+	branch3.addTip(valueObject)
+	tipManager.AddTip(valueObject, branch3)
+
+	assert.Equal(t, branch1.tipCount()+branch2.tipCount(), tipManager.TipCount())
+	_, ok = tipManager.branches[branch3.ID()].tips[removedTip1]
+	assert.False(t, ok)
+	_, ok = tipManager.branches[branch3.ID()].tips[removedTip2]
+	assert.False(t, ok)
+	_, ok = tipManager.branches[branch3.ID()].tips[valueObject.ID()]
+	assert.True(t, ok)
+}
+
+// TestTipManagerParallel tests the TipManager's functionality by adding and selecting tips concurrently.
+func TestTipManagerConcurrent(t *testing.T) {
+	numThreads := 10
+	numTips := 100
+	branches := make([]*mockBranch, numThreads)
+
+	tipManager := New()
 	var wg sync.WaitGroup
-	for i := 0; i < totalThreads; i++ {
+
+	// add tips to numThreads branches in parallel
+	for i := 0; i < numThreads; i++ {
 		wg.Add(1)
-		go func() {
+		go func(number int) {
 			defer wg.Done()
 
-			for t := 0; t < totalTips; t++ {
-				tip := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
-				random := rand.Intn(totalBranches)
-				tipManager.AddTip(tip, branches[random])
+			branch := newMockBranch(transaction.RandomID(), number < numThreads/2)
+			branches[number] = branch
+
+			// add tips within a branch in parallel
+			var wg2 sync.WaitGroup
+			for t := 0; t < numTips; t++ {
+				v := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction())
+				branch.addTip(v)
+				wg2.Add(1)
+				go func() {
+					defer wg2.Done()
+					tipManager.AddTip(v, branch)
+				}()
 			}
-		}()
+			wg2.Wait()
+
+			removedTip1 := branch.tip()
+			removedTip2 := branch.tip()
+			valueObject := payload.New(removedTip1, removedTip2, createDummyTransaction())
+			branch.addTip(valueObject)
+			tipManager.AddTip(valueObject, branch)
+		}(i)
 	}
 	wg.Wait()
 
-	// check total tip count
-	assert.Equal(t, totalTips*totalThreads, tipManager.TipCount(branches...))
+	// verify tips of every branch
+	for i := 0; i < numThreads; i++ {
+		branch := branches[i]
+		assert.Equal(t, branch.tipCount(), len(tipManager.branches[branch.ID()].tips))
+
+		for tip := range branch.tips {
+			// all tips should be in branch's list
+			_, ok := tipManager.branches[branch.ID()].tips[tip]
+			assert.True(t, ok)
+			// make sure that all tips from liked branches (and none from disliked branches) are in tipmanager.tips
+			_, ok = tipManager.tips.Get(tip)
+			assert.Equal(t, branch.Liked(), ok)
+		}
+	}
+
+	// total tips: numThreads/2 (liked branches) * numTips - numThreads/2 (selected tips)
+	assert.Equal(t, numThreads/2*numTips-numThreads/2, tipManager.TipCount())
+}
+
+type mockBranch struct {
+	id    branchmanager.BranchID
+	liked bool
+	tips  map[payload.ID]payload.ID
+}
+
+func newMockBranch(txID transaction.ID, liked bool) *mockBranch {
+	return &mockBranch{
+		id:    branchmanager.NewBranchID(txID),
+		liked: liked,
+		tips:  make(map[payload.ID]payload.ID),
+	}
+}
+
+func (m *mockBranch) ID() branchmanager.BranchID {
+	return m.id
+}
+func (m *mockBranch) Liked() bool {
+	return m.liked
+}
+
+func (m *mockBranch) addTip(valueObject *payload.Payload) {
+	m.tips[valueObject.ID()] = valueObject.ID()
+}
+
+func (m *mockBranch) tip() payload.ID {
+	var tip payload.ID
+
+	// get some element
+	for t := range m.tips {
+		tip = t
+	}
+	delete(m.tips, tip)
+
+	return tip
+}
+
+func (m *mockBranch) tipCount() int {
+	return len(m.tips)
 }
-*/
 
 func createDummyTransaction() *transaction.Transaction {
 	return transaction.New(