@bcoders.gr/abi-codec
Version:
High-performance ABI encoding/decoding for Ethereum with receipt log processing - now supports tuple decoding
226 lines (170 loc) • 5.82 kB
Markdown
# High-Performance ABI Codec
A blazing-fast Node.js module for encoding/decoding Ethereum ABI data compatible with Geth. Optimized for maximum performance with zero-copy Buffer operations.
## Features
- ✅ **Function encoding/decoding** - All Solidity types supported
- ✅ **Event log decoding** - Individual logs and full receipts
- ✅ **Receipt processing** - Decode all matching logs from transaction receipts
- ✅ **Maximum performance** - Direct Buffer operations, minimal hex conversions
- ✅ **Geth compatibility** - 100% compatible with Ethereum node outputs
- ✅ **Minimal dependencies** - Only ethers.js utils for keccak256
## Installation
```bash
npm install
```
## Quick Start
```javascript
const { ABICodec } = require('./index');
// Your contract ABI
const contractABI = [
{
"type": "function",
"name": "transfer",
"inputs": [
{"name": "to", "type": "address"},
{"name": "amount", "type": "uint256"}
],
"outputs": [{"name": "success", "type": "bool"}]
},
{
"type": "event",
"name": "Transfer",
"inputs": [
{"name": "from", "type": "address", "indexed": true},
{"name": "to", "type": "address", "indexed": true},
{"name": "value", "type": "uint256", "indexed": false}
]
}
];
const codec = new ABICodec(contractABI);
```
## API Reference
### Function Encoding/Decoding
```javascript
// Encode function call
const calldata = codec.encodeFunction('transfer', [
'0x742d35Cc6634C0532925a3b8D8e9eED89B7A6de6',
BigInt('1000000000000000000')
]);
// Decode function call
const params = codec.decodeFunction('transfer', calldata);
// Returns: ['0x742d35Cc6634C0532925a3b8D8e9eED89B7A6de6', 1000000000000000000n]
// Decode function result
const result = codec.decodeFunctionResult('transfer', '0x0000...0001');
// Returns: [true]
```
### Event Log Decoding
```javascript
// Decode single event log
const decodedLog = codec.decodeLog(logData, topics, 'Transfer');
// Returns: { name: 'Transfer', args: { from: '0x...', to: '0x...', value: 1000n } }
// Decode all logs from a transaction receipt
const decodedLogs = codec.decodeReceiptLogs(receipt);
// Returns: Array of decoded logs that match ABI events
// Decode multiple receipts at once
const allLogs = codec.decodeMultipleReceipts([receipt1, receipt2, receipt3]);
// Filter logs by event name
const transferLogs = codec.filterLogsByEvent(decodedLogs, 'Transfer');
```
### Utility Functions
```javascript
// Get function selector (4-byte signature)
const selector = codec.getFunctionSelector('transfer');
// Returns: '0xa9059cbb'
// Get event selector (32-byte topic hash)
const eventTopic = codec.getEventSelector('Transfer');
// Returns: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
// Get all event topics this codec can decode
const knownTopics = codec.getKnownEventTopics();
// Returns: ['0xddf252...', '0x8c5be1...', ...]
```
## Supported Types
| Solidity Type | Supported | Notes |
|---------------|-----------|-------|
| `uint8` to `uint256` | ✅ | Including `uint` (defaults to `uint256`) |
| `int8` to `int256` | ✅ | Including `int` (defaults to `int256`) |
| `bool` | ✅ | |
| `address` | ✅ | |
| `bytes1` to `bytes32` | ✅ | Fixed-size byte arrays |
| `bytes` | ✅ | Dynamic byte arrays |
| `string` | ✅ | UTF-8 encoded |
| `uint256[]` | ✅ | Dynamic arrays |
| `uint256[5]` | ✅ | Fixed-size arrays |
| `tuple` | 🔄 | Coming soon (structs) |
## Performance
Benchmarked on typical hardware:
- **10,000 function encodings**: ~96ms (0.0096ms per encoding)
- **1,000 receipt decodings**: ~12ms (0.012ms per receipt)
- **Zero-copy operations** where possible for maximum speed
## Real-World Usage
### Processing Transaction Receipts
```javascript
// Process multiple transaction receipts
function processTransactions(receipts, codec) {
const results = {
totalLogs: 0,
decodedLogs: 0,
events: {}
};
for (const receipt of receipts) {
results.totalLogs += receipt.logs?.length || 0;
const decoded = codec.decodeReceiptLogs(receipt);
results.decodedLogs += decoded.length;
// Count events by type
decoded.forEach(log => {
results.events[log.name] = (results.events[log.name] || 0) + 1;
});
}
return results;
}
const stats = processTransactions(receipts, codec);
console.log(\`Decoded \${stats.decodedLogs}/\${stats.totalLogs} logs\`);
```
### Event Filtering and Analysis
```javascript
// Get all Transfer events from multiple receipts
const allLogs = codec.decodeMultipleReceipts(receipts);
const transfers = codec.filterLogsByEvent(allLogs, 'Transfer');
// Analyze transfer patterns
const transferAnalysis = transfers.map(log => ({
from: log.args.from,
to: log.args.to,
amount: log.args.value,
blockNumber: log.blockNumber,
txHash: log.transactionHash
}));
```
## Error Handling
The codec throws descriptive errors for invalid inputs:
```javascript
try {
const result = codec.encodeFunction('unknownFunction', []);
} catch (error) {
console.error('Function not found:', error.message);
}
try {
const result = codec.decodeReceiptLogs(invalidReceipt);
} catch (error) {
console.error('Invalid receipt format:', error.message);
}
```
## Testing
Run the test suite:
```bash
# Basic functionality tests
node test/basic.test.js
# Receipt decoding tests
node test/receipt.test.js
# Usage examples
node example.js
node receipt-example.js
```
## License
MIT
## Contributing
This module is optimized for performance. When contributing:
1. Maintain zero-copy Buffer operations where possible
2. Avoid unnecessary hex string conversions
3. Pre-compile and cache expensive operations
4. Add performance benchmarks for new features
---
Built for maximum performance with Ethereum/Geth compatibility in mind. 🚀