From 35b9e4cc5bb9593ada958fa35c7b8622ffdc3e7a Mon Sep 17 00:00:00 2001 From: "kody.low" Date: Sat, 23 Mar 2024 14:17:09 -0700 Subject: [PATCH] feat: activeGatewayId for lightning --- .../router/handlers/fedimint/admin/join.rs | 8 +- wrappers/fedimint-ts/FedimintClient.ts | 156 ++++++++++++++---- wrappers/fedimint-ts/test.ts | 11 +- wrappers/fedimint-ts/types.ts | 34 +++- 4 files changed, 170 insertions(+), 39 deletions(-) diff --git a/fedimint-clientd/src/router/handlers/fedimint/admin/join.rs b/fedimint-clientd/src/router/handlers/fedimint/admin/join.rs index 5d248dd..c47376a 100644 --- a/fedimint-clientd/src/router/handlers/fedimint/admin/join.rs +++ b/fedimint-clientd/src/router/handlers/fedimint/admin/join.rs @@ -23,6 +23,7 @@ pub struct JoinRequest { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct JoinResponse { + pub this_federation_id: FederationId, pub federation_ids: Vec, } @@ -40,13 +41,16 @@ async fn _join(mut multimint: MultiMint, req: JoinRequest) -> Result>(); - Ok(JoinResponse { federation_ids }) + Ok(JoinResponse { + this_federation_id, + federation_ids, + }) } pub async fn handle_ws(state: AppState, v: Value) -> Result { diff --git a/wrappers/fedimint-ts/FedimintClient.ts b/wrappers/fedimint-ts/FedimintClient.ts index bc3fedc..922c6d6 100644 --- a/wrappers/fedimint-ts/FedimintClient.ts +++ b/wrappers/fedimint-ts/FedimintClient.ts @@ -40,6 +40,7 @@ import type { NotesJson, MintEncodeNotesResponse, MintDecodeNotesResponse, + JoinResponse, } from "./types"; type FedimintResponse = Promise; @@ -48,11 +49,13 @@ class FedimintClientBuilder { private baseUrl: string; private password: string; private activeFederationId: string; + private activeGatewayId: string; constructor() { this.baseUrl = ""; this.password = ""; this.activeFederationId = ""; + this.activeGatewayId = ""; } setBaseUrl(baseUrl: string): FedimintClientBuilder { @@ -73,19 +76,22 @@ class FedimintClientBuilder { return this; } + setActiveGatewayId(gatewayId: string): FedimintClientBuilder { + this.activeGatewayId = gatewayId; + + return this; + } + build(): FedimintClient { - if ( - this.baseUrl === "" || - this.password === "" || - this.activeFederationId === "" - ) { - throw new Error("baseUrl, password, and activeFederationId must be set"); + if (this.baseUrl === "" || this.password === "") { + throw new Error("baseUrl, and password must be set"); } const client = new FedimintClient( this.baseUrl, this.password, - this.activeFederationId + this.activeFederationId, + this.activeGatewayId ); return client; @@ -96,19 +102,61 @@ class FedimintClient { private baseUrl: string; private password: string; private activeFederationId: string; - - constructor(baseUrl: string, password: string, activeFederationId: string) { + private activeGatewayId: string; + + constructor( + baseUrl: string, + password: string, + activeFederationId: string, + activeGatewayId: string = "" + ) { this.baseUrl = baseUrl + "/v2"; this.password = password; this.activeFederationId = activeFederationId; + this.activeGatewayId = activeGatewayId; + console.log( + "Fedimint Client initialized, must set activeGatewayId after intitalization to use lightning module methods or manually pass in gateways" + ); } getActiveFederationId(): string { return this.activeFederationId; } - setActiveFederationId(federationId: string) { + setActiveFederationId(federationId: string, useDefaultGateway: boolean) { this.activeFederationId = federationId; + console.log("Changed active federation id to: ", federationId); + + if (useDefaultGateway) { + this.ln.listGateways().then((gateways) => { + this.activeGatewayId = gateways[0].info.gateway_id; + }); + } else { + console.log( + "Clearing active gateway id, must be set manually on lightning calls or setDefaultGatewayId to true" + ); + this.activeGatewayId = ""; + } + } + + getActiveGatewayId(): string { + return this.activeGatewayId; + } + + setActiveGatewayId(gatewayId: string) { + this.activeGatewayId = gatewayId; + } + + async useDefaultGateway() { + // hits list_gateways and sets activeGatewayId to the first gateway + try { + const gateways = await this.ln.listGateways(); + console.log("Gateways: ", gateways); + this.activeGatewayId = gateways[0].info.gateway_id; + console.log("Set active gateway id to: ", this.activeGatewayId); + } catch (error) { + console.error("Error setting default gateway id: ", error); + } } /** @@ -158,14 +206,11 @@ class FedimintClient { return (await res.json()) as T; } - // Adjust postWithId to not require federationId as a mandatory parameter - // since ensureactiveFederationId will ensure it's set. - private async postWithId( + private async postWithFederationId( endpoint: string, body: any, federationId?: string ): FedimintResponse { - // Note: No need to call ensureactiveFederationId here since post already does. const effectiveFederationId = federationId || this.activeFederationId; return this.post(endpoint, { @@ -174,6 +219,33 @@ class FedimintClient { }); } + private async postWithGatewayIdAndFederationId( + endpoint: string, + body: any, + gatewayId?: string, + federationId?: string + ): FedimintResponse { + try { + const effectiveGatewayId = gatewayId || this.activeGatewayId; + const effectiveFederationId = federationId || this.activeFederationId; + + if (effectiveFederationId === "" || effectiveGatewayId === "") { + throw new Error( + "Must set active federation and gateway id before posting with them" + ); + } + + return this.post(endpoint, { + ...body, + federationId: effectiveFederationId, + gatewayId: effectiveGatewayId, + }); + } catch (error) { + console.error("Error posting with federation and gateway id: ", error); + throw error; + } + } + /** * Uploads the encrypted snapshot of mint notest to the federation */ @@ -181,7 +253,11 @@ class FedimintClient { metadata: BackupRequest, federationId?: string ): FedimintResponse { - await this.postWithId("/admin/backup", metadata, federationId); + await this.postWithFederationId( + "/admin/backup", + metadata, + federationId + ); } /** @@ -226,11 +302,19 @@ class FedimintClient { */ public async join( inviteCode: string, + setActiveFederationId: boolean, + useDefaultGateway: boolean, useManualSecret: boolean = false - ): FedimintResponse { + ): FedimintResponse { const request: JoinRequest = { inviteCode, useManualSecret }; - return await this.post("/admin/join", request); + const response = await this.post("/admin/join", request); + + if (setActiveFederationId) { + this.setActiveFederationId(response.thisFederationId, useDefaultGateway); + } + + return response; } /** @@ -242,7 +326,7 @@ class FedimintClient { ): FedimintResponse { const request: ListOperationsRequest = { limit }; - return await this.postWithId( + return await this.postWithFederationId( "/admin/list-operations", request, federationId @@ -260,13 +344,15 @@ class FedimintClient { amountMsat: number, description: string, expiryTime?: number, + gatewayId?: string, federationId?: string ): FedimintResponse => { const request: LnInvoiceRequest = { amountMsat, description, expiryTime }; - return await this.postWithId( + return await this.postWithGatewayIdAndFederationId( "/ln/invoice", request, + gatewayId, federationId ); }, @@ -280,6 +366,7 @@ class FedimintClient { amountMsat: number, description: string, expiryTime?: number, + gatewayId?: string, federationId?: string ): FedimintResponse => { const request: LnInvoiceExternalPubkeyRequest = { @@ -289,9 +376,10 @@ class FedimintClient { expiryTime, }; - return await this.postWithId( + return await this.postWithGatewayIdAndFederationId( "/ln/invoice-external-pubkey", request, + gatewayId, federationId ); }, @@ -307,6 +395,7 @@ class FedimintClient { amountMsat: number, description: string, expiryTime?: number, + gatewayId?: string, federationId?: string ): FedimintResponse => { const request: LnInvoiceExternalPubkeyTweakedRequest = { @@ -317,9 +406,10 @@ class FedimintClient { expiryTime, }; - return await this.postWithId( + return await this.postWithGatewayIdAndFederationId( "/ln/invoice-external-pubkey-tweaked", request, + gatewayId, federationId ); }, @@ -333,7 +423,7 @@ class FedimintClient { ): FedimintResponse => { const request: LnClaimPubkeyReceiveRequest = { privateKey }; - return await this.postWithId( + return await this.postWithFederationId( "/ln/claim-external-receive", request, federationId @@ -354,7 +444,7 @@ class FedimintClient { tweaks, }; - return await this.postWithId( + return await this.postWithFederationId( "/ln/claim-external-receive-tweaked", request, federationId @@ -369,7 +459,7 @@ class FedimintClient { ): FedimintResponse => { const request: LnAwaitInvoiceRequest = { operationId }; - return await this.postWithId( + return await this.postWithFederationId( "/ln/await-invoice", request, federationId @@ -383,6 +473,7 @@ class FedimintClient { paymentInfo: string, amountMsat?: number, lnurlComment?: string, + gatewayId?: string, federationId?: string ): FedimintResponse => { const request: LnPayRequest = { @@ -391,9 +482,10 @@ class FedimintClient { lnurlComment, }; - return await this.postWithId( + return await this.postWithGatewayIdAndFederationId( "/ln/pay", request, + gatewayId, federationId ); }, @@ -402,7 +494,7 @@ class FedimintClient { * Outputs a list of registered lighting lightning gateways */ listGateways: async (): FedimintResponse => - await this.postWithId("/ln/list-gateways", {}), + await this.postWithFederationId("/ln/list-gateways", {}), }; /** @@ -451,7 +543,7 @@ class FedimintClient { ): FedimintResponse => { const request: MintReissueRequest = { notes }; - return await this.postWithId( + return await this.postWithFederationId( "/mint/reissue", request, federationId @@ -475,7 +567,7 @@ class FedimintClient { includeInvite, }; - return await this.postWithId( + return await this.postWithFederationId( "/mint/spend", request, federationId @@ -491,7 +583,7 @@ class FedimintClient { ): FedimintResponse => { const request: MintValidateRequest = { notes }; - return await this.postWithId( + return await this.postWithFederationId( "/mint/validate", request, federationId @@ -532,7 +624,7 @@ class FedimintClient { ): FedimintResponse => { const request: OnchainDepositAddressRequest = { timeout }; - return await this.postWithId( + return await this.postWithFederationId( "/wallet/deposit-address", request, federationId @@ -548,7 +640,7 @@ class FedimintClient { ): FedimintResponse => { const request: OnchainAwaitDepositRequest = { operationId }; - return await this.postWithId( + return await this.postWithFederationId( "/wallet/await-deposit", request, federationId @@ -565,7 +657,7 @@ class FedimintClient { ): FedimintResponse => { const request: OnchainWithdrawRequest = { address, amountSat }; - return await this.postWithId( + return await this.postWithFederationId( "/wallet/withdraw", request, federationId diff --git a/wrappers/fedimint-ts/test.ts b/wrappers/fedimint-ts/test.ts index d1b7c53..9ae021f 100644 --- a/wrappers/fedimint-ts/test.ts +++ b/wrappers/fedimint-ts/test.ts @@ -43,13 +43,18 @@ async function buildTestClient() { "15db8cb4f1ec8e484d73b889372bec94812580f929e8148b7437d359af422cd3" // Fedi Alpha Mutinynet ); - return await builder.build(); + const client = await builder.build(); + + await client.useDefaultGateway(); + + console.log("Default gateway id: ", client.getActiveGatewayId()); + + return client; } // Runs through all of the methods in the Fedimint Client async function main() { const fedimintClient = await buildTestClient(); - const keyPair = newKeyPair(); console.log("Generated key pair: ", keyPair); @@ -75,7 +80,7 @@ async function main() { process.env.INVITE_CODE || "fed11qgqrgvnhwden5te0v9k8q6rp9ekh2arfdeukuet595cr2ttpd3jhq6rzve6zuer9wchxvetyd938gcewvdhk6tcqqysptkuvknc7erjgf4em3zfh90kffqf9srujn6q53d6r056e4apze5cw27h75"; logMethod("/v2/admin/join"); - data = await fedimintClient.join(inviteCode); + data = await fedimintClient.join(inviteCode, true, true); logInputAndOutput({ inviteCode }, data); // `/v2/admin/list-operations` logMethod("/v2/admin/list-operations"); diff --git a/wrappers/fedimint-ts/types.ts b/wrappers/fedimint-ts/types.ts index 88ada07..dea6180 100644 --- a/wrappers/fedimint-ts/types.ts +++ b/wrappers/fedimint-ts/types.ts @@ -32,6 +32,11 @@ interface JoinRequest { useManualSecret: boolean; } +interface JoinResponse { + thisFederationId: string; + federationIds: string[]; +} + interface BackupRequest { metadata: { [key: string]: string }; } @@ -165,9 +170,33 @@ interface LnAwaitPayRequest { operationId: string; } +interface GatewayInfo { + api: string; + fees: GatewayFees; + gateway_id: string; + gateway_redeem_key: string; + lightning_alias: string; + mint_channel_id: number; + node_pub_key: string; + route_hints: any[]; // Adjust the type according to the actual structure of route hints + supports_private_payments: boolean; +} + +interface GatewayFees { + base_msat: number; + proportional_millionths: number; +} + +interface GatewayTTL { + nanos: number; + secs: number; +} + interface Gateway { - nodePubKey: string; - active: boolean; + federation_id: string; + info: GatewayInfo; + ttl: GatewayTTL; + vetted: boolean; } interface SwitchGatewayRequest { @@ -258,6 +287,7 @@ export type { DiscoverVersionRequest, DiscoverVersionResponse, JoinRequest, + JoinResponse, BackupRequest, ListOperationsRequest, OperationOutput,