From 78809a1a4741af4163d76deb2655a7cdb76a39af Mon Sep 17 00:00:00 2001 From: "kody.low" Date: Wed, 3 Apr 2024 17:25:01 -0700 Subject: [PATCH 1/4] feat: python updates continued --- wrappers/fedimint-py/AsyncFedimintClient.py | 287 ++++++++++++++------ wrappers/fedimint-py/models/common.py | 52 +++- wrappers/fedimint-py/models/lightning.py | 81 ++++++ wrappers/fedimint-py/models/ln.py | 44 --- wrappers/fedimint-py/models/mint.py | 94 +++---- wrappers/fedimint-py/models/onchain.py | 53 ++++ wrappers/fedimint-py/models/wallet.py | 28 -- wrappers/fedimint-ts/FedimintClient.ts | 53 ++-- wrappers/fedimint-ts/types.ts | 75 +++-- 9 files changed, 483 insertions(+), 284 deletions(-) create mode 100644 wrappers/fedimint-py/models/lightning.py delete mode 100644 wrappers/fedimint-py/models/ln.py create mode 100644 wrappers/fedimint-py/models/onchain.py delete mode 100644 wrappers/fedimint-py/models/wallet.py diff --git a/wrappers/fedimint-py/AsyncFedimintClient.py b/wrappers/fedimint-py/AsyncFedimintClient.py index 411d102..e20a245 100644 --- a/wrappers/fedimint-py/AsyncFedimintClient.py +++ b/wrappers/fedimint-py/AsyncFedimintClient.py @@ -1,46 +1,94 @@ +import logging +from typing import List import aiohttp + from models.common import ( + DiscoverVersionRequest, + DiscoverVersionResponse, InfoResponse, ListOperationsRequest, - OperationOutput, - BackupRequest, ) -from models.ln import ( - LnInvoiceRequest, - LnInvoiceResponse, - AwaitInvoiceRequest, - LnPayRequest, - LnPayResponse, - AwaitLnPayRequest, + +from models.lightning import ( Gateway, + LightningClaimPubkeReceivesRequest, + LightningCreateInvoiceRequest, + LightningCreateInvoiceResponse, + LightningAwaitInvoiceRequest, + LightningAwaitInvoiceResponse, + LightningPayRequest, + LightningPayResponse, + LightningAwaitPayRequest, + LightningAwaitPayResponse, + ListGatewaysResponse, SwitchGatewayRequest, ) -from models.wallet import DepositAddressRequest, AwaitDepositRequest, WithdrawRequest + +from models.onchain import ( + OnchainAwaitDepositRequest, + OnchainAwaitDepositResponse, + OnchainDepositAddressRequest, + OnchainDepositAddressResponse, + OnchainWithdrawRequest, + OnchainWithdrawResponse, +) + from models.mint import ( - ReissueRequest, - SpendRequest, - ValidateRequest, - SplitRequest, - CombineRequest, + MintReissueRequest, + MintReissueResponse, + MintSpendRequest, + MintSpendResponse, + MintValidateRequest, + MintValidateResponse, + MintSplitRequest, + MintSplitResponse, + MintCombineRequest, + MintCombineResponse, ) class AsyncFedimintClient: - def __init__(self, base_url: str, password: str, active_federation_id: str): + def __init__( + self, + base_url: str, + password: str, + active_federationId: str, + active_gatewayId: str = None, + ): self.base_url = f"{base_url}/fedimint/v2" self.password = password - self.active_federation_id = active_federation_id + self.active_federationId = active_federationId + self.active_gatewayId = active_gatewayId self.session = aiohttp.ClientSession() - self.ln = self.LN(self) - self.wallet = self.Wallet(self) + self.lightning = self.Lightning(self) + self.onchain = self.Onchain(self) self.mint = self.Mint(self) + logging.info( + "Initialized fedimint client, must set active gateway id after intitalization to use lightning module methods or manually pass in gateways" + ) - def get_active_federation_id(self): - return self.active_federation_id + def get_active_federationId(self): + return self.active_federationId - def set_active_federation_id(self, federation_id: str): - self.active_federation_id = federation_id + def set_active_federationId(self, federationId: str): + self.active_federationId = federationId + + def get_active_gatewayId(self): + return self.active_gatewayId + + def set_active_gatewayId(self, gatewayId: str): + self.active_gatewayId = gatewayId + + def use_default_gateway(self): + # hits list_gateways and sets active_gatewayId to the first gateway + try: + gateways = self.lightning.list_gateways() + logging.info("Gateways: ", gateways) + self.active_gateway_id = gateways[0].info.gatewayId + logging.info("Set active gateway id to: ", self.active_gatewayId) + except Exception as e: + logging.error("Error setting default gateway id: ", e) async def _handle_response(self, response): if response.status != 200: @@ -66,127 +114,210 @@ async def _post(self, endpoint: str, data=None): await self._handle_response(response) return await response.json() - async def _post_with_id(self, endpoint: str, data=None, federation_id: str = None): - if federation_id is None: - federation_id = self.get_active_federation_id() + async def _post_with_federation_id( + self, endpoint: str, data=None, federationId: str = None + ): + if federationId is None: + federationId = self.get_active_federation_id() if data is None: data = {} - data["federationId"] = federation_id + data["federationId"] = federationId return await self._post(endpoint, data) - async def info(self): - return await self._get("/admin/info") + async def _post_with_gateway_id_and_federation_id( + self, + endpoint: str, + data=None, + gatewayId: str = None, + federationId: str = None, + ): + + if gatewayId is None: + gatewayId = self.get_active_gateway_id() - async def backup(self, request: BackupRequest, federation_id: str = None): - return await self._post_with_id("/admin/backup", request, federation_id) + if federationId is None: + federationId = self.get_active_federation_id() + + if federationId is None or gatewayId is None: + raise Exception( + "Must set active gateway id and active federation id before calling this method" + ) + + if data is None: + data = {} + data["gatewayId"] = gatewayId + data["federationId"] = federationId + + return await self._post(endpoint, data) + + async def info(self) -> InfoResponse: + return await self._get("/admin/info") async def config(self): return await self._get("/admin/config") - async def discover_version(self): - return await self._get("/admin/discover-version") + # TODO: Unsupported method + # async def backup(self, request: BackupRequest, federationId: str = None): + # return await self._post_with_id("/admin/backup", request, federationId) + + async def discover_version(self, threshold: int) -> DiscoverVersionResponse: + request: DiscoverVersionRequest = {"threshold": threshold} + return await self._post("/admin/discover-version", request) async def federation_ids(self): return await self._get("/admin/federation-ids") async def list_operations(self, request: ListOperationsRequest): - return await self._post_with_id("/admin/list-operations", request) + return await self._post_with_federation_id("/admin/list-operations", request) async def join(self, invite_code: str, set_default: bool = False): return await self._post( "/admin/join", {"inviteCode": invite_code, "setDefault": set_default} ) - class LN: + class Lightning: def __init__(self, client): self.client = client async def create_invoice( - self, request: LnInvoiceRequest, federation_id: str = None - ): - return await self.client._post_with_id( - "/ln/invoice", request, federation_id + self, + amount_msat: int, + description: str, + expiry_time: int = None, + gateway_id: str = None, + federation_id: str = None, + ) -> LightningCreateInvoiceResponse: + request: LightningCreateInvoiceRequest = { + "amountMsat": amount_msat, + "description": description, + "expiryTime": expiry_time, + } + + return await self.client._post_with_gateway_id_and_federation_id( + "/ln/invoice", + data=request, + gateway_id=gateway_id, + federation_id=federation_id, ) - async def await_invoice( - self, request: AwaitInvoiceRequest, federation_id: str = None - ): - return await self.client._post_with_id( - "/ln/await-invoice", request, federation_id + async def create_invoice_for_pubkey_tweak( + self, + pubkey: str, + tweak: int, + amount_msat: int, + description: str, + expiry_time: int = None, + gateway_id: str = None, + federation_id: str = None, + ) -> LightningCreateInvoiceResponse: + request: LightningCreateInvoiceRequest = { + "pubkey": pubkey, + "tweak": tweak, + "amountMsat": amount_msat, + "description": description, + "expiryTime": expiry_time, + } + + return await self.client._post_with_gateway_id_and_federation_id( + "/ln/invoice-external-pubkey-tweaked", + data=request, + gateway_id=gateway_id, + federation_id=federation_id, ) - async def pay(self, request: LnPayRequest, federation_id: str = None): - return await self.client._post_with_id("/ln/pay", request, federation_id) + async def claim_pubkey_tweak_receives( + self, + private_key: str, + tweaks: List[int], + federation_id: str = None, + ) -> InfoResponse: + request: LightningClaimPubkeReceivesRequest = { + "privateKey": private_key, + "tweaks": tweaks, + } + + return await self.client._post_with_federation_id( + "/ln/claim-external-pubkey-tweaked", + data=request, + federation_id=federation_id, + ) - async def await_pay( - self, request: AwaitLnPayRequest, federation_id: str = None - ): - return await self.client._post_with_id( - "/ln/await-pay", request, federation_id + async def await_invoice( + self, request: LightningAwaitInvoiceRequest, federationId: str = None + ) -> InfoResponse: + return await self.client._post_with_gateway_id_and_federation_id( + "/ln/await-invoice", request, federationId ) - async def list_gateways(self): - return await self.client._get("/ln/list-gateways") + async def pay( + self, request: LightningPayRequest, federationId: str = None + ) -> LightningPayResponse: + return await self.client._post_with_gateway_id_and_federation_id( + "/ln/pay", request, federationId + ) - async def switch_gateway( - self, request: SwitchGatewayRequest, federation_id: str = None + async def await_pay( + self, request: LightningAwaitPayRequest, federationId: str = None ): - return await self.client._post_with_id( - "/ln/switch-gateway", request, federation_id + return await self.client._post_with_gateway_id_and_federation_id( + "/ln/await-pay", request, federationId ) - class Wallet: + async def list_gateways(self) -> List[Gateway]: + return await self.client._post_with_federation_id("/ln/list-gateways") + + class Onchain: def __init__(self, client): self.client = client async def create_deposit_address( - self, request: DepositAddressRequest, federation_id: str = None + self, request: OnchainDepositAddressRequest, federationId: str = None ): return self.client._post_with_id( - "/wallet/deposit-address", data=request, federation_id=federation_id + "/wallet/deposit-address", data=request, federationId=federationId ) async def await_deposit( - self, request: AwaitDepositRequest, federation_id: str = None + self, request: OnchainAwaitDepositRequest, federationId: str = None ): return await self.client._post_with_id( - "/wallet/await-deposit", request, federation_id + "/wallet/await-deposit", request, federationId ) - async def withdraw(self, request: WithdrawRequest, federation_id: str = None): + async def withdraw( + self, request: OnchainWithdrawRequest, federationId: str = None + ): return await self.client._post_with_id( - "/wallet/withdraw", request, federation_id + "/wallet/withdraw", request, federationId ) class Mint: def __init__(self, client): self.client = client - async def reissue(self, request: ReissueRequest, federation_id: str = None): + async def reissue(self, request: MintReissueRequest, federationId: str = None): return await self.client._post_with_id( - "/mint/reissue", request, federation_id + "/mint/reissue", request, federationId ) - async def spend(self, request: SpendRequest, federation_id: str = None): - return await self.client._post_with_id( - "/mint/spend", request, federation_id - ) + async def spend(self, request: MintSpendRequest, federationId: str = None): + return await self.client._post_with_id("/mint/spend", request, federationId) - async def validate(self, request: ValidateRequest, federation_id: str = None): + async def validate( + self, request: MintValidateRequest, federationId: str = None + ): return await self.client._post_with_id( - "/mint/validate", request, federation_id + "/mint/validate", request, federationId ) - async def split(self, request: SplitRequest, federation_id: str = None): - return await self.client._post_with_id( - "/mint/split", request, federation_id - ) + async def split(self, request: MintSplitRequest, federationId: str = None): + return await self.client._post_with_id("/mint/split", request, federationId) - async def combine(self, request: CombineRequest, federation_id: str = None): + async def combine(self, request: MintCombineRequest, federationId: str = None): return await self.client._post_with_id( - "/mint/combine", request, federation_id + "/mint/combine", request, federationId ) async def close(self): diff --git a/wrappers/fedimint-py/models/common.py b/wrappers/fedimint-py/models/common.py index 4eebd4a..71142e8 100644 --- a/wrappers/fedimint-py/models/common.py +++ b/wrappers/fedimint-py/models/common.py @@ -1,5 +1,5 @@ from pydantic import RootModel, BaseModel -from typing import Optional, Dict, Any +from typing import List, Optional, Dict, Any class Tiered(RootModel): @@ -10,13 +10,16 @@ class TieredSummary(BaseModel): tiered: Tiered -class InfoResponse(BaseModel): - federation_id: str +class FederationInfo(BaseModel): network: str meta: Dict[str, str] - total_amount_msat: int - total_num_notes: int - denominations_msat: TieredSummary + totalAmountMsat: int + totalNumNotes: int + denominationsMsat: TieredSummary + + +class InfoResponse(BaseModel): + __root__: Dict[str, FederationInfo] class BackupRequest(BaseModel): @@ -29,7 +32,38 @@ class ListOperationsRequest(BaseModel): class OperationOutput(BaseModel): id: str - creation_time: str - operation_kind: str - operation_meta: Any + creationTime: str + operationKind: str + operationMeta: Any + outcome: Optional[Any] + + +class DiscoverVersionRequest(BaseModel): + threshold: Optional[int] + + +# Returns a dictionary of federation_ids and their api versions +class DiscoverVersionResponse(BaseModel): + __root__: Dict[str, Any] + + +class JoinRequest(BaseModel): + inviteCode: str + useManualSecret: bool + + +class JoinResponse(BaseModel): + thisFederationId: str + federationIds: List[str] + + +class ListOperationsRequest(BaseModel): + limit: int + + +class OperationOutput(BaseModel): + id: str + creationTime: str + operationKind: str + operationMeta: Any outcome: Optional[Any] diff --git a/wrappers/fedimint-py/models/lightning.py b/wrappers/fedimint-py/models/lightning.py new file mode 100644 index 0000000..984aa98 --- /dev/null +++ b/wrappers/fedimint-py/models/lightning.py @@ -0,0 +1,81 @@ +from pydantic import BaseModel +from typing import Any, List, Optional + + +class LightningInvoiceRequest(BaseModel): + amountMsat: int + description: str + expiryTime: Optional[int] + + +class LightningInvoiceResponse(BaseModel): + operationId: str + invoice: str + + +class LightningInvoicePubkeyTweakedRequest(BaseModel): + amountMsat: int + description: str + externalPubkey: str + tweak: int + expiryTime: Optional[int] + + +class LightningInvoicePubkeyTweakedResponse(BaseModel): + operationId: str + invoice: str + + +class LightningClaimPubkeReceivesRequest(BaseModel): + privateKey: str + tweaks: List[int] + + +class LightningAwaitInvoiceRequest(BaseModel): + operationId: str + + +class LightningPayRequest(BaseModel): + paymentInfo: str + amountMsat: Optional[int] + lightningUrlComment: Optional[str] + + +class LightningPayResponse(BaseModel): + operationId: str + paymentType: str + contractId: str + fee: int + + +class LightningAwaitPayRequest(BaseModel): + operationId: str + + +class GatewayFees(BaseModel): + baseMsat: int + proportionalMillionths: int + + +class GatewayInfo(BaseModel): + api: str + fees: GatewayFees + gatewayId: str + gatewayRedeemKey: str + lightningAlias: str + mintChannelId: int + nodePubKey: str + routeHints: List[Any] + supportsPrivatePayments: bool + + +class GatewayTTL(BaseModel): + nanos: int + secs: int + + +class Gateway(BaseModel): + federationId: str + info: GatewayInfo + ttl: GatewayTTL + vetted: bool diff --git a/wrappers/fedimint-py/models/ln.py b/wrappers/fedimint-py/models/ln.py deleted file mode 100644 index 3d09006..0000000 --- a/wrappers/fedimint-py/models/ln.py +++ /dev/null @@ -1,44 +0,0 @@ -from pydantic import BaseModel -from typing import Optional - - -class LnInvoiceRequest(BaseModel): - amount_msat: int - description: str - expiry_time: Optional[int] - - -class LnInvoiceResponse(BaseModel): - operation_id: str - invoice: str - - -class AwaitInvoiceRequest(BaseModel): - operation_id: str - - -class LnPayRequest(BaseModel): - payment_info: str - amount_msat: Optional[int] - finish_in_background: bool - lnurl_comment: Optional[str] - - -class LnPayResponse(BaseModel): - operation_id: str - payment_type: str - contract_id: str - fee: int - - -class AwaitLnPayRequest(BaseModel): - operation_id: str - - -class Gateway(BaseModel): - node_pub_key: str - active: bool - - -class SwitchGatewayRequest(BaseModel): - gateway_id: str diff --git a/wrappers/fedimint-py/models/mint.py b/wrappers/fedimint-py/models/mint.py index bde3ce2..674dc76 100644 --- a/wrappers/fedimint-py/models/mint.py +++ b/wrappers/fedimint-py/models/mint.py @@ -1,90 +1,72 @@ -from pydantic import BaseModel, RootModel -from typing import Optional, Dict, List, Union +from pydantic import BaseModel +from typing import Dict, List -class FederationIdPrefix(RootModel): - root: List[int] +class MintDecodeNotesRequest(BaseModel): + notes: str -class Fp(RootModel): - root: List[int] +class Note(BaseModel): + signature: str + spendKey: str -class Choice(RootModel): - root: int +class NotesJson(BaseModel): + federation_id_prefix: str + notes: Dict[str, List[Note]] -class G1Affine(BaseModel): - x: Fp - y: Fp - infinity: Choice +class MintDecodeNotesResponse(BaseModel): + notesJson: NotesJson -class Signature(RootModel): - root: G1Affine +class MintEncodeNotesRequest(BaseModel): + notesJsonStr: str -class KeyPair(RootModel): - root: List[int] +class MintEncodeNotesResponse(BaseModel): + notes: str -class SpendableNote(BaseModel): - signature: Signature - spend_key: KeyPair +class MintReissueRequest(BaseModel): + notes: str -class TieredMulti(RootModel): - root: Dict[int, List[SpendableNote]] +class MintReissueResponse(BaseModel): + amountMsat: int -class OOBNotesData(BaseModel): - Notes: Optional[TieredMulti] - FederationIdPrefix: Optional[FederationIdPrefix] - Default: Optional[Dict[str, Union[int, List[int]]]] - - -class OOBNotes(RootModel): - root: List[OOBNotesData] - - -class ReissueRequest(BaseModel): - notes: OOBNotes - - -class ReissueResponse(BaseModel): - amount_msat: int - - -class SpendRequest(BaseModel): - amount_msat: int - allow_overpay: bool +class MintSpendRequest(BaseModel): + amountMsat: int + allowOverpay: bool timeout: int + includeInvite: bool -class SpendResponse(BaseModel): +class MintSpendResponse(BaseModel): operation: str - notes: OOBNotes + notes: str -class ValidateRequest(BaseModel): - notes: OOBNotes +class MintValidateRequest(BaseModel): + notes: str -class ValidateResponse(BaseModel): - amount_msat: int +class MintValidateResponse(BaseModel): + amountMsat: int -class SplitRequest(BaseModel): - notes: OOBNotes +class MintSplitRequest(BaseModel): + notes: str -class SplitResponse(BaseModel): - notes: Dict[int, OOBNotes] +class MintSplitResponse(BaseModel): + notes: Dict[int, str] -class CombineRequest(BaseModel): - notes: List[OOBNotes] +class MintCombineRequest(BaseModel): + notesVec: List[str] -class CombineResponse(BaseModel): - notes: OOBNotes +class MintCombineResponse(BaseModel): + notes: str diff --git a/wrappers/fedimint-py/models/onchain.py b/wrappers/fedimint-py/models/onchain.py new file mode 100644 index 0000000..8481e78 --- /dev/null +++ b/wrappers/fedimint-py/models/onchain.py @@ -0,0 +1,53 @@ +from typing import Dict, List, Union +from pydantic import BaseModel + + +class OnchainDepositAddressRequest(BaseModel): + timeout: int + + +class OnchainDepositAddressResponse(BaseModel): + operation_id: str + address: str + + +class OnchainAwaitDepositRequest(BaseModel): + operation_id: str + + +class BTCInput(BaseModel): + previous_output: str + script_sig: str + sequence: int + witness: List[str] + + +class BTCOutput(BaseModel): + value: int + script_pubkey: str + + +class BTCTransaction(BaseModel): + version: int + lock_time: int + input: List[BTCInput] + output: List[BTCOutput] + + +class AwaitDepositResponseConfirmed(BaseModel): + btc_transaction: BTCTransaction + out_idx: int + + +class OnchainAwaitDepositResponse(BaseModel): + status: Union[Dict[str, AwaitDepositResponseConfirmed], Dict[str, str]] + + +class OnchainWithdrawRequest(BaseModel): + address: str + amount_sat: int | "all" + + +class OnchainWithdrawResponse(BaseModel): + txid: str + fees_sat: int diff --git a/wrappers/fedimint-py/models/wallet.py b/wrappers/fedimint-py/models/wallet.py deleted file mode 100644 index 83879cb..0000000 --- a/wrappers/fedimint-py/models/wallet.py +++ /dev/null @@ -1,28 +0,0 @@ -from pydantic import BaseModel - - -class DepositAddressRequest(BaseModel): - timeout: int - - -class DepositAddressResponse(BaseModel): - operation_id: str - address: str - - -class AwaitDepositRequest(BaseModel): - operation_id: str - - -class AwaitDepositResponse(BaseModel): - status: str - - -class WithdrawRequest(BaseModel): - address: str - amount_msat: str - - -class WithdrawResponse(BaseModel): - txid: str - fees_sat: int diff --git a/wrappers/fedimint-ts/FedimintClient.ts b/wrappers/fedimint-ts/FedimintClient.ts index f84ce4b..da35cd3 100644 --- a/wrappers/fedimint-ts/FedimintClient.ts +++ b/wrappers/fedimint-ts/FedimintClient.ts @@ -7,15 +7,12 @@ import type { DiscoverVersionRequest, DiscoverVersionResponse, JoinRequest, - LnInvoiceExternalPubkeyTweakedRequest, - LnInvoiceExternalPubkeyTweakedResponse, - LnClaimPubkeyReceiveTweakedRequest, - LnAwaitInvoiceRequest, + LightningInvoiceExternalPubkeyTweakedRequest, + LightningClaimPubkeyReceiveTweakedRequest as LightningClaimPubkeyTweakReceivesRequest, + LightningAwaitInvoiceRequest, Gateway, - LnInvoiceRequest, - LnInvoiceResponse, - LnPayRequest, - LnPayResponse, + LightningInvoiceRequest, + LightningPayRequest, MintCombineRequest, MintCombineResponse, MintReissueRequest, @@ -38,6 +35,9 @@ import type { MintEncodeNotesResponse, MintDecodeNotesResponse, JoinResponse, + LightningInvoiceResponse, + LightningInvoiceExternalPubkeyTweakedResponse, + LightningPayResponse, } from "./types"; /** @@ -217,8 +217,6 @@ class FedimintClient { * @param body - The body of the request. */ private async post(endpoint: string, body: any): FedimintResponse { - console.log("body: ", body); - console.log("endpoint: ", this.baseUrl + endpoint); const res = await fetch(`${this.baseUrl}${endpoint}`, { method: "POST", headers: { @@ -228,8 +226,6 @@ class FedimintClient { body: JSON.stringify(body), }); - console.log("res: ", res); - if (!res.ok) { throw new Error( `POST request failed. Status: ${res.status}, Body: ${await res.text()}` @@ -337,7 +333,6 @@ class FedimintClient { threshold?: number ): FedimintResponse { const request: DiscoverVersionRequest = threshold ? { threshold } : {}; - console.log("request", request); return this.post( "/admin/discover-version", @@ -410,10 +405,14 @@ class FedimintClient { expiryTime?: number, gatewayId?: string, federationId?: string - ): FedimintResponse => { - const request: LnInvoiceRequest = { amountMsat, description, expiryTime }; + ): FedimintResponse => { + const request: LightningInvoiceRequest = { + amountMsat, + description, + expiryTime, + }; - return await this.postWithGatewayIdAndFederationId( + return await this.postWithGatewayIdAndFederationId( "/ln/invoice", request, gatewayId, @@ -441,8 +440,8 @@ class FedimintClient { expiryTime?: number, gatewayId?: string, federationId?: string - ): FedimintResponse => { - const request: LnInvoiceExternalPubkeyTweakedRequest = { + ): FedimintResponse => { + const request: LightningInvoiceExternalPubkeyTweakedRequest = { externalPubkey: pubkey, tweak, amountMsat, @@ -450,7 +449,7 @@ class FedimintClient { expiryTime, }; - return await this.postWithGatewayIdAndFederationId( + return await this.postWithGatewayIdAndFederationId( "/ln/invoice-external-pubkey-tweaked", request, gatewayId, @@ -470,7 +469,7 @@ class FedimintClient { tweaks: number[], federationId: string ): FedimintResponse => { - const request: LnClaimPubkeyReceiveTweakedRequest = { + const request: LightningClaimPubkeyTweakReceivesRequest = { privateKey, tweaks, }; @@ -491,7 +490,7 @@ class FedimintClient { operationId: string, federationId?: string ): FedimintResponse => { - const request: LnAwaitInvoiceRequest = { operationId }; + const request: LightningAwaitInvoiceRequest = { operationId }; return await this.postWithFederationId( "/ln/await-invoice", @@ -501,22 +500,22 @@ class FedimintClient { }, /** - * Pays a lightning invoice or lnurl via a gateway + * Pays a lightning invoice or Lightningurl via a gateway */ pay: async ( paymentInfo: string, amountMsat?: number, - lnurlComment?: string, + LightningurlComment?: string, gatewayId?: string, federationId?: string - ): FedimintResponse => { - const request: LnPayRequest = { + ): FedimintResponse => { + const request: LightningPayRequest = { paymentInfo, amountMsat, - lnurlComment, + LightningurlComment, }; - return await this.postWithGatewayIdAndFederationId( + return await this.postWithGatewayIdAndFederationId( "/ln/pay", request, gatewayId, diff --git a/wrappers/fedimint-ts/types.ts b/wrappers/fedimint-ts/types.ts index 0c24d92..09cc701 100644 --- a/wrappers/fedimint-ts/types.ts +++ b/wrappers/fedimint-ts/types.ts @@ -105,30 +105,30 @@ interface OnchainWithdrawResponse { feesSat: number; } -interface LnInvoiceRequest { +interface LightningInvoiceRequest { amountMsat: number; description: string; expiryTime?: number; } -interface LnInvoiceResponse { +interface LightningInvoiceResponse { operationId: string; invoice: string; } -interface LnInvoiceExternalPubkeyRequest { +interface LightningInvoiceExternalPubkeyRequest { amountMsat: number; description: string; externalPubkey: string; expiryTime?: number; } -interface LnInvoiceExternalPubkeyResponse { +interface LightningInvoiceExternalPubkeyResponse { operationId: string; invoice: string; } -interface LnInvoiceExternalPubkeyTweakedRequest { +interface LightningInvoiceExternalPubkeyTweakedRequest { amountMsat: number; description: string; externalPubkey: string; @@ -136,38 +136,38 @@ interface LnInvoiceExternalPubkeyTweakedRequest { expiryTime?: number; } -interface LnInvoiceExternalPubkeyTweakedResponse { +interface LightningInvoiceExternalPubkeyTweakedResponse { operationId: string; invoice: string; } -interface LnClaimPubkeyReceiveRequest { +interface LightningClaimPubkeyReceiveRequest { privateKey: string; } -interface LnClaimPubkeyReceiveTweakedRequest { +interface LightningClaimPubkeyReceiveTweakedRequest { privateKey: string; tweaks: number[]; } -interface LnAwaitInvoiceRequest { +interface LightningAwaitInvoiceRequest { operationId: string; } -interface LnPayRequest { +interface LightningPayRequest { paymentInfo: string; amountMsat?: number; - lnurlComment?: string; + LightningurlComment?: string; } -interface LnPayResponse { +interface LightningPayResponse { operationId: string; paymentType: string; contractId: string; fee: number; } -interface LnAwaitPayRequest { +interface LightningAwaitPayRequest { operationId: string; } @@ -184,8 +184,8 @@ interface GatewayInfo { } interface GatewayFees { - base_msat: number; - proportional_millionths: number; + baseMsat: number; + proportionalMillionths: number; } interface GatewayTTL { @@ -200,16 +200,6 @@ interface Gateway { vetted: boolean; } -interface SwitchGatewayRequest { - gatewayId: string; -} - -type FederationIdPrefix = string; - -interface TieredMulti { - [amount: number]: T[]; -} - interface MintDecodeNotesRequest { notes: string; } @@ -270,13 +260,15 @@ interface MintCombineResponse { notes: string; } +interface Note { + signature: string; + spend_key: string; +} + interface NotesJson { federation_id_prefix: string; notes: { - [denomination: string]: Array<{ - signature: string; - spend_key: string; - }>; + [denomination: string]: Note[]; }; } @@ -298,21 +290,20 @@ export type { OnchainAwaitDepositResponse, OnchainWithdrawRequest, OnchainWithdrawResponse, - LnInvoiceRequest, - LnInvoiceResponse, - LnInvoiceExternalPubkeyRequest, - LnInvoiceExternalPubkeyResponse, - LnInvoiceExternalPubkeyTweakedRequest, - LnInvoiceExternalPubkeyTweakedResponse, - LnClaimPubkeyReceiveRequest, - LnClaimPubkeyReceiveTweakedRequest, - LnAwaitInvoiceRequest, - LnPayRequest, - LnPayResponse, - LnAwaitPayRequest, + LightningInvoiceRequest, + LightningInvoiceResponse, + LightningInvoiceExternalPubkeyRequest, + LightningInvoiceExternalPubkeyResponse, + LightningInvoiceExternalPubkeyTweakedRequest, + LightningInvoiceExternalPubkeyTweakedResponse, + LightningClaimPubkeyReceiveRequest, + LightningClaimPubkeyReceiveTweakedRequest, + LightningAwaitInvoiceRequest, + LightningPayRequest, + LightningPayResponse, + LightningAwaitPayRequest, Gateway, NotesJson, - SwitchGatewayRequest, MintDecodeNotesRequest, MintDecodeNotesResponse, MintEncodeNotesRequest, From 4aa78a4a380443ff9f7141145d3f04b240d5d829 Mon Sep 17 00:00:00 2001 From: "kody.low" Date: Wed, 3 Apr 2024 18:40:01 -0700 Subject: [PATCH 2/4] fix: python updates --- wrappers/fedimint-py/AsyncFedimintClient.py | 131 +++++++++++++------- wrappers/fedimint-py/models/lightning.py | 8 +- 2 files changed, 93 insertions(+), 46 deletions(-) diff --git a/wrappers/fedimint-py/AsyncFedimintClient.py b/wrappers/fedimint-py/AsyncFedimintClient.py index e20a245..c85e543 100644 --- a/wrappers/fedimint-py/AsyncFedimintClient.py +++ b/wrappers/fedimint-py/AsyncFedimintClient.py @@ -1,3 +1,4 @@ +import json import logging from typing import List import aiohttp @@ -12,16 +13,14 @@ from models.lightning import ( Gateway, LightningClaimPubkeReceivesRequest, + LightningAwaitInvoiceRequest, LightningCreateInvoiceRequest, LightningCreateInvoiceResponse, - LightningAwaitInvoiceRequest, - LightningAwaitInvoiceResponse, + LightningInvoiceForPubkeyTweakRequest, + LightningInvoiceForPubkeyTweakResponse, LightningPayRequest, LightningPayResponse, LightningAwaitPayRequest, - LightningAwaitPayResponse, - ListGatewaysResponse, - SwitchGatewayRequest, ) from models.onchain import ( @@ -34,6 +33,10 @@ ) from models.mint import ( + MintDecodeNotesRequest, + MintDecodeNotesResponse, + MintEncodeNotesRequest, + MintEncodeNotesResponse, MintReissueRequest, MintReissueResponse, MintSpendRequest, @@ -41,9 +44,8 @@ MintValidateRequest, MintValidateResponse, MintSplitRequest, - MintSplitResponse, MintCombineRequest, - MintCombineResponse, + NotesJson, ) @@ -55,7 +57,7 @@ def __init__( active_federationId: str, active_gatewayId: str = None, ): - self.base_url = f"{base_url}/fedimint/v2" + self.base_url = f"{base_url}/v2" self.password = password self.active_federationId = active_federationId self.active_gatewayId = active_gatewayId @@ -211,8 +213,8 @@ async def create_invoice_for_pubkey_tweak( expiry_time: int = None, gateway_id: str = None, federation_id: str = None, - ) -> LightningCreateInvoiceResponse: - request: LightningCreateInvoiceRequest = { + ) -> LightningInvoiceForPubkeyTweakResponse: + request: LightningInvoiceForPubkeyTweakRequest = { "pubkey": pubkey, "tweak": tweak, "amountMsat": amount_msat, @@ -268,57 +270,102 @@ async def await_pay( async def list_gateways(self) -> List[Gateway]: return await self.client._post_with_federation_id("/ln/list-gateways") - class Onchain: + class Mint: def __init__(self, client): self.client = client - async def create_deposit_address( - self, request: OnchainDepositAddressRequest, federationId: str = None - ): - return self.client._post_with_id( - "/wallet/deposit-address", data=request, federationId=federationId + async def decode_notes( + self, notes: str, federationId: str = None + ) -> MintDecodeNotesResponse: + request = MintDecodeNotesRequest({"notes": notes}) + return await self.client._post_with_federation_id( + "/mint/decode-notes", request, federationId ) - async def await_deposit( - self, request: OnchainAwaitDepositRequest, federationId: str = None - ): - return await self.client._post_with_id( - "/wallet/await-deposit", request, federationId - ) + async def encode_notes( + self, notes_json: NotesJson, federationId: str = None + ) -> MintEncodeNotesResponse: + request = MintEncodeNotesRequest({"notesJsonStr": json.dumps(notes_json)}) - async def withdraw( - self, request: OnchainWithdrawRequest, federationId: str = None - ): - return await self.client._post_with_id( - "/wallet/withdraw", request, federationId + return await self.client._post_with_federation_id( + "/mint/encode-notes", request, federationId ) - class Mint: - def __init__(self, client): - self.client = client - - async def reissue(self, request: MintReissueRequest, federationId: str = None): - return await self.client._post_with_id( + async def reissue( + self, notes: str, federationId: str = None + ) -> MintReissueResponse: + request = MintReissueRequest({"notes": notes}) + return await self.client._post_with_federation_id( "/mint/reissue", request, federationId ) - async def spend(self, request: MintSpendRequest, federationId: str = None): - return await self.client._post_with_id("/mint/spend", request, federationId) + async def spend( + self, + amount_msat: int, + allow_overpay: bool, + timeout: int, + include_invite: bool, + federationId: str = None, + ) -> MintSpendResponse: + request = MintSpendRequest( + { + "amountMsat": amount_msat, + "allowOverpay": allow_overpay, + "timeout": timeout, + "includeInvite": include_invite, + } + ) + return await self.client._post_with_federation_id( + "/mint/spend", request, federationId + ) async def validate( - self, request: MintValidateRequest, federationId: str = None - ): - return await self.client._post_with_id( + self, notes: str, federationId: str = None + ) -> MintValidateResponse: + request = MintValidateRequest({"notes": notes}) + return await self.client._post_with_federation_id( "/mint/validate", request, federationId ) - async def split(self, request: MintSplitRequest, federationId: str = None): - return await self.client._post_with_id("/mint/split", request, federationId) + async def split(self, notes: str, federationId: str = None): + request = MintSplitRequest({"notes": notes}) + return await self.client._post_with_federation_id( + "/mint/split", request, federationId + ) - async def combine(self, request: MintCombineRequest, federationId: str = None): - return await self.client._post_with_id( + async def combine(self, notes_vec: List[str], federationId: str = None): + request = MintCombineRequest({"notesVec": notes_vec}) + return await self.client._post_with_federation_id( "/mint/combine", request, federationId ) + class Onchain: + def __init__(self, client): + self.client = client + + async def create_deposit_address(self, timeout: int, federationId: str = None): + request = OnchainDepositAddressRequest({"timeout": timeout}) + return await self.client._post_with_federation_id( + "/wallet/deposit-address", request, federationId + ) + + async def await_deposit( + self, operation_id: str, federationId: str = None + ) -> OnchainAwaitDepositResponse: + request = OnchainAwaitDepositRequest({"operationId": operation_id}) + return await self.client._post_with_federation_id( + "/wallet/await-deposit", request, federationId + ) + + async def withdraw( + self, address: str, amount_sat: int | "all", federationId: str = None + ) -> OnchainWithdrawResponse: + request = OnchainWithdrawRequest( + {"address": address, "amountSat": amount_sat} + ) + return await self.client._post_with_federation_id( + "/wallet/withdraw", request, federationId + ) + async def close(self): await self.session.close() diff --git a/wrappers/fedimint-py/models/lightning.py b/wrappers/fedimint-py/models/lightning.py index 984aa98..b291d52 100644 --- a/wrappers/fedimint-py/models/lightning.py +++ b/wrappers/fedimint-py/models/lightning.py @@ -2,18 +2,18 @@ from typing import Any, List, Optional -class LightningInvoiceRequest(BaseModel): +class LightningCreateInvoiceRequest(BaseModel): amountMsat: int description: str expiryTime: Optional[int] -class LightningInvoiceResponse(BaseModel): +class LightningCreateInvoiceResponse(BaseModel): operationId: str invoice: str -class LightningInvoicePubkeyTweakedRequest(BaseModel): +class LightningInvoiceForPubkeyTweakRequest(BaseModel): amountMsat: int description: str externalPubkey: str @@ -21,7 +21,7 @@ class LightningInvoicePubkeyTweakedRequest(BaseModel): expiryTime: Optional[int] -class LightningInvoicePubkeyTweakedResponse(BaseModel): +class LightningInvoiceForPubkeyTweakResponse(BaseModel): operationId: str invoice: str From 05b1d8dc08b0b030f04feafd68bf0ad8ad2bee97 Mon Sep 17 00:00:00 2001 From: "kody.low" Date: Wed, 3 Apr 2024 18:52:40 -0700 Subject: [PATCH 3/4] fix: python tests --- wrappers/fedimint-py/FedimintClient.py | 353 ++++++++++++++++++------- wrappers/fedimint-py/test.py | 143 ++++++++++ wrappers/fedimint-py/test_async.py | 143 ++++++++++ 3 files changed, 539 insertions(+), 100 deletions(-) create mode 100644 wrappers/fedimint-py/test.py create mode 100644 wrappers/fedimint-py/test_async.py diff --git a/wrappers/fedimint-py/FedimintClient.py b/wrappers/fedimint-py/FedimintClient.py index 61b605e..fe45bf6 100644 --- a/wrappers/fedimint-py/FedimintClient.py +++ b/wrappers/fedimint-py/FedimintClient.py @@ -1,48 +1,97 @@ +import json +import logging import requests + from models.common import ( + DiscoverVersionRequest, + DiscoverVersionResponse, InfoResponse, ListOperationsRequest, - OperationOutput, - BackupRequest, ) -from models.ln import ( - LnInvoiceRequest, - LnInvoiceResponse, - AwaitInvoiceRequest, - LnPayRequest, - LnPayResponse, - AwaitLnPayRequest, + +from models.lightning import ( Gateway, - SwitchGatewayRequest, + LightningClaimPubkeReceivesRequest, + LightningAwaitInvoiceRequest, + LightningCreateInvoiceRequest, + LightningCreateInvoiceResponse, + LightningInvoiceForPubkeyTweakRequest, + LightningInvoiceForPubkeyTweakResponse, + LightningPayRequest, + LightningPayResponse, + LightningAwaitPayRequest, ) -from models.wallet import DepositAddressRequest, AwaitDepositRequest, WithdrawRequest + +from models.onchain import ( + OnchainAwaitDepositRequest, + OnchainAwaitDepositResponse, + OnchainDepositAddressRequest, + OnchainDepositAddressResponse, + OnchainWithdrawRequest, + OnchainWithdrawResponse, +) + from models.mint import ( - ReissueRequest, - SpendRequest, - ValidateRequest, - SplitRequest, - CombineRequest, + MintDecodeNotesRequest, + MintDecodeNotesResponse, + MintEncodeNotesRequest, + MintEncodeNotesResponse, + MintReissueRequest, + MintReissueResponse, + MintSpendRequest, + MintSpendResponse, + MintValidateRequest, + MintValidateResponse, + MintSplitRequest, + MintCombineRequest, + NotesJson, ) class FedimintClient: - def __init__(self, base_url: str, password: str, active_federation_id: str): - self.base_url = f"{base_url}/fedimint/v2" + def __init__( + self, + base_url: str, + password: str, + active_federationId: str, + active_gatewayId: str = None, + ): + self.base_url = f"{base_url}/v2" self.password = password - self.active_federation_id = active_federation_id + self.active_federationId = active_federationId + self.active_gatewayId = active_gatewayId - self.ln = self.LN(self) - self.wallet = self.Wallet(self) + self.lightning = self.Lightning(self) + self.onchain = self.Onchain(self) self.mint = self.Mint(self) + logging.info( + "Initialized fedimint client, must set active gateway id after initialization to use lightning module methods or manually pass in gateways" + ) + + def get_active_federationId(self): + return self.active_federationId + + def set_active_federationId(self, federationId: str): + self.active_federationId = federationId - def get_active_federation_id(self): - return self.active_federation_id + def get_active_gatewayId(self): + return self.active_gatewayId - def set_active_federation_id(self, federation_id: str): - self.active_federation_id = federation_id + def set_active_gatewayId(self, gatewayId: str): + self.active_gatewayId = gatewayId + + def use_default_gateway(self): + # hits list_gateways and sets active_gatewayId to the first gateway + try: + gateways = self.lightning.list_gateways() + logging.info("Gateways: ", gateways) + self.active_gateway_id = gateways[0].info.gatewayId + logging.info("Set active gateway id to: ", self.active_gatewayId) + except Exception as e: + logging.error("Error setting default gateway id: ", e) def _handle_response(self, response): - if not response.ok: + if response.status_code != 200: raise Exception( f"HTTP error! status: {response.status_code}, Body: {response.text}" ) @@ -64,129 +113,233 @@ def _post(self, endpoint: str, data=None): self._handle_response(response) return response.json() - def _post_with_id(self, endpoint: str, data=None, federation_id: str = None): - if federation_id is None: - federation_id = self.get_active_federation_id() + def _post_with_federation_id( + self, endpoint: str, data=None, federationId: str = None + ): + if federationId is None: + federationId = self.get_active_federationId() if data is None: data = {} - data["federationId"] = federation_id + data["federationId"] = federationId return self._post(endpoint, data) - def info(self): - return self._get("/admin/info") + def _post_with_gateway_id_and_federation_id( + self, + endpoint: str, + data=None, + gatewayId: str = None, + federationId: str = None, + ): + + if gatewayId is None: + gatewayId = self.get_active_gatewayId() + + if federationId is None: + federationId = self.get_active_federationId() + + if federationId is None or gatewayId is None: + raise Exception( + "Must set active gateway id and active federation id before calling this method" + ) + + if data is None: + data = {} + data["gatewayId"] = gatewayId + data["federationId"] = federationId + + return self._post(endpoint, data) - def backup(self, request: BackupRequest, federation_id: str = None): - return self._post_with_id("/admin/backup", request, federation_id) + def info(self) -> InfoResponse: + return self._get("/admin/info") def config(self): - return self.get("/admin/config") + return self._get("/admin/config") - def discover_version(self): - return self._get("/admin/discover-version") + def discover_version(self, threshold: int) -> DiscoverVersionResponse: + request: DiscoverVersionRequest = {"threshold": threshold} + return self._post("/admin/discover-version", request) def federation_ids(self): return self._get("/admin/federation-ids") def list_operations(self, request: ListOperationsRequest): - return self._fetch_with_auth("/admin/list-operations", "POST", data=request) + return self._post_with_federation_id("/admin/list-operations", request) def join(self, invite_code: str, set_default: bool = False): return self._post( "/admin/join", {"inviteCode": invite_code, "setDefault": set_default} ) - # def module(self, name: str): - # return self._fetch_with_auth(f'/admin/module', 'POST') - - # def restore(self, request: RestoreRequest): - # return self._fetch_with_auth('/admin/restore', 'POST', data=request) - - class LN: + class Lightning: def __init__(self, client): self.client = client - def create_invoice(self, request: LnInvoiceRequest, federation_id: str = None): - return self.client._post_with_id( - "/ln/invoice", data=request, federation_id=federation_id + def create_invoice( + self, + amount_msat: int, + description: str, + expiry_time: int = None, + gateway_id: str = None, + federation_id: str = None, + ) -> LightningCreateInvoiceResponse: + request: LightningCreateInvoiceRequest = { + "amountMsat": amount_msat, + "description": description, + "expiryTime": expiry_time, + } + + return self.client._post_with_gateway_id_and_federation_id( + "/ln/invoice", + data=request, + gateway_id=gateway_id, + federation_id=federation_id, ) - def await_invoice( - self, request: AwaitInvoiceRequest, federation_id: str = None - ): - return self.client._post_with_id( - "/ln/await-invoice", data=request, federation_id=federation_id + def create_invoice_for_pubkey_tweak( + self, + pubkey: str, + tweak: int, + amount_msat: int, + description: str, + expiry_time: int = None, + gateway_id: str = None, + federation_id: str = None, + ) -> LightningInvoiceForPubkeyTweakResponse: + request: LightningInvoiceForPubkeyTweakRequest = { + "pubkey": pubkey, + "tweak": tweak, + "amountMsat": amount_msat, + "description": description, + "expiryTime": expiry_time, + } + + return self.client._post_with_gateway_id_and_federation_id( + "/ln/invoice-external-pubkey-tweaked", + data=request, + gateway_id=gateway_id, + federation_id=federation_id, ) - def pay(self, request: LnPayRequest, federation_id: str = None): - return self.client._post_with_id( - "/ln/pay", data=request, federation_id=federation_id + def claim_pubkey_tweak_receives( + self, + private_key: str, + tweaks: List[int], + federation_id: str = None, + ) -> InfoResponse: + request: LightningClaimPubkeReceivesRequest = { + "privateKey": private_key, + "tweaks": tweaks, + } + + return self.client._post_with_federation_id( + "/ln/claim-external-pubkey-tweaked", + data=request, + federation_id=federation_id, ) - def await_pay(self, request: AwaitLnPayRequest, federation_id: str = None): - return self.client._post_with_id( - "/ln/await-pay", data=request, federation_id=federation_id + def pay( + self, request: LightningPayRequest, federationId: str = None + ) -> LightningPayResponse: + return self.client._post_with_gateway_id_and_federation_id( + "/ln/pay", request, federationId ) - def list_gateways(self): - return self.client._get("/ln/list-gateways") - - def switch_gateway( - self, request: SwitchGatewayRequest, federation_id: str = None - ): - return self.client._post_with_id( - "/ln/switch-gateway", data=request, federation_id=federation_id - ) + def list_gateways(self) -> List[Gateway]: + return self.client._post_with_federation_id("/ln/list-gateways") - class Wallet: + class Mint: def __init__(self, client): self.client = client - def create_deposit_address( - self, request: DepositAddressRequest, federation_id: str = None - ): - return self.client._post_with_id( - "/wallet/deposit-address", data=request, federation_id=federation_id + def decode_notes( + self, notes: str, federationId: str = None + ) -> MintDecodeNotesResponse: + request = MintDecodeNotesRequest({"notes": notes}) + return self.client._post_with_federation_id( + "/mint/decode-notes", request, federationId ) - def await_deposit( - self, request: AwaitDepositRequest, federation_id: str = None - ): - return self.client._post_with_id( - "/wallet/await-deposit", data=request, federation_id=federation_id + def encode_notes( + self, notes_json: NotesJson, federationId: str = None + ) -> MintEncodeNotesResponse: + request = MintEncodeNotesRequest({"notesJsonStr": json.dumps(notes_json)}) + + return self.client._post_with_federation_id( + "/mint/encode-notes", request, federationId ) - def withdraw(self, request: WithdrawRequest, federation_id: str = None): - return self.client._post_with_id( - "/wallet/withdraw", data=request, federation_id=federation_id + def reissue(self, notes: str, federationId: str = None) -> MintReissueResponse: + request = MintReissueRequest({"notes": notes}) + return self.client._post_with_federation_id( + "/mint/reissue", request, federationId ) - class Mint: - def __init__(self, client): - self.client = client + def spend( + self, + amount_msat: int, + allow_overpay: bool, + timeout: int, + include_invite: bool, + federationId: str = None, + ) -> MintSpendResponse: + request = MintSpendRequest( + { + "amountMsat": amount_msat, + "allowOverpay": allow_overpay, + "timeout": timeout, + "includeInvite": include_invite, + } + ) + return self.client._post_with_federation_id( + "/mint/spend", request, federationId + ) + + def validate( + self, notes: str, federationId: str = None + ) -> MintValidateResponse: + request = MintValidateRequest({"notes": notes}) + return self.client._post_with_federation_id( + "/mint/validate", request, federationId + ) - def reissue(self, request: ReissueRequest, federation_id: str = None): - return self.client._post_with_id( - "/mint/reissue", data=request, federation_id=federation_id + def split(self, notes: str, federationId: str = None): + request = MintSplitRequest({"notes": notes}) + return self.client._post_with_federation_id( + "/mint/split", request, federationId ) - def spend(self, request: SpendRequest, federation_id: str = None): - return self.client._post_with_id( - "/mint/spend", data=request, federation_id=federation_id + def combine(self, notes_vec: List[str], federationId: str = None): + request = MintCombineRequest({"notesVec": notes_vec}) + return self.client._post_with_federation_id( + "/mint/combine", request, federationId ) - def validate(self, request: ValidateRequest, federation_id: str = None): - return self.client._post_with_id( - "/mint/validate", data=request, federation_id=federation_id + class Onchain: + def __init__(self, client): + self.client = client + + def create_deposit_address(self, timeout: int, federationId: str = None): + request = OnchainDepositAddressRequest({"timeout": timeout}) + return self.client._post_with_federation_id( + "/wallet/deposit-address", request, federationId ) - def split(self, request: SplitRequest, federation_id: str = None): - return self.client._post_with_id( - "/mint/split", data=request, federation_id=federation_id + def await_deposit( + self, operation_id: str, federationId: str = None + ) -> OnchainAwaitDepositResponse: + request = OnchainAwaitDepositRequest({"operationId": operation_id}) + return self.client._post_with_federation_id( + "/wallet/await-deposit", request, federationId ) - def combine(self, request: CombineRequest, federation_id: str = None): - return self.client._post_with_id( - "/mint/combine", data=request, federation_id=federation_id + def withdraw( + self, address: str, amount_sat: int | "all", federationId: str = None + ) -> OnchainWithdrawResponse: + request = OnchainWithdrawRequest( + {"address": address, "amountSat": amount_sat} + ) + return self.client._post_with_federation_id( + "/wallet/withdraw", request, federationId ) diff --git a/wrappers/fedimint-py/test.py b/wrappers/fedimint-py/test.py new file mode 100644 index 0000000..6345b55 --- /dev/null +++ b/wrappers/fedimint-py/test.py @@ -0,0 +1,143 @@ +import os +from coincurve import PrivateKey +from wrappers.fedimint_py.FedimintClient import FedimintClient + + +def log_method(method: str): + print("--------------------") + print(f"Method: {method}") + + +def log_input_and_output(input_data, output): + print("Input: ", input_data) + print("Output: ", output) + print("--------------------") + + +def new_key_pair(): + private_key = PrivateKey() + public_key = private_key.public_key.format(compressed=False).hex() + return {"privateKey": private_key.to_hex(), "publicKey": public_key} + + +def build_test_client(): + base_url = os.getenv("FEDIMINT_CLIENTD_BASE_URL", "127.0.0.1:3333") + password = os.getenv("FEDIMINT_CLIENTD_PASSWORD", "password") + active_federation_id = os.getenv( + "FEDIMINT_CLIENTD_ACTIVE_FEDERATION_ID", + "15db8cb4f1ec8e484d73b889372bec94812580f929e8148b7437d359af422cd3", + ) + + client = FedimintClient(base_url, password, active_federation_id) + client.use_default_gateway() + print("Default gateway id: ", client.get_active_gatewayId()) + return client + + +def main(): + fedimint_client = build_test_client() + key_pair = new_key_pair() + print("Generated key pair: ", key_pair) + + # ADMIN METHODS + # `/v2/admin/config` + log_method("/v2/admin/config") + data = fedimint_client.config() + log_input_and_output({}, data) + # `/v2/admin/discover-version` + log_method("/v2/admin/discover-version") + data = fedimint_client.discover_version( + 1 + ) # Assuming threshold is required, adjust as needed + log_input_and_output({}, data) + # `/v2/admin/federation-ids` + log_method("/v2/admin/federation-ids") + data = fedimint_client.federation_ids() + log_input_and_output({}, data) + # `/v2/admin/info` + log_method("/v2/admin/info") + data = fedimint_client.info() + log_input_and_output({}, data) + # `/v2/admin/join` + invite_code = os.getenv("INVITE_CODE", "your_invite_code_here") + log_method("/v2/admin/join") + data = fedimint_client.join(invite_code, True) + log_input_and_output({"inviteCode": invite_code}, data) + # `/v2/admin/list-operations` + log_method("/v2/admin/list-operations") + data = fedimint_client.list_operations( + {"limit": 10} + ) # Adjust the request as needed + log_input_and_output({"limit": 10}, data) + + # LIGHTNING METHODS + # `/v2/ln/list-gateways` + log_method("/v2/ln/list-gateways") + data = fedimint_client.lightning.list_gateways() + log_input_and_output({}, data) + # `/v2/ln/invoice` + log_method("/v2/ln/invoice") + data = fedimint_client.lightning.create_invoice(10000, "test") + log_input_and_output({"amountMsat": 10000, "description": "test"}, data) + # `/v2/ln/pay` + log_method("/v2/ln/pay") + pay_response = fedimint_client.lightning.pay( + {"invoice": data.invoice} + ) # Adjust the request as needed + log_input_and_output({"paymentInfo": data.invoice}, pay_response) + # `/v2/ln/await-invoice` + log_method("/v2/ln/await-invoice") + data = fedimint_client.lightning.await_invoice( + {"operationId": data.operation_id} + ) # Adjust the request as needed + log_input_and_output({"operationId": data.operation_id}, data) + + # MINT METHODS + # `/v2/mint/spend` + log_method("/v2/mint/spend") + mint_data = fedimint_client.mint.spend(3000, True, 1000, False) + log_input_and_output({"allowOverpay": True, "timeout": 1000}, mint_data) + # `/v2/mint/decode-notes` + log_method("/v2/mint/decode-notes") + data = fedimint_client.mint.decode_notes(mint_data.notes) + log_input_and_output({"notes": mint_data.notes}, data) + # `/v2/mint/encode-notes` + log_method("/v2/mint/encode-notes") + data = fedimint_client.mint.encode_notes(data.notes_json) + log_input_and_output({"notesJson": data.notes_json}, data) + # `/v2/mint/validate` + log_method("/v2/mint/validate") + data = fedimint_client.mint.validate(mint_data.notes) + log_input_and_output({"notes": mint_data.notes}, data) + # `/v2/mint/reissue` + log_method("/v2/mint/reissue") + data = fedimint_client.mint.reissue(mint_data.notes) + log_input_and_output({"notes": mint_data.notes}, data) + # `/v2/mint/split` + log_method("/v2/mint/split") + data = fedimint_client.mint.split(mint_data.notes) + log_input_and_output({"notes": mint_data.notes}, data) + # `/v2/mint/combine` + log_method("/v2/mint/combine") + notes_vec = [data.notes] # Assuming `data.notes` is correct, adjust as needed + data = fedimint_client.mint.combine(notes_vec) + log_input_and_output({"notesVec": notes_vec}, data) + + # ONCHAIN METHODS + # `/v2/onchain/deposit-address` + log_method("/v2/onchain/deposit-address") + data = fedimint_client.onchain.create_deposit_address(1000) + log_input_and_output({"timeout": 1000}, data) + # `/v2/onchain/withdraw` + log_method("/v2/onchain/withdraw") + data = fedimint_client.onchain.withdraw(data.address, 1000) + log_input_and_output({"address": data.address, "amountSat": 1000}, data) + # `/v2/onchain/await-deposit` + # This method might be commented out or not implemented in the original script. Adjust as needed. + + print("Done: All methods tested successfully!") + + +# Run the main function +if __name__ == "__main__": + main() diff --git a/wrappers/fedimint-py/test_async.py b/wrappers/fedimint-py/test_async.py new file mode 100644 index 0000000..89ee4dc --- /dev/null +++ b/wrappers/fedimint-py/test_async.py @@ -0,0 +1,143 @@ +import asyncio +import os +from coincurve import PrivateKey +from AsyncFedimintClient import AsyncFedimintClient + + +def log_method(method: str): + print("--------------------") + print(f"Method: {method}") + + +def log_input_and_output(input_data, output): + print("Input: ", input_data) + print("Output: ", output) + print("--------------------") + + +def new_key_pair(): + private_key = PrivateKey() + public_key = private_key.public_key.format(compressed=False).hex() + return {"privateKey": private_key.to_hex(), "publicKey": public_key} + + +async def build_test_client(): + base_url = os.getenv("FEDIMINT_CLIENTD_BASE_URL", "127.0.0.1:3333") + password = os.getenv("FEDIMINT_CLIENTD_PASSWORD", "password") + active_federation_id = os.getenv( + "FEDIMINT_CLIENTD_ACTIVE_FEDERATION_ID", + "15db8cb4f1ec8e484d73b889372bec94812580f929e8148b7437d359af422cd3", + ) + + client = AsyncFedimintClient(base_url, password, active_federation_id) + await client.use_default_gateway() + print("Default gateway id: ", client.get_active_gatewayId()) + return client + + +async def main(): + fedimint_client = await build_test_client() + key_pair = new_key_pair() + print("Generated key pair: ", key_pair) + + # ADMIN METHODS + # `/v2/admin/config` + log_method("/v2/admin/config") + data = await fedimint_client.config() + log_input_and_output({}, data) + # `/v2/admin/discover-version` + log_method("/v2/admin/discover-version") + data = await fedimint_client.discover_version( + 1 + ) # Assuming threshold is required, adjust as needed + log_input_and_output({}, data) + # `/v2/admin/federation-ids` + log_method("/v2/admin/federation-ids") + data = await fedimint_client.federation_ids() + log_input_and_output({}, data) + # `/v2/admin/info` + log_method("/v2/admin/info") + data = await fedimint_client.info() + log_input_and_output({}, data) + # `/v2/admin/join` + invite_code = os.getenv("INVITE_CODE", "your_invite_code_here") + log_method("/v2/admin/join") + data = await fedimint_client.join(invite_code, True) + log_input_and_output({"inviteCode": invite_code}, data) + # `/v2/admin/list-operations` + log_method("/v2/admin/list-operations") + data = await fedimint_client.list_operations( + {"limit": 10} + ) # Adjust the request as needed + log_input_and_output({"limit": 10}, data) + + # LIGHTNING METHODS + # `/v2/ln/list-gateways` + log_method("/v2/ln/list-gateways") + data = await fedimint_client.lightning.list_gateways() + log_input_and_output({}, data) + # `/v2/ln/invoice` + log_method("/v2/ln/invoice") + data = await fedimint_client.lightning.create_invoice(10000, "test") + log_input_and_output({"amountMsat": 10000, "description": "test"}, data) + # `/v2/ln/pay` + log_method("/v2/ln/pay") + pay_response = await fedimint_client.lightning.pay( + {"invoice": data.invoice} + ) # Adjust the request as needed + log_input_and_output({"paymentInfo": data.invoice}, pay_response) + # `/v2/ln/await-invoice` + log_method("/v2/ln/await-invoice") + data = await fedimint_client.lightning.await_invoice( + {"operationId": data.operation_id} + ) # Adjust the request as needed + log_input_and_output({"operationId": data.operation_id}, data) + + # MINT METHODS + # `/v2/mint/spend` + log_method("/v2/mint/spend") + mint_data = await fedimint_client.mint.spend(3000, True, 1000, False) + log_input_and_output({"allowOverpay": True, "timeout": 1000}, mint_data) + # `/v2/mint/decode-notes` + log_method("/v2/mint/decode-notes") + data = await fedimint_client.mint.decode_notes(mint_data.notes) + log_input_and_output({"notes": mint_data.notes}, data) + # `/v2/mint/encode-notes` + log_method("/v2/mint/encode-notes") + data = await fedimint_client.mint.encode_notes(data.notes_json) + log_input_and_output({"notesJson": data.notes_json}, data) + # `/v2/mint/validate` + log_method("/v2/mint/validate") + data = await fedimint_client.mint.validate(mint_data.notes) + log_input_and_output({"notes": mint_data.notes}, data) + # `/v2/mint/reissue` + log_method("/v2/mint/reissue") + data = await fedimint_client.mint.reissue(mint_data.notes) + log_input_and_output({"notes": mint_data.notes}, data) + # `/v2/mint/split` + log_method("/v2/mint/split") + data = await fedimint_client.mint.split(mint_data.notes) + log_input_and_output({"notes": mint_data.notes}, data) + # `/v2/mint/combine` + log_method("/v2/mint/combine") + notes_vec = [data.notes] # Assuming `data.notes` is correct, adjust as needed + data = await fedimint_client.mint.combine(notes_vec) + log_input_and_output({"notesVec": notes_vec}, data) + + # ONCHAIN METHODS + # `/v2/onchain/deposit-address` + log_method("/v2/onchain/deposit-address") + data = await fedimint_client.onchain.create_deposit_address(1000) + log_input_and_output({"timeout": 1000}, data) + # `/v2/onchain/withdraw` + log_method("/v2/onchain/withdraw") + data = await fedimint_client.onchain.withdraw(data.address, 1000) + log_input_and_output({"address": data.address, "amountSat": 1000}, data) + # `/v2/onchain/await-deposit` + # This method might be commented out or not implemented in the original script. Adjust as needed. + + print("Done: All methods tested successfully!") + + +# Run the main function +asyncio.run(main()) From 7dd48cbb141de2bc2a0f9322d465794abb0eb94d Mon Sep 17 00:00:00 2001 From: "kody.low" Date: Thu, 4 Apr 2024 17:46:01 -0700 Subject: [PATCH 4/4] feat: finish updating python clients --- justfile.local.just | 6 + wrappers/fedimint-py/AsyncFedimintClient.py | 197 ++++++++++++-------- wrappers/fedimint-py/FedimintClient.py | 175 +++++++++-------- wrappers/fedimint-py/models/common.py | 9 +- wrappers/fedimint-py/models/onchain.py | 4 +- wrappers/fedimint-py/requirements.txt | 2 +- wrappers/fedimint-py/test.py | 125 +++++++------ wrappers/fedimint-py/test_async.py | 95 +++++++--- 8 files changed, 361 insertions(+), 252 deletions(-) diff --git a/justfile.local.just b/justfile.local.just index 4b54102..178441d 100644 --- a/justfile.local.just +++ b/justfile.local.just @@ -7,6 +7,12 @@ mprocs: test-ts: bun run wrappers/fedimint-ts/test.ts +test-py: + python3.11 wrappers/fedimint-py/test.py + +test-py-async: + python3.11 wrappers/fedimint-py/test_async.py + test-go: cd wrappers/fedimint-go && go run cmd/main.go diff --git a/wrappers/fedimint-py/AsyncFedimintClient.py b/wrappers/fedimint-py/AsyncFedimintClient.py index c85e543..95a26a8 100644 --- a/wrappers/fedimint-py/AsyncFedimintClient.py +++ b/wrappers/fedimint-py/AsyncFedimintClient.py @@ -1,7 +1,9 @@ import json import logging -from typing import List +from typing import List, Literal, Optional, Union +import asyncio import aiohttp +import atexit from models.common import ( DiscoverVersionRequest, @@ -50,18 +52,20 @@ class AsyncFedimintClient: + def __init__( self, base_url: str, password: str, - active_federationId: str, - active_gatewayId: str = None, + active_federation_id: str, + active_gateway_id: str = None, ): self.base_url = f"{base_url}/v2" self.password = password - self.active_federationId = active_federationId - self.active_gatewayId = active_gatewayId + self.active_federation_id = active_federation_id + self.active_gateway_id = active_gateway_id self.session = aiohttp.ClientSession() + atexit.register(self.close_sync) # Register the cleanup function self.lightning = self.Lightning(self) self.onchain = self.Onchain(self) @@ -70,25 +74,41 @@ def __init__( "Initialized fedimint client, must set active gateway id after intitalization to use lightning module methods or manually pass in gateways" ) - def get_active_federationId(self): - return self.active_federationId + async def close(self): + await self.session.close() + + def close_sync(self): + try: + loop = asyncio.get_event_loop() + except RuntimeError as e: + # If no event loop is available, create a new one for cleanup + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + if loop.is_running(): + loop.create_task(self.close()) + else: + loop.run_until_complete(self.close()) + + def get_active_federation_id(self): + return self.active_federation_id - def set_active_federationId(self, federationId: str): - self.active_federationId = federationId + def set_active_federation_id(self, federation_id: str): + self.active_federation_id = federation_id - def get_active_gatewayId(self): - return self.active_gatewayId + def get_active_gateway_id(self): + return self.active_gateway_id - def set_active_gatewayId(self, gatewayId: str): - self.active_gatewayId = gatewayId + def set_active_gateway_id(self, gateway_id: str): + self.active_gateway_id = gateway_id - def use_default_gateway(self): + async def use_default_gateway(self): # hits list_gateways and sets active_gatewayId to the first gateway try: - gateways = self.lightning.list_gateways() + gateways = await self.lightning.list_gateways() logging.info("Gateways: ", gateways) - self.active_gateway_id = gateways[0].info.gatewayId - logging.info("Set active gateway id to: ", self.active_gatewayId) + self.active_gateway_id = gateways[0]["info"]["gateway_id"] + logging.info("Set active gateway id to: ", self.active_gateway_id) except Exception as e: logging.error("Error setting default gateway id: ", e) @@ -117,14 +137,14 @@ async def _post(self, endpoint: str, data=None): return await response.json() async def _post_with_federation_id( - self, endpoint: str, data=None, federationId: str = None + self, endpoint: str, data=None, federation_id: str = None ): - if federationId is None: - federationId = self.get_active_federation_id() + if federation_id is None: + federation_id = self.get_active_federation_id() if data is None: data = {} - data["federationId"] = federationId + data["federationId"] = federation_id return await self._post(endpoint, data) @@ -132,25 +152,25 @@ async def _post_with_gateway_id_and_federation_id( self, endpoint: str, data=None, - gatewayId: str = None, - federationId: str = None, + gateway_id: str = None, + federation_id: str = None, ): - if gatewayId is None: - gatewayId = self.get_active_gateway_id() + if gateway_id is None: + gateway_id = self.get_active_gateway_id() - if federationId is None: - federationId = self.get_active_federation_id() + if federation_id is None: + federation_id = self.get_active_federation_id() - if federationId is None or gatewayId is None: + if federation_id is None or gateway_id is None: raise Exception( "Must set active gateway id and active federation id before calling this method" ) if data is None: data = {} - data["gatewayId"] = gatewayId - data["federationId"] = federationId + data["gatewayId"] = gateway_id + data["federationId"] = federation_id return await self._post(endpoint, data) @@ -174,9 +194,10 @@ async def federation_ids(self): async def list_operations(self, request: ListOperationsRequest): return await self._post_with_federation_id("/admin/list-operations", request) - async def join(self, invite_code: str, set_default: bool = False): + async def join(self, invite_code: str, use_manual_secret: bool = False): return await self._post( - "/admin/join", {"inviteCode": invite_code, "setDefault": set_default} + "/admin/join", + {"inviteCode": invite_code, "useManualSecret": use_manual_secret}, ) class Lightning: @@ -215,7 +236,7 @@ async def create_invoice_for_pubkey_tweak( federation_id: str = None, ) -> LightningInvoiceForPubkeyTweakResponse: request: LightningInvoiceForPubkeyTweakRequest = { - "pubkey": pubkey, + "externalPubkey": pubkey, "tweak": tweak, "amountMsat": amount_msat, "description": description, @@ -241,30 +262,45 @@ async def claim_pubkey_tweak_receives( } return await self.client._post_with_federation_id( - "/ln/claim-external-pubkey-tweaked", + "/ln/claim-external-receive-tweaked", data=request, federation_id=federation_id, ) async def await_invoice( - self, request: LightningAwaitInvoiceRequest, federationId: str = None + self, operation_id: str, federation_id: str = None ) -> InfoResponse: + request: LightningAwaitInvoiceRequest = {"operationId": operation_id} return await self.client._post_with_gateway_id_and_federation_id( - "/ln/await-invoice", request, federationId + "/ln/await-invoice", request, federation_id=federation_id ) async def pay( - self, request: LightningPayRequest, federationId: str = None + self, + payment_info: str, + amount_msat: Optional[int], + lightning_url_comment: Optional[str], + gateway_id: str = None, + federation_id: str = None, ) -> LightningPayResponse: + request: LightningPayRequest = { + "paymentInfo": payment_info, + "amountMsat": amount_msat, + "lightningUrlComment": lightning_url_comment, + } return await self.client._post_with_gateway_id_and_federation_id( - "/ln/pay", request, federationId + "/ln/pay", request, gateway_id=gateway_id, federation_id=federation_id ) async def await_pay( - self, request: LightningAwaitPayRequest, federationId: str = None + self, operation_id: str, gateway_id: str = None, federation_id: str = None ): + request: LightningAwaitPayRequest = {"operationId": operation_id} return await self.client._post_with_gateway_id_and_federation_id( - "/ln/await-pay", request, federationId + "/ln/await-pay", + request, + gateway_id=gateway_id, + federation_id=federation_id, ) async def list_gateways(self) -> List[Gateway]: @@ -275,28 +311,28 @@ def __init__(self, client): self.client = client async def decode_notes( - self, notes: str, federationId: str = None + self, notes: str, federation_id: str = None ) -> MintDecodeNotesResponse: - request = MintDecodeNotesRequest({"notes": notes}) + request: MintDecodeNotesRequest = {"notes": notes} return await self.client._post_with_federation_id( - "/mint/decode-notes", request, federationId + "/mint/decode-notes", request, federation_id ) async def encode_notes( - self, notes_json: NotesJson, federationId: str = None + self, notes_json: NotesJson, federation_id: str = None ) -> MintEncodeNotesResponse: - request = MintEncodeNotesRequest({"notesJsonStr": json.dumps(notes_json)}) + request: MintEncodeNotesRequest = {"notesJsonStr": json.dumps(notes_json)} return await self.client._post_with_federation_id( - "/mint/encode-notes", request, federationId + "/mint/encode-notes", request, federation_id ) async def reissue( - self, notes: str, federationId: str = None + self, notes: str, federation_id: str = None ) -> MintReissueResponse: - request = MintReissueRequest({"notes": notes}) + request: MintReissueRequest = {"notes": notes} return await self.client._post_with_federation_id( - "/mint/reissue", request, federationId + "/mint/reissue", request, federation_id ) async def spend( @@ -305,67 +341,66 @@ async def spend( allow_overpay: bool, timeout: int, include_invite: bool, - federationId: str = None, + federation_id: str = None, ) -> MintSpendResponse: - request = MintSpendRequest( - { - "amountMsat": amount_msat, - "allowOverpay": allow_overpay, - "timeout": timeout, - "includeInvite": include_invite, - } - ) + request: MintSpendRequest = { + "amountMsat": amount_msat, + "allowOverpay": allow_overpay, + "timeout": timeout, + "includeInvite": include_invite, + } return await self.client._post_with_federation_id( - "/mint/spend", request, federationId + "/mint/spend", request, federation_id ) async def validate( - self, notes: str, federationId: str = None + self, notes: str, federation_id: str = None ) -> MintValidateResponse: - request = MintValidateRequest({"notes": notes}) + request: MintValidateRequest = {"notes": notes} return await self.client._post_with_federation_id( - "/mint/validate", request, federationId + "/mint/validate", request, federation_id ) - async def split(self, notes: str, federationId: str = None): - request = MintSplitRequest({"notes": notes}) + async def split(self, notes: str, federation_id: str = None): + request: MintSplitRequest = {"notes": notes} return await self.client._post_with_federation_id( - "/mint/split", request, federationId + "/mint/split", request, federation_id ) - async def combine(self, notes_vec: List[str], federationId: str = None): - request = MintCombineRequest({"notesVec": notes_vec}) + async def combine(self, notes_vec: List[str], federation_id: str = None): + request: MintCombineRequest = {"notesVec": notes_vec} return await self.client._post_with_federation_id( - "/mint/combine", request, federationId + "/mint/combine", request, federation_id ) class Onchain: def __init__(self, client): self.client = client - async def create_deposit_address(self, timeout: int, federationId: str = None): - request = OnchainDepositAddressRequest({"timeout": timeout}) + async def create_deposit_address(self, timeout: int, federation_id: str = None): + request: OnchainDepositAddressRequest = {"timeout": timeout} return await self.client._post_with_federation_id( - "/wallet/deposit-address", request, federationId + "/wallet/deposit-address", request, federation_id ) async def await_deposit( - self, operation_id: str, federationId: str = None + self, operation_id: str, federation_id: str = None ) -> OnchainAwaitDepositResponse: - request = OnchainAwaitDepositRequest({"operationId": operation_id}) + request: OnchainAwaitDepositRequest = {"operationId": operation_id} return await self.client._post_with_federation_id( - "/wallet/await-deposit", request, federationId + "/wallet/await-deposit", request, federation_id ) async def withdraw( - self, address: str, amount_sat: int | "all", federationId: str = None + self, + address: str, + amount_sat: Union[int, Literal["all"]], + federationId: str = None, ) -> OnchainWithdrawResponse: - request = OnchainWithdrawRequest( - {"address": address, "amountSat": amount_sat} - ) + request: OnchainWithdrawRequest = { + "address": address, + "amountSat": amount_sat, + } return await self.client._post_with_federation_id( "/wallet/withdraw", request, federationId ) - - async def close(self): - await self.session.close() diff --git a/wrappers/fedimint-py/FedimintClient.py b/wrappers/fedimint-py/FedimintClient.py index fe45bf6..ecd5810 100644 --- a/wrappers/fedimint-py/FedimintClient.py +++ b/wrappers/fedimint-py/FedimintClient.py @@ -1,5 +1,6 @@ import json import logging +from typing import List, Literal, Union import requests from models.common import ( @@ -11,22 +12,20 @@ from models.lightning import ( Gateway, - LightningClaimPubkeReceivesRequest, LightningAwaitInvoiceRequest, + LightningClaimPubkeReceivesRequest, LightningCreateInvoiceRequest, LightningCreateInvoiceResponse, LightningInvoiceForPubkeyTweakRequest, LightningInvoiceForPubkeyTweakResponse, LightningPayRequest, LightningPayResponse, - LightningAwaitPayRequest, ) from models.onchain import ( OnchainAwaitDepositRequest, OnchainAwaitDepositResponse, OnchainDepositAddressRequest, - OnchainDepositAddressResponse, OnchainWithdrawRequest, OnchainWithdrawResponse, ) @@ -53,13 +52,13 @@ def __init__( self, base_url: str, password: str, - active_federationId: str, - active_gatewayId: str = None, + active_federation_id: str, + active_gateway_id: str = None, ): self.base_url = f"{base_url}/v2" self.password = password - self.active_federationId = active_federationId - self.active_gatewayId = active_gatewayId + self.active_federation_id = active_federation_id + self.active_gateway_id = active_gateway_id self.lightning = self.Lightning(self) self.onchain = self.Onchain(self) @@ -68,25 +67,25 @@ def __init__( "Initialized fedimint client, must set active gateway id after initialization to use lightning module methods or manually pass in gateways" ) - def get_active_federationId(self): - return self.active_federationId + def get_active_federation_id(self): + return self.active_federation_id - def set_active_federationId(self, federationId: str): - self.active_federationId = federationId + def set_active_federation_id(self, federation_id: str): + self.active_federation_id = federation_id - def get_active_gatewayId(self): - return self.active_gatewayId + def get_active_gateway_id(self): + return self.active_gateway_id - def set_active_gatewayId(self, gatewayId: str): - self.active_gatewayId = gatewayId + def set_active_gateway_id(self, gateway_id: str): + self.active_gateway_id = gateway_id def use_default_gateway(self): # hits list_gateways and sets active_gatewayId to the first gateway try: gateways = self.lightning.list_gateways() logging.info("Gateways: ", gateways) - self.active_gateway_id = gateways[0].info.gatewayId - logging.info("Set active gateway id to: ", self.active_gatewayId) + self.active_gateway_id = gateways[0]["info"]["gateway_id"] + logging.info("Set active gateway id to: ", self.active_gateway_id) except Exception as e: logging.error("Error setting default gateway id: ", e) @@ -114,14 +113,14 @@ def _post(self, endpoint: str, data=None): return response.json() def _post_with_federation_id( - self, endpoint: str, data=None, federationId: str = None + self, endpoint: str, data=None, federation_id: str = None ): - if federationId is None: - federationId = self.get_active_federationId() + if federation_id is None: + federation_id = self.get_active_federation_id() if data is None: data = {} - data["federationId"] = federationId + data["federationId"] = federation_id return self._post(endpoint, data) @@ -129,25 +128,24 @@ def _post_with_gateway_id_and_federation_id( self, endpoint: str, data=None, - gatewayId: str = None, - federationId: str = None, + gateway_id: str = None, + federation_id: str = None, ): + if gateway_id is None: + gateway_id = self.get_active_gateway_id() - if gatewayId is None: - gatewayId = self.get_active_gatewayId() + if federation_id is None: + federation_id = self.get_active_federation_id() - if federationId is None: - federationId = self.get_active_federationId() - - if federationId is None or gatewayId is None: + if federation_id is None or gateway_id is None: raise Exception( "Must set active gateway id and active federation id before calling this method" ) if data is None: data = {} - data["gatewayId"] = gatewayId - data["federationId"] = federationId + data["gatewayId"] = gateway_id + data["federationId"] = federation_id return self._post(endpoint, data) @@ -167,9 +165,10 @@ def federation_ids(self): def list_operations(self, request: ListOperationsRequest): return self._post_with_federation_id("/admin/list-operations", request) - def join(self, invite_code: str, set_default: bool = False): + def join(self, invite_code: str, use_manual_secret: bool = False): return self._post( - "/admin/join", {"inviteCode": invite_code, "setDefault": set_default} + "/admin/join", + {"inviteCode": invite_code, "useManualSecret": use_manual_secret}, ) class Lightning: @@ -197,6 +196,14 @@ def create_invoice( federation_id=federation_id, ) + def await_invoice( + self, operation_id: str, federation_id: str = None + ) -> InfoResponse: + request: LightningAwaitInvoiceRequest = {"operationId": operation_id} + return self.client._post_with_federation_id( + "/ln/await-invoice", request, federation_id + ) + def create_invoice_for_pubkey_tweak( self, pubkey: str, @@ -208,7 +215,7 @@ def create_invoice_for_pubkey_tweak( federation_id: str = None, ) -> LightningInvoiceForPubkeyTweakResponse: request: LightningInvoiceForPubkeyTweakRequest = { - "pubkey": pubkey, + "externalPubkey": pubkey, "tweak": tweak, "amountMsat": amount_msat, "description": description, @@ -234,16 +241,29 @@ def claim_pubkey_tweak_receives( } return self.client._post_with_federation_id( - "/ln/claim-external-pubkey-tweaked", + "/ln/claim-external-receive-tweaked", data=request, federation_id=federation_id, ) def pay( - self, request: LightningPayRequest, federationId: str = None + self, + payment_info: str, + amount_msat: int, + lightning_url_comment: str, + gateway_id: str = None, + federation_id: str = None, ) -> LightningPayResponse: + request: LightningPayRequest = { + "paymentInfo": payment_info, + "amountMsat": amount_msat, + "lightningUrlComment": lightning_url_comment, + } return self.client._post_with_gateway_id_and_federation_id( - "/ln/pay", request, federationId + "/ln/pay", + data=request, + gateway_id=gateway_id, + federation_id=federation_id, ) def list_gateways(self) -> List[Gateway]: @@ -254,26 +274,26 @@ def __init__(self, client): self.client = client def decode_notes( - self, notes: str, federationId: str = None + self, notes: str, federation_id: str = None ) -> MintDecodeNotesResponse: - request = MintDecodeNotesRequest({"notes": notes}) + request: MintDecodeNotesRequest = {"notes": notes} return self.client._post_with_federation_id( - "/mint/decode-notes", request, federationId + "/mint/decode-notes", request, federation_id ) def encode_notes( - self, notes_json: NotesJson, federationId: str = None + self, notes_json: NotesJson, federation_id: str = None ) -> MintEncodeNotesResponse: - request = MintEncodeNotesRequest({"notesJsonStr": json.dumps(notes_json)}) + request: MintEncodeNotesRequest = {"notesJsonStr": json.dumps(notes_json)} return self.client._post_with_federation_id( - "/mint/encode-notes", request, federationId + "/mint/encode-notes", request, federation_id ) - def reissue(self, notes: str, federationId: str = None) -> MintReissueResponse: - request = MintReissueRequest({"notes": notes}) + def reissue(self, notes: str, federation_id: str = None) -> MintReissueResponse: + request: MintReissueRequest = {"notes": notes} return self.client._post_with_federation_id( - "/mint/reissue", request, federationId + "/mint/reissue", request, federation_id ) def spend( @@ -282,64 +302,67 @@ def spend( allow_overpay: bool, timeout: int, include_invite: bool, - federationId: str = None, + federation_id: str = None, ) -> MintSpendResponse: - request = MintSpendRequest( - { - "amountMsat": amount_msat, - "allowOverpay": allow_overpay, - "timeout": timeout, - "includeInvite": include_invite, - } - ) + request: MintSpendRequest = { + "amountMsat": amount_msat, + "allowOverpay": allow_overpay, + "timeout": timeout, + "includeInvite": include_invite, + } + return self.client._post_with_federation_id( - "/mint/spend", request, federationId + "/mint/spend", request, federation_id ) def validate( - self, notes: str, federationId: str = None + self, notes: str, federation_id: str = None ) -> MintValidateResponse: - request = MintValidateRequest({"notes": notes}) + request: MintValidateRequest = {"notes": notes} return self.client._post_with_federation_id( - "/mint/validate", request, federationId + "/mint/validate", request, federation_id ) - def split(self, notes: str, federationId: str = None): - request = MintSplitRequest({"notes": notes}) + def split(self, notes: str, federation_id: str = None): + request: MintSplitRequest = {"notes": notes} return self.client._post_with_federation_id( - "/mint/split", request, federationId + "/mint/split", request, federation_id ) - def combine(self, notes_vec: List[str], federationId: str = None): - request = MintCombineRequest({"notesVec": notes_vec}) + def combine(self, notes_vec: List[str], federation_id: str = None): + request: MintCombineRequest = {"notesVec": notes_vec} return self.client._post_with_federation_id( - "/mint/combine", request, federationId + "/mint/combine", request, federation_id ) class Onchain: def __init__(self, client): self.client = client - def create_deposit_address(self, timeout: int, federationId: str = None): - request = OnchainDepositAddressRequest({"timeout": timeout}) + def create_deposit_address(self, timeout: int, federation_id: str = None): + request: OnchainDepositAddressRequest = {"timeout": timeout} return self.client._post_with_federation_id( - "/wallet/deposit-address", request, federationId + "/wallet/deposit-address", request, federation_id ) def await_deposit( - self, operation_id: str, federationId: str = None + self, operation_id: str, federation_id: str = None ) -> OnchainAwaitDepositResponse: - request = OnchainAwaitDepositRequest({"operationId": operation_id}) + request: OnchainAwaitDepositRequest = {"operationId": operation_id} return self.client._post_with_federation_id( - "/wallet/await-deposit", request, federationId + "/wallet/await-deposit", request, federation_id ) def withdraw( - self, address: str, amount_sat: int | "all", federationId: str = None + self, + address: str, + amount_sat: Union[int, Literal["all"]], + federation_id: str = None, ) -> OnchainWithdrawResponse: - request = OnchainWithdrawRequest( - {"address": address, "amountSat": amount_sat} - ) + request: OnchainWithdrawRequest = { + "address": address, + "amountSat": amount_sat, + } return self.client._post_with_federation_id( - "/wallet/withdraw", request, federationId + "/wallet/withdraw", request, federation_id ) diff --git a/wrappers/fedimint-py/models/common.py b/wrappers/fedimint-py/models/common.py index 71142e8..3268185 100644 --- a/wrappers/fedimint-py/models/common.py +++ b/wrappers/fedimint-py/models/common.py @@ -2,8 +2,7 @@ from typing import List, Optional, Dict, Any -class Tiered(RootModel): - root: Dict[int, Any] +Tiered = RootModel[Dict[int, Any]] class TieredSummary(BaseModel): @@ -18,8 +17,7 @@ class FederationInfo(BaseModel): denominationsMsat: TieredSummary -class InfoResponse(BaseModel): - __root__: Dict[str, FederationInfo] +InfoResponse = RootModel[Dict[str, FederationInfo]] class BackupRequest(BaseModel): @@ -43,8 +41,7 @@ class DiscoverVersionRequest(BaseModel): # Returns a dictionary of federation_ids and their api versions -class DiscoverVersionResponse(BaseModel): - __root__: Dict[str, Any] +DiscoverVersionResponse = RootModel[Dict[str, Any]] class JoinRequest(BaseModel): diff --git a/wrappers/fedimint-py/models/onchain.py b/wrappers/fedimint-py/models/onchain.py index 8481e78..53d1482 100644 --- a/wrappers/fedimint-py/models/onchain.py +++ b/wrappers/fedimint-py/models/onchain.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Union +from typing import Dict, List, Literal, Union from pydantic import BaseModel @@ -45,7 +45,7 @@ class OnchainAwaitDepositResponse(BaseModel): class OnchainWithdrawRequest(BaseModel): address: str - amount_sat: int | "all" + amount_sat: Union[int, Literal["all"]] class OnchainWithdrawResponse(BaseModel): diff --git a/wrappers/fedimint-py/requirements.txt b/wrappers/fedimint-py/requirements.txt index f229360..570fdd0 100644 --- a/wrappers/fedimint-py/requirements.txt +++ b/wrappers/fedimint-py/requirements.txt @@ -1 +1 @@ -requests +requests coincurve diff --git a/wrappers/fedimint-py/test.py b/wrappers/fedimint-py/test.py index 6345b55..46fcd06 100644 --- a/wrappers/fedimint-py/test.py +++ b/wrappers/fedimint-py/test.py @@ -1,6 +1,6 @@ import os from coincurve import PrivateKey -from wrappers.fedimint_py.FedimintClient import FedimintClient +from FedimintClient import FedimintClient def log_method(method: str): @@ -30,7 +30,7 @@ def build_test_client(): client = FedimintClient(base_url, password, active_federation_id) client.use_default_gateway() - print("Default gateway id: ", client.get_active_gatewayId()) + print("Default gateway id: ", client.get_active_gateway_id()) return client @@ -40,104 +40,119 @@ def main(): print("Generated key pair: ", key_pair) # ADMIN METHODS - # `/v2/admin/config` log_method("/v2/admin/config") data = fedimint_client.config() log_input_and_output({}, data) - # `/v2/admin/discover-version` + log_method("/v2/admin/discover-version") - data = fedimint_client.discover_version( - 1 - ) # Assuming threshold is required, adjust as needed + data = fedimint_client.discover_version(1) log_input_and_output({}, data) - # `/v2/admin/federation-ids` + log_method("/v2/admin/federation-ids") data = fedimint_client.federation_ids() log_input_and_output({}, data) - # `/v2/admin/info` + log_method("/v2/admin/info") data = fedimint_client.info() log_input_and_output({}, data) - # `/v2/admin/join` - invite_code = os.getenv("INVITE_CODE", "your_invite_code_here") + + invite_code = os.getenv( + "INVITE_CODE", + "fed11qgqrgvnhwden5te0v9k8q6rp9ekh2arfdeukuet595cr2ttpd3jhq6rzve6zuer9wchxvetyd938gcewvdhk6tcqqysptkuvknc7erjgf4em3zfh90kffqf9srujn6q53d6r056e4apze5cw27h75", + ) log_method("/v2/admin/join") - data = fedimint_client.join(invite_code, True) + data = fedimint_client.join(invite_code, False) log_input_and_output({"inviteCode": invite_code}, data) - # `/v2/admin/list-operations` + log_method("/v2/admin/list-operations") - data = fedimint_client.list_operations( - {"limit": 10} - ) # Adjust the request as needed + data = fedimint_client.list_operations({"limit": 10}) log_input_and_output({"limit": 10}, data) # LIGHTNING METHODS - # `/v2/ln/list-gateways` log_method("/v2/ln/list-gateways") data = fedimint_client.lightning.list_gateways() log_input_and_output({}, data) - # `/v2/ln/invoice` + log_method("/v2/ln/invoice") data = fedimint_client.lightning.create_invoice(10000, "test") log_input_and_output({"amountMsat": 10000, "description": "test"}, data) - # `/v2/ln/pay` + log_method("/v2/ln/pay") - pay_response = fedimint_client.lightning.pay( - {"invoice": data.invoice} - ) # Adjust the request as needed - log_input_and_output({"paymentInfo": data.invoice}, pay_response) - # `/v2/ln/await-invoice` + pay_response = fedimint_client.lightning.pay(data["invoice"], None, None) + log_input_and_output({"paymentInfo": data["invoice"]}, pay_response) + log_method("/v2/ln/await-invoice") - data = fedimint_client.lightning.await_invoice( - {"operationId": data.operation_id} - ) # Adjust the request as needed - log_input_and_output({"operationId": data.operation_id}, data) + pay_data = fedimint_client.lightning.await_invoice(data["operationId"]) + log_input_and_output({"operationId": data["operationId"]}, pay_data) + + log_method("/v2/ln/create-invoice-for-pubkey-tweaked") + data = fedimint_client.lightning.create_invoice_for_pubkey_tweak( + pubkey=key_pair["publicKey"], tweak=1, amount_msat=1000, description="test" + ) + log_input_and_output( + { + "pubkey": key_pair["publicKey"], + "tweak": 1, + "amountMsat": 1000, + "description": "test", + }, + data, + ) + + fedimint_client.lightning.pay(data["invoice"], None, None) + print("Paid locked invoice!") + + log_method("/v2/ln/claim-external-receive-tweaked") + data = fedimint_client.lightning.claim_pubkey_tweak_receives( + private_key=key_pair["privateKey"], + tweaks=[1], + federation_id=fedimint_client.get_active_federation_id(), + ) + log_input_and_output({"privateKey": key_pair["privateKey"], "tweaks": [1]}, data) # MINT METHODS - # `/v2/mint/spend` log_method("/v2/mint/spend") mint_data = fedimint_client.mint.spend(3000, True, 1000, False) log_input_and_output({"allowOverpay": True, "timeout": 1000}, mint_data) - # `/v2/mint/decode-notes` + log_method("/v2/mint/decode-notes") - data = fedimint_client.mint.decode_notes(mint_data.notes) - log_input_and_output({"notes": mint_data.notes}, data) - # `/v2/mint/encode-notes` + data = fedimint_client.mint.decode_notes(mint_data["notes"]) + log_input_and_output({"notes": mint_data["notes"]}, data) + log_method("/v2/mint/encode-notes") - data = fedimint_client.mint.encode_notes(data.notes_json) - log_input_and_output({"notesJson": data.notes_json}, data) - # `/v2/mint/validate` + data = fedimint_client.mint.encode_notes(data["notesJson"]) + log_input_and_output({"notesJson": data}, data) + log_method("/v2/mint/validate") - data = fedimint_client.mint.validate(mint_data.notes) - log_input_and_output({"notes": mint_data.notes}, data) - # `/v2/mint/reissue` + data = fedimint_client.mint.validate(mint_data["notes"]) + log_input_and_output({"notes": mint_data["notes"]}, data) + log_method("/v2/mint/reissue") - data = fedimint_client.mint.reissue(mint_data.notes) - log_input_and_output({"notes": mint_data.notes}, data) - # `/v2/mint/split` + data = fedimint_client.mint.reissue(mint_data["notes"]) + log_input_and_output({"notes": mint_data["notes"]}, data) + log_method("/v2/mint/split") - data = fedimint_client.mint.split(mint_data.notes) - log_input_and_output({"notes": mint_data.notes}, data) - # `/v2/mint/combine` + data = fedimint_client.mint.split(mint_data["notes"]) + log_input_and_output({"notes": mint_data["notes"]}, data) + log_method("/v2/mint/combine") - notes_vec = [data.notes] # Assuming `data.notes` is correct, adjust as needed - data = fedimint_client.mint.combine(notes_vec) - log_input_and_output({"notesVec": notes_vec}, data) + notes_vec = [data["notes"]] + notes_values_vec = [ + value for note_dict in notes_vec for value in note_dict.values() + ] + data = fedimint_client.mint.combine(notes_values_vec) + log_input_and_output({"notesVec": notes_values_vec}, data) # ONCHAIN METHODS - # `/v2/onchain/deposit-address` log_method("/v2/onchain/deposit-address") data = fedimint_client.onchain.create_deposit_address(1000) log_input_and_output({"timeout": 1000}, data) - # `/v2/onchain/withdraw` log_method("/v2/onchain/withdraw") - data = fedimint_client.onchain.withdraw(data.address, 1000) - log_input_and_output({"address": data.address, "amountSat": 1000}, data) - # `/v2/onchain/await-deposit` - # This method might be commented out or not implemented in the original script. Adjust as needed. + withdraw_data = fedimint_client.onchain.withdraw(data["address"], 1000) + log_input_and_output({"address": data["address"], "amountSat": 1000}, withdraw_data) print("Done: All methods tested successfully!") -# Run the main function if __name__ == "__main__": main() diff --git a/wrappers/fedimint-py/test_async.py b/wrappers/fedimint-py/test_async.py index 89ee4dc..cfdbee8 100644 --- a/wrappers/fedimint-py/test_async.py +++ b/wrappers/fedimint-py/test_async.py @@ -31,7 +31,7 @@ async def build_test_client(): client = AsyncFedimintClient(base_url, password, active_federation_id) await client.use_default_gateway() - print("Default gateway id: ", client.get_active_gatewayId()) + print("Default gateway id: ", client.get_active_gateway_id()) return client @@ -44,7 +44,7 @@ async def main(): # `/v2/admin/config` log_method("/v2/admin/config") data = await fedimint_client.config() - log_input_and_output({}, data) + # log_input_and_output({}, data) # `/v2/admin/discover-version` log_method("/v2/admin/discover-version") data = await fedimint_client.discover_version( @@ -60,15 +60,16 @@ async def main(): data = await fedimint_client.info() log_input_and_output({}, data) # `/v2/admin/join` - invite_code = os.getenv("INVITE_CODE", "your_invite_code_here") + invite_code = os.getenv( + "INVITE_CODE", + "fed11qgqrgvnhwden5te0v9k8q6rp9ekh2arfdeukuet595cr2ttpd3jhq6rzve6zuer9wchxvetyd938gcewvdhk6tcqqysptkuvknc7erjgf4em3zfh90kffqf9srujn6q53d6r056e4apze5cw27h75", + ) log_method("/v2/admin/join") - data = await fedimint_client.join(invite_code, True) + data = await fedimint_client.join(invite_code, False) log_input_and_output({"inviteCode": invite_code}, data) # `/v2/admin/list-operations` log_method("/v2/admin/list-operations") - data = await fedimint_client.list_operations( - {"limit": 10} - ) # Adjust the request as needed + data = await fedimint_client.list_operations({"limit": 10}) log_input_and_output({"limit": 10}, data) # LIGHTNING METHODS @@ -82,16 +83,41 @@ async def main(): log_input_and_output({"amountMsat": 10000, "description": "test"}, data) # `/v2/ln/pay` log_method("/v2/ln/pay") - pay_response = await fedimint_client.lightning.pay( - {"invoice": data.invoice} - ) # Adjust the request as needed - log_input_and_output({"paymentInfo": data.invoice}, pay_response) + pay_response = await fedimint_client.lightning.pay(data["invoice"], None, None) + log_input_and_output({"paymentInfo": data["invoice"]}, pay_response) # `/v2/ln/await-invoice` log_method("/v2/ln/await-invoice") - data = await fedimint_client.lightning.await_invoice( - {"operationId": data.operation_id} - ) # Adjust the request as needed - log_input_and_output({"operationId": data.operation_id}, data) + pay_data = await fedimint_client.lightning.await_invoice(data["operationId"]) + log_input_and_output({"operationId": data["operationId"]}, pay_data) + + # LIGHTNING METHODS FOR PUBKEY TWEAK + # `/v2/ln/create-invoice-for-pubkey-tweaked` + log_method("/v2/ln/create-invoice-for-pubkey-tweaked") + data = await fedimint_client.lightning.create_invoice_for_pubkey_tweak( + pubkey=key_pair["publicKey"], tweak=1, amount_msat=1000, description="test" + ) + log_input_and_output( + { + "pubkey": key_pair["publicKey"], + "tweak": 1, + "amountMsat": 1000, + "description": "test", + }, + data, + ) + + # Pay the invoice + fedimint_client.lightning.pay(data["invoice"], None, None) + print("Paid locked invoice!") + + # `/v2/ln/claim-external-pubkey-tweaked` + log_method("/v2/ln/claim-external-pubkey-tweaked") + data = await fedimint_client.lightning.claim_pubkey_tweak_receives( + private_key=key_pair["privateKey"], + tweaks=[1], + federation_id=fedimint_client.get_active_federation_id(), + ) + log_input_and_output({"privateKey": key_pair["privateKey"], "tweaks": [1]}, data) # MINT METHODS # `/v2/mint/spend` @@ -100,44 +126,51 @@ async def main(): log_input_and_output({"allowOverpay": True, "timeout": 1000}, mint_data) # `/v2/mint/decode-notes` log_method("/v2/mint/decode-notes") - data = await fedimint_client.mint.decode_notes(mint_data.notes) - log_input_and_output({"notes": mint_data.notes}, data) + data = await fedimint_client.mint.decode_notes(mint_data["notes"]) + log_input_and_output({"notes": mint_data["notes"]}, data) # `/v2/mint/encode-notes` log_method("/v2/mint/encode-notes") - data = await fedimint_client.mint.encode_notes(data.notes_json) - log_input_and_output({"notesJson": data.notes_json}, data) + data = await fedimint_client.mint.encode_notes(data["notesJson"]) + log_input_and_output({"notesJson": data}, data) # `/v2/mint/validate` log_method("/v2/mint/validate") - data = await fedimint_client.mint.validate(mint_data.notes) - log_input_and_output({"notes": mint_data.notes}, data) + data = await fedimint_client.mint.validate(mint_data["notes"]) + log_input_and_output({"notes": mint_data["notes"]}, data) # `/v2/mint/reissue` log_method("/v2/mint/reissue") - data = await fedimint_client.mint.reissue(mint_data.notes) - log_input_and_output({"notes": mint_data.notes}, data) + data = await fedimint_client.mint.reissue(mint_data["notes"]) + log_input_and_output({"notes": mint_data["notes"]}, data) # `/v2/mint/split` log_method("/v2/mint/split") - data = await fedimint_client.mint.split(mint_data.notes) - log_input_and_output({"notes": mint_data.notes}, data) + data = await fedimint_client.mint.split(mint_data["notes"]) + log_input_and_output({"notes": mint_data["notes"]}, data) # `/v2/mint/combine` log_method("/v2/mint/combine") - notes_vec = [data.notes] # Assuming `data.notes` is correct, adjust as needed - data = await fedimint_client.mint.combine(notes_vec) - log_input_and_output({"notesVec": notes_vec}, data) + notes_vec = [data["notes"]] + print("notes_vec: ", notes_vec) + notes_values_vec = [ + value for note_dict in notes_vec for value in note_dict.values() + ] + data = await fedimint_client.mint.combine(notes_values_vec) + log_input_and_output({"notesVec": notes_values_vec}, data) # ONCHAIN METHODS # `/v2/onchain/deposit-address` log_method("/v2/onchain/deposit-address") data = await fedimint_client.onchain.create_deposit_address(1000) + print("data: ", data) log_input_and_output({"timeout": 1000}, data) # `/v2/onchain/withdraw` log_method("/v2/onchain/withdraw") - data = await fedimint_client.onchain.withdraw(data.address, 1000) - log_input_and_output({"address": data.address, "amountSat": 1000}, data) + withdraw_data = await fedimint_client.onchain.withdraw(data["address"], 1000) + log_input_and_output({"address": data["address"], "amountSat": 1000}, data) # `/v2/onchain/await-deposit` - # This method might be commented out or not implemented in the original script. Adjust as needed. + # this blocks for 10 minutes so it's not a good test print("Done: All methods tested successfully!") + await fedimint_client.close() + # Run the main function asyncio.run(main())