diff --git a/dapps/valuetransfers/packages/tangle/tangle.go b/dapps/valuetransfers/packages/tangle/tangle.go
index 71c3dbb6db8992fbad9fd3370d9f0c9c0a0d46e5..104194ff524c394dfd7bf17933fb5b06414d2eba 100644
--- a/dapps/valuetransfers/packages/tangle/tangle.go
+++ b/dapps/valuetransfers/packages/tangle/tangle.go
@@ -167,6 +167,7 @@ func (tangle *Tangle) BranchManager() *branchmanager.BranchManager {
 
 // LoadSnapshot creates a set of outputs in the value tangle, that are forming the genesis for future transactions.
 func (tangle *Tangle) LoadSnapshot(snapshot Snapshot) {
+	// TODO: snapshot should also reflect the consumers of transactions
 	for transactionID, addressBalances := range snapshot {
 		for outputAddress, balances := range addressBalances {
 			input := NewOutput(outputAddress, transactionID, branchmanager.MasterBranchID, balances)
diff --git a/plugins/webapi/value/gettransactionbyid/handler.go b/plugins/webapi/value/gettransactionbyid/handler.go
index ba4d9248bf24eac9cdca043f1c6da2bd2aa3d8c4..3bd214df64635497b423d0ee0319f303e5987ef3 100644
--- a/plugins/webapi/value/gettransactionbyid/handler.go
+++ b/plugins/webapi/value/gettransactionbyid/handler.go
@@ -19,20 +19,28 @@ func Handler(c echo.Context) error {
 	}
 
 	// get txn by txn id
-	txnObj := valuetransfers.Tangle.Transaction(txnID)
-	defer txnObj.Release()
-	if !txnObj.Exists() {
+	cachedTxnMetaObj := valuetransfers.Tangle.TransactionMetadata(txnID)
+	defer cachedTxnMetaObj.Release()
+	if !cachedTxnMetaObj.Exists() {
 		return c.JSON(http.StatusNotFound, Response{Error: "Transaction not found"})
 	}
-	txn := utils.ParseTransaction(txnObj.Unwrap())
+	cachedTxnObj := valuetransfers.Tangle.Transaction(txnID)
+	defer cachedTxnObj.Release()
+	if !cachedTxnObj.Exists() {
+		return c.JSON(http.StatusNotFound, Response{Error: "Transaction not found"})
+	}
+	txn := utils.ParseTransaction(cachedTxnObj.Unwrap())
 
-	// TODO: get inclusion state
+	txnMeta := cachedTxnMetaObj.Unwrap()
 	return c.JSON(http.StatusOK, Response{
 		Transaction: txn,
 		InclusionState: utils.InclusionState{
-			Confirmed: true,
-			Conflict:  false,
-			Liked:     true,
+			Confirmed: txnMeta.Confirmed(),
+			Conflict:  txnMeta.Conflicting(),
+			Liked:     txnMeta.Liked(),
+			Solid:     txnMeta.Solid(),
+			Rejected:  txnMeta.Rejected(),
+			Finalized: txnMeta.Finalized(),
 		},
 	})
 }
diff --git a/plugins/webapi/value/unspentoutputs/handler.go b/plugins/webapi/value/unspentoutputs/handler.go
index acb5f5b83ff7f39b2cc176b8d76d792834bfde31..7b77c21544d9521d07317ccc090f99016215429c 100644
--- a/plugins/webapi/value/unspentoutputs/handler.go
+++ b/plugins/webapi/value/unspentoutputs/handler.go
@@ -28,9 +28,13 @@ func Handler(c echo.Context) error {
 
 		outputids := make([]OutputID, 0)
 		// get outputids by address
-		for id, outputObj := range valuetransfers.Tangle.OutputsOnAddress(address) {
-			defer outputObj.Release()
-			output := outputObj.Unwrap()
+		for id, cachedOutput := range valuetransfers.Tangle.OutputsOnAddress(address) {
+			// TODO: don't do this in a for
+			defer cachedOutput.Release()
+			output := cachedOutput.Unwrap()
+			cachedTxMeta := valuetransfers.Tangle.TransactionMetadata(output.TransactionID())
+			// TODO: don't do this in a for
+			defer cachedTxMeta.Release()
 
 			// TODO: get inclusion state
 			if output.ConsumerCount() == 0 {
@@ -43,14 +47,20 @@ func Handler(c echo.Context) error {
 					})
 				}
 
+				inclusionState := utils.InclusionState{}
+				if cachedTxMeta.Exists() {
+					txMeta := cachedTxMeta.Unwrap()
+					inclusionState.Confirmed = txMeta.Confirmed()
+					inclusionState.Liked = txMeta.Liked()
+					inclusionState.Rejected = txMeta.Rejected()
+					inclusionState.Finalized = txMeta.Finalized()
+					inclusionState.Conflict = txMeta.Conflicting()
+					inclusionState.Confirmed = txMeta.Confirmed()
+				}
 				outputids = append(outputids, OutputID{
-					ID:       id.String(),
-					Balances: b,
-					InclusionState: utils.InclusionState{
-						Confirmed: true,
-						Conflict:  false,
-						Liked:     true,
-					},
+					ID:             id.String(),
+					Balances:       b,
+					InclusionState: inclusionState,
 				})
 			}
 		}
diff --git a/plugins/webapi/value/utils/transaction_handler.go b/plugins/webapi/value/utils/transaction_handler.go
index 15a587ead8c24e2233a4e6b78249f56146c94fb8..26acd8d9e25001a6428128348d3ae4cd84ec3b12 100644
--- a/plugins/webapi/value/utils/transaction_handler.go
+++ b/plugins/webapi/value/utils/transaction_handler.go
@@ -67,4 +67,7 @@ type InclusionState struct {
 	Confirmed bool `json:"confirmed,omitempty"`
 	Conflict  bool `json:"conflict,omitempty"`
 	Liked     bool `json:"liked,omitempty"`
+	Solid     bool `json:"solid,omitempty"`
+	Rejected  bool `json:"rejected,omitempty"`
+	Finalized bool `json:"finalized,omitempty"`
 }
diff --git a/tools/integration-tests/assets/entrypoint.sh b/tools/integration-tests/assets/entrypoint.sh
index a5ec0407b81c57c162ead53b0494c224f3c76ae7..7e5f060f00c41ae1b7f58e452fddc87a7909221d 100644
--- a/tools/integration-tests/assets/entrypoint.sh
+++ b/tools/integration-tests/assets/entrypoint.sh
@@ -6,4 +6,4 @@ chmod 777 /assets/*
 echo "assets:"
 ls /assets
 echo "running tests..."
-go test ./tests/"${TEST_NAME}" -v -timeout 30m
+go test ./tests/"${TEST_NAME}" -run TestConsensusConflicts -v -timeout 30m
diff --git a/tools/integration-tests/tester/framework/framework.go b/tools/integration-tests/tester/framework/framework.go
index d4456d5740694f9ffb31fbe9f01d2f73715cb871..4c19cc4ac861bf931af96574c19e1b94b2ee70dc 100644
--- a/tools/integration-tests/tester/framework/framework.go
+++ b/tools/integration-tests/tester/framework/framework.go
@@ -80,7 +80,7 @@ func (f *Framework) CreateNetwork(name string, peers int, minimumNeighbors int,
 	// create peers/GoShimmer nodes
 	for i := 0; i < peers; i++ {
 		config := GoShimmerConfig{
-			Bootstrap:                             i == 0,
+			Bootstrap:                             true,
 			BootstrapInitialIssuanceTimePeriodSec: bootstrapInitialIssuanceTimePeriodSec,
 			Faucet:                                i == 0,
 		}
@@ -128,9 +128,7 @@ func (f *Framework) CreateNetworkWithPartitions(name string, peers, partitions,
 
 	// create peers/GoShimmer nodes
 	for i := 0; i < peers; i++ {
-		config := GoShimmerConfig{
-			Bootstrap: i == 0,
-		}
+		config := GoShimmerConfig{Bootstrap: true}
 		if _, err = network.CreatePeer(config); err != nil {
 			return nil, err
 		}
diff --git a/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go b/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..1c22d2e5b46859943efd4645f57c2fb5ceebae94
--- /dev/null
+++ b/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go
@@ -0,0 +1,103 @@
+package consensus
+
+import (
+	"log"
+	"testing"
+	"time"
+
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
+	"github.com/iotaledger/goshimmer/plugins/webapi/value/utils"
+	"github.com/iotaledger/goshimmer/tools/integration-tests/tester/tests"
+	"github.com/mr-tron/base58/base58"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+// TestConsensusConflicts issues valid conflicting value objects and makes sure that
+// the conflicts are resolved via FPC.
+func TestConsensusConflicts(t *testing.T) {
+	n, err := f.CreateNetworkWithPartitions("consensus_TestConsensusConflicts", 8, 2, 2)
+	require.NoError(t, err)
+	defer tests.ShutdownNetwork(t, n)
+
+	time.Sleep(10 * time.Second)
+
+	// split the network
+	//assert.NoError(t, n.Split(n.Peers()[:len(n.Peers())/2], n.Peers()[len(n.Peers())/2:]))
+
+	// genesis wallet
+	genesisSeedBytes, err := base58.Decode("7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih")
+	require.NoError(t, err, "couldn't decode genesis seed from base58 seed")
+
+	const genesisBalance = 1000000000
+	genesisWallet := wallet.New(genesisSeedBytes)
+	genesisAddr := genesisWallet.Seed().Address(0)
+	genesisOutputID := transaction.NewOutputID(genesisAddr, transaction.GenesisID)
+
+	// issue transactions which spend the same genesis output in all partitions
+	conflictingTxs := make([]*transaction.Transaction, len(n.Partitions()))
+	conflictingTxIDs := make([]string, len(n.Partitions()))
+	receiverWallets := make([]*wallet.Wallet, len(n.Partitions()))
+	for i, partition := range n.Partitions() {
+		partitionReceiverWallet := wallet.New()
+		destAddr := partitionReceiverWallet.Seed().Address(0)
+		receiverWallets[i] = partitionReceiverWallet
+		tx := transaction.New(
+			transaction.NewInputs(genesisOutputID),
+			transaction.NewOutputs(map[address.Address][]*balance.Balance{
+				destAddr: {
+					{Value: genesisBalance / 2, Color: balance.ColorIOTA},
+				},
+			}))
+		tx = tx.Sign(signaturescheme.ED25519(*genesisWallet.Seed().KeyPair(0)))
+		conflictingTxs[i] = tx
+		conflictingTxIDs[i] = tx.ID().String()
+		log.Println("issuing conflict transaction on partition", i, tx.ID().String())
+		_, err := partition.Peers()[0].SendTransaction(tx.Bytes())
+		require.NoError(t, err)
+	}
+
+	// sleep the avg. network delay so both partitions prefer their own first seen transaction
+	time.Sleep(valuetransfers.AverageNetworkDelay)
+
+	// merge back the partitions
+	log.Println("merging partitions...")
+	assert.NoError(t, n.DeletePartitions(), "merging the network partitions should work")
+	log.Println("waiting for resolved partitions to autopeer to each other")
+	err = n.WaitForAutopeering(5)
+	require.NoError(t, err)
+
+	// ensure message flow so that both partitions will get the conflicting tx
+	for _, p := range n.Peers() {
+		tests.SendDataMessage(t, p, []byte("DATA"), 10)
+	}
+
+	log.Println("waiting for transactions to be available on all peers...")
+	err = tests.AwaitTransactionAvailability(n.Peers(), conflictingTxIDs, 30*time.Second)
+	assert.NoError(t, err, "transactions should have been available")
+
+	expectations := map[string]*tests.ExpectedTransaction{}
+	for _, conflictingTx := range conflictingTxs {
+		utilsTx := utils.ParseTransaction(conflictingTx)
+		expectations[conflictingTx.ID().String()] = &tests.ExpectedTransaction{
+			Inputs:    &utilsTx.Inputs,
+			Outputs:   &utilsTx.Outputs,
+			Signature: &utilsTx.Signature,
+		}
+	}
+
+	tests.CheckTransactions(t, n.Peers(), expectations, true, tests.ExpectedInclusionState{
+		Confirmed: tests.False(),
+		Finalized: tests.False(),
+		// should be part of a conflict set
+		Conflict: tests.True(),
+		Solid:    tests.True(),
+		Rejected: tests.False(),
+		Liked:    tests.False(),
+	})
+}
diff --git a/tools/integration-tests/tester/tests/consensus/consensus_noconflicts_test.go b/tools/integration-tests/tester/tests/consensus/consensus_noconflicts_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..720334b2c3c2b83ea4dac783622047cacb542ad7
--- /dev/null
+++ b/tools/integration-tests/tester/tests/consensus/consensus_noconflicts_test.go
@@ -0,0 +1,118 @@
+package consensus
+
+import (
+	"math/rand"
+	"testing"
+	"time"
+
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
+	"github.com/iotaledger/goshimmer/plugins/webapi/value/utils"
+	"github.com/iotaledger/goshimmer/tools/integration-tests/tester/tests"
+	"github.com/mr-tron/base58/base58"
+	"github.com/stretchr/testify/require"
+)
+
+// TestConsensusNoConflicts issues valid non-conflicting value objects and then checks
+// whether the ledger of every peer reflects the same correct state.
+func TestConsensusNoConflicts(t *testing.T) {
+	n, err := f.CreateNetwork("consensus_TestConsensusNoConflicts", 4, 2)
+	require.NoError(t, err)
+	defer tests.ShutdownNetwork(t, n)
+
+	time.Sleep(5 * time.Second)
+
+	// genesis wallet
+	genesisSeedBytes, err := base58.Decode("7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih")
+	require.NoError(t, err, "couldn't decode genesis seed from base58 seed")
+
+	const genesisBalance = 1000000000
+	genesisWallet := wallet.New(genesisSeedBytes)
+	genesisAddr := genesisWallet.Seed().Address(0)
+	genesisOutputID := transaction.NewOutputID(genesisAddr, transaction.GenesisID)
+
+	firstReceiver := wallet.New()
+	const depositCount = 10
+	const deposit = genesisBalance / depositCount
+	firstReceiverAddresses := make([]string, depositCount)
+	firstReceiverDepositAddrs := make([]address.Address, depositCount)
+	firstReceiverDepositOutputs := map[address.Address][]*balance.Balance{}
+	firstReceiverExpectedBalances := map[string]map[balance.Color]int64{}
+	for i := 0; i < depositCount; i++ {
+		addr := firstReceiver.Seed().Address(uint64(i))
+		firstReceiverDepositAddrs[i] = addr
+		firstReceiverAddresses[i] = addr.String()
+		firstReceiverDepositOutputs[addr] = []*balance.Balance{{Value: deposit, Color: balance.ColorIOTA}}
+		firstReceiverExpectedBalances[addr.String()] = map[balance.Color]int64{balance.ColorIOTA: deposit}
+	}
+
+	// issue transaction spending from the genesis output
+	tx := transaction.New(transaction.NewInputs(genesisOutputID), transaction.NewOutputs(firstReceiverDepositOutputs))
+	tx = tx.Sign(signaturescheme.ED25519(*genesisWallet.Seed().KeyPair(0)))
+	utilsTx := utils.ParseTransaction(tx)
+
+	txID, err := n.Peers()[0].SendTransaction(tx.Bytes())
+	require.NoError(t, err)
+
+	// wait for the transaction to be propagated through the network
+	// and it becoming preferred, finalized and confirmed
+	time.Sleep(valuetransfers.AverageNetworkDelay*2 + valuetransfers.AverageNetworkDelay/2)
+
+	// since we just issued a transaction spending the genesis output, there
+	// shouldn't be any UTXOs on the genesis address anymore
+	tests.CheckAddressOutputsFullyConsumed(t, n.Peers(), []string{genesisAddr.String()})
+
+	// since we waited 2.5 avg. network delays and there were no conflicting transactions,
+	// the transaction we just issued must be preferred, liked, finalized and confirmed
+	tests.CheckTransactions(t, n.Peers(), map[string]*tests.ExpectedTransaction{
+		txID: {Inputs: &utilsTx.Inputs, Outputs: &utilsTx.Outputs, Signature: &utilsTx.Signature},
+	}, true, tests.ExpectedInclusionState{
+		Confirmed: tests.True(), Finalized: tests.True(),
+		Conflict: tests.False(), Solid: tests.True(),
+		Rejected: tests.False(), Liked: tests.True(),
+	})
+
+	// check balances on peers
+	tests.CheckBalances(t, n.Peers(), firstReceiverExpectedBalances)
+
+	// issue transactions spending all the outputs which were just created from a random peer
+	secondReceiverWallet := wallet.New()
+	secondReceiverAddresses := make([]string, depositCount)
+	secondReceiverExpectedBalances := map[string]map[balance.Color]int64{}
+	secondReceiverExpectedTransactions := map[string]*tests.ExpectedTransaction{}
+	for i := 0; i < depositCount; i++ {
+		addr := secondReceiverWallet.Seed().Address(uint64(i))
+		tx := transaction.New(
+			transaction.NewInputs(transaction.NewOutputID(firstReceiver.Seed().Address(uint64(i)), tx.ID())),
+			transaction.NewOutputs(map[address.Address][]*balance.Balance{
+				addr: {{Value: deposit, Color: balance.ColorIOTA}},
+			}),
+		)
+		secondReceiverAddresses[i] = addr.String()
+		tx = tx.Sign(signaturescheme.ED25519(*secondReceiverWallet.Seed().KeyPair(uint64(i))))
+		txID, err := n.Peers()[rand.Intn(len(n.Peers()))].SendTransaction(tx.Bytes())
+		require.NoError(t, err)
+
+		utilsTx := utils.ParseTransaction(tx)
+		secondReceiverExpectedBalances[addr.String()] = map[balance.Color]int64{balance.ColorIOTA: deposit}
+		secondReceiverExpectedTransactions[txID] = &tests.ExpectedTransaction{
+			Inputs: &utilsTx.Inputs, Outputs: &utilsTx.Outputs, Signature: &utilsTx.Signature,
+		}
+	}
+
+	// wait again some network delays for the transactions to materialize
+	time.Sleep(valuetransfers.AverageNetworkDelay*2 + valuetransfers.AverageNetworkDelay/2)
+	tests.CheckAddressOutputsFullyConsumed(t, n.Peers(), firstReceiverAddresses)
+	tests.CheckTransactions(t, n.Peers(), secondReceiverExpectedTransactions, true,
+		tests.ExpectedInclusionState{
+			Confirmed: tests.True(), Finalized: tests.True(),
+			Conflict: tests.False(), Solid: tests.True(),
+			Rejected: tests.False(), Liked: tests.True(),
+		},
+	)
+	tests.CheckBalances(t, n.Peers(), secondReceiverExpectedBalances)
+}
diff --git a/tools/integration-tests/tester/tests/consensus/consensus_test.go b/tools/integration-tests/tester/tests/consensus/consensus_test.go
deleted file mode 100644
index 3dca33caebc9d0b831dd54ad0aa4999862c92574..0000000000000000000000000000000000000000
--- a/tools/integration-tests/tester/tests/consensus/consensus_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package autopeering
-
-import (
-	"testing"
-	"time"
-
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
-	"github.com/iotaledger/goshimmer/plugins/webapi/value/utils"
-	"github.com/iotaledger/goshimmer/tools/integration-tests/tester/tests"
-	"github.com/mr-tron/base58/base58"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-// TestConsensusNoConflicts issues valid non-conflicting value objects and then checks
-// whether the ledger of every peer reflects the same correct state.
-func TestConsensusNoConflicts(t *testing.T) {
-	n, err := f.CreateNetwork("consensus_TestConsensusNoConflicts", 4, 2)
-	require.NoError(t, err)
-	defer tests.ShutdownNetwork(t, n)
-
-	time.Sleep(5 * time.Second)
-
-	// genesis wallet
-	genesisSeedBytes, err := base58.Decode("7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih")
-	require.NoError(t, err, "couldn't decode genesis seed from base58 seed")
-
-	const genesisBalance = 1000000000
-	genesisWallet := wallet.New(genesisSeedBytes)
-	genesisAddr := genesisWallet.Seed().Address(0)
-	genesisOutputID := transaction.NewOutputID(genesisAddr, transaction.GenesisID)
-
-	w := wallet.New()
-	addr1 := w.Seed().Address(0)
-	addr2 := w.Seed().Address(1)
-	const deposit = genesisBalance / 2
-
-	// issue transaction spending from the genesis output
-	tx := transaction.New(
-		transaction.NewInputs(genesisOutputID),
-		transaction.NewOutputs(map[address.Address][]*balance.Balance{
-			addr1: {{Value: deposit, Color: balance.ColorIOTA}},
-			addr2: {{Value: deposit, Color: balance.ColorIOTA}},
-		}),
-	)
-	tx = tx.Sign(signaturescheme.ED25519(*genesisWallet.Seed().KeyPair(0)))
-	utilsTx := utils.ParseTransaction(tx)
-
-	txID, err := n.Peers()[0].SendTransaction(tx.Bytes())
-	require.NoError(t, err)
-
-	// wait for the transaction to be propagated through the network
-	time.Sleep(20 * time.Second)
-
-	// check that each node has the same perception
-	for _, p := range n.Peers() {
-		// check existence of the transaction we just created
-		res, err := p.GetTransactionByID(txID)
-		assert.NoError(t, err)
-		assert.Len(t, res.Error, 0, "there shouldn't be any error from submitting the valid transaction")
-		assert.EqualValues(t, utilsTx.Inputs, res.Transaction.Inputs)
-		assert.EqualValues(t, utilsTx.Outputs, res.Transaction.Outputs)
-		assert.EqualValues(t, utilsTx.Signature, res.Transaction.Signature)
-
-		// check that genesis UTXO is spent
-		utxos, err := p.GetUnspentOutputs([]string{genesisAddr.String()})
-		assert.NoError(t, err)
-		assert.Len(t, utxos.Error, 0, "there shouldn't be any error from querying UTXOs")
-		assert.Len(t, utxos.UnspentOutputs, 0, "genesis address should not have any UTXOs")
-
-		// check UTXOs
-		utxos, err = p.GetUnspentOutputs([]string{addr1.String(), addr2.String()})
-		assert.Len(t, utxos.UnspentOutputs, 2, "addresses should have UTXOs")
-		assert.Equal(t, addr1.String(), utxos.UnspentOutputs[0].Address)
-		assert.EqualValues(t, deposit, utxos.UnspentOutputs[0].OutputIDs[0].Balances[0].Value)
-		assert.Equal(t, addr2.String(), utxos.UnspentOutputs[1].Address)
-		assert.EqualValues(t, deposit, utxos.UnspentOutputs[1].OutputIDs[0].Balances[0].Value)
-	}
-}
diff --git a/tools/integration-tests/tester/tests/consensus/main_test.go b/tools/integration-tests/tester/tests/consensus/main_test.go
index bcc09c2e53590df061cbff5a2159071d567440f1..422928f9627868376b0610691f0e3ac2f6108636 100644
--- a/tools/integration-tests/tester/tests/consensus/main_test.go
+++ b/tools/integration-tests/tester/tests/consensus/main_test.go
@@ -1,4 +1,4 @@
-package autopeering
+package consensus
 
 import (
 	"os"
diff --git a/tools/integration-tests/tester/tests/testutil.go b/tools/integration-tests/tester/tests/testutil.go
index a18532a0327b0422d4032e5ebabe5a3ade934cc0..38cee7a2c99d2bd8c043132d57ba4176a3f85dfe 100644
--- a/tools/integration-tests/tester/tests/testutil.go
+++ b/tools/integration-tests/tester/tests/testutil.go
@@ -1,8 +1,11 @@
 package tests
 
 import (
+	"errors"
 	"fmt"
 	"math/rand"
+	"sync"
+	"sync/atomic"
 	"testing"
 	"time"
 
@@ -11,11 +14,16 @@ 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/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
+var (
+	ErrTransactionNotAvailableInTime = errors.New("transaction was not available in time")
+)
+
 // DataMessageSent defines a struct to identify from which issuer a data message was sent.
 type DataMessageSent struct {
 	number          int
@@ -346,8 +354,63 @@ func CheckBalances(t *testing.T, peers []*framework.Peer, addrBalance map[string
 	}
 }
 
-// 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) {
+// 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.
+	Conflict *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
+}
+
+// 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) {
 	for _, peer := range peers {
 		if checkSynchronized {
 			// check that the peer sees itself as synchronized
@@ -356,14 +419,68 @@ func CheckTransactions(t *testing.T, peers []*framework.Peer, transactionIDs []s
 			require.True(t, info.Synced)
 		}
 
-		for _, txId := range transactionIDs {
+		for txId, expectedTransaction := range transactionIDs {
 			resp, err := peer.GetTransactionByID(txId)
 			require.NoError(t, err)
 
 			// check inclusion state
-			assert.True(t, resp.InclusionState.Confirmed)
+			if expectedInclusionState.Confirmed != nil {
+				assert.Equal(t, *expectedInclusionState.Confirmed, resp.InclusionState.Confirmed, "confirmed state doesn't match")
+			}
+			if expectedInclusionState.Conflict != nil {
+				assert.Equal(t, *expectedInclusionState.Conflict, resp.InclusionState.Conflict, "conflict state doesn't match")
+			}
+			if expectedInclusionState.Solid != nil {
+				assert.Equal(t, *expectedInclusionState.Solid, resp.InclusionState.Solid, "solid state doesn't match")
+			}
+			if expectedInclusionState.Rejected != nil {
+				assert.Equal(t, *expectedInclusionState.Rejected, resp.InclusionState.Rejected, "rejected state doesn't match")
+			}
+			if expectedInclusionState.Liked != nil {
+				assert.Equal(t, *expectedInclusionState.Liked, resp.InclusionState.Liked, "liked state doesn't match")
+			}
+
+			if expectedTransaction != nil {
+				if expectedTransaction.Inputs != nil {
+					assert.Equal(t, *expectedTransaction.Inputs, resp.Transaction.Inputs, "inputs do not match")
+				}
+				if expectedTransaction.Outputs != nil {
+					assert.Equal(t, *expectedTransaction.Outputs, resp.Transaction.Outputs, "outputs do not match")
+				}
+				if expectedTransaction.Signature != nil {
+					assert.Equal(t, *expectedTransaction.Signature, resp.Transaction.Signature, "signatures do not match")
+				}
+			}
+		}
+	}
+}
+
+// AwaitTransactionAvailability awaits until the given transaction IDs become available on all given peers or
+// the max duration is reached.
+func AwaitTransactionAvailability(peers []*framework.Peer, transactionIDs []string, maxAwait time.Duration) error {
+	s := time.Now()
+	for ; time.Since(s) < maxAwait; {
+		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 {
+						atomic.AddInt32(&counter, -1)
+					}
+				}
+			}(p)
+		}
+		wg.Wait()
+		if counter == 0 {
+			// everything available
+			return nil
 		}
 	}
+	return ErrTransactionNotAvailableInTime
 }
 
 // ShutdownNetwork shuts down the network and reports errors.
diff --git a/tools/integration-tests/tester/tests/value/value_test.go b/tools/integration-tests/tester/tests/value/value_test.go
index 4280c3354eaac9d4e813930c861a775ff8909e40..f0cb8109be17c0bea4ffaa1959042cd902070f43 100644
--- a/tools/integration-tests/tester/tests/value/value_test.go
+++ b/tools/integration-tests/tester/tests/value/value_test.go
@@ -18,26 +18,36 @@ func TestValueIotaPersistence(t *testing.T) {
 	time.Sleep(5 * time.Second)
 
 	// master node sends funds to all peers in the network
-	txIds, addrBalance := tests.SendValueMessagesOnFaucet(t, n.Peers())
+	txIdsSlice, addrBalance := tests.SendValueMessagesOnFaucet(t, n.Peers())
+	txIds := make(map[string]*tests.ExpectedTransaction)
+	for _, txID := range txIdsSlice {
+		txIds[txID] = nil
+	}
 
 	// wait for messages to be gossiped
 	time.Sleep(10 * time.Second)
 
 	// check whether the first issued transaction is available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true)
+	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
+		Confirmed: tests.True(),
+	})
 
 	// check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)
 
 	// send value message randomly
 	randomTxIds := tests.SendValueMessagesOnRandomPeer(t, n.Peers(), addrBalance, 10)
-	txIds = append(txIds, randomTxIds...)
+	for _, randomTxId := range randomTxIds {
+		txIds[randomTxId] = nil
+	}
 
 	// wait for messages to be gossiped
 	time.Sleep(10 * time.Second)
 
 	// check whether all issued transactions are persistently available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true)
+	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
+		Confirmed: tests.True(),
+	})
 
 	// check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)
@@ -58,7 +68,9 @@ func TestValueIotaPersistence(t *testing.T) {
 	time.Sleep(10 * time.Second)
 
 	// check whether all issued transactions are persistently available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true)
+	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
+		Confirmed: tests.True(),
+	})
 
 	// 5. check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)
@@ -74,26 +86,36 @@ func TestValueColoredPersistence(t *testing.T) {
 	time.Sleep(5 * time.Second)
 
 	// master node sends funds to all peers in the network
-	txIds, addrBalance := tests.SendValueMessagesOnFaucet(t, n.Peers())
+	txIdsSlice, addrBalance := tests.SendValueMessagesOnFaucet(t, n.Peers())
+	txIds := make(map[string]*tests.ExpectedTransaction)
+	for _, txID := range txIdsSlice {
+		txIds[txID] = nil
+	}
 
 	// wait for messages to be gossiped
 	time.Sleep(10 * time.Second)
 
 	// check whether the transactions are available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true)
+	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
+		Confirmed: tests.True(),
+	})
 
 	// check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)
 
 	// send funds around
 	randomTxIds := tests.SendColoredValueMessagesOnRandomPeer(t, n.Peers(), addrBalance, 10)
-	txIds = append(txIds, randomTxIds...)
+	for _, randomTxId := range randomTxIds {
+		txIds[randomTxId] = nil
+	}
 
 	// wait for value messages to be gossiped
 	time.Sleep(10 * time.Second)
 
 	// check whether all issued transactions are persistently available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true)
+	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
+		Confirmed: tests.True(),
+	})
 
 	// check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)
@@ -114,7 +136,9 @@ func TestValueColoredPersistence(t *testing.T) {
 	time.Sleep(10 * time.Second)
 
 	// check whether all issued transactions are persistently available on all nodes, and confirmed
-	tests.CheckTransactions(t, n.Peers(), txIds, true)
+	tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{
+		Confirmed: tests.True(),
+	})
 
 	// 5. check ledger state
 	tests.CheckBalances(t, n.Peers(), addrBalance)