Update NDK to v2.12.2 (#2041)

* Update NDK to v2.12.2

* Fix NWC with @nostr-dev-kit/ndk-wallet

* Add test for nip-57 zap receipts
This commit is contained in:
ekzyis 2025-04-03 00:20:47 +02:00 committed by GitHub
parent 644899469f
commit 2cf0f1d268
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 291 additions and 35 deletions

View File

@ -22,10 +22,10 @@ export const RELAYS_BLACKLIST = []
/**
* @import {NDKSigner} from '@nostr-dev-kit/ndk'
* @import { NDK } from '@nostr-dev-kit/ndk'
* @import {NDKNwc} from '@nostr-dev-kit/ndk'
* @import {NDKNWCWallet} from '@nostr-dev-kit/ndk-wallet'
* @typedef {Object} Nostr
* @property {NDK} ndk
* @property {function(string, {logger: Object}): Promise<NDKNwc>} nwc
* @property {function(string, {logger: Object}): Promise<NDKNWCWallet>} nwc
* @property {function(Object, {privKey: string, signer: NDKSigner}): Promise<NDKEvent>} sign
* @property {function(Object, {relays: Array<string>, privKey: string, signer: NDKSigner}): Promise<NDKEvent>} publish
*/

231
package-lock.json generated
View File

@ -12,10 +12,12 @@
"@apollo/server": "^4.11.0",
"@as-integrations/next": "^3.1.0",
"@auth/prisma-adapter": "^2.7.0",
"@cashu/cashu-ts": "^2.4.1",
"@graphql-tools/schema": "^10.0.6",
"@lightninglabs/lnc-web": "^0.3.2-alpha",
"@noble/curves": "^1.6.0",
"@nostr-dev-kit/ndk": "^2.10.5",
"@nostr-dev-kit/ndk": "^2.12.2",
"@nostr-dev-kit/ndk-wallet": "^0.5.0",
"@opensearch-project/opensearch": "^2.12.0",
"@prisma/client": "^5.20.0",
"@slack/web-api": "^7.6.0",
@ -2430,6 +2432,91 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
"node_modules/@cashu/cashu-ts": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@cashu/cashu-ts/-/cashu-ts-2.4.1.tgz",
"integrity": "sha512-9lDHP5GtWvC/mIDPRg5KdRQAsqoYYm93efPyfgDtRd9eW1BhWrzLuS0sN1WVkL9noAXCZoWjjpX8TElMXhpFhA==",
"license": "MIT",
"dependencies": {
"@cashu/crypto": "^0.3.4",
"@noble/curves": "^1.3.0",
"@noble/hashes": "^1.3.3",
"buffer": "^6.0.3"
}
},
"node_modules/@cashu/cashu-ts/node_modules/@noble/hashes": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz",
"integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@cashu/crypto": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@cashu/crypto/-/crypto-0.3.4.tgz",
"integrity": "sha512-mfv1Pj4iL1PXzUj9NKIJbmncCLMqYfnEDqh/OPxAX0nNBt6BOnVJJLjLWFlQeYxlnEfWABSNkrqPje1t5zcyhA==",
"license": "MIT",
"dependencies": {
"@noble/curves": "^1.6.0",
"@noble/hashes": "^1.5.0",
"@scure/bip32": "^1.5.0",
"@scure/bip39": "^1.4.0",
"buffer": "^6.0.3"
}
},
"node_modules/@cashu/crypto/node_modules/@noble/hashes": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz",
"integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@cashu/crypto/node_modules/@scure/base": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz",
"integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==",
"license": "MIT",
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@cashu/crypto/node_modules/@scure/bip32": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz",
"integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==",
"license": "MIT",
"dependencies": {
"@noble/curves": "~1.8.1",
"@noble/hashes": "~1.7.1",
"@scure/base": "~1.2.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@cashu/crypto/node_modules/@scure/bip39": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz",
"integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "~1.7.1",
"@scure/base": "~1.2.4"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.23.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
@ -4338,11 +4425,12 @@
}
},
"node_modules/@noble/curves": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz",
"integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==",
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz",
"integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.5.0"
"@noble/hashes": "1.7.1"
},
"engines": {
"node": "^14.21.3 || >=16"
@ -4352,9 +4440,10 @@
}
},
"node_modules/@noble/curves/node_modules/@noble/hashes": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz",
"integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==",
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz",
"integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
@ -4418,9 +4507,9 @@
}
},
"node_modules/@nostr-dev-kit/ndk": {
"version": "2.10.5",
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.10.5.tgz",
"integrity": "sha512-QEnarJL9BGCxeenSIE9jxNSDyYQYjzD30YL3sVJ9cNybNZX8tl/I1/vhEUeRRMBz/qjROLtt0M2RV68rZ205tg==",
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.12.2.tgz",
"integrity": "sha512-uvautgwbpk3AgddoFpew67/FiaV/zpKwwvSnjCvbE/tAdJBpUUS+VjWR5WfUnJvxTy/ZZpPW+X2TkwVFHhUdvA==",
"license": "MIT",
"dependencies": {
"@noble/curves": "^1.6.0",
@ -4439,6 +4528,69 @@
"node": ">=16"
}
},
"node_modules/@nostr-dev-kit/ndk-wallet": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-wallet/-/ndk-wallet-0.5.0.tgz",
"integrity": "sha512-iHMh5kRKbEEw42fmE89KIH+4cnsRQzpW4SrbtpLCbdGFeu8iUOE2C1bBAWWW3HdtcjuRxa83iYXI+eo/Wake2w==",
"license": "MIT",
"dependencies": {
"@nostr-dev-kit/ndk": "2.13.0-rc2",
"debug": "^4.3.4",
"light-bolt11-decoder": "^3.0.0",
"tseep": "^1.1.1",
"typescript": "^5.4.4",
"webln": "^0.3.2"
},
"peerDependencies": {
"@cashu/cashu-ts": "*",
"@cashu/crypto": "*"
}
},
"node_modules/@nostr-dev-kit/ndk-wallet/node_modules/@noble/hashes": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz",
"integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@nostr-dev-kit/ndk-wallet/node_modules/@nostr-dev-kit/ndk": {
"version": "2.13.0-rc2",
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.13.0-rc2.tgz",
"integrity": "sha512-oTj19rqcft6VCxvlkosIuCl8aQNrQzU0NJ/yi0XRbtlVReAyJhZ7iT6rZixO9aL4pAG9ZCBp+ej3GRcU3OIEeQ==",
"license": "MIT",
"dependencies": {
"@noble/curves": "^1.6.0",
"@noble/hashes": "^1.5.0",
"@noble/secp256k1": "^2.1.0",
"@scure/base": "^1.1.9",
"debug": "^4.3.6",
"light-bolt11-decoder": "^3.2.0",
"tseep": "^1.2.2",
"typescript-lru-cache": "^2.0.0",
"utf8-buffer": "^1.0.0",
"websocket-polyfill": "^0.0.3"
},
"engines": {
"node": ">=16"
},
"peerDependencies": {
"nostr-tools": "^2.7.1"
}
},
"node_modules/@nostr-dev-kit/ndk-wallet/node_modules/@scure/base": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz",
"integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==",
"license": "MIT",
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@nostr-dev-kit/ndk/node_modules/@noble/hashes": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz",
@ -7341,6 +7493,30 @@
"node-int64": "^0.4.0"
}
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/buffer-compare": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-compare/-/buffer-compare-1.1.1.tgz",
@ -11176,6 +11352,26 @@
"resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
"integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause"
},
"node_modules/ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -19780,6 +19976,19 @@
"resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
},
"node_modules/typescript": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/typescript-lru-cache": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/typescript-lru-cache/-/typescript-lru-cache-2.0.0.tgz",

View File

@ -17,10 +17,12 @@
"@apollo/server": "^4.11.0",
"@as-integrations/next": "^3.1.0",
"@auth/prisma-adapter": "^2.7.0",
"@cashu/cashu-ts": "^2.4.1",
"@graphql-tools/schema": "^10.0.6",
"@lightninglabs/lnc-web": "^0.3.2-alpha",
"@noble/curves": "^1.6.0",
"@nostr-dev-kit/ndk": "^2.10.5",
"@nostr-dev-kit/ndk": "^2.12.2",
"@nostr-dev-kit/ndk-wallet": "^0.5.0",
"@opensearch-project/opensearch": "^2.12.0",
"@prisma/client": "^5.20.0",
"@slack/web-api": "^7.6.0",

62
scripts/test-nip57.sh Normal file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env bash
# https://github.com/nostr-protocol/nips/blob/master/57.md
set -e
# test user with attached wallet
# TODO: attach wallet to test01 via psql if not already attached?
USERNAME=test01
# XXX this should match NOSTR_PRIVATE_KEY in .env.development
NOSTR_PRIVATE_KEY=5f30b7e7714360f51f2be2e30c1d93b7fdf67366e730658e85777dfcc4e4245f
NOSTR_PUBLIC_KEY=$(nak key public $NOSTR_PRIVATE_KEY)
SINCE=$(date +%s)
function create_event() {
nak event -k 9734 \
--tag p=$NOSTR_PUBLIC_KEY \
--tag 'relays=wss://relay.primal.net' \
--tag amount=100000
}
function url_encode() {
cat - | jq -sRr @uri
}
function test_exit() {
if [ $1 -eq 0 ]; then
echo "worker publishes nip-57 zap receipts: PASSED"
else
echo "worker publishes nip-57 zap receipts: FAILED"
fi
exit $1
}
create_event | nak verify
# create a zap request event (kind 9734)
EVENT="$(create_event)"
echo "generated zap request event:"
echo "$EVENT" | jq
echo $EVENT | nak verify
# XXX make sure amount is higher than dust limit of receiver's wallet
echo -n "sending zap request event LNURL endpoint ... "
PR=$(curl -s "http://localhost:3000/api/lnurlp/$USERNAME/pay?amount=100000&nostr=$(echo $EVENT | url_encode)" | jq -r .pr)
echo "OK"
[ "$PR" == "null" ] && echo "error: LNURL endpoint did not return bolt11" && test_exit 1
echo $PR
sndev fund --cln $PR
# subscribe to zap receipt event (kind 9735)
echo -n "waiting for zap receipt event ... "
sleep 3
PR2=$(nak -q req -k 9735 -p $NOSTR_PUBLIC_KEY --limit 1 wss://relay.primal.net | jq -r '.tags[] | select(.[0] == "bolt11") | .[1]')
echo "OK"
[ "$PR" == "$PR2" ] && test_exit 0 || test_exit 1

View File

@ -9,6 +9,6 @@ export async function testSendPayment ({ nwcUrl }, { signal }) {
}
export async function sendPayment (bolt11, { nwcUrl }, { signal }) {
const result = await nwcTryRun(nwc => nwc.payInvoice(bolt11), { nwcUrl }, { signal })
const result = await nwcTryRun(nwc => nwc.lnPay({ pr: bolt11 }), { nwcUrl }, { signal })
return result.preimage
}

View File

@ -1,10 +1,7 @@
import Nostr from '@/lib/nostr'
import { string } from '@/lib/yup'
import { parseNwcUrl } from '@/lib/url'
import { NDKNwc } from '@nostr-dev-kit/ndk'
import { TimeoutError } from '@/lib/time'
const NWC_CONNECT_TIMEOUT_MS = 15_000
import { NDKNWCWallet } from '@nostr-dev-kit/ndk-wallet'
export const name = 'nwc'
export const walletType = 'NWC'
@ -39,23 +36,11 @@ export const card = {
async function getNwc (nostr, nwcUrl, { signal }) {
const ndk = nostr.ndk
const { walletPubkey, secret, relayUrls } = parseNwcUrl(nwcUrl)
const nwc = new NDKNwc({
ndk,
const nwc = new NDKNWCWallet(ndk, {
pubkey: walletPubkey,
relayUrls,
secret
})
// TODO: support AbortSignal
try {
await nwc.blockUntilReady(NWC_CONNECT_TIMEOUT_MS)
} catch (err) {
if (err.message === 'Timeout') {
throw new TimeoutError(NWC_CONNECT_TIMEOUT_MS)
}
throw err
}
return nwc
}
@ -69,9 +54,7 @@ export async function nwcTryRun (fun, { nwcUrl }, { signal }) {
const nostr = new Nostr()
try {
const nwc = await getNwc(nostr, nwcUrl, { signal })
const { error, result } = await fun(nwc)
if (error) throw new Error(error.message || error.code)
return result
return await fun(nwc)
} catch (e) {
if (e.error) throw new Error(e.error.message || e.error.code)
throw e