near_dart 0.2.2
near_dart: ^0.2.2 copied to clipboard
Complete NEAR Protocol SDK for Flutter/Dart. RPC client, wallet integration, and type-safe primitives.
near_dart #
Complete NEAR Protocol SDK for Flutter/Dart.
A type-safe, platform-agnostic SDK for building NEAR Protocol applications with Flutter and Dart. Works on iOS, Android, Web, and Desktop.
The example app — local Sign & Send and wallet connect — running on Android.
Features #
- Local Signing & Sending: ed25519 key pairs, Borsh serialization,
send_txbroadcasting — byte-for-byte compatible with near-api-js - High-Level
AccountAPI:transfer()/callFunction()in one call (nonce + block hash resolved automatically) - RPC Client: Query blockchain state, accounts, contracts, and validators — FastNear endpoints by default, automatic failover
- Wallet Integration: Connect wallets via WalletConnect or deep links
- Type-Safe Primitives:
AccountId,NearToken,PublicKey,CryptoHash - Transaction Building: All NEAR actions, including NEP-591 Global Contracts
- NEP-413 Support: Message signing for authentication
- Tested against the real chain: every release runs a real sign-and-send E2E on testnet
Installation #
dependencies:
near_dart: ^0.2.0
Quick Start #
import 'package:near_dart/near_dart.dart';
void main() async {
// Create client (mainnet or testnet)
final client = NearRpcClient.mainnet();
// Get network status
final status = await client.status();
switch (status) {
case RpcSuccess(:final value):
print('Chain: ${value.chainId}');
print('Block: ${value.syncInfo.latestBlockHeight}');
case RpcFailure(:final error):
print('Error: ${error.message}');
}
// Query account
final result = await client.viewAccount(
accountId: AccountId('alice.near'),
blockReference: BlockReference.finality(Finality.final_),
);
if (result.isSuccess) {
final account = result.getOrNull()!;
print('Balance: ${account.amount.toNear()} NEAR');
}
client.close();
}
Sign & Send Transactions (local keys) #
The fastest way to execute transactions — no wallet redirect needed:
import 'package:near_dart/near_dart.dart';
void main() async {
final client = NearRpcClient.testnet();
final account = Account(
accountId: AccountId('alice.testnet'),
keyPair: await KeyPairEd25519.fromString('ed25519:<your secret key>'),
client: client,
);
// Transfer NEAR
final result = await account.transfer(
receiverId: AccountId('bob.testnet'),
amount: NearToken.fromNear(1),
);
switch (result) {
case RpcSuccess(:final value):
print('Executed! https://testnet.nearblocks.io/txns/${value.transaction.hash}');
case RpcFailure(:final error):
print('Failed: ${error.message}');
}
// Call a contract method that changes state
await account.callFunction(
contractId: AccountId('wrap.testnet'),
methodName: 'near_deposit',
deposit: NearToken.fromNear(1),
);
client.close();
}
Need lower-level control? Sign and broadcast manually:
final signed = await signTransaction(
Transaction(
signerId: AccountId('alice.testnet'),
receiverId: AccountId('bob.testnet'),
nonce: nonce, // access key nonce + 1
blockHash: recentBlockHash, // from viewAccessKey or block()
actions: [TransferAction(deposit: NearToken.fromNear(1))],
),
keyPair,
);
print(signed.hash); // transaction hash (base58)
await client.sendTransaction(signed, waitUntil: TxExecutionStatus.final_);
Serialization and signatures are validated byte-for-byte against canonical near-api-js vectors, and the full pipeline runs end-to-end against real testnet in CI.
RPC Client #
Network Status #
final result = await client.status();
Account Information #
final result = await client.viewAccount(
accountId: AccountId('alice.near'),
blockReference: BlockReference.finality(Finality.final_),
);
Call Contract View Function #
final result = await client.callFunction(
accountId: AccountId('token.near'),
methodName: 'ft_balance_of',
args: {'account_id': 'alice.near'},
blockReference: BlockReference.finality(Finality.final_),
);
if (result.isSuccess) {
final balance = result.getOrNull()!.resultAsJson();
print('Token balance: $balance');
}
Validators #
final result = await client.validators();
if (result.isSuccess) {
final validators = result.getOrNull()!;
print('Current validators: ${validators.currentValidators.length}');
}
Wallet Integration #
Building Transactions #
// Simple transfer
final tx = Transaction(
signerId: AccountId('alice.near'),
receiverId: AccountId('bob.near'),
actions: [
TransferAction(deposit: NearToken.fromNear(1)),
],
);
// Contract function call
final tx = Transaction(
signerId: AccountId('alice.near'),
receiverId: AccountId('token.near'),
actions: [
FunctionCallAction(
methodName: 'ft_transfer',
args: {'receiver_id': 'bob.near', 'amount': '1000000'},
deposit: NearToken.oneYocto(),
),
],
);
Action Types #
CreateAccountAction()
DeployContractAction(code: wasmBytes)
FunctionCallAction(methodName: 'method', args: {...}, deposit: NearToken.zero())
TransferAction(deposit: NearToken.fromNear(10))
StakeAction(stake: NearToken.fromNear(100), publicKey: PublicKey('ed25519:...'))
AddKeyAction(publicKey: key, accessKey: FullAccessKey())
DeleteKeyAction(publicKey: key)
DeleteAccountAction(beneficiaryId: AccountId('beneficiary.near'))
MyNearWallet Integration (connect once, then sign locally) #
signIn() generates a function-call key, redirects to MyNearWallet to
provision it, and completeSignIn() stores the private key — so afterward
you call contracts locally, with no more redirects.
final adapter = MyNearWalletAdapter(
config: MyNearWalletConfig(
contractId: AccountId('app.near'),
successUrl: 'myapp://callback/success', // https URL on web
failureUrl: 'myapp://callback/failure',
network: MyNearWalletNetwork.mainnet,
),
// Persist keys across the redirect/restarts (see the example app's
// SharedPrefsKeyStore). Defaults to InMemoryKeyStore.
keyStore: myPersistentKeyStore,
launchUrl: (uri) => launchUrl(uri, mode: LaunchMode.externalApplication),
);
// 1. Connect: generates a key and redirects to the wallet.
await adapter.signIn(contractId: AccountId('app.near'));
// 2. When the wallet redirects back (deep link on mobile, app URL on web):
final account = await adapter.completeSignIn(callbackUri);
print('Connected: ${account?.accountId}');
// 3. From now on, sign contract calls locally — no redirect:
final near = Account(
accountId: account!.accountId,
keyPair: (await adapter.keyFor(account.accountId))!,
client: NearRpcClient.mainnet(),
);
await near.callFunction(
contractId: AccountId('app.near'),
methodName: 'set_greeting',
args: {'greeting': 'hola'},
);
Type-Safe Primitives #
AccountId #
final account = AccountId('alice.near'); // Validates format
NearToken #
final amount = NearToken.fromNear(10); // 10 NEAR
final small = NearToken.oneYocto(); // 1 yoctoNEAR
final zero = NearToken.zero();
print(amount.toNear()); // 10.0
PublicKey #
final key = PublicKey('ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp');
print(key.keyType); // KeyType.ed25519
BlockReference #
BlockReference.finality(Finality.final_) // Latest finalized
BlockReference.finality(Finality.optimistic) // Latest (may reorg)
BlockReference.blockId(123456789) // Specific height
BlockReference.blockHash(CryptoHash('...')) // Specific hash
Error Handling #
final result = await client.viewAccount(...);
switch (result) {
case RpcSuccess(:final value):
print('Balance: ${value.amount.toNear()}');
case RpcFailure(:final error):
switch (error.kind) {
case RpcErrorKind.rpcError:
print('RPC error: ${error.message}');
case RpcErrorKind.networkError:
print('Network error');
case RpcErrorKind.timeout:
print('Request timeout');
default:
print('Error: ${error.message}');
}
}
Example App #
See the example directory for a complete Flutter app demonstrating every SDK feature: local Sign & Send, wallet connect (redirect + deep link), and all RPC queries — with network switching between testnet and mainnet.
Verified on real devices & chains #
Recorded evidence from the example app running against real NEAR testnet (no mocks at any layer):
| Demo | Evidence | On-chain proof |
|---|---|---|
| Android: generate key → faucet → sign & send on-chain | video / gif | JByxPfTt…34cZG |
Android: wallet connect → browser → nearsdk:// deep link → connected |
video / gif | function-call key provisioning flow |
Additionally verified: web (Chrome, dart2js and dart2wasm — byte-identical signatures vs near-api-js), real on-chain transfers from the browser, and a scheduled CI E2E that signs and sends a real testnet transaction. iOS builds are verified on every push via a macOS CI job.
Testing #
dart test --exclude-tags integration # 394 offline tests (no network)
dart test test/integration/testnet/ # live testnet RPC tests
dart test test/e2e/ # incl. a REAL sign+send on testnet
Serialization and signatures are validated byte-for-byte against canonical
near-api-js@7.2.0 vectors (test/fixtures/near_api_js_vectors.json).
License #
MIT License - see LICENSE for details.