@@ -3,9 +3,9 @@ package token_transfer
33import (
44 "fmt"
55 "io"
6+ "math/big"
67
78 "github.com/google/go-cmp/cmp"
8- "github.com/stellar/go-stellar-sdk/amount"
99 "github.com/stellar/go-stellar-sdk/ingest"
1010 "github.com/stellar/go-stellar-sdk/strkey"
1111 "github.com/stellar/go-stellar-sdk/xdr"
@@ -18,18 +18,25 @@ type balanceKey struct {
1818}
1919
2020// updateBalanceMap updates the map and removes the entry if the value becomes 0
21- func updateBalanceMap (m map [balanceKey ]int64 , key balanceKey , delta int64 ) {
21+ func updateBalanceMap (m map [balanceKey ]* big. Int , key balanceKey , delta * big. Int ) {
2222 // We dont include movement to/from contract address is balance delta tracking, since there is no standard way to derive/verify from contractData
2323 if strkey .IsValidContractAddress (key .holder ) {
2424 return
2525 }
26- m [key ] += delta
27- if m [key ] == 0 {
28- delete (m , key )
26+ if delta .Sign () == 0 {
27+ return
28+ }
29+ if existing , ok := m [key ]; ok {
30+ existing .Add (existing , delta )
31+ if existing .Sign () == 0 {
32+ delete (m , key )
33+ }
34+ } else {
35+ m [key ] = new (big.Int ).Set (delta )
2936 }
3037}
3138
32- func fetchAccountDeltaFromChange (change ingest.Change , m map [balanceKey ]int64 ) {
39+ func fetchAccountDeltaFromChange (change ingest.Change , m map [balanceKey ]* big. Int ) {
3340 var accountKey string
3441 var pre , post xdr.Int64
3542
@@ -44,11 +51,11 @@ func fetchAccountDeltaFromChange(change ingest.Change, m map[balanceKey]int64) {
4451 post = entry .Balance
4552 }
4653
47- delta := int64 (post - pre )
54+ delta := big . NewInt ( int64 (post - pre ) )
4855 updateBalanceMap (m , balanceKey {holder : accountKey , asset : xlmAsset .StringCanonical ()}, delta )
4956}
5057
51- func fetchTrustlineDeltaFromChange (change ingest.Change , m map [balanceKey ]int64 ) {
58+ func fetchTrustlineDeltaFromChange (change ingest.Change , m map [balanceKey ]* big. Int ) {
5259 var trustlineKey string
5360 var asset string
5461 var pre , post xdr.Int64
@@ -72,11 +79,11 @@ func fetchTrustlineDeltaFromChange(change ingest.Change, m map[balanceKey]int64)
7279 asset = entry .Asset .ToAsset ().StringCanonical ()
7380 }
7481
75- delta := int64 (post - pre )
82+ delta := big . NewInt ( int64 (post - pre ) )
7683 updateBalanceMap (m , balanceKey {holder : trustlineKey , asset : asset }, delta )
7784}
7885
79- func fetchClaimableDeltaFromChange (change ingest.Change , m map [balanceKey ]int64 ) {
86+ func fetchClaimableDeltaFromChange (change ingest.Change , m map [balanceKey ]* big. Int ) {
8087 var cbKey string
8188 var asset string
8289 var pre , post xdr.Int64
@@ -94,11 +101,11 @@ func fetchClaimableDeltaFromChange(change ingest.Change, m map[balanceKey]int64)
94101 post = entry .Amount
95102 }
96103
97- delta := int64 (post - pre )
104+ delta := big . NewInt ( int64 (post - pre ) )
98105 updateBalanceMap (m , balanceKey {holder : cbKey , asset : asset }, delta )
99106}
100107
101- func fetchLiquidityPoolDeltaFromChange (change ingest.Change , m map [balanceKey ]int64 ) {
108+ func fetchLiquidityPoolDeltaFromChange (change ingest.Change , m map [balanceKey ]* big. Int ) {
102109 var lpKey string
103110 var assetA , assetB string
104111 var preA , preB , postA , postB xdr.Int64
@@ -119,16 +126,16 @@ func fetchLiquidityPoolDeltaFromChange(change ingest.Change, m map[balanceKey]in
119126 postA , postB = cp .ReserveA , cp .ReserveB
120127 }
121128
122- deltaA := int64 (postA - preA )
123- deltaB := int64 (postB - preB )
129+ deltaA := big . NewInt ( int64 (postA - preA ) )
130+ deltaB := big . NewInt ( int64 (postB - preB ) )
124131
125132 updateBalanceMap (m , balanceKey {holder : lpKey , asset : assetA }, deltaA )
126133 updateBalanceMap (m , balanceKey {holder : lpKey , asset : assetB }, deltaB )
127134}
128135
129136// findBalanceDeltasFromChanges aggregates all balance changes from ledger entry changes
130- func findBalanceDeltasFromChanges (changes []ingest.Change ) map [balanceKey ]int64 {
131- hashmap := make (map [balanceKey ]int64 )
137+ func findBalanceDeltasFromChanges (changes []ingest.Change ) map [balanceKey ]* big. Int {
138+ hashmap := make (map [balanceKey ]* big. Int )
132139 for _ , change := range changes {
133140 switch change .Type {
134141 case xdr .LedgerEntryTypeAccount :
@@ -144,9 +151,18 @@ func findBalanceDeltasFromChanges(changes []ingest.Change) map[balanceKey]int64
144151 return hashmap
145152}
146153
154+ // parseAmount parses a raw amount string into a *big.Int.
155+ func parseAmount (amountStr string , eventType string ) (* big.Int , error ) {
156+ amt , ok := new (big.Int ).SetString (amountStr , 10 )
157+ if ! ok {
158+ return nil , fmt .Errorf ("invalid amount %q in %s event" , amountStr , eventType )
159+ }
160+ return amt , nil
161+ }
162+
147163// findBalanceDeltasFromEvents aggregates all balance changes from token transfer events
148- func findBalanceDeltasFromEvents (events []* TokenTransferEvent ) map [balanceKey ]int64 {
149- hashmap := make (map [balanceKey ]int64 )
164+ func findBalanceDeltasFromEvents (events []* TokenTransferEvent ) ( map [balanceKey ]* big. Int , error ) {
165+ hashmap := make (map [balanceKey ]* big. Int )
150166
151167 for _ , event := range events {
152168 if event .GetAsset () == nil { // needed check for custom token events which won't have an asset
@@ -158,50 +174,65 @@ func findBalanceDeltasFromEvents(events []*TokenTransferEvent) map[balanceKey]in
158174 ev := event .GetFee ()
159175 address := ev .From
160176 asset := xlmAsset .StringCanonical ()
161- amt := amount .MustParseInt64Raw (ev .Amount )
177+ amt , err := parseAmount (ev .Amount , string (event .GetEventType ()))
178+ if err != nil {
179+ return nil , err
180+ }
162181 // Address' balance reduces by amt in FEE
163- updateBalanceMap (hashmap , balanceKey {holder : address , asset : asset }, - amt )
182+ updateBalanceMap (hashmap , balanceKey {holder : address , asset : asset }, new (big. Int ). Neg ( amt ) )
164183
165184 case * TokenTransferEvent_Transfer :
166185 ev := event .GetTransfer ()
167186 fromAddress := ev .From
168187 toAddress := ev .To
169- amt := amount .MustParseInt64Raw (ev .Amount )
188+ amt , err := parseAmount (ev .Amount , string (event .GetEventType ()))
189+ if err != nil {
190+ return nil , err
191+ }
170192 asset := event .GetAsset ().ToXdrAsset ().StringCanonical ()
171193 // FromAddress' balance reduces by amt in TRANSFER
172- updateBalanceMap (hashmap , balanceKey {holder : fromAddress , asset : asset }, - amt )
194+ updateBalanceMap (hashmap , balanceKey {holder : fromAddress , asset : asset }, new (big. Int ). Neg ( amt ) )
173195 // ToAddress' balance increases by amt in TRANSFER
174196 updateBalanceMap (hashmap , balanceKey {holder : toAddress , asset : asset }, amt )
175197
176198 case * TokenTransferEvent_Mint :
177199 ev := event .GetMint ()
178200 toAddress := ev .To
179201 asset := event .GetAsset ().ToXdrAsset ().StringCanonical ()
180- amt := amount .MustParseInt64Raw (ev .Amount )
202+ amt , err := parseAmount (ev .Amount , string (event .GetEventType ()))
203+ if err != nil {
204+ return nil , err
205+ }
181206 // ToAddress' balance increases by amt in MINT
182207 updateBalanceMap (hashmap , balanceKey {holder : toAddress , asset : asset }, amt )
183208
184209 case * TokenTransferEvent_Burn :
185210 ev := event .GetBurn ()
186211 fromAddress := ev .From
187212 asset := event .GetAsset ().ToXdrAsset ().StringCanonical ()
188- amt := amount .MustParseInt64Raw (ev .Amount )
213+ amt , err := parseAmount (ev .Amount , string (event .GetEventType ()))
214+ if err != nil {
215+ return nil , err
216+ }
189217 // FromAddress' balance reduces by amt in BURN
190- updateBalanceMap (hashmap , balanceKey {holder : fromAddress , asset : asset }, - amt )
218+ updateBalanceMap (hashmap , balanceKey {holder : fromAddress , asset : asset }, new (big. Int ). Neg ( amt ) )
191219
192220 case * TokenTransferEvent_Clawback :
193221 ev := event .GetClawback ()
194222 fromAddress := ev .From
195223 asset := event .GetAsset ().ToXdrAsset ().StringCanonical ()
196- amt := amount .MustParseInt64Raw (ev .Amount )
224+ amt , err := parseAmount (ev .Amount , string (event .GetEventType ()))
225+ if err != nil {
226+ return nil , err
227+ }
197228 // FromAddress' balance reduces by amt in CLAWBACK
198- updateBalanceMap (hashmap , balanceKey {holder : fromAddress , asset : asset }, - amt )
229+ updateBalanceMap (hashmap , balanceKey {holder : fromAddress , asset : asset }, new (big. Int ). Neg ( amt ) )
199230
200231 default :
201- panic ( fmt .Errorf ("unknown event type %s" , event .GetEventType () ))
232+ return nil , fmt .Errorf ("unknown event type %s" , event .GetEventType ())
202233 }
203234 }
204- return hashmap
235+ return hashmap , nil
205236}
206237
207238func VerifyEvents (ledger xdr.LedgerCloseMeta , passphrase string , readFromUnifiedEvents bool ) error {
@@ -216,6 +247,13 @@ func VerifyEvents(ledger xdr.LedgerCloseMeta, passphrase string, readFromUnified
216247 return fmt .Errorf ("error creating transaction reader: %w" , err )
217248 }
218249
250+ bigIntComparer := cmp .Comparer (func (a , b * big.Int ) bool {
251+ if a == nil || b == nil {
252+ return a == b
253+ }
254+ return a .Cmp (b ) == 0
255+ })
256+
219257 for {
220258 var tx ingest.LedgerTransaction
221259 var events []* TokenTransferEvent
@@ -246,10 +284,13 @@ func VerifyEvents(ledger xdr.LedgerCloseMeta, passphrase string, readFromUnified
246284 changes := append (feeChanges , txChanges ... )
247285 changes = append (changes , postTxApplyFeeChanges ... )
248286
249- txEventsMap := findBalanceDeltasFromEvents (events )
287+ txEventsMap , err := findBalanceDeltasFromEvents (events )
288+ if err != nil {
289+ return fmt .Errorf ("verifyEventsError: %w" , err )
290+ }
250291 txChangesMap := findBalanceDeltasFromChanges (changes )
251292
252- if diff := cmp .Diff (txEventsMap , txChangesMap ); diff != "" {
293+ if diff := cmp .Diff (txEventsMap , txChangesMap , bigIntComparer ); diff != "" {
253294 return fmt .Errorf ("balance delta mismatch between events and ledger changes for ledgerSequence: %v, closedAt: %v, txHash: %v\n " +
254295 "('-' indicates missing or different in events, '+' indicates missing or different in ledger changes)\n %s" , ledger .LedgerSequence (), ledger .ClosedAt (), txHash , diff )
255296 }
0 commit comments