aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-14 15:46:01 -0800
committerAdam Soltys <[email protected]>2026-02-14 15:46:01 -0800
commit526b21bc668c453250ebce8dece9ba330bac0133 (patch)
tree5e9c84a2cca6b4a5766c6cdc9f33f6c62f5776f3 /lib
parent843c343db567cd224dadf347ac16736bdc8b24a8 (diff)
downloadcoinos-server-526b21bc668c453250ebce8dece9ba330bac0133.tar.gz
coinos-server-526b21bc668c453250ebce8dece9ba330bac0133.zip
custodial ark sends
Diffstat (limited to 'lib')
-rw-r--r--lib/ark.ts14
-rw-r--r--lib/db.ts8
-rw-r--r--lib/invoices.ts18
-rw-r--r--lib/lightning.ts4
-rw-r--r--lib/migrate.ts12
-rw-r--r--lib/payments.ts22
6 files changed, 50 insertions, 28 deletions
diff --git a/lib/ark.ts b/lib/ark.ts
index 00ea4db4..0d2d180f 100644
--- a/lib/ark.ts
+++ b/lib/ark.ts
@@ -9,16 +9,22 @@ let wallet: any;
const getWallet = async () => {
if (wallet) return wallet;
- const { arkPrivateKey, arkServerUrl } = config.ark;
+ const { arkPrivateKey, arkServerUrl, esploraUrl } = config.ark;
const identity = SingleKey.fromHex(arkPrivateKey);
- wallet = await Wallet.create({ identity, arkServerUrl });
+ wallet = await Wallet.create({ identity, arkServerUrl, esploraUrl });
return wallet;
};
export const sendArk = async (address: string, amount: number) => {
- const w = await getWallet();
- return w.sendBitcoin({ address, amount });
+ const timeout = new Promise((_, reject) =>
+ setTimeout(() => reject(new Error("Ark send timed out")), 45000),
+ );
+ const send = async () => {
+ const w = await getWallet();
+ return w.sendBitcoin({ address, amount });
+ };
+ return Promise.race([send(), timeout]);
};
export const getArkAddress = async () => {
diff --git a/lib/db.ts b/lib/db.ts
index 5ebe38b5..61e542ae 100644
--- a/lib/db.ts
+++ b/lib/db.ts
@@ -67,9 +67,15 @@ export const g = async (k) => {
export const s = (k, v) => {
if (k === "user:null" || k === "user:undefined") fail("null user");
- db.set(k, JSON.stringify(v));
+ return db.set(k, JSON.stringify(v));
};
+export async function* scan(pattern: string) {
+ for await (const keys of db.scanIterator({ MATCH: pattern })) {
+ for (const k of keys) yield k;
+ }
+}
+
export const ga = async (k) => {
const v = await archive.get(k);
try {
diff --git a/lib/invoices.ts b/lib/invoices.ts
index bf6c511e..f7bb6897 100644
--- a/lib/invoices.ts
+++ b/lib/invoices.ts
@@ -69,9 +69,25 @@ export const generate = async ({ invoice, user }) => {
let path;
let paymentHash;
- if (account.type === "ark") {
+ if (type === PaymentType.ark && account.type !== "ark") {
+ // Custodial account receiving via Ark: use vault's ark address
+ const accounts = await db.lRange(`${user.id}:accounts`, 0, -1);
+ let vaultAddress;
+ for (const accId of accounts) {
+ const acc = await g(`account:${accId}`);
+ if (acc?.type === "ark" && acc.arkAddress) {
+ vaultAddress = acc.arkAddress;
+ break;
+ }
+ }
+ if (!vaultAddress) fail("No ark vault found");
+ text = vaultAddress;
+ hash = id;
+ await s(`custodial-ark-invoice:${vaultAddress}`, id);
+ } else if (account.type === "ark") {
type = PaymentType.ark;
text = account.arkAddress;
+ hash = id;
} else if (account.seed || (account.pubkey && account.fingerprint)) {
type = PaymentType.bitcoin;
const nextIndex = account.nextIndex || 0;
diff --git a/lib/lightning.ts b/lib/lightning.ts
index e4c2e3cb..4824d9ab 100644
--- a/lib/lightning.ts
+++ b/lib/lightning.ts
@@ -1,4 +1,4 @@
-import { db, g, s } from "$lib/db";
+import { db, g, s, scan } from "$lib/db";
import ln, { lnListen, LightningUnavailableError } from "$lib/ln";
import { err, l, warn } from "$lib/logging";
import { handleZap } from "$lib/nostr";
@@ -171,7 +171,7 @@ export async function replay(index) {
}
export const fixBolt12 = async (_, res) => {
- for await (const k of db.scanIterator({ MATCH: "payment:*" })) {
+ for await (const k of scan("payment:*")) {
const p = await g(k);
if (p.type === "bolt12") {
console.log(k);
diff --git a/lib/migrate.ts b/lib/migrate.ts
index d0e38276..79f9546c 100644
--- a/lib/migrate.ts
+++ b/lib/migrate.ts
@@ -1,4 +1,4 @@
-import { db, g, s } from "$lib/db";
+import { db, g, s, scan } from "$lib/db";
import { findLastUsedIndex, parseDescriptor } from "$lib/esplora";
import { l, warn } from "$lib/logging";
import {
@@ -64,7 +64,7 @@ export const migrateBalancesToTB = async () => {
let count = 0;
// Migrate balance:* keys
- for await (const k of db.scanIterator({ MATCH: "balance:*" })) {
+ for await (const k of scan("balance:*")) {
try {
const id = k.split(":")[1];
const balance = Number.parseInt((await db.get(k)) || "0");
@@ -77,7 +77,7 @@ export const migrateBalancesToTB = async () => {
}
// Migrate pending:* keys
- for await (const k of db.scanIterator({ MATCH: "pending:*" })) {
+ for await (const k of scan("pending:*")) {
try {
const id = k.split(":")[1];
const pending = Number.parseInt((await db.get(k)) || "0");
@@ -88,7 +88,7 @@ export const migrateBalancesToTB = async () => {
}
// Migrate credit:*:* keys
- for await (const k of db.scanIterator({ MATCH: "credit:*:*" })) {
+ for await (const k of scan("credit:*:*")) {
try {
const parts = k.split(":");
const type = parts[1];
@@ -113,7 +113,7 @@ export const migrateToMicrosats = async () => {
let count = 0;
// Iterate through all users and multiply their TB balances
- for await (const k of db.scanIterator({ MATCH: "user:*" })) {
+ for await (const k of scan("user:*")) {
try {
const raw = await db.get(k);
if (!raw) continue;
@@ -142,7 +142,7 @@ export const migrateAutowithdraw = async () => {
let count = 0;
- for await (const k of db.scanIterator({ MATCH: "user:*" })) {
+ for await (const k of scan("user:*")) {
try {
const raw = await db.get(k);
if (!raw || !raw.startsWith("{")) continue;
diff --git a/lib/payments.ts b/lib/payments.ts
index f6a6bd23..03b52dfa 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -520,6 +520,14 @@ const pay = async ({ aid = undefined, amount, to, user }) => {
});
if (to.startsWith("ark") || to.startsWith("tark")) {
+ // Check for local vault - do internal transfer
+ const vault = await g(`arkaddr:${to}`);
+ if (vault) {
+ const recipient = await getUser(vault.uid);
+ if (recipient)
+ return sendInternal({ amount, recipient, sender: user });
+ }
+
const txid = await sendArk(to, amount);
const p = await debit({
hash: txid,
@@ -528,20 +536,6 @@ const pay = async ({ aid = undefined, amount, to, user }) => {
type: PaymentType.ark,
});
- const vault = await g(`arkaddr:${to}`);
- if (vault) {
- const { rate, currency } = await getUserRate(user);
- await createArkPayment({
- aid: vault.aid,
- uid: vault.uid,
- amount,
- hash: txid,
- rate,
- currency,
- extraHashMappings: [txid],
- });
- }
-
return p;
}