@1sat/wallet-server 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accounts/paymentMiddleware.d.ts +43 -0
- package/dist/accounts/paymentMiddleware.d.ts.map +1 -0
- package/dist/accounts/paymentMiddleware.js +134 -0
- package/dist/accounts/paymentMiddleware.js.map +1 -0
- package/dist/accounts/paymentRoute.d.ts +9 -14
- package/dist/accounts/paymentRoute.d.ts.map +1 -1
- package/dist/accounts/paymentRoute.js +87 -109
- package/dist/accounts/paymentRoute.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/topUp.d.ts +12 -18
- package/dist/topUp.d.ts.map +1 -1
- package/dist/topUp.js +36 -57
- package/dist/topUp.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BRC-41 payment middleware, vendored from `@bsv/payment-express-middleware`
|
|
3
|
+
* with one addition: an `onPaymentInternalize` hook that lets callers
|
|
4
|
+
* customize the `internalizeAction` args the middleware builds (labels,
|
|
5
|
+
* description, basket tags, etc).
|
|
6
|
+
*
|
|
7
|
+
* Upstream runs `wallet.internalizeAction` with only a hardcoded description.
|
|
8
|
+
* We need the wallet-server accounts ledger to internalize with structured
|
|
9
|
+
* labels (`wallet-storage-payment`, `payer:<id>`, `bytes:<n>`, `block:<n>`)
|
|
10
|
+
* so `listPaymentsForPayer` can later find the record. The hook gives us
|
|
11
|
+
* that insertion point without replacing the middleware wholesale.
|
|
12
|
+
*
|
|
13
|
+
* Keep this in sync with upstream by diffing this file against
|
|
14
|
+
* `@bsv/payment-express-middleware/src/index.ts` — only the hook-related
|
|
15
|
+
* additions should diverge.
|
|
16
|
+
*/
|
|
17
|
+
import { type AtomicBEEF, type InternalizeActionArgs, type WalletInterface } from '@bsv/sdk';
|
|
18
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
19
|
+
export interface BsvPayment {
|
|
20
|
+
derivationPrefix: string;
|
|
21
|
+
derivationSuffix: string;
|
|
22
|
+
transaction: unknown;
|
|
23
|
+
}
|
|
24
|
+
/** Default args the middleware would internalize with if no hook is provided. */
|
|
25
|
+
export interface DefaultInternalizeArgs {
|
|
26
|
+
tx: AtomicBEEF;
|
|
27
|
+
outputs: NonNullable<InternalizeActionArgs['outputs']>;
|
|
28
|
+
description: string;
|
|
29
|
+
}
|
|
30
|
+
export interface AccountsPaymentMiddlewareOptions {
|
|
31
|
+
wallet: WalletInterface;
|
|
32
|
+
/** Returns the sats owed for this request. 0 = pass through, no payment. */
|
|
33
|
+
calculateRequestPrice?: (req: Request) => number | Promise<number>;
|
|
34
|
+
/**
|
|
35
|
+
* Called just before `wallet.internalizeAction`. Receives the default
|
|
36
|
+
* args the middleware would have used and must return the args to
|
|
37
|
+
* actually use (merge or override). Useful for attaching labels, custom
|
|
38
|
+
* descriptions, basket tags, etc.
|
|
39
|
+
*/
|
|
40
|
+
onPaymentInternalize?: (req: Request, defaults: DefaultInternalizeArgs) => InternalizeActionArgs | Promise<InternalizeActionArgs>;
|
|
41
|
+
}
|
|
42
|
+
export declare function createAccountsPaymentMiddleware(options: AccountsPaymentMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
43
|
+
//# sourceMappingURL=paymentMiddleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paymentMiddleware.d.ts","sourceRoot":"","sources":["../../src/accounts/paymentMiddleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACN,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EAIpB,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAI9D,MAAM,WAAW,UAAU;IAC1B,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;CACpB;AAED,iFAAiF;AACjF,MAAM,WAAW,sBAAsB;IACtC,EAAE,EAAE,UAAU,CAAA;IACd,OAAO,EAAE,WAAW,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,CAAA;IACtD,WAAW,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,gCAAgC;IAChD,MAAM,EAAE,eAAe,CAAA;IACvB,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,CACtB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,sBAAsB,KAC5B,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAA;CAC3D;AAED,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,gCAAgC,GACvC,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CA+HpE"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BRC-41 payment middleware, vendored from `@bsv/payment-express-middleware`
|
|
3
|
+
* with one addition: an `onPaymentInternalize` hook that lets callers
|
|
4
|
+
* customize the `internalizeAction` args the middleware builds (labels,
|
|
5
|
+
* description, basket tags, etc).
|
|
6
|
+
*
|
|
7
|
+
* Upstream runs `wallet.internalizeAction` with only a hardcoded description.
|
|
8
|
+
* We need the wallet-server accounts ledger to internalize with structured
|
|
9
|
+
* labels (`wallet-storage-payment`, `payer:<id>`, `bytes:<n>`, `block:<n>`)
|
|
10
|
+
* so `listPaymentsForPayer` can later find the record. The hook gives us
|
|
11
|
+
* that insertion point without replacing the middleware wholesale.
|
|
12
|
+
*
|
|
13
|
+
* Keep this in sync with upstream by diffing this file against
|
|
14
|
+
* `@bsv/payment-express-middleware/src/index.ts` — only the hook-related
|
|
15
|
+
* additions should diverge.
|
|
16
|
+
*/
|
|
17
|
+
import { createNonce, Utils, verifyNonce, } from '@bsv/sdk';
|
|
18
|
+
const PAYMENT_VERSION = '1.0';
|
|
19
|
+
export function createAccountsPaymentMiddleware(options) {
|
|
20
|
+
const { wallet, calculateRequestPrice = () => 100, onPaymentInternalize, } = options;
|
|
21
|
+
if (typeof calculateRequestPrice !== 'function') {
|
|
22
|
+
throw new Error('calculateRequestPrice must be a function.');
|
|
23
|
+
}
|
|
24
|
+
if (wallet === undefined || typeof wallet !== 'object') {
|
|
25
|
+
throw new Error('A valid wallet must be supplied to the payment middleware.');
|
|
26
|
+
}
|
|
27
|
+
return async (req, res, next) => {
|
|
28
|
+
const authedReq = req;
|
|
29
|
+
if (authedReq.auth === undefined ||
|
|
30
|
+
typeof authedReq.auth.identityKey !== 'string') {
|
|
31
|
+
res.status(500).json({
|
|
32
|
+
status: 'error',
|
|
33
|
+
code: 'ERR_SERVER_MISCONFIGURED',
|
|
34
|
+
description: 'The payment middleware must be executed after the Auth middleware.',
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let requestPrice;
|
|
39
|
+
try {
|
|
40
|
+
requestPrice = await calculateRequestPrice(req);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
console.error('[accounts-payment] calculateRequestPrice failed', err);
|
|
44
|
+
res.status(500).json({
|
|
45
|
+
status: 'error',
|
|
46
|
+
code: 'ERR_PAYMENT_INTERNAL',
|
|
47
|
+
description: 'An internal error occurred while determining the payment required.',
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (requestPrice === 0) {
|
|
52
|
+
;
|
|
53
|
+
req.payment = { satoshisPaid: 0 };
|
|
54
|
+
return next();
|
|
55
|
+
}
|
|
56
|
+
const bsvPaymentHeader = req.headers['x-bsv-payment'];
|
|
57
|
+
if (bsvPaymentHeader === undefined) {
|
|
58
|
+
const derivationPrefix = await createNonce(wallet);
|
|
59
|
+
res
|
|
60
|
+
.status(402)
|
|
61
|
+
.set({
|
|
62
|
+
'x-bsv-payment-version': PAYMENT_VERSION,
|
|
63
|
+
'x-bsv-payment-satoshis-required': String(requestPrice),
|
|
64
|
+
'x-bsv-payment-derivation-prefix': derivationPrefix,
|
|
65
|
+
})
|
|
66
|
+
.json({
|
|
67
|
+
status: 'error',
|
|
68
|
+
code: 'ERR_PAYMENT_REQUIRED',
|
|
69
|
+
satoshisRequired: requestPrice,
|
|
70
|
+
description: 'A BSV payment is required. Retry with the X-BSV-Payment header.',
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
let paymentData;
|
|
75
|
+
try {
|
|
76
|
+
paymentData = JSON.parse(String(bsvPaymentHeader));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
res.status(400).json({
|
|
80
|
+
status: 'error',
|
|
81
|
+
code: 'ERR_MALFORMED_PAYMENT',
|
|
82
|
+
description: 'The X-BSV-Payment header is not valid JSON.',
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const valid = await verifyNonce(paymentData.derivationPrefix, wallet);
|
|
88
|
+
if (!valid)
|
|
89
|
+
throw new Error('invalid nonce');
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
res.status(400).json({
|
|
93
|
+
status: 'error',
|
|
94
|
+
code: 'ERR_INVALID_DERIVATION_PREFIX',
|
|
95
|
+
description: 'The X-BSV-Payment-Derivation-Prefix is not valid.',
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const defaults = {
|
|
100
|
+
tx: Utils.toArray(paymentData.transaction, 'base64'),
|
|
101
|
+
outputs: [
|
|
102
|
+
{
|
|
103
|
+
paymentRemittance: {
|
|
104
|
+
derivationPrefix: paymentData.derivationPrefix,
|
|
105
|
+
derivationSuffix: paymentData.derivationSuffix,
|
|
106
|
+
senderIdentityKey: authedReq.auth.identityKey,
|
|
107
|
+
},
|
|
108
|
+
outputIndex: 0,
|
|
109
|
+
protocol: 'wallet payment',
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
description: 'Payment for request',
|
|
113
|
+
};
|
|
114
|
+
const internalizeArgs = onPaymentInternalize
|
|
115
|
+
? await onPaymentInternalize(req, defaults)
|
|
116
|
+
: defaults;
|
|
117
|
+
try {
|
|
118
|
+
const { accepted } = await wallet.internalizeAction(internalizeArgs);
|
|
119
|
+
req.payment = {
|
|
120
|
+
satoshisPaid: requestPrice,
|
|
121
|
+
accepted,
|
|
122
|
+
tx: paymentData.transaction,
|
|
123
|
+
};
|
|
124
|
+
res.set({ 'x-bsv-payment-satoshis-paid': String(requestPrice) });
|
|
125
|
+
next();
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
const code = err?.code ?? 'ERR_PAYMENT_FAILED';
|
|
129
|
+
const description = err?.message ?? 'Payment failed.';
|
|
130
|
+
res.status(400).json({ status: 'error', code, description });
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=paymentMiddleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paymentMiddleware.js","sourceRoot":"","sources":["../../src/accounts/paymentMiddleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Out-of-band payment delivery for the backup sync case.
|
|
3
3
|
*
|
|
4
|
-
* Mounts `POST {basePath}/account/payment
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* a description, no labels. Our accounting ledger is label-indexed, so a
|
|
12
|
-
* label-less internalize goes unseen. The endpoint here keeps the same
|
|
13
|
-
* 402 wire protocol (so client-side `AuthFetch` pays automatically) but
|
|
14
|
-
* supplies the labels on internalize.
|
|
4
|
+
* Mounts `POST {basePath}/account/payment` with our BRC-41 payment
|
|
5
|
+
* middleware (vendored copy of `@bsv/payment-express-middleware` with an
|
|
6
|
+
* internalize hook added). The middleware handles the 402 handshake,
|
|
7
|
+
* nonce verification, and internalizeAction. Our hook attaches the
|
|
8
|
+
* accounts-ledger labels (`wallet-storage-payment`, `payer:<id>`,
|
|
9
|
+
* `bytes:<n>`, `block:<n>`) so `listPaymentsForPayer` can find the
|
|
10
|
+
* payment at `/account/status` time.
|
|
15
11
|
*
|
|
16
12
|
* Scope: only used for the backup path (sync writes). The active path
|
|
17
13
|
* keeps its inline capacity-gate bypass + auto-internalize in
|
|
18
14
|
* `accountsCapacityGate` because the active case has a structural
|
|
19
|
-
* chicken-and-egg — paying the active with a tx built on the active
|
|
20
|
-
* that the inline bypass solves.
|
|
15
|
+
* chicken-and-egg — paying the active with a tx built on the active.
|
|
21
16
|
*/
|
|
22
|
-
import {
|
|
17
|
+
import type { WalletInterface } from '@bsv/sdk';
|
|
23
18
|
import type { Express } from 'express';
|
|
24
19
|
import type { AccountsConfig, IdentityKey } from './types';
|
|
25
20
|
interface StorageWithMeter {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paymentRoute.d.ts","sourceRoot":"","sources":["../../src/accounts/paymentRoute.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"paymentRoute.d.ts","sourceRoot":"","sources":["../../src/accounts/paymentRoute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAmC,MAAM,SAAS,CAAA;AAWvE,OAAO,KAAK,EAEX,cAAc,EACd,WAAW,EACX,MAAM,SAAS,CAAA;AAEhB,UAAU,gBAAgB;IACzB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;IAC5E,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACjD;AAED,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,eAAe,CAAA;IACvB,aAAa,EAAE,gBAAgB,CAAA;IAC/B,iBAAiB,EAAE,WAAW,CAAA;IAC9B,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CACnC;AA2FD,wBAAgB,iBAAiB,CAChC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,gBAAgB,GACpB,IAAI,CAwDN"}
|
|
@@ -1,28 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Out-of-band payment delivery for the backup sync case.
|
|
3
3
|
*
|
|
4
|
-
* Mounts `POST {basePath}/account/payment
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* a description, no labels. Our accounting ledger is label-indexed, so a
|
|
12
|
-
* label-less internalize goes unseen. The endpoint here keeps the same
|
|
13
|
-
* 402 wire protocol (so client-side `AuthFetch` pays automatically) but
|
|
14
|
-
* supplies the labels on internalize.
|
|
4
|
+
* Mounts `POST {basePath}/account/payment` with our BRC-41 payment
|
|
5
|
+
* middleware (vendored copy of `@bsv/payment-express-middleware` with an
|
|
6
|
+
* internalize hook added). The middleware handles the 402 handshake,
|
|
7
|
+
* nonce verification, and internalizeAction. Our hook attaches the
|
|
8
|
+
* accounts-ledger labels (`wallet-storage-payment`, `payer:<id>`,
|
|
9
|
+
* `bytes:<n>`, `block:<n>`) so `listPaymentsForPayer` can find the
|
|
10
|
+
* payment at `/account/status` time.
|
|
15
11
|
*
|
|
16
12
|
* Scope: only used for the backup path (sync writes). The active path
|
|
17
13
|
* keeps its inline capacity-gate bypass + auto-internalize in
|
|
18
14
|
* `accountsCapacityGate` because the active case has a structural
|
|
19
|
-
* chicken-and-egg — paying the active with a tx built on the active
|
|
20
|
-
* that the inline bypass solves.
|
|
15
|
+
* chicken-and-egg — paying the active with a tx built on the active.
|
|
21
16
|
*/
|
|
22
|
-
import {
|
|
17
|
+
import { nextPaymentDerivation } from './middleware';
|
|
18
|
+
import { createAccountsPaymentMiddleware } from './paymentMiddleware';
|
|
23
19
|
import { quoteRefundedCharge } from './pricing';
|
|
24
20
|
import { PAYMENT_LABEL, blockLabel, bytesLabel, latestActivePaymentForPayer, payerLabel, } from './queries';
|
|
25
|
-
const PAYMENT_VERSION = '1.0';
|
|
26
21
|
async function computeQuote(deps, identityKey) {
|
|
27
22
|
if (!deps.config.enabled)
|
|
28
23
|
return undefined;
|
|
@@ -34,10 +29,10 @@ async function computeQuote(deps, identityKey) {
|
|
|
34
29
|
const userId = userResult?.user?.userId;
|
|
35
30
|
if (userId == null)
|
|
36
31
|
return undefined;
|
|
37
|
-
const
|
|
32
|
+
const currentBlock = await deps.currentBlock();
|
|
33
|
+
const [usedBytes, currentPayment] = await Promise.all([
|
|
38
34
|
deps.walletStorage.measureUsedBytes(userId),
|
|
39
|
-
latestActivePaymentForPayer(deps.wallet, identityKey,
|
|
40
|
-
deps.currentBlock(),
|
|
35
|
+
latestActivePaymentForPayer(deps.wallet, identityKey, currentBlock),
|
|
41
36
|
]);
|
|
42
37
|
const quote = quoteRefundedCharge({
|
|
43
38
|
usedBytes,
|
|
@@ -53,9 +48,81 @@ async function computeQuote(deps, identityKey) {
|
|
|
53
48
|
paidThroughBlock: quote.paidThroughBlock,
|
|
54
49
|
};
|
|
55
50
|
}
|
|
51
|
+
async function buildAccountStatus(deps, identityKey) {
|
|
52
|
+
const currentBlock = await deps.currentBlock();
|
|
53
|
+
const userResult = await deps.walletStorage.findOrInsertUser(identityKey);
|
|
54
|
+
const userId = userResult?.user?.userId;
|
|
55
|
+
const usedBytes = userId == null ? 0 : await deps.walletStorage.measureUsedBytes(userId);
|
|
56
|
+
if (!deps.config.enabled) {
|
|
57
|
+
return {
|
|
58
|
+
identityKey,
|
|
59
|
+
serverIdentityKey: deps.serverIdentityKey,
|
|
60
|
+
accountsEnabled: false,
|
|
61
|
+
currentBlock,
|
|
62
|
+
usedBytes,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const currentPayment = await latestActivePaymentForPayer(deps.wallet, identityKey, currentBlock);
|
|
66
|
+
const paidBytes = currentPayment?.bytesCovered ?? 0;
|
|
67
|
+
const capacityBytes = deps.config.baselineBytes + paidBytes;
|
|
68
|
+
const deficitBytes = Math.max(0, usedBytes - capacityBytes);
|
|
69
|
+
const nextPayment = await nextPaymentDerivation(identityKey, deps.wallet);
|
|
70
|
+
return {
|
|
71
|
+
identityKey,
|
|
72
|
+
serverIdentityKey: deps.serverIdentityKey,
|
|
73
|
+
accountsEnabled: true,
|
|
74
|
+
currentBlock,
|
|
75
|
+
usedBytes,
|
|
76
|
+
baselineBytes: deps.config.baselineBytes,
|
|
77
|
+
paidBytes,
|
|
78
|
+
capacityBytes,
|
|
79
|
+
deficitBytes,
|
|
80
|
+
paidThroughBlock: currentPayment?.paidThroughBlock ?? null,
|
|
81
|
+
pricing: {
|
|
82
|
+
purchaseUnitBytes: deps.config.purchaseUnitBytes,
|
|
83
|
+
satsPerUnit: deps.config.satsPerUnit,
|
|
84
|
+
durationBlocks: deps.config.durationBlocks,
|
|
85
|
+
},
|
|
86
|
+
nextPayment,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
56
89
|
export function mountPaymentRoute(app, basePath, deps) {
|
|
57
90
|
const paymentPath = joinPath(basePath, 'account/payment');
|
|
58
|
-
|
|
91
|
+
// Keyed by req — stashed during calculateRequestPrice, read in the
|
|
92
|
+
// onPaymentInternalize hook to derive the accounts-ledger labels.
|
|
93
|
+
const quoteByReq = new WeakMap();
|
|
94
|
+
const paymentMw = createAccountsPaymentMiddleware({
|
|
95
|
+
wallet: deps.wallet,
|
|
96
|
+
calculateRequestPrice: async (req) => {
|
|
97
|
+
const identityKey = req.auth?.identityKey;
|
|
98
|
+
if (!identityKey || identityKey === 'unknown')
|
|
99
|
+
return 0;
|
|
100
|
+
const quote = await computeQuote(deps, identityKey);
|
|
101
|
+
if (!quote)
|
|
102
|
+
return 0;
|
|
103
|
+
quoteByReq.set(req, quote);
|
|
104
|
+
return quote.chargeSats;
|
|
105
|
+
},
|
|
106
|
+
onPaymentInternalize: (req, defaults) => {
|
|
107
|
+
const identityKey = req.auth?.identityKey ?? '';
|
|
108
|
+
const quote = quoteByReq.get(req);
|
|
109
|
+
return {
|
|
110
|
+
...defaults,
|
|
111
|
+
description: 'wallet-server storage payment',
|
|
112
|
+
labels: [
|
|
113
|
+
PAYMENT_LABEL,
|
|
114
|
+
payerLabel(identityKey),
|
|
115
|
+
...(quote
|
|
116
|
+
? [
|
|
117
|
+
bytesLabel(quote.bytesCovered),
|
|
118
|
+
blockLabel(quote.paidThroughBlock),
|
|
119
|
+
]
|
|
120
|
+
: []),
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
app.post(paymentPath, paymentMw, async (req, res, next) => {
|
|
59
126
|
try {
|
|
60
127
|
const identityKey = req.auth?.identityKey;
|
|
61
128
|
if (!identityKey || identityKey === 'unknown') {
|
|
@@ -65,96 +132,7 @@ export function mountPaymentRoute(app, basePath, deps) {
|
|
|
65
132
|
description: 'Mutual-authenticated identity required.',
|
|
66
133
|
});
|
|
67
134
|
}
|
|
68
|
-
|
|
69
|
-
if (!quote || quote.chargeSats === 0) {
|
|
70
|
-
return res.status(200).json({
|
|
71
|
-
status: 'ok',
|
|
72
|
-
accountsEnabled: deps.config.enabled,
|
|
73
|
-
chargeSats: 0,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
const bsvPaymentHeader = req.headers['x-bsv-payment'];
|
|
77
|
-
if (bsvPaymentHeader === undefined) {
|
|
78
|
-
const derivationPrefix = await createNonce(deps.wallet);
|
|
79
|
-
return res
|
|
80
|
-
.status(402)
|
|
81
|
-
.set({
|
|
82
|
-
'x-bsv-payment-version': PAYMENT_VERSION,
|
|
83
|
-
'x-bsv-payment-satoshis-required': String(quote.chargeSats),
|
|
84
|
-
'x-bsv-payment-derivation-prefix': derivationPrefix,
|
|
85
|
-
})
|
|
86
|
-
.json({
|
|
87
|
-
status: 'error',
|
|
88
|
-
code: 'ERR_PAYMENT_REQUIRED',
|
|
89
|
-
satoshisRequired: quote.chargeSats,
|
|
90
|
-
description: 'BSV payment required. Retry with the X-BSV-Payment header.',
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
let paymentData;
|
|
94
|
-
try {
|
|
95
|
-
paymentData = JSON.parse(String(bsvPaymentHeader));
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
return res.status(400).json({
|
|
99
|
-
status: 'error',
|
|
100
|
-
code: 'ERR_MALFORMED_PAYMENT',
|
|
101
|
-
description: 'The X-BSV-Payment header is not valid JSON.',
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
try {
|
|
105
|
-
const valid = await verifyNonce(paymentData.derivationPrefix, deps.wallet);
|
|
106
|
-
if (!valid)
|
|
107
|
-
throw new Error('invalid nonce');
|
|
108
|
-
}
|
|
109
|
-
catch {
|
|
110
|
-
return res.status(400).json({
|
|
111
|
-
status: 'error',
|
|
112
|
-
code: 'ERR_INVALID_DERIVATION_PREFIX',
|
|
113
|
-
description: 'The X-BSV-Payment-Derivation-Prefix is not valid.',
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
try {
|
|
117
|
-
await deps.wallet.internalizeAction({
|
|
118
|
-
tx: Utils.toArray(paymentData.transaction, 'base64'),
|
|
119
|
-
outputs: [
|
|
120
|
-
{
|
|
121
|
-
paymentRemittance: {
|
|
122
|
-
derivationPrefix: paymentData.derivationPrefix,
|
|
123
|
-
derivationSuffix: paymentData.derivationSuffix,
|
|
124
|
-
senderIdentityKey: identityKey,
|
|
125
|
-
},
|
|
126
|
-
outputIndex: 0,
|
|
127
|
-
protocol: 'wallet payment',
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
labels: [
|
|
131
|
-
PAYMENT_LABEL,
|
|
132
|
-
payerLabel(identityKey),
|
|
133
|
-
bytesLabel(quote.bytesCovered),
|
|
134
|
-
blockLabel(quote.paidThroughBlock),
|
|
135
|
-
],
|
|
136
|
-
description: 'wallet-server storage payment',
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
catch (err) {
|
|
140
|
-
const code = err?.code ?? 'ERR_PAYMENT_FAILED';
|
|
141
|
-
const description = err?.message ?? 'Payment failed.';
|
|
142
|
-
return res.status(400).json({
|
|
143
|
-
status: 'error',
|
|
144
|
-
code,
|
|
145
|
-
description,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
return res
|
|
149
|
-
.status(200)
|
|
150
|
-
.set({
|
|
151
|
-
'x-bsv-payment-satoshis-paid': String(quote.chargeSats),
|
|
152
|
-
})
|
|
153
|
-
.json({
|
|
154
|
-
status: 'ok',
|
|
155
|
-
bytesCovered: quote.bytesCovered,
|
|
156
|
-
paidThroughBlock: quote.paidThroughBlock,
|
|
157
|
-
});
|
|
135
|
+
return res.status(200).json(await buildAccountStatus(deps, identityKey));
|
|
158
136
|
}
|
|
159
137
|
catch (err) {
|
|
160
138
|
next(err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paymentRoute.js","sourceRoot":"","sources":["../../src/accounts/paymentRoute.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"paymentRoute.js","sourceRoot":"","sources":["../../src/accounts/paymentRoute.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { createWalletServer } from './createWalletServer';
|
|
|
6
6
|
export type { WalletServerConfig, WalletServerHandle, } from './createWalletServer';
|
|
7
7
|
export { WalletServerClient } from './client';
|
|
8
8
|
export { topUpStorage } from './topUp';
|
|
9
|
-
export type {
|
|
9
|
+
export type { TopUpResult } from './topUp';
|
|
10
10
|
export { createWalletMonitor } from './createWalletMonitor';
|
|
11
11
|
export type { WalletMonitorConfig, WalletMonitorHandle, } from './createWalletMonitor';
|
|
12
12
|
export { BILLABLE_METHODS, dispatch, isBillableMethod } from './dispatch';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,YAAY,EACX,kBAAkB,EAClB,kBAAkB,GAClB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,YAAY,EACX,kBAAkB,EAClB,kBAAkB,GAClB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,YAAY,EACX,kBAAkB,EAClB,kBAAkB,GAClB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,YAAY,EACX,kBAAkB,EAClB,kBAAkB,GAClB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,YAAY,EACX,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACzE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEhE,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAE1B,YAAY,EACX,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,GACrB,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/topUp.d.ts
CHANGED
|
@@ -1,28 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Top-up helper: buys capacity on a remote wallet-server by
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Top-up helper: buys capacity on a remote wallet-server by POSTing to
|
|
3
|
+
* `{remoteUrl}/account/payment`. The endpoint issues a BRC-41 402 with
|
|
4
|
+
* price + derivation prefix; `AuthFetch` on the client side sees the 402
|
|
5
|
+
* and automatically attaches a BRC-29 payment (built from the caller's
|
|
6
|
+
* wallet) and retries. The server verifies, internalizes with the
|
|
7
|
+
* accounts-ledger labels, and returns 200 with the post-payment account
|
|
8
|
+
* status.
|
|
7
9
|
*
|
|
8
|
-
* Shared by the CLI `1sat remote topup` command
|
|
9
|
-
*
|
|
10
|
+
* Shared by the CLI `1sat remote topup` command. Also useful as a direct
|
|
11
|
+
* test harness for the payment endpoint independently of the sync path.
|
|
10
12
|
*/
|
|
11
13
|
import { type WalletInterface } from '@bsv/sdk';
|
|
12
14
|
import type { AccountStatusResponse } from './accounts/types';
|
|
13
|
-
import { WalletServerClient } from './client';
|
|
14
|
-
export interface TopUpOptions {
|
|
15
|
-
/** Buy this many purchase units. Defaults to `ceil(deficit / unitBytes)`. */
|
|
16
|
-
units?: number;
|
|
17
|
-
/** Reuse an existing client. Defaults to a fresh instance from `remoteUrl`. */
|
|
18
|
-
client?: WalletServerClient;
|
|
19
|
-
}
|
|
20
15
|
export interface TopUpResult {
|
|
21
|
-
|
|
16
|
+
/** Sats paid — reported by the server's `x-bsv-payment-satoshis-paid` header. */
|
|
22
17
|
satsPaid: number;
|
|
23
|
-
|
|
24
|
-
/** Account status after the payment is recorded. */
|
|
18
|
+
/** Post-payment account status. */
|
|
25
19
|
status: AccountStatusResponse;
|
|
26
20
|
}
|
|
27
|
-
export declare function topUpStorage(wallet: WalletInterface, remoteUrl: string
|
|
21
|
+
export declare function topUpStorage(wallet: WalletInterface, remoteUrl: string): Promise<TopUpResult>;
|
|
28
22
|
//# sourceMappingURL=topUp.d.ts.map
|
package/dist/topUp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"topUp.d.ts","sourceRoot":"","sources":["../src/topUp.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"topUp.d.ts","sourceRoot":"","sources":["../src/topUp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAE7D,MAAM,WAAW,WAAW;IAC3B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAA;IAChB,mCAAmC;IACnC,MAAM,EAAE,qBAAqB,CAAA;CAC7B;AAED,wBAAsB,YAAY,CACjC,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CAiCtB"}
|
package/dist/topUp.js
CHANGED
|
@@ -1,67 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Top-up helper: buys capacity on a remote wallet-server by
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Top-up helper: buys capacity on a remote wallet-server by POSTing to
|
|
3
|
+
* `{remoteUrl}/account/payment`. The endpoint issues a BRC-41 402 with
|
|
4
|
+
* price + derivation prefix; `AuthFetch` on the client side sees the 402
|
|
5
|
+
* and automatically attaches a BRC-29 payment (built from the caller's
|
|
6
|
+
* wallet) and retries. The server verifies, internalizes with the
|
|
7
|
+
* accounts-ledger labels, and returns 200 with the post-payment account
|
|
8
|
+
* status.
|
|
7
9
|
*
|
|
8
|
-
* Shared by the CLI `1sat remote topup` command
|
|
9
|
-
*
|
|
10
|
+
* Shared by the CLI `1sat remote topup` command. Also useful as a direct
|
|
11
|
+
* test harness for the payment endpoint independently of the sync path.
|
|
10
12
|
*/
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
import { AuthFetch } from '@bsv/sdk/auth';
|
|
14
|
+
export async function topUpStorage(wallet, remoteUrl) {
|
|
15
|
+
const authFetch = new AuthFetch(wallet);
|
|
16
|
+
const paymentUrl = joinUrl(remoteUrl, 'account/payment');
|
|
17
|
+
const response = await authFetch.fetch(paymentUrl, {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: { 'content-type': 'application/json' },
|
|
20
|
+
body: JSON.stringify({}),
|
|
21
|
+
});
|
|
22
|
+
const bodyText = await response.text();
|
|
23
|
+
let body;
|
|
24
|
+
try {
|
|
25
|
+
body = JSON.parse(bodyText);
|
|
20
26
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (units <= 0) {
|
|
24
|
-
throw new Error(`no topup needed — deficit is ${status.deficitBytes} bytes`);
|
|
27
|
+
catch {
|
|
28
|
+
throw new Error(`payment endpoint returned ${response.status} with non-JSON body: ${bodyText.slice(0, 200)}`);
|
|
25
29
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const { publicKey } = await wallet.getPublicKey({
|
|
30
|
-
protocolID: BRC29_PROTOCOL_ID,
|
|
31
|
-
keyID,
|
|
32
|
-
counterparty: status.serverIdentityKey,
|
|
33
|
-
forSelf: false,
|
|
34
|
-
});
|
|
35
|
-
const address = PublicKey.fromString(publicKey).toAddress();
|
|
36
|
-
const lockingScript = new P2PKH().lock(address).toHex();
|
|
37
|
-
const createResult = await wallet.createAction({
|
|
38
|
-
description: `wallet-server storage payment (${units} unit${units === 1 ? '' : 's'})`,
|
|
39
|
-
labels: [`account-payment:${status.serverIdentityKey}`],
|
|
40
|
-
outputs: [
|
|
41
|
-
{
|
|
42
|
-
lockingScript,
|
|
43
|
-
satoshis: sats,
|
|
44
|
-
outputDescription: 'storage capacity payment',
|
|
45
|
-
customInstructions: JSON.stringify({
|
|
46
|
-
derivationPrefix,
|
|
47
|
-
derivationSuffix,
|
|
48
|
-
}),
|
|
49
|
-
},
|
|
50
|
-
],
|
|
51
|
-
options: {
|
|
52
|
-
randomizeOutputs: false,
|
|
53
|
-
acceptDelayedBroadcast: false,
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
if (!createResult.txid) {
|
|
57
|
-
throw new Error('createAction did not return a broadcast txid');
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const errBody = body;
|
|
32
|
+
throw new Error(`payment endpoint returned ${response.status}: ${errBody.code ?? errBody.description ?? bodyText.slice(0, 200)}`);
|
|
58
33
|
}
|
|
59
|
-
const
|
|
34
|
+
const satsPaidHeader = response.headers.get('x-bsv-payment-satoshis-paid');
|
|
35
|
+
const satsPaid = satsPaidHeader ? Number(satsPaidHeader) : 0;
|
|
60
36
|
return {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
txid: createResult.txid,
|
|
64
|
-
status: newStatus,
|
|
37
|
+
satsPaid,
|
|
38
|
+
status: body,
|
|
65
39
|
};
|
|
66
40
|
}
|
|
41
|
+
function joinUrl(base, path) {
|
|
42
|
+
const b = base.endsWith('/') ? base.slice(0, -1) : base;
|
|
43
|
+
const p = path.startsWith('/') ? path.slice(1) : path;
|
|
44
|
+
return `${b}/${p}`;
|
|
45
|
+
}
|
|
67
46
|
//# sourceMappingURL=topUp.js.map
|
package/dist/topUp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"topUp.js","sourceRoot":"","sources":["../src/topUp.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"topUp.js","sourceRoot":"","sources":["../src/topUp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAUzC,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,MAAuB,EACvB,SAAiB;IAEjB,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAA;IACvC,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAA;IACxD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,UAAU,EAAE;QAClD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;KACxB,CAAC,CAAA;IAEF,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;IACtC,IAAI,IAAqE,CAAA;IACzE,IAAI,CAAC;QACJ,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC5B,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,KAAK,CACd,6BAA6B,QAAQ,CAAC,MAAM,wBAAwB,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC5F,CAAA;IACF,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,OAAO,GAAG,IAA+C,CAAA;QAC/D,MAAM,IAAI,KAAK,CACd,6BAA6B,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,WAAW,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAChH,CAAA;IACF,CAAC;IAED,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAA;IAC1E,MAAM,QAAQ,GAAG,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAE5D,OAAO;QACN,QAAQ;QACR,MAAM,EAAE,IAA6B;KACrC,CAAA;AACF,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,IAAY;IAC1C,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACvD,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACrD,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAA;AACnB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1sat/wallet-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "BRC-100 wallet storage RPC server for 1Sat Ordinals SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
],
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@1sat/client": "0.0.
|
|
35
|
-
"@1sat/wallet": "0.0.
|
|
34
|
+
"@1sat/client": "0.0.25",
|
|
35
|
+
"@1sat/wallet": "0.0.59",
|
|
36
36
|
"express": "^4.21.2"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@bsv/auth-express-middleware": "^2.0.4",
|
|
40
40
|
"@bsv/sdk": "^2.0.13",
|
|
41
|
-
"@bsv/wallet-toolbox": "npm:@bopen-io/wallet-toolbox@2.1.21-parity-fix.
|
|
41
|
+
"@bsv/wallet-toolbox": "npm:@bopen-io/wallet-toolbox@2.1.21-parity-fix.2",
|
|
42
42
|
"knex": "^3.1.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependenciesMeta": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@bsv/auth-express-middleware": "^2.0.4",
|
|
51
51
|
"@bsv/sdk": "^2.0.13",
|
|
52
|
-
"@bsv/wallet-toolbox": "npm:@bopen-io/wallet-toolbox@2.1.21-parity-fix.
|
|
52
|
+
"@bsv/wallet-toolbox": "npm:@bopen-io/wallet-toolbox@2.1.21-parity-fix.2",
|
|
53
53
|
"@types/bun": "^1.3.10",
|
|
54
54
|
"@types/express": "^4.17.21",
|
|
55
55
|
"knex": "^3.1.0",
|