github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/ledger/stmnt_from_log.go (about) 1 package ledger 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 8 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/colors" 9 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 10 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 11 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 12 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils" 13 ) 14 15 var transferTopic = base.HexToHash( 16 "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", 17 ) 18 19 var ErrNonIndexedTransfer = fmt.Errorf("non-indexed transfer") 20 21 // getStatementsFromLog returns a statement from a given log 22 func (l *Ledger) getStatementsFromLog(conn *rpc.Connection, logIn *types.Log) (types.Statement, error) { 23 if logIn.Topics[0] != transferTopic { 24 // Not a transfer 25 return types.Statement{}, nil 26 } 27 28 if log, err := l.normalizeTransfer(logIn); err != nil { 29 return types.Statement{}, err 30 31 } else { 32 sym := log.Address.Prefix(6) 33 decimals := base.Value(18) 34 name := l.Names[log.Address] 35 if name.Address == log.Address { 36 if name.Symbol != "" { 37 sym = name.Symbol 38 } 39 if name.Decimals != 0 { 40 decimals = base.Value(name.Decimals) 41 } 42 } 43 44 sender := base.HexToAddress(log.Topics[1].Hex()) 45 recipient := base.HexToAddress(log.Topics[2].Hex()) 46 var amountIn, amountOut base.Wei 47 var amt *base.Wei 48 if amt, _ = new(base.Wei).SetString(strings.Replace(log.Data, "0x", "", -1), 16); amt == nil { 49 amt = base.NewWei(0) 50 } 51 52 ofInterest := false 53 54 // Do not collapse, may be both 55 if l.AccountFor == sender { 56 amountOut = *amt 57 ofInterest = true 58 } 59 60 // Do not collapse, may be both 61 if l.AccountFor == recipient { 62 amountIn = *amt 63 ofInterest = true 64 } 65 66 s := types.Statement{ 67 AccountedFor: l.AccountFor, 68 Sender: sender, 69 Recipient: recipient, 70 BlockNumber: log.BlockNumber, 71 TransactionIndex: log.TransactionIndex, 72 LogIndex: log.LogIndex, 73 TransactionHash: log.TransactionHash, 74 Timestamp: log.Timestamp, 75 AssetAddr: log.Address, 76 AssetSymbol: sym, 77 Decimals: decimals, 78 SpotPrice: 0.0, 79 PriceSource: "not-priced", 80 AmountIn: amountIn, 81 AmountOut: amountOut, 82 } 83 84 // TODO: BOGUS PERF - WE HIT GETBALANCE THREE TIMES FOR EACH APPEARANCE. SPIN THROUGH ONCE 85 // TODO: AND CACHE RESULTS IN MEMORY, BUT BE CAREFUL OF MULTIPLE LOGS PER BLOCK (OR TRANSACTION) 86 key := l.ctxKey(log.BlockNumber, log.TransactionIndex) 87 ctx := l.Contexts[key] 88 89 if ofInterest { 90 var err error 91 pBal := new(base.Wei) 92 if pBal, err = conn.GetBalanceAtToken(log.Address, l.AccountFor, fmt.Sprintf("0x%x", ctx.PrevBlock)); pBal == nil { 93 return s, err 94 } 95 s.PrevBal = *pBal 96 97 bBal := new(base.Wei) 98 if bBal, err = conn.GetBalanceAtToken(log.Address, l.AccountFor, fmt.Sprintf("0x%x", ctx.CurBlock-1)); bBal == nil { 99 return s, err 100 } 101 s.BegBal = *bBal 102 103 eBal := new(base.Wei) 104 if eBal, err = conn.GetBalanceAtToken(log.Address, l.AccountFor, fmt.Sprintf("0x%x", ctx.CurBlock)); eBal == nil { 105 return s, err 106 } 107 s.EndBal = *eBal 108 109 id := fmt.Sprintf(" %d.%d.%d", s.BlockNumber, s.TransactionIndex, s.LogIndex) 110 if !l.trialBalance("token", &s) { 111 if !utils.IsFuzzing() { 112 logger.Warn(colors.Yellow+"Log statement at ", id, " does not reconcile."+colors.Off) 113 } 114 } else { 115 if !utils.IsFuzzing() { 116 logger.Progress(true, colors.Green+"Transaction", id, "reconciled "+colors.Off) 117 } 118 } 119 } 120 121 return s, nil 122 } 123 } 124 125 func (l *Ledger) normalizeTransfer(log *types.Log) (*types.Log, error) { 126 if len(log.Topics) < 3 { 127 // Transfer(address _from, address _to, uint256 _tokenId) - no indexed topics 128 // Transfer(address indexed _from, address indexed _to, uint256 _value) - two indexed topics 129 // Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId) - three indexed topics 130 // TODO: This may be a transfer. Returning here is wrong. What this means is that 131 // TODO:the some of the transfer's data is not indexed. Too short topics happens 132 // TODO: (sometimes) because the ABI says that the data is not index, but it is 133 // TODO: or visa versa. In either case, we get the same topic0. We need to 134 // TODO: attempt both with and without indexed parameters. See issues/1366. 135 // TODO: We could fix this and call back in recursively... 136 return nil, ErrNonIndexedTransfer 137 } 138 139 return log, nil 140 }