diff options
| author | Adam Soltys <[email protected]> | 2026-02-03 09:15:39 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-02-06 08:58:12 -0800 |
| commit | 5e3bfba5eb3d54cc03551f98624a2e92dca5825f (patch) | |
| tree | 723e0be4e4f0a987fa2375fcf0360022f1232eaa | |
| parent | 4850177f50b7ff5a0f37acaa92190219d6f72da5 (diff) | |
| download | coinos-server-5e3bfba5eb3d54cc03551f98624a2e92dca5825f.tar.gz coinos-server-5e3bfba5eb3d54cc03551f98624a2e92dca5825f.zip | |
custodial ark payments
| -rwxr-xr-x | bun.lockb | bin | 231433 -> 234863 bytes | |||
| -rw-r--r-- | fund-and-settle.sh | 31 | ||||
| -rw-r--r-- | inspect-ark.ts | 23 | ||||
| -rw-r--r-- | lib/ark.ts | 32 | ||||
| -rw-r--r-- | lib/invoices.ts | 2 | ||||
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | routes/payments.ts | 44 | ||||
| -rw-r--r-- | routes/users.ts | 2 |
8 files changed, 109 insertions, 26 deletions
| Binary files differ diff --git a/fund-and-settle.sh b/fund-and-settle.sh new file mode 100644 index 00000000..103ef055 --- /dev/null +++ b/fund-and-settle.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +# 1. Send BTC to boarding address and mine +echo "Funding boarding address..." +docker exec bc bitcoin-cli -regtest -rpcuser=admin1 -rpcpassword=123 -rpcwallet=coinos sendtoaddress "bcrt1p4pm6myqffj07g8quw9er2vdnvf8rj7ns4nwwt9l5gxjf8pmjkueqh9hezk" 1 +docker exec bc bitcoin-cli -regtest -rpcuser=admin1 -rpcpassword=123 generatetoaddress 1 "$(docker exec bc bitcoin-cli -regtest -rpcuser=admin1 -rpcpassword=123 -rpcwallet=coinos getnewaddress)" +echo "Funded and confirmed." + +# 2. Start settle in background +echo "Starting settle..." +cd /home/adam/coinos-server && bun inspect-ark.ts & +SETTLE_PID=$! + +# 3. Wait for intent to register then mine blocks +echo "Waiting for intent registration..." +for i in $(seq 1 60); do + sleep 5 + INTENTS=$(docker exec arkd arkd intents 2>&1) + if echo "$INTENTS" | grep -q "boardingInputs"; then + echo "Intent registered! Mining block..." + docker exec bc bitcoin-cli -regtest -rpcuser=admin1 -rpcpassword=123 generatetoaddress 1 "$(docker exec bc bitcoin-cli -regtest -rpcuser=admin1 -rpcpassword=123 -rpcwallet=coinos getnewaddress)" + break + fi + echo "No intent yet (attempt $i)..." +done + +# 4. Wait for settle to complete +echo "Waiting for settle..." +wait $SETTLE_PID +echo "Done." diff --git a/inspect-ark.ts b/inspect-ark.ts new file mode 100644 index 00000000..8c1a551b --- /dev/null +++ b/inspect-ark.ts @@ -0,0 +1,23 @@ +import { EventSource } from "eventsource"; +if (!globalThis.EventSource) globalThis.EventSource = EventSource; +import { SingleKey, Wallet } from "@arkade-os/sdk"; + +const key = "0ebe9a690651e9e08d7c2f4db106d8ec264725241fdb34c57aedb26155b0e027"; +const identity = SingleKey.fromHex(key); +const wallet = await Wallet.create({ identity, arkServerUrl: "http://localhost:7070" }); + +const balance = await wallet.getBalance(); +console.log("Balance:", JSON.stringify(balance)); + +console.log("Settling..."); +try { + const txid = await wallet.settle(); + console.log("Settle txid:", txid); + + const balanceAfter = await wallet.getBalance(); + console.log("Balance after:", JSON.stringify(balanceAfter)); +} catch(e: any) { + console.error("Error:", e.message); +} + +process.exit(0); diff --git a/lib/ark.ts b/lib/ark.ts new file mode 100644 index 00000000..44fa4fdd --- /dev/null +++ b/lib/ark.ts @@ -0,0 +1,32 @@ +import { EventSource } from "eventsource"; +if (!globalThis.EventSource) globalThis.EventSource = EventSource; + +import config from "$config"; +import { SingleKey, Wallet } from "@arkade-os/sdk"; + +let wallet: any; + +const getWallet = async () => { + if (wallet) return wallet; + + const { arkPrivateKey, arkServerUrl } = config.ark; + const identity = SingleKey.fromHex(arkPrivateKey); + + wallet = await Wallet.create({ identity, arkServerUrl }); + return wallet; +}; + +export const sendArk = async (address: string, amount: number) => { + const w = await getWallet(); + return w.sendBitcoin({ address, amount }); +}; + +export const getArkAddress = async () => { + const w = await getWallet(); + return w.getAddress(); +}; + +export const getArkBalance = async () => { + const w = await getWallet(); + return w.getBalance(); +}; diff --git a/lib/invoices.ts b/lib/invoices.ts index 6ddffe54..50cddd9a 100644 --- a/lib/invoices.ts +++ b/lib/invoices.ts @@ -131,6 +131,8 @@ export const generate = async ({ invoice, user }) => { } else if (type === PaymentType.ecash) { hash = id; text = request(id, amount, memo); + } else if (type === PaymentType.ark) { + hash = id; } else { fail(`unrecognized type ${type}`); } diff --git a/package.json b/package.json index f682fcf7..f5132e71 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "format": "biome format *.js **/*.js --write" }, "dependencies": { + "@arkade-os/sdk": "^0.3.12", "@asoltys/clightning-client": "^0.1.10", "@aws-sdk/client-ses": "^3.886.0", "@cashu/cashu-ts": "^2.7.2", diff --git a/routes/payments.ts b/routes/payments.ts index 752c7ce8..9b079e11 100644 --- a/routes/payments.ts +++ b/routes/payments.ts @@ -1,5 +1,6 @@ import config from "$config"; import api from "$lib/api"; +import { sendArk } from "$lib/ark"; import { requirePin } from "$lib/auth"; import { archive, db, g, gf, s } from "$lib/db"; import { generate } from "$lib/invoices"; @@ -751,34 +752,27 @@ export default { }, async ark(req, res) { - const { user } = req; - const { amount, hash, aid } = req.body; + const { body, user } = req; + const { address, amount, aid } = body; - const { currency } = user; - const rates = await g("rates"); - const rate = rates[currency]; - const p = { - id: v4(), - aid, - amount: -amount, - hash, - confirmed: true, - rate, - currency, - type: PaymentType.ark, - created: Date.now(), - }; + try { + await requirePin({ body, user }); + const txid = await sendArk(address, amount); + const hash = txid; - const { id: uid } = user; - await s(`payment:${hash}`, p.id); - await s(`payment:${p.id}`, p); - await db - .multi() - .lPush(`${aid || uid}:payments`, p.id) - .set(`${aid || uid}:payments:last`, p.created) - .exec(); + const p = await debit({ + hash, + amount, + user, + type: PaymentType.ark, + aid, + }); - res.send(p); + res.send(p); + } catch (e) { + warn(user.username, "ark payment failed", e.message); + res.code(500).send(e.message); + } }, async arkReceive(req, res) { diff --git a/routes/users.ts b/routes/users.ts index ca15924f..a63ac4ae 100644 --- a/routes/users.ts +++ b/routes/users.ts @@ -846,7 +846,7 @@ export default { const vtxoHex = Buffer.from(vtxoTaprootKey).toString("hex"); const r = await fetch( - `http://arkd:7070/v1/indexer/vtxos?scripts=5120${vtxoHex}`, + `http://arkd:7070/v1/indexer/vtxos?scripts=5120${vtxoHex}&spendable_only=true`, { "content-type": "application/json" }, ); const { vtxos } = await r.json(); |
