From b92c394236ca2842131a4401dfa199ab6925ee60 Mon Sep 17 00:00:00 2001 From: Sam Peters Date: Fri, 6 Oct 2023 10:02:54 -0500 Subject: [PATCH] feat: expose walletById and invoiceByHash --- .../dev/apollo-federation/supergraph.graphql | 5 ++++ .../wallets/get-invoice-for-wallet-by-hash.ts | 15 ++++------- core/api/src/app/wallets/index.ts | 21 +++++++++++++++ core/api/src/graphql/admin/schema.graphql | 10 +++++++ core/api/src/graphql/public/schema.graphql | 5 ++++ .../graphql/public/types/abstract/account.ts | 9 ++++++- .../public/types/object/consumer-account.ts | 20 ++++++++++++++ .../graphql/shared/types/abstract/wallet.ts | 10 +++++++ .../graphql/shared/types/object/btc-wallet.ts | 26 +++++++++++++++++++ .../graphql/shared/types/object/usd-wallet.ts | 26 +++++++++++++++++++ 10 files changed, 136 insertions(+), 11 deletions(-) diff --git a/core/api/dev/apollo-federation/supergraph.graphql b/core/api/dev/apollo-federation/supergraph.graphql index 08bc0e011fc..953077353ed 100644 --- a/core/api/dev/apollo-federation/supergraph.graphql +++ b/core/api/dev/apollo-federation/supergraph.graphql @@ -47,6 +47,7 @@ interface Account last: Int walletIds: [WalletId] ): TransactionConnection + walletById(walletId: WalletId!): Wallet! wallets: [Wallet!]! } @@ -178,6 +179,7 @@ type BTCWallet implements Wallet """A balance stored in BTC.""" balance: SignedAmount! id: ID! + invoiceByHash(paymentHash: PaymentHash!): LnInvoice! """An unconfirmed incoming onchain balance.""" pendingIncomingBalance: SignedAmount! @@ -324,6 +326,7 @@ type ConsumerAccount implements Account last: Int walletIds: [WalletId] ): TransactionConnection + walletById(walletId: WalletId!): Wallet! wallets: [Wallet!]! } @@ -1582,6 +1585,7 @@ type UsdWallet implements Wallet accountId: ID! balance: SignedAmount! id: ID! + invoiceByHash(paymentHash: PaymentHash!): LnInvoice! """An unconfirmed incoming onchain balance.""" pendingIncomingBalance: SignedAmount! @@ -1894,6 +1898,7 @@ interface Wallet accountId: ID! balance: SignedAmount! id: ID! + invoiceByHash(paymentHash: PaymentHash!): LnInvoice! pendingIncomingBalance: SignedAmount! """ diff --git a/core/api/src/app/wallets/get-invoice-for-wallet-by-hash.ts b/core/api/src/app/wallets/get-invoice-for-wallet-by-hash.ts index f481321ca9a..bb8dc2045e1 100644 --- a/core/api/src/app/wallets/get-invoice-for-wallet-by-hash.ts +++ b/core/api/src/app/wallets/get-invoice-for-wallet-by-hash.ts @@ -1,24 +1,19 @@ -import { CouldNotFindWalletInvoiceError } from "@domain/errors" -import { checkedToWalletId } from "@domain/wallets" -import { WalletInvoicesRepository } from "@services/mongoose" +import { CouldNotFindWalletInvoiceError } from "@/domain/errors" +import { WalletInvoicesRepository } from "@/services/mongoose" export const getInvoiceForWalletByHash = async ({ - walletId: uncheckedWalletId, + walletId, paymentHash, }: { - walletId: string + walletId: WalletId paymentHash: PaymentHash -}) => { - const walletId = checkedToWalletId(uncheckedWalletId) - if (walletId instanceof Error) return walletId - +}): Promise => { const walletInvoicesRepository = WalletInvoicesRepository() const walletInvoice = await walletInvoicesRepository.findByPaymentHash(paymentHash) if (walletInvoice instanceof Error) return walletInvoice - // QUESTION: Should we make the walletId part of the repository method or do this check here? if (walletInvoice.recipientWalletDescriptor.id !== walletId) { return new CouldNotFindWalletInvoiceError() } diff --git a/core/api/src/app/wallets/index.ts b/core/api/src/app/wallets/index.ts index 85bfd638327..4c8dda24f7d 100644 --- a/core/api/src/app/wallets/index.ts +++ b/core/api/src/app/wallets/index.ts @@ -16,7 +16,9 @@ export * from "./settle-payout-txn" export * from "./update-legacy-on-chain-receipt" export * from "./update-pending-invoices" export * from "./validate" +export * from "./get-invoice-for-wallet-by-hash" +import { CouldNotFindWalletFromIdError } from "@/domain/errors" import { WalletsRepository } from "@/services/mongoose" export const getWallet = async (walletId: WalletId) => { @@ -24,6 +26,25 @@ export const getWallet = async (walletId: WalletId) => { return wallets.findById(walletId) } +export const getWalletForAccountById = async ({ + accountId, + walletId, +}: { + accountId: AccountId + walletId: WalletId +}): Promise => { + const wallets = WalletsRepository() + const wallet = await wallets.findById(walletId) + + if (wallet instanceof Error) return wallet + + if (wallet.accountId !== accountId) { + return new CouldNotFindWalletFromIdError() + } + + return wallet +} + export const listWalletsByAccountId = async ( accountId: AccountId, ): Promise => { diff --git a/core/api/src/graphql/admin/schema.graphql b/core/api/src/graphql/admin/schema.graphql index 0998abf30a9..293da911db2 100644 --- a/core/api/src/graphql/admin/schema.graphql +++ b/core/api/src/graphql/admin/schema.graphql @@ -88,6 +88,7 @@ type BTCWallet implements Wallet { """A balance stored in BTC.""" balance: SignedAmount! id: ID! + invoiceByHash(paymentHash: PaymentHash!): LnInvoice! """An unconfirmed incoming onchain balance.""" pendingIncomingBalance: SignedAmount! @@ -223,6 +224,13 @@ type LightningPayment { status: LnPaymentStatus } +type LnInvoice { + paymentHash: PaymentHash! + paymentRequest: LnPaymentRequest! + paymentSecret: LnPaymentSecret! + satoshis: SatAmount +} + scalar LnPaymentPreImage """BOLT11 lightning invoice payment request with the amount included""" @@ -436,6 +444,7 @@ type UsdWallet implements Wallet { accountId: ID! balance: SignedAmount! id: ID! + invoiceByHash(paymentHash: PaymentHash!): LnInvoice! """An unconfirmed incoming onchain balance.""" pendingIncomingBalance: SignedAmount! @@ -491,6 +500,7 @@ interface Wallet { accountId: ID! balance: SignedAmount! id: ID! + invoiceByHash(paymentHash: PaymentHash!): LnInvoice! pendingIncomingBalance: SignedAmount! """ diff --git a/core/api/src/graphql/public/schema.graphql b/core/api/src/graphql/public/schema.graphql index 467a0d554d3..2195b98873e 100644 --- a/core/api/src/graphql/public/schema.graphql +++ b/core/api/src/graphql/public/schema.graphql @@ -22,6 +22,7 @@ interface Account { last: Int walletIds: [WalletId] ): TransactionConnection + walletById(walletId: WalletId!): Wallet! wallets: [Wallet!]! } @@ -121,6 +122,7 @@ type BTCWallet implements Wallet { """A balance stored in BTC.""" balance: SignedAmount! id: ID! + invoiceByHash(paymentHash: PaymentHash!): LnInvoice! """An unconfirmed incoming onchain balance.""" pendingIncomingBalance: SignedAmount! @@ -245,6 +247,7 @@ type ConsumerAccount implements Account { last: Int walletIds: [WalletId] ): TransactionConnection + walletById(walletId: WalletId!): Wallet! wallets: [Wallet!]! } @@ -1243,6 +1246,7 @@ type UsdWallet implements Wallet { accountId: ID! balance: SignedAmount! id: ID! + invoiceByHash(paymentHash: PaymentHash!): LnInvoice! """An unconfirmed incoming onchain balance.""" pendingIncomingBalance: SignedAmount! @@ -1487,6 +1491,7 @@ interface Wallet { accountId: ID! balance: SignedAmount! id: ID! + invoiceByHash(paymentHash: PaymentHash!): LnInvoice! pendingIncomingBalance: SignedAmount! """ diff --git a/core/api/src/graphql/public/types/abstract/account.ts b/core/api/src/graphql/public/types/abstract/account.ts index 4245c833c59..0466996f321 100644 --- a/core/api/src/graphql/public/types/abstract/account.ts +++ b/core/api/src/graphql/public/types/abstract/account.ts @@ -62,7 +62,14 @@ const IAccount = GT.Interface({ notificationSettings: { type: GT.NonNull(NotificationSettings), }, - + walletById: { + type: GT.NonNull(Wallet), + args: { + walletId: { + type: GT.NonNull(WalletId), + }, + }, + }, // FUTURE-PLAN: Support a `users: [User!]!` field here }), }) diff --git a/core/api/src/graphql/public/types/object/consumer-account.ts b/core/api/src/graphql/public/types/object/consumer-account.ts index e8800b89368..bf40cad1477 100644 --- a/core/api/src/graphql/public/types/object/consumer-account.ts +++ b/core/api/src/graphql/public/types/object/consumer-account.ts @@ -62,6 +62,26 @@ const ConsumerAccount = GT.Object({ }, }, + walletById: { + type: GT.NonNull(Wallet), + args: { + walletId: { + type: GT.NonNull(WalletId), + }, + }, + resolve: async (source, args) => { + const { walletId } = args + const wallet = await Wallets.getWalletForAccountById({ + walletId, + accountId: source.id, + }) + if (wallet instanceof Error) { + throw mapError(wallet) + } + return wallet + }, + }, + defaultWalletId: { type: GT.NonNull(WalletId), resolve: (source) => source.defaultWalletId, diff --git a/core/api/src/graphql/shared/types/abstract/wallet.ts b/core/api/src/graphql/shared/types/abstract/wallet.ts index 49456e43f4e..2bed415a7e6 100644 --- a/core/api/src/graphql/shared/types/abstract/wallet.ts +++ b/core/api/src/graphql/shared/types/abstract/wallet.ts @@ -4,9 +4,11 @@ import { TransactionConnection } from "../object/transaction" import WalletCurrency from "../scalar/wallet-currency" import SignedAmount from "../scalar/signed-amount" import OnChainAddress from "../scalar/on-chain-address" +import PaymentHash from "../scalar/payment-hash" import { connectionArgs } from "@/graphql/connections" import { GT } from "@/graphql/index" +import LnInvoice from "@/graphql/public/types/object/ln-invoice" const IWallet = GT.Interface({ name: "Wallet", @@ -45,6 +47,14 @@ const IWallet = GT.Interface({ }, }, }, + invoiceByHash: { + type: GT.NonNull(LnInvoice), + args: { + paymentHash: { + type: GT.NonNull(PaymentHash), + }, + }, + }, }), }) diff --git a/core/api/src/graphql/shared/types/object/btc-wallet.ts b/core/api/src/graphql/shared/types/object/btc-wallet.ts index f32d1dd9f1a..7c24ad8d1f6 100644 --- a/core/api/src/graphql/shared/types/object/btc-wallet.ts +++ b/core/api/src/graphql/shared/types/object/btc-wallet.ts @@ -6,6 +6,8 @@ import WalletCurrency from "../scalar/wallet-currency" import OnChainAddress from "../scalar/on-chain-address" +import PaymentHash from "../scalar/payment-hash" + import { TransactionConnection } from "./transaction" import { GT } from "@/graphql/index" @@ -20,6 +22,7 @@ import { mapError } from "@/graphql/error-map" import { Wallets } from "@/app" import { WalletCurrency as WalletCurrencyDomain } from "@/domain/shared" +import LnInvoice from "@/graphql/public/types/object/ln-invoice" const BtcWallet = GT.Object({ name: "BTCWallet", @@ -125,6 +128,29 @@ const BtcWallet = GT.Object({ ) }, }, + invoiceByHash: { + type: GT.NonNull(LnInvoice), + args: { + paymentHash: { + type: GT.NonNull(PaymentHash), + }, + }, + resolve: async (source, args) => { + const { paymentHash } = args + if (paymentHash instanceof Error) throw paymentHash + + const invoice = await Wallets.getInvoiceForWalletByHash({ + walletId: source.id, + paymentHash, + }) + + if (invoice instanceof Error) { + throw mapError(invoice) + } + + return invoice + }, + }, }), }) diff --git a/core/api/src/graphql/shared/types/object/usd-wallet.ts b/core/api/src/graphql/shared/types/object/usd-wallet.ts index 2a7c71afe94..50d7ed6bf65 100644 --- a/core/api/src/graphql/shared/types/object/usd-wallet.ts +++ b/core/api/src/graphql/shared/types/object/usd-wallet.ts @@ -6,6 +6,8 @@ import SignedAmount from "../scalar/signed-amount" import OnChainAddress from "../scalar/on-chain-address" +import PaymentHash from "../scalar/payment-hash" + import { TransactionConnection } from "./transaction" import { GT } from "@/graphql/index" @@ -20,6 +22,7 @@ import { mapError } from "@/graphql/error-map" import { Wallets } from "@/app" import { WalletCurrency as WalletCurrencyDomain } from "@/domain/shared" +import LnInvoice from "@/graphql/public/types/object/ln-invoice" const UsdWallet = GT.Object({ name: "UsdWallet", @@ -123,6 +126,29 @@ const UsdWallet = GT.Object({ ) }, }, + invoiceByHash: { + type: GT.NonNull(LnInvoice), + args: { + paymentHash: { + type: GT.NonNull(PaymentHash), + }, + }, + resolve: async (source, args) => { + const { paymentHash } = args + if (paymentHash instanceof Error) throw paymentHash + + const invoice = await Wallets.getInvoiceForWalletByHash({ + walletId: source.id, + paymentHash, + }) + + if (invoice instanceof Error) { + throw mapError(invoice) + } + + return invoice + }, + }, }), })