Skip to content

[ANCHOR-1242]: Contract sub invocation transfers are skipped#1971

Draft
amandagonsalves wants to merge 2 commits into
developfrom
fix/anchor-1242
Draft

[ANCHOR-1242]: Contract sub invocation transfers are skipped#1971
amandagonsalves wants to merge 2 commits into
developfrom
fix/anchor-1242

Conversation

@amandagonsalves

Copy link
Copy Markdown
Collaborator

Description

Follow-up: credit Variant B instead of skipping it. The first pass logged an errorF and skipped when no matching operation was found. The Soroban transfer event's 4th topic is already the SEP-11 asset string - shouldProcess() already decodes it into ShouldProcessResult.sep11Asset, unused. org.stellar.sdk.Asset.create(String) parses that exact format back into an Asset, then .toXdr() - the same method SacToAssetMapper.getAssetFromSac itself already uses for the same conversion, just from a different source string.

Changes

  • StellarRpcPaymentObserver.processTransferEvent: replaced txn.getOperations().get(operationIndex) with a TOID-based identity lookup - derives wantedOpId from new TOID(sequenceNumber, applicationOrder, operationIndex + 1).toInt64(), then stream-filters txn.getOperations() by getOperationId(o).equals(wantedOpId). Adds import org.stellar.sdk.TOID.
  • StellarRpcPaymentObserver.getOperationId: new private helper that extracts the id field from whichever sub-operation (paymentOperation, pathPaymentOperation, or invokeHostFunctionOperation) is set on a LedgerOperation.
  • StellarRpcPaymentObserver.buildSubInvocationOperation: new private helper - when the TOID lookup finds no match, builds a synthetic LedgerInvokeHostFunctionOperation directly from the transfer event's already-decoded fromAddr/toAddr/amount/sep11Asset fields. processTransferEvent replaces txn's (filtered-empty) operations list with this single synthetic operation and falls through to processOperation unchanged, so the synthetic payment flows through the exact same SEP-6/24/31 matching and crediting pipeline as any other payment. Logs infoF on success; only logs errorF/skips if the event's asset string can't be parsed at all (not expected for a protocol-compliant event).
  • StellarRpcPaymentObserver.processOperation: guarded the INVOKE_HOST_FUNCTION case's SAC lookup (if (invokeOp.getAsset(false) == null)) so it doesn't discard the asset already resolved from the event, mirroring the guard DefaultPaymentListener.validate() already uses for the same call.
  • StellarRpcPaymentObserverTest: rewrote the sub-invocation "skip" test into a "credit" test (asserts handleEvent is called with the correct from/to/amount/operationId); added a new test for the genuine give-up path (malformed event asset string → skipped, no throw).
  • StellarRpcObserverIndexDomainTest: updated the dedicated sub-invocation test and the mixed-batch test to assert crediting instead of skipping.

Acceptance Criteria

  • A two-operation transaction [MANAGE_DATA, PAYMENT→dist] with transfer event at operationIndex=1 is credited with the correct from/to/amount/operationId.
  • A transfer event whose corresponding full-list operation is absent from the compacted list (contract sub-invocation) is credited via a synthetic operation built from the event's own fields, with the correct from/to/amount/operationId.
  • A transfer event with an unparseable asset string is skipped without throwing, and the observer stays RUNNING.
  • A mixed batch containing a normal single-op, a multi-op, and a sub-invocation event processes all three without crashing, crediting all three.
  • Observer status remains RUNNING after any of the above scenarios.
  • Existing StellarRpcPaymentObserverTest and StellarRpcObserverPoisonResilienceTest tests continue to pass unchanged.
  • Horizon mode (HorizonPaymentObserver) is unaffected.

Context

#3791580

Testing

  • Unit: ./gradlew :platform:test --tests "org.stellar.anchor.platform.observer.stellar.StellarRpcPaymentObserverTest"
  • Integration: ./gradlew :platform:test --tests "org.stellar.anchor.platform.observer.stellar.StellarRpcObserverIndexDomainTest"

Documentation

N/A

Known limitations

N/A

* add `buildsubinvocationoperation` to create synthetic operations for contract sub-invocation transfers.

* update `processtransferevent` to credit sub-invocation transfers that lack a direct top-level operation.

* update `buildpaymenttransferevent` to conditionally map sac to asset.

* add tests to verify the crediting of sub-invocation transfers.

* add tests to verify skipping sub-invocation transfers with malformed asset data.
@amandagonsalves amandagonsalves changed the title fix(stellar-rpc-observer): credit sub-invocation transfers [ANCHOR-1242]: Contract sub invocation transfers are skipped Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant