diff --git a/packages/app-node/src/backend/settlement-schemes/modules/stub.ts b/packages/app-node/src/backend/settlement-schemes/modules/stub.ts index 637012de..f1aa05e1 100644 --- a/packages/app-node/src/backend/settlement-schemes/modules/stub.ts +++ b/packages/app-node/src/backend/settlement-schemes/modules/stub.ts @@ -31,7 +31,11 @@ const stub = { behavior: ({ host }) => { let balance = INITIAL_BALANCE - host.reportOnLedgerBalance({ ledgerId: ledger.id, balance }) + const reportBalance = () => { + host.reportOnLedgerBalance({ ledgerId: ledger.id, balance }) + } + + reportBalance() return { getPeeringInfo: () => { @@ -63,6 +67,7 @@ const stub = { logger.info(`Sending settlement for ${amount} units to ${peerId}`) balance -= amount + reportBalance() return { proof: new Uint8Array(), @@ -72,6 +77,7 @@ const stub = { logger.info(`Received settlement for ${amount} units from ${peerId}`) balance += amount + reportBalance() return { result: "accept" as const, @@ -80,6 +86,12 @@ const stub = { handleMessage: () => { // no-op }, + handleDeposit: ({ amount }) => { + logger.info(`Received deposit for ${amount} units`) + + balance += amount + reportBalance() + }, } }, } satisfies SettlementSchemeModule diff --git a/packages/app-node/src/backend/settlement-schemes/modules/xrpl/xrpl-testnet.ts b/packages/app-node/src/backend/settlement-schemes/modules/xrpl/xrpl-testnet.ts index f79528cb..3aa72e82 100644 --- a/packages/app-node/src/backend/settlement-schemes/modules/xrpl/xrpl-testnet.ts +++ b/packages/app-node/src/backend/settlement-schemes/modules/xrpl/xrpl-testnet.ts @@ -283,6 +283,9 @@ const xrplTestnet = { handleMessage: () => { // no-op }, + handleDeposit: () => { + throw new Error("not implemented") + }, } }, } satisfies SettlementSchemeModule diff --git a/packages/app-node/src/backend/settlement-schemes/trpc-routers/settlement.ts b/packages/app-node/src/backend/settlement-schemes/trpc-routers/settlement.ts new file mode 100644 index 00000000..5557a305 --- /dev/null +++ b/packages/app-node/src/backend/settlement-schemes/trpc-routers/settlement.ts @@ -0,0 +1,28 @@ +import { z } from "zod" + +import { trpc } from "../../local-ipc-server/trpc-context" +import { SettlementSchemeId } from "../../peer-protocol/types/settlement-scheme-id" +import { protectedProcedure } from "../../trpc-server/middlewares/auth" +import { ManageSettlementSchemeInstancesActor } from "../manage-settlement-scheme-instances" + +export const settlementRouter = trpc.router({ + stubDeposit: protectedProcedure + .input(z.string()) + .mutation(async ({ input: amount, ctx: { sig } }) => { + const manageSettlementSchemeInstancesActor = sig.reactor.use( + ManageSettlementSchemeInstancesActor, + ) + + const stubActor = manageSettlementSchemeInstancesActor.get( + "stub" as SettlementSchemeId, + ) + + if (!stubActor) { + throw new Error("Stub settlement scheme is not enabled") + } + + await stubActor.api.handleDeposit.ask({ amount: BigInt(amount) }) + + return true + }), +}) diff --git a/packages/app-node/src/backend/settlement-schemes/types/settlement-scheme-module.ts b/packages/app-node/src/backend/settlement-schemes/types/settlement-scheme-module.ts index 162aae87..cf9c0751 100644 --- a/packages/app-node/src/backend/settlement-schemes/types/settlement-scheme-module.ts +++ b/packages/app-node/src/backend/settlement-schemes/types/settlement-scheme-module.ts @@ -126,6 +126,11 @@ export interface SettlementSchemeActorMethods< peerId: NodeId message: Uint8Array }) => Promisable + + /** + * Process an incoming deposit by the owner. + */ + handleDeposit: (parameters: { amount: bigint }) => Promisable } export interface SettlementSchemeBehaviorParameters { diff --git a/packages/app-node/src/backend/trpc-server/app-router.ts b/packages/app-node/src/backend/trpc-server/app-router.ts index 64514fd4..b592dcb4 100644 --- a/packages/app-node/src/backend/trpc-server/app-router.ts +++ b/packages/app-node/src/backend/trpc-server/app-router.ts @@ -2,6 +2,7 @@ import { acmeRouter } from "../acme-certificate-manager/trpc-routers/acme" import { apiKeysRouter } from "../api-keys/trpc-routers/api-keys" import { configAdminRouter } from "../config/trpc-routers/config-admin" import { tlsAdminRouter } from "../http-server/trpc-routers/tls-admin" +import { settlementRouter } from "../settlement-schemes/trpc-routers/settlement" import { debugRouter } from "./routers/debug" import { generalRouter } from "./routers/general" import { paymentRouter } from "./routers/payment" @@ -15,6 +16,7 @@ export const appRouter = trpc.router({ tls: tlsAdminRouter, config: configAdminRouter, apiKeys: apiKeysRouter, + settlement: settlementRouter, }) export type AppRouter = typeof appRouter diff --git a/packages/app-node/src/frontend/app.tsx b/packages/app-node/src/frontend/app.tsx index 181bd9d5..7325f0d4 100644 --- a/packages/app-node/src/frontend/app.tsx +++ b/packages/app-node/src/frontend/app.tsx @@ -10,6 +10,7 @@ import { Routing } from "./pages/debug/routing/routing" import { State } from "./pages/debug/state/state" import { LoginPage } from "./pages/login/login" import { PaymentStatus } from "./pages/payment-status/payment-status" +import { ReceivePage } from "./pages/receive/receive" import { Send } from "./pages/send/send" import { Settings } from "./pages/settings/settings" import { Setup } from "./pages/setup/setup" @@ -45,6 +46,7 @@ const App = () => { + diff --git a/packages/app-node/src/frontend/pages/account/account.tsx b/packages/app-node/src/frontend/pages/account/account.tsx index eb31bb9f..42a28f82 100644 --- a/packages/app-node/src/frontend/pages/account/account.tsx +++ b/packages/app-node/src/frontend/pages/account/account.tsx @@ -1,3 +1,4 @@ +import { DownloadIcon, UploadIcon } from "lucide-react" import { Link } from "wouter" import { Amount } from "../../components/amount/amount" @@ -35,9 +36,19 @@ export const Account = () => { - - - +
+ + + + + + +
) } diff --git a/packages/app-node/src/frontend/pages/receive/receive.tsx b/packages/app-node/src/frontend/pages/receive/receive.tsx new file mode 100644 index 00000000..3493297f --- /dev/null +++ b/packages/app-node/src/frontend/pages/receive/receive.tsx @@ -0,0 +1,91 @@ +import { ComponentType, LazyExoticComponent, Suspense, lazy } from "react" +import { Link } from "wouter" + +import { SettlementSchemeId } from "../../../backend/peer-protocol/types/settlement-scheme-id" +import { Button } from "../../components/ui/button" +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from "../../components/ui/card" +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "../../components/ui/tabs" +import { trpc } from "../../utils/trpc" + +const SCHEME_DEPOSIT_NAME_MAP: Record = { + ["stub" as SettlementSchemeId]: "Stub", +} + +const SCHEME_DEPOSIT_UI_MAP: Record< + SettlementSchemeId, + LazyExoticComponent +> = { + ["stub" as SettlementSchemeId]: lazy( + () => import("./settlement-schemes/stub"), + ), +} + +export function ReceivePage() { + const basicState = trpc.general.getBasicState.useQuery() + + const activeSettlementSchemes = basicState.data?.activeSettlementSchemes ?? [] + + return ( +
+ + + + Receive + + + + + + Interledger + {activeSettlementSchemes.map((schemeId) => + SCHEME_DEPOSIT_NAME_MAP[schemeId] ? ( + + {SCHEME_DEPOSIT_NAME_MAP[schemeId]} + + ) : null, + )} + + +

Send money to the following payment pointer:

+
+ {basicState.data ? `$${basicState.data.hostname}` : "???"} +
+
+ + {activeSettlementSchemes.map((schemeId) => { + const Component = SCHEME_DEPOSIT_UI_MAP[schemeId] + + if (!Component) { + return null + } + + return ( + + Loading...
}> + + + + ) + })} + + + + + + + + + + ) +} diff --git a/packages/app-node/src/frontend/pages/receive/settlement-schemes/stub.tsx b/packages/app-node/src/frontend/pages/receive/settlement-schemes/stub.tsx new file mode 100644 index 00000000..2c1e04ba --- /dev/null +++ b/packages/app-node/src/frontend/pages/receive/settlement-schemes/stub.tsx @@ -0,0 +1,28 @@ +import { useLocation } from "wouter" + +import { Button } from "../../../components/ui/button" +import { trpc } from "../../../utils/trpc" + +export default function StubDeposit() { + const stubDepositMutation = trpc.settlement.stubDeposit.useMutation() + const [, setLocation] = useLocation() + + return ( +
+

+ The "stub" settlement method only simulates a ledger rather + than using a real one. You can click the button below to simulate + receiving funds on the underlying ledger. +

+ +
+ ) +}