@pot-sdk2/pay 0.9.2 → 0.9.3

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/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ TransactionPolicy: () => TransactionPolicy,
23
24
  VERIFIER_PROFILES: () => VERIFIER_PROFILES,
24
25
  buildAttestationHeaders: () => buildAttestationHeaders,
25
26
  getProfile: () => getProfile,
@@ -295,8 +296,62 @@ function wrapClient(client, options) {
295
296
  };
296
297
  return wrapped;
297
298
  }
299
+
300
+ // src/transaction-policy.ts
301
+ var TransactionPolicy = class {
302
+ config;
303
+ dailySpend = /* @__PURE__ */ new Map();
304
+ constructor(config = {}) {
305
+ this.config = {
306
+ requireVerificationAbove: 50,
307
+ ...config
308
+ };
309
+ }
310
+ check(tx) {
311
+ const { to, amount } = tx;
312
+ const threshold = this.config.requireVerificationAbove ?? 50;
313
+ const requiresVerification = amount >= threshold;
314
+ if (this.config.blockedAddresses?.length) {
315
+ const toLower = to.toLowerCase();
316
+ if (this.config.blockedAddresses.some((a) => a.toLowerCase() === toLower)) {
317
+ return { allowed: false, reason: `Address ${to} is blocked`, requiresVerification };
318
+ }
319
+ }
320
+ if (this.config.allowedAddresses?.length) {
321
+ const toLower = to.toLowerCase();
322
+ if (!this.config.allowedAddresses.some((a) => a.toLowerCase() === toLower)) {
323
+ return { allowed: false, reason: `Address ${to} is not in allowedAddresses`, requiresVerification };
324
+ }
325
+ }
326
+ if (this.config.maxPerTransaction !== void 0 && amount > this.config.maxPerTransaction) {
327
+ return {
328
+ allowed: false,
329
+ reason: `Amount $${amount} exceeds maxPerTransaction ($${this.config.maxPerTransaction})`,
330
+ requiresVerification
331
+ };
332
+ }
333
+ if (this.config.dailyCap !== void 0) {
334
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
335
+ const spent = this.dailySpend.get(today) ?? 0;
336
+ if (spent + amount > this.config.dailyCap) {
337
+ return {
338
+ allowed: false,
339
+ reason: `Daily cap ($${this.config.dailyCap}) would be exceeded. Already spent: $${spent}`,
340
+ requiresVerification
341
+ };
342
+ }
343
+ this.dailySpend.set(today, spent + amount);
344
+ }
345
+ return { allowed: true, requiresVerification };
346
+ }
347
+ /** Reset daily spend tracking (useful for testing) */
348
+ resetDailySpend() {
349
+ this.dailySpend.clear();
350
+ }
351
+ };
298
352
  // Annotate the CommonJS export names for ESM import in node:
299
353
  0 && (module.exports = {
354
+ TransactionPolicy,
300
355
  VERIFIER_PROFILES,
301
356
  buildAttestationHeaders,
302
357
  getProfile,
package/dist/index.d.cts CHANGED
@@ -101,6 +101,39 @@ interface PolicyResult {
101
101
  }
102
102
  declare function resolvePolicy(amount: number, policy?: 'tiered' | 'always' | 'skip'): PolicyResult;
103
103
 
104
+ /**
105
+ * TransactionPolicy — spending limits, address allowlists, verification thresholds
106
+ * @pot-sdk2/pay v0.9.3
107
+ */
108
+ interface TransactionPolicyConfig {
109
+ /** Max USD per single transaction */
110
+ maxPerTransaction?: number;
111
+ /** Max USD spent per calendar day */
112
+ dailyCap?: number;
113
+ /** If set, only these addresses are allowed (case-insensitive) */
114
+ allowedAddresses?: string[];
115
+ /** Always blocked addresses (case-insensitive) */
116
+ blockedAddresses?: string[];
117
+ /** Require reasoning verification above this USD amount (default: 50) */
118
+ requireVerificationAbove?: number;
119
+ }
120
+ interface PolicyCheckResult {
121
+ allowed: boolean;
122
+ reason?: string;
123
+ requiresVerification: boolean;
124
+ }
125
+ declare class TransactionPolicy {
126
+ private config;
127
+ private dailySpend;
128
+ constructor(config?: TransactionPolicyConfig);
129
+ check(tx: {
130
+ to: string;
131
+ amount: number;
132
+ }): PolicyCheckResult;
133
+ /** Reset daily spend tracking (useful for testing) */
134
+ resetDailySpend(): void;
135
+ }
136
+
104
137
  /**
105
138
  * Generates X-402-Attestation-* headers from a verify result.
106
139
  * These headers can be attached to x402 payment requests.
@@ -172,4 +205,4 @@ declare function warnIfNoHighPerformanceVerifier(modelIds: string[]): string | n
172
205
  */
173
206
  declare function getWeight(modelId: string): number;
174
207
 
175
- export { type PayVerifyOptions, type PayVerifyResult, type PayWrapOptions, type PaymentIntent, VERIFIER_PROFILES, type VerifierProfile, buildAttestationHeaders, getProfile, getRecommendedVerifiers, getWeight, resolvePolicy, verifyPayment, warnIfNoHighPerformanceVerifier, wrapClient };
208
+ export { type PayVerifyOptions, type PayVerifyResult, type PayWrapOptions, type PaymentIntent, type PolicyCheckResult, TransactionPolicy, type TransactionPolicyConfig, VERIFIER_PROFILES, type VerifierProfile, buildAttestationHeaders, getProfile, getRecommendedVerifiers, getWeight, resolvePolicy, verifyPayment, warnIfNoHighPerformanceVerifier, wrapClient };
package/dist/index.d.ts CHANGED
@@ -101,6 +101,39 @@ interface PolicyResult {
101
101
  }
102
102
  declare function resolvePolicy(amount: number, policy?: 'tiered' | 'always' | 'skip'): PolicyResult;
103
103
 
104
+ /**
105
+ * TransactionPolicy — spending limits, address allowlists, verification thresholds
106
+ * @pot-sdk2/pay v0.9.3
107
+ */
108
+ interface TransactionPolicyConfig {
109
+ /** Max USD per single transaction */
110
+ maxPerTransaction?: number;
111
+ /** Max USD spent per calendar day */
112
+ dailyCap?: number;
113
+ /** If set, only these addresses are allowed (case-insensitive) */
114
+ allowedAddresses?: string[];
115
+ /** Always blocked addresses (case-insensitive) */
116
+ blockedAddresses?: string[];
117
+ /** Require reasoning verification above this USD amount (default: 50) */
118
+ requireVerificationAbove?: number;
119
+ }
120
+ interface PolicyCheckResult {
121
+ allowed: boolean;
122
+ reason?: string;
123
+ requiresVerification: boolean;
124
+ }
125
+ declare class TransactionPolicy {
126
+ private config;
127
+ private dailySpend;
128
+ constructor(config?: TransactionPolicyConfig);
129
+ check(tx: {
130
+ to: string;
131
+ amount: number;
132
+ }): PolicyCheckResult;
133
+ /** Reset daily spend tracking (useful for testing) */
134
+ resetDailySpend(): void;
135
+ }
136
+
104
137
  /**
105
138
  * Generates X-402-Attestation-* headers from a verify result.
106
139
  * These headers can be attached to x402 payment requests.
@@ -172,4 +205,4 @@ declare function warnIfNoHighPerformanceVerifier(modelIds: string[]): string | n
172
205
  */
173
206
  declare function getWeight(modelId: string): number;
174
207
 
175
- export { type PayVerifyOptions, type PayVerifyResult, type PayWrapOptions, type PaymentIntent, VERIFIER_PROFILES, type VerifierProfile, buildAttestationHeaders, getProfile, getRecommendedVerifiers, getWeight, resolvePolicy, verifyPayment, warnIfNoHighPerformanceVerifier, wrapClient };
208
+ export { type PayVerifyOptions, type PayVerifyResult, type PayWrapOptions, type PaymentIntent, type PolicyCheckResult, TransactionPolicy, type TransactionPolicyConfig, VERIFIER_PROFILES, type VerifierProfile, buildAttestationHeaders, getProfile, getRecommendedVerifiers, getWeight, resolvePolicy, verifyPayment, warnIfNoHighPerformanceVerifier, wrapClient };
package/dist/index.js CHANGED
@@ -261,7 +261,61 @@ function wrapClient(client, options) {
261
261
  };
262
262
  return wrapped;
263
263
  }
264
+
265
+ // src/transaction-policy.ts
266
+ var TransactionPolicy = class {
267
+ config;
268
+ dailySpend = /* @__PURE__ */ new Map();
269
+ constructor(config = {}) {
270
+ this.config = {
271
+ requireVerificationAbove: 50,
272
+ ...config
273
+ };
274
+ }
275
+ check(tx) {
276
+ const { to, amount } = tx;
277
+ const threshold = this.config.requireVerificationAbove ?? 50;
278
+ const requiresVerification = amount >= threshold;
279
+ if (this.config.blockedAddresses?.length) {
280
+ const toLower = to.toLowerCase();
281
+ if (this.config.blockedAddresses.some((a) => a.toLowerCase() === toLower)) {
282
+ return { allowed: false, reason: `Address ${to} is blocked`, requiresVerification };
283
+ }
284
+ }
285
+ if (this.config.allowedAddresses?.length) {
286
+ const toLower = to.toLowerCase();
287
+ if (!this.config.allowedAddresses.some((a) => a.toLowerCase() === toLower)) {
288
+ return { allowed: false, reason: `Address ${to} is not in allowedAddresses`, requiresVerification };
289
+ }
290
+ }
291
+ if (this.config.maxPerTransaction !== void 0 && amount > this.config.maxPerTransaction) {
292
+ return {
293
+ allowed: false,
294
+ reason: `Amount $${amount} exceeds maxPerTransaction ($${this.config.maxPerTransaction})`,
295
+ requiresVerification
296
+ };
297
+ }
298
+ if (this.config.dailyCap !== void 0) {
299
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
300
+ const spent = this.dailySpend.get(today) ?? 0;
301
+ if (spent + amount > this.config.dailyCap) {
302
+ return {
303
+ allowed: false,
304
+ reason: `Daily cap ($${this.config.dailyCap}) would be exceeded. Already spent: $${spent}`,
305
+ requiresVerification
306
+ };
307
+ }
308
+ this.dailySpend.set(today, spent + amount);
309
+ }
310
+ return { allowed: true, requiresVerification };
311
+ }
312
+ /** Reset daily spend tracking (useful for testing) */
313
+ resetDailySpend() {
314
+ this.dailySpend.clear();
315
+ }
316
+ };
264
317
  export {
318
+ TransactionPolicy,
265
319
  VERIFIER_PROFILES,
266
320
  buildAttestationHeaders,
267
321
  getProfile,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pot-sdk2/pay",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "Payment reasoning verification for pot-sdk — x402 attestation layer",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -13,7 +13,9 @@
13
13
  "types": "./dist/index.d.ts"
14
14
  }
15
15
  },
16
- "engines": { "node": ">=22.5.0" },
16
+ "engines": {
17
+ "node": ">=22.5.0"
18
+ },
17
19
  "scripts": {
18
20
  "build": "tsup src/index.ts --format esm,cjs --dts",
19
21
  "test": "node --experimental-sqlite --import tsx/esm tests/pay.test.ts",
@@ -29,7 +31,14 @@
29
31
  "typescript": "^5.0.0"
30
32
  },
31
33
  "license": "MIT",
32
- "keywords": ["thoughtproof", "pot-sdk", "x402", "agent-payments", "verification", "attestation"],
34
+ "keywords": [
35
+ "thoughtproof",
36
+ "pot-sdk",
37
+ "x402",
38
+ "agent-payments",
39
+ "verification",
40
+ "attestation"
41
+ ],
33
42
  "homepage": "https://thoughtproof.ai",
34
43
  "repository": {
35
44
  "type": "git",
package/src/index.ts CHANGED
@@ -29,6 +29,8 @@
29
29
  export { verifyPayment } from './verify-payment.js';
30
30
  export { wrapClient } from './middleware.js';
31
31
  export { resolvePolicy } from './policy.js';
32
+ export { TransactionPolicy } from './transaction-policy.js';
33
+ export type { TransactionPolicyConfig, PolicyCheckResult } from './transaction-policy.js';
32
34
  export { buildAttestationHeaders } from './headers.js';
33
35
  export {
34
36
  VERIFIER_PROFILES,
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { TransactionPolicy } from './transaction-policy.js';
3
+
4
+ describe('TransactionPolicy', () => {
5
+ const ATTACKER = '0xdead000000000000000000000000000000001337';
6
+ const SAFE = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
7
+
8
+ it('blocks tx above maxPerTransaction', () => {
9
+ const policy = new TransactionPolicy({ maxPerTransaction: 100 });
10
+ const result = policy.check({ to: SAFE, amount: 101 });
11
+ expect(result.allowed).toBe(false);
12
+ expect(result.reason).toContain('maxPerTransaction');
13
+ });
14
+
15
+ it('allows tx within maxPerTransaction', () => {
16
+ const policy = new TransactionPolicy({ maxPerTransaction: 100 });
17
+ const result = policy.check({ to: SAFE, amount: 99 });
18
+ expect(result.allowed).toBe(true);
19
+ });
20
+
21
+ it('blocks unknown address when allowedAddresses is set', () => {
22
+ const policy = new TransactionPolicy({ allowedAddresses: [SAFE] });
23
+ const result = policy.check({ to: ATTACKER, amount: 10 });
24
+ expect(result.allowed).toBe(false);
25
+ expect(result.reason).toContain('allowedAddresses');
26
+ });
27
+
28
+ it('allows known address when allowedAddresses is set', () => {
29
+ const policy = new TransactionPolicy({ allowedAddresses: [SAFE] });
30
+ const result = policy.check({ to: SAFE, amount: 10 });
31
+ expect(result.allowed).toBe(true);
32
+ });
33
+
34
+ it('blocks when dailyCap exceeded', () => {
35
+ const policy = new TransactionPolicy({ dailyCap: 100 });
36
+ policy.check({ to: SAFE, amount: 80 }); // first tx — OK, records spend
37
+ const result = policy.check({ to: SAFE, amount: 30 }); // 80+30=110 > 100
38
+ expect(result.allowed).toBe(false);
39
+ expect(result.reason).toContain('Daily cap');
40
+ });
41
+
42
+ it('sets requiresVerification=true above threshold', () => {
43
+ const policy = new TransactionPolicy({ requireVerificationAbove: 50 });
44
+ const below = policy.check({ to: SAFE, amount: 49 });
45
+ const above = policy.check({ to: SAFE, amount: 51 });
46
+ expect(below.requiresVerification).toBe(false);
47
+ expect(above.requiresVerification).toBe(true);
48
+ });
49
+
50
+ it('blocks blockedAddresses', () => {
51
+ const policy = new TransactionPolicy({ blockedAddresses: [ATTACKER] });
52
+ const result = policy.check({ to: ATTACKER, amount: 1 });
53
+ expect(result.allowed).toBe(false);
54
+ expect(result.reason).toContain('blocked');
55
+ });
56
+ });
@@ -0,0 +1,88 @@
1
+ /**
2
+ * TransactionPolicy — spending limits, address allowlists, verification thresholds
3
+ * @pot-sdk2/pay v0.9.3
4
+ */
5
+
6
+ export interface TransactionPolicyConfig {
7
+ /** Max USD per single transaction */
8
+ maxPerTransaction?: number;
9
+ /** Max USD spent per calendar day */
10
+ dailyCap?: number;
11
+ /** If set, only these addresses are allowed (case-insensitive) */
12
+ allowedAddresses?: string[];
13
+ /** Always blocked addresses (case-insensitive) */
14
+ blockedAddresses?: string[];
15
+ /** Require reasoning verification above this USD amount (default: 50) */
16
+ requireVerificationAbove?: number;
17
+ }
18
+
19
+ export interface PolicyCheckResult {
20
+ allowed: boolean;
21
+ reason?: string;
22
+ requiresVerification: boolean;
23
+ }
24
+
25
+ export class TransactionPolicy {
26
+ private config: TransactionPolicyConfig;
27
+ private dailySpend: Map<string, number> = new Map();
28
+
29
+ constructor(config: TransactionPolicyConfig = {}) {
30
+ this.config = {
31
+ requireVerificationAbove: 50,
32
+ ...config,
33
+ };
34
+ }
35
+
36
+ check(tx: { to: string; amount: number }): PolicyCheckResult {
37
+ const { to, amount } = tx;
38
+ const threshold = this.config.requireVerificationAbove ?? 50;
39
+ const requiresVerification = amount >= threshold;
40
+
41
+ // 1. Blocked addresses
42
+ if (this.config.blockedAddresses?.length) {
43
+ const toLower = to.toLowerCase();
44
+ if (this.config.blockedAddresses.some(a => a.toLowerCase() === toLower)) {
45
+ return { allowed: false, reason: `Address ${to} is blocked`, requiresVerification };
46
+ }
47
+ }
48
+
49
+ // 2. Allowlist check
50
+ if (this.config.allowedAddresses?.length) {
51
+ const toLower = to.toLowerCase();
52
+ if (!this.config.allowedAddresses.some(a => a.toLowerCase() === toLower)) {
53
+ return { allowed: false, reason: `Address ${to} is not in allowedAddresses`, requiresVerification };
54
+ }
55
+ }
56
+
57
+ // 3. Per-transaction limit
58
+ if (this.config.maxPerTransaction !== undefined && amount > this.config.maxPerTransaction) {
59
+ return {
60
+ allowed: false,
61
+ reason: `Amount $${amount} exceeds maxPerTransaction ($${this.config.maxPerTransaction})`,
62
+ requiresVerification,
63
+ };
64
+ }
65
+
66
+ // 4. Daily cap
67
+ if (this.config.dailyCap !== undefined) {
68
+ const today = new Date().toISOString().slice(0, 10);
69
+ const spent = this.dailySpend.get(today) ?? 0;
70
+ if (spent + amount > this.config.dailyCap) {
71
+ return {
72
+ allowed: false,
73
+ reason: `Daily cap ($${this.config.dailyCap}) would be exceeded. Already spent: $${spent}`,
74
+ requiresVerification,
75
+ };
76
+ }
77
+ // Record spend
78
+ this.dailySpend.set(today, spent + amount);
79
+ }
80
+
81
+ return { allowed: true, requiresVerification };
82
+ }
83
+
84
+ /** Reset daily spend tracking (useful for testing) */
85
+ resetDailySpend(): void {
86
+ this.dailySpend.clear();
87
+ }
88
+ }