Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/sdk-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"big.js": "^3.1.3",
"bigint-crypto-utils": "3.1.4",
"bignumber.js": "^9.1.1",
"bolt11": "^1.0.0",
"bs58": "^4.0.1",
"create-hmac": "^1.1.7",
"debug": "^3.1.0",
Expand Down
38 changes: 38 additions & 0 deletions modules/sdk-core/src/coins/ofcToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
* @prettier
*/
import { OfcTokenConfig } from '@bitgo/statics';
import * as bolt11 from 'bolt11';
import { isString } from 'lodash';
import {
BitGoBase,
CoinConstructor,
SignTransactionOptions as BaseSignTransactionOptions,
SignedTransaction,
ITransactionRecipient,
} from '../';
import { Ofc } from './ofc';

Expand All @@ -21,6 +23,18 @@ export interface SignTransactionOptions extends BaseSignTransactionOptions {
export { OfcTokenConfig };

const publicIdRegex = /^[a-f\d]{32}$/i;

function isBolt11Invoice(value: unknown): value is string {
if (!isString(value)) {
return false;
}
try {
bolt11.decode(value);
return true;
} catch (_e) {
return false;
}
}
export class OfcToken extends Ofc {
public readonly tokenConfig: OfcTokenConfig;

Expand Down Expand Up @@ -65,6 +79,30 @@ export class OfcToken extends Ofc {
return this.tokenConfig.type;
}

checkRecipient(recipient: ITransactionRecipient): void {
if (isBolt11Invoice(recipient.address)) {
// amount for bolt11 invoices is either 'invoice' or a non-zero bigint
if (recipient.amount === 'invoice') {
return;
}
// try to parse the amount as a bigint
let amount: bigint;
try {
amount = BigInt(recipient.amount);
} catch (e) {
throw new Error(
`invalid argument ${recipient.amount} for amount - lightning invoice amount must be >= 0 or 'invoice'`
);
}
if (amount > 0n) {
return;
}
throw new Error(`invalid argument for amount - lightning invoice amount must be a non-zero bigint or 'invoice'`);
}

super.checkRecipient(recipient);
}

/**
* Flag for sending value of 0
* @returns {boolean} True if okay to send 0 value, false otherwise
Expand Down
41 changes: 41 additions & 0 deletions modules/sdk-core/test/unit/coins/ofcToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'should';

import { OfcToken } from '../../../src/coins/ofcToken';

describe('OfcToken.checkRecipient', () => {
const token = new OfcToken(
{} as any,
{
coin: 'tofc',
decimalPlaces: 2,
name: 'Test OFC Token',
backingCoin: 'ofc',
isFiat: false,
type: 'tofc',
} as any
);

// Valid BOLT11 invoice (taken from BitGoJS fixtures / BOLTs examples)
const bolt11 =
'lntb20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un989qrsgqdj545axuxtnfemtpwkc45hx9d2ft7x04mt8q7y6t0k2dge9e7h8kpy9p34ytyslj3yu569aalz2xdk8xkd7ltxqld94u8h2esmsmacgpghe9k8';

it('should allow bolt11 invoices with amount "invoice"', () => {
(() => token.checkRecipient({ address: bolt11, amount: 'invoice' } as any)).should.not.throw();
});

it('should allow bolt11 invoices with non-zero bigint amount', () => {
(() => token.checkRecipient({ address: bolt11, amount: 1n } as any)).should.not.throw();
});

it('should reject bolt11 invoices with non-bigint numeric/string amounts', () => {
(() => token.checkRecipient({ address: bolt11, amount: '1' } as any)).should.throw();
(() => token.checkRecipient({ address: bolt11, amount: 1 } as any)).should.throw();
});

it('should defer to super.checkRecipient for non-bolt11 addresses', () => {
// BaseCoin.checkRecipient rejects zero amounts when valuelessTransferAllowed() is false (default for OfcToken).
(() => token.checkRecipient({ address: 'bg-0123456789abcdef0123456789abcdef', amount: '0' } as any)).should.throw();
(() =>
token.checkRecipient({ address: 'bg-0123456789abcdef0123456789abcdef', amount: '1' } as any)).should.not.throw();
});
});
64 changes: 21 additions & 43 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7867,7 +7867,7 @@ bech32-buffer@^0.2.1:
resolved "https://registry.npmjs.org/bech32-buffer/-/bech32-buffer-0.2.1.tgz"
integrity sha512-fCG1TyZuCN48Sdw97p/IR39fvqpFlWDVpG7qnuU1Uc3+Xtc/0uqAp8U7bMW/bGuVF5CcNVIXwxQsWwUr6un6FQ==

bech32@1.1.4, bech32@^1.1.3, bech32@^1.1.4:
bech32@1.1.4, bech32@^1.1.2, bech32@^1.1.3, bech32@^1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz"
integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==
Expand Down Expand Up @@ -8014,7 +8014,7 @@ bitcoin-ops@^1.3.0:
resolved "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz"
integrity sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow==

bitcoinjs-lib@^6.1.5, bitcoinjs-lib@^6.1.7:
bitcoinjs-lib@^6.0.0, bitcoinjs-lib@^6.1.5, bitcoinjs-lib@^6.1.7:
version "6.1.7"
resolved "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.7.tgz"
integrity sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg==
Expand Down Expand Up @@ -8143,6 +8143,20 @@ body-parser@1.20.3, body-parser@^1.19.0, body-parser@^1.20.3:
type-is "~1.6.18"
unpipe "1.0.0"

bolt11@^1.0.0:
version "1.4.1"
resolved "https://registry.npmjs.org/bolt11/-/bolt11-1.4.1.tgz#4363041b8c9f477b7f42c12d96e771fec39a00f1"
integrity sha512-jR0Y+MO+CK2at1Cg5mltLJ+6tdOwNKoTS/DJOBDdzVkQ+R9D6UgZMayTWOsuzY7OgV1gEqlyT5Tzk6t6r4XcNQ==
dependencies:
"@types/bn.js" "^4.11.3"
bech32 "^1.1.2"
bitcoinjs-lib "^6.0.0"
bn.js "^4.11.8"
create-hash "^1.2.0"
lodash "^4.17.11"
safe-buffer "^5.1.1"
secp256k1 "^4.0.2"

bonjour-service@^1.2.1:
version "1.3.0"
resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz"
Expand Down Expand Up @@ -12791,18 +12805,7 @@ html-minifier-terser@^6.0.2:
tapable "^1.1.3"
util.promisify "1.0.0"

"html-webpack-plugin-5@npm:html-webpack-plugin@^5":
version "5.6.4"
resolved "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.4.tgz"
integrity sha512-V/PZeWsqhfpE27nKeX9EO2sbR+D17A+tLf6qU+ht66jdUsN0QLKJN27Z+1+gHrVMKgndBahes0PU6rRihDgHTw==
dependencies:
"@types/html-minifier-terser" "^6.0.0"
html-minifier-terser "^6.0.2"
lodash "^4.17.21"
pretty-error "^4.0.0"
tapable "^2.0.0"

html-webpack-plugin@^5.5.0:
"html-webpack-plugin-5@npm:html-webpack-plugin@^5", html-webpack-plugin@^5.5.0:
version "5.6.4"
resolved "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.4.tgz"
integrity sha512-V/PZeWsqhfpE27nKeX9EO2sbR+D17A+tLf6qU+ht66jdUsN0QLKJN27Z+1+gHrVMKgndBahes0PU6rRihDgHTw==
Expand Down Expand Up @@ -18689,7 +18692,7 @@ scrypt-js@3.0.1, scrypt-js@^3.0.0:
resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz"
integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==

secp256k1@3.7.1, secp256k1@5.0.1, secp256k1@^3.0.1, secp256k1@^4.0.0, secp256k1@^4.0.1, secp256k1@^5.0.0:
secp256k1@3.7.1, secp256k1@5.0.1, secp256k1@^3.0.1, secp256k1@^4.0.0, secp256k1@^4.0.1, secp256k1@^4.0.2, secp256k1@^5.0.0:
version "5.0.1"
resolved "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.1.tgz"
integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==
Expand Down Expand Up @@ -19600,16 +19603,7 @@ string-argv@^0.3.1:
resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz"
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==

"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -19685,7 +19679,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand All @@ -19699,13 +19693,6 @@ strip-ansi@^3.0.1:
dependencies:
ansi-regex "^2.0.0"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz"
Expand Down Expand Up @@ -21428,7 +21415,7 @@ workerpool@^6.5.1:
resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz"
integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand All @@ -21446,15 +21433,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz"
Expand Down
Loading