aboutsummaryrefslogtreecommitdiffstats
path: root/src/wallet.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet.js')
-rw-r--r--src/wallet.js25
1 files changed, 23 insertions, 2 deletions
diff --git a/src/wallet.js b/src/wallet.js
index edacc05..08f5d9e 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -9,9 +9,9 @@
// transactions, only the (txid, vout, value, address) of each UTXO.
import { HDKey } from '@scure/bip32';
-import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39';
+import { generateMnemonic, mnemonicToSeedSync, validateMnemonic, mnemonicToEntropy, entropyToMnemonic } from '@scure/bip39';
import { wordlist } from '@scure/bip39/wordlists/english';
-import { hex } from '@scure/base';
+import { hex, base64urlnopad } from '@scure/base';
import { sha256 } from '@noble/hashes/sha256';
import * as btc from '@scure/btc-signer';
import { p2wpkh } from '@scure/btc-signer/payment';
@@ -1185,6 +1185,27 @@ export function utxoId(u) {
return `${u.txid}:${u.vout}`;
}
+// Gift links: encode a wallet (12-word seed as BIP39 entropy, plus the optional
+// passphrase) compactly for the #claim= fragment as base64url, and decode it
+// back to { mnemonic, passphrase }. Returns null if the code isn't valid.
+export function encodeGift(mnemonic, passphrase = '') {
+ let code = base64urlnopad.encode(mnemonicToEntropy(mnemonic.trim().replace(/\s+/g, ' '), wordlist));
+ if (passphrase) code += '~' + base64urlnopad.encode(new TextEncoder().encode(passphrase));
+ return code;
+}
+
+export function decodeGift(code) {
+ try {
+ const [seedPart, passPart] = code.split('~');
+ const mnemonic = entropyToMnemonic(base64urlnopad.decode(seedPart), wordlist);
+ if (!validateMnemonic(mnemonic, wordlist)) return null;
+ const passphrase = passPart ? new TextDecoder().decode(base64urlnopad.decode(passPart)) : '';
+ return { mnemonic, passphrase };
+ } catch {
+ return null;
+ }
+}
+
function firstUnused(chain) {
const u = chain.find((a) => !a.used);
return u ? u.index : chain.length;