Skip to content

Commit

Permalink
feat: add flag handling to intraledger destination parsing (#329)
Browse files Browse the repository at this point in the history
  • Loading branch information
vindard authored Feb 16, 2024
1 parent ec9bca7 commit c3b3088
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 7 deletions.
72 changes: 72 additions & 0 deletions src/parsing/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,64 @@ describe("parsePaymentDestination IntraLedger handles", () => {
)
})

it("validates a handle with flag", () => {
const paymentDestination = parsePaymentDestination({
destination: "Nakamoto+usd",
network: "mainnet",
lnAddressDomains: [],
})
expect(paymentDestination).toEqual(
expect.objectContaining({
paymentType: PaymentType.IntraledgerWithFlag,
handle: "Nakamoto",
flag: "usd",
}),
)
})

it("validates a handle with invalid flag", () => {
const paymentDestination = parsePaymentDestination({
destination: "Nakamoto+btc",
network: "mainnet",
lnAddressDomains: [],
})
expect(paymentDestination).toEqual(
expect.objectContaining({
paymentType: PaymentType.Unknown,
valid: false,
}),
)
})

it("validates a handle with flag and bad username", () => {
const paymentDestination = parsePaymentDestination({
destination: "me+usd",
network: "mainnet",
lnAddressDomains: [],
})
expect(paymentDestination).toEqual(
expect.objectContaining({
paymentType: PaymentType.Unknown,
valid: false,
}),
)
})

it("validates an http handle", () => {
const paymentDestination = parsePaymentDestination({
destination: "https://some.where/userName",
network: "mainnet",
lnAddressDomains: ["some.where"],
})
expect(paymentDestination).toEqual(
expect.objectContaining({
handle: "userName",
paymentType: PaymentType.Intraledger,
}),
)
})

it("validates an http handle with invalid domain", () => {
const paymentDestination = parsePaymentDestination({
destination: "https://some.where/userName",
network: "mainnet",
Expand All @@ -612,4 +669,19 @@ describe("parsePaymentDestination IntraLedger handles", () => {
}),
)
})

it("validates an http handle with flag", () => {
const paymentDestination = parsePaymentDestination({
destination: "https://some.where/userName+usd",
network: "mainnet",
lnAddressDomains: ["some.where"],
})
expect(paymentDestination).toEqual(
expect.objectContaining({
paymentType: PaymentType.IntraledgerWithFlag,
handle: "userName",
flag: "usd",
}),
)
})
})
53 changes: 46 additions & 7 deletions src/parsing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export const getHashFromInvoice = (
export const PaymentType = {
Lightning: "lightning",
Intraledger: "intraledger",
IntraledgerWithFlag: "intraledgerWithFlag",
Onchain: "onchain",
Lnurl: "lnurl",
NullInput: "nullInput",
Expand Down Expand Up @@ -158,10 +159,16 @@ export type OnchainPaymentDestination =
invalidReason: InvalidOnchainDestinationReason
}

export const IntraledgerFlag = {
Usd: "usd",
} as const

export const InvalidIntraledgerReason = {
WrongDomain: "WrongDomain",
} as const

export type IntraledgerFlag = (typeof IntraledgerFlag)[keyof typeof IntraledgerFlag]

export type InvalidIntraledgerReason =
(typeof InvalidIntraledgerReason)[keyof typeof InvalidIntraledgerReason]

Expand All @@ -171,6 +178,12 @@ export type IntraledgerPaymentDestination =
paymentType: typeof PaymentType.Intraledger
handle: string
}
| {
valid: true
paymentType: typeof PaymentType.IntraledgerWithFlag
handle: string
flag: IntraledgerFlag
}
| {
valid: false
paymentType: typeof PaymentType.Intraledger
Expand Down Expand Up @@ -205,6 +218,8 @@ export const decodeInvoiceString = (
return bolt11.decode(invoice, parseBolt11Network(network))
}

const reUsername = /(?!^(1|3|bc1|lnbc1))^[0-9a-z_]{3,50}$/iu

// from https://github.com/bitcoin/bips/blob/master/bip-0020.mediawiki#Transfer%20amount/size
const reAmount = /^(([\d.]+)(X(\d+))?|x([\da-f]*)(\.([\da-f]*))?(X([\da-f]+))?)$/iu
const parseAmount = (txt: string): number => {
Expand Down Expand Up @@ -305,10 +320,21 @@ const getPaymentType = ({
]
: destinationWithoutProtocol

if (handle?.match(/(?!^(1|3|bc1|lnbc1))^[0-9a-z_]{3,50}$/iu)) {
if (handle?.match(reUsername)) {
return PaymentType.Intraledger
}

const handleAndFlag = handle?.split("+")
if (
handleAndFlag?.length === 2 &&
handleAndFlag[0].match(reUsername) &&
Object.values(IntraledgerFlag).includes(
handleAndFlag[1].toLowerCase() as IntraledgerFlag,
)
) {
return PaymentType.IntraledgerWithFlag
}

return PaymentType.Unknown
}

Expand All @@ -321,8 +347,6 @@ const getIntraLedgerPayResponse = ({
destination: string
lnAddressDomains: string[]
}): IntraledgerPaymentDestination | UnknownPaymentDestination => {
const paymentType = PaymentType.Intraledger

const handle = destinationWithoutProtocol.match(/^(http|\/\/)/iu)
? destinationWithoutProtocol.split("/")[
destinationWithoutProtocol.split("/").length - 1
Expand All @@ -334,21 +358,36 @@ const getIntraLedgerPayResponse = ({
if (!lnAddressDomains.find((lnAddressDomain) => lnAddressDomain === domain)) {
return {
valid: false,
paymentType,
paymentType: PaymentType.Intraledger,
handle,
invalidReason: InvalidIntraledgerReason.WrongDomain,
}
}
}

if (handle?.match(/(?!^(1|3|bc1|lnbc1))^[0-9a-z_]{3,50}$/iu)) {
if (handle?.match(reUsername)) {
return {
valid: true,
paymentType,
paymentType: PaymentType.Intraledger,
handle,
}
}

const handleAndFlag = handle?.split("+")
const flag = handleAndFlag[1]?.toLowerCase()
if (
handleAndFlag?.length === 2 &&
handleAndFlag[0].match(reUsername) &&
flag === IntraledgerFlag.Usd
) {
return {
valid: true,
paymentType: PaymentType.IntraledgerWithFlag,
handle: handleAndFlag[0],
flag,
}
}

return {
valid: false,
paymentType: PaymentType.Unknown,
Expand Down Expand Up @@ -564,7 +603,6 @@ export const parsePaymentDestination = ({
destinationWithoutProtocol,
rawDestination: destination,
})

switch (paymentType) {
case PaymentType.Lnurl:
return getLNURLPayResponse({
Expand All @@ -576,6 +614,7 @@ export const parsePaymentDestination = ({
case PaymentType.Onchain:
return getOnChainPayResponse({ destinationWithoutProtocol, network })
case PaymentType.Intraledger:
case PaymentType.IntraledgerWithFlag:
return getIntraLedgerPayResponse({
destinationWithoutProtocol,
destination,
Expand Down

0 comments on commit c3b3088

Please sign in to comment.