mppx 0.13.6 → 0.14.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 +20 -0
- package/dist/cli/cli.js +1 -1
- package/dist/internal/version.d.ts +2 -2
- package/dist/internal/version.js +1 -1
- package/dist/tempo/server/Methods.d.ts +7 -2
- package/dist/tempo/server/Methods.js +9 -2
- package/dist/tempo/session/precompile/Chain.d.ts +2 -2
- package/dist/tempo/session/precompile/Chain.js +3 -2
- package/dist/tempo/session/server/ChannelStore.d.ts +152 -3
- package/dist/tempo/session/server/ChannelStore.js +243 -3
- package/dist/tempo/session/server/CloseSupervisor.d.ts +110 -0
- package/dist/tempo/session/server/CloseSupervisor.js +310 -0
- package/dist/tempo/session/server/Session.d.ts +41 -2
- package/dist/tempo/session/server/Session.js +43 -4
- package/dist/tempo/session/server/Settlement.d.ts +9 -4
- package/dist/tempo/session/server/Settlement.js +107 -32
- package/dist/tempo/session/server/SponsorshipLimit.d.ts +52 -0
- package/dist/tempo/session/server/SponsorshipLimit.js +107 -0
- package/dist/tempo/session/server/index.d.ts +12 -2
- package/dist/tempo/session/server/index.js +7 -1
- package/package.json +1 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Address } from 'ox';
|
|
2
|
+
import type * as Store from '../../../Store.js';
|
|
3
|
+
import type { SessionCredentialPayload } from '../precompile/Protocol.js';
|
|
4
|
+
/**
|
|
5
|
+
* Sponsored opens, and separately sponsored top-ups, each verified payer may make per UTC day. It is
|
|
6
|
+
* tempoxyz/mpp-proxy's `DAILY_CHANNEL_OPEN_LIMIT`, the limit its proxy applied to sponsored channel
|
|
7
|
+
* opens before the limit moved into mppx.
|
|
8
|
+
*/
|
|
9
|
+
export declare const defaultPerDay = 5;
|
|
10
|
+
/** Limits on fee-payer-sponsored channel opens and top-ups. */
|
|
11
|
+
export type Parameters = {
|
|
12
|
+
/** Atomic store holding one usage record per payer and chain. */
|
|
13
|
+
store: Store.AtomicStore;
|
|
14
|
+
/** Sponsored opens, and separately sponsored top-ups, per payer per UTC day. @default 5 */
|
|
15
|
+
perDay?: number | undefined;
|
|
16
|
+
};
|
|
17
|
+
/** Validates a configured limit. */
|
|
18
|
+
export declare function assertParameters(parameters: Parameters | undefined): void;
|
|
19
|
+
/**
|
|
20
|
+
* Returns the verified signer of a serialized Tempo transaction, lowercased, or null. A
|
|
21
|
+
* credential's `source` is client-supplied and unverified at this point, and so are the 0x78
|
|
22
|
+
* explicit sender slot and a keychain signature's account, so limits key on what the signatures
|
|
23
|
+
* prove:
|
|
24
|
+
* - a direct signature: its signer;
|
|
25
|
+
* - an access key authorized in the same transaction: the account that signed the authorization,
|
|
26
|
+
* so fresh keys do not get a fresh quota;
|
|
27
|
+
* - an access key authorized earlier: the key itself (its account link is only checked on chain).
|
|
28
|
+
*/
|
|
29
|
+
export declare function verifiedPayer(serializedTransaction: string): Address.Address | null;
|
|
30
|
+
/**
|
|
31
|
+
* Records one sponsored `action` for `payer` on the current UTC day. Returns false, recording
|
|
32
|
+
* nothing, once the payer has used `perDay` of them.
|
|
33
|
+
*/
|
|
34
|
+
export declare function reserve(store: Store.AtomicStore, parameters: {
|
|
35
|
+
action: 'open' | 'topUp';
|
|
36
|
+
chainId: number;
|
|
37
|
+
payer: string;
|
|
38
|
+
perDay?: number | undefined;
|
|
39
|
+
now?: Date | undefined;
|
|
40
|
+
}): Promise<boolean>;
|
|
41
|
+
/**
|
|
42
|
+
* Returns `feePayer` when the credential's open or top-up may be sponsored, and `undefined` once
|
|
43
|
+
* its verified payer used the day's sponsored transactions, or when no signature proves a payer.
|
|
44
|
+
* The transaction is then broadcast as the payer signed it, paying its own fees.
|
|
45
|
+
*/
|
|
46
|
+
export declare function limitFeePayer<feePayer>(parameters: {
|
|
47
|
+
chainId: number;
|
|
48
|
+
feePayer: feePayer | undefined;
|
|
49
|
+
limit: Parameters | undefined;
|
|
50
|
+
payload: SessionCredentialPayload;
|
|
51
|
+
}): Promise<feePayer | undefined>;
|
|
52
|
+
//# sourceMappingURL=SponsorshipLimit.d.ts.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Address } from 'ox';
|
|
2
|
+
import { KeyAuthorization, SignatureEnvelope, TxEnvelopeTempo } from 'ox/tempo';
|
|
3
|
+
/**
|
|
4
|
+
* Sponsored opens, and separately sponsored top-ups, each verified payer may make per UTC day. It is
|
|
5
|
+
* tempoxyz/mpp-proxy's `DAILY_CHANNEL_OPEN_LIMIT`, the limit its proxy applied to sponsored channel
|
|
6
|
+
* opens before the limit moved into mppx.
|
|
7
|
+
*/
|
|
8
|
+
export const defaultPerDay = 5;
|
|
9
|
+
/** Validates a configured limit. */
|
|
10
|
+
export function assertParameters(parameters) {
|
|
11
|
+
const perDay = parameters?.perDay;
|
|
12
|
+
if (perDay !== undefined && (!Number.isSafeInteger(perDay) || perDay < 0))
|
|
13
|
+
throw new Error('`sponsorshipLimit.perDay` must be a non-negative integer.');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The address that provably produced `signature` over `payload`, or null. secp256k1 recovery is
|
|
17
|
+
* inherently verified; P256 and WebAuthn envelopes carry a public key, so their signature is
|
|
18
|
+
* checked against it. Multisig has no single signer.
|
|
19
|
+
*/
|
|
20
|
+
function verifiedSigner(payload, signature) {
|
|
21
|
+
if (signature.type === 'secp256k1')
|
|
22
|
+
return SignatureEnvelope.extractAddress({ payload, signature });
|
|
23
|
+
if (signature.type === 'p256' || signature.type === 'webAuthn') {
|
|
24
|
+
const { publicKey } = signature;
|
|
25
|
+
return SignatureEnvelope.verify(signature, { payload, publicKey })
|
|
26
|
+
? Address.fromPublicKey(publicKey)
|
|
27
|
+
: null;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Returns the verified signer of a serialized Tempo transaction, lowercased, or null. A
|
|
33
|
+
* credential's `source` is client-supplied and unverified at this point, and so are the 0x78
|
|
34
|
+
* explicit sender slot and a keychain signature's account, so limits key on what the signatures
|
|
35
|
+
* prove:
|
|
36
|
+
* - a direct signature: its signer;
|
|
37
|
+
* - an access key authorized in the same transaction: the account that signed the authorization,
|
|
38
|
+
* so fresh keys do not get a fresh quota;
|
|
39
|
+
* - an access key authorized earlier: the key itself (its account link is only checked on chain).
|
|
40
|
+
*/
|
|
41
|
+
export function verifiedPayer(serializedTransaction) {
|
|
42
|
+
if (!/^0x7[68][0-9a-fA-F]+$/.test(serializedTransaction))
|
|
43
|
+
return null;
|
|
44
|
+
try {
|
|
45
|
+
const { from: _claimedSender, ...fields } = TxEnvelopeTempo.deserialize(serializedTransaction);
|
|
46
|
+
const { signature } = fields;
|
|
47
|
+
if (!signature)
|
|
48
|
+
return null;
|
|
49
|
+
const envelope = TxEnvelopeTempo.from(fields);
|
|
50
|
+
if (signature.type !== 'keychain')
|
|
51
|
+
return lower(verifiedSigner(TxEnvelopeTempo.getSignPayload(envelope), signature));
|
|
52
|
+
// Keychain V2 signs over the account too: keccak256(0x04 || sigHash || userAddress).
|
|
53
|
+
const accessKey = verifiedSigner(TxEnvelopeTempo.getSignPayload(envelope, signature.version === 'v2' ? { from: signature.userAddress } : {}), signature.inner);
|
|
54
|
+
if (!accessKey)
|
|
55
|
+
return null;
|
|
56
|
+
const authorization = fields.keyAuthorization;
|
|
57
|
+
if (!authorization)
|
|
58
|
+
return lower(accessKey);
|
|
59
|
+
if (!authorization.signature || !Address.isEqual(authorization.address, accessKey))
|
|
60
|
+
return null;
|
|
61
|
+
return lower(verifiedSigner(KeyAuthorization.getSignPayload(authorization), authorization.signature));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function lower(address) {
|
|
68
|
+
return address ? address.toLowerCase() : null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Records one sponsored `action` for `payer` on the current UTC day. Returns false, recording
|
|
72
|
+
* nothing, once the payer has used `perDay` of them.
|
|
73
|
+
*/
|
|
74
|
+
export async function reserve(store, parameters) {
|
|
75
|
+
const { action, chainId, payer, perDay = defaultPerDay } = parameters;
|
|
76
|
+
const day = (parameters.now ?? new Date()).toISOString().slice(0, 10);
|
|
77
|
+
const key = `mppx:session:sponsorship:${chainId}:${payer.toLowerCase()}`;
|
|
78
|
+
return store.update(key, (current) => {
|
|
79
|
+
const usage = current?.day === day ? current : { day, open: 0, topUp: 0 };
|
|
80
|
+
if (usage[action] >= perDay)
|
|
81
|
+
return { op: 'noop', result: false };
|
|
82
|
+
return { op: 'set', value: { ...usage, [action]: usage[action] + 1 }, result: true };
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Returns `feePayer` when the credential's open or top-up may be sponsored, and `undefined` once
|
|
87
|
+
* its verified payer used the day's sponsored transactions, or when no signature proves a payer.
|
|
88
|
+
* The transaction is then broadcast as the payer signed it, paying its own fees.
|
|
89
|
+
*/
|
|
90
|
+
export async function limitFeePayer(parameters) {
|
|
91
|
+
const { chainId, feePayer, limit, payload } = parameters;
|
|
92
|
+
if (!feePayer || !limit)
|
|
93
|
+
return feePayer;
|
|
94
|
+
if (payload.action !== 'open' && payload.action !== 'topUp')
|
|
95
|
+
return feePayer;
|
|
96
|
+
const payer = verifiedPayer(payload.transaction);
|
|
97
|
+
if (!payer)
|
|
98
|
+
return undefined;
|
|
99
|
+
const allowed = await reserve(limit.store, {
|
|
100
|
+
action: payload.action,
|
|
101
|
+
chainId,
|
|
102
|
+
payer,
|
|
103
|
+
perDay: limit.perDay,
|
|
104
|
+
});
|
|
105
|
+
return allowed ? feePayer : undefined;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=SponsorshipLimit.js.map
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
export { charge, session, settle, settleBatch } from './Session.js';
|
|
1
|
+
export { charge, credit, session, settle, settleBatch } from './Session.js';
|
|
2
|
+
/** Close supervision for payer close requests on idle channels. */
|
|
3
|
+
export { superviseCloses } from './CloseSupervisor.js';
|
|
4
|
+
export type { CloseSupervision, CloseSupervisionOutcome, SuperviseClosesParameters, } from './CloseSupervisor.js';
|
|
5
|
+
/** Idempotent charge and credit options and outcomes, and the voucher a settlement captures. */
|
|
6
|
+
export { IdempotencyKeyConflictError, resolveSettleableVoucher } from './ChannelStore.js';
|
|
7
|
+
export type { ChargeOptions, CreditDeclineReason, CreditOptions, CreditResult, State as ChannelState, } from './ChannelStore.js';
|
|
8
|
+
/** Limits on fee-payer-sponsored channel opens and top-ups. */
|
|
9
|
+
export type { Parameters as SponsorshipLimitParameters } from './SponsorshipLimit.js';
|
|
2
10
|
/** SSE helpers and types for Tempo session streams. */
|
|
3
11
|
export * as Sse from './Sse.js';
|
|
4
|
-
/** Server-side automatic settlement schedule. */
|
|
12
|
+
/** Server-side automatic settlement schedule, and its raw-unit form `superviseCloses` takes. */
|
|
13
|
+
export { resolveSettlementSchedule } from './Settlement.js';
|
|
14
|
+
export type { ResolvedSettlementSchedule } from './Settlement.js';
|
|
5
15
|
export type { OnSessionSettlement, OnSessionSettlementFailure, ResolveSessionChannelId, ResolveSessionChannelIdParameters, SessionChannelIdRequest, SessionSettlementContext, SessionSettlementFailureContext, SettlementSchedule, } from './Session.js';
|
|
6
16
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
export { charge, session, settle, settleBatch } from './Session.js';
|
|
1
|
+
export { charge, credit, session, settle, settleBatch } from './Session.js';
|
|
2
|
+
/** Close supervision for payer close requests on idle channels. */
|
|
3
|
+
export { superviseCloses } from './CloseSupervisor.js';
|
|
4
|
+
/** Idempotent charge and credit options and outcomes, and the voucher a settlement captures. */
|
|
5
|
+
export { IdempotencyKeyConflictError, resolveSettleableVoucher } from './ChannelStore.js';
|
|
2
6
|
/** SSE helpers and types for Tempo session streams. */
|
|
3
7
|
export * as Sse from './Sse.js';
|
|
8
|
+
/** Server-side automatic settlement schedule, and its raw-unit form `superviseCloses` takes. */
|
|
9
|
+
export { resolveSettlementSchedule } from './Settlement.js';
|
|
4
10
|
//# sourceMappingURL=index.js.map
|