Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Zebedee send attachment #1742

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions prisma/migrations/20250106164328_zebedee_sender/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "WalletType" ADD VALUE 'ZEBEDEE';
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ enum WalletType {
BLINK
LNC
WEBLN
ZEBEDEE
}

model Wallet {
Expand Down
1 change: 1 addition & 0 deletions public/wallets/zbd-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/wallets/zbd.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion wallets/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ import * as lnd from '@/wallets/lnd/client'
import * as webln from '@/wallets/webln/client'
import * as blink from '@/wallets/blink/client'
import * as phoenixd from '@/wallets/phoenixd/client'
import * as zebedee from '@/wallets/zebedee/client'

export default [nwc, lnbits, lnc, lnAddr, cln, lnd, webln, blink, phoenixd]
export default [nwc, lnbits, lnc, lnAddr, cln, lnd, webln, blink, phoenixd, zebedee]
3 changes: 2 additions & 1 deletion wallets/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as blink from '@/wallets/blink/server'
// we import only the metadata of client side wallets
import * as lnc from '@/wallets/lnc'
import * as webln from '@/wallets/webln'
import * as zebedee from '@/wallets/zebedee'

import { walletLogger } from '@/api/resolvers/wallet'
import walletDefs from '@/wallets/server'
Expand All @@ -20,7 +21,7 @@ import { timeoutSignal, withTimeout } from '@/lib/time'
import { canReceive } from './common'
import wrapInvoice from './wrap'

export default [lnd, cln, lnAddr, lnbits, nwc, phoenixd, blink, lnc, webln]
export default [lnd, cln, lnAddr, lnbits, nwc, phoenixd, blink, lnc, webln, zebedee]

const MAX_PENDING_INVOICES_PER_WALLET = 25

Expand Down
73 changes: 73 additions & 0 deletions wallets/zebedee/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { API_URL } from '@/wallets/zebedee'
import { assertContentTypeJson } from '@/lib/url'
export * from '@/wallets/zebedee'

export async function testSendPayment ({ apiKey }, { signal }) {
const wallet = await apiCall('wallet', { apiKey, method: 'GET' }, { signal })
if (!wallet.data) throw new Error('wallet not found')
}

export async function sendPayment (bolt11, { apiKey }, { signal }) {
const res = await apiCall('payments', { body: { invoice: bolt11 }, apiKey }, { signal })
if (!res?.data) throw new Error('payment failed')

const { id, preimage } = res.data
if (preimage) return preimage

// the api might return before the invoice is paid, so we'll wait for the preimage
return await waitForPreimage(id, { apiKey }, { signal })
}

async function waitForPreimage (id, { apiKey }, { signal }) {
while (!signal.aborted) {
const res = await apiCall('payments/{id}', { body: { id }, apiKey, method: 'GET' }, { signal })

// return preimage if it's available
const preimage = res?.data?.preimage
if (preimage) return preimage

// wait a before checking again
await new Promise(resolve => setTimeout(resolve, 30))
}
return null
}

export async function apiCall (api, { body, apiKey, method = 'POST' }, { signal }) {
// if get request, put params into the url
if (method === 'GET' && body) {
for (const [k, v] of Object.entries(body)) {
api = api.replace(`{${k}}`, v)
}
}

const res = await fetch(API_URL + api, {
method,
headers: {
apikey: apiKey,
'Content-Type': 'application/json'
},
signal,
body: method === 'POST' ? JSON.stringify(body) : undefined
})

// Catch errors
// ref: https://zbd.dev/api-reference/errors
if (res.status < 200 || res.status > 299) {
// try to extract the error message from the response
let error
try {
assertContentTypeJson(res)
const json = await res.json()
if (json?.message) error = json.message
} catch (e) {
console.log('failed to parse error', e)
}

// throw the error, if we don't have one, we try to use the request status
if (!error) error = res.statusText || `error ${res.status}`
throw new Error(error)
}

assertContentTypeJson(res)
return await res.json()
}
26 changes: 26 additions & 0 deletions wallets/zebedee/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { string } from '@/lib/yup'

export const DASHBOARD_URL = 'https://dashboard.zebedee.io/'
export const API_URL = 'https://api.zebedee.io/v0/'

export const name = 'zebedee'
export const walletType = 'ZEBEDEE'
export const walletField = 'walletZebedee'

export const fields = [
{
name: 'apiKey',
label: 'api key',
type: 'password',
optional: 'for sending',
help: `you can get an API key from [Zebedee Dashboard](${DASHBOARD_URL}) from \n\`Project->API->Live\``,
clientOnly: true,
validate: string().min(8, 'invalid api key').max(64, 'api key is too long')
}
]

export const card = {
title: 'Zebedee',
subtitle: 'use [Zebedee](https://zebedee.io) for payments',
image: { src: '/wallets/zbd.svg' }
}
Loading