Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add flag handling to intraledger destination parsing #329

Merged
merged 1 commit into from
Feb 16, 2024
Merged
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
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
Loading