Skip to content

Commit 85fc6d6

Browse files
feat(sdk-core): add Lightning invoice support to OFC tokens
Implement support for Bolt11 Lightning Network invoices in the OfcToken class, allowing users to send to Lightning addresses. Added validation for Lightning invoice addresses and proper amount handling. - Created new lightning.ts module with isBolt11Invoice validation function - Extended checkRecipient method in OfcToken class to handle Lightning invoices - Added validation for Lightning invoice amounts and backing coins - Added unit tests for the Lightning invoice validation BTC-2775
1 parent 13d2e09 commit 85fc6d6

3 files changed

Lines changed: 68 additions & 0 deletions

File tree

modules/sdk-core/src/coins/ofcToken.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import {
88
CoinConstructor,
99
SignTransactionOptions as BaseSignTransactionOptions,
1010
SignedTransaction,
11+
ITransactionRecipient,
1112
} from '../';
13+
import { isBolt11Invoice } from '../lightning';
14+
1215
import { Ofc } from './ofc';
1316

1417
export interface SignTransactionOptions extends BaseSignTransactionOptions {
@@ -21,6 +24,7 @@ export interface SignTransactionOptions extends BaseSignTransactionOptions {
2124
export { OfcTokenConfig };
2225

2326
const publicIdRegex = /^[a-f\d]{32}$/i;
27+
2428
export class OfcToken extends Ofc {
2529
public readonly tokenConfig: OfcTokenConfig;
2630

@@ -65,6 +69,35 @@ export class OfcToken extends Ofc {
6569
return this.tokenConfig.type;
6670
}
6771

72+
checkRecipient(recipient: ITransactionRecipient): void {
73+
if (isBolt11Invoice(recipient.address)) {
74+
// should throw error if this isnt bitcoin (mainnet or testnet)
75+
if (this.backingCoin !== 'btc' && this.backingCoin !== 'tbtc') {
76+
throw new Error(`invalid argument - lightning invoice is only supported for bitcoin`);
77+
}
78+
79+
// amount for bolt11 invoices is either 'invoice' or a non-zero bigint
80+
if (recipient.amount === 'invoice') {
81+
return;
82+
}
83+
// try to parse the amount as a bigint
84+
let amount: bigint;
85+
try {
86+
amount = BigInt(recipient.amount);
87+
} catch (e) {
88+
throw new Error(
89+
`invalid argument ${recipient.amount} for amount - lightning invoice amount must be >= 0 or 'invoice'`
90+
);
91+
}
92+
if (amount > 0n) {
93+
return;
94+
}
95+
throw new Error(`invalid argument for amount - lightning invoice amount must be a non-zero bigint or 'invoice'`);
96+
}
97+
98+
super.checkRecipient(recipient);
99+
}
100+
68101
/**
69102
* Flag for sending value of 0
70103
* @returns {boolean} True if okay to send 0 value, false otherwise

modules/sdk-core/src/lightning.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function isBolt11Invoice(value: unknown): value is string {
2+
if (typeof value !== 'string') {
3+
return false;
4+
}
5+
if (value.startsWith('lnbc') || value.startsWith('lntb')) {
6+
return true;
7+
}
8+
return false;
9+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'should';
2+
3+
import { isBolt11Invoice } from '../../src/lightning';
4+
5+
describe('lightning', () => {
6+
describe('isBolt11Invoice', () => {
7+
it('should return true for a valid bolt11 invoice', () => {
8+
const invoice =
9+
'lnbc500n1p3zv5vkpp5x0thcaz8wep54clc2xt5895azjdzmthyskzzh9yslggy74qtvl6sdpdg3hkuct5d9hkugrxdaezqjn0dphk2fmnypkk2mtsdahkccqzpgxqyz5vqsp5v80q4vq4pwakq2l0hcqgtelgajsymv4ud4jdcrqtnzhvet55qlus9qyyssquqh2wl2m866qs5n72c5vg6wmqx9vzwhs5ypualq4mcu76h2tdkcq3jtjwtggfff7xwtdqxlnwqk8cxpzryjghrmmq3syraswp9vjr7cqry9l96';
10+
11+
isBolt11Invoice(invoice).should.equal(true);
12+
});
13+
14+
it('should return false for non-string values', () => {
15+
isBolt11Invoice(undefined).should.equal(false);
16+
isBolt11Invoice(null as any).should.equal(false);
17+
isBolt11Invoice(123 as any).should.equal(false);
18+
isBolt11Invoice({} as any).should.equal(false);
19+
});
20+
21+
it('should return false for invalid invoice strings', () => {
22+
isBolt11Invoice('').should.equal(false);
23+
isBolt11Invoice('not-an-invoice').should.equal(false);
24+
});
25+
});
26+
});

0 commit comments

Comments
 (0)