Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8204459
Updates for tagging
tjayrush Sep 28, 2025
814dde3
Update to v5.9.3
tjayrush Sep 28, 2025
9b7312f
Updates go.mods
tjayrush Sep 28, 2025
657c430
Adds sorts to Approval type
tjayrush Sep 28, 2025
336e805
Auto generate app_types.go
tjayrush Sep 28, 2025
cbb3b32
Updates go.mods
tjayrush Sep 28, 2025
4f014a0
Better auto-code-gen
tjayrush Sep 28, 2025
a53848a
Cleaning up one of the examples
tjayrush Oct 4, 2025
c3d4097
Adding RawMap/CalcMap to types for use in the SDK and apps.
tjayrush Oct 4, 2025
21741d8
Adding RawMap/CalcMap to types for use in the SDK and apps.
tjayrush Oct 4, 2025
f5880bb
Adding RawMap/CalcMap to transfer
tjayrush Oct 5, 2025
92dfe44
Adding RawMap/CalcMap to trace and related
tjayrush Oct 5, 2025
17a5a74
Adding RawMap/CalcMap to statement
tjayrush Oct 5, 2025
240ea71
Adding RawMap/CalcMap to token (and tests)
tjayrush Oct 5, 2025
1bc69e8
Adding RawMap/CalcMap to withdrawal
tjayrush Oct 5, 2025
df3c3cc
Adding RawMap/CalcMap to slurp (including updating to new EtherScan apis
tjayrush Oct 5, 2025
480c58b
Adding RawMap/CalcMap to state, status, and timestamp
tjayrush Oct 5, 2025
7c92edc
Adding RawMap/CalcMap to rangedates, receipts, reportchecks, and results
tjayrush Oct 5, 2025
ed16a77
Test
tjayrush Oct 6, 2025
f018182
Updates tests
tjayrush Oct 6, 2025
f74a521
Adding RawMap/CalcMap to the rest of the types
tjayrush Oct 6, 2025
704d5fa
Uses new RawMap/CalcMap
tjayrush Oct 6, 2025
7554734
Consistency improvements
tjayrush Oct 6, 2025
fad9960
Consistency improvements
tjayrush Oct 6, 2025
79956fb
Consistency improvements
tjayrush Oct 6, 2025
ec9f63c
Cleaning
tjayrush Oct 6, 2025
1e575cc
Adds commnents to CalcMap so we kno what needs to be calced for each …
tjayrush Oct 7, 2025
1aa9c38
Adds EnsureCalc and Calcs pointer to all types
tjayrush Oct 8, 2025
6a5771a
Fixes lints
tjayrush Oct 8, 2025
7d20fcf
chifra export --approvals return Log not Transaction
tjayrush Oct 14, 2025
81cbe6a
Sorting
tjayrush Oct 14, 2025
65e3e56
Back to Transactions for chifra export --approvals
tjayrush Oct 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.9.1
5.9.3
46 changes: 25 additions & 21 deletions docs/content/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ info:
license:
name: GPL 3.0
url: http://www.gnu.org/licenses/
version: 5.9.1
version: 5.9.3
description: >
A REST layer over the TrueBlocks chifra command line. With `chifra daemon`, you can
run this on your own machine, and make calls to `localhost`.
Expand Down Expand Up @@ -3586,46 +3586,50 @@ components:
description: "an ERC-20 token approval granting spending permission from owner to spender"
type: object
properties:
allowance:
type: string
format: wei
description: "the amount of tokens approved for spending"
blockNumber:
type: number
format: blknum
description: "the block number at which this call was made"
description: "the current block number when the report was generated"
timestamp:
type: number
format: timestamp
description: "the timestamp of the block for this call"
description: "the current timestamp when the report was generated"
date:
type: string
format: datetime
description: "the timestamp as a date (calculated)"
address:
owner:
type: string
format: address
description: "the address of the owner of the token (the approver)"
addressName:
type: string
format: string
description: "the name of the holder, if available"
spender:
type: string
format: address
description: "the address being granted approval to spend tokens"
spenderName:
type: string
format: string
description: "the name of the spender, if available"
amount:
type: string
format: wei
description: "the amount of tokens approved for spending"
token:
type: string
format: address
description: "the address of the ERC-20 token being approved"
tokenName:
type: string
format: string
description: "the name of the token, if available"
lastAppBlock:
type: number
format: blknum
description: "the block number of the last approval event"
lastAppTs:
type: number
format: timestamp
description: "the timestamp of the last approval event"
lastAppTxID:
type: number
format: txnum
description: "the transaction index of the last approval event"
lastAppLogID:
type: number
format: lognum
description: "the log index of the last approval event"
appearanceTable:
description: "an appearance table for an address"
type: object
Expand Down Expand Up @@ -4415,7 +4419,7 @@ components:
date:
type: string
format: datetime
description: "date when this contract state was last updated"
description: "date when this contract state was last updated (calculated)"
errorCount:
type: number
format: int64
Expand Down
25 changes: 13 additions & 12 deletions docs/content/data-model/accounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,18 +266,19 @@ The following commands produce and manage Approvals:

Approvals consist of the following fields:

| Field | Description | Type |
| ----------- | ---------------------------------------------------- | --------- |
| blockNumber | the block number at which this call was made | blknum |
| timestamp | the timestamp of the block for this call | timestamp |
| date | the timestamp as a date (calculated) | datetime |
| address | the address of the owner of the token (the approver) | address |
| addressName | the name of the holder, if available | string |
| spender | the address being granted approval to spend tokens | address |
| spenderName | the name of the spender, if available | string |
| amount | the amount of tokens approved for spending | wei |
| token | the address of the ERC-20 token being approved | address |
| tokenName | the name of the token, if available | string |
| Field | Description | Type |
| ------------ | ------------------------------------------------------ | --------- |
| allowance | the amount of tokens approved for spending | wei |
| blockNumber | the current block number when the report was generated | blknum |
| timestamp | the current timestamp when the report was generated | timestamp |
| date | the timestamp as a date (calculated) | datetime |
| owner | the address of the owner of the token (the approver) | address |
| spender | the address being granted approval to spend tokens | address |
| token | the address of the ERC-20 token being approved | address |
| lastAppBlock | the block number of the last approval event | blknum |
| lastAppTs | the timestamp of the last approval event | timestamp |
| lastAppTxID | the transaction index of the last approval event | txnum |
| lastAppLogID | the log index of the last approval event | lognum |

## AppearanceTable

Expand Down
18 changes: 9 additions & 9 deletions docs/content/data-model/chainstate.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,15 @@ The following commands produce and manage Contracts:

Contracts consist of the following fields:

| Field | Description | Type |
| ----------- | -------------------------------------------------------- | ----------------------------- |
| address | the address of this smart contract | address |
| name | the name of this contract (if available) | string |
| abi | the ABI for this contract | [Abi](/data-model/other/#abi) |
| lastUpdated | timestamp when this contract state was last updated | timestamp |
| date | date when this contract state was last updated | datetime |
| errorCount | number of errors encountered when calling read functions | int64 |
| lastError | the most recent error message when calling functions | string |
| Field | Description | Type |
| ----------- | ----------------------------------------------------------- | ----------------------------- |
| address | the address of this smart contract | address |
| name | the name of this contract (if available) | string |
| abi | the ABI for this contract | [Abi](/data-model/other/#abi) |
| lastUpdated | timestamp when this contract state was last updated | timestamp |
| date | date when this contract state was last updated (calculated) | datetime |
| errorCount | number of errors encountered when calling read functions | int64 |
| lastError | the most recent error message when calling functions | string |

## Base types

Expand Down
2 changes: 1 addition & 1 deletion khedra
Submodule khedra updated 2 files
+2 −2 go.mod
+2 −2 go.sum
2 changes: 1 addition & 1 deletion release-name.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v5.9.1 Too Much To List - Lancaster
v5.9.3 Approvals - Lancaster
72 changes: 47 additions & 25 deletions src/apps/chifra/internal/export/handle_approvals.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ func (opts *ExportOptions) HandleApprovals(rCtx *output.RenderCtx, monitorArray
abiCache := articulate.NewAbiCache(opts.Conn, opts.Articulate)
filter := types.NewFilter(
opts.Reversed,
opts.Reverted,
opts.Fourbytes,
false,
[]string{topics.ApprovalFourbyte},
ranges.BlockRange{First: opts.FirstBlock, Last: opts.LastBlock},
ranges.RecordRange{First: opts.FirstRecord, Last: opts.GetMax()},
)
Expand All @@ -37,10 +37,9 @@ func (opts *ExportOptions) HandleApprovals(rCtx *output.RenderCtx, monitorArray
addrArray = append(addrArray, mon.Address)
}
logFilter := rpc.NewLogFilter(opts.Emitter, []string{topics.ApprovalTopic.Hex()})
opts.Fourbytes = []string{topics.ApprovalFourbyte.Hex()}

for _, mon := range monitorArray {
if sliceOfMaps, cnt, err := monitor.AsSliceOfItemMaps[[]*types.Log](&mon, filter, filter.Reversed); err != nil {
if sliceOfMaps, cnt, err := monitor.AsSliceOfItemMaps[types.Transaction](&mon, filter, filter.Reversed); err != nil {
errorChan <- err
rCtx.Cancel()

Expand All @@ -67,25 +66,45 @@ func (opts *ExportOptions) HandleApprovals(rCtx *output.RenderCtx, monitorArray
}

for app := range thisMap {
thisMap[app] = &[]*types.Log{}
thisMap[app] = new(types.Transaction)
}

iterFunc := func(app types.Appearance, value *[]*types.Log) error {
iterFunc := func(app types.Appearance, value *types.Transaction) error {
if tx, err := opts.Conn.GetTransactionByAppearance(&app, false); err != nil {
return err
} else {
passes := filter.PassesTxFilter(tx)
if passes && tx.Receipt != nil {
passesFourByte := filter.PassesTxFilter(tx)
hasApprovalLogs := false
if tx.Receipt != nil {
filteredLogs := make([]types.Log, 0, len(tx.Receipt.Logs))
hasEmitterLogs := len(opts.Emitter) == 0
for _, log := range tx.Receipt.Logs {
if filter.PassesLogFilter(&log, addrArray) && logFilter.PassesFilter(&log) {
if opts.Articulate {
if err := abiCache.ArticulateLog(&log); err != nil {
errorChan <- fmt.Errorf("error articulating log: %v", err)
if !hasEmitterLogs {
for _, emitter := range opts.Emitter {
if log.Address.Hex() == emitter {
hasEmitterLogs = true
break
}
}
*value = append(*value, &log)
}

if filter.PassesLogFilter(&log, addrArray) && logFilter.PassesFilter(&log) {
log.BlockHash = tx.BlockHash
log.BlockNumber = tx.BlockNumber
log.TransactionHash = tx.Hash
log.TransactionIndex = tx.TransactionIndex
filteredLogs = append(filteredLogs, log)
hasApprovalLogs = true
}
}

shouldInclude := (passesFourByte && hasEmitterLogs) || hasApprovalLogs
if shouldInclude {
tx.Receipt.Logs = filteredLogs
*value = *tx
}
} else if passesFourByte && len(opts.Emitter) == 0 {
*value = *tx
}
if bar != nil {
bar.Tick()
Expand All @@ -104,11 +123,15 @@ func (opts *ExportOptions) HandleApprovals(rCtx *output.RenderCtx, monitorArray
return
}

// Now safely collect all logs from all log slices
items := make([]*types.Log, 0)
for _, logSlice := range thisMap {
if logSlice != nil && *logSlice != nil {
items = append(items, *logSlice...)
items := make([]*types.Transaction, 0, len(thisMap))
for _, tx := range thisMap {
if !tx.BlockHash.IsZero() {
if opts.Articulate {
if err := abiCache.ArticulateTransaction(tx); err != nil {
errorChan <- err // continue even on error
}
}
items = append(items, tx)
}
}

Expand All @@ -117,19 +140,18 @@ func (opts *ExportOptions) HandleApprovals(rCtx *output.RenderCtx, monitorArray
i, j = j, i
}
if items[i].BlockNumber == items[j].BlockNumber {
if items[i].TransactionIndex == items[j].TransactionIndex {
return items[i].LogIndex < items[j].LogIndex
}
return items[i].TransactionIndex < items[j].TransactionIndex
}
return items[i].BlockNumber < items[j].BlockNumber
})

for _, item := range items {
var passes1, passes2 bool
passes1, finished = filter.PassesCountFilter()
passes2 = !opts.Nfts || item.IsNFT()
if passes1 && passes2 {
if item.BlockHash.IsZero() {
continue
}
var passes bool
passes, finished = filter.PassesCountFilter()
if passes {
modelChan <- item
}
if finished {
Expand Down
2 changes: 1 addition & 1 deletion src/apps/chifra/internal/export/handle_receipts.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (opts *ExportOptions) HandleReceipts(rCtx *output.RenderCtx, monitorArray [
}
}

// Set up and interate over the map calling iterFunc for each appearance
// Set up and iterate over the map calling iterFunc for each appearance
iterCtx, iterCancel := context.WithCancel(context.Background())
defer iterCancel()
errChan := make(chan error)
Expand Down
2 changes: 1 addition & 1 deletion src/apps/chifra/internal/export/handle_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (opts *ExportOptions) HandleShow(rCtx *output.RenderCtx, monitorArray []mon
}
}

// Set up and interate over the map calling iterFunc for each appearance
// Set up and iterate over the map calling iterFunc for each appearance
iterCtx, iterCancel := context.WithCancel(context.Background())
defer iterCancel()
errChan := make(chan error)
Expand Down
5 changes: 4 additions & 1 deletion src/apps/chifra/internal/list/handle_bounds.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,8 @@ func (opts *ListOptions) HandleBounds(rCtx *output.RenderCtx, monitorArray []mon
}
}

return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOpts())
extraOpts := map[string]any{
"appearances": true,
}
return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts))
}
4 changes: 2 additions & 2 deletions src/apps/chifra/internal/state/handle_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (opts *StateOptions) HandleShow(rCtx *output.RenderCtx) error {
}
}

stateFields, outputFields, none := types.SliceToStateParts(opts.Parts)
stateFields, outFields, none := types.SliceToStateParts(opts.Parts)

cnt := 0
fetchData := func(modelChan chan types.Modeler, errorChan chan error) {
Expand Down Expand Up @@ -99,7 +99,7 @@ func (opts *StateOptions) HandleShow(rCtx *output.RenderCtx) error {
}

extraOpts := map[string]any{
"fields": outputFields,
"outFields": outFields,
}

return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts))
Expand Down
2 changes: 1 addition & 1 deletion src/apps/chifra/pkg/abi/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (abiMap *SelectorSyncMap) downloadAbi(conn *rpc.Connection, address base.Ad
return errors.New("cannot read Etherscan API key")
}
url := fmt.Sprintf(
"https://api.etherscan.io/api?module=contract&action=getabi&address=%s&apikey=%s",
"https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getabi&address=%s&apikey=%s",
address.Hex(),
key,
)
Expand Down
4 changes: 2 additions & 2 deletions src/apps/chifra/pkg/rpc/provider/etherscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const etherscanFirstPage = 1
const etherscanRequestsPerSecond = 5
const etherscanMaxPerPage = 3000

var etherscanBaseUrl = "https://api.etherscan.io"
var etherscanBaseUrl = "https://api.etherscan.io/v2"

type EtherscanProvider struct {
printProgress bool
Expand Down Expand Up @@ -295,7 +295,7 @@ func (p *EtherscanProvider) url(value string, paginator Paginator, requestType s
tt = "txhash"
}

const str = "[{BASE_URL}]/api?module=[{MODULE}]&sort=asc&action=[{ACTION}]&[{TT}]=[{VALUE}]&page=[{PAGE}]&offset=[{PER_PAGE}]"
const str = "[{BASE_URL}]/api?chainid=1&module=[{MODULE}]&sort=asc&action=[{ACTION}]&[{TT}]=[{VALUE}]&page=[{PAGE}]&offset=[{PER_PAGE}]"
ret := strings.ReplaceAll(str, "[{BASE_URL}]", p.baseUrl)
ret = strings.ReplaceAll(ret, "[{MODULE}]", module)
ret = strings.ReplaceAll(ret, "[{TT}]", tt)
Expand Down
4 changes: 2 additions & 2 deletions src/apps/chifra/pkg/rpc/provider/etherscan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestEtherscanProvider_url(t *testing.T) {
t.Fatal(err)
}

expected = "https://api.etherscan.io/api?module=account&sort=asc&action=txlistinternal&address=0xf503017d7baf7fbc0fff7492b751025c6a78179b&page=1&offset=10&apikey="
expected = "https://api.etherscan.io/v2/api?chainid=1&module=account&sort=asc&action=txlistinternal&address=0xf503017d7baf7fbc0fff7492b751025c6a78179b&page=1&offset=10&apikey="
if result != expected {
t.Fatal("wrong value", result)
}
Expand All @@ -52,7 +52,7 @@ func TestEtherscanProvider_url(t *testing.T) {
t.Fatal(err)
}

expected = "https://api.etherscan.io/api?module=account&sort=asc&action=txlistinternal&address=0xf503017d7baf7fbc0fff7492b751025c6a78179b&page=2&offset=10&apikey="
expected = "https://api.etherscan.io/v2/api?chainid=1&module=account&sort=asc&action=txlistinternal&address=0xf503017d7baf7fbc0fff7492b751025c6a78179b&page=2&offset=10&apikey="
if result != expected {
t.Fatal("wrong value", result)
}
Expand Down
2 changes: 1 addition & 1 deletion src/apps/chifra/pkg/topics/topics.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var EnsTransferTopic = base.HexToHash(
"0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266",
)

var ApprovalFourbyte = base.HexToHash("0x095ea7b3")
var ApprovalFourbyte = string("0x095ea7b3")

var KnownTopics = map[base.Hash]bool{
TransferTopic: true,
Expand Down
Loading
Loading