implement first draft of nwc client and cli
This commit is contained in:
162
cmd/nwcclient/README.md
Normal file
162
cmd/nwcclient/README.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# NWC Client CLI Tool
|
||||
|
||||
A command-line interface tool for making calls to Nostr Wallet Connect (NWC) services.
|
||||
|
||||
## Overview
|
||||
|
||||
This CLI tool allows you to interact with NWC wallet services using the methods defined in the NIP-47 specification. It provides a simple interface for executing wallet operations and displays the JSON response from the wallet service.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
nwcclient <connection URL> <method> [parameters...]
|
||||
```
|
||||
|
||||
### Connection URL
|
||||
|
||||
The connection URL should be in the Nostr Wallet Connect format:
|
||||
|
||||
```
|
||||
nostr+walletconnect://<wallet_pubkey>?relay=<relay_url>&secret=<secret>
|
||||
```
|
||||
|
||||
### Supported Methods
|
||||
|
||||
The following methods are supported by this CLI tool:
|
||||
|
||||
- `get_info` - Get wallet information
|
||||
- `get_balance` - Get wallet balance
|
||||
- `get_budget` - Get wallet budget
|
||||
- `make_invoice` - Create an invoice
|
||||
- `pay_invoice` - Pay an invoice
|
||||
- `pay_keysend` - Send a keysend payment
|
||||
- `lookup_invoice` - Look up an invoice
|
||||
- `list_transactions` - List transactions
|
||||
- `sign_message` - Sign a message
|
||||
|
||||
### Unsupported Methods
|
||||
|
||||
The following methods are defined in the NIP-47 specification but are not directly supported by this CLI tool due to limitations in the underlying nwc package:
|
||||
|
||||
- `create_connection` - Create a connection
|
||||
- `make_hold_invoice` - Create a hold invoice
|
||||
- `settle_hold_invoice` - Settle a hold invoice
|
||||
- `cancel_hold_invoice` - Cancel a hold invoice
|
||||
- `multi_pay_invoice` - Pay multiple invoices
|
||||
- `multi_pay_keysend` - Send multiple keysend payments
|
||||
|
||||
## Method Parameters
|
||||
|
||||
### Methods with No Parameters
|
||||
|
||||
- `get_info`
|
||||
- `get_balance`
|
||||
- `get_budget`
|
||||
|
||||
Example:
|
||||
```
|
||||
nwcclient <connection URL> get_info
|
||||
```
|
||||
|
||||
### Methods with Parameters
|
||||
|
||||
#### make_invoice
|
||||
|
||||
```
|
||||
nwcclient <connection URL> make_invoice <amount> <description> [description_hash] [expiry]
|
||||
```
|
||||
|
||||
- `amount` - Amount in millisatoshis (msats)
|
||||
- `description` - Invoice description
|
||||
- `description_hash` (optional) - Hash of the description
|
||||
- `expiry` (optional) - Expiry time in seconds
|
||||
|
||||
Example:
|
||||
```
|
||||
nwcclient <connection URL> make_invoice 1000000 "Test invoice" "" 3600
|
||||
```
|
||||
|
||||
#### pay_invoice
|
||||
|
||||
```
|
||||
nwcclient <connection URL> pay_invoice <invoice> [amount]
|
||||
```
|
||||
|
||||
- `invoice` - BOLT11 invoice
|
||||
- `amount` (optional) - Amount in millisatoshis (msats)
|
||||
|
||||
Example:
|
||||
```
|
||||
nwcclient <connection URL> pay_invoice lnbc1...
|
||||
```
|
||||
|
||||
#### pay_keysend
|
||||
|
||||
```
|
||||
nwcclient <connection URL> pay_keysend <amount> <pubkey> [preimage]
|
||||
```
|
||||
|
||||
- `amount` - Amount in millisatoshis (msats)
|
||||
- `pubkey` - Recipient's public key
|
||||
- `preimage` (optional) - Payment preimage
|
||||
|
||||
Example:
|
||||
```
|
||||
nwcclient <connection URL> pay_keysend 1000000 03...
|
||||
```
|
||||
|
||||
#### lookup_invoice
|
||||
|
||||
```
|
||||
nwcclient <connection URL> lookup_invoice <payment_hash_or_invoice>
|
||||
```
|
||||
|
||||
- `payment_hash_or_invoice` - Payment hash or BOLT11 invoice
|
||||
|
||||
Example:
|
||||
```
|
||||
nwcclient <connection URL> lookup_invoice 3d...
|
||||
```
|
||||
|
||||
#### list_transactions
|
||||
|
||||
```
|
||||
nwcclient <connection URL> list_transactions [from <timestamp>] [until <timestamp>] [limit <count>] [offset <count>] [unpaid <true|false>] [type <incoming|outgoing>]
|
||||
```
|
||||
|
||||
Parameters are specified as name-value pairs:
|
||||
|
||||
- `from` - Start timestamp
|
||||
- `until` - End timestamp
|
||||
- `limit` - Maximum number of transactions to return
|
||||
- `offset` - Number of transactions to skip
|
||||
- `unpaid` - Whether to include unpaid transactions
|
||||
- `type` - Transaction type (incoming or outgoing)
|
||||
|
||||
Example:
|
||||
```
|
||||
nwcclient <connection URL> list_transactions limit 10 type incoming
|
||||
```
|
||||
|
||||
#### sign_message
|
||||
|
||||
```
|
||||
nwcclient <connection URL> sign_message <message>
|
||||
```
|
||||
|
||||
- `message` - Message to sign
|
||||
|
||||
Example:
|
||||
```
|
||||
nwcclient <connection URL> sign_message "Hello, world!"
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
The tool prints the JSON response from the wallet service to stdout. If an error occurs, an error message is printed to stderr.
|
||||
|
||||
## Limitations
|
||||
|
||||
- The tool only supports methods that have direct client methods in the nwc package.
|
||||
- Complex parameters like metadata are not supported.
|
||||
- The tool does not support interactive authentication or authorization.
|
||||
285
cmd/nwcclient/main.go
Normal file
285
cmd/nwcclient/main.go
Normal file
@@ -0,0 +1,285 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"orly.dev/pkg/protocol/nwc"
|
||||
)
|
||||
|
||||
func printUsage() {
|
||||
fmt.Println("Usage: nwcclient \"<connection URL>\" <method> [parameters...]")
|
||||
fmt.Println("\nSupported methods:")
|
||||
fmt.Println(" get_info - Get wallet information")
|
||||
fmt.Println(" get_balance - Get wallet balance")
|
||||
fmt.Println(" get_budget - Get wallet budget")
|
||||
fmt.Println(" make_invoice - Create an invoice (amount, description, [description_hash], [expiry])")
|
||||
fmt.Println(" pay_invoice - Pay an invoice (invoice, [amount])")
|
||||
fmt.Println(" pay_keysend - Send a keysend payment (amount, pubkey, [preimage])")
|
||||
fmt.Println(" lookup_invoice - Look up an invoice (payment_hash or invoice)")
|
||||
fmt.Println(" list_transactions - List transactions ([from], [until], [limit], [offset], [unpaid], [type])")
|
||||
fmt.Println(" sign_message - Sign a message (message)")
|
||||
fmt.Println("\nUnsupported methods (due to limitations in the nwc package):")
|
||||
fmt.Println(" create_connection - Create a connection")
|
||||
fmt.Println(" make_hold_invoice - Create a hold invoice")
|
||||
fmt.Println(" settle_hold_invoice - Settle a hold invoice")
|
||||
fmt.Println(" cancel_hold_invoice - Cancel a hold invoice")
|
||||
fmt.Println(" multi_pay_invoice - Pay multiple invoices")
|
||||
fmt.Println(" multi_pay_keysend - Send multiple keysend payments")
|
||||
fmt.Println("\nParameters format:")
|
||||
fmt.Println(" - Positional parameters are used for required fields")
|
||||
fmt.Println(" - For list_transactions, named parameters are used: 'from', 'until', 'limit', 'offset', 'unpaid', 'type'")
|
||||
fmt.Println(" Example: nwcclient <url> list_transactions limit 10 type incoming")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Check if we have enough arguments
|
||||
if len(os.Args) < 3 {
|
||||
printUsage()
|
||||
}
|
||||
|
||||
// Parse connection URL and method
|
||||
connectionURL := os.Args[1]
|
||||
methodStr := os.Args[2]
|
||||
method := nwc.Method(methodStr)
|
||||
|
||||
// Parse the wallet connect URL
|
||||
opts, err := nwc.ParseWalletConnectURL(connectionURL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing connection URL: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Create a new NWC client
|
||||
client, err := nwc.NewNWCClient(opts)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating NWC client: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// Execute the requested method
|
||||
var result interface{}
|
||||
|
||||
switch method {
|
||||
case nwc.GetInfo:
|
||||
result, err = client.GetInfo()
|
||||
|
||||
case nwc.GetBalance:
|
||||
result, err = client.GetBalance()
|
||||
|
||||
case nwc.GetBudget:
|
||||
result, err = client.GetBudget()
|
||||
|
||||
case nwc.MakeInvoice:
|
||||
if len(os.Args) < 5 {
|
||||
fmt.Fprintf(
|
||||
os.Stderr,
|
||||
"Error: make_invoice requires at least amount and description\n",
|
||||
)
|
||||
printUsage()
|
||||
}
|
||||
amount, err := strconv.ParseInt(os.Args[3], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing amount: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
description := os.Args[4]
|
||||
|
||||
req := &nwc.MakeInvoiceRequest{
|
||||
Amount: amount,
|
||||
Description: description,
|
||||
}
|
||||
|
||||
// Optional parameters
|
||||
if len(os.Args) > 5 {
|
||||
req.DescriptionHash = os.Args[5]
|
||||
}
|
||||
if len(os.Args) > 6 {
|
||||
expiry, err := strconv.ParseInt(os.Args[6], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing expiry: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
req.Expiry = &expiry
|
||||
}
|
||||
|
||||
result, err = client.MakeInvoice(req)
|
||||
|
||||
case nwc.PayInvoice:
|
||||
if len(os.Args) < 4 {
|
||||
fmt.Fprintf(os.Stderr, "Error: pay_invoice requires an invoice\n")
|
||||
printUsage()
|
||||
}
|
||||
|
||||
req := &nwc.PayInvoiceRequest{
|
||||
Invoice: os.Args[3],
|
||||
}
|
||||
|
||||
// Optional amount parameter
|
||||
if len(os.Args) > 4 {
|
||||
amount, err := strconv.ParseInt(os.Args[4], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing amount: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
req.Amount = &amount
|
||||
}
|
||||
|
||||
result, err = client.PayInvoice(req)
|
||||
|
||||
case nwc.PayKeysend:
|
||||
if len(os.Args) < 5 {
|
||||
fmt.Fprintf(
|
||||
os.Stderr, "Error: pay_keysend requires amount and pubkey\n",
|
||||
)
|
||||
printUsage()
|
||||
}
|
||||
|
||||
amount, err := strconv.ParseInt(os.Args[3], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing amount: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
req := &nwc.PayKeysendRequest{
|
||||
Amount: amount,
|
||||
Pubkey: os.Args[4],
|
||||
}
|
||||
|
||||
// Optional preimage
|
||||
if len(os.Args) > 5 {
|
||||
req.Preimage = os.Args[5]
|
||||
}
|
||||
|
||||
result, err = client.PayKeysend(req)
|
||||
|
||||
case nwc.LookupInvoice:
|
||||
if len(os.Args) < 4 {
|
||||
fmt.Fprintf(
|
||||
os.Stderr,
|
||||
"Error: lookup_invoice requires a payment_hash or invoice\n",
|
||||
)
|
||||
printUsage()
|
||||
}
|
||||
|
||||
param := os.Args[3]
|
||||
req := &nwc.LookupInvoiceRequest{}
|
||||
|
||||
// Determine if the parameter is a payment hash or an invoice
|
||||
if strings.HasPrefix(param, "ln") {
|
||||
req.Invoice = param
|
||||
} else {
|
||||
req.PaymentHash = param
|
||||
}
|
||||
|
||||
result, err = client.LookupInvoice(req)
|
||||
|
||||
case nwc.ListTransactions:
|
||||
req := &nwc.ListTransactionsRequest{}
|
||||
|
||||
// Parse optional parameters
|
||||
paramIndex := 3
|
||||
for paramIndex < len(os.Args) {
|
||||
if paramIndex+1 >= len(os.Args) {
|
||||
break
|
||||
}
|
||||
|
||||
paramName := os.Args[paramIndex]
|
||||
paramValue := os.Args[paramIndex+1]
|
||||
|
||||
switch paramName {
|
||||
case "from":
|
||||
val, err := strconv.ParseInt(paramValue, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing from: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
req.From = &val
|
||||
case "until":
|
||||
val, err := strconv.ParseInt(paramValue, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing until: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
req.Until = &val
|
||||
case "limit":
|
||||
val, err := strconv.ParseInt(paramValue, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing limit: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
req.Limit = &val
|
||||
case "offset":
|
||||
val, err := strconv.ParseInt(paramValue, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing offset: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
req.Offset = &val
|
||||
case "unpaid":
|
||||
val := paramValue == "true"
|
||||
req.Unpaid = &val
|
||||
case "type":
|
||||
req.Type = ¶mValue
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unknown parameter: %s\n", paramName)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
paramIndex += 2
|
||||
}
|
||||
|
||||
result, err = client.ListTransactions(req)
|
||||
|
||||
case nwc.SignMessage:
|
||||
if len(os.Args) < 4 {
|
||||
fmt.Fprintf(os.Stderr, "Error: sign_message requires a message\n")
|
||||
printUsage()
|
||||
}
|
||||
|
||||
req := &nwc.SignMessageRequest{
|
||||
Message: os.Args[3],
|
||||
}
|
||||
|
||||
result, err = client.SignMessage(req)
|
||||
|
||||
case nwc.CreateConnection, nwc.MakeHoldInvoice, nwc.SettleHoldInvoice, nwc.CancelHoldInvoice, nwc.MultiPayInvoice, nwc.MultiPayKeysend:
|
||||
fmt.Fprintf(
|
||||
os.Stderr,
|
||||
"Error: Method %s is not directly supported by the CLI tool.\n",
|
||||
methodStr,
|
||||
)
|
||||
fmt.Fprintf(
|
||||
os.Stderr,
|
||||
"This is because these methods don't have exported client methods in the nwc package.\n",
|
||||
)
|
||||
fmt.Fprintf(
|
||||
os.Stderr,
|
||||
"Only the following methods are currently supported: get_info, get_balance, get_budget, make_invoice, pay_invoice, pay_keysend, lookup_invoice, list_transactions, sign_message\n",
|
||||
)
|
||||
os.Exit(1)
|
||||
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Error: Unsupported method: %s\n", methodStr)
|
||||
printUsage()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error executing method: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Print the result as JSON
|
||||
jsonData, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error marshaling result to JSON: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println(string(jsonData))
|
||||
}
|
||||
Reference in New Issue
Block a user