A modern, Swift-native Ethereum JSON-RPC client built with Swift Concurrency (async/await). Perfect for iOS, macOS, watchOS, and tvOS applications.
Add EthereumKit to your project:
dependencies:[.package(url:"https://github.com/asyncswap/EthereumKit.git", from:"1.0.0")]Or in Xcode:
- File → Add Package Dependencies
- Enter the repository URL
- Select version and add to your target
import EthereumKit
// Initialize the service
letservice=EthereumService(rpcURL:"https://ethereum-rpc.publicnode.com")
// Get latest block number
letblockNumber=tryawait service.getLatestBlockNumber()print("Latest block: \(blockNumber)")
// Get ETH balance
letbalance=tryawait service.getBalanceInEther(address:"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb")print("Balance: \(balance) ETH")
// Get USDC balance
letusdc=tryawait service.getUSDCBalance(
address:"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
usdcContractAddress:"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")print("USDC Balance: \(usdc)")// Use pre-configured networks
letconfig=NetworkConfig.polygon
letservice=EthereumService(rpcURL: config.rpcURL)iflet usdcAddress = config.usdcAddress {letbalance=tryawait service.getUSDCBalance(
address:"0x...",
usdcContractAddress: usdcAddress
)}letclient=JSONRPCClient(rpcURLString:"https://ethereum-rpc.publicnode.com")!
// Simple call
letblockNumber:String=tryawait client.call(method:"eth_blockNumber")
// Call with parameters
letbalance:String=tryawait client.call(
method:"eth_getBalance",
params:[.string("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"),.string("latest")])// Convert hex to integers
letblockNum="0x1234".hexToInt // Returns 4660
// Convert integers to hex
lethex=100.toHex // Returns "0x64"
// Convert Wei to Ether
letether="0xde0b6b3a7640000".weiToEther // Returns 1.0
// Pad addresses for ABI encoding
letpadded=HexUtils.padAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb")Pre-configured networks available out of the box:
| Network | Chain ID | USDC Address |
|---|---|---|
| Ethereum Mainnet | 1 | ✅ |
| Polygon | 137 | ✅ |
| Arbitrum One | 42161 | ✅ |
| Base | 8453 | ✅ |
| Optimism | 10 | ✅ |
| Sepolia Testnet | 11155111 | ✅ |
getLatestBlockNumber()- Get current block numbergetChainId()- Get network chain ID
getBalance()- Get ETH balance (Wei as hex)getBalanceInEther()- Get ETH balance (as Double)getBalanceInWei()- Get ETH balance (as UInt64)
getERC20Balance()- Get token balance (raw hex)getERC20BalanceFormatted()- Get token balance with decimalsgetUSDCBalance()- Convenience method for USDC
getGasPrice()- Get current gas pricegetGasPriceInGwei()- Get gas price in GweiestimateGas()- Estimate gas for transactiongetTransactionCount()- Get nonce for addresssendTransaction()- Send signed transactiongetTransactionReceipt()- Get transaction receipt
All standard Ethereum JSON-RPC methods are available:
eth_blockNumbereth_getBalanceeth_calleth_sendRawTransactioneth_estimateGas- And more...
do{letbalance=tryawait service.getBalanceInEther(address:"0x...")print("Balance: \(balance)")}catchlet error as JSONRPCError{
// JSON-RPC error from server
print("RPC Error [\(error.code)]: \(error.message)")}catchlet error as EthereumServiceError{
// Service-level error
print("Service Error: \(error.localizedDescription)")}catch{
// Network or other errors
print("Error: \(error)")}- JSONRPCClient (Actor) - Thread-safe, low-level RPC client
- EthereumService (Class) - High-level API with convenience methods
- HexUtils - Utilities for hex/decimal conversions
- NetworkConfig - Pre-configured network settings
JSONRPCClient is an actor, ensuring all RPC calls are thread-safe and can be called from any context:
Task{letbalance=tryawait client.eth_getBalance(address:"0x...")}Task.detached{letgasPrice=tryawait client.eth_gasPrice()}letaddresses=["0xAddress1","0xAddress2","0xAddress3"]awaitwithTaskGroup(of:(String, Double).self){ group inforaddressin addresses {
group.addTask{letbalance=tryawait service.getBalanceInEther(address: address)return(address, balance)}}forawait(address, balance)in group {print("\(address): \(balance) ETH")}}// Example: Get ERC-20 token symbol
func getTokenSymbol(contractAddress:String)asyncthrows->String{letfunctionSelector="0x95d89b41" // symbol()
letresult=tryawait client.eth_call(to: contractAddress, data: functionSelector)
// Parse result...
return result
}Run tests using Swift Testing:
swift test# or
RUN_NETWORK_TESTS=1 swift testOr in Xcode: Cmd + U
- iOS 16.0+ / macOS 13.0+ / watchOS 9.0+ / tvOS 16.0+
- Swift 5.9+
- Xcode 15.0+
MIT License - feel free to use in your projects!
Contributions welcome! Please feel free to submit a Pull Request.
- Transaction signing support
- ENS resolution
- WebSocket support for subscriptions
- Batch RPC calls
- Gas price estimation strategies
- Transaction status monitoring
Built with ❤️ for the iOS Ethereum community.