Repository: stellar/go
Pull request #5215 (2024-02-20 18:56:33)

Port 2.28.3 back to mainline
<!-- If you're making a doc PR or something tiny where the below is irrelevant, delete this
template and use a short description, but in your description aim to include both what the
change is, and why it is being made, with enough context for anyone to understand. -->

<details>
  <summary>PR Checklist</summary>
  
### PR Structure

* [x] This PR has reasonably narrow scope (if not, break it down into smaller PRs).
* [x] This PR avoids mixing refactoring changes with feature changes (split into two PRs
  otherwise).
* [x] This PR's title starts with name of package that is most changed in the PR, ex.
  `services/friendbot`, or `all` or `doc` if the changes are broad or impact many
  packages.

### Thoroughness

* [ ] This PR adds tests for the most critical parts of the new functionality or fixes.
* [ ] I've updated any docs ([developer docs](https://developers.stellar.org/api/), `.md`
  files, etc... affected by this change). Take a look in the `docs` folder for a given service,
  like [this one](https://github.com/stellar/go/tree/master/services/horizon/internal/docs).

### Release planning

* [ ] I've updated the relevant CHANGELOG ([here](services/horizon/CHANGELOG.md) for Horizon) if
  needed with deprecations, added features, breaking changes, and DB schema changes.
* [ ] I've decided if this PR requires a new major/minor version according to
  [semver](https://semver.org/), or if it's mainly a patch change. The PR is targeted at the next
  release branch if it's not a patch change.
</details>

### What

merging the changes that were done on 2.28.3 back to master

### Why

to keep master up to date with released content.

### Known limitations





Patch:
diff --git a/services/horizon/CHANGELOG.md b/services/horizon/CHANGELOG.md
index 0e1b1169..cd411b71 100644
--- a/services/horizon/CHANGELOG.md
+++ b/services/horizon/CHANGELOG.md
@@ -3,6 +3,18 @@
 All notable changes to this project will be documented in this
 file. This project adheres to [Semantic Versioning](http://semver.org/).
 
+## 2.28.3
+
+### Fixed
+- Fix claimable_balance_claimants subquery in GetClaimableBalances() [5207](https://github.com/stellar/go/pull/5207)
+
+### Added
+- New optional config `SKIP_TXMETA` ([5189](https://github.com/stellar/go/issues/5189)). Defaults to `FALSE`, when `TRUE` the following will occur:
+  * history_transactions.tx_meta column will have serialized xdr that equates to empty for any protocol version, such as for `xdr.TransactionMeta.V3`, `Operations`, `TxChangesAfter`, `TxChangesBefore` will be empty arrays and `SorobanMeta` will be nil. 
+
+### Breaking Changes
+- Removed `DISABLE_SOROBAN_INGEST` configuration parameter, use the new `SKIP_TXMETA` parameter instead.
+
 ## 2.28.2
 
 ### Fixed
diff --git a/services/horizon/cmd/db.go b/services/horizon/cmd/db.go
index 7d14ca31..965d5d71 100644
--- a/services/horizon/cmd/db.go
+++ b/services/horizon/cmd/db.go
@@ -420,7 +420,7 @@ func runDBReingestRange(ledgerRanges []history.LedgerRange, reingestForce bool,
 		RoundingSlippageFilter:      config.RoundingSlippageFilter,
 		EnableIngestionFiltering:    config.EnableIngestionFiltering,
 		MaxLedgerPerFlush:           maxLedgersPerFlush,
-		SkipSorobanIngestion:        config.SkipSorobanIngestion,
+		SkipTxmeta:                  config.SkipTxmeta,
 	}
 
 	if ingestConfig.HistorySession, err = db.Open("postgres", config.DatabaseURL); err != nil {
diff --git a/services/horizon/internal/config.go b/services/horizon/internal/config.go
index 54f843b8..f1f8ac07 100644
--- a/services/horizon/internal/config.go
+++ b/services/horizon/internal/config.go
@@ -109,6 +109,6 @@ type Config struct {
 	Network string
 	// DisableTxSub disables transaction submission functionality for Horizon.
 	DisableTxSub bool
-	// SkipSorobanIngestion skips Soroban related ingestion processing.
-	SkipSorobanIngestion bool
+	// SkipTxmeta, when enabled, will not store meta xdr in history transaction table
+	SkipTxmeta bool
 }
diff --git a/services/horizon/internal/db2/history/claimable_balances.go b/services/horizon/internal/db2/history/claimable_balances.go
index d45780a4..c198ee16 100644
--- a/services/horizon/internal/db2/history/claimable_balances.go
+++ b/services/horizon/internal/db2/history/claimable_balances.go
@@ -10,6 +10,7 @@ import (
 
 	sq "github.com/Masterminds/squirrel"
 	"github.com/guregu/null"
+
 	"github.com/stellar/go/services/horizon/internal/db2"
 	"github.com/stellar/go/support/errors"
 	"github.com/stellar/go/xdr"
@@ -57,7 +58,7 @@ func (cbq ClaimableBalancesQuery) Cursor() (int64, string, error) {
 // ApplyCursor applies cursor to the given sql. For performance reason the limit
 // is not applied here. This allows us to hint the planner later to use the right
 // indexes.
-func applyClaimableBalancesQueriesCursor(sql sq.SelectBuilder, lCursor int64, rCursor string, order string) (sq.SelectBuilder, error) {
+func applyClaimableBalancesQueriesCursor(sql sq.SelectBuilder, tableName string, lCursor int64, rCursor string, order string) (sq.SelectBuilder, error) {
 	hasPagedLimit := false
 	if lCursor > 0 && rCursor != "" {
 		hasPagedLimit = true
@@ -67,17 +68,26 @@ func applyClaimableBalancesQueriesCursor(sql sq.SelectBuilder, lCursor int64, rC
 	case db2.OrderAscending:
 		if hasPagedLimit {
 			sql = sql.
-				Where(sq.Expr("(cb.last_modified_ledger, cb.id) > (?, ?)", lCursor, rCursor))
-
+				Where(
+					sq.Expr(
+						fmt.Sprintf("(%s.last_modified_ledger, %s.id) > (?, ?)", tableName, tableName),
+						lCursor, rCursor,
+					),
+				)
 		}
-		sql = sql.OrderBy("cb.last_modified_ledger asc, cb.id asc")
+		sql = sql.OrderBy(fmt.Sprintf("%s.last_modified_ledger asc, %s.id asc", tableName, tableName))
 	case db2.OrderDescending:
 		if hasPagedLimit {
 			sql = sql.
-				Where(sq.Expr("(cb.last_modified_ledger, cb.id) < (?, ?)", lCursor, rCursor))
+				Where(
+					sq.Expr(
+						fmt.Sprintf("(%s.last_modified_ledger, %s.id) < (?, ?)", tableName, tableName),
+						lCursor,
+						rCursor,
+					),
+				)
 		}
-
-		sql = sql.OrderBy("cb.last_modified_ledger desc, cb.id desc")
+		sql = sql.OrderBy(fmt.Sprintf("%s.last_modified_ledger desc, %s.id desc", tableName, tableName))
 	default:
 		return sql, errors.Errorf("invalid order: %s", order)
 	}
@@ -216,7 +226,7 @@ func (q *Q) GetClaimableBalances(ctx context.Context, query ClaimableBalancesQue
 		return nil, errors.Wrap(err, "error getting cursor")
 	}
 
-	sql, err := applyClaimableBalancesQueriesCursor(selectClaimableBalances, l, r, query.PageQuery.Order)
+	sql, err := applyClaimableBalancesQueriesCursor(selectClaimableBalances, "cb", l, r, query.PageQuery.Order)
 	if err != nil {
 		return nil, errors.Wrap(err, "could not apply query to page")
 	}
@@ -242,10 +252,10 @@ func (q *Q) GetClaimableBalances(ctx context.Context, query ClaimableBalancesQue
 		// does not perform efficiently. Instead, use a subquery (with LIMIT) to retrieve claimable balances based on
 		// the claimant's address.
 
-		var selectClaimableBalanceClaimants = sq.Select("id").From("claimable_balance_claimants").
-			Where("destination = ?", query.Claimant.Address()).Limit(query.PageQuery.Limit)
+		var selectClaimableBalanceClaimants = sq.Select("claimable_balance_claimants.id").From("claimable_balance_claimants").
+			Where("claimable_balance_claimants.destination = ?", query.Claimant.Address()).Limit(query.PageQuery.Limit)
 
-		subSql, err := applyClaimableBalancesQueriesCursor(selectClaimableBalanceClaimants, l, r, query.PageQuery.Order)
+		subSql, err := applyClaimableBalancesQueriesCursor(selectClaimableBalanceClaimants, "claimable_balance_claimants", l, r, query.PageQuery.Order)
 		if err != nil {
 			return nil, errors.Wrap(err, "could not apply subquery to page")
 		}
diff --git a/services/horizon/internal/flags.go b/services/horizon/internal/flags.go
index 87deb28c..4c8e4dc2 100644
--- a/services/horizon/internal/flags.go
+++ b/services/horizon/internal/flags.go
@@ -60,8 +60,8 @@ const (
 	EnableIngestionFilteringFlagName = "exp-enable-ingestion-filtering"
 	// DisableTxSubFlagName is the command line flag for disabling transaction submission feature of Horizon
 	DisableTxSubFlagName = "disable-tx-sub"
-	// SkipSorobanIngestionFlagName is the command line flag for disabling Soroban related ingestion processing
-	SkipSorobanIngestionFlagName = "disable-soroban-ingest"
+	// SkipTxmeta is the command line flag for disabling persistence of tx meta in history transaction table
+	SkipTxmeta = "skip-txmeta"
 
 	// StellarPubnet is a constant representing the Stellar public network
 	StellarPubnet = "pubnet"
@@ -740,12 +740,12 @@ func Flags() (*Config, support.ConfigOptions) {
 			UsedInCommands: IngestionCommands,
 		},
 		&support.ConfigOption{
-			Name:           SkipSorobanIngestionFlagName,
-			ConfigKey:      &config.SkipSorobanIngestion,
+			Name:           SkipTxmeta,
+			ConfigKey:      &config.SkipTxmeta,
 			OptType:        types.Bool,
 			FlagDefault:    false,
 			Required:       false,
-			Usage:          "excludes Soroban data during ingestion processing",
+			Usage:          "excludes tx meta from persistence on transaction history",
 			UsedInCommands: IngestionCommands,
 		},
 	}
diff --git a/services/horizon/internal/ingest/main.go b/services/horizon/internal/ingest/main.go
index 98acd68f..ae049800 100644
--- a/services/horizon/internal/ingest/main.go
+++ b/services/horizon/internal/ingest/main.go
@@ -109,8 +109,7 @@ type Config struct {
 
 	EnableIngestionFiltering bool
 	MaxLedgerPerFlush        uint32
-
-	SkipSorobanIngestion bool
+	SkipTxmeta               bool
 }
 
 const (
diff --git a/services/horizon/internal/ingest/processor_runner.go b/services/horizon/internal/ingest/processor_runner.go
index a09442b4..0023ca31 100644
--- a/services/horizon/internal/ingest/processor_runner.go
+++ b/services/horizon/internal/ingest/processor_runner.go
@@ -111,7 +111,6 @@ func buildChangeProcessor(
 	source ingestionSource,
 	ledgerSequence uint32,
 	networkPassphrase string,
-	skipSorobanIngestion bool,
 ) *groupChangeProcessors {
 	statsChangeProcessor := &statsChangeProcessor{
 		StatsChangeProcessor: changeStats,
@@ -145,13 +144,13 @@ func (s *ProcessorRunner) buildTransactionProcessor(ledgersProcessor *processors
 
 	processors := []horizonTransactionProcessor{
 		statsLedgerTransactionProcessor,
-		processors.NewEffectProcessor(accountLoader, s.historyQ.NewEffectBatchInsertBuilder(), s.config.NetworkPassphrase, s.config.SkipSorobanIngestion),
+		processors.NewEffectProcessor(accountLoader, s.historyQ.NewEffectBatchInsertBuilder(), s.config.NetworkPassphrase),
 		ledgersProcessor,
-		processors.NewOperationProcessor(s.historyQ.NewOperationBatchInsertBuilder(), s.config.NetworkPassphrase, s.config.SkipSorobanIngestion),
+		processors.NewOperationProcessor(s.historyQ.NewOperationBatchInsertBuilder(), s.config.NetworkPassphrase),
 		tradeProcessor,
 		processors.NewParticipantsProcessor(accountLoader,
 			s.historyQ.NewTransactionParticipantsBatchInsertBuilder(), s.historyQ.NewOperationParticipantBatchInsertBuilder()),
-		processors.NewTransactionProcessor(s.historyQ.NewTransactionBatchInsertBuilder(), s.config.SkipSorobanIngestion),
+		processors.NewTransactionProcessor(s.historyQ.NewTransactionBatchInsertBuilder(), s.config.SkipTxmeta),
 		processors.NewClaimableBalancesTransactionProcessor(cbLoader,
 			s.historyQ.NewTransactionClaimableBalanceBatchInsertBuilder(), s.historyQ.NewOperationClaimableBalanceBatchInsertBuilder()),
 		processors.NewLiquidityPoolsTransactionProcessor(lpLoader,
@@ -173,10 +172,7 @@ func (s *ProcessorRunner) buildFilteredOutProcessor() *groupTransactionProcessor
 	// when in online mode, the submission result processor must always run (regardless of filtering)
 	var p []horizonTransactionProcessor
 	if s.config.EnableIngestionFiltering {
-		txSubProc := processors.NewTransactionFilteredTmpProcessor(
-			s.historyQ.NewTransactionFilteredTmpBatchInsertBuilder(),
-			s.config.SkipSorobanIngestion,
-		)
+		txSubProc := processors.NewTransactionFilteredTmpProcessor(s.historyQ.NewTransactionFilteredTmpBatchInsertBuilder(), s.config.SkipTxmeta)
 		p = append(p, txSubProc)
 	}
 
@@ -239,7 +235,6 @@ func (s *ProcessorRunner) RunHistoryArchiveIngestion(
 		historyArchiveSource,
 		checkpointLedger,
 		s.config.NetworkPassphrase,
-		s.config.SkipSorobanIngestion,
 	)
 
 	if checkpointLedger == 1 {
@@ -498,7 +493,6 @@ func (s *ProcessorRunner) RunAllProcessorsOnLedger(ledger xdr.LedgerCloseMeta) (
 		ledgerSource,
 		ledger.LedgerSequence(),
 		s.config.NetworkPassphrase,
-		s.config.SkipSorobanIngestion,
 	)
 	err = s.runChangeProcessorOnLedger(groupChangeProcessors, ledger)
 	if err != nil {
diff --git a/services/horizon/internal/ingest/processors/effects_processor.go b/services/horizon/internal/ingest/processors/effects_processor.go
index 830632f5..34e9f916 100644
--- a/services/horizon/internal/ingest/processors/effects_processor.go
+++ b/services/horizon/internal/ingest/processors/effects_processor.go
@@ -28,20 +28,17 @@ type EffectProcessor struct {
 	accountLoader *history.AccountLoader
 	batch         history.EffectBatchInsertBuilder
 	network       string
-	skipSoroban   bool
 }
 
 func NewEffectProcessor(
 	accountLoader *history.AccountLoader,
 	batch history.EffectBatchInsertBuilder,
 	network string,
-	skipSoroban bool,
 ) *EffectProcessor {
 	return &EffectProcessor{
 		accountLoader: accountLoader,
 		batch:         batch,
 		network:       network,
-		skipSoroban:   skipSoroban,
 	}
 }
 
@@ -53,29 +50,14 @@ func (p *EffectProcessor) ProcessTransaction(
 		return nil
 	}
 
-	elidedTransaction := transaction
-
-	if p.skipSoroban &&
-		elidedTransaction.UnsafeMeta.V == 3 &&
-		elidedTransaction.UnsafeMeta.V3.SorobanMeta != nil {
-		elidedTransaction.UnsafeMeta.V3 = &xdr.TransactionMetaV3{
-			Ext:             xdr.ExtensionPoint{},
-			TxChangesBefore: xdr.LedgerEntryChanges{},
-			Operations:      []xdr.OperationMeta{},
-			TxChangesAfter:  xdr.LedgerEntryChanges{},
-			SorobanMeta:     nil,
-		}
-	}
-
-	for opi, op := range elidedTransaction.Envelope.Operations() {
+	for opi, op := range transaction.Envelope.Operations() {
 		operation := transactionOperationWrapper{
 			index:          uint32(opi),
-			transaction:    elidedTransaction,
+			transaction:    transaction,
 			operation:      op,
 			ledgerSequence: uint32(lcm.LedgerSequence()),
 			network:        p.network,
 		}
-
 		if err := operation.ingestEffects(p.accountLoader, p.batch); err != nil {
 			return errors.Wrapf(err, "reading operation %v effects", operation.ID())
 		}
diff --git a/services/horizon/internal/ingest/processors/operations_processor.go b/services/horizon/internal/ingest/processors/operations_processor.go
index 92a4b870..8ad02314 100644
--- a/services/horizon/internal/ingest/processors/operations_processor.go
+++ b/services/horizon/internal/ingest/processors/operations_processor.go
@@ -22,16 +22,14 @@ import (
 
 // OperationProcessor operations processor
 type OperationProcessor struct {
-	batch       history.OperationBatchInsertBuilder
-	network     string
-	skipSoroban bool
+	batch   history.OperationBatchInsertBuilder
+	network string
 }
 
-func NewOperationProcessor(batch history.OperationBatchInsertBuilder, network string, skipSoroban bool) *OperationProcessor {
+func NewOperationProcessor(batch history.OperationBatchInsertBuilder, network string) *OperationProcessor {
 	return &OperationProcessor{
-		batch:       batch,
-		network:     network,
-		skipSoroban: skipSoroban,
+		batch:   batch,
+		network: network,
 	}
 }
 
@@ -39,12 +37,11 @@ func NewOperationProcessor(batch history.OperationBatchInsertBuilder, network st
 func (p *OperationProcessor) ProcessTransaction(lcm xdr.LedgerCloseMeta, transaction ingest.LedgerTransaction) error {
 	for i, op := range transaction.Envelope.Operations() {
 		operation := transactionOperationWrapper{
-			index:              uint32(i),
-			transaction:        transaction,
-			operation:          op,
-			ledgerSequence:     lcm.LedgerSequence(),
-			network:            p.network,
-			skipSorobanDetails: p.skipSoroban,
+			index:          uint32(i),
+			transaction:    transaction,
+			operation:      op,
+			ledgerSequence: lcm.LedgerSequence(),
+			network:        p.network,
 		}
 		details, err := operation.Details()
 		if err != nil {
@@ -85,12 +82,11 @@ func (p *OperationProcessor) Flush(ctx context.Context, session db.SessionInterf
 
 // transactionOperationWrapper represents the data for a single operation within a transaction
 type transactionOperationWrapper struct {
-	index              uint32
-	transaction        ingest.LedgerTransaction
-	operation          xdr.Operation
-	ledgerSequence     uint32
-	network            string
-	skipSorobanDetails bool
+	index          uint32
+	transaction    ingest.LedgerTransaction
+	operation      xdr.Operation
+	ledgerSequence uint32
+	network        string
 }
 
 // ID returns the ID for the operation.
@@ -270,11 +266,6 @@ func (operation *transactionOperationWrapper) IsPayment() bool {
 	case xdr.OperationTypeAccountMerge:
 		return true
 	case xdr.OperationTypeInvokeHostFunction:
-		// #5175, may want to consider skipping this parsing of payment from contracts
-		// as part of eliding soroban ingestion aspects when DISABLE_SOROBAN_INGEST.
-		// but, may cause inconsistencies that aren't worth the gain,
-		// as payments won't be thoroughly accurate, i.e. a payment could have
-		// happened within a contract invoke.
 		diagnosticEvents, err := operation.transaction.GetDiagnosticEvents()
 		if err != nil {
 			return false
@@ -698,18 +689,11 @@ func (operation *transactionOperationWrapper) Details() (map[string]interface{},
 			}
 			details["parameters"] = params
 
-			var balanceChanges []map[string]interface{}
-			var parseErr error
-			if operation.skipSorobanDetails {
-				// https://github.com/stellar/go/issues/5175
-				// intentionally toggle off parsing soroban meta into "asset_balance_changes"
-				balanceChanges = make([]map[string]interface{}, 0)
+			if balanceChanges, err := operation.parseAssetBalanceChangesFromContractEvents(); err != nil {
+				return nil, err
 			} else {
-				if balanceChanges, parseErr = operation.parseAssetBalanceChangesFromContractEvents(); parseErr != nil {
-					return nil, parseErr
-				}
+				details["asset_balance_changes"] = balanceChanges
 			}
-			details["asset_balance_changes"] = balanceChanges
 
 		case xdr.HostFunctionTypeHostFunctionTypeCreateContract:
 			args := op.HostFunction.MustCreateContract()
diff --git a/services/horizon/internal/ingest/processors/transactions_processor.go b/services/horizon/internal/ingest/processors/transactions_processor.go
index b82934d8..28aae6f3 100644
--- a/services/horizon/internal/ingest/processors/transactions_processor.go
+++ b/services/horizon/internal/ingest/processors/transactions_processor.go
@@ -11,36 +11,50 @@ import (
 )
 
 type TransactionProcessor struct {
-	batch       history.TransactionBatchInsertBuilder
-	skipSoroban bool
+	batch      history.TransactionBatchInsertBuilder
+	skipTxmeta bool
 }
 
-func NewTransactionFilteredTmpProcessor(batch history.TransactionBatchInsertBuilder, skipSoroban bool) *TransactionProcessor {
+func NewTransactionFilteredTmpProcessor(batch history.TransactionBatchInsertBuilder, skipTxmeta bool) *TransactionProcessor {
 	return &TransactionProcessor{
-		batch:       batch,
-		skipSoroban: skipSoroban,
+		batch:      batch,
+		skipTxmeta: skipTxmeta,
 	}
 }
 
-func NewTransactionProcessor(batch history.TransactionBatchInsertBuilder, skipSoroban bool) *TransactionProcessor {
+func NewTransactionProcessor(batch history.TransactionBatchInsertBuilder, skipTxmeta bool) *TransactionProcessor {
 	return &TransactionProcessor{
-		batch:       batch,
-		skipSoroban: skipSoroban,
+		batch:      batch,
+		skipTxmeta: skipTxmeta,
 	}
 }
 
 func (p *TransactionProcessor) ProcessTransaction(lcm xdr.LedgerCloseMeta, transaction ingest.LedgerTransaction) error {
 	elidedTransaction := transaction
 
-	if p.skipSoroban &&
-		elidedTransaction.UnsafeMeta.V == 3 &&
-		elidedTransaction.UnsafeMeta.MustV3().SorobanMeta != nil {
-		elidedTransaction.UnsafeMeta.V3 = &xdr.TransactionMetaV3{
-			Ext:             xdr.ExtensionPoint{},
-			TxChangesBefore: xdr.LedgerEntryChanges{},
-			Operations:      []xdr.OperationMeta{},
-			TxChangesAfter:  xdr.LedgerEntryChanges{},
-			SorobanMeta:     nil,
+	if p.skipTxmeta {
+		switch elidedTransaction.UnsafeMeta.V {
+		case 3:
+			elidedTransaction.UnsafeMeta.V3 = &xdr.TransactionMetaV3{
+				Ext:             xdr.ExtensionPoint{},
+				TxChangesBefore: xdr.LedgerEntryChanges{},
+				Operations:      []xdr.OperationMeta{},
+				TxChangesAfter:  xdr.LedgerEntryChanges{},
+				SorobanMeta:     nil,
+			}
+		case 2:
+			elidedTransaction.UnsafeMeta.V2 = &xdr.TransactionMetaV2{
+				TxChangesBefore: xdr.LedgerEntryChanges{},
+				Operations:      []xdr.OperationMeta{},
+				TxChangesAfter:  xdr.LedgerEntryChanges{},
+			}
+		case 1:
+			elidedTransaction.UnsafeMeta.V1 = &xdr.TransactionMetaV1{
+				TxChanges:  xdr.LedgerEntryChanges{},
+				Operations: []xdr.OperationMeta{},
+			}
+		default:
+			return errors.Errorf("SKIP_TXMETA is enabled, but received an un-supported tx-meta version %v, can't proceed with removal", elidedTransaction.UnsafeMeta.V)
 		}
 	}
 
diff --git a/services/horizon/internal/init.go b/services/horizon/internal/init.go
index 60ba7b6c..0c0fe2c2 100644
--- a/services/horizon/internal/init.go
+++ b/services/horizon/internal/init.go
@@ -111,7 +111,7 @@ func initIngester(app *App) {
 		EnableExtendedLogLedgerStats:         app.config.IngestEnableExtendedLogLedgerStats,
 		RoundingSlippageFilter:               app.config.RoundingSlippageFilter,
 		EnableIngestionFiltering:             app.config.EnableIngestionFiltering,
-		SkipSorobanIngestion:                 app.config.SkipSorobanIngestion,
+		SkipTxmeta:                           app.config.SkipTxmeta,
 	})
 
 	if err != nil {


Test patch:
diff --git a/services/horizon/internal/db2/history/claimable_balances_test.go b/services/horizon/internal/db2/history/claimable_balances_test.go
index 769ab3bc..2e6d6219 100644
--- a/services/horizon/internal/db2/history/claimable_balances_test.go
+++ b/services/horizon/internal/db2/history/claimable_balances_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/guregu/null"
+
 	"github.com/stellar/go/services/horizon/internal/db2"
 	"github.com/stellar/go/services/horizon/internal/test"
 	"github.com/stellar/go/xdr"
@@ -203,7 +204,7 @@ func TestFindClaimableBalancesByDestination(t *testing.T) {
 	tt.Assert.Equal(dest2, cbs[0].Claimants[1].Destination)
 
 	// this validates the cb query with claimant and cb.id/ledger cursor parameters
-	query.PageQuery = db2.MustPageQuery(fmt.Sprintf("%v-%s", 150, cbs[0].BalanceID), false, "", 10)
+	query.PageQuery = db2.MustPageQuery(fmt.Sprintf("%v-%s", 150, cbs[0].BalanceID), false, "asc", 10)
 	query.Claimant = xdr.MustAddressPtr(dest1)
 	cbs, err = q.GetClaimableBalances(tt.Ctx, query)
 	tt.Assert.NoError(err)
@@ -212,7 +213,7 @@ func TestFindClaimableBalancesByDestination(t *testing.T) {
 
 	// this validates the cb query with no claimant parameter,
 	// should still produce working sql, as it triggers different LIMIT position in sql.
-	query.PageQuery = db2.MustPageQuery("", false, "", 1)
+	query.PageQuery = db2.MustPageQuery("", false, "desc", 1)
 	query.Claimant = nil
 	cbs, err = q.GetClaimableBalances(tt.Ctx, query)
 	tt.Assert.NoError(err)
diff --git a/services/horizon/internal/ingest/processor_runner_test.go b/services/horizon/internal/ingest/processor_runner_test.go
index ddac48aa..eaeca956 100644
--- a/services/horizon/internal/ingest/processor_runner_test.go
+++ b/services/horizon/internal/ingest/processor_runner_test.go
@@ -180,7 +180,7 @@ func TestProcessorRunnerBuildChangeProcessor(t *testing.T) {
 	}
 
 	stats := &ingest.StatsChangeProcessor{}
-	processor := buildChangeProcessor(runner.historyQ, stats, ledgerSource, 123, "", false)
+	processor := buildChangeProcessor(runner.historyQ, stats, ledgerSource, 123, "")
 	assert.IsType(t, &groupChangeProcessors{}, processor)
 
 	assert.IsType(t, &statsChangeProcessor{}, processor.processors[0])
@@ -201,7 +201,7 @@ func TestProcessorRunnerBuildChangeProcessor(t *testing.T) {
 		filters:  &MockFilters{},
 	}
 
-	processor = buildChangeProcessor(runner.historyQ, stats, historyArchiveSource, 456, "", false)
+	processor = buildChangeProcessor(runner.historyQ, stats, historyArchiveSource, 456, "")
 	assert.IsType(t, &groupChangeProcessors{}, processor)
 
 	assert.IsType(t, &statsChangeProcessor{}, processor.processors[0])
@@ -271,7 +271,6 @@ func TestProcessorRunnerWithFilterEnabled(t *testing.T) {
 	config := Config{
 		NetworkPassphrase:        network.PublicNetworkPassphrase,
 		EnableIngestionFiltering: true,
-		SkipSorobanIngestion:     false,
 	}
 
 	q := &mockDBQ{}
diff --git a/services/horizon/internal/ingest/processors/claimable_balances_transaction_processor_test.go b/services/horizon/internal/ingest/processors/claimable_balances_transaction_processor_test.go
index 11ce5450..ca918e08 100644
--- a/services/horizon/internal/ingest/processors/claimable_balances_transaction_processor_test.go
+++ b/services/horizon/internal/ingest/processors/claimable_balances_transaction_processor_test.go
@@ -67,7 +67,7 @@ func (s *ClaimableBalancesTransactionProcessorTestSuiteLedger) TestEmptyClaimabl
 
 func (s *ClaimableBalancesTransactionProcessorTestSuiteLedger) testOperationInserts(balanceID xdr.ClaimableBalanceId, body xdr.OperationBody, change xdr.LedgerEntryChange) {
 	// Setup the transaction
-	txn := createTransaction(true, 1)
+	txn := createTransaction(true, 1, 2)
 	txn.Envelope.Operations()[0].Body = body
 	txn.UnsafeMeta.V = 2
 	txn.UnsafeMeta.V2.Operations = []xdr.OperationMeta{
diff --git a/services/horizon/internal/ingest/processors/effects_processor_test.go b/services/horizon/internal/ingest/processors/effects_processor_test.go
index 70af2173..0243768f 100644
--- a/services/horizon/internal/ingest/processors/effects_processor_test.go
+++ b/services/horizon/internal/ingest/processors/effects_processor_test.go
@@ -143,7 +143,6 @@ func (s *EffectsProcessorTestSuiteLedger) SetupTest() {
 		s.accountLoader,
 		s.mockBatchInsertBuilder,
 		networkPassphrase,
-		false,
 	)
 
 	s.txs = []ingest.LedgerTransaction{
diff --git a/services/horizon/internal/ingest/processors/ledgers_processor_test.go b/services/horizon/internal/ingest/processors/ledgers_processor_test.go
index 1df66402..73111687 100644
--- a/services/horizon/internal/ingest/processors/ledgers_processor_test.go
+++ b/services/horizon/internal/ingest/processors/ledgers_processor_test.go
@@ -33,7 +33,7 @@ func TestLedgersProcessorTestSuiteLedger(t *testing.T) {
 	suite.Run(t, new(LedgersProcessorTestSuiteLedger))
 }
 
-func createTransaction(successful bool, numOps int) ingest.LedgerTransaction {
+func createTransaction(successful bool, numOps int, metaVer int32) ingest.LedgerTransaction {
 	code := xdr.TransactionResultCodeTxSuccess
 	if !successful {
 		code = xdr.TransactionResultCodeTxFailed
@@ -50,6 +50,32 @@ func createTransaction(successful bool, numOps int) ingest.LedgerTransaction {
 		})
 	}
 	sourceAID := xdr.MustAddress("GAUJETIZVEP2NRYLUESJ3LS66NVCEGMON4UDCBCSBEVPIID773P2W6AY")
+
+	var txMeta xdr.TransactionMeta
+	switch metaVer {
+	case 3:
+		txMeta = xdr.TransactionMeta{
+			V: 3,
+			V3: &xdr.TransactionMetaV3{
+				Operations: make([]xdr.OperationMeta, numOps, numOps),
+			},
+		}
+	case 2:
+		txMeta = xdr.TransactionMeta{
+			V: 2,
+			V2: &xdr.TransactionMetaV2{
+				Operations: make([]xdr.OperationMeta, numOps, numOps),
+			},
+		}
+	case 1:
+		txMeta = xdr.TransactionMeta{
+			V: 1,
+			V1: &xdr.TransactionMetaV1{
+				Operations: make([]xdr.OperationMeta, numOps, numOps),
+			},
+		}
+	}
+
 	return ingest.LedgerTransaction{
 		Result: xdr.TransactionResultPair{
 			TransactionHash: xdr.Hash{},
@@ -70,12 +96,7 @@ func createTransaction(successful bool, numOps int) ingest.LedgerTransaction {
 				},
 			},
 		},
-		UnsafeMeta: xdr.TransactionMeta{
-			V: 2,
-			V2: &xdr.TransactionMetaV2{
-				Operations: make([]xdr.OperationMeta, numOps, numOps),
-			},
-		},
+		UnsafeMeta: txMeta,
 	}
 }
 
@@ -94,9 +115,9 @@ func (s *LedgersProcessorTestSuiteLedger) SetupTest() {
 	)
 
 	s.txs = []ingest.LedgerTransaction{
-		createTransaction(true, 1),
-		createTransaction(false, 3),
-		createTransaction(true, 4),
+		createTransaction(true, 1, 1),
+		createTransaction(false, 3, 2),
+		createTransaction(true, 4, 3),
 	}
 
 	s.successCount = 2
@@ -127,8 +148,8 @@ func (s *LedgersProcessorTestSuiteLedger) TestInsertLedgerSucceeds() {
 		},
 	}
 	nextTransactions := []ingest.LedgerTransaction{
-		createTransaction(true, 1),
-		createTransaction(false, 2),
+		createTransaction(true, 1, 2),
+		createTransaction(false, 2, 2),
 	}
 	for _, tx := range nextTransactions {
 		err := s.processor.ProcessTransaction(xdr.LedgerCloseMeta{
diff --git a/services/horizon/internal/ingest/processors/liquidity_pools_transaction_processor_test.go b/services/horizon/internal/ingest/processors/liquidity_pools_transaction_processor_test.go
index 485d890d..8d08e44d 100644
--- a/services/horizon/internal/ingest/processors/liquidity_pools_transaction_processor_test.go
+++ b/services/horizon/internal/ingest/processors/liquidity_pools_transaction_processor_test.go
@@ -68,7 +68,7 @@ func (s *LiquidityPoolsTransactionProcessorTestSuiteLedger) TestEmptyLiquidityPo
 
 func (s *LiquidityPoolsTransactionProcessorTestSuiteLedger) testOperationInserts(poolID xdr.PoolId, body xdr.OperationBody, change xdr.LedgerEntryChange) {
 	// Setup the transaction
-	txn := createTransaction(true, 1)
+	txn := createTransaction(true, 1, 2)
 	txn.Envelope.Operations()[0].Body = body
 	txn.UnsafeMeta.V = 2
 	txn.UnsafeMeta.V2.Operations = []xdr.OperationMeta{
diff --git a/services/horizon/internal/ingest/processors/operations_processor_test.go b/services/horizon/internal/ingest/processors/operations_processor_test.go
index 275a6056..f13641b6 100644
--- a/services/horizon/internal/ingest/processors/operations_processor_test.go
+++ b/services/horizon/internal/ingest/processors/operations_processor_test.go
@@ -42,7 +42,6 @@ func (s *OperationsProcessorTestSuiteLedger) SetupTest() {
 	s.processor = NewOperationProcessor(
 		s.mockBatchInsertBuilder,
 		"test network",
-		false,
 	)
 }
 
@@ -376,65 +375,6 @@ func (s *OperationsProcessorTestSuiteLedger) TestOperationTypeInvokeHostFunction
 		}
 		s.Assert().Equal(found, 4, "should have one balance changed record for each of mint, burn, clawback, transfer")
 	})
-
-	s.T().Run("InvokeContractAssetBalancesElidedFromDetails", func(t *testing.T) {
-		randomIssuer := keypair.MustRandom()
-		randomAsset := xdr.MustNewCreditAsset("TESTING", randomIssuer.Address())
-		passphrase := "passphrase"
-		randomAccount := keypair.MustRandom().Address()
-		contractId := [32]byte{}
-		zeroContractStrKey, err := strkey.Encode(strkey.VersionByteContract, contractId[:])
-		s.Assert().NoError(err)
-
-		transferContractEvent := contractevents.GenerateEvent(contractevents.EventTypeTransfer, randomAccount, zeroContractStrKey, "", randomAsset, big.NewInt(10000000), passphrase)
-		burnContractEvent := contractevents.GenerateEvent(contractevents.EventTypeBurn, zeroContractStrKey, "", "", randomAsset, big.NewInt(10000000), passphrase)
-		mintContractEvent := contractevents.GenerateEvent(contractevents.EventTypeMint, "", zeroContractStrKey, randomAccount, randomAsset, big.NewInt(10000000), passphrase)
-		clawbackContractEvent := contractevents.GenerateEvent(contractevents.EventTypeClawback, zeroContractStrKey, "", randomAccount, randomAsset, big.NewInt(10000000), passphrase)
-
-		tx = ingest.LedgerTransaction{
-			UnsafeMeta: xdr.TransactionMeta{
-				V: 3,
-				V3: &xdr.TransactionMetaV3{
-					SorobanMeta: &xdr.SorobanTransactionMeta{
-						Events: []xdr.ContractEvent{
-							transferContractEvent,
-							burnContractEvent,
-							mintContractEvent,
-							clawbackContractEvent,
-						},
-					},
-				},
-			},
-		}
-		wrapper := transactionOperationWrapper{
-			skipSorobanDetails: true,
-			transaction:        tx,
-			operation: xdr.Operation{
-				SourceAccount: &source,
-				Body: xdr.OperationBody{
-					Type: xdr.OperationTypeInvokeHostFunction,
-					InvokeHostFunctionOp: &xdr.InvokeHostFunctionOp{
-						HostFunction: xdr.HostFunction{
-							Type: xdr.HostFunctionTypeHostFunctionTypeInvokeContract,
-							InvokeContract: &xdr.InvokeContractArgs{
-								ContractAddress: xdr.ScAddress{
-									Type:       xdr.ScAddressTypeScAddressTypeContract,
-									ContractId: &xdr.Hash{0x1, 0x2},
-								},
-								FunctionName: "foo",
-								Args:         xdr.ScVec{},
-							},
-						},
-					},
-				},
-			},
-			network: passphrase,
-		}
-
-		details, err := wrapper.Details()
-		s.Assert().NoError(err)
-		s.Assert().Len(details["asset_balance_changes"], 0, "for invokehostfn op, no asset balances should be in details when skip soroban is enabled")
-	})
 }
 
 func (s *OperationsProcessorTestSuiteLedger) assertInvokeHostFunctionParameter(parameters []map[string]string, paramPosition int, expectedType string, expectedVal xdr.ScVal) {
@@ -467,7 +407,7 @@ func (s *OperationsProcessorTestSuiteLedger) TestAddOperationSucceeds() {
 			Ed25519: *unmuxed.Ed25519,
 		},
 	}
-	firstTx := createTransaction(true, 1)
+	firstTx := createTransaction(true, 1, 2)
 	firstTx.Index = 1
 	firstTx.Envelope.Operations()[0].Body = xdr.OperationBody{
 		Type: xdr.OperationTypePayment,
@@ -478,8 +418,8 @@ func (s *OperationsProcessorTestSuiteLedger) TestAddOperationSucceeds() {
 		},
 	}
 	firstTx.Envelope.V1.Tx.SourceAccount = muxed
-	secondTx := createTransaction(false, 3)
-	thirdTx := createTransaction(true, 4)
+	secondTx := createTransaction(false, 3, 2)
+	thirdTx := createTransaction(true, 4, 2)
 
 	txs := []ingest.LedgerTransaction{
 		firstTx,
@@ -511,7 +451,7 @@ func (s *OperationsProcessorTestSuiteLedger) TestAddOperationFails() {
 			},
 		},
 	}
-	tx := createTransaction(true, 1)
+	tx := createTransaction(true, 1, 2)
 
 	s.mockBatchInsertBuilder.
 		On(
@@ -542,7 +482,7 @@ func (s *OperationsProcessorTestSuiteLedger) TestExecFails() {
 			},
 		},
 	}
-	tx := createTransaction(true, 1)
+	tx := createTransaction(true, 1, 2)
 
 	s.mockBatchInsertBuilder.
 		On(
diff --git a/services/horizon/internal/ingest/processors/participants_processor_test.go b/services/horizon/internal/ingest/processors/participants_processor_test.go
index 2348b79e..b81bd22f 100644
--- a/services/horizon/internal/ingest/processors/participants_processor_test.go
+++ b/services/horizon/internal/ingest/processors/participants_processor_test.go
@@ -62,13 +62,13 @@ func (s *ParticipantsProcessorTestSuiteLedger) SetupTest() {
 		"GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H",
 	}
 
-	s.firstTx = createTransaction(true, 1)
+	s.firstTx = createTransaction(true, 1, 2)
 	s.firstTx.Index = 1
 	aid := xdr.MustAddress(s.addresses[0])
 	s.firstTx.Envelope.V1.Tx.SourceAccount = aid.ToMuxedAccount()
 	s.firstTxID = toid.New(int32(sequence), 1, 0).ToInt64()
 
-	s.secondTx = createTransaction(true, 1)
+	s.secondTx = createTransaction(true, 1, 2)
 	s.secondTx.Index = 2
 	s.secondTx.Envelope.Operations()[0].Body = xdr.OperationBody{
 		Type: xdr.OperationTypeCreateAccount,
@@ -80,7 +80,7 @@ func (s *ParticipantsProcessorTestSuiteLedger) SetupTest() {
 	s.secondTx.Envelope.V1.Tx.SourceAccount = aid.ToMuxedAccount()
 	s.secondTxID = toid.New(int32(sequence), 2, 0).ToInt64()
 
-	s.thirdTx = createTransaction(true, 1)
+	s.thirdTx = createTransaction(true, 1, 2)
 	s.thirdTx.Index = 3
 	aid = xdr.MustAddress(s.addresses[0])
 	s.thirdTx.Envelope.V1.Tx.SourceAccount = aid.ToMuxedAccount()
@@ -150,7 +150,7 @@ func (s *ParticipantsProcessorTestSuiteLedger) TestEmptyParticipants() {
 }
 
 func (s *ParticipantsProcessorTestSuiteLedger) TestFeeBumptransaction() {
-	feeBumpTx := createTransaction(true, 0)
+	feeBumpTx := createTransaction(true, 0, 2)
 	feeBumpTx.Index = 1
 	aid := xdr.MustAddress(s.addresses[0])
 	feeBumpTx.Envelope.V1.Tx.SourceAccount = aid.ToMuxedAccount()
diff --git a/services/horizon/internal/ingest/processors/trades_processor_test.go b/services/horizon/internal/ingest/processors/trades_processor_test.go
index 8a7733f4..d2437588 100644
--- a/services/horizon/internal/ingest/processors/trades_processor_test.go
+++ b/services/horizon/internal/ingest/processors/trades_processor_test.go
@@ -229,7 +229,7 @@ func (s *TradeProcessorTestSuiteLedger) TearDownTest() {
 
 func (s *TradeProcessorTestSuiteLedger) TestIgnoreFailedTransactions() {
 	ctx := context.Background()
-	err := s.processor.ProcessTransaction(s.lcm, createTransaction(false, 1))
+	err := s.processor.ProcessTransaction(s.lcm, createTransaction(false, 1, 2))
 	s.Assert().NoError(err)
 
 	err = s.processor.Flush(ctx, s.mockSession)
@@ -806,7 +806,7 @@ func TestTradeProcessor_ProcessTransaction_MuxedAccount(t *testing.T) {
 			Ed25519: *unmuxed.Ed25519,
 		},
 	}
-	tx := createTransaction(true, 1)
+	tx := createTransaction(true, 1, 2)
 	tx.Index = 1
 	tx.Envelope.Operations()[0].Body = xdr.OperationBody{
 		Type: xdr.OperationTypePayment,
diff --git a/services/horizon/internal/ingest/processors/transaction_operation_wrapper_test.go b/services/horizon/internal/ingest/processors/transaction_operation_wrapper_test.go
index 97b83f8f..48b1f729 100644
--- a/services/horizon/internal/ingest/processors/transaction_operation_wrapper_test.go
+++ b/services/horizon/internal/ingest/processors/transaction_operation_wrapper_test.go
@@ -798,7 +798,7 @@ func TestTransactionOperationDetails(t *testing.T) {
 		})
 	}
 
-	tx := createTransaction(true, 1)
+	tx := createTransaction(true, 1, 2)
 	tx.Index = 1
 
 	tx.Envelope.Operations()[0].Body = xdr.OperationBody{
@@ -1477,7 +1477,7 @@ var (
 
 func getSponsoredSandwichWrappers() []*transactionOperationWrapper {
 	const ledgerSeq = uint32(12345)
-	tx := createTransaction(true, 3)
+	tx := createTransaction(true, 3, 2)
 	tx.Index = 1
 	tx.UnsafeMeta = xdr.TransactionMeta{
 		V: 2,
diff --git a/services/horizon/internal/ingest/processors/transactions_processor_test.go b/services/horizon/internal/ingest/processors/transactions_processor_test.go
index 873a72af..934924ad 100644
--- a/services/horizon/internal/ingest/processors/transactions_processor_test.go
+++ b/services/horizon/internal/ingest/processors/transactions_processor_test.go
@@ -6,11 +6,13 @@ import (
 	"context"
 	"testing"
 
+	"github.com/stellar/go/ingest"
 	"github.com/stellar/go/services/horizon/internal/db2/history"
 	"github.com/stellar/go/support/db"
 	"github.com/stellar/go/support/errors"
 	"github.com/stellar/go/xdr"
 
+	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/suite"
 )
 
@@ -36,7 +38,7 @@ func (s *TransactionsProcessorTestSuiteLedger) TearDownTest() {
 	s.mockBatchInsertBuilder.AssertExpectations(s.T())
 }
 
-func (s *TransactionsProcessorTestSuiteLedger) TestAddTransactionsSucceeds() {
+func (s *TransactionsProcessorTestSuiteLedger) TestAddTransactionsWithMetaSucceeds() {
 	sequence := uint32(20)
 	lcm := xdr.LedgerCloseMeta{
 		V0: &xdr.LedgerCloseMetaV0{
@@ -47,13 +49,55 @@ func (s *TransactionsProcessorTestSuiteLedger) TestAddTransactionsSucceeds() {
 			},
 		},
 	}
-	firstTx := createTransaction(true, 1)
-	secondTx := createTransaction(false, 3)
-	thirdTx := createTransaction(true, 4)
+	creator := xdr.MustAddress("GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H")
+	ledgerEntryChange := xdr.LedgerEntryChange{
+		Type: 3,
+		State: &xdr.LedgerEntry{
+			LastModifiedLedgerSeq: 0x39,
+			Data: xdr.LedgerEntryData{
+				Type: 0,
+				Account: &xdr.AccountEntry{
+					AccountId:     creator,
+					Balance:       800152377009533292,
+					SeqNum:        25,
+					InflationDest: &creator,
+					Thresholds:    xdr.Thresholds{0x1, 0x0, 0x0, 0x0},
+				},
+			},
+		},
+	}
+
+	firstTx := createTransaction(true, 1, 1)
+	firstTx.UnsafeMeta.V1.TxChanges = xdr.LedgerEntryChanges{ledgerEntryChange}
+	secondTx := createTransaction(false, 3, 2)
+	secondTx.UnsafeMeta.V2.TxChangesBefore = xdr.LedgerEntryChanges{ledgerEntryChange}
+	secondTx.UnsafeMeta.V2.TxChangesAfter = xdr.LedgerEntryChanges{ledgerEntryChange}
+	thirdTx := createTransaction(true, 4, 3)
+	thirdTx.UnsafeMeta.V3.TxChangesBefore = xdr.LedgerEntryChanges{ledgerEntryChange}
+	thirdTx.UnsafeMeta.V3.TxChangesAfter = xdr.LedgerEntryChanges{ledgerEntryChange}
+	thirdTx.UnsafeMeta.V3.SorobanMeta = &xdr.SorobanTransactionMeta{}
+
+	s.mockBatchInsertBuilder.On("Add", firstTx, sequence).Run(func(args mock.Arguments) {
+		tx := args.Get(0).(ingest.LedgerTransaction)
+		s.Assert().Len(tx.UnsafeMeta.V1.TxChanges, 1)
+		s.Assert().Len(tx.UnsafeMeta.V1.Operations, 1)
+	}).Return(nil).Once()
+
+	s.mockBatchInsertBuilder.On("Add", secondTx, sequence).Run(func(args mock.Arguments) {
+		tx := args.Get(0).(ingest.LedgerTransaction)
+		s.Assert().Len(tx.UnsafeMeta.V2.TxChangesAfter, 1)
+		s.Assert().Len(tx.UnsafeMeta.V2.TxChangesBefore, 1)
+		s.Assert().Len(tx.UnsafeMeta.V2.Operations, 3)
+	}).Return(nil).Once()
+
+	s.mockBatchInsertBuilder.On("Add", thirdTx, sequence+1).Run(func(args mock.Arguments) {
+		tx := args.Get(0).(ingest.LedgerTransaction)
+		s.Assert().Len(tx.UnsafeMeta.V3.TxChangesAfter, 1)
+		s.Assert().Len(tx.UnsafeMeta.V3.TxChangesBefore, 1)
+		s.Assert().NotNil(tx.UnsafeMeta.V3.SorobanMeta)
+		s.Assert().Len(tx.UnsafeMeta.V3.Operations, 4)
+	}).Return(nil).Once()
 
-	s.mockBatchInsertBuilder.On("Add", firstTx, sequence).Return(nil).Once()
-	s.mockBatchInsertBuilder.On("Add", secondTx, sequence).Return(nil).Once()
-	s.mockBatchInsertBuilder.On("Add", thirdTx, sequence+1).Return(nil).Once()
 	s.mockBatchInsertBuilder.On("Exec", s.ctx, s.mockSession).Return(nil).Once()
 
 	s.Assert().NoError(s.processor.ProcessTransaction(lcm, firstTx))
@@ -64,6 +108,102 @@ func (s *TransactionsProcessorTestSuiteLedger) TestAddTransactionsSucceeds() {
 	s.Assert().NoError(s.processor.Flush(s.ctx, s.mockSession))
 }
 
+func (s *TransactionsProcessorTestSuiteLedger) TestAddTransactionsWithSkippedMetaSucceeds() {
+	elidingTxProcessor := NewTransactionProcessor(s.mockBatchInsertBuilder, true)
+
+	sequence := uint32(20)
+	lcm := xdr.LedgerCloseMeta{
+		V0: &xdr.LedgerCloseMetaV0{
+			LedgerHeader: xdr.LedgerHeaderHistoryEntry{
+				Header: xdr.LedgerHeader{
+					LedgerSeq: xdr.Uint32(sequence),
+				},
+			},
+		},
+	}
+	creator := xdr.MustAddress("GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H")
+	ledgerEntryChange := xdr.LedgerEntryChange{
+		Type: 3,
+		State: &xdr.LedgerEntry{
+			LastModifiedLedgerSeq: 0x39,
+			Data: xdr.LedgerEntryData{
+				Type: 0,
+				Account: &xdr.AccountEntry{
+					AccountId:     creator,
+					Balance:       800152377009533292,
+					SeqNum:        25,
+					InflationDest: &creator,
+					Thresholds:    xdr.Thresholds{0x1, 0x0, 0x0, 0x0},
+				},
+			},
+		},
+	}
+
+	firstTx := createTransaction(true, 1, 1)
+	firstTx.UnsafeMeta.V1.TxChanges = xdr.LedgerEntryChanges{ledgerEntryChange}
+	secondTx := createTransaction(false, 3, 2)
+	secondTx.UnsafeMeta.V2.TxChangesBefore = xdr.LedgerEntryChanges{ledgerEntryChange}
+	secondTx.UnsafeMeta.V2.TxChangesAfter = xdr.LedgerEntryChanges{ledgerEntryChange}
+	thirdTx := createTransaction(true, 4, 3)
+	thirdTx.UnsafeMeta.V3.TxChangesBefore = xdr.LedgerEntryChanges{ledgerEntryChange}
+	thirdTx.UnsafeMeta.V3.TxChangesAfter = xdr.LedgerEntryChanges{ledgerEntryChange}
+	thirdTx.UnsafeMeta.V3.SorobanMeta = &xdr.SorobanTransactionMeta{}
+
+	s.mockBatchInsertBuilder.On("Add", mock.AnythingOfType("ingest.LedgerTransaction"), sequence).Run(func(args mock.Arguments) {
+		tx := args.Get(0).(ingest.LedgerTransaction)
+		s.Assert().Len(tx.UnsafeMeta.V1.TxChanges, 0)
+		s.Assert().Len(tx.UnsafeMeta.V1.Operations, 0)
+	}).Return(nil).Once()
+
+	sequence++
+	s.mockBatchInsertBuilder.On("Add", mock.AnythingOfType("ingest.LedgerTransaction"), sequence).Run(func(args mock.Arguments) {
+		tx := args.Get(0).(ingest.LedgerTransaction)
+		s.Assert().Len(tx.UnsafeMeta.V2.TxChangesAfter, 0)
+		s.Assert().Len(tx.UnsafeMeta.V2.TxChangesBefore, 0)
+		s.Assert().Len(tx.UnsafeMeta.V2.Operations, 0)
+	}).Return(nil).Once()
+
+	sequence++
+	s.mockBatchInsertBuilder.On("Add", mock.AnythingOfType("ingest.LedgerTransaction"), sequence).Run(func(args mock.Arguments) {
+		tx := args.Get(0).(ingest.LedgerTransaction)
+		s.Assert().Len(tx.UnsafeMeta.V3.TxChangesAfter, 0)
+		s.Assert().Len(tx.UnsafeMeta.V3.TxChangesBefore, 0)
+		s.Assert().Nil(tx.UnsafeMeta.V3.SorobanMeta)
+		s.Assert().Len(tx.UnsafeMeta.V3.Operations, 0)
+	}).Return(nil).Once()
+
+	s.mockBatchInsertBuilder.On("Exec", s.ctx, s.mockSession).Return(nil).Once()
+
+	s.Assert().NoError(elidingTxProcessor.ProcessTransaction(lcm, firstTx))
+	lcm.V0.LedgerHeader.Header.LedgerSeq++
+	s.Assert().NoError(elidingTxProcessor.ProcessTransaction(lcm, secondTx))
+	lcm.V0.LedgerHeader.Header.LedgerSeq++
+	s.Assert().NoError(elidingTxProcessor.ProcessTransaction(lcm, thirdTx))
+
+	s.Assert().NoError(elidingTxProcessor.Flush(s.ctx, s.mockSession))
+}
+
+func (s *TransactionsProcessorTestSuiteLedger) TestAddTransactionsWithSkippedMetaFails() {
+	elidingTxProcessor := NewTransactionProcessor(s.mockBatchInsertBuilder, true)
+
+	sequence := uint32(20)
+	lcm := xdr.LedgerCloseMeta{
+		V0: &xdr.LedgerCloseMetaV0{
+			LedgerHeader: xdr.LedgerHeaderHistoryEntry{
+				Header: xdr.LedgerHeader{
+					LedgerSeq: xdr.Uint32(sequence),
+				},
+			},
+		},
+	}
+
+	firstTx := createTransaction(true, 1, 1)
+	// intentionally mangle the transaction to have an invalid tx meta version
+	firstTx.UnsafeMeta.V = 8
+
+	s.Assert().ErrorContains(elidingTxProcessor.ProcessTransaction(lcm, firstTx), "received an un-supported tx-meta version 8")
+}
+
 func (s *TransactionsProcessorTestSuiteLedger) TestAddTransactionsFails() {
 	sequence := uint32(20)
 	lcm := xdr.LedgerCloseMeta{
@@ -75,7 +215,7 @@ func (s *TransactionsProcessorTestSuiteLedger) TestAddTransactionsFails() {
 			},
 		},
 	}
-	firstTx := createTransaction(true, 1)
+	firstTx := createTransaction(true, 1, 2)
 	s.mockBatchInsertBuilder.On("Add", firstTx, sequence).
 		Return(errors.New("transient error")).Once()
 
@@ -95,7 +235,7 @@ func (s *TransactionsProcessorTestSuiteLedger) TestExecFails() {
 			},
 		},
 	}
-	firstTx := createTransaction(true, 1)
+	firstTx := createTransaction(true, 1, 2)
 
 	s.mockBatchInsertBuilder.On("Add", firstTx, sequence).Return(nil).Once()
 	s.mockBatchInsertBuilder.On("Exec", s.ctx, s.mockSession).Return(errors.New("transient error")).Once()
diff --git a/services/horizon/internal/ingest/verify_test.go b/services/horizon/internal/ingest/verify_test.go
index e3c0e4ec..901f21a0 100644
--- a/services/horizon/internal/ingest/verify_test.go
+++ b/services/horizon/internal/ingest/verify_test.go
@@ -292,7 +292,7 @@ func TestStateVerifierLockBusy(t *testing.T) {
 	tt.Assert.NoError(q.BeginTx(tt.Ctx, &sql.TxOptions{}))
 
 	checkpointLedger := uint32(63)
-	changeProcessor := buildChangeProcessor(q, &ingest.StatsChangeProcessor{}, ledgerSource, checkpointLedger, "", false)
+	changeProcessor := buildChangeProcessor(q, &ingest.StatsChangeProcessor{}, ledgerSource, checkpointLedger, "")
 
 	gen := randxdr.NewGenerator()
 	var changes []xdr.LedgerEntryChange
@@ -350,7 +350,7 @@ func TestStateVerifier(t *testing.T) {
 
 	ledger := rand.Int31()
 	checkpointLedger := uint32(ledger - (ledger % 64) - 1)
-	changeProcessor := buildChangeProcessor(q, &ingest.StatsChangeProcessor{}, ledgerSource, checkpointLedger, "", false)
+	changeProcessor := buildChangeProcessor(q, &ingest.StatsChangeProcessor{}, ledgerSource, checkpointLedger, "")
 	mockChangeReader := &ingest.MockChangeReader{}
 
 	gen := randxdr.NewGenerator()
diff --git a/services/horizon/internal/integration/invokehostfunction_test.go b/services/horizon/internal/integration/invokehostfunction_test.go
index 1b1edc09..275f0de2 100644
--- a/services/horizon/internal/integration/invokehostfunction_test.go
+++ b/services/horizon/internal/integration/invokehostfunction_test.go
@@ -3,13 +3,11 @@ package integration
 import (
 	"crypto/sha256"
 	"encoding/hex"
-	"fmt"
 	"os"
 	"path/filepath"
 	"testing"
 
 	"github.com/stellar/go/clients/horizonclient"
-	"github.com/stellar/go/protocols/horizon"
 	"github.com/stellar/go/protocols/horizon/operations"
 	"github.com/stellar/go/services/horizon/internal/test/integration"
 	"github.com/stellar/go/txnbuild"
@@ -26,42 +24,13 @@ const increment_contract = "soroban_increment_contract.wasm"
 // Refer to ./services/horizon/internal/integration/contracts/README.md on how to recompile
 // contract code if needed to new wasm.
 
-func TestInvokeHostFns(t *testing.T) {
-	// first test contracts when soroban processing is enabled
-	DisabledSoroban = false
-	runAllTests(t)
-	// now test same contracts when soroban processing is disabled
-	DisabledSoroban = true
-	runAllTests(t)
-}
-
-func runAllTests(t *testing.T) {
-	tests := []struct {
-		name string
-		fn   func(*testing.T)
-	}{
-		{"CaseContractInvokeHostFunctionInstallContract", CaseContractInvokeHostFunctionInstallContract},
-		{"CaseContractInvokeHostFunctionCreateContractByAddress", CaseContractInvokeHostFunctionCreateContractByAddress},
-		{"CaseContractInvokeHostFunctionInvokeStatelessContractFn", CaseContractInvokeHostFunctionInvokeStatelessContractFn},
-		{"CaseContractInvokeHostFunctionInvokeStatefulContractFn", CaseContractInvokeHostFunctionInvokeStatefulContractFn},
-	}
-
-	for _, tt := range tests {
-		t.Run(fmt.Sprintf("Soroban Processing Disabled = %v. ", DisabledSoroban)+tt.name, func(t *testing.T) {
-			tt.fn(t)
-		})
-	}
-}
-
-func CaseContractInvokeHostFunctionInstallContract(t *testing.T) {
+func TestContractInvokeHostFunctionInstallContract(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -77,7 +46,6 @@ func CaseContractInvokeHostFunctionInstallContract(t *testing.T) {
 
 	clientTx, err := itest.Client().TransactionDetail(tx.Hash)
 	require.NoError(t, err)
-	verifySorobanMeta(t, clientTx)
 
 	assert.Equal(t, tx.Hash, clientTx.Hash)
 	var txResult xdr.TransactionResult
@@ -103,17 +71,16 @@ func CaseContractInvokeHostFunctionInstallContract(t *testing.T) {
 	invokeHostFunctionOpJson, ok := clientInvokeOp.Embedded.Records[0].(operations.InvokeHostFunction)
 	assert.True(t, ok)
 	assert.Equal(t, invokeHostFunctionOpJson.Function, "HostFunctionTypeHostFunctionTypeUploadContractWasm")
+
 }
 
-func CaseContractInvokeHostFunctionCreateContractByAddress(t *testing.T) {
+func TestContractInvokeHostFunctionCreateContractByAddress(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -136,7 +103,6 @@ func CaseContractInvokeHostFunctionCreateContractByAddress(t *testing.T) {
 
 	clientTx, err := itest.Client().TransactionDetail(tx.Hash)
 	require.NoError(t, err)
-	verifySorobanMeta(t, clientTx)
 
 	assert.Equal(t, tx.Hash, clientTx.Hash)
 	var txResult xdr.TransactionResult
@@ -162,15 +128,13 @@ func CaseContractInvokeHostFunctionCreateContractByAddress(t *testing.T) {
 	assert.Equal(t, invokeHostFunctionOpJson.Salt, "110986164698320180327942133831752629430491002266485370052238869825166557303060")
 }
 
-func CaseContractInvokeHostFunctionInvokeStatelessContractFn(t *testing.T) {
+func TestContractInvokeHostFunctionInvokeStatelessContractFn(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -232,7 +196,6 @@ func CaseContractInvokeHostFunctionInvokeStatelessContractFn(t *testing.T) {
 
 	clientTx, err := itest.Client().TransactionDetail(tx.Hash)
 	require.NoError(t, err)
-	verifySorobanMeta(t, clientTx)
 
 	assert.Equal(t, tx.Hash, clientTx.Hash)
 	var txResult xdr.TransactionResult
@@ -246,14 +209,12 @@ func CaseContractInvokeHostFunctionInvokeStatelessContractFn(t *testing.T) {
 	assert.True(t, ok)
 	assert.Equal(t, invokeHostFunctionResult.Code, xdr.InvokeHostFunctionResultCodeInvokeHostFunctionSuccess)
 
-	if !DisabledSoroban {
-		// check the function response, should have summed the two input numbers
-		invokeResult := xdr.Uint64(9)
-		expectedScVal := xdr.ScVal{Type: xdr.ScValTypeScvU64, U64: &invokeResult}
-		var transactionMeta xdr.TransactionMeta
-		assert.NoError(t, xdr.SafeUnmarshalBase64(tx.ResultMetaXdr, &transactionMeta))
-		assert.True(t, expectedScVal.Equals(transactionMeta.V3.SorobanMeta.ReturnValue))
-	}
+	// check the function response, should have summed the two input numbers
+	invokeResult := xdr.Uint64(9)
+	expectedScVal := xdr.ScVal{Type: xdr.ScValTypeScvU64, U64: &invokeResult}
+	var transactionMeta xdr.TransactionMeta
+	assert.NoError(t, xdr.SafeUnmarshalBase64(tx.ResultMetaXdr, &transactionMeta))
+	assert.True(t, expectedScVal.Equals(transactionMeta.V3.SorobanMeta.ReturnValue))
 
 	clientInvokeOp, err := itest.Client().Operations(horizonclient.OperationRequest{
 		ForTransaction: tx.Hash,
@@ -276,15 +237,13 @@ func CaseContractInvokeHostFunctionInvokeStatelessContractFn(t *testing.T) {
 	assert.Equal(t, invokeHostFunctionOpJson.Parameters[3].Type, "U64")
 }
 
-func CaseContractInvokeHostFunctionInvokeStatefulContractFn(t *testing.T) {
+func TestContractInvokeHostFunctionInvokeStatefulContractFn(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -333,7 +292,6 @@ func CaseContractInvokeHostFunctionInvokeStatefulContractFn(t *testing.T) {
 
 	clientTx, err := itest.Client().TransactionDetail(tx.Hash)
 	require.NoError(t, err)
-	verifySorobanMeta(t, clientTx)
 
 	assert.Equal(t, tx.Hash, clientTx.Hash)
 	var txResult xdr.TransactionResult
@@ -347,14 +305,12 @@ func CaseContractInvokeHostFunctionInvokeStatefulContractFn(t *testing.T) {
 	assert.True(t, ok)
 	assert.Equal(t, invokeHostFunctionResult.Code, xdr.InvokeHostFunctionResultCodeInvokeHostFunctionSuccess)
 
-	if !DisabledSoroban {
-		// check the function response, should have incremented state from 0 to 1
-		invokeResult := xdr.Uint32(1)
-		expectedScVal := xdr.ScVal{Type: xdr.ScValTypeScvU32, U32: &invokeResult}
-		var transactionMeta xdr.TransactionMeta
-		assert.NoError(t, xdr.SafeUnmarshalBase64(clientTx.ResultMetaXdr, &transactionMeta))
-		assert.True(t, expectedScVal.Equals(transactionMeta.V3.SorobanMeta.ReturnValue))
-	}
+	// check the function response, should have incremented state from 0 to 1
+	invokeResult := xdr.Uint32(1)
+	expectedScVal := xdr.ScVal{Type: xdr.ScValTypeScvU32, U32: &invokeResult}
+	var transactionMeta xdr.TransactionMeta
+	assert.NoError(t, xdr.SafeUnmarshalBase64(clientTx.ResultMetaXdr, &transactionMeta))
+	assert.True(t, expectedScVal.Equals(transactionMeta.V3.SorobanMeta.ReturnValue))
 
 	clientInvokeOp, err := itest.Client().Operations(horizonclient.OperationRequest{
 		ForTransaction: tx.Hash,
@@ -428,20 +384,3 @@ func assembleCreateContractOp(t *testing.T, sourceAccount string, wasmFileName s
 		SourceAccount: sourceAccount,
 	}
 }
-
-func verifySorobanMeta(t *testing.T, clientTx horizon.Transaction) {
-	var txMeta xdr.TransactionMeta
-	err := xdr.SafeUnmarshalBase64(clientTx.ResultMetaXdr, &txMeta)
-	require.NoError(t, err)
-	require.NotNil(t, txMeta.V3)
-
-	if !DisabledSoroban {
-		require.NotNil(t, txMeta.V3.SorobanMeta)
-		return
-	}
-
-	require.Empty(t, txMeta.V3.Operations)
-	require.Empty(t, txMeta.V3.TxChangesAfter)
-	require.Empty(t, txMeta.V3.TxChangesBefore)
-	require.Nil(t, txMeta.V3.SorobanMeta)
-}
diff --git a/services/horizon/internal/integration/sac_test.go b/services/horizon/internal/integration/sac_test.go
index c790b5a5..64c772b4 100644
--- a/services/horizon/internal/integration/sac_test.go
+++ b/services/horizon/internal/integration/sac_test.go
@@ -2,7 +2,6 @@ package integration
 
 import (
 	"context"
-	"fmt"
 	"math"
 	"math/big"
 	"strings"
@@ -31,127 +30,19 @@ const sac_contract = "soroban_sac_test.wasm"
 // of the integration tests.
 const LongTermTTL = 10000
 
-var (
-	DisabledSoroban bool
-)
-
-func TestSAC(t *testing.T) {
-	// first test contracts when soroban processing is enabled
-	DisabledSoroban = false
-	runAllSACTests(t)
-	// now test same contracts when soroban processing is disabled
-	DisabledSoroban = true
-	runAllSACTests(t)
-}
-
-func runAllSACTests(t *testing.T) {
-	tests := []struct {
-		name string
-		fn   func(*testing.T)
-	}{
-		{"CaseContractMintToAccount", CaseContractMintToAccount},
-		{"CaseContractMintToContract", CaseContractMintToContract},
-		{"CaseExpirationAndRestoration", CaseExpirationAndRestoration},
-		{"CaseContractTransferBetweenAccounts", CaseContractTransferBetweenAccounts},
-		{"CaseContractTransferBetweenAccountAndContract", CaseContractTransferBetweenAccountAndContract},
-		{"CaseContractTransferBetweenContracts", CaseContractTransferBetweenContracts},
-		{"CaseContractBurnFromAccount", CaseContractBurnFromAccount},
-		{"CaseContractBurnFromContract", CaseContractBurnFromContract},
-		{"CaseContractClawbackFromAccount", CaseContractClawbackFromAccount},
-		{"CaseContractClawbackFromContract", CaseContractClawbackFromContract},
-	}
-
-	for _, tt := range tests {
-		t.Run(fmt.Sprintf("Soroban Processing Disabled = %v. ", DisabledSoroban)+tt.name, func(t *testing.T) {
-			tt.fn(t)
-		})
-	}
-}
-
 // Tests use precompiled wasm bin files that are added to the testdata directory.
 // Refer to ./services/horizon/internal/integration/contracts/README.md on how to recompile
 // contract code if needed to new wasm.
 
-func createSAC(itest *integration.Test, asset xdr.Asset) {
-	invokeHostFunction := &txnbuild.InvokeHostFunction{
-		HostFunction: xdr.HostFunction{
-			Type: xdr.HostFunctionTypeHostFunctionTypeCreateContract,
-			CreateContract: &xdr.CreateContractArgs{
-				ContractIdPreimage: xdr.ContractIdPreimage{
-					Type:      xdr.ContractIdPreimageTypeContractIdPreimageFromAsset,
-					FromAsset: &asset,
-				},
-				Executable: xdr.ContractExecutable{
-					Type:     xdr.ContractExecutableTypeContractExecutableStellarAsset,
-					WasmHash: nil,
-				},
-			},
-		},
-		SourceAccount: itest.Master().Address(),
-	}
-	_, _, preFlightOp := assertInvokeHostFnSucceeds(itest, itest.Master(), invokeHostFunction)
-	sourceAccount, extendTTLOp, minFee := itest.PreflightExtendExpiration(
-		itest.Master().Address(),
-		preFlightOp.Ext.SorobanData.Resources.Footprint.ReadWrite,
-		LongTermTTL,
-	)
-	itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &extendTTLOp)
-}
-
-func invokeStoreSet(
-	itest *integration.Test,
-	storeContractID xdr.Hash,
-	ledgerEntryData xdr.LedgerEntryData,
-) *txnbuild.InvokeHostFunction {
-	key := ledgerEntryData.MustContractData().Key
-	val := ledgerEntryData.MustContractData().Val
-	return &txnbuild.InvokeHostFunction{
-		HostFunction: xdr.HostFunction{
-			Type: xdr.HostFunctionTypeHostFunctionTypeInvokeContract,
-			InvokeContract: &xdr.InvokeContractArgs{
-				ContractAddress: contractIDParam(storeContractID),
-				FunctionName:    "set",
-				Args: xdr.ScVec{
-					key,
-					val,
-				},
-			},
-		},
-		SourceAccount: itest.Master().Address(),
-	}
-}
-
-func invokeStoreRemove(
-	itest *integration.Test,
-	storeContractID xdr.Hash,
-	ledgerKey xdr.LedgerKey,
-) *txnbuild.InvokeHostFunction {
-	return &txnbuild.InvokeHostFunction{
-		HostFunction: xdr.HostFunction{
-			Type: xdr.HostFunctionTypeHostFunctionTypeInvokeContract,
-			InvokeContract: &xdr.InvokeContractArgs{
-				ContractAddress: contractIDParam(storeContractID),
-				FunctionName:    "remove",
-				Args: xdr.ScVec{
-					ledgerKey.MustContractData().Key,
-				},
-			},
-		},
-		SourceAccount: itest.Master().Address(),
-	}
-}
-
-func CaseContractMintToAccount(t *testing.T) {
+func TestContractMintToAccount(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban),
-		},
-		EnableSorobanRPC: true,
+		ProtocolVersion:    20,
+		HorizonEnvironment: map[string]string{"INGEST_DISABLE_STATE_VERIFICATION": "true", "CONNECTION_TIMEOUT": "360000"},
+		EnableSorobanRPC:   true,
 	})
 
 	issuer := itest.Master().Address()
@@ -181,21 +72,16 @@ func CaseContractMintToAccount(t *testing.T) {
 		balanceContracts:         big.NewInt(0),
 		contractID:               stellarAssetContractID(itest, asset),
 	})
-	assertEventPayments(itest, mintTx, asset, "", recipient.GetAccountID(), "mint", "20.0000000")
 
-	if !DisabledSoroban {
-		fx := getTxEffects(itest, mintTx, asset)
-		require.Len(t, fx, 1)
-		creditEffect := assertContainsEffect(t, fx,
-			effects.EffectAccountCredited)[0].(effects.AccountCredited)
-		assert.Equal(t, recipientKp.Address(), creditEffect.Account)
-		assert.Equal(t, issuer, creditEffect.Asset.Issuer)
-		assert.Equal(t, code, creditEffect.Asset.Code)
-		assert.Equal(t, "20.0000000", creditEffect.Amount)
-	} else {
-		fx := getTxEffects(itest, mintTx, asset)
-		require.Len(t, fx, 0)
-	}
+	fx := getTxEffects(itest, mintTx, asset)
+	require.Len(t, fx, 1)
+	creditEffect := assertContainsEffect(t, fx,
+		effects.EffectAccountCredited)[0].(effects.AccountCredited)
+	assert.Equal(t, recipientKp.Address(), creditEffect.Account)
+	assert.Equal(t, issuer, creditEffect.Asset.Issuer)
+	assert.Equal(t, code, creditEffect.Asset.Code)
+	assert.Equal(t, "20.0000000", creditEffect.Amount)
+	assertEventPayments(itest, mintTx, asset, "", recipient.GetAccountID(), "mint", "20.0000000")
 
 	otherRecipientKp, otherRecipient := itest.CreateAccount("100")
 	itest.MustEstablishTrustline(otherRecipientKp, otherRecipient, txnbuild.MustAssetFromXDR(asset))
@@ -208,6 +94,12 @@ func CaseContractMintToAccount(t *testing.T) {
 	)
 	assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("20"))
 	assertContainsBalance(itest, otherRecipientKp, issuer, code, amount.MustParse("30"))
+
+	fx = getTxEffects(itest, transferTx, asset)
+	assert.Len(t, fx, 2)
+	assertContainsEffect(t, fx,
+		effects.EffectAccountCredited,
+		effects.EffectAccountDebited)
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -219,28 +111,41 @@ func CaseContractMintToAccount(t *testing.T) {
 		balanceContracts:         big.NewInt(0),
 		contractID:               stellarAssetContractID(itest, asset),
 	})
+}
 
-	if !DisabledSoroban {
-		fx := getTxEffects(itest, transferTx, asset)
-		assert.Len(t, fx, 2)
-		assertContainsEffect(t, fx,
-			effects.EffectAccountCredited,
-			effects.EffectAccountDebited)
-	} else {
-		fx := getTxEffects(itest, transferTx, asset)
-		require.Len(t, fx, 0)
+func createSAC(itest *integration.Test, asset xdr.Asset) {
+	invokeHostFunction := &txnbuild.InvokeHostFunction{
+		HostFunction: xdr.HostFunction{
+			Type: xdr.HostFunctionTypeHostFunctionTypeCreateContract,
+			CreateContract: &xdr.CreateContractArgs{
+				ContractIdPreimage: xdr.ContractIdPreimage{
+					Type:      xdr.ContractIdPreimageTypeContractIdPreimageFromAsset,
+					FromAsset: &asset,
+				},
+				Executable: xdr.ContractExecutable{
+					Type:     xdr.ContractExecutableTypeContractExecutableStellarAsset,
+					WasmHash: nil,
+				},
+			},
+		},
+		SourceAccount: itest.Master().Address(),
 	}
+	_, _, preFlightOp := assertInvokeHostFnSucceeds(itest, itest.Master(), invokeHostFunction)
+	sourceAccount, extendTTLOp, minFee := itest.PreflightExtendExpiration(
+		itest.Master().Address(),
+		preFlightOp.Ext.SorobanData.Resources.Footprint.ReadWrite,
+		LongTermTTL,
+	)
+	itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &extendTTLOp)
 }
 
-func CaseContractMintToContract(t *testing.T) {
+func TestContractMintToContract(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -265,25 +170,19 @@ func CaseContractMintToContract(t *testing.T) {
 			i128Param(int64(mintAmount.Hi), uint64(mintAmount.Lo)),
 			contractAddressParam(recipientContractID)),
 	)
+	assertContainsEffect(t, getTxEffects(itest, mintTx, asset),
+		effects.EffectContractCredited)
 
+	balanceAmount, _, _ := assertInvokeHostFnSucceeds(
+		itest,
+		itest.Master(),
+		contractBalance(itest, issuer, asset, recipientContractID),
+	)
+	assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type)
+	assert.Equal(itest.CurrentTest(), xdr.Uint64(math.MaxUint64-3), (*balanceAmount.I128).Lo)
+	assert.Equal(itest.CurrentTest(), xdr.Int64(math.MaxInt64), (*balanceAmount.I128).Hi)
 	assertEventPayments(itest, mintTx, asset, "", strkeyRecipientContractID, "mint", amount.String128(mintAmount))
 
-	if !DisabledSoroban {
-		assertContainsEffect(t, getTxEffects(itest, mintTx, asset),
-			effects.EffectContractCredited)
-
-		balanceAmount, _, _ := assertInvokeHostFnSucceeds(
-			itest,
-			itest.Master(),
-			contractBalance(itest, issuer, asset, recipientContractID),
-		)
-		assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type)
-		assert.Equal(itest.CurrentTest(), xdr.Uint64(math.MaxUint64-3), (*balanceAmount.I128).Lo)
-		assert.Equal(itest.CurrentTest(), xdr.Int64(math.MaxInt64), (*balanceAmount.I128).Hi)
-	} else {
-		fx := getTxEffects(itest, mintTx, asset)
-		require.Len(t, fx, 0)
-	}
 	// calling transfer from the issuer account will also mint the asset
 	_, transferTx, _ := assertInvokeHostFnSucceeds(
 		itest,
@@ -291,6 +190,19 @@ func CaseContractMintToContract(t *testing.T) {
 		transferWithAmount(itest, issuer, asset, i128Param(0, 3), contractAddressParam(recipientContractID)),
 	)
 
+	assertContainsEffect(t, getTxEffects(itest, transferTx, asset),
+		effects.EffectAccountDebited,
+		effects.EffectContractCredited)
+
+	balanceAmount, _, _ = assertInvokeHostFnSucceeds(
+		itest,
+		itest.Master(),
+		contractBalance(itest, issuer, asset, recipientContractID),
+	)
+	assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type)
+	assert.Equal(itest.CurrentTest(), xdr.Uint64(math.MaxUint64), (*balanceAmount.I128).Lo)
+	assert.Equal(itest.CurrentTest(), xdr.Int64(math.MaxInt64), (*balanceAmount.I128).Hi)
+
 	// 2^127 - 1
 	balanceContracts := new(big.Int).Lsh(big.NewInt(1), 127)
 	balanceContracts.Sub(balanceContracts, big.NewInt(1))
@@ -305,27 +217,9 @@ func CaseContractMintToContract(t *testing.T) {
 		balanceContracts:         balanceContracts,
 		contractID:               stellarAssetContractID(itest, asset),
 	})
-
-	if !DisabledSoroban {
-		assertContainsEffect(t, getTxEffects(itest, transferTx, asset),
-			effects.EffectAccountDebited,
-			effects.EffectContractCredited)
-
-		balanceAmount, _, _ := assertInvokeHostFnSucceeds(
-			itest,
-			itest.Master(),
-			contractBalance(itest, issuer, asset, recipientContractID),
-		)
-		assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type)
-		assert.Equal(itest.CurrentTest(), xdr.Uint64(math.MaxUint64), (*balanceAmount.I128).Lo)
-		assert.Equal(itest.CurrentTest(), xdr.Int64(math.MaxInt64), (*balanceAmount.I128).Hi)
-	} else {
-		fx := getTxEffects(itest, transferTx, asset)
-		require.Len(t, fx, 0)
-	}
 }
 
-func CaseExpirationAndRestoration(t *testing.T) {
+func TestExpirationAndRestoration(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
@@ -338,7 +232,6 @@ func CaseExpirationAndRestoration(t *testing.T) {
 			// a fake asset contract in the horizon db and we don't
 			// want state verification to detect this
 			"ingest-disable-state-verification": "true",
-			"disable-soroban-ingest":            fmt.Sprint(DisabledSoroban),
 		},
 	})
 
@@ -401,7 +294,6 @@ func CaseExpirationAndRestoration(t *testing.T) {
 		LongTermTTL,
 	)
 	itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &extendTTLOp)
-
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -429,16 +321,6 @@ func CaseExpirationAndRestoration(t *testing.T) {
 			balanceToExpire,
 		),
 	)
-
-	balanceToExpireLedgerKey := xdr.LedgerKey{
-		Type: xdr.LedgerEntryTypeContractData,
-		ContractData: &xdr.LedgerKeyContractData{
-			Contract:   balanceToExpire.ContractData.Contract,
-			Key:        balanceToExpire.ContractData.Key,
-			Durability: balanceToExpire.ContractData.Durability,
-		},
-	}
-
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -451,6 +333,14 @@ func CaseExpirationAndRestoration(t *testing.T) {
 		contractID:               storeContractID,
 	})
 
+	balanceToExpireLedgerKey := xdr.LedgerKey{
+		Type: xdr.LedgerEntryTypeContractData,
+		ContractData: &xdr.LedgerKeyContractData{
+			Contract:   balanceToExpire.ContractData.Contract,
+			Key:        balanceToExpire.ContractData.Key,
+			Durability: balanceToExpire.ContractData.Durability,
+		},
+	}
 	// The TESTING_MINIMUM_PERSISTENT_ENTRY_LIFETIME=10 configuration in stellar-core
 	// will ensure that the ledger entry expires after 10 ledgers.
 	// Because ARTIFICIALLY_ACCELERATE_TIME_FOR_TESTING is set to true, 10 ledgers
@@ -482,7 +372,6 @@ func CaseExpirationAndRestoration(t *testing.T) {
 			),
 		),
 	)
-
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -501,7 +390,6 @@ func CaseExpirationAndRestoration(t *testing.T) {
 		balanceToExpireLedgerKey,
 	)
 	itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &restoreFootprint)
-
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -531,7 +419,6 @@ func CaseExpirationAndRestoration(t *testing.T) {
 			),
 		),
 	)
-
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -557,7 +444,6 @@ func CaseExpirationAndRestoration(t *testing.T) {
 			),
 		),
 	)
-
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -571,15 +457,56 @@ func CaseExpirationAndRestoration(t *testing.T) {
 	})
 }
 
-func CaseContractTransferBetweenAccounts(t *testing.T) {
+func invokeStoreSet(
+	itest *integration.Test,
+	storeContractID xdr.Hash,
+	ledgerEntryData xdr.LedgerEntryData,
+) *txnbuild.InvokeHostFunction {
+	key := ledgerEntryData.MustContractData().Key
+	val := ledgerEntryData.MustContractData().Val
+	return &txnbuild.InvokeHostFunction{
+		HostFunction: xdr.HostFunction{
+			Type: xdr.HostFunctionTypeHostFunctionTypeInvokeContract,
+			InvokeContract: &xdr.InvokeContractArgs{
+				ContractAddress: contractIDParam(storeContractID),
+				FunctionName:    "set",
+				Args: xdr.ScVec{
+					key,
+					val,
+				},
+			},
+		},
+		SourceAccount: itest.Master().Address(),
+	}
+}
+
+func invokeStoreRemove(
+	itest *integration.Test,
+	storeContractID xdr.Hash,
+	ledgerKey xdr.LedgerKey,
+) *txnbuild.InvokeHostFunction {
+	return &txnbuild.InvokeHostFunction{
+		HostFunction: xdr.HostFunction{
+			Type: xdr.HostFunctionTypeHostFunctionTypeInvokeContract,
+			InvokeContract: &xdr.InvokeContractArgs{
+				ContractAddress: contractIDParam(storeContractID),
+				FunctionName:    "remove",
+				Args: xdr.ScVec{
+					ledgerKey.MustContractData().Key,
+				},
+			},
+		},
+		SourceAccount: itest.Master().Address(),
+	}
+}
+
+func TestContractTransferBetweenAccounts(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -607,7 +534,6 @@ func CaseContractTransferBetweenAccounts(t *testing.T) {
 	)
 
 	assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("1000"))
-
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -631,6 +557,10 @@ func CaseContractTransferBetweenAccounts(t *testing.T) {
 
 	assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("970"))
 	assertContainsBalance(itest, otherRecipientKp, issuer, code, amount.MustParse("30"))
+
+	fx := getTxEffects(itest, transferTx, asset)
+	assert.NotEmpty(t, fx)
+	assertContainsEffect(t, fx, effects.EffectAccountCredited, effects.EffectAccountDebited)
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -643,26 +573,15 @@ func CaseContractTransferBetweenAccounts(t *testing.T) {
 		contractID:               stellarAssetContractID(itest, asset),
 	})
 	assertEventPayments(itest, transferTx, asset, recipientKp.Address(), otherRecipient.GetAccountID(), "transfer", "30.0000000")
-
-	if !DisabledSoroban {
-		fx := getTxEffects(itest, transferTx, asset)
-		assert.NotEmpty(t, fx)
-		assertContainsEffect(t, fx, effects.EffectAccountCredited, effects.EffectAccountDebited)
-	} else {
-		fx := getTxEffects(itest, transferTx, asset)
-		require.Len(t, fx, 0)
-	}
 }
 
-func CaseContractTransferBetweenAccountAndContract(t *testing.T) {
+func TestContractTransferBetweenAccountAndContract(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -708,6 +627,9 @@ func CaseContractTransferBetweenAccountAndContract(t *testing.T) {
 		mint(itest, issuer, asset, "1000", contractAddressParam(recipientContractID)),
 	)
 	assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("1000"))
+	assertContainsEffect(t, getTxEffects(itest, mintTx, asset),
+		effects.EffectContractCredited)
+
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -720,14 +642,6 @@ func CaseContractTransferBetweenAccountAndContract(t *testing.T) {
 		contractID:               stellarAssetContractID(itest, asset),
 	})
 
-	if !DisabledSoroban {
-		assertContainsEffect(t, getTxEffects(itest, mintTx, asset),
-			effects.EffectContractCredited)
-	} else {
-		fx := getTxEffects(itest, mintTx, asset)
-		require.Len(t, fx, 0)
-	}
-
 	// transfer from account to contract
 	_, transferTx, _ := assertInvokeHostFnSucceeds(
 		itest,
@@ -735,6 +649,8 @@ func CaseContractTransferBetweenAccountAndContract(t *testing.T) {
 		transfer(itest, recipientKp.Address(), asset, "30", contractAddressParam(recipientContractID)),
 	)
 	assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("970"))
+	assertContainsEffect(t, getTxEffects(itest, transferTx, asset),
+		effects.EffectAccountDebited, effects.EffectContractCredited)
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -748,19 +664,14 @@ func CaseContractTransferBetweenAccountAndContract(t *testing.T) {
 	})
 	assertEventPayments(itest, transferTx, asset, recipientKp.Address(), strkeyRecipientContractID, "transfer", "30.0000000")
 
-	if !DisabledSoroban {
-		assertContainsEffect(t, getTxEffects(itest, transferTx, asset),
-			effects.EffectAccountDebited, effects.EffectContractCredited)
-	} else {
-		fx := getTxEffects(itest, transferTx, asset)
-		require.Len(t, fx, 0)
-	}
 	// transfer from contract to account
 	_, transferTx, _ = assertInvokeHostFnSucceeds(
 		itest,
 		recipientKp,
 		transferFromContract(itest, recipientKp.Address(), asset, recipientContractID, recipientContractHash, "500", accountAddressParam(recipient.GetAccountID())),
 	)
+	assertContainsEffect(t, getTxEffects(itest, transferTx, asset),
+		effects.EffectContractDebited, effects.EffectAccountCredited)
 	assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("1470"))
 	assertAssetStats(itest, assetStats{
 		code:                     code,
@@ -775,13 +686,6 @@ func CaseContractTransferBetweenAccountAndContract(t *testing.T) {
 	})
 	assertEventPayments(itest, transferTx, asset, strkeyRecipientContractID, recipientKp.Address(), "transfer", "500.0000000")
 
-	if DisabledSoroban {
-		fx := getTxEffects(itest, transferTx, asset)
-		require.Len(t, fx, 0)
-		return
-	}
-	assertContainsEffect(t, getTxEffects(itest, transferTx, asset),
-		effects.EffectContractDebited, effects.EffectAccountCredited)
 	balanceAmount, _, _ := assertInvokeHostFnSucceeds(
 		itest,
 		itest.Master(),
@@ -792,15 +696,13 @@ func CaseContractTransferBetweenAccountAndContract(t *testing.T) {
 	assert.Equal(itest.CurrentTest(), xdr.Int64(0), (*balanceAmount.I128).Hi)
 }
 
-func CaseContractTransferBetweenContracts(t *testing.T) {
+func TestContractTransferBetweenContracts(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -840,28 +742,8 @@ func CaseContractTransferBetweenContracts(t *testing.T) {
 		itest.Master(),
 		transferFromContract(itest, issuer, asset, emitterContractID, emitterContractHash, "10", contractAddressParam(recipientContractID)),
 	)
-
-	assertAssetStats(itest, assetStats{
-		code:                     code,
-		issuer:                   issuer,
-		numAccounts:              0,
-		balanceAccounts:          0,
-		balanceArchivedContracts: big.NewInt(0),
-		numArchivedContracts:     0,
-		numContracts:             2,
-		balanceContracts:         big.NewInt(int64(amount.MustParse("1000"))),
-		contractID:               stellarAssetContractID(itest, asset),
-	})
-	assertEventPayments(itest, transferTx, asset, strkeyEmitterContractID, strkeyRecipientContractID, "transfer", "10.0000000")
-
-	if !DisabledSoroban {
-		assertContainsEffect(t, getTxEffects(itest, transferTx, asset),
-			effects.EffectContractCredited, effects.EffectContractDebited)
-	} else {
-		fx := getTxEffects(itest, transferTx, asset)
-		require.Len(t, fx, 0)
-		return
-	}
+	assertContainsEffect(t, getTxEffects(itest, transferTx, asset),
+		effects.EffectContractCredited, effects.EffectContractDebited)
 
 	// Check balances of emitter and recipient
 	emitterBalanceAmount, _, _ := assertInvokeHostFnSucceeds(
@@ -881,17 +763,28 @@ func CaseContractTransferBetweenContracts(t *testing.T) {
 	assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, recipientBalanceAmount.Type)
 	assert.Equal(itest.CurrentTest(), xdr.Uint64(100000000), (*recipientBalanceAmount.I128).Lo)
 	assert.Equal(itest.CurrentTest(), xdr.Int64(0), (*recipientBalanceAmount.I128).Hi)
+
+	assertAssetStats(itest, assetStats{
+		code:                     code,
+		issuer:                   issuer,
+		numAccounts:              0,
+		balanceAccounts:          0,
+		balanceArchivedContracts: big.NewInt(0),
+		numArchivedContracts:     0,
+		numContracts:             2,
+		balanceContracts:         big.NewInt(int64(amount.MustParse("1000"))),
+		contractID:               stellarAssetContractID(itest, asset),
+	})
+	assertEventPayments(itest, transferTx, asset, strkeyEmitterContractID, strkeyRecipientContractID, "transfer", "10.0000000")
 }
 
-func CaseContractBurnFromAccount(t *testing.T) {
+func TestContractBurnFromAccount(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -937,6 +830,16 @@ func CaseContractBurnFromAccount(t *testing.T) {
 		burn(itest, recipientKp.Address(), asset, "500"),
 	)
 
+	fx := getTxEffects(itest, burnTx, asset)
+	require.Len(t, fx, 1)
+	assetEffects := assertContainsEffect(t, fx, effects.EffectAccountDebited)
+	require.GreaterOrEqual(t, len(assetEffects), 1)
+	burnEffect := assetEffects[0].(effects.AccountDebited)
+
+	assert.Equal(t, issuer, burnEffect.Asset.Issuer)
+	assert.Equal(t, code, burnEffect.Asset.Code)
+	assert.Equal(t, "500.0000000", burnEffect.Amount)
+	assert.Equal(t, recipientKp.Address(), burnEffect.Account)
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -949,33 +852,15 @@ func CaseContractBurnFromAccount(t *testing.T) {
 		contractID:               stellarAssetContractID(itest, asset),
 	})
 	assertEventPayments(itest, burnTx, asset, recipientKp.Address(), "", "burn", "500.0000000")
-
-	if !DisabledSoroban {
-		fx := getTxEffects(itest, burnTx, asset)
-		require.Len(t, fx, 1)
-		assetEffects := assertContainsEffect(t, fx, effects.EffectAccountDebited)
-		require.GreaterOrEqual(t, len(assetEffects), 1)
-		burnEffect := assetEffects[0].(effects.AccountDebited)
-
-		assert.Equal(t, issuer, burnEffect.Asset.Issuer)
-		assert.Equal(t, code, burnEffect.Asset.Code)
-		assert.Equal(t, "500.0000000", burnEffect.Amount)
-		assert.Equal(t, recipientKp.Address(), burnEffect.Account)
-	} else {
-		fx := getTxEffects(itest, burnTx, asset)
-		require.Len(t, fx, 0)
-	}
 }
 
-func CaseContractBurnFromContract(t *testing.T) {
+func TestContractBurnFromContract(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -1010,6 +895,19 @@ func CaseContractBurnFromContract(t *testing.T) {
 		burnSelf(itest, issuer, asset, recipientContractID, recipientContractHash, "10"),
 	)
 
+	balanceAmount, _, _ := assertInvokeHostFnSucceeds(
+		itest,
+		itest.Master(),
+		contractBalance(itest, issuer, asset, recipientContractID),
+	)
+
+	assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type)
+	assert.Equal(itest.CurrentTest(), xdr.Uint64(9900000000), (*balanceAmount.I128).Lo)
+	assert.Equal(itest.CurrentTest(), xdr.Int64(0), (*balanceAmount.I128).Hi)
+
+	assertContainsEffect(t, getTxEffects(itest, burnTx, asset),
+		effects.EffectContractDebited)
+
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -1022,35 +920,15 @@ func CaseContractBurnFromContract(t *testing.T) {
 		contractID:               stellarAssetContractID(itest, asset),
 	})
 	assertEventPayments(itest, burnTx, asset, strkeyRecipientContractID, "", "burn", "10.0000000")
-
-	if !DisabledSoroban {
-		balanceAmount, _, _ := assertInvokeHostFnSucceeds(
-			itest,
-			itest.Master(),
-			contractBalance(itest, issuer, asset, recipientContractID),
-		)
-
-		assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type)
-		assert.Equal(itest.CurrentTest(), xdr.Uint64(9900000000), (*balanceAmount.I128).Lo)
-		assert.Equal(itest.CurrentTest(), xdr.Int64(0), (*balanceAmount.I128).Hi)
-
-		assertContainsEffect(t, getTxEffects(itest, burnTx, asset),
-			effects.EffectContractDebited)
-	} else {
-		fx := getTxEffects(itest, burnTx, asset)
-		require.Len(t, fx, 0)
-	}
 }
 
-func CaseContractClawbackFromAccount(t *testing.T) {
+func TestContractClawbackFromAccount(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -1088,7 +966,6 @@ func CaseContractClawbackFromAccount(t *testing.T) {
 	)
 
 	assertContainsBalance(itest, recipientKp, issuer, code, amount.MustParse("1000"))
-
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -1106,6 +983,8 @@ func CaseContractClawbackFromAccount(t *testing.T) {
 		itest.Master(),
 		clawback(itest, issuer, asset, "1000", accountAddressParam(recipientKp.Address())),
 	)
+
+	assertContainsEffect(t, getTxEffects(itest, clawTx, asset), effects.EffectAccountDebited)
 	assertContainsBalance(itest, recipientKp, issuer, code, 0)
 	assertAssetStats(itest, assetStats{
 		code:                     code,
@@ -1119,24 +998,15 @@ func CaseContractClawbackFromAccount(t *testing.T) {
 		contractID:               stellarAssetContractID(itest, asset),
 	})
 	assertEventPayments(itest, clawTx, asset, recipientKp.Address(), "", "clawback", "1000.0000000")
-
-	if !DisabledSoroban {
-		assertContainsEffect(t, getTxEffects(itest, clawTx, asset), effects.EffectAccountDebited)
-	} else {
-		fx := getTxEffects(itest, clawTx, asset)
-		require.Len(t, fx, 0)
-	}
 }
 
-func CaseContractClawbackFromContract(t *testing.T) {
+func TestContractClawbackFromContract(t *testing.T) {
 	if integration.GetCoreMaxSupportedProtocol() < 20 {
 		t.Skip("This test run does not support less than Protocol 20")
 	}
 
 	itest := integration.NewTest(t, integration.Config{
-		ProtocolVersion: 20,
-		HorizonEnvironment: map[string]string{
-			"DISABLE_SOROBAN_INGEST": fmt.Sprint(DisabledSoroban)},
+		ProtocolVersion:  20,
 		EnableSorobanRPC: true,
 	})
 
@@ -1174,6 +1044,19 @@ func CaseContractClawbackFromContract(t *testing.T) {
 		itest.Master(),
 		clawback(itest, issuer, asset, "10", contractAddressParam(recipientContractID)),
 	)
+
+	balanceAmount, _, _ := assertInvokeHostFnSucceeds(
+		itest,
+		itest.Master(),
+		contractBalance(itest, issuer, asset, recipientContractID),
+	)
+	assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type)
+	assert.Equal(itest.CurrentTest(), xdr.Uint64(9900000000), (*balanceAmount.I128).Lo)
+	assert.Equal(itest.CurrentTest(), xdr.Int64(0), (*balanceAmount.I128).Hi)
+
+	assertContainsEffect(t, getTxEffects(itest, clawTx, asset),
+		effects.EffectContractDebited)
+
 	assertAssetStats(itest, assetStats{
 		code:                     code,
 		issuer:                   issuer,
@@ -1186,23 +1069,6 @@ func CaseContractClawbackFromContract(t *testing.T) {
 		contractID:               stellarAssetContractID(itest, asset),
 	})
 	assertEventPayments(itest, clawTx, asset, strkeyRecipientContractID, "", "clawback", "10.0000000")
-
-	if !DisabledSoroban {
-		balanceAmount, _, _ := assertInvokeHostFnSucceeds(
-			itest,
-			itest.Master(),
-			contractBalance(itest, issuer, asset, recipientContractID),
-		)
-		assert.Equal(itest.CurrentTest(), xdr.ScValTypeScvI128, balanceAmount.Type)
-		assert.Equal(itest.CurrentTest(), xdr.Uint64(9900000000), (*balanceAmount.I128).Lo)
-		assert.Equal(itest.CurrentTest(), xdr.Int64(0), (*balanceAmount.I128).Hi)
-
-		assertContainsEffect(t, getTxEffects(itest, clawTx, asset),
-			effects.EffectContractDebited)
-	} else {
-		fx := getTxEffects(itest, clawTx, asset)
-		require.Len(t, fx, 0)
-	}
 }
 
 func assertContainsBalance(itest *integration.Test, acct *keypair.Full, issuer, code string, amt xdr.Int64) {
@@ -1313,12 +1179,6 @@ func assertEventPayments(itest *integration.Test, txHash string, asset xdr.Asset
 
 	invokeHostFn := ops.Embedded.Records[0].(operations.InvokeHostFunction)
 	assert.Equal(itest.CurrentTest(), invokeHostFn.Function, "HostFunctionTypeHostFunctionTypeInvokeContract")
-
-	if DisabledSoroban {
-		require.Equal(itest.CurrentTest(), 0, len(invokeHostFn.AssetBalanceChanges))
-		return
-	}
-
 	require.Equal(itest.CurrentTest(), 1, len(invokeHostFn.AssetBalanceChanges))
 	assetBalanceChange := invokeHostFn.AssetBalanceChanges[0]
 	assert.Equal(itest.CurrentTest(), assetBalanceChange.Amount, amount)
@@ -1540,6 +1400,10 @@ func assertInvokeHostFnSucceeds(itest *integration.Test, signer *keypair.Full, o
 	err = xdr.SafeUnmarshalBase64(clientTx.ResultXdr, &txResult)
 	require.NoError(itest.CurrentTest(), err)
 
+	var txMetaResult xdr.TransactionMeta
+	err = xdr.SafeUnmarshalBase64(clientTx.ResultMetaXdr, &txMetaResult)
+	require.NoError(itest.CurrentTest(), err)
+
 	opResults, ok := txResult.OperationResults()
 	assert.True(itest.CurrentTest(), ok)
 	assert.Equal(itest.CurrentTest(), len(opResults), 1)
@@ -1547,18 +1411,9 @@ func assertInvokeHostFnSucceeds(itest *integration.Test, signer *keypair.Full, o
 	assert.True(itest.CurrentTest(), ok)
 	assert.Equal(itest.CurrentTest(), invokeHostFunctionResult.Code, xdr.InvokeHostFunctionResultCodeInvokeHostFunctionSuccess)
 
-	var returnValue *xdr.ScVal
-
-	if !DisabledSoroban {
-		var txMetaResult xdr.TransactionMeta
-		err = xdr.SafeUnmarshalBase64(clientTx.ResultMetaXdr, &txMetaResult)
-		require.NoError(itest.CurrentTest(), err)
-		returnValue = &txMetaResult.MustV3().SorobanMeta.ReturnValue
-	} else {
-		verifySorobanMeta(itest.CurrentTest(), clientTx)
-	}
+	returnValue := txMetaResult.MustV3().SorobanMeta.ReturnValue
 
-	return returnValue, clientTx.Hash, &preFlightOp
+	return &returnValue, clientTx.Hash, &preFlightOp
 }
 
 func stellarAssetContractID(itest *integration.Test, asset xdr.Asset) xdr.Hash {
diff --git a/services/horizon/internal/integration/transaction_test.go b/services/horizon/internal/integration/transaction_test.go
new file mode 100644
index 00000000..85fbe785
--- /dev/null
+++ b/services/horizon/internal/integration/transaction_test.go
@@ -0,0 +1,134 @@
+package integration
+
+import (
+	"testing"
+
+	"github.com/stellar/go/clients/horizonclient"
+	"github.com/stellar/go/services/horizon/internal/test/integration"
+	"github.com/stellar/go/txnbuild"
+	"github.com/stellar/go/xdr"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestP19MetaTransaction(t *testing.T) {
+	itest := integration.NewTest(t, integration.Config{
+		ProtocolVersion:  19,
+		EnableSorobanRPC: false,
+	})
+
+	masterAccount, err := itest.Client().AccountDetail(horizonclient.AccountRequest{
+		AccountID: itest.Master().Address(),
+	})
+	require.NoError(t, err)
+
+	op := &txnbuild.Payment{
+		SourceAccount: itest.Master().Address(),
+		Destination:   itest.Master().Address(),
+		Asset:         txnbuild.NativeAsset{},
+		Amount:        "10",
+	}
+
+	clientTx := itest.MustSubmitOperations(&masterAccount, itest.Master(), op)
+
+	var txMetaResult xdr.TransactionMeta
+	err = xdr.SafeUnmarshalBase64(clientTx.ResultMetaXdr, &txMetaResult)
+	require.NoError(t, err)
+
+	assert.Greater(t, len(txMetaResult.MustV2().Operations), 0)
+	assert.Greater(t, len(txMetaResult.MustV2().TxChangesBefore), 0)
+	// TODO figure out how to generate TxChangesAfter also
+	//assert.Greater(t, len(txMetaResult.MustV2().TxChangesAfter), 0)
+}
+
+func TestP19MetaDisabledTransaction(t *testing.T) {
+	itest := integration.NewTest(t, integration.Config{
+		ProtocolVersion:    19,
+		HorizonEnvironment: map[string]string{"SKIP_TXMETA": "TRUE"},
+		EnableSorobanRPC:   false,
+	})
+
+	masterAccount, err := itest.Client().AccountDetail(horizonclient.AccountRequest{
+		AccountID: itest.Master().Address(),
+	})
+	require.NoError(t, err)
+
+	op := &txnbuild.Payment{
+		SourceAccount: itest.Master().Address(),
+		Destination:   itest.Master().Address(),
+		Asset:         txnbuild.NativeAsset{},
+		Amount:        "10",
+	}
+
+	clientTx := itest.MustSubmitOperations(&masterAccount, itest.Master(), op)
+
+	var txMetaResult xdr.TransactionMeta
+	err = xdr.SafeUnmarshalBase64(clientTx.ResultMetaXdr, &txMetaResult)
+	require.NoError(t, err)
+
+	assert.Equal(t, len(txMetaResult.MustV2().Operations), 0)
+	assert.Equal(t, len(txMetaResult.MustV2().TxChangesAfter), 0)
+	assert.Equal(t, len(txMetaResult.MustV2().TxChangesBefore), 0)
+}
+
+func TestP20MetaTransaction(t *testing.T) {
+	if integration.GetCoreMaxSupportedProtocol() < 20 {
+		t.Skip("This test run does not support less than Protocol 20")
+	}
+
+	itest := integration.NewTest(t, integration.Config{
+		ProtocolVersion:  20,
+		EnableSorobanRPC: true,
+	})
+
+	// establish which account will be contract owner, and load it's current seq
+	sourceAccount, err := itest.Client().AccountDetail(horizonclient.AccountRequest{
+		AccountID: itest.Master().Address(),
+	})
+	require.NoError(t, err)
+
+	installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), add_u64_contract)
+	preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp)
+	clientTx := itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp)
+
+	var txMetaResult xdr.TransactionMeta
+	err = xdr.SafeUnmarshalBase64(clientTx.ResultMetaXdr, &txMetaResult)
+	require.NoError(t, err)
+
+	assert.Greater(t, len(txMetaResult.MustV3().Operations), 0)
+	assert.NotNil(t, txMetaResult.MustV3().SorobanMeta)
+	assert.Greater(t, len(txMetaResult.MustV3().TxChangesAfter), 0)
+	assert.Greater(t, len(txMetaResult.MustV3().TxChangesBefore), 0)
+}
+
+func TestP20MetaDisabledTransaction(t *testing.T) {
+	if integration.GetCoreMaxSupportedProtocol() < 20 {
+		t.Skip("This test run does not support less than Protocol 20")
+	}
+
+	itest := integration.NewTest(t, integration.Config{
+		ProtocolVersion:    20,
+		HorizonEnvironment: map[string]string{"SKIP_TXMETA": "TRUE"},
+		EnableSorobanRPC:   true,
+	})
+
+	// establish which account will be contract owner, and load it's current seq
+	sourceAccount, err := itest.Client().AccountDetail(horizonclient.AccountRequest{
+		AccountID: itest.Master().Address(),
+	})
+	require.NoError(t, err)
+
+	installContractOp := assembleInstallContractCodeOp(t, itest.Master().Address(), add_u64_contract)
+	preFlightOp, minFee := itest.PreflightHostFunctions(&sourceAccount, *installContractOp)
+	clientTx := itest.MustSubmitOperationsWithFee(&sourceAccount, itest.Master(), minFee+txnbuild.MinBaseFee, &preFlightOp)
+
+	var txMetaResult xdr.TransactionMeta
+	err = xdr.SafeUnmarshalBase64(clientTx.ResultMetaXdr, &txMetaResult)
+	require.NoError(t, err)
+
+	assert.Equal(t, len(txMetaResult.MustV3().Operations), 0)
+	assert.Nil(t, txMetaResult.MustV3().SorobanMeta)
+	assert.Equal(t, len(txMetaResult.MustV3().TxChangesAfter), 0)
+	assert.Equal(t, len(txMetaResult.MustV3().TxChangesBefore), 0)
+}