polybrainz_etherscan 1.0.0
polybrainz_etherscan: ^1.0.0 copied to clipboard
A production-ready Dart wrapper for the Etherscan API V2, supporting 60+ EVM chains with a single API key. Features include rate limiting, caching, circuit breaker pattern, and type-safe models.
polybrainz_etherscan #
A production-ready Dart wrapper for the Etherscan API V2, supporting 60+ EVM chains with a single API key.
Features #
- Multi-chain Support: Ethereum, Polygon, Arbitrum, Optimism, Base, BSC, Avalanche, and 50+ more chains
- Type-safe: Extension types for addresses, hashes, and Wei amounts with compile-time validation
- Resilient: Built-in rate limiting, circuit breaker, and retry with exponential backoff
- Cached: In-memory LRU cache with configurable TTL
- No Magic Strings: All constants are strongly typed enums
- Code Generation: Freezed models with JSON serialization
Supported Chains #
| Mainnets | Testnets |
|---|---|
| Ethereum, Polygon, Arbitrum One, Optimism, Base, BSC, Avalanche, Fantom, Cronos, Gnosis, Linea, Scroll, zkSync Era, Polygon zkEVM, Mantle, Celo, Moonbeam, Moonriver, Blast, Fraxtal, Taiko, WEMIX, Kroma, opBNB, Zora | Sepolia, Holesky, Goerli, BSC Testnet, Polygon Amoy, Arbitrum Sepolia, Optimism Sepolia, Base Sepolia, Blast Sepolia |
Installation #
dependencies:
polybrainz_etherscan: ^1.0.0
Quick Start #
import 'package:polybrainz_etherscan/polybrainz_etherscan.dart';
void main() async {
// Create API instance
final api = await EtherscanApi.create(apiKey: 'YOUR_API_KEY');
// Get ETH balance
final result = await api.account.getBalance(
EthereumAddress('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'),
);
result.when(
success: (balance) => print('Balance: ${balance.balanceInEther} ETH'),
failure: (error) => print('Error: ${error.message}'),
);
// Switch to Polygon
final polygonApi = api.forChain(Chain.polygon);
final polygonBalance = await polygonApi.account.getBalance(
EthereumAddress('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'),
);
}
API Modules #
Account #
// Single balance
final balance = await api.account.getBalance(address);
// Multiple balances (up to 20 addresses)
final balances = await api.account.getBalanceMulti([address1, address2]);
// Normal transactions
final txs = await api.account.getTransactions(address, page: 1, offset: 10);
// Internal transactions
final internalTxs = await api.account.getInternalTransactions(address);
// ERC-20 token transfers
final tokens = await api.account.getTokenTransfers(address);
// ERC-721 NFT transfers
final nfts = await api.account.getNftTransfers(address);
Contract #
// Get verified contract ABI
final abi = await api.contract.getAbi(contractAddress);
// Get source code
final source = await api.contract.getSourceCode(contractAddress);
Gas #
// Get gas oracle (safe, propose, fast prices)
final gas = await api.gas.getGasOracle();
gas.when(
success: (oracle) {
print('Safe: ${oracle.safeGasPrice.toGwei} Gwei');
print('Propose: ${oracle.proposeGasPrice.toGwei} Gwei');
print('Fast: ${oracle.fastGasPrice.toGwei} Gwei');
},
failure: (e) => print(e),
);
// Estimate confirmation time for a gas price
final time = await api.gas.estimateConfirmationTime(Wei.fromGwei(50));
Stats #
// Get ETH price
final price = await api.stats.getEthPrice();
// Get total ETH supply
final supply = await api.stats.getEthSupply();
Transaction #
// Check execution status
final status = await api.transaction.getStatus(txHash);
// Check receipt status
final receipt = await api.transaction.getReceiptStatus(txHash);
Logs #
// Get event logs
final logs = await api.logs.getLogs(
address: contractAddress,
fromBlock: BlockNumber(18000000),
toBlock: BlockNumber(18001000),
topic0: '0xddf252ad...', // Transfer event signature
);
Type-Safe Values #
// Ethereum address with checksum validation
final address = EthereumAddress('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045');
// Transaction hash validation
final txHash = TransactionHash('0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a');
// Wei with safe arithmetic
final wei = Wei.fromEther(1.5);
print(wei.toGwei); // 1500000000.0
print(wei.toEther); // 1.5
// Block tags
final latest = BlockTagName.latest;
final specific = BlockNumber(18000000);
Error Handling #
The library uses a Result<T> type for explicit error handling:
final result = await api.account.getBalance(address);
// Pattern matching
result.when(
success: (balance) => print(balance.balanceInEther),
failure: (error) => print(error.message),
);
// Or use isSuccess/isFailure
if (result.isSuccess) {
final balance = result.value;
}
// Exception hierarchy
try {
// ...
} on RateLimitException catch (e) {
print('Rate limited, retry after: ${e.retryAfter}');
} on ApiErrorException catch (e) {
print('API error: ${e.message}');
} on NetworkException catch (e) {
print('Network error: ${e.message}');
} on EtherscanException catch (e) {
print('General error: ${e.message}');
}
Configuration #
final api = await EtherscanApi.create(
apiKey: 'YOUR_API_KEY',
chain: Chain.ethereum,
config: EtherscanConfig(
connectTimeout: Duration(seconds: 30),
receiveTimeout: Duration(seconds: 30),
rateLimitConfig: RateLimitConfig(
requestsPerSecond: 5,
burstSize: 10,
),
circuitBreakerConfig: CircuitBreakerConfig(
failureThreshold: 5,
resetTimeout: Duration(minutes: 1),
),
cacheConfig: CacheConfig(
memoryCacheSize: 1000,
),
),
);
Multi-Chain Usage #
// Start with Ethereum
final api = await EtherscanApi.create(apiKey: 'YOUR_API_KEY');
// Switch chains easily
final polygonApi = api.forChain(Chain.polygon);
final arbitrumApi = api.forChain(Chain.arbitrumOne);
final baseApi = api.forChain(Chain.base);
// Each instance shares the same HTTP client and cache
final ethBalance = await api.account.getBalance(address);
final maticBalance = await polygonApi.account.getBalance(address);
Getting an API Key #
- Create a free account at Etherscan.io
- Go to API Keys
- Create a new API key
- The same key works for all 60+ supported chains
License #
MIT License - see LICENSE for details.