Skip to content

Commit

Permalink
zbd
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardobl committed Dec 19, 2024
1 parent e4ca2d6 commit 2d0c49a
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 2 deletions.
3 changes: 3 additions & 0 deletions fragments/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ export const WALLET_FIELDS = gql`
apiKeyRecv
currencyRecv
}
... on WalletZebedee {
gamerTagId
}
}
}
`
Expand Down
24 changes: 24 additions & 0 deletions prisma/migrations/20241219120508_zebedee_attachment/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- AlterEnum
ALTER TYPE "WalletType" ADD VALUE 'ZEBEDEE';

-- CreateTable
CREATE TABLE "WalletZebedee" (
"id" SERIAL NOT NULL,
"walletId" INTEGER NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"gamerTagId" TEXT,

CONSTRAINT "WalletZebedee_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "WalletZebedee_walletId_key" ON "WalletZebedee"("walletId");

-- AddForeignKey
ALTER TABLE "WalletZebedee" ADD CONSTRAINT "WalletZebedee_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "Wallet"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- Update wallet json
CREATE TRIGGER wallet_zebedee_as_jsonb
AFTER INSERT OR UPDATE ON "WalletZebedee"
FOR EACH ROW EXECUTE PROCEDURE wallet_wallet_type_as_jsonb();
11 changes: 11 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ enum WalletType {
BLINK
LNC
WEBLN
ZEBEDEE
}

model Wallet {
Expand Down Expand Up @@ -216,6 +217,7 @@ model Wallet {
walletNWC WalletNWC?
walletPhoenixd WalletPhoenixd?
walletBlink WalletBlink?
walletZebedee WalletZebedee?
vaultEntries VaultEntry[] @relation("VaultEntries")
withdrawals Withdrawl[]
Expand Down Expand Up @@ -325,6 +327,15 @@ model WalletPhoenixd {
secondaryPassword String?
}

model WalletZebedee {
id Int @id @default(autoincrement())
walletId Int @unique
wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
gamerTagId String?
}

model Mute {
muterId Int
mutedId Int
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 @@ -6,6 +6,7 @@ import * as lnbits from '@/wallets/lnbits/server'
import * as nwc from '@/wallets/nwc/server'
import * as phoenixd from '@/wallets/phoenixd/server'
import * as blink from '@/wallets/blink/server'
import * as zebedee from '@/wallets/zebedee/server'

// we import only the metadata of client side wallets
import * as lnc from '@/wallets/lnc'
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
63 changes: 63 additions & 0 deletions wallets/zebedee/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { API_URL, PREIMAGE_AWAIT_TIMEOUT_MS } from '@/wallets/zebedee'
import { assertContentTypeJson } from '@/lib/url'
import { callWithTimeout } from '@/lib/time'
import { fetchWithTimeout } from '@/lib/fetch'

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 })
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 }) {
return await callWithTimeout(async () => {
let preimage
while (true) {
const res = await apiCall('payments/{id}', { body: { id }, apiKey, method: 'GET' }, { signal })
preimage = res?.data?.preimage
if (preimage) break
await new Promise(resolve => setTimeout(resolve, 10))
}
return preimage
}, PREIMAGE_AWAIT_TIMEOUT_MS)
}

export async function apiCall (api, { body, apiKey, method = 'POST' }, { signal }) {
const headers = {
apikey: apiKey,
'Content-Type': 'application/json'
}
if (method === 'GET' && body) {
for (const [k, v] of Object.entries(body)) {
api = api.replace('{' + k + '}', v)
}
}
const res = await fetchWithTimeout(API_URL + api, {
method,
headers,
signal,
body: method === 'POST' ? JSON.stringify(body) : undefined
})
// https://zbd.dev/api-reference/errors
if (res.status !== 200) {
let error
try {
assertContentTypeJson(res)
const json = await res.json()
if (json?.message) error = json.message
} catch (e) {
error = res.statusText || 'error ' + res.status
}
throw new Error(error)
}
return res.json()
}
42 changes: 42 additions & 0 deletions wallets/zebedee/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { string } from '@/lib/yup'

export const PREIMAGE_AWAIT_TIMEOUT_MS = 1_200
export const STATIC_CHARGE_URL = 'https://api.zebedee.io/v0/process-static-charges/'
export const DASHBOARD_URL = 'https://dashboard.zebedee.io/'
export const GAMER_TAG_LNADDR_BASEURL = 'https://zbd.gg/.well-known/lnurlp/'
export const API_URL = 'https://api.zebedee.io/v0/'
export const ZEBEDEE_LNDOMAIN = 'zbd.gg'

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,
requiredWithout: 'gamerTagId',
validate: string()
},
{
name: 'gamerTagId',
label: 'gamer tag or id',
type: 'text',
optional: 'for receiving',
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.`,
serverOnly: true,
requiredWithout: 'apiKey',
validate: string()
}
]

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

}
Loading

0 comments on commit 2d0c49a

Please sign in to comment.