Skip to content

Commit c58e542

Browse files
Merge pull request #7912 from BitGo/BTC-2775.bolt11
feat(sdk-core): add support for BOLT11 lightning invoices in OFC token
2 parents 5defefb + 30f26d9 commit c58e542

3 files changed

Lines changed: 72 additions & 0 deletions

File tree

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

Lines changed: 35 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, LIGHTNING_INVOICE } 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,37 @@ 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 === LIGHTNING_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 ${LIGHTNING_INVOICE}`
90+
);
91+
}
92+
if (amount > 0n) {
93+
return;
94+
}
95+
throw new Error(
96+
`invalid argument for amount - lightning invoice amount must be a non-zero bigint or ${LIGHTNING_INVOICE}`
97+
);
98+
}
99+
100+
super.checkRecipient(recipient);
101+
}
102+
68103
/**
69104
* Flag for sending value of 0
70105
* @returns {boolean} True if okay to send 0 value, false otherwise

modules/sdk-core/src/lightning.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const LIGHTNING_INVOICE = 'invoice';
2+
3+
export function isBolt11Invoice(value: unknown): value is string {
4+
if (typeof value !== 'string') {
5+
return false;
6+
}
7+
if (value.startsWith('lnbc') || value.startsWith('lntb')) {
8+
return true;
9+
}
10+
return false;
11+
}
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)