NArk.Core
1.0.228-beta
See the version list below for details.
dotnet add package NArk.Core --version 1.0.228-beta
NuGet\Install-Package NArk.Core -Version 1.0.228-beta
<PackageReference Include="NArk.Core" Version="1.0.228-beta" />
<PackageVersion Include="NArk.Core" Version="1.0.228-beta" />
<PackageReference Include="NArk.Core" />
paket add NArk.Core --version 1.0.228-beta
#r "nuget: NArk.Core, 1.0.228-beta"
#:package NArk.Core@1.0.228-beta
#addin nuget:?package=NArk.Core&version=1.0.228-beta&prerelease
#tool nuget:?package=NArk.Core&version=1.0.228-beta&prerelease
NArk .NET SDK
A .NET SDK for building applications on Arkade — a Bitcoin virtual execution layer that enables instant, low-cost, programmable off-chain transactions using virtual UTXOs (VTXOs).
Packages
| Package | Description |
|---|---|
| NArk.Abstractions | Interfaces and domain types (IVtxoStorage, IContractStorage, IWalletProvider, ArkCoin, ArkVtxo, etc.) |
| NArk.Core | Core services: spending, batch management, VTXO sync, sweeping, wallet infrastructure, gRPC transport |
| NArk.Swaps | Boltz swap integration for BTC-to-Ark and Ark-to-BTC chain/submarine swaps |
| NArk.Storage.EfCore | Entity Framework Core storage implementations (provider-agnostic — works with PostgreSQL, SQLite, etc.) |
| NArk | Meta-package that pulls in NArk.Core + NArk.Swaps |
Quick Start
Install
dotnet add package NArk # Core + Swaps
dotnet add package NArk.Storage.EfCore # EF Core persistence
Minimal Setup with Generic Host
using NArk.Hosting;
using NArk.Core.Wallet;
using NArk.Storage.EfCore;
using NArk.Storage.EfCore.Hosting;
var builder = Host.CreateDefaultBuilder(args)
.AddArk()
.WithVtxoStorage<EfCoreVtxoStorage>()
.WithContractStorage<EfCoreContractStorage>()
.WithIntentStorage<EfCoreIntentStorage>()
.WithWalletProvider<DefaultWalletProvider>()
.WithSafetyService<YourSafetyService>()
.WithTimeProvider<YourChainTimeProvider>()
.OnMainnet()
.EnableSwaps();
// Register your DbContext and EF Core storage
builder.ConfigureServices((_, services) =>
{
services.AddDbContextFactory<YourDbContext>(opts =>
opts.UseNpgsql(connectionString));
services.AddArkEfCoreStorage<YourDbContext>();
});
var app = builder.Build();
await app.RunAsync();
Setup with IServiceCollection (plugin/non-host scenarios)
using NArk.Hosting;
using NArk.Core.Wallet;
using NArk.Storage.EfCore.Hosting;
services.AddArkCoreServices();
services.AddArkNetwork(ArkNetworkConfig.Mainnet);
services.AddArkSwapServices();
services.AddDbContextFactory<YourDbContext>(opts =>
opts.UseNpgsql(connectionString));
services.AddArkEfCoreStorage<YourDbContext>();
// Register remaining required services
services.AddSingleton<IWalletProvider, DefaultWalletProvider>();
services.AddSingleton<ISafetyService, YourSafetyService>();
services.AddSingleton<IChainTimeProvider, YourChainTimeProvider>();
Architecture
NArk (meta-package)
├── NArk.Core
│ ├── Services (spending, batches, VTXO sync, sweeping, intents)
│ ├── Wallet (WalletFactory, signers, address providers)
│ ├── Hosting (DI extensions, ArkApplicationBuilder)
│ └── Transport (gRPC client for Ark server communication)
│
├── NArk.Swaps
│ ├── Boltz client (submarine & chain swaps)
│ └── Swap management service
│
└── NArk.Abstractions
├── Domain types (ArkCoin, ArkVtxo, ArkContract, ArkAddress, etc.)
├── Storage interfaces (IVtxoStorage, IContractStorage, IIntentStorage)
└── Wallet interfaces (IWalletProvider, IArkadeWalletSigner)
NArk.Storage.EfCore (optional, provider-agnostic persistence)
├── EF Core entity mappings
├── Storage implementations
└── DI extension: AddArkEfCoreStorage<TDbContext>()
Wallet Management
The SDK supports two wallet types:
HD Wallets — BIP-39 mnemonic with BIP-86 taproot derivation (m/86'/cointype'/0'):
var serverInfo = await transport.GetServerInfoAsync();
var wallet = await WalletFactory.CreateWallet(
"abandon abandon abandon ... about", // BIP-39 mnemonic
destination: null,
serverInfo);
// wallet.WalletType == WalletType.HD
Single-Key Wallets — nostr nsec format (Bech32-encoded secp256k1 key):
var wallet = await WalletFactory.CreateWallet(
"nsec1...",
destination: null,
serverInfo);
// wallet.WalletType == WalletType.SingleKey
Save and load wallets through IWalletStorage:
await walletStorage.SaveWallet(wallet);
var loaded = await walletStorage.LoadWallet(wallet.Id);
var all = await walletStorage.LoadAllWallets();
Spending
Use ISpendingService to send Ark transactions:
// Automatic coin selection
var txId = await spendingService.Spend(
walletId,
outputs: [new ArkTxOut(recipientAddress, Money.Satoshis(10_000))]);
// Manual coin selection
var coins = await spendingService.GetAvailableCoins(walletId);
var txId = await spendingService.Spend(
walletId,
inputs: coins.Take(2).ToArray(),
outputs: [new ArkTxOut(recipientAddress, Money.Satoshis(5_000))]);
Assets
The SDK supports issuing, transferring, and burning assets on Ark. Assets are encoded as AssetGroup entries inside an OP_RETURN output (an "asset packet") attached to each Ark transaction. The asset ID is derived from {txid, groupIndex} after submission.
Issuance
Use IAssetManager to create new assets:
var result = await assetManager.IssueAsync(walletId,
new IssuanceParams(Amount: 1000));
// result.AssetId — the unique asset identifier
// result.ArkTxId — the Ark transaction that created it
Issue with metadata:
var result = await assetManager.IssueAsync(walletId,
new IssuanceParams(
Amount: 1000,
Metadata: new Dictionary<string, string>
{
{ "name", "My Token" },
{ "ticker", "MTK" },
{ "decimals", "8" }
}));
Controlled Issuance & Reissuance
A control asset acts as a minting key — only the holder can issue more supply:
// Issue a control asset (amount=1, acts as the minting authority)
var control = await assetManager.IssueAsync(walletId,
new IssuanceParams(Amount: 1));
// Issue a token controlled by that asset
var token = await assetManager.IssueAsync(walletId,
new IssuanceParams(Amount: 1000, ControlAssetId: control.AssetId));
// Reissue more supply later (requires holding the control asset)
await assetManager.ReissueAsync(walletId,
new ReissuanceParams(control.AssetId, Amount: 500));
Transfer
Asset transfers use the standard SpendingService.Spend() with ArkTxOut.Assets:
await spendingService.Spend(walletId,
[
new ArkTxOut(ArkTxOutType.Vtxo, serverInfo.Dust, recipientAddress)
{
Assets = [new ArkTxOutAsset(assetId, 400)]
}
]);
// Automatic coin selection handles BTC fees and asset change.
// Sender retains remaining units (e.g. 600 of 1000) as asset change.
Burn
Reduce the circulating supply of an asset:
await assetManager.BurnAsync(walletId,
new BurnParams(assetId, Amount: 400));
// Remaining 600 units are returned as change
Querying Assets
Check asset balances from local VTXO storage:
var coins = await spendingService.GetAvailableCoins(walletId);
foreach (var coin in coins.Where(c => c.Assets is { Count: > 0 }))
{
foreach (var asset in coin.Assets!)
Console.WriteLine($"Asset {asset.AssetId}: {asset.Amount} units");
}
Query asset details from the Ark server:
var details = await transport.GetAssetDetailsAsync(assetId);
// details.Supply — total circulating supply
// details.AssetId — the asset identifier
// details.Metadata — key-value metadata (if set during issuance)
Delegation
Delegation solves the VTXO liveness problem — VTXOs expire if not refreshed. A delegate service (e.g., Fulmine) participates in batch rounds on your behalf, rolling VTXOs over before expiry.
Automated Delegation
When AddArkDelegation is configured, the SDK automatically:
- Derives delegate contracts — HD wallets produce
ArkDelegateContractinstead ofArkPaymentContractfor Receive/SendToSelf operations - Auto-delegates VTXOs — when VTXOs arrive at delegate contract addresses, the SDK builds partially signed intent + ACP forfeit txs and sends them to the delegator
services.AddArkCoreServices();
// Enable automated delegation (Fulmine delegator gRPC endpoint)
services.AddArkDelegation("http://localhost:7012");
// That's it. HD wallets will now:
// - Derive ArkDelegateContract for new receive/change addresses
// - Auto-delegate incoming VTXOs to the delegator on receipt
// nsec wallets (hashlock/note contracts) are unaffected.
The delegate contract has three spending paths:
- CollaborativePath (User + Server, 2-of-2) — collaborative spending, same as a regular payment contract
- DelegatePath (User + Delegate + Server, 3-of-3) — used by the delegator for ACP forfeit txs
- ExitPath (User only, after CSV delay) — unilateral recovery
Manual Delegation
For fine-grained control, you can manually construct delegate contracts and delegate VTXOs:
// Get delegator info
var info = await delegationService.GetDelegatorInfoAsync();
// Create a delegate contract
var delegateContract = new ArkDelegateContract(
serverInfo.SignerKey,
serverInfo.UnilateralExit,
userKey,
KeyExtensions.ParseOutputDescriptor(info.Pubkey, network),
cltvLocktime: new LockTime(currentHeight + 100)); // optional safety window
// Send VTXOs to the delegate contract address
await spendingService.Spend(walletId,
outputs: [new ArkTxOut(delegateContract.GetArkAddress(), amount)]);
// Delegate to the delegator
await delegationService.DelegateAsync(
intentMessage: intentJson,
intentProof: proofPsbtBase64,
forfeitTxs: forfeitTxHexArray,
rejectReplace: false);
The CLTV locktime is optional — when set, it prevents the delegate from acting before a specific block height, giving the owner a safety window.
Custom Contract Delegation
The SDK uses an IDelegationTransformer pattern to support delegating different contract types. The built-in DelegateContractDelegationTransformer handles ArkDelegateContract VTXOs. Register additional transformers for other contract types:
services.AddTransient<IDelegationTransformer, MyCustomDelegationTransformer>();
Each transformer implements:
CanDelegate(walletId, contract, delegatePubkey)— check eligibilityGetDelegationScriptBuilders(contract)— return (intentScript, forfeitScript) for building delegation artifacts
Collaborative Exits (On-chain)
Move funds from Ark back to the Bitcoin base layer:
var btcTxId = await onchainService.InitiateCollaborativeExit(
walletId,
new ArkTxOut(bitcoinAddress, Money.Satoshis(50_000)));
Boarding (On-chain → Ark)
Boarding lets users move on-chain Bitcoin UTXOs into the Ark VTXO tree. The user deposits BTC to a boarding address (a P2TR output with a collaborative spend path and a CSV-locked unilateral exit). Once confirmed, the boarding UTXO is automatically picked up by the intent/batch pipeline — no manual intervention needed.
1. Derive a Boarding Address
var boardingContract = (ArkBoardingContract)await contractService.DeriveContract(
walletId,
NextContractPurpose.Boarding);
// Get the on-chain P2TR (bc1p...) address for the user to deposit BTC to
var onchainAddress = boardingContract.GetOnchainAddress(network);
2. Sync On-chain UTXOs
BoardingUtxoSyncService polls a blockchain indexer for confirmed UTXOs at your boarding addresses and upserts them into VTXO storage. It takes an IBoardingUtxoProvider — choose Esplora or NBXplorer depending on your setup:
// Option A: Esplora (mempool.space, Chopsticks, etc.)
IBoardingUtxoProvider utxoProvider = new EsploraBoardingUtxoProvider(
new Uri("https://mempool.space/api/"));
// Option B: NBXplorer (BTCPay Server, self-hosted)
IBoardingUtxoProvider utxoProvider = new NBXplorerBoardingUtxoProvider(
network, new Uri("http://localhost:32838"));
// Register the sync service and provider
services.AddSingleton<IBoardingUtxoProvider>(utxoProvider);
services.AddSingleton<BoardingUtxoSyncService>();
// Register the poll service — automatically polls every 30s
// when unspent boarding VTXOs exist
services.AddSingleton<BoardingUtxoPollService>();
services.AddHostedService(sp => sp.GetRequiredService<BoardingUtxoPollService>());
The BoardingUtxoPollService automatically checks for unspent boarding VTXOs every 30 seconds and syncs confirmation state changes. It complements event-driven sync (e.g., NBXplorer transaction events) to catch missed events during provider reconnects or block confirmations.
Once a boarding UTXO is synced and confirmed, the SDK's IntentGenerationService automatically creates an intent for it. The next batch round moves it into the VTXO tree.
3. Handle Expired Boarding UTXOs (Optional)
If a boarding UTXO isn't batched before its CSV timelock expires, OnchainSweepService detects it. Register a custom IOnchainSweepHandler to control what happens:
public class MySweepHandler : IOnchainSweepHandler
{
public async Task<bool> HandleExpiredUtxoAsync(
string walletId, ArkVtxo vtxo, ArkContractEntity contract,
CancellationToken ct)
{
// Sweep to a new boarding address, cold storage, etc.
return true; // true = handled, false = fall back to default
}
}
services.AddSingleton<IOnchainSweepHandler, MySweepHandler>();
Then call SweepExpiredUtxosAsync() periodically:
var sweepService = new OnchainSweepService(
vtxoStorage, contractStorage, chainTimeProvider,
contractService, walletProvider, sweepHandler);
await sweepService.SweepExpiredUtxosAsync(ct);
Contracts
Derive receiving addresses and manage contracts:
// Derive a new receive contract (generates a new Ark address)
var contract = await contractService.DeriveContract(
walletId,
NextContractPurpose.Receive);
// The contract's script can be converted to an ArkAddress for display
EF Core Storage
NArk.Storage.EfCore provides ready-made storage implementations. It is provider-agnostic — no dependency on Npgsql or any specific database driver.
DbContext Setup
In your DbContext.OnModelCreating, call ConfigureArkEntities:
public class MyDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ConfigureArkEntities(opts =>
{
opts.Schema = "ark"; // default
// opts.WalletsTable = "Wallets"; // all table names configurable
});
}
}
Storage Options
ArkStorageOptions controls schema, table names, and provider-specific behavior:
services.AddArkEfCoreStorage<MyDbContext>(opts =>
{
opts.Schema = "my_schema";
// PostgreSQL-specific text search on contract metadata
opts.ContractSearchProvider = (query, searchText) =>
query.Where(c => EF.Functions.ILike(c.Metadata, $"%{searchText}%"));
});
Entities
| Entity | Table | Primary Key |
|---|---|---|
ArkWalletEntity |
Wallets |
Id |
ArkWalletContractEntity |
WalletContracts |
(Script, WalletId) |
VtxoEntity |
Vtxos |
(TransactionId, TransactionOutputIndex) |
ArkIntentEntity |
Intents |
IntentTxId |
ArkIntentVtxoEntity |
IntentVtxos |
(IntentTxId, VtxoTransactionId, VtxoTransactionOutputIndex) |
ArkSwapEntity |
Swaps |
(SwapId, WalletId) |
Networks
Pre-configured network environments:
// Fluent builder
builder.AddArk().OnMainnet();
builder.AddArk().OnMutinynet();
builder.AddArk().OnRegtest();
builder.AddArk().OnCustomGrpcArk("http://my-ark-server:7070");
// IServiceCollection
services.AddArkNetwork(ArkNetworkConfig.Mainnet);
services.AddArkNetwork(new ArkNetworkConfig(
ArkUri: "http://my-ark-server:7070",
BoltzUri: "http://my-boltz:9069/"));
Swaps (Boltz Integration)
Enable Bitcoin ↔ Ark swaps through Boltz:
// Fluent builder
builder.AddArk()
.EnableSwaps()
// or with custom Boltz URL:
.OnCustomBoltz("https://api.boltz.exchange", websocketUrl: null);
// IServiceCollection
services.AddArkSwapServices();
services.AddHttpClient<BoltzClient>();
The SwapsManagementService handles swap lifecycle automatically — monitoring status, cooperative claim signing, and VHTLC management.
Extensibility Points
The SDK uses a pluggable architecture. Register your implementations for:
| Interface | Purpose | Default |
|---|---|---|
IVtxoStorage |
VTXO persistence | EfCoreVtxoStorage |
IContractStorage |
Contract persistence | EfCoreContractStorage |
IIntentStorage |
Intent persistence | EfCoreIntentStorage |
ISwapStorage |
Swap persistence | EfCoreSwapStorage |
IWalletStorage |
Wallet persistence | EfCoreWalletStorage |
IWalletProvider |
Wallet signer/address resolution | DefaultWalletProvider |
ISafetyService |
Distributed locking | Must implement |
IChainTimeProvider |
Current blockchain height/time | Must implement |
IFeeEstimator |
Transaction fee estimation | DefaultFeeEstimator |
ICoinSelector |
UTXO selection strategy | DefaultCoinSelector |
ISweepPolicy |
VTXO consolidation rules | Register zero or more |
IContractTransformer |
Custom contract → coin transforms | Register zero or more |
IDelegationTransformer |
Check contract eligibility and provide delegation script builders | DelegateContractDelegationTransformer |
IContractTransformer (delegate) |
Make delegate VTXOs spendable | DelegateContractTransformer |
IEventHandler<T> |
React to batch/sweep/spend events | Register zero or more |
Local Development
Running Tests
# Unit tests
dotnet test NArk.Tests
# End-to-end tests (requires Docker + nigiri)
# Start the regtest infrastructure first:
chmod +x ./NArk.Tests.End2End/Infrastructure/start-env.sh
./NArk.Tests.End2End/Infrastructure/start-env.sh --clean
# Then run the tests:
dotnet test NArk.Tests.End2End
The E2E tests use nigiri for a local Bitcoin regtest environment with a Docker Compose overlay for arkd, Boltz, and Fulmine services.
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- AsyncKeyedLock (>= 8.0.1)
- Cel (>= 0.3.2)
- Google.Api.CommonProtos (>= 2.17.0)
- Grpc.Net.Client (>= 2.76.0)
- Microsoft.Extensions.Hosting (>= 9.0.0)
- Microsoft.Extensions.Logging (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.0)
- NArk.Abstractions (>= 1.0.228-beta)
- NBitcoin (>= 9.0.4)
- NBitcoin.Secp256k1 (>= 3.2.0)
- NBXplorer.Client (>= 5.0.5)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on NArk.Core:
| Package | Downloads |
|---|---|
|
NArk
Meta-package for the Ark protocol .NET SDK. Pulls in NArk.Core (spending, batches, VTXO sync, wallets) and NArk.Swaps (Boltz integration). |
|
|
NArk.Swaps
Boltz swap integration for the Ark protocol .NET SDK. Enables BTC-to-Ark and Ark-to-BTC submarine and chain swaps. |
|
|
NArk.Transport.GrpcClient
Package Description |
|
|
NArk.Hosting
Package Description |
|
|
NArk.Storage.EfCore
Entity Framework Core storage implementations for the Ark protocol .NET SDK. Provider-agnostic persistence for VTXOs, contracts, intents, swaps, and wallets. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.230-beta | 26 | 3/20/2026 |
| 1.0.228-beta | 29 | 3/20/2026 |
| 1.0.222-beta | 36 | 3/19/2026 |
| 1.0.221-beta | 34 | 3/17/2026 |
| 1.0.220-beta | 38 | 3/17/2026 |
| 1.0.218-beta | 50 | 3/16/2026 |
| 1.0.212-beta | 43 | 3/13/2026 |
| 1.0.202-beta | 47 | 3/10/2026 |
| 1.0.179-beta | 43 | 3/9/2026 |
| 1.0.158-beta | 37 | 3/9/2026 |
| 1.0.154-beta | 42 | 3/3/2026 |
| 1.0.153-beta | 43 | 3/3/2026 |
| 1.0.144-beta | 45 | 3/2/2026 |
| 1.0.141-beta | 47 | 2/27/2026 |
| 1.0.140-beta | 44 | 2/27/2026 |
| 1.0.139-beta | 44 | 2/27/2026 |
| 1.0.83-beta | 41 | 2/24/2026 |
| 1.0.82-beta | 46 | 2/19/2026 |
| 1.0.80-beta | 53 | 2/18/2026 |
| 1.0.55-beta | 51 | 2/13/2026 |