mppx 0.4.12 → 0.5.1
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/CHANGELOG.md +12 -0
- package/dist/Expires.d.ts +7 -0
- package/dist/Expires.d.ts.map +1 -1
- package/dist/Expires.js +21 -0
- package/dist/Expires.js.map +1 -1
- package/dist/cli/account.d.ts.map +1 -1
- package/dist/cli/account.js +12 -2
- package/dist/cli/account.js.map +1 -1
- package/dist/server/Mppx.js +6 -5
- package/dist/server/Mppx.js.map +1 -1
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Charge.js +3 -3
- package/dist/stripe/server/Charge.js.map +1 -1
- package/dist/tempo/Methods.d.ts +3 -0
- package/dist/tempo/Methods.d.ts.map +1 -1
- package/dist/tempo/Methods.js +1 -0
- package/dist/tempo/Methods.js.map +1 -1
- package/dist/tempo/client/Charge.d.ts +3 -0
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +18 -2
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +3 -0
- package/dist/tempo/client/Methods.d.ts.map +1 -1
- package/dist/tempo/internal/proof.d.ts +29 -0
- package/dist/tempo/internal/proof.d.ts.map +1 -0
- package/dist/tempo/internal/proof.js +32 -0
- package/dist/tempo/internal/proof.js.map +1 -0
- package/dist/tempo/server/Charge.d.ts +11 -3
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +54 -4
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +3 -0
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Expires.ts +25 -0
- package/src/cli/account.ts +13 -2
- package/src/cli/cli.test.ts +230 -1
- package/src/middlewares/elysia.test.ts +130 -9
- package/src/middlewares/express.test.ts +123 -59
- package/src/middlewares/hono.test.ts +81 -39
- package/src/middlewares/nextjs.test.ts +162 -41
- package/src/server/Mppx.test.ts +86 -0
- package/src/server/Mppx.ts +5 -5
- package/src/stripe/server/Charge.ts +3 -7
- package/src/tempo/Methods.test.ts +26 -0
- package/src/tempo/Methods.ts +1 -0
- package/src/tempo/client/Charge.ts +26 -3
- package/src/tempo/internal/charge.test.ts +66 -0
- package/src/tempo/internal/proof.test.ts +83 -0
- package/src/tempo/internal/proof.ts +35 -0
- package/src/tempo/server/Charge.test.ts +660 -1
- package/src/tempo/server/Charge.ts +80 -5
- package/src/tempo/server/Session.test.ts +1123 -53
- package/src/tempo/server/internal/transport.test.ts +32 -0
- package/src/tempo/session/Chain.test.ts +35 -0
- package/src/tempo/session/Sse.test.ts +31 -0
|
@@ -4,12 +4,13 @@ import {
|
|
|
4
4
|
sendRawTransaction,
|
|
5
5
|
sendRawTransactionSync,
|
|
6
6
|
signTransaction,
|
|
7
|
+
verifyTypedData,
|
|
7
8
|
call as viem_call,
|
|
8
9
|
} from 'viem/actions'
|
|
9
10
|
import { tempo as tempo_chain } from 'viem/chains'
|
|
10
11
|
import { Abis, Transaction } from 'viem/tempo'
|
|
11
12
|
|
|
12
|
-
import
|
|
13
|
+
import * as Expires from '../../Expires.js'
|
|
13
14
|
import type { LooseOmit, NoExtraKeys } from '../../internal/types.js'
|
|
14
15
|
import * as Method from '../../Method.js'
|
|
15
16
|
import * as Store from '../../Store.js'
|
|
@@ -19,6 +20,7 @@ import * as TempoAddress from '../internal/address.js'
|
|
|
19
20
|
import * as Charge_internal from '../internal/charge.js'
|
|
20
21
|
import * as defaults from '../internal/defaults.js'
|
|
21
22
|
import * as FeePayer from '../internal/fee-payer.js'
|
|
23
|
+
import * as Proof from '../internal/proof.js'
|
|
22
24
|
import * as Selectors from '../internal/selectors.js'
|
|
23
25
|
import type * as types from '../internal/types.js'
|
|
24
26
|
import * as Methods from '../Methods.js'
|
|
@@ -49,6 +51,7 @@ export function charge<const parameters extends charge.Parameters>(
|
|
|
49
51
|
waitForConfirmation = true,
|
|
50
52
|
} = parameters
|
|
51
53
|
const store = (parameters.store ?? Store.memory()) as Store.Store<charge.StoreItemMap>
|
|
54
|
+
const proofStore = parameters.store as Store.Store<charge.StoreItemMap> | undefined
|
|
52
55
|
|
|
53
56
|
const { recipient, feePayer, feePayerUrl } = Account.resolve(parameters)
|
|
54
57
|
|
|
@@ -118,11 +121,15 @@ export function charge<const parameters extends charge.Parameters>(
|
|
|
118
121
|
const currency = challengeRequest.currency as `0x${string}`
|
|
119
122
|
const recipient = challengeRequest.recipient as `0x${string}`
|
|
120
123
|
|
|
121
|
-
|
|
124
|
+
Expires.assert(expires, challenge.id)
|
|
122
125
|
|
|
123
126
|
const memo = methodDetails?.memo as `0x${string}` | undefined
|
|
124
127
|
|
|
125
128
|
const payload = credential.payload
|
|
129
|
+
const isZeroAmount = BigInt(amount) === 0n
|
|
130
|
+
|
|
131
|
+
if (isZeroAmount && payload.type !== 'proof')
|
|
132
|
+
throw new MismatchError('Zero-amount challenges require a proof credential.', {})
|
|
126
133
|
|
|
127
134
|
switch (payload.type) {
|
|
128
135
|
case 'hash': {
|
|
@@ -142,6 +149,47 @@ export function charge<const parameters extends charge.Parameters>(
|
|
|
142
149
|
return toReceipt(receipt)
|
|
143
150
|
}
|
|
144
151
|
|
|
152
|
+
case 'proof': {
|
|
153
|
+
if (!isZeroAmount)
|
|
154
|
+
throw new MismatchError(
|
|
155
|
+
'Proof credentials are only valid for zero-amount challenges.',
|
|
156
|
+
{},
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
const expectedSource = credential.source
|
|
160
|
+
if (!expectedSource)
|
|
161
|
+
throw new MismatchError('Proof credential must include a source.', {})
|
|
162
|
+
|
|
163
|
+
const resolvedChainId = challenge.request.methodDetails?.chainId ?? chainId!
|
|
164
|
+
const source = Proof.parseProofSource(expectedSource)
|
|
165
|
+
|
|
166
|
+
if (!source || source.chainId !== resolvedChainId) {
|
|
167
|
+
throw new MismatchError('Proof credential source is invalid.', {})
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const valid = await verifyTypedData(client, {
|
|
171
|
+
address: source.address,
|
|
172
|
+
domain: Proof.domain(resolvedChainId),
|
|
173
|
+
types: Proof.types,
|
|
174
|
+
primaryType: 'Proof',
|
|
175
|
+
message: Proof.message(challenge.id),
|
|
176
|
+
signature: payload.signature as `0x${string}`,
|
|
177
|
+
})
|
|
178
|
+
if (!valid) throw new MismatchError('Proof signature does not match source.', {})
|
|
179
|
+
|
|
180
|
+
if (proofStore) {
|
|
181
|
+
await assertProofUnused(proofStore, challenge.id)
|
|
182
|
+
await markProofUsed(proofStore, challenge.id)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
method: 'tempo',
|
|
187
|
+
status: 'success',
|
|
188
|
+
timestamp: new Date().toISOString(),
|
|
189
|
+
reference: challenge.id,
|
|
190
|
+
} as const
|
|
191
|
+
}
|
|
192
|
+
|
|
145
193
|
case 'transaction': {
|
|
146
194
|
const serializedTransaction = payload.signature as Transaction.TransactionSerializedTempo
|
|
147
195
|
|
|
@@ -247,10 +295,15 @@ export declare namespace charge {
|
|
|
247
295
|
/** Testnet mode. */
|
|
248
296
|
testnet?: boolean | undefined
|
|
249
297
|
/**
|
|
250
|
-
* Store for
|
|
298
|
+
* Store for charge replay protection.
|
|
251
299
|
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
300
|
+
* Non-zero charge flows default to an in-memory store if omitted. For
|
|
301
|
+
* zero-dollar proof auth, replay prevention is enabled only when a store
|
|
302
|
+
* is explicitly provided; otherwise proofs remain reusable until the
|
|
303
|
+
* challenge expires.
|
|
304
|
+
*
|
|
305
|
+
* Use a shared store in multi-instance deployments so consumed hashes and
|
|
306
|
+
* proofs are visible across all server instances.
|
|
254
307
|
*/
|
|
255
308
|
store?: Store.Store | undefined
|
|
256
309
|
/**
|
|
@@ -466,6 +519,11 @@ function getHashStoreKey(hash: `0x${string}`): `mppx:charge:${string}` {
|
|
|
466
519
|
return `mppx:charge:${hash.toLowerCase()}`
|
|
467
520
|
}
|
|
468
521
|
|
|
522
|
+
/** @internal */
|
|
523
|
+
function getProofStoreKey(challengeId: string): `mppx:charge:${string}` {
|
|
524
|
+
return `mppx:charge:proof:${challengeId}`
|
|
525
|
+
}
|
|
526
|
+
|
|
469
527
|
/** @internal */
|
|
470
528
|
async function assertHashUnused(
|
|
471
529
|
store: Store.Store<charge.StoreItemMap>,
|
|
@@ -483,6 +541,23 @@ async function markHashUsed(
|
|
|
483
541
|
await store.put(getHashStoreKey(hash), Date.now())
|
|
484
542
|
}
|
|
485
543
|
|
|
544
|
+
/** @internal */
|
|
545
|
+
async function assertProofUnused(
|
|
546
|
+
store: Store.Store<charge.StoreItemMap>,
|
|
547
|
+
challengeId: string,
|
|
548
|
+
): Promise<void> {
|
|
549
|
+
const seen = await store.get(getProofStoreKey(challengeId))
|
|
550
|
+
if (seen !== null) throw new Error('Proof credential has already been used.')
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/** @internal */
|
|
554
|
+
async function markProofUsed(
|
|
555
|
+
store: Store.Store<charge.StoreItemMap>,
|
|
556
|
+
challengeId: string,
|
|
557
|
+
): Promise<void> {
|
|
558
|
+
await store.put(getProofStoreKey(challengeId), Date.now())
|
|
559
|
+
}
|
|
560
|
+
|
|
486
561
|
/** @internal */
|
|
487
562
|
function toReceipt(receipt: TransactionReceipt) {
|
|
488
563
|
const { status, transactionHash } = receipt
|