该NIP-47协议定义了一种客户端通过标准协议访问远程闪电网络钱包的方式。它描述了客户端和钱包服务之间通过Nostr中继上的E2E加密直接消息进行的通信,使用户能够进行支付、查询余额、生成发票等操作,同时支持钱包事件的通知。
草案
可选
这个 NIP 描述了一种客户端通过标准化协议访问远程闪电钱包的方法。托管方可以实现它,或者用户可以运行一个桥接器,桥接他们的钱包/节点和 Nostr Wallet Connect 协议。
从根本上说,NWC 是 client 和 wallet service 之间通过 Nostr 中继上的 E2E 加密直接消息进行的通信。中继知道 note 的种类和标签,但不知道加密 payload 的内容。不使用 user 的身份密钥,以避免将支付活动与用户联系起来。理想情况下,每个单独的连接都使用唯一的密钥。
希望使用此 NIP 允许 client(s) 与其钱包交互的 users 必须首先从兼容 NIP-47 的钱包应用程序获取一个特殊的“连接” URI。钱包应用程序可以使用 QR 屏幕、可粘贴的字符串或其他方式来提供此 URI。
然后,user 应该通过粘贴或扫描 QR 等方式将此 URI 复制到他们的 client(s) 中。client(s) 应该保存此 URI,并在以后每当 user(或代表用户的 client)想要与钱包交互时使用它。然后,client 应该从 URI 中指定的 relay 请求一个 info
(13194) 事件。wallet service 之前会将该事件发送到这些 relay,并且这些 relay 会将其作为可替换事件保存。
当 user 发起支付时,他们的 nostr client 创建一个 pay_invoice
请求,使用 URI 中的 token 对其进行加密,并将其(kind 23194)发送到连接 URI 中指定的 relay。wallet service 将会监听这些 relay,解密请求,然后联系 user 的钱包应用程序以发送支付。wallet service 将知道如何与钱包应用程序通信,因为连接 URI 指定了可以访问钱包应用程序 API 的 relay。
支付完成后,wallet service 将通过 URI 中的 relay 向 user 发送加密的 response
(kind 23195)。
wallet service 可能会向 client 发送有关钱包事件(例如收到的付款)的加密通知(kind 23196)。
有四种事件类型:
NIP-47 info event
: 13194NIP-47 request
: 23194NIP-47 response
: 23195NIP-47 notification event
: 23196info 事件应该是由 wallet service 在 relay 上发布的可替换事件,以表明它支持哪些功能。
内容应该是一个纯文本字符串,其中包含以空格分隔的支持的功能,例如 pay_invoice get_balance notifications
。
如果 wallet service 支持通知,则 info 事件应该包含一个 notifications
标签,其中包含以空格分隔的支持的通知类型,例如 payment_received payment_sent
。
request 和 response 事件都应该包含一个 p
标签,如果这是 request,则包含 wallet service 的公钥;如果这是 response,则包含 client 的公钥。response 事件应该包含一个 e
标签,其中包含它正在响应的 request 事件的 id。
(可选)request 可以有一个 expiration
标签,其中包含以秒为单位的 unix 时间戳。如果在此时间戳之后收到 request,则应忽略它。
request 和 response 的内容使用 NIP04 加密,并且是一个具有半固定结构的类 JSON-RPC 对象:
Request:
{
"method": "pay_invoice", // method, string
"params": { // params, object
"invoice": "lnbc50n1..." // command-related data
}
}
Response:
{
"result_type": "pay_invoice", //表明 result 字段的结构
"error": { //object, 在出错的情况下非空
"code": "UNAUTHORIZED", //string 错误代码,见下文
"message": "human readable error message"
},
"result": { // result, object. 在出错的情况下为空。
"preimage": "0123456789abcdef..." // command-related data
}
}
result_type
字段必须包含此事件正在响应的方法的名称。
如果命令不成功,error
字段必须包含一个带有可读错误消息的 message
字段和一个带有错误代码的 code
字段。
如果命令成功,则 error
字段必须为空。
notification 事件应该包含一个 p
标签,即 client 的公钥。
notification 的内容使用 NIP04 加密,并且是一个具有半固定结构的类 JSON-RPC 对象:
{
"notification_type": "payment_received", //表明 notification 字段的结构
"notification": {
"payment_hash": "0123456789abcdef..." // notification 相关的数据
}
}
RATE_LIMITED
:客户端发送命令的速度太快。它应该在几秒钟后重试。NOT_IMPLEMENTED
:该命令未知或故意未实现。INSUFFICIENT_BALANCE
:钱包没有足够的资金来支付手续费或支付金额。QUOTA_EXCEEDED
:钱包已超过其支出限额。RESTRICTED
:不允许此公钥执行此操作。UNAUTHORIZED
:此公钥没有连接钱包。INTERNAL
:内部错误。OTHER
:其他错误。client 和 wallet service 之间的通信需要两个密钥才能加密和解密消息。连接 URI 包括 client 的私钥和 wallet service 的公钥。
client 通过扫描 QR 码、处理 deeplink 或粘贴 URI 来发现 wallet service。
wallet service 生成此连接 URI,协议为 nostr+walletconnect://
,基本路径为其 32 字节的十六进制编码 pubkey
,对于每个客户端连接都应该是唯一的。
连接 URI 包含以下查询字符串参数:
relay
必需。wallet service 连接并将侦听事件的中继的 URL。可以不止一个。secret
必需。32 字节的随机生成的十六进制编码字符串。client 必须使用它来签名事件并在与 wallet service 通信时加密 payload。wallet service 必须使用此 secret 对应的公钥与 client 通信。
lud16
推荐。一个闪电地址,客户端可以使用它来自动设置用户个人资料上的 lud16
字段(如果他们没有配置)。然后,client 应该存储此连接,并在用户想要执行支付发票等操作时使用它。由于此 NIP 使用临时事件,因此建议选择不关闭非活动连接的中继,以避免丢弃事件,理想情况下,保留事件直到它们被使用或变得陈旧。
secret
和 wallet service 的 pubkey
来加密或解密。secret
对应的公钥来加密或解密。wallet service 不应存储它为客户端生成的 secret,并且不得依赖于知道 client secret 来进行常规操作。nostr+walletconnect://b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&secret=71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c
pay_invoice
描述:请求支付发票。
Request:
{
"method": "pay_invoice",
"params": {
"invoice": "lnbc50n1...", // bolt11 invoice
"amount": 123, // invoice amount in msats, optional
}
}
Response:
{
"result_type": "pay_invoice",
"result": {
"preimage": "0123456789abcdef...", // preimage of the payment
"fees_paid": 123, // value in msats, optional
}
}
Errors:
PAYMENT_FAILED
:支付失败。这可能是由于超时、耗尽所有路由、容量不足或类似原因造成的。multi_pay_invoice
描述:请求支付多个发票。
Request:
{
"method": "multi_pay_invoice",
"params": {
"invoices": [
{"id":"4da52c32a1", "invoice": "lnbc1...", "amount": 123}, // bolt11 invoice and amount in msats, amount is optional
{"id":"3da52c32a1", "invoice": "lnbc50n1..."},
],
}
}
Response:
对于请求中的每个发票,都会发送单独的 response 事件。为了区分 response,每个 response 事件都包含一个 d
标签,其中包含它所响应的发票的 id;如果没有给出 id,则应使用发票的支付哈希。
{
"result_type": "multi_pay_invoice",
"result": {
"preimage": "0123456789abcdef...", // preimage of the payment
"fees_paid": 123, // value in msats, optional
}
}
Errors:
PAYMENT_FAILED
:支付失败。这可能是由于超时、耗尽所有路由、容量不足或类似原因造成的。pay_keysend
Request:
{
"method": "pay_keysend",
"params": {
"amount": 123, // invoice amount in msats, required
"pubkey": "03...", // payee pubkey, required
"preimage": "0123456789abcdef...", // preimage of the payment, optional
"tlv_records": [ // tlv records, optional
{
"type": 5482373484, // tlv type
"value": "0123456789abcdef" // hex encoded tlv value
}
]
}
}
Response:
{
"result_type": "pay_keysend",
"result": {
"preimage": "0123456789abcdef...", // preimage of the payment
"fees_paid": 123, // value in msats, optional
}
}
Errors:
PAYMENT_FAILED
:支付失败。这可能是由于超时、耗尽所有路由、容量不足或类似原因造成的。multi_pay_keysend
描述:请求多个 keysend 支付。
具有一个 keysend 数组,这些 keysend 遵循与 pay_keysend
相同的语义,只是以批处理方式完成
Request:
{
"method": "multi_pay_keysend",
"params": {
"keysends": [
{"id": "4c5b24a351", "pubkey": "03...", "amount": 123},
{"id": "3da52c32a1", "pubkey": "02...", "amount": 567, "preimage": "abc123..", "tlv_records": [{"type": 696969, "value": "77616c5f6872444873305242454d353736"}]},
],
}
}
Response:
对于请求中的每个 keysend,都会发送单独的 response 事件。为了区分 response,每个 response 事件都包含一个 d
标签,其中包含它所响应的 keysend 的 id;如果没有给出 id,则应使用 pubkey。
{
"result_type": "multi_pay_keysend",
"result": {
"preimage": "0123456789abcdef...", // preimage of the payment
"fees_paid": 123, // value in msats, optional
}
}
Errors:
PAYMENT_FAILED
:支付失败。这可能是由于超时、耗尽所有路由、容量不足或类似原因造成的。make_invoice
Request:
{
"method": "make_invoice",
"params": {
"amount": 123, // value in msats
"description": "string", // invoice's description, optional
"description_hash": "string", // invoice's description hash, optional
"expiry": 213 // expiry in seconds from time invoice is created, optional
}
}
Response:
{
"result_type": "make_invoice",
"result": {
"type": "incoming", // "incoming" for invoices, "outgoing" for payments
"invoice": "string", // encoded invoice, optional
"description": "string", // invoice's description, optional
"description_hash": "string", // invoice's description hash, optional
"preimage": "string", // payment's preimage, optional if unpaid
"payment_hash": "string", // Payment hash for the payment
"amount": 123, // value in msats
"fees_paid": 123, // value in msats
"created_at": unixtimestamp, // invoice/payment creation time
"expires_at": unixtimestamp, // invoice expiration time, optional if not applicable
"metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc.
}
}
lookup_invoice
Request:
{
"method": "lookup_invoice",
"params": {
"payment_hash": "31afdf1..", // payment hash of the invoice, one of payment_hash or invoice is required
"invoice": "lnbc50n1..." // invoice to lookup
}
}
Response:
{
"result_type": "lookup_invoice",
"result": {
"type": "incoming", // "incoming" for invoices, "outgoing" for payments
"invoice": "string", // encoded invoice, optional
"description": "string", // invoice's description, optional
"description_hash": "string", // invoice's description hash, optional
"preimage": "string", // payment's preimage, optional if unpaid
"payment_hash": "string", // Payment hash for the payment
"amount": 123, // value in msats
"fees_paid": 123, // value in msats
"created_at": unixtimestamp, // invoice/payment creation time
"expires_at": unixtimestamp, // invoice expiration time, optional if not applicable
"settled_at": unixtimestamp, // invoice/payment settlement time, optional if unpaid
"metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc.
}
}
Errors:
NOT_FOUND
: 无法通过给定的参数找到发票。list_transactions
列出发票和付款。如果未指定 type
,则返回发票和付款。
from
和 until
参数是以秒为单位的自 epoch 以来的时间戳。如果未指定 from
,则默认为 0。
如果未指定 until
,则默认为当前时间。事务按创建时间的降序返回。
Request:
{
"method": "list_transactions",
"params": {
"from": 1693876973, // starting timestamp in seconds since epoch (inclusive), optional
"until": 1703225078, // ending timestamp in seconds since epoch (inclusive), optional
"limit": 10, // maximum number of invoices to return, optional
"offset": 0, // offset of the first invoice to return, optional
"unpaid": true, // include unpaid invoices, optional, default false
"type": "incoming", // "incoming" for invoices, "outgoing" for payments, undefined for both
}
}
Response:
{
"result_type": "list_transactions",
"result": {
"transactions": [
{
"type": "incoming", // "incoming" for invoices, "outgoing" for payments
"invoice": "string", // encoded invoice, optional
"description": "string", // invoice's description, optional
"description_hash": "string", // invoice's description hash, optional
"preimage": "string", // payment's preimage, optional if unpaid
"payment_hash": "string", // Payment hash for the payment
"amount": 123, // value in msats
"fees_paid": 123, // value in msats
"created_at": unixtimestamp, // invoice/payment creation time
"expires_at": unixtimestamp, // invoice expiration time, optional if not applicable
"settled_at": unixtimestamp, // invoice/payment settlement time, optional if unpaid
"metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc.
}
],
},
}
get_balance
Request:
{
"method": "get_balance",
"params": {}
}
Response:
{
"result_type": "get_balance",
"result": {
"balance": 10000, // user's balance in msats
}
}
get_info
Request:
{
"method": "get_info",
"params": {}
}
Response:
{
"result_type": "get_info",
"result": {
"alias": "string",
"color": "hex string",
"pubkey": "hex string",
"network": "string", // mainnet, testnet, signet, or regtest
"block_height": 1,
"block_hash": "hex string",
"methods": ["pay_invoice", "get_balance", "make_invoice", "lookup_invoice", "list_transactions", "get_info"], // list of supported methods for this connection
"notifications": ["payment_received", "payment_sent"], // list of supported notifications for this connection, optional.
}
}
payment_received
描述:钱包已成功收到付款。
Notification:
{
"notification_type": "payment_received",
"notification": {
"type": "incoming",
"invoice": "string", // encoded invoice
"description": "string", // invoice's description, optional
"description_hash": "string", // invoice's description hash, optional
"preimage": "string", // payment's preimage
"payment_hash": "string", // Payment hash for the payment
"amount": 123, // value in msats
"fees_paid": 123, // value in msats
"created_at": unixtimestamp, // invoice/payment creation time
"expires_at": unixtimestamp, // invoice expiration time, optional if not applicable
"settled_at": unixtimestamp, // invoice/payment settlement time
"metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc.
}
}
payment_sent
描述:钱包已成功发送付款。
Notification:
{
"notification_type": "payment_sent",
"notification": {
"type": "outgoing",
"invoice": "string", // encoded invoice
"description": "string", // invoice's description, optional
"description_hash": "string", // invoice's description hash, optional
"preimage": "string", // payment's preimage
"payment_hash": "string", // Payment hash for the payment
"amount": 123, // value in msats
"fees_paid": 123, // value in msats
"created_at": unixtimestamp, // invoice/payment creation time
"expires_at": unixtimestamp, // invoice expiration time, optional if not applicable
"settled_at": unixtimestamp, // invoice/payment settlement time
"metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc.
}
}
nostr+walletconnect://
deeplink 或手动配置连接详细信息。23194
的事件。内容是一个 pay_invoice
请求。私钥是上面连接字符串中的 secret。23195
的事件来响应事件,并且内容是一个 response,其中包含错误消息或 preimage。此 NIP 没有指定对所用中继类型的任何要求。但是,如果用户正在使用托管服务,则使用由托管服务托管的中继可能是有意义的。然后,中继可以强制执行身份验证以防止元数据泄漏。在这种情况下,不依赖第三方中继也可以提高可靠性。
{
"id": "df467db0a9f9ec77ffe6f561811714ccaa2e26051c20f58f33c3d66d6c2b4d1c",
"pubkey": "c04ccd5c82fc1ea3499b9c6a5c0a7ab627fbe00a0116110d4c750faeaecba1e2",
"created_at": 1713883677,
"kind": 13194,
"tags": [
[
"notifications",
"payment_received payment_sent"
]
],
"content": "pay_invoice pay_keysend get_balance get_info make_invoice lookup_invoice list_transactions multi_pay_invoice multi_pay_keysend sign_message notifications",
"sig": "31f57b369459b5306a5353aa9e03be7fbde169bc881c3233625605dd12f53548179def16b9fe1137e6465d7e4d5bb27ce81fd6e75908c46b06269f4233c845d8"
}
- 原文链接: github.com/nostr-protoco...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!