Skip to content

Commit d81c862

Browse files
docs: Update architecture docs with abi_helper.go and butler call details
- CLAUDE.md: add call.go, abi_helper.go to architecture diagram, add dynamic ABI encoding pattern, add CallContract to RPC list, add arbitrary contract reads to data source strategy - README.md: add call.go, abi_helper.go to project structure, add CallResult to domain types, add dynamic ABI design decision Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 334bed5 commit d81c862

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

CLAUDE.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ go run ./cmd/butler address 0x1234...
2121
go run ./cmd/butler tx 0xabcd... --json
2222
go run ./cmd/butler block latest
2323
go run ./cmd/butler chain-info --json
24+
go run ./cmd/butler call 0x1234... "totalSupply()(uint256)"
2425

2526
# Build
2627
go build -o butler ./cmd/butler
@@ -84,6 +85,9 @@ cmd/butler/
8485
Supports "latest" keyword or specific block number
8586
chaininfo.go `butler chain-info` — chain status
8687
Parallel: block number + gas price
88+
call.go `butler call <contract> <sig> [args]` — generic eth_call
89+
Uses abi_helper.go for encoding/decoding
90+
Supports cast-style sig: "name(inputs)(outputs)"
8791
8892
internal/
8993
domain/
@@ -106,8 +110,15 @@ internal/
106110
client.go RPC queries (each function creates/closes its own ethclient):
107111
GetBalance, GetNonce, GetCode, GetChainID,
108112
GetGasPrice, GetLatestBlockNumber, GetTransaction,
109-
GetTransactionReceipt, GetBlock, SendTransaction (EIP-1559),
113+
GetTransactionReceipt, GetBlock, CallContract,
114+
SendTransaction (EIP-1559),
110115
FormatBalance, ParseAmount, GetAddressFromPrivateKey
116+
abi_helper.go Dynamic ABI encoding/decoding without abigen:
117+
ParseCallSignature — splits "name(in)(out)" via paren depth
118+
BuildCalldata — ParseSelector + Pack → 4byte selector + args
119+
ConvertArg — CLI string → Go type (address, uint, bool, etc.)
120+
DecodeOutputs — Unpack + FormatValue → human strings
121+
Reusable for validators, staking, logs commands
111122
erc20.go ERC-20: GetTokenBalance, SendTokenTransaction,
112123
FormatTokenBalance, ParseTokenAmount
113124
pow10() uses big.Int (safe for any decimal count)
@@ -154,6 +165,8 @@ internal/
154165

155166
**Type-safe contract bindings:** ERC-20 interactions use `abigen`-generated Go code, not manual ABI encoding.
156167

168+
**Dynamic ABI encoding:** `butler call` uses `abi.ParseSelector` + `abi.Arguments.Pack/Unpack` for runtime encoding without JSON ABI files. The `abi_helper.go` module is designed for reuse by future commands (validators, staking, logs).
169+
157170
**Config-driven extensibility:** Chains, tokens, and contacts are pure JSON. Both TUI and CLI dynamically use these at startup.
158171

159172
**Async blockchain calls:** RPC operations run as concurrent goroutines (CLI uses sync.WaitGroup, TUI uses Bubble Tea commands).
@@ -171,6 +184,7 @@ internal/
171184
| Block by number | RPC | |
172185
| **Tx history by address** | **Explorer API** | RPC cannot do this — no `eth_getTransactionsByAddress` exists |
173186
| **All token holdings** | **Explorer API** | Token discovery requires indexer |
187+
| **Arbitrary contract reads** | RPC `eth_call` | via `butler call` with dynamic ABI encoding |
174188

175189
### Config Path Resolution
176190

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,16 +340,18 @@ cmd/butler/
340340
tx.go butler tx — tx + receipt lookup
341341
block.go butler block — block by number
342342
chaininfo.go butler chain-info — chain status
343+
call.go butler call — generic contract reads (eth_call)
343344
344345
internal/
345346
domain/
346347
models.go Chain, Token, Wallet, Contact structs
347-
output.go AddressInfo, TxDetail, BlockInfo, ChainStatus
348+
output.go AddressInfo, TxDetail, BlockInfo, ChainStatus, CallResult
348349
349350
infra/
350351
config/config.go JSON/env config loading + path resolution
351352
ethereum/
352-
client.go RPC: balance, nonce, code, blocks, tx, gas
353+
client.go RPC: balance, nonce, code, blocks, tx, gas, call
354+
abi_helper.go Dynamic ABI encoding/decoding (no abigen needed)
353355
erc20.go ERC-20: balance, transfer, formatting
354356
abi/erc20.json Standard ERC-20 ABI
355357
contracts/erc20.go Auto-generated Go bindings (abigen)
@@ -380,6 +382,8 @@ internal/
380382

381383
**pow10 uses big.Int** — The original `int64`-based `pow10()` would silently overflow for tokens with >18 decimals. Fixed to use `big.Int.Exp()` which is safe for any decimal count.
382384

385+
**Dynamic ABI via `abi_helper.go`**`butler call` uses `abi.ParseSelector` + `Arguments.Pack/Unpack` for runtime ABI encoding without JSON files. The same module will be reused by future commands (validators, staking, event logs). `ParseCallSignature` splits cast-style signatures via parenthesis depth counting because `abi.ParseSelector` rejects trailing characters.
386+
383387
</details>
384388

385389
## Releasing

0 commit comments

Comments
 (0)