Warning
monero-csharp is currently under development and unfunded, expect bugs and breaking changes. Only the wallet and daemon RPC clients (MoneroWalletRpc and MoneroDaemonRpc) are implemented. The client-side wallets (MoneroWalletFull and MoneroWalletKeys) are not implemented yet, and the multisig, view-only and offline wallets are not tested yet.
A C# library for creating Monero applications using RPC and a C ABI to monero v0.18.5.3 'Fluorine Fermi'.
- Supports wallet and daemon RPC clients.
- Supports client-side wallets using native bindings.
- Supports multisig, view-only, and offline wallets.
- Wallet types are interchangeable by conforming to a common interface,
MoneroWallet. - Uses a clearly defined data model and API specification intended to be intuitive and robust.
- Query wallet transactions, transfers, and outputs by their properties.
- Fetch and process binary data from the daemon (e.g. raw blocks).
- Receive notifications when blocks are added to the chain or when wallets sync, send, or receive.

Build C# applications using RPC or the C ABI of monero-c to monero-project/monero. Wallet implementations are interchangeable by conforming to a common abstract class, MoneroWallet.
Every call is async and takes a CancellationToken. The call itself is made by monero-c, and the library runs the calls of a daemon or a wallet one after the other on a thread of its own, so an await never blocks a thread of the pool. Native interop and async design explains how.
using Monero.Common;
using Monero.Daemon;
using Monero.Wallet;
using Monero.Wallet.Model;
// connect to daemon
MoneroDaemon daemon = new MoneroDaemonRpc("http://localhost:38081", "superuser", "abctesting123");
ulong height = await daemon.GetHeightAsync(); // 1523651
var txsInPool = await daemon.GetTxPoolAsync(); // get transactions in the pool
// connect to wallet RPC and open wallet
await using var wallet = new MoneroWalletRpc("http://localhost:38083", "rpc_user", "abc123");
await wallet.OpenWalletAsync("sample_wallet_rpc", "supersecretpassword123");
string primaryAddress = await wallet.GetPrimaryAddressAsync(); // 555zgduFhmKd2o8rPUz...
ulong balance = await wallet.GetBalanceAsync(); // 533648366742
var txs = await wallet.GetTxsAsync(); // get transactions containing transfers to/from the wallet
// receive notifications when funds are received, confirmed, and unlocked
wallet.AddListener(new MyWalletListener());
// synchronize in the background every 5 seconds
await wallet.StartSyncingAsync(5000);
// send funds to the second subaddress of the wallet
var txConfig = new MoneroTxConfig
{
AccountIndex = 0,
Address = await wallet.GetAddressAsync(1, 0),
Amount = 250000000000, // send 0.25 XMR (denominated in atomic units)
Relay = false // create transaction and relay to the network if true
};
MoneroTxWallet createdTx = await wallet.CreateTxAsync(txConfig);
ulong? fee = createdTx.Fee; // "Are you sure you want to send... ?"
await wallet.RelayTxAsync(createdTx); // relay the transaction
// save and close wallet
await wallet.CloseAsync(true);
class MyWalletListener : MoneroWalletListener
{
public override void OnOutputReceived(MoneroOutputWallet output)
{
ulong? amount = output.Amount;
}
}- Native interop and async design: how the classes call monero-c, and how they expose async methods.
- API documentation of monero-python and monero-ts translate to C#, since the model and the method names are the same.
- Data model and API specification.
The package is Monero.NET. It depends on Monero.Native, the package with the native library for Linux, Windows and macOS. Neither is on nuget.org yet, so until then build them from source as described below.
You need a daemon and a wallet RPC server to connect to:
- Download and install Monero CLI.
- Start monerod, e.g.:
./monerod --stagenet(or use a remote daemon). - Start monero-wallet-rpc, e.g.:
./monero-wallet-rpc --daemon-address http://localhost:38081 --stagenet --rpc-bind-port 38083 --rpc-login rpc_user:abc123 --wallet-dir ./
MoneroWalletRpc can't do everything that MoneroWallet declares, because monero-wallet-rpc can't. The methods that throw a MoneroError on it are listed in the documentation of the class.
You need the .NET 10 SDK.
git clone --recurse-submodules https://github.com/libmonero/monero-csharp.git
cd monero-csharp
dotnet buildexternal/monero-c is monero-c, the C library that Monero calls. Monero references its project Monero.Native, which builds the library before Monero and copies it to the output of Monero and of the tests. The first build takes hours, and the next ones only what changed. It needs the dependencies of the .NET build guide.
To skip the native build, for example when the library of the submodule is already built, use -p:MoneroCSkipNative=true.
To pack the library, and to run a program that uses the package and not the project:
dotnet pack Monero/Monero.csproj --configuration Release --output Monero.PackageTests/packages
dotnet run --project Monero.PackageTests --configuration ReleaseThe unit tests read and write the JSON of the models, which is the JSON that monero-c writes, in Monero.UnitTests/Fixtures. They need no node and no native library.
dotnet test Monero.UnitTestsTo get the coverage of the library:
dotnet test Monero.UnitTests --collect "XPlat Code Coverage" --settings coverlet.runsettingsMonero.IntegrationTests tests MoneroDaemonRpc and MoneroWalletRpc against a regtest network of two nodes and three monero-wallet-rpc servers. They need Docker and the package Monero.Native. A wallet test needs about 2 GB of free memory when it mines on the node.
- Set up the docker test environment:
docker compose -f Monero.IntegrationTests/docker-compose.yml up -d
- Configure the RPC endpoints, authentication, and other settings in config.json. The daemon URIs can be overridden with
MONERO_DAEMON_RPC_URIandMONERO_MINING_DAEMON_RPC_URI. - Run the tests:
dotnet test Monero.IntegrationTests - Clean up the docker test environment:
docker compose -f Monero.IntegrationTests/docker-compose.yml down -v
The first run mines the blocks that the tests need, and funds the test wallet from the mining wallet. down -v gives the next run a new chain.
- Build builds
Monero.Nativefrom the submodule (Native), then the library and the tests, collects the coverage, and calls Package, which packs the NuGet package, checks what is in it and uploads it. A tagv<version>has to match the version inVersion.props. - Unit tests waits for the Build run of the same commit, and runs the unit tests that Build made on Linux (x86_64 and arm64), Windows and macOS (arm64 and x86_64), without building them again.
- Integration tests waits for the Build run, and runs
Monero.IntegrationTeststhat Build made on Linux x86_64, against the regtest network ofdocker-compose.yml. - Package tests waits for the Build run, and installs the package that Build made in
Monero.PackageTests, on Linux, Windows and macOS. - Codacy Coverage Reporter sends the coverage of Build to Codacy, when the repository has the
CODACY_PROJECT_TOKENsecret.
This project is licensed under MIT.
If this library has been valuable to you, please consider donating to support its continued development. 🙏
XMR

8BBjopSDammd96czUaBSirDgkFhoEXm3xXwqerDob1mb4kcQArRQs1fjNhPrNokJGtPkebVxX1rE5DnVF4PzSxopHZw8QpE