Skip to content

Commit 2d0c49a

Browse files
committed
zbd
1 parent e4ca2d6 commit 2d0c49a

File tree

10 files changed

+207
-2
lines changed

10 files changed

+207
-2
lines changed

fragments/wallet.js

+3
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ export const WALLET_FIELDS = gql`
169169
apiKeyRecv
170170
currencyRecv
171171
}
172+
... on WalletZebedee {
173+
gamerTagId
174+
}
172175
}
173176
}
174177
`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- AlterEnum
2+
ALTER TYPE "WalletType" ADD VALUE 'ZEBEDEE';
3+
4+
-- CreateTable
5+
CREATE TABLE "WalletZebedee" (
6+
"id" SERIAL NOT NULL,
7+
"walletId" INTEGER NOT NULL,
8+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
9+
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
10+
"gamerTagId" TEXT,
11+
12+
CONSTRAINT "WalletZebedee_pkey" PRIMARY KEY ("id")
13+
);
14+
15+
-- CreateIndex
16+
CREATE UNIQUE INDEX "WalletZebedee_walletId_key" ON "WalletZebedee"("walletId");
17+
18+
-- AddForeignKey
19+
ALTER TABLE "WalletZebedee" ADD CONSTRAINT "WalletZebedee_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "Wallet"("id") ON DELETE CASCADE ON UPDATE CASCADE;
20+
21+
-- Update wallet json
22+
CREATE TRIGGER wallet_zebedee_as_jsonb
23+
AFTER INSERT OR UPDATE ON "WalletZebedee"
24+
FOR EACH ROW EXECUTE PROCEDURE wallet_wallet_type_as_jsonb();

prisma/schema.prisma

+11
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ enum WalletType {
189189
BLINK
190190
LNC
191191
WEBLN
192+
ZEBEDEE
192193
}
193194

194195
model Wallet {
@@ -216,6 +217,7 @@ model Wallet {
216217
walletNWC WalletNWC?
217218
walletPhoenixd WalletPhoenixd?
218219
walletBlink WalletBlink?
220+
walletZebedee WalletZebedee?
219221
220222
vaultEntries VaultEntry[] @relation("VaultEntries")
221223
withdrawals Withdrawl[]
@@ -325,6 +327,15 @@ model WalletPhoenixd {
325327
secondaryPassword String?
326328
}
327329

330+
model WalletZebedee {
331+
id Int @id @default(autoincrement())
332+
walletId Int @unique
333+
wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)
334+
createdAt DateTime @default(now()) @map("created_at")
335+
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
336+
gamerTagId String?
337+
}
338+
328339
model Mute {
329340
muterId Int
330341
mutedId Int

public/wallets/zbd-dark.svg

+1
Loading

public/wallets/zbd.svg

+1
Loading

wallets/client.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ import * as lnd from '@/wallets/lnd/client'
77
import * as webln from '@/wallets/webln/client'
88
import * as blink from '@/wallets/blink/client'
99
import * as phoenixd from '@/wallets/phoenixd/client'
10+
import * as zebedee from '@/wallets/zebedee/client'
1011

11-
export default [nwc, lnbits, lnc, lnAddr, cln, lnd, webln, blink, phoenixd]
12+
export default [nwc, lnbits, lnc, lnAddr, cln, lnd, webln, blink, phoenixd, zebedee]

wallets/server.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as lnbits from '@/wallets/lnbits/server'
66
import * as nwc from '@/wallets/nwc/server'
77
import * as phoenixd from '@/wallets/phoenixd/server'
88
import * as blink from '@/wallets/blink/server'
9+
import * as zebedee from '@/wallets/zebedee/server'
910

1011
// we import only the metadata of client side wallets
1112
import * as lnc from '@/wallets/lnc'
@@ -20,7 +21,7 @@ import { timeoutSignal, withTimeout } from '@/lib/time'
2021
import { canReceive } from './common'
2122
import wrapInvoice from './wrap'
2223

23-
export default [lnd, cln, lnAddr, lnbits, nwc, phoenixd, blink, lnc, webln]
24+
export default [lnd, cln, lnAddr, lnbits, nwc, phoenixd, blink, lnc, webln, zebedee]
2425

2526
const MAX_PENDING_INVOICES_PER_WALLET = 25
2627

wallets/zebedee/client.js

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { API_URL, PREIMAGE_AWAIT_TIMEOUT_MS } from '@/wallets/zebedee'
2+
import { assertContentTypeJson } from '@/lib/url'
3+
import { callWithTimeout } from '@/lib/time'
4+
import { fetchWithTimeout } from '@/lib/fetch'
5+
6+
export * from '@/wallets/zebedee'
7+
8+
export async function testSendPayment ({ apiKey }, { signal }) {
9+
const wallet = await apiCall('wallet', { apiKey, method: 'GET' }, { signal })
10+
if (!wallet.data) throw new Error('wallet not found')
11+
}
12+
13+
export async function sendPayment (bolt11, { apiKey }, { signal }) {
14+
const res = await apiCall('payments', { body: { invoice: bolt11 }, apiKey }, { signal })
15+
const { id, preimage } = res?.data
16+
if (preimage) return preimage
17+
// the api might return before the invoice is paid, so we'll wait for the preimage
18+
return await waitForPreimage(id, { apiKey }, { signal })
19+
}
20+
21+
async function waitForPreimage (id, { apiKey }, { signal }) {
22+
return await callWithTimeout(async () => {
23+
let preimage
24+
while (true) {
25+
const res = await apiCall('payments/{id}', { body: { id }, apiKey, method: 'GET' }, { signal })
26+
preimage = res?.data?.preimage
27+
if (preimage) break
28+
await new Promise(resolve => setTimeout(resolve, 10))
29+
}
30+
return preimage
31+
}, PREIMAGE_AWAIT_TIMEOUT_MS)
32+
}
33+
34+
export async function apiCall (api, { body, apiKey, method = 'POST' }, { signal }) {
35+
const headers = {
36+
apikey: apiKey,
37+
'Content-Type': 'application/json'
38+
}
39+
if (method === 'GET' && body) {
40+
for (const [k, v] of Object.entries(body)) {
41+
api = api.replace('{' + k + '}', v)
42+
}
43+
}
44+
const res = await fetchWithTimeout(API_URL + api, {
45+
method,
46+
headers,
47+
signal,
48+
body: method === 'POST' ? JSON.stringify(body) : undefined
49+
})
50+
// https://zbd.dev/api-reference/errors
51+
if (res.status !== 200) {
52+
let error
53+
try {
54+
assertContentTypeJson(res)
55+
const json = await res.json()
56+
if (json?.message) error = json.message
57+
} catch (e) {
58+
error = res.statusText || 'error ' + res.status
59+
}
60+
throw new Error(error)
61+
}
62+
return res.json()
63+
}

wallets/zebedee/index.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { string } from '@/lib/yup'
2+
3+
export const PREIMAGE_AWAIT_TIMEOUT_MS = 1_200
4+
export const STATIC_CHARGE_URL = 'https://api.zebedee.io/v0/process-static-charges/'
5+
export const DASHBOARD_URL = 'https://dashboard.zebedee.io/'
6+
export const GAMER_TAG_LNADDR_BASEURL = 'https://zbd.gg/.well-known/lnurlp/'
7+
export const API_URL = 'https://api.zebedee.io/v0/'
8+
export const ZEBEDEE_LNDOMAIN = 'zbd.gg'
9+
10+
export const name = 'zebedee'
11+
export const walletType = 'ZEBEDEE'
12+
export const walletField = 'walletZebedee'
13+
14+
export const fields = [
15+
{
16+
name: 'apiKey',
17+
label: 'api key',
18+
type: 'password',
19+
optional: 'for sending',
20+
help: `you can get an API key from [Zebedee Dashboard](${DASHBOARD_URL}) from \n\`Project->API->Live\``,
21+
clientOnly: true,
22+
requiredWithout: 'gamerTagId',
23+
validate: string()
24+
},
25+
{
26+
name: 'gamerTagId',
27+
label: 'gamer tag or id',
28+
type: 'text',
29+
optional: 'for receiving',
30+
help: `you can find your Gamertag in the [Zebedee Dashboard](${DASHBOARD_URL}) under \n\`Account->Gamertag\`\n section, or in the Zebedee app on the Wallet card.\nNote: You can also use your \`@${ZEBEDEE_LNDOMAIN}\` Lightning address here.`,
31+
serverOnly: true,
32+
requiredWithout: 'apiKey',
33+
validate: string()
34+
}
35+
]
36+
37+
export const card = {
38+
title: 'Zebedee',
39+
subtitle: 'use [Zebedee](https://zebedee.io) for payments',
40+
image: { src: '/wallets/zbd.svg' }
41+
42+
}

0 commit comments

Comments
 (0)