Interact with Smart Contracts
maincontracts.dart example file in the repository. Code generation (see 'Generate Dart code from Smart Contract ABIs') is the recommended way to handle these interactions easily.repository·main·Indexed 41 days ago
https://github.com/xclud/web3dartA Dart library for interacting with Ethereum and Celo-compatible blockchain nodes via JSON-RPC. It provides high-level abstractions for managing accounts, sending transactions, calling smart contracts, and listening to blockchain events. Includes support for V3 wallet files, ABI parsing, and a code generation package called web3dart_builders.
contracts.dart example file in the repository. Code generation (see 'Generate Dart code from Smart Contract ABIs') is the recommended way to handle these interactions easily.To send transactions, you need Credentials. The library supports creating them from hex private keys, generating them randomly, or using V3 wallet files.
Use EthPrivateKey.fromHex() to create credentials from a hex string.
Use EthPrivateKey.createRandom(rng) with a secure random number generator to create a new identity.
Load a JSON wallet file using Wallet.fromJson(content, "password"). This returns a Wallet object from which you can extract the privateKey as Credentials.
Encrypt a private key into a V3 wallet using Wallet.createNew(credentials, "password", random). The resulting JSON can be exported via wallet.toJson() and is compatible with tools like MyEtherWallet or geth.
import 'dart:math';
import 'package:web3dart/web3dart.dart';
// From hex
Credentials fromHex = EthPrivateKey.fromHex("c87509a[...]dc0d3");
// Randomly
var rng = Random.secure();
Credentials random = EthPrivateKey.createRandom(rng);
// From V3 Wallet file
String content = File("wallet.json").readAsStringSync();
Wallet wallet = Wallet.fromJson(content, "testpassword");
Credentials unlocked = wallet.privateKey;
// Create new wallet
Wallet newWallet = Wallet.createNew(credentials, "password", rng);
print(newWallet.toJson());The library interacts with Ethereum or Celo nodes via an RPC endpoint. You must provide an RPC URL (e.g., from Infura, Geth, or Ganache) and an http.Client to initialize a Web3Client.
Once connected, you can call common RPC methods like getBalance to query the network.
import 'package:http/http.dart';
import 'package:web3dart/web3dart.dart';
var apiUrl = "http://localhost:7545"; // Your RPC endpoint
var httpClient = Client();
var ethClient = Web3Client(apiUrl, httpClient);
var credentials = EthPrivateKey.fromHex("0x...");
var address = credentials.address;
// Query balance
EtherAmount balance = await ethClient.getBalance(address);
print(balance.getValueInUnit(EtherUnit.ether));You can automate the creation of Dart classes for interacting with smart contracts using the web3dart_builders package and Dart's build system.
dart pub add web3dart_builders --devlib/ directory. The filename must end with .abi.json (e.g., my_contract.abi.json).dart run build_runner buildThis generates a .g.dart file containing the code needed to call contract functions and listen to events.
If your ABI function names violate Dart naming conventions, the analyzer will show warnings in the generated files. To ignore these, create an analysis_options.yaml file in your project root:
analyzer:
exclude:
- '**/*.g.dart'# Install builder
dart pub add web3dart_builders --dev
# Run generation
dart run build_runner buildThe Transaction class provides helper properties to identify the transaction type:
isEIP1559: Returns true if maxFeePerGas or maxPriorityFeePerGas is set.isCeloTx: Returns true if the transaction is an EIP-1559 type and a feeCurrency is provided (specific to the Celo network).If your application does not hold the private key locally (for example, if signing happens in a hardware wallet or a browser extension), you can implement the CustomTransactionSender interface. This allows you to integrate external signing logic into the web3dart workflow by providing a custom implementation of sendTransaction.
class MyHardwareWalletSender extends CustomTransactionSender {
@override
Future<String> sendTransaction(Transaction transaction) async {
// Implement logic to send the transaction to a hardware wallet
// and return the transaction hash.
}
}The ContractFunctionType enum categorizes the role of a function within a contract:
function: A standard contract function.constructor: A function called during contract deployment.fallback: The default function called when no other function matches the request.Helper Properties on ContractFunction:
isConstructor: Returns true if the type is constructor.isDefault: Returns true if the type is fallback.The StateMutability enum defines how a contract function interacts with the blockchain. This determines if a function can be called locally (for free) or requires a transaction (with gas costs).
| Mutability | Description |
|---|---|
pure | Output depends solely on input; does not read from the blockchain. |
view | Reads from the blockchain but does not write to it. |
nonPayable | May write to the blockchain but does not accept Ether. |
payable | May write to the blockchain and accepts Ether. |
Helper Properties on ContractFunction:
isConstant: Returns true if the function is pure or view (can be called locally without gas).isPayable: Returns true if the function is payable (can receive Ether).The Web3Client is the primary entry point for interacting with Ethereum/Celo nodes via JSON-RPC. You can initialize it using an HTTP URL and an HTTP client. For real-time features like event listening, you can provide a socketConnector to enable WebSocket support.
import "package:web3dart/web3dart.dart";
import "package:http/http.dart";
import "package:web_socket_channel/io.dart";
// Standard HTTP initialization
final client = Web3Client(rpcUrl, Client());
// Initialization with WebSocket support for events
final wsClient = Web3Client(rpcUrl, Client(), socketConnector: () {
return IOWebSocketChannel.connect(wsUrl).cast<String>();
});To send a transaction, use Web3Client.sendTransaction. You must provide the Credentials and a Transaction object.
Required/Common fields in Transaction:
to: The destination EthereumAddress.gasPrice: The price per unit of gas (EtherAmount).maxGas: The maximum gas limit.value: The amount of Ether to send (EtherAmount).If fields like gasPrice, the sender, or the nonce are omitted, the library will attempt to fetch them from the connected node. If you only need to sign a transaction without broadcasting it, use client.signTransaction instead.
import 'package:web3dart/web3dart.dart';
// ethClient initialized via Web3Client(apiUrl, httpClient)
var credentials = EthPrivateKey.fromHex("0x...");
await ethClient.sendTransaction(
credentials,
Transaction(
to: EthereumAddress.fromHex('0xC91...3706'),
gasPrice: EtherAmount.inWei(BigInt.one),
maxGas: 100000,
value: EtherAmount.fromUnitAndValue(EtherUnit.ether, 1),
),
);When using Web3Client.events, use FilterOptions to define the scope of the logs you want to monitor. You can filter by block range, contract address, and event topics.
fromBlock: The earliest block to consider. Use BlockNum.current for the last mined block or BlockNum.pending for unmined transactions.toBlock: The last block to consider. Defaults to BlockNum.current.address: Limits the filter to a specific EthereumAddress. If null, all logs are reported.topics: A list of topic filters. Topics must be hex-encoded strings prefixed with 0x and have an even number of digits.Topics are order-dependent:
[]: Matches everything.[A]: Matches A in the first position.[null, B]: Matches anything in the first position and B in the second.[A, B]: Matches A in the first and B in the second.[[A, B], [A, B]]: Matches (A or B) in the first position AND (A or B) in the second position.// Example: Creating options for a specific contract event
final options = FilterOptions.events(
contract: myDeployedContract,
event: myContractEvent,
fromBlock: BlockNum.current,
);To sign a transaction, you must first obtain its unsigned serialized pre-image using getUnsignedSerialized({int? chainId}). This method handles different transaction types:
isCeloTx is true, it uses the Celo-specific type 123 (prefix 0x7b) and specialized encoding.Note: You should provide the chainId to ensure the pre-image is correctly bound to the network.
// Example of getting the pre-image for signing
Uint8List unsignedTx = transaction.getUnsignedSerialized(chainId: 1);