<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: lifeme2027</title>
    <description>The latest articles on DEV Community by lifeme2027 (@lifeme2027).</description>
    <link>https://dev.to/lifeme2027</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4226266%2F308ab614-249d-4eb3-b2f8-a8b40bb072ad.jpg</url>
      <title>DEV Community: lifeme2027</title>
      <link>https://dev.to/lifeme2027</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lifeme2027"/>
    <language>en</language>
    <item>
      <title>在 C# 里连接 Kaspa 节点并发一笔交易</title>
      <dc:creator>lifeme2027</dc:creator>
      <pubDate>Wed, 04 Nov 2026 13:15:55 +0000</pubDate>
      <link>https://dev.to/lifeme2027/zai-c-li-lian-jie-kaspa-jie-dian-bing-fa-bi-jiao-yi-lea</link>
      <guid>https://dev.to/lifeme2027/zai-c-li-lian-jie-kaspa-jie-dian-bing-fa-bi-jiao-yi-lea</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;免责声明：&lt;/strong&gt; NKaspa 目前是 alpha 版本（0.1.11-alpha），未经安全审计。它是社区项目，不是 Kaspa 官方项目。本教程只在 Kaspa testnet 上演示（&lt;strong&gt;仅 testnet-10&lt;/strong&gt;），请只用测试币和小额。不要用于真实资金。公共节点由第三方运营，不保证可用。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;本文介绍如何用社区 .NET 库 NKaspa（NuGet 上可直接安装）在 C# 里使用 Kaspa，也就是一个 Kaspa .NET SDK 的入门：通过 wRPC 连接 testnet-10 节点、检查节点状态、读取 UTXO、构建并签名交易、在本地校验，然后提交。&lt;/p&gt;

&lt;p&gt;流程：&lt;code&gt;KaspaRpcClient.Create&lt;/code&gt; → &lt;code&gt;ConnectAsync&lt;/code&gt; → &lt;code&gt;GetServerInfoAsync&lt;/code&gt; → &lt;code&gt;GetUtxosByAddressesAsync&lt;/code&gt; → &lt;code&gt;TransactionBuilder&lt;/code&gt; → &lt;code&gt;tx.Verify&lt;/code&gt; → &lt;code&gt;SubmitTransactionAsync&lt;/code&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  用的是 wRPC，不是 gRPC
&lt;/h2&gt;

&lt;p&gt;NKaspa 不包含 gRPC 客户端。它通过 &lt;strong&gt;WebSocket 上的 wRPC&lt;/strong&gt; 与 kaspad 通信，编码可选 JSON 或 Borsh。如果你需要 gRPC，请改用 Kaspa 官方的 proto 文件（见 docs.kaspa.org）。&lt;/p&gt;

&lt;h2&gt;
  
  
  准备工作
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;.NET 8 SDK（仓库的 &lt;code&gt;global.json&lt;/code&gt; 固定为 8.0.427，&lt;code&gt;rollForward: latestFeature&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;安装包：
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package NKaspa &lt;span class="nt"&gt;--version&lt;/span&gt; 0.1.11-alpha
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;0.1.11-alpha 已在 nuget.org 上线，可在新建的 &lt;code&gt;net8.0&lt;/code&gt; 控制台项目中正常还原（restore）。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;一个 testnet-10 wRPC 节点。可以自建 kaspad，也可以使用 &lt;code&gt;KaspaResolver&lt;/code&gt; 源码（&lt;code&gt;src/NKaspa/RPC/KaspaResolver.cs&lt;/code&gt;）中列出的两个 testnet-10 回退节点之一：

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wss://vector-10.kaspa.green/kaspa/testnet-10/wrpc/json&lt;/code&gt;（或 &lt;code&gt;/borsh&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wss://muon-10.kaspa.blue/kaspa/testnet-10/wrpc/json&lt;/code&gt;（或 &lt;code&gt;/borsh&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些是第三方节点，不由 NKaspa 运营，不保证可用。撰写时 &lt;code&gt;vector-10&lt;/code&gt; 可以连通，&lt;code&gt;muon-10&lt;/code&gt; 连不上。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;testnet 测试币：Kaspa 官方 Wiki 列出的 testnet-10 水龙头 &lt;a href="https://faucet-tn10.kaspanet.io/" rel="noopener noreferrer"&gt;https://faucet-tn10.kaspanet.io/&lt;/a&gt; 。请在浏览器中打开，页面会要求人机验证（captcha）。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  端口表：选 JSON 还是 Borsh？
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;网络&lt;/th&gt;
&lt;th&gt;gRPC（会被拒绝）&lt;/th&gt;
&lt;th&gt;wRPC Borsh&lt;/th&gt;
&lt;th&gt;wRPC JSON&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;mainnet&lt;/td&gt;
&lt;td&gt;16110&lt;/td&gt;
&lt;td&gt;17110&lt;/td&gt;
&lt;td&gt;18110&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;testnet-10&lt;/td&gt;
&lt;td&gt;16210&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17210&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18210&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;simnet&lt;/td&gt;
&lt;td&gt;16510&lt;/td&gt;
&lt;td&gt;17510&lt;/td&gt;
&lt;td&gt;18510&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;devnet&lt;/td&gt;
&lt;td&gt;16610&lt;/td&gt;
&lt;td&gt;17610&lt;/td&gt;
&lt;td&gt;18610&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;使用 &lt;code&gt;KaspaRpcEncoding.Auto&lt;/code&gt;（默认值）时，URL &lt;strong&gt;路径&lt;/strong&gt;优先于端口：路径含 &lt;code&gt;/wrpc/borsh&lt;/code&gt; 选 Borsh，含 &lt;code&gt;/wrpc/json&lt;/code&gt; 选 JSON；否则按默认 wRPC 端口决定。如果路径和端口都无法识别（例如 443 端口的 &lt;code&gt;wss&lt;/code&gt; 且路径没有提示），&lt;code&gt;Create&lt;/code&gt; 会抛异常，不会自行猜测。填 gRPC 端口时一律抛 &lt;code&gt;ArgumentException&lt;/code&gt;，错误信息会提示改用 wRPC 端口。&lt;/p&gt;

&lt;p&gt;如果服务器发来的帧类型不对（JSON 客户端收到二进制帧，或 Borsh 客户端收到文本帧），挂起的请求会立即以 &lt;code&gt;KaspaRpcEncodingMismatchException&lt;/code&gt; 失败。客户端不会等到超时，也不会重连。&lt;/p&gt;

&lt;p&gt;两种编码功能相同，跟着节点开放的端口选即可。&lt;/p&gt;

&lt;h2&gt;
  
  
  连接节点
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;NKaspa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;NKaspa.RPC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KaspaRpcClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// returns a KaspaRpcConnection; does NOT connect&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConnectAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;方法签名：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;KaspaRpcConnection&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;KaspaRpcEncoding&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KaspaRpcEncoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Auto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;KaspaWebSocketOptions&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;KaspaRpcConnection&lt;/code&gt; 实现了 &lt;code&gt;IKaspaRpcClient&lt;/code&gt; 和 &lt;code&gt;IAsyncDisposable&lt;/code&gt;，请用 &lt;code&gt;await using&lt;/code&gt; 释放。&lt;code&gt;KaspaWebSocketOptions&lt;/code&gt; 可设置连接超时（默认 15 秒）、请求超时（默认 30 秒）、&lt;code&gt;AutoReconnect&lt;/code&gt; 和 &lt;code&gt;MaxMessageBytes&lt;/code&gt;（默认 16 MiB）。&lt;/p&gt;

&lt;h2&gt;
  
  
  先检查节点
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetServerInfoAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"network=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NetworkId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; synced=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSynced&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; utxoindex=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasUtxoIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSynced&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasUtxoIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// stop here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;节点未同步或没有开启 UTXO index 时请停止；没有 UTXO index 就无法列出 UTXO。在 testnet-10 上，&lt;code&gt;NetworkId&lt;/code&gt; 为 &lt;code&gt;testnet-10&lt;/code&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  从助记词派生 testnet 地址
&lt;/h2&gt;

&lt;p&gt;从环境变量读取助记词。不要把它写进源代码、日志或截图。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NKASPA_MNEMONIC"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mnemonic&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Mnemonic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Wordlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;English&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;mnemonic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValidChecksum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mnemonic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DeriveExtKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Derive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KeyPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;KaspaReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;changeKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Derive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KeyPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;KaspaChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;spendKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrivateKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Testnet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spendKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                      &lt;span class="c1"&gt;// kaspatest:...&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;changeAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;changeKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrivateKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;请始终显式传入 &lt;code&gt;Network.Testnet&lt;/code&gt;。在 0.1.11-alpha 中，&lt;code&gt;ExtPubKey.Parse&lt;/code&gt; 无法区分主网和 testnet 的扩展公钥，网络由你传给 &lt;code&gt;GetAddress&lt;/code&gt; 的参数决定。testnet 地址以 &lt;code&gt;kaspatest:&lt;/code&gt; 开头。&lt;/p&gt;

&lt;p&gt;另见仓库中的 &lt;code&gt;docs/tutorials/generate-kaspa-address-csharp.md&lt;/code&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  读取 UTXO 并转换为 Coin
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;listed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUtxosByAddressesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"No UTXO on &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;own&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Nothing was submitted."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;coins&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Coin&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;listed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;listed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Coin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Outpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;其他读取方法：&lt;code&gt;GetBalanceByAddressAsync&lt;/code&gt;、&lt;code&gt;GetBalancesByAddressesAsync&lt;/code&gt;、&lt;code&gt;GetFeeEstimateAsync&lt;/code&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  构建并签名交易
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;payee&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KaspaAddress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Coins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.1m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// small amount&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TransactionBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;AddKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spendKey&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;AddCoins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendMinimumRelayFee&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true = sign&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;说明：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;也可以用 &lt;code&gt;SendEstimatedFees(FeeRate)&lt;/code&gt; 代替最低中继手续费。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SetVersion(1)&lt;/code&gt; 构建版本 1 的交易。&lt;/li&gt;
&lt;li&gt;低于 &lt;code&gt;MinimumChange&lt;/code&gt;（默认 0.02 KAS）的找零会并入手续费。可通过 &lt;code&gt;builder.ChangeFoldedIntoFee&lt;/code&gt; 查看并入了多少。&lt;/li&gt;
&lt;li&gt;参考数据来自仓库的离线示例（不联网）：单输入支付 203,600 sompi（0.002036 KAS），中继 mass 为 2,036。这些只是离线示例数值，不代表节点实际收取的费用。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  广播前在本地校验
&lt;/h2&gt;

&lt;p&gt;以下各项全部通过才提交：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;有一个输出按 &lt;code&gt;amount&lt;/code&gt; 准确支付到收款方脚本。&lt;/li&gt;
&lt;li&gt;输入总额 ≥ 输出总额。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tx.Verify(entries)&lt;/code&gt; 返回 true（&lt;code&gt;entries&lt;/code&gt; 为每个输入对应的 &lt;code&gt;UtxoEntry&lt;/code&gt;，按输入顺序）。也可以用 &lt;code&gt;Script.VerifyScript&lt;/code&gt; 逐个输入检查。&lt;/li&gt;
&lt;li&gt;手续费等于 &lt;code&gt;MassCalculator.MinimumRelayFee(MassCalculator.Calculate(tx, entries))&lt;/code&gt;。
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;paid&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;outputSum&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inputSum&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// nothing submitted&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fee&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inputSum&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;outputSum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MassCalculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MinimumRelayFee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MassCalculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fee&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;构建 &lt;code&gt;entries&lt;/code&gt;、&lt;code&gt;inputSum&lt;/code&gt;、&lt;code&gt;outputSum&lt;/code&gt; 和 &lt;code&gt;paid&lt;/code&gt; 的完整循环见 &lt;code&gt;samples/Tutorials/SendTutorial.cs&lt;/code&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  提交到 testnet
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SubmitTransactionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"submitted: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;签名：&lt;code&gt;Task&amp;lt;uint256&amp;gt; SubmitTransactionAsync(Transaction transaction, bool allowOrphan = false, CancellationToken cancellationToken = default)&lt;/code&gt;，返回交易 id。&lt;/p&gt;

&lt;p&gt;输出示例：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;network: testnet
fee-sompi: &amp;lt;fee&amp;gt;
submitted: &amp;lt;txid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;完整方法是 &lt;code&gt;samples/Tutorials&lt;/code&gt; 中的 &lt;code&gt;SendTutorial.SendOnTestnetAsync&lt;/code&gt;。它默认使用 &lt;code&gt;Network.Testnet&lt;/code&gt;，未设置 &lt;code&gt;NKASPA_MNEMONIC&lt;/code&gt;、&lt;code&gt;NKASPA_WRPC_URL&lt;/code&gt;、&lt;code&gt;NKASPA_TO&lt;/code&gt; 和 &lt;code&gt;NKASPA_AMOUNT&lt;/code&gt; 时拒绝运行，默认也拒绝主网。示例的 &lt;code&gt;Main&lt;/code&gt; 不会调用它。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;未做端到端实测，仅为示例代码。&lt;/strong&gt; 上文的连接和查询步骤（&lt;code&gt;ConnectAsync&lt;/code&gt;、&lt;code&gt;GetServerInfoAsync&lt;/code&gt;、余额和 UTXO 读取）已通过 JSON 和 Borsh 两种编码在 &lt;code&gt;vector-10&lt;/code&gt; testnet 节点上实测成功。构建、签名和提交步骤尚未在真实节点上运行。请在你自己的环境中用测试币自行验证。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;samples/Tutorials&lt;/code&gt; 只运行离线检查，不会发送。要试发送路径，请新建一个引用 NKaspa 0.1.11-alpha 的控制台项目，把 &lt;code&gt;SendTutorial.cs&lt;/code&gt; 复制进去，然后自己调用：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;NKaspa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Samples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tutorials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendTutorial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendOnTestnetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NKASPA_MNEMONIC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;throwaway testnet mnemonic&amp;gt;'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NKASPA_WRPC_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'wss://vector-10.kaspa.green/kaspa/testnet-10/wrpc/borsh'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NKASPA_TO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;another kaspatest: address&amp;gt;'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NKASPA_AMOUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'0.1'&lt;/span&gt;
dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  断线重连与订阅
&lt;/h2&gt;

&lt;p&gt;在 &lt;code&gt;KaspaWebSocketOptions&lt;/code&gt; 中设置 &lt;code&gt;AutoReconnect&lt;/code&gt; 可在断线后重连。&lt;strong&gt;订阅不会自动恢复。&lt;/strong&gt; 重连后，请重新调用 &lt;code&gt;NotifyUtxosChangedAsync&lt;/code&gt;（以及其他 &lt;code&gt;Notify*&lt;/code&gt;），并重新查询余额，以免漏掉变化。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;KaspaRpcConnection&lt;/code&gt; 暴露两个事件：&lt;code&gt;Reconnected&lt;/code&gt;（&lt;code&gt;Action&lt;/code&gt;，在断开的 socket 重新打开后触发；首次 &lt;code&gt;ConnectAsync&lt;/code&gt; 不触发）和 &lt;code&gt;StateChanged&lt;/code&gt;（&lt;code&gt;Action&amp;lt;KaspaWebSocketState&amp;gt;&lt;/code&gt;）。&lt;code&gt;State&lt;/code&gt; 是只读属性，不是事件。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StateChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"state: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reconnected&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Subscriptions are gone: subscribe again with NotifyUtxosChangedAsync&lt;/span&gt;
    &lt;span class="c1"&gt;// and re-read the balance (do async work off this handler).&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;完整的只读（watch-only）示例见 &lt;code&gt;samples/AspNetCheckout&lt;/code&gt;（&lt;strong&gt;仅在 &lt;code&gt;main&lt;/code&gt; 上&lt;/strong&gt;，于 0.1.11-alpha 发布之后加入；它是示例，不是库 API）。&lt;/p&gt;

&lt;h2&gt;
  
  
  常见错误排查
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;提到 gRPC 的 &lt;code&gt;ArgumentException&lt;/code&gt;&lt;/strong&gt; — 你用了 16x10 端口。testnet-10 请用 17210（Borsh）或 18210（JSON）。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;443 端口的 URL 让 &lt;code&gt;Create&lt;/code&gt; 抛异常&lt;/strong&gt; — 在路径中加上 &lt;code&gt;/wrpc/json&lt;/code&gt; 或 &lt;code&gt;/wrpc/borsh&lt;/code&gt;，或显式传入编码。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;KaspaRpcEncodingMismatchException&lt;/code&gt;&lt;/strong&gt; — 节点使用的是另一种编码。请更换路径或编码。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;没有 UTXO&lt;/strong&gt; — 先从水龙头给地址充值并等待；同时确认节点开启了 UTXO index。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;节点拒绝手续费&lt;/strong&gt; — 按 Toccata 之前的费率（1 sompi/gram）算出的手续费低于 Toccata 之后的下限（100 sompi/gram）。请使用 &lt;code&gt;SendMinimumRelayFee()&lt;/code&gt; 或当前网络的手续费估算。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;找零“消失”了&lt;/strong&gt; — 找零低于 &lt;code&gt;MinimumChange&lt;/code&gt;，已并入手续费；查看 &lt;code&gt;ChangeFoldedIntoFee&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;C# 有 Kaspa SDK 吗？&lt;/strong&gt; 有一个社区库 NKaspa（NuGet &lt;code&gt;NKaspa&lt;/code&gt;，0.1.11-alpha，未审计）。它提供地址、HD 派生、wRPC 客户端（JSON 和 Borsh）以及交易构建和签名。它不是 Kaspa 官方项目。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NKaspa 支持 gRPC 吗？&lt;/strong&gt; 不支持，只支持 WebSocket 上的 wRPC。把 16x10 的 gRPC 端口传给 &lt;code&gt;KaspaRpcClient.Create&lt;/code&gt; 会抛异常，并提示改用 17x10（Borsh）或 18x10（JSON）。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON 和 Borsh 选哪个？&lt;/strong&gt; 都可以，两者提供相同的调用。&lt;code&gt;KaspaRpcEncoding.Auto&lt;/code&gt; 会根据 URL 路径或端口自动选择，用节点提供的那种即可。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;KaspaRpcClient.Create&lt;/code&gt; 会自动连接吗？&lt;/strong&gt; 不会。它返回 &lt;code&gt;KaspaRpcConnection&lt;/code&gt;；需要调用 &lt;code&gt;ConnectAsync&lt;/code&gt;，并用 &lt;code&gt;await using&lt;/code&gt; 释放。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;怎么在 C# 里发送 Kaspa 交易？&lt;/strong&gt; 读取 UTXO，用 &lt;code&gt;TransactionBuilder&lt;/code&gt; 构建并签名，用 &lt;code&gt;tx.Verify&lt;/code&gt; 在本地校验，然后调用 &lt;code&gt;SubmitTransactionAsync&lt;/code&gt;。本教程只在 testnet 上演示。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;断线后订阅还在吗？&lt;/strong&gt; 不在。开启 &lt;code&gt;AutoReconnect&lt;/code&gt; 后连接会恢复，但需要重新订阅。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;怎么确保用的是 testnet？&lt;/strong&gt; 显式传入 &lt;code&gt;Network.Testnet&lt;/code&gt;，并检查地址以 &lt;code&gt;kaspatest:&lt;/code&gt; 开头。不要依赖 &lt;code&gt;ExtPubKey.Parse&lt;/code&gt; 来判断网络。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;能离线签名吗？&lt;/strong&gt; 可以。构建和签名不需要连接。&lt;code&gt;BuildPSKT&lt;/code&gt; 可生成部分签名交易。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;能用于真实资金吗？&lt;/strong&gt; 不建议。NKaspa 是 alpha 版本、未经审计。请先在 testnet 上用小额测试，并自行评估风险。&lt;/p&gt;

&lt;h2&gt;
  
  
  下一步
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;收款：&lt;code&gt;samples/AspNetCheckout&lt;/code&gt;（仅在 main 上）以及收款教程（发布后补链接）。&lt;/li&gt;
&lt;li&gt;生成地址：&lt;code&gt;docs/tutorials/generate-kaspa-address-csharp.md&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;NKaspa 仓库中的发送教程：&lt;code&gt;docs/tutorials/send-kaspa-transaction-dotnet.md&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;PSKT 多签：&lt;code&gt;samples/PsktMultisig&lt;/code&gt;。节点查询：&lt;code&gt;samples/NodeQuery&lt;/code&gt;。花费：&lt;code&gt;samples/Spend&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;NBitcoin 用户：&lt;code&gt;docs/NBitcoin-Mapping.en.md&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;NKaspaDoc（API 参考与指南，GitHub 仓库）：&lt;a href="https://github.com/lifeme2027/NKaspaDoc" rel="noopener noreferrer"&gt;https://github.com/lifeme2027/NKaspaDoc&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;觉得有用就去 GitHub 给 NKaspa 点个 Star：github.com/lifeme2027/NKaspa。&lt;code&gt;dotnet add package NKaspa --version 0.1.11-alpha&lt;/code&gt;，在 testnet 上跑一遍，遇到问题欢迎提 issue。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;免责声明：&lt;/strong&gt; NKaspa 目前是 alpha 版本（0.1.11-alpha），未经安全审计。它是社区项目，不是 Kaspa 官方项目。本教程只在 Kaspa testnet 上演示（仅 testnet-10），请只用测试币和小额。不要用于真实资金。公共节点由第三方运营，不保证可用。&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>kaspa</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Connect to a Kaspa Node and Send a Transaction in C# (.NET, Testnet)</title>
      <dc:creator>lifeme2027</dc:creator>
      <pubDate>Wed, 04 Nov 2026 13:14:35 +0000</pubDate>
      <link>https://dev.to/lifeme2027/connect-to-a-kaspa-node-and-send-a-transaction-in-c-net-testnet-3d7m</link>
      <guid>https://dev.to/lifeme2027/connect-to-a-kaspa-node-and-send-a-transaction-in-c-net-testnet-3d7m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; NKaspa is alpha software (0.1.11-alpha) and has not been security-audited. It is a community project, not an official Kaspa project. This tutorial runs on Kaspa &lt;strong&gt;testnet-10 only&lt;/strong&gt;. Use test coins and small amounts. Do not use it for real funds. Public nodes are run by third parties and are not guaranteed to be available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This guide shows how to use Kaspa from C# with NKaspa, a community .NET library on NuGet. You will connect to a testnet-10 node over wRPC, check the node, read UTXOs, build and sign a transaction, verify it locally, and submit it.&lt;/p&gt;

&lt;p&gt;Flow: &lt;code&gt;KaspaRpcClient.Create&lt;/code&gt; → &lt;code&gt;ConnectAsync&lt;/code&gt; → &lt;code&gt;GetServerInfoAsync&lt;/code&gt; → &lt;code&gt;GetUtxosByAddressesAsync&lt;/code&gt; → &lt;code&gt;TransactionBuilder&lt;/code&gt; → &lt;code&gt;tx.Verify&lt;/code&gt; → &lt;code&gt;SubmitTransactionAsync&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  wRPC, not gRPC
&lt;/h2&gt;

&lt;p&gt;NKaspa does not include a gRPC client. It talks to kaspad over &lt;strong&gt;wRPC on a WebSocket&lt;/strong&gt;, using either JSON or Borsh encoding. If you need gRPC, use the official Kaspa proto files instead (see docs.kaspa.org).&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;.NET 8 SDK (the repo's &lt;code&gt;global.json&lt;/code&gt; pins 8.0.427 with &lt;code&gt;rollForward: latestFeature&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The package:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package NKaspa &lt;span class="nt"&gt;--version&lt;/span&gt; 0.1.11-alpha
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;0.1.11-alpha is on nuget.org and restores into a new &lt;code&gt;net8.0&lt;/code&gt; console project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A testnet-10 wRPC node. Run your own kaspad, or use one of the two testnet-10 fallback nodes listed in the &lt;code&gt;KaspaResolver&lt;/code&gt; source (&lt;code&gt;src/NKaspa/RPC/KaspaResolver.cs&lt;/code&gt;):

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wss://vector-10.kaspa.green/kaspa/testnet-10/wrpc/json&lt;/code&gt; (or &lt;code&gt;/borsh&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wss://muon-10.kaspa.blue/kaspa/testnet-10/wrpc/json&lt;/code&gt; (or &lt;code&gt;/borsh&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are third-party nodes, not run by NKaspa, and they are not guaranteed to be available. At the time of writing, &lt;code&gt;vector-10&lt;/code&gt; answered and &lt;code&gt;muon-10&lt;/code&gt; did not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Testnet KAS from the testnet-10 faucet listed in the official Kaspa Wiki: &lt;a href="https://faucet-tn10.kaspanet.io/" rel="noopener noreferrer"&gt;https://faucet-tn10.kaspanet.io/&lt;/a&gt; . Open it in a browser; it asks for a human check (captcha).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ports: JSON or Borsh?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Network&lt;/th&gt;
&lt;th&gt;gRPC (rejected)&lt;/th&gt;
&lt;th&gt;wRPC Borsh&lt;/th&gt;
&lt;th&gt;wRPC JSON&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;mainnet&lt;/td&gt;
&lt;td&gt;16110&lt;/td&gt;
&lt;td&gt;17110&lt;/td&gt;
&lt;td&gt;18110&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;testnet-10&lt;/td&gt;
&lt;td&gt;16210&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17210&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18210&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;simnet&lt;/td&gt;
&lt;td&gt;16510&lt;/td&gt;
&lt;td&gt;17510&lt;/td&gt;
&lt;td&gt;18510&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;devnet&lt;/td&gt;
&lt;td&gt;16610&lt;/td&gt;
&lt;td&gt;17610&lt;/td&gt;
&lt;td&gt;18610&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With &lt;code&gt;KaspaRpcEncoding.Auto&lt;/code&gt; (the default), the URL &lt;strong&gt;path&lt;/strong&gt; wins over the port: a path with &lt;code&gt;/wrpc/borsh&lt;/code&gt; selects Borsh and &lt;code&gt;/wrpc/json&lt;/code&gt; selects JSON. Otherwise the default wRPC port decides. If neither the path nor the port is recognized (for example &lt;code&gt;wss&lt;/code&gt; on 443 with no hint), &lt;code&gt;Create&lt;/code&gt; throws; it does not guess. A gRPC port always throws &lt;code&gt;ArgumentException&lt;/code&gt; with a message pointing you to the wRPC ports.&lt;/p&gt;

&lt;p&gt;If the server sends the wrong frame type (a JSON client gets binary, or a Borsh client gets text), pending requests fail with &lt;code&gt;KaspaRpcEncodingMismatchException&lt;/code&gt; right away. The client does not wait for a timeout and does not reconnect.&lt;/p&gt;

&lt;p&gt;The two encodings do the same job. Pick whichever your node exposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect to the node
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;NKaspa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;NKaspa.RPC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KaspaRpcClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// returns a KaspaRpcConnection; does NOT connect&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConnectAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signature is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;KaspaRpcConnection&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;KaspaRpcEncoding&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KaspaRpcEncoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Auto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;KaspaWebSocketOptions&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;KaspaRpcConnection&lt;/code&gt; implements &lt;code&gt;IKaspaRpcClient&lt;/code&gt; and &lt;code&gt;IAsyncDisposable&lt;/code&gt;, so dispose it with &lt;code&gt;await using&lt;/code&gt;. &lt;code&gt;KaspaWebSocketOptions&lt;/code&gt; controls the connect timeout (default 15 s), request timeout (default 30 s), &lt;code&gt;AutoReconnect&lt;/code&gt;, and &lt;code&gt;MaxMessageBytes&lt;/code&gt; (default 16 MiB).&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the node first
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetServerInfoAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"network=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NetworkId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; synced=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSynced&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; utxoindex=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasUtxoIndex&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSynced&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasUtxoIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// stop here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop if the node is not synced or does not have the UTXO index; you cannot list UTXOs without it. On testnet-10, &lt;code&gt;NetworkId&lt;/code&gt; is &lt;code&gt;testnet-10&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Derive a testnet address from a mnemonic
&lt;/h2&gt;

&lt;p&gt;Read the mnemonic from an environment variable. Never put it in source code, logs, or screenshots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NKASPA_MNEMONIC"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mnemonic&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Mnemonic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Wordlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;English&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;mnemonic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValidChecksum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mnemonic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DeriveExtKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Derive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KeyPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;KaspaReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;changeKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Derive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KeyPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;KaspaChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;spendKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrivateKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Testnet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spendKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                      &lt;span class="c1"&gt;// kaspatest:...&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;changeAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;changeKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrivateKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always pass &lt;code&gt;Network.Testnet&lt;/code&gt; explicitly. In 0.1.11-alpha, &lt;code&gt;ExtPubKey.Parse&lt;/code&gt; cannot tell a mainnet extended public key from a testnet one, so the network comes from what you pass to &lt;code&gt;GetAddress&lt;/code&gt;. Testnet addresses start with &lt;code&gt;kaspatest:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See also: &lt;code&gt;docs/tutorials/generate-kaspa-address-csharp.md&lt;/code&gt; in the repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read UTXOs and turn them into coins
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;listed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUtxosByAddressesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"No UTXO on &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;own&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Nothing was submitted."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;coins&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Coin&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;listed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;listed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Coin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Outpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other read calls: &lt;code&gt;GetBalanceByAddressAsync&lt;/code&gt;, &lt;code&gt;GetBalancesByAddressesAsync&lt;/code&gt;, &lt;code&gt;GetFeeEstimateAsync&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build and sign the transaction
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;payee&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KaspaAddress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Coins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.1m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// small amount&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TransactionBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;AddKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spendKey&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;AddCoins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendMinimumRelayFee&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true = sign&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SendEstimatedFees(FeeRate)&lt;/code&gt; is an alternative to the minimum relay fee.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SetVersion(1)&lt;/code&gt; builds a version-1 transaction.&lt;/li&gt;
&lt;li&gt;Change below &lt;code&gt;MinimumChange&lt;/code&gt; (default 0.02 KAS) is folded into the fee. Check &lt;code&gt;builder.ChangeFoldedIntoFee&lt;/code&gt; to see how much.&lt;/li&gt;
&lt;li&gt;Reference numbers from the repo's offline sample (no network): one input pays 203,600 sompi (0.002036 KAS) at relay mass 2,036. These are offline sample values, not a promise of what a node will charge.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Verify locally before you broadcast
&lt;/h2&gt;

&lt;p&gt;Do not submit unless all of these pass:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;One output pays exactly &lt;code&gt;amount&lt;/code&gt; to the payee's script.&lt;/li&gt;
&lt;li&gt;Sum of inputs ≥ sum of outputs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tx.Verify(entries)&lt;/code&gt; returns true (&lt;code&gt;entries&lt;/code&gt; = the &lt;code&gt;UtxoEntry&lt;/code&gt; of each input, in input order). You can also check each input with &lt;code&gt;Script.VerifyScript&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The fee equals &lt;code&gt;MassCalculator.MinimumRelayFee(MassCalculator.Calculate(tx, entries))&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;paid&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;outputSum&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inputSum&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// nothing submitted&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fee&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inputSum&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;outputSum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MassCalculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MinimumRelayFee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MassCalculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fee&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full loop that builds &lt;code&gt;entries&lt;/code&gt;, &lt;code&gt;inputSum&lt;/code&gt;, &lt;code&gt;outputSum&lt;/code&gt; and &lt;code&gt;paid&lt;/code&gt; is in &lt;code&gt;samples/Tutorials/SendTutorial.cs&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Submit to testnet
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SubmitTransactionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"submitted: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Signature: &lt;code&gt;Task&amp;lt;uint256&amp;gt; SubmitTransactionAsync(Transaction transaction, bool allowOrphan = false, CancellationToken cancellationToken = default)&lt;/code&gt;. It returns the transaction id.&lt;/p&gt;

&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;network: testnet
fee-sompi: &amp;lt;fee&amp;gt;
submitted: &amp;lt;txid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete method is &lt;code&gt;SendTutorial.SendOnTestnetAsync&lt;/code&gt; in &lt;code&gt;samples/Tutorials&lt;/code&gt;. It defaults to &lt;code&gt;Network.Testnet&lt;/code&gt; and refuses to run unless &lt;code&gt;NKASPA_MNEMONIC&lt;/code&gt;, &lt;code&gt;NKASPA_WRPC_URL&lt;/code&gt;, &lt;code&gt;NKASPA_TO&lt;/code&gt; and &lt;code&gt;NKASPA_AMOUNT&lt;/code&gt; are set. It also refuses mainnet by default. The sample's &lt;code&gt;Main&lt;/code&gt; does not call it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Not tested end to end; example code.&lt;/strong&gt; The connect and query steps above (&lt;code&gt;ConnectAsync&lt;/code&gt;, &lt;code&gt;GetServerInfoAsync&lt;/code&gt;, balance and UTXO reads) were run successfully against the &lt;code&gt;vector-10&lt;/code&gt; testnet node over both JSON and Borsh. The build, sign and submit steps have not been run against a live node. Verify them in your own environment with test coins.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;samples/Tutorials&lt;/code&gt; only runs offline checks and does not send. To try the send path, create a console project that references NKaspa 0.1.11-alpha, copy in &lt;code&gt;SendTutorial.cs&lt;/code&gt;, and call it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;NKaspa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Samples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tutorials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendTutorial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendOnTestnetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NKASPA_MNEMONIC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;throwaway testnet mnemonic&amp;gt;'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NKASPA_WRPC_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'wss://vector-10.kaspa.green/kaspa/testnet-10/wrpc/borsh'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NKASPA_TO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;another kaspatest: address&amp;gt;'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NKASPA_AMOUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'0.1'&lt;/span&gt;
dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reconnects and subscriptions
&lt;/h2&gt;

&lt;p&gt;Set &lt;code&gt;AutoReconnect&lt;/code&gt; in &lt;code&gt;KaspaWebSocketOptions&lt;/code&gt; to reconnect after a drop. &lt;strong&gt;Subscriptions are not restored automatically.&lt;/strong&gt; After a reconnect, call &lt;code&gt;NotifyUtxosChangedAsync&lt;/code&gt; (and any other &lt;code&gt;Notify*&lt;/code&gt;) again and re-query the balance so you don't miss changes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;KaspaRpcConnection&lt;/code&gt; exposes two events: &lt;code&gt;Reconnected&lt;/code&gt; (&lt;code&gt;Action&lt;/code&gt;, raised after a dropped socket opens again; not raised by the first &lt;code&gt;ConnectAsync&lt;/code&gt;) and &lt;code&gt;StateChanged&lt;/code&gt; (&lt;code&gt;Action&amp;lt;KaspaWebSocketState&amp;gt;&lt;/code&gt;). &lt;code&gt;State&lt;/code&gt; is a read-only property, not an event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StateChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"state: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reconnected&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Subscriptions are gone: subscribe again with NotifyUtxosChangedAsync&lt;/span&gt;
    &lt;span class="c1"&gt;// and re-read the balance (do async work off this handler).&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a full watch-only example, see &lt;code&gt;samples/AspNetCheckout&lt;/code&gt; (&lt;strong&gt;on &lt;code&gt;main&lt;/code&gt; only&lt;/strong&gt;, added after the 0.1.11-alpha release; it is a sample, not a library API).&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ArgumentException&lt;/code&gt; mentioning gRPC&lt;/strong&gt; — you used 16x10. Use 17210 (Borsh) or 18210 (JSON) on testnet-10.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Create&lt;/code&gt; throws on a 443 URL&lt;/strong&gt; — add &lt;code&gt;/wrpc/json&lt;/code&gt; or &lt;code&gt;/wrpc/borsh&lt;/code&gt; to the path, or pass the encoding explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;KaspaRpcEncodingMismatchException&lt;/code&gt;&lt;/strong&gt; — the endpoint speaks the other encoding. Switch the path or encoding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No UTXOs&lt;/strong&gt; — fund the address from a faucet and wait; also confirm the node has the UTXO index.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node rejects the fee&lt;/strong&gt; — a fee computed at the pre-Toccata rate (1 sompi/gram) is below the post-Toccata floor (100 sompi/gram). Use &lt;code&gt;SendMinimumRelayFee()&lt;/code&gt; or a fee estimate for the current network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change "disappeared"&lt;/strong&gt; — it was below &lt;code&gt;MinimumChange&lt;/code&gt; and folded into the fee; check &lt;code&gt;ChangeFoldedIntoFee&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is there a Kaspa SDK for C#?&lt;/strong&gt; Yes, there is a community library, NKaspa (NuGet &lt;code&gt;NKaspa&lt;/code&gt;, 0.1.11-alpha, unaudited). It covers addresses, HD derivation, a wRPC client (JSON and Borsh), and transaction building and signing. It is not an official Kaspa project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does NKaspa support gRPC?&lt;/strong&gt; No. It uses wRPC over WebSocket only. Passing a 16x10 gRPC port to &lt;code&gt;KaspaRpcClient.Create&lt;/code&gt; throws and points you to 17x10 (Borsh) or 18x10 (JSON).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I use JSON or Borsh?&lt;/strong&gt; Either. Both expose the same calls. &lt;code&gt;KaspaRpcEncoding.Auto&lt;/code&gt; picks one from the URL path or port, so use whatever your node offers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does &lt;code&gt;KaspaRpcClient.Create&lt;/code&gt; connect automatically?&lt;/strong&gt; No. It returns a &lt;code&gt;KaspaRpcConnection&lt;/code&gt;; call &lt;code&gt;ConnectAsync&lt;/code&gt;, and dispose it with &lt;code&gt;await using&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I send a Kaspa transaction in C#?&lt;/strong&gt; Read UTXOs, build and sign with &lt;code&gt;TransactionBuilder&lt;/code&gt;, verify locally with &lt;code&gt;tx.Verify&lt;/code&gt;, then call &lt;code&gt;SubmitTransactionAsync&lt;/code&gt;. This tutorial only does this on testnet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do subscriptions survive a disconnect?&lt;/strong&gt; No. With &lt;code&gt;AutoReconnect&lt;/code&gt; the connection comes back, but you must subscribe again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I make sure I'm on testnet?&lt;/strong&gt; Pass &lt;code&gt;Network.Testnet&lt;/code&gt; explicitly and check that addresses start with &lt;code&gt;kaspatest:&lt;/code&gt;. Don't rely on &lt;code&gt;ExtPubKey.Parse&lt;/code&gt; to tell you the network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I sign offline?&lt;/strong&gt; Yes. Building and signing need no connection. &lt;code&gt;BuildPSKT&lt;/code&gt; produces a partially signed transaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I use it with real funds?&lt;/strong&gt; Not recommended. NKaspa is alpha and unaudited. Test on testnet with small amounts and judge the risk yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Accept payments: &lt;code&gt;samples/AspNetCheckout&lt;/code&gt; (main only) and the checkout tutorial (link after publication).&lt;/li&gt;
&lt;li&gt;Generate addresses: &lt;code&gt;docs/tutorials/generate-kaspa-address-csharp.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Send tutorial in the NKaspa repo: &lt;code&gt;docs/tutorials/send-kaspa-transaction-dotnet.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Multisig with PSKT: &lt;code&gt;samples/PsktMultisig&lt;/code&gt;. Node reads: &lt;code&gt;samples/NodeQuery&lt;/code&gt;. Spending: &lt;code&gt;samples/Spend&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;NBitcoin users: &lt;code&gt;docs/NBitcoin-Mapping.en.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;NKaspaDoc (API reference and guides, GitHub repo): &lt;a href="https://github.com/lifeme2027/NKaspaDoc" rel="noopener noreferrer"&gt;https://github.com/lifeme2027/NKaspaDoc&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this helped, a star on GitHub is welcome: github.com/lifeme2027/NKaspa. Try &lt;code&gt;dotnet add package NKaspa --version 0.1.11-alpha&lt;/code&gt; on testnet and open an issue if you hit a problem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; NKaspa is alpha software (0.1.11-alpha) and has not been security-audited. It is a community project, not an official Kaspa project. This tutorial runs on Kaspa testnet-10 only. Use test coins and small amounts. Do not use it for real funds. Public nodes are run by third parties and are not guaranteed to be available.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>kaspa</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
