Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Token Transfer Processor - Derive classic events from operation and operation results #5609

Draft
wants to merge 45 commits into
base: master
Choose a base branch
from

Conversation

karthikiyer56
Copy link
Contributor

@karthikiyer56 karthikiyer56 commented Feb 19, 2025

This PR deals with deriving TokenTransferEvents from operation and operation results as described in #5580
This PR is strictly dealing with classic and classic operations, i.e the PR implements the ask from #5592

@karthikiyer56 karthikiyer56 force-pushed the karthik/ttp-classic-operations branch from 36946d4 to 571679c Compare February 20, 2025 23:41
@@ -592,31 +596,6 @@ func (t *LedgerTransaction) AccountMuxed() (string, bool) {

}

func (t *LedgerTransaction) FeeAccount() (string, bool) {
Copy link
Contributor Author

@karthikiyer56 karthikiyer56 Feb 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of the function seems to suggest that this function will give the underlying fee account - be it a regular transaction or a fee bump transaction.
IMO, this is very misleading.

  • I found that it behaved contrary to what the name suggested when my code was not working as expected, in that if it the tx is not a fee bump tx, it returns "".
  • xxxAccount being the function name, i'd expect the return type to be xdr.MuxedAccount, to be consistent with other functions here.
    Besides, I dont see it being used anywhere other than in a sample unit test.
    I am removing this function and the function below so as to not cause further confusion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with your assessment but we should sync with @chowbao to make sure etl isn't impacted by this change


import (
"github.com/stellar/go/ingest"
addressProto "github.com/stellar/go/ingest/address"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is the real deal and needs to be reviewed thoroughly.
Adding a comment to ensure that this doesnt slip up - since the load diff might not render the file.

)

var (
someTxAccount = xdr.MustMuxedAddress("GBF3XFXGBGNQDN3HOSZ7NVRF6TJ2JOD5U6ELIWJOOEI6T5WKMQT2YSXQ")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several variables here that I am using throughout the test, and so they have been clubbed here at the top.
They, IMHO, improve the code readability when reviewing the tests, and I am not inclined to move them to individual tests, but rather have them at the top-level here

@@ -18,6 +22,240 @@ import (
"github.com/stellar/go/xdr"
)

func TestLiquidityPoolHappyPath2(t *testing.T) {
Copy link
Contributor Author

@karthikiyer56 karthikiyer56 Mar 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the changes in this integration test are purely for PoC.
will be removed later

return generatedClaimableBalanceIds, nil
}

func getClaimableBalanceEntriesFromOperationChanges(tx ingest.LedgerTransaction, opIndex uint32) ([]xdr.ClaimableBalanceEntry, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function is used to find claimable balances that were either created or removed. however, the caller expects the returned list to never have a mix between the two types. I think it would be better to add a xdr.LedgerEntryChangeType parameter which can be used to filter for the exact change types desired by the caller (e.g. only created claimable balances or only removed claimable balances)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing that this is an internal function, I see little chance for misuse/mis-representation of what this function is intended to do.
at any rate, I have changed the function to accept a changeType xdr.LedgerEntryChangeType and added a comment at the top.
callers of the function will either pass - LedgerEntryChangeTypeLedgerEntryRemoved or LedgerEntryChangeTypeLedgerEntryCreated. It will fail when anything else is passed

Comment on lines 551 to 553
if len(entries) == 0 {
return nil, ErrNoLiquidityPoolEntryFound
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think it's better style to return the empty list without an error so the caller can decide how the empty list should be handled. it's not obvious from the name of the function that at least 1 liquidity pool should be present and anything otherwise would be an error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reworked to do error/length check at call-site instead of inside the function

Comment on lines 305 to 307
if len(entries) == 0 {
return nil, ErrNoClaimableBalanceEntryFound
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, same comment as ErrNoLiquidityPoolEntryFound:

I think it's better style to return the empty list without an error so the caller can decide how the empty list should be handled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reworked to do error/length check at call-site instead of inside the function

@karthikiyer56 karthikiyer56 changed the title Initial boiler plate commit for token transfer processor Token Transfer Processor - Derive classic events from operation and operation results Mar 4, 2025
@karthikiyer56 karthikiyer56 requested review from a team March 4, 2025 07:01
return generateEventsForRevokedTrustlines(tx, opIndex)
}

func generateEventsForRevokedTrustlines(tx ingest.LedgerTransaction, opIndex uint32) ([]*TokenTransferEvent, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the dooziest logic of all the code in this file.
I have written unit tests that test this ad-nauseum
This is the only part of the code where I have felt the need to be over-liberal with comments to indicate what the code is doing, since it is very non-trivial how the Cbs are generated from LPs when trustline revocation of an asset happens for a trustor account

Copy link
Contributor

@tamirms tamirms Mar 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the logic and it looks correct to me. I think the code would be easier to understand if you refactored it to avoid iterating over impactedLiquidityPools twice, but that's just my personal opinion. Here's my attempt to simplify the code:

ab27384

feel free to take or reject whichever parts you see fit

)

/*
Helper function to convert LiquidityPoolId to generate ClaimableBalanceIds in a deterministic way as per
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed a place to park this generic logic that generates CBids from LPids.
I use this in the code and also in the unit tests.

It didnt make sense to subsume it under token_transfer_processor, so parking it here under a new directory -converters under support
Happy to move this to another place, so long as this is a public function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would put it either in the xdr package or along with the xdrill code in the ingest package. Also, I suggest using a more concise name for the function, maybe, ClaimableBalanceIdFromRevocation or BalanceIdFromRevocation?

Comment on lines +263 to +265
if !(changeType == xdr.LedgerEntryChangeTypeLedgerEntryRemoved || changeType == xdr.LedgerEntryChangeTypeLedgerEntryCreated) {
return nil, fmt.Errorf("changeType: %v, not allowed", changeType)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this error is necessary. The function will obtain all cb changes occurring from an operation filtered by a change type. The fact that the function happens to be called only for removed or created changes is something determined by the caller

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh nevermind, I just realized that with updates it's not clear which claimable balance (either pre or post) to include. you can ignore this comment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could simplify the if statement condition to:

Suggested change
if !(changeType == xdr.LedgerEntryChangeTypeLedgerEntryRemoved || changeType == xdr.LedgerEntryChangeTypeLedgerEntryCreated) {
return nil, fmt.Errorf("changeType: %v, not allowed", changeType)
}
if changeType == xdr.LedgerEntryChangeTypeLedgerEntryUpdated {
return nil, fmt.Errorf("LEDGER_ENTRY_UPDATED is not a valid filter")
}

Comment on lines +281 to +292
if change.Type != xdr.LedgerEntryTypeClaimableBalance {
continue
}
// Check if claimable balance entry is deleted
//?? maybe it is not necessary to check change.Pre != nil && change.Post since that will be true for deleted entries?
if changeType == xdr.LedgerEntryChangeTypeLedgerEntryRemoved && change.Pre != nil && change.Post == nil {
cb = change.Pre.Data.MustClaimableBalance()
entries = append(entries, cb)
} else if changeType == xdr.LedgerEntryChangeTypeLedgerEntryCreated && change.Post != nil && change.Pre == nil { // check if claimable balance entry is created
cb = change.Post.Data.MustClaimableBalance()
entries = append(entries, cb)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if change.Type != xdr.LedgerEntryTypeClaimableBalance {
continue
}
// Check if claimable balance entry is deleted
//?? maybe it is not necessary to check change.Pre != nil && change.Post since that will be true for deleted entries?
if changeType == xdr.LedgerEntryChangeTypeLedgerEntryRemoved && change.Pre != nil && change.Post == nil {
cb = change.Pre.Data.MustClaimableBalance()
entries = append(entries, cb)
} else if changeType == xdr.LedgerEntryChangeTypeLedgerEntryCreated && change.Post != nil && change.Pre == nil { // check if claimable balance entry is created
cb = change.Post.Data.MustClaimableBalance()
entries = append(entries, cb)
}
if change.Type != xdr.LedgerEntryTypeClaimableBalance || change.LedgerEntryChangeType() != changeType {
continue
}
if change.Pre != nil {
cb = change.Pre.Data.MustClaimableBalance()
entries = append(entries, cb)
} else if change.Post != nil {
cb = change.Post.Data.MustClaimableBalance()
entries = append(entries, cb)
}

amtA, amtB := delta.amountChangeForAssetA, delta.amountChangeForAssetB
if amtA <= 0 {
return nil,
fmt.Errorf("deposited amount (%v) for asset: %v, cannot be negative in LiquidityPool: %v", amtA, assetA.String(), lpIdToStrkey(lpId))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error string indicates negative values are not allowed but the if statement is actually including 0 values as well in the illegal state

var (
xlmProtoAsset = assetProto.NewNativeAsset()
ErrNoLiquidityPoolEntryFound = errors.New("no liquidity pool entry found in operation changes")
ErrNoClaimableBalanceEntryFound = errors.New("no claimable balance entry found in operation changes")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is no longer used


func lpIdToStrkey(lpId xdr.PoolId) string {
return xdr.Hash(lpId).HexString()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file is quite long, it would be easier to read the code if the code in this file was split into multiple files. maybe this could be refactored into three files:

  • one for path payment and offer related operations
  • one for revocation and liquidity pool operations
  • one for everything else

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.

Generate TokenTransferEvents from operation and operation results for classic operations
2 participants