diff options
| author | Adam Soltys <[email protected]> | 2026-08-08 08:21:11 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-08 08:21:11 -0700 |
| commit | 19c14131ce3fc69a06d3c0af8d445b056d703bf9 (patch) | |
| tree | 2d116ff73bccf2af784d8700ed6fd825837928a4 /src/features/gifts-wallet.js | |
| parent | 47919c94e861e98fc5c21956c3c53e896052aea4 (diff) | |
| download | coinosv3-19c14131ce3fc69a06d3c0af8d445b056d703bf9.tar.gz coinosv3-19c14131ce3fc69a06d3c0af8d445b056d703bf9.zip | |
Profiles wear their latest notes, each one zappable; a zap with no ark address to land on becomes a gift only their key can open
Co-Authored-By: Claude Fable 5 <[email protected]>
Diffstat (limited to 'src/features/gifts-wallet.js')
| -rw-r--r-- | src/features/gifts-wallet.js | 22 |
1 files changed, 22 insertions, 0 deletions
diff --git a/src/features/gifts-wallet.js b/src/features/gifts-wallet.js index bf8a82d..c25e85a 100644 --- a/src/features/gifts-wallet.js +++ b/src/features/gifts-wallet.js @@ -5,6 +5,7 @@ import * as btc from '@scure/btc-signer'; import { hex, base64urlnopad, base32nopad } from '@scure/base'; +import { encryptGiftPayload } from '../nostr.js'; import { utxoId } from '../wallet.js'; import { p2wpkh } from '@scure/btc-signer/payment'; import { concatBytes } from '@noble/hashes/utils'; @@ -467,3 +468,24 @@ export function buildClaimTx(code, toAddress, feeRate, net) { // public (anyone can see what it is and who it's for), but the claim payload (the // gift code) is NIP-44-encrypted to the recipient via an ephemeral key — so the // link can be shared openly yet only the recipient's nostr key can claim it. +// v2 wraps an ARK gift code (v1 wrapped on-chain PSBT gifts and is retired — +// its links no longer exist in the wild). Two ways in, same plaintext: the +// one-time claim code DM'd to the recipient, or a NIP-07 extension holding +// the recipient's key decrypting the ephemeral-key ciphertext in place. +const LOCKED_GIFT_VERSION = 2; +// Encrypt the gift's claim payload under a one-time code. The code is delivered +// to the recipient via a nostr DM; the public link carries only the ciphertext. +// Returns { blob (for the /lg/ link), claimCode (to DM) }. +export function lockGift(code, amount, recipientPkHex) { + const { code: claimCode, ctCode, eph, ctKey } = encryptGiftPayload(code, recipientPkHex); + const blob = base64urlnopad.encode(new TextEncoder().encode(JSON.stringify({ v: LOCKED_GIFT_VERSION, amount, to: recipientPkHex, ct: ctCode, eph, ctKey }))); + return { blob, claimCode }; +} +// The public fields of a locked-gift blob (or null). Decryption needs the code. +export function previewLockedGift(blob) { + try { + const o = JSON.parse(new TextDecoder().decode(base64urlnopad.decode(blob))); + if (o.v === LOCKED_GIFT_VERSION && typeof o.amount === 'number' && o.to && o.ct) return o; + } catch {} + return null; +} |
