near_dart 0.5.0 copy "near_dart: ^0.5.0" to clipboard
near_dart: ^0.5.0 copied to clipboard

Complete NEAR Protocol SDK for Flutter/Dart. RPC client, wallet integration, NEAR Intents, and type-safe primitives.

near_dart #

Complete NEAR Protocol SDK for Flutter/Dart.

pub package License: MIT

A type-safe, platform-agnostic SDK for building NEAR Protocol applications with Flutter and Dart. Works on iOS, Android, Web, and Desktop.

NEAR Flutter SDK demo on Android

The example app — local Sign & Send and wallet connect — running on Android.

Features #

  • Local Signing & Sending: ed25519 key pairs, Borsh serialization, send_tx broadcasting — byte-for-byte compatible with near-api-js
  • High-Level Account API: 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
  • NEAR Intents: 1Click asset discovery, quote builder, swap lifecycle polling, Explorer history, signed-intent helpers, and Message Bus JSON-RPC for partner solver integrations
  • Typed Network Config: NearNetwork.mainnet, NearNetwork.testnet, and NearNetwork.custom(...) with RPC, wallet, explorer, and chain metadata
  • 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

Platform support #

Feature Android iOS Web macOS Windows Linux
RPC queries
Local signing (Borsh + ed25519)
NEP-413 sign / verify
MyNearWallet redirect ⚠️ untested ⚠️ untested ⚠️ untested
Intear Wallet (bridge + deep link) ⚠️ needs the native app ⚠️ untested ⚠️ untested ⚠️ untested
HOT Wallet (relay) ✅ mainnet only ✅ mainnet only ✅ mainnet only ⚠️ untested ⚠️ untested ⚠️ untested
Secure key storage ✅ Keystore ✅ Keychain ⚠️ plain storage (no OS secrets on web) ✅ Keychain ✅ DPAPI ✅ libsecret

Rows marked untested should work (pure Dart + url_launcher) but have no verified end-to-end run yet. Security details: docs/security.md. NEAR Intents guide: docs/intents.md. Reference app evidence: NearCoffee SDK + Intents tutorial.

Guides #

Installation #

dependencies:
  near_dart: ^0.5.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}');
}

Typed Network Configuration #

final network = NearNetwork.custom(
  name: 'localnet',
  rpcUrl: 'http://127.0.0.1:3030',
  explorerUrl: 'http://127.0.0.1:4000',
);

final client = NearRpcClient.forNetwork(network);
print(network.transactionUrl('tx-hash'));

NEAR Intents #

Build cross-chain swap UX with the 1Click API without hand-routing solver transactions:

final intents = OneClickClient();
final catalog = OneClickAssetCatalog(client: intents);
final builder = OneClickQuoteBuilder();

final wnear = await catalog.requireByAssetId('nep141:wrap.near');
final usdc = await catalog.requireByAssetId('nep141:usdc.near');

final request = builder.exactInput(
  originToken: wnear,
  destinationToken: usdc,
  amount: '0.1', // converted exactly from decimals, no float math
  refundTo: 'alice.near',
  recipient: 'alice.near',
);

final swap = OneClickSwapController(client: intents);
final quote = await swap.quote(request);

print(quote.quote.amountOutFormatted);

For live swaps set dry: false, send funds to the returned deposit address, then use submitDeposit() and pollStatus() until success, refunded, or failed. Historical 1Click activity is available through OneClickExplorerClient for dashboards and support tooling.

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.

0
likes
160
points
392
downloads

Documentation

API reference

Publisher

verified publisher0xj.dev

Weekly Downloads

Complete NEAR Protocol SDK for Flutter/Dart. RPC client, wallet integration, NEAR Intents, and type-safe primitives.

Homepage
Repository (GitHub)
View/report issues

Topics

#near #blockchain #web3 #crypto #wallet

License

MIT (license)

Dependencies

crypto, cryptography, equatable, http, meta, web_socket_channel

More

Packages that depend on near_dart