payid 0.3.1 → 0.3.2

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.d.ts CHANGED
@@ -1 +1,107 @@
1
- export { PayID } from "./payid";
1
+ import { RuleContext, RuleConfig, RuleResult } from 'payid-types';
2
+ import { ethers } from 'ethers';
3
+
4
+ type DecisionValue = 0 | 1;
5
+ interface DecisionPayload {
6
+ version: "payid.decision.v1";
7
+ payId: string;
8
+ owner: string;
9
+ decision: DecisionValue;
10
+ contextHash: string;
11
+ ruleSetHash: string;
12
+ issuedAt: number;
13
+ expiresAt: number;
14
+ nonce: string;
15
+ }
16
+ interface DecisionProof {
17
+ payload: DecisionPayload;
18
+ signature: string;
19
+ }
20
+
21
+ interface RuleSource {
22
+ uri: string;
23
+ hash?: string;
24
+ }
25
+
26
+ interface UserOperation {
27
+ sender: string;
28
+ nonce: string;
29
+ initCode: string;
30
+ callData: string;
31
+ callGasLimit: string;
32
+ verificationGasLimit: string;
33
+ preVerificationGas: string;
34
+ maxFeePerGas: string;
35
+ maxPriorityFeePerGas: string;
36
+ paymasterAndData: string;
37
+ signature: string;
38
+ }
39
+
40
+ declare class PayID {
41
+ private wasm;
42
+ constructor(wasmPath: string);
43
+ evaluate(context: RuleContext, ruleConfig: RuleConfig): Promise<RuleResult>;
44
+ evaluateAndProve(params: {
45
+ context: RuleContext;
46
+ ruleConfig: RuleConfig;
47
+ payId: string;
48
+ owner: string;
49
+ signer: ethers.Signer;
50
+ chainId: number;
51
+ verifyingContract: string;
52
+ ttlSeconds?: number;
53
+ }): Promise<{
54
+ result: RuleResult;
55
+ proof: DecisionProof;
56
+ }>;
57
+ evaluateWithRuleSource(context: RuleContext, ruleSource: RuleSource): Promise<RuleResult>;
58
+ evaluateAndProveFromSource(params: {
59
+ context: RuleContext;
60
+ ruleSource: RuleSource;
61
+ payId: string;
62
+ owner: string;
63
+ signer: ethers.Signer;
64
+ chainId: number;
65
+ verifyingContract: string;
66
+ ttlSeconds?: number;
67
+ }): Promise<{
68
+ result: RuleResult;
69
+ proof: DecisionProof;
70
+ } | {
71
+ result: {
72
+ decision: string;
73
+ code: string;
74
+ reason: any;
75
+ };
76
+ proof: null;
77
+ }>;
78
+ evaluateProveAndBuildUserOp(params: {
79
+ context: RuleContext;
80
+ ruleSource: {
81
+ uri: string;
82
+ hash?: string;
83
+ };
84
+ payId: string;
85
+ owner: string;
86
+ signer: ethers.Signer;
87
+ smartAccount: string;
88
+ targetContract: string;
89
+ nonce: string;
90
+ gas: {
91
+ callGasLimit: string;
92
+ verificationGasLimit: string;
93
+ preVerificationGas: string;
94
+ maxFeePerGas: string;
95
+ maxPriorityFeePerGas: string;
96
+ };
97
+ paymasterAndData?: string;
98
+ chainId: number;
99
+ verifyingContract: string;
100
+ }): Promise<{
101
+ result: RuleResult;
102
+ userOp: UserOperation | null;
103
+ proof: any;
104
+ }>;
105
+ }
106
+
107
+ export { PayID };
package/dist/index.js CHANGED
@@ -1 +1,314 @@
1
- export { PayID } from "./payid";
1
+ // src/evaluate.ts
2
+ import { executeRule } from "payid-rule-engine";
3
+
4
+ // src/normalize.ts
5
+ function normalizeContext(ctx) {
6
+ return {
7
+ ...ctx,
8
+ tx: {
9
+ ...ctx.tx,
10
+ sender: ctx.tx.sender?.toLowerCase(),
11
+ receiver: ctx.tx.receiver?.toLowerCase(),
12
+ asset: ctx.tx.asset.toUpperCase()
13
+ }
14
+ };
15
+ }
16
+
17
+ // src/evaluate.ts
18
+ import { preprocessContextV2 } from "payid-rule-engine";
19
+ async function evaluate(wasmBinary, context, ruleConfig, options) {
20
+ if (!context || typeof context !== "object") {
21
+ throw new Error("evaluate(): context is required");
22
+ }
23
+ if (!context.tx) {
24
+ throw new Error("evaluate(): context.tx is required");
25
+ }
26
+ if (!ruleConfig || typeof ruleConfig !== "object") {
27
+ throw new Error("evaluate(): ruleConfig is required");
28
+ }
29
+ let result;
30
+ try {
31
+ const preparedContext = options?.trustedIssuers ? preprocessContextV2(context, ruleConfig, options.trustedIssuers) : context;
32
+ const normalized = normalizeContext(preparedContext);
33
+ result = await executeRule(wasmBinary, normalized, ruleConfig);
34
+ } catch (err) {
35
+ return {
36
+ decision: "REJECT",
37
+ code: "CONTEXT_OR_ENGINE_ERROR",
38
+ reason: err?.message ?? "rule evaluation failed"
39
+ };
40
+ }
41
+ if (result.decision !== "ALLOW" && result.decision !== "REJECT") {
42
+ return {
43
+ decision: "REJECT",
44
+ code: "INVALID_ENGINE_OUTPUT",
45
+ reason: "invalid decision value"
46
+ };
47
+ }
48
+ return {
49
+ decision: result.decision,
50
+ code: result.code || "UNKNOWN",
51
+ reason: result.reason
52
+ };
53
+ }
54
+
55
+ // src/decision-proof/generate.ts
56
+ import { randomBytes } from "crypto";
57
+
58
+ // src/decision-proof/hash.ts
59
+ import { keccak256 } from "ethers";
60
+ function stableStringify(obj) {
61
+ if (Array.isArray(obj)) {
62
+ return `[${obj.map(stableStringify).join(",")}]`;
63
+ }
64
+ if (obj && typeof obj === "object") {
65
+ return `{${Object.keys(obj).sort().map(
66
+ (k) => `"${k}":${stableStringify(obj[k])}`
67
+ ).join(",")}}`;
68
+ }
69
+ return JSON.stringify(obj);
70
+ }
71
+ function hashContext(context) {
72
+ return keccak256(Buffer.from(stableStringify(context)));
73
+ }
74
+ function hashRuleSet(ruleConfig) {
75
+ return keccak256(Buffer.from(stableStringify(ruleConfig)));
76
+ }
77
+
78
+ // src/decision-proof/sign.ts
79
+ import "ethers";
80
+ async function signDecision(signer, chainId, verifyingContract, payload) {
81
+ const domain = {
82
+ name: "PAY.ID Decision",
83
+ version: "1",
84
+ chainId,
85
+ verifyingContract
86
+ };
87
+ const types = {
88
+ Decision: [
89
+ { name: "version", type: "string" },
90
+ { name: "payId", type: "string" },
91
+ { name: "owner", type: "address" },
92
+ { name: "decision", type: "uint8" },
93
+ { name: "contextHash", type: "bytes32" },
94
+ { name: "ruleSetHash", type: "bytes32" },
95
+ { name: "issuedAt", type: "uint64" },
96
+ { name: "expiresAt", type: "uint64" },
97
+ { name: "nonce", type: "bytes32" }
98
+ ]
99
+ };
100
+ if (typeof signer.signTypedData === "function") {
101
+ return await signer.signTypedData(domain, types, payload);
102
+ }
103
+ if (typeof signer._signTypedData === "function") {
104
+ return await signer._signTypedData(domain, types, payload);
105
+ }
106
+ throw new Error(
107
+ "Signer does not support EIP-712 signing (signTypedData)"
108
+ );
109
+ }
110
+
111
+ // src/decision-proof/generate.ts
112
+ import "ethers";
113
+ async function generateDecisionProof(params) {
114
+ const issuedAt = Math.floor(Date.now() / 1e3);
115
+ const expiresAt = issuedAt + (params.ttlSeconds ?? 60);
116
+ const payload = {
117
+ version: "payid.decision.v1",
118
+ payId: params.payId,
119
+ owner: params.owner,
120
+ decision: params.decision === "ALLOW" ? 1 : 0,
121
+ contextHash: hashContext(params.context),
122
+ ruleSetHash: hashRuleSet(params.ruleConfig),
123
+ issuedAt,
124
+ expiresAt,
125
+ nonce: `0x${randomBytes(32).toString("hex")}`
126
+ };
127
+ const signature = await signDecision(
128
+ params.signer,
129
+ params.chainId,
130
+ params.verifyingContract,
131
+ payload
132
+ );
133
+ return { payload, signature };
134
+ }
135
+
136
+ // src/resolver/utils.ts
137
+ import { keccak256 as keccak2562 } from "ethers";
138
+ function verifyHash(content, expectedHash) {
139
+ if (!expectedHash) return;
140
+ const actual = keccak2562(Buffer.from(content));
141
+ if (actual !== expectedHash) {
142
+ throw new Error("RULE_HASH_MISMATCH");
143
+ }
144
+ }
145
+
146
+ // src/resolver/http.ts
147
+ async function resolveHttpRule(uri, expectedHash) {
148
+ const res = await fetch(uri);
149
+ if (!res.ok) {
150
+ throw new Error(`HTTP_RULE_FETCH_FAILED: ${res.status}`);
151
+ }
152
+ const text = await res.text();
153
+ verifyHash(text, expectedHash);
154
+ return JSON.parse(text);
155
+ }
156
+
157
+ // src/resolver/ipfs.ts
158
+ var DEFAULT_GATEWAY = "https://ipfs.io/ipfs/";
159
+ async function resolveIpfsRule(uri, expectedHash, gateway = DEFAULT_GATEWAY) {
160
+ const cid = uri.replace("ipfs://", "");
161
+ const url = `${gateway}${cid}`;
162
+ const res = await fetch(url);
163
+ if (!res.ok) {
164
+ throw new Error(`IPFS_RULE_FETCH_FAILED: ${res.status}`);
165
+ }
166
+ const text = await res.text();
167
+ verifyHash(text, expectedHash);
168
+ return JSON.parse(text);
169
+ }
170
+
171
+ // src/resolver/resolver.ts
172
+ async function resolveRule(source) {
173
+ const { uri, hash } = source;
174
+ let config;
175
+ if (uri.startsWith("ipfs://")) {
176
+ config = await resolveIpfsRule(uri, hash);
177
+ } else if (uri.startsWith("http://") || uri.startsWith("https://")) {
178
+ config = await resolveHttpRule(uri, hash);
179
+ } else {
180
+ throw new Error("UNSUPPORTED_RULE_URI");
181
+ }
182
+ return {
183
+ config,
184
+ source
185
+ };
186
+ }
187
+
188
+ // src/payid.ts
189
+ import "ethers";
190
+ import * as fs from "fs";
191
+
192
+ // src/erc4337/build.ts
193
+ import { ethers as ethers3 } from "ethers";
194
+ function buildPayCallData(contractAddress, proof) {
195
+ const iface = new ethers3.Interface([
196
+ "function pay(bytes payload, bytes signature)"
197
+ ]);
198
+ return iface.encodeFunctionData("pay", [
199
+ ethers3.toUtf8Bytes(JSON.stringify(proof.payload)),
200
+ proof.signature
201
+ ]);
202
+ }
203
+
204
+ // src/erc4337/userop.ts
205
+ function buildUserOperation(params) {
206
+ return {
207
+ sender: params.sender,
208
+ nonce: params.nonce,
209
+ initCode: params.initCode ?? "0x",
210
+ callData: params.callData,
211
+ callGasLimit: params.gas.callGasLimit,
212
+ verificationGasLimit: params.gas.verificationGasLimit,
213
+ preVerificationGas: params.gas.preVerificationGas,
214
+ maxFeePerGas: params.gas.maxFeePerGas,
215
+ maxPriorityFeePerGas: params.gas.maxPriorityFeePerGas,
216
+ paymasterAndData: params.paymasterAndData ?? "0x",
217
+ signature: "0x"
218
+ // signed later by smart account
219
+ };
220
+ }
221
+
222
+ // src/payid.ts
223
+ var PayID = class {
224
+ wasm;
225
+ constructor(wasmPath) {
226
+ this.wasm = fs.readFileSync(wasmPath);
227
+ }
228
+ async evaluate(context, ruleConfig) {
229
+ return evaluate(this.wasm, context, ruleConfig);
230
+ }
231
+ async evaluateAndProve(params) {
232
+ const result = await this.evaluate(params.context, params.ruleConfig);
233
+ const proof = await generateDecisionProof({
234
+ payId: params.payId,
235
+ owner: params.owner,
236
+ decision: result.decision,
237
+ context: params.context,
238
+ ruleConfig: params.ruleConfig,
239
+ signer: params.signer,
240
+ chainId: params.chainId,
241
+ verifyingContract: params.verifyingContract,
242
+ ttlSeconds: params.ttlSeconds
243
+ });
244
+ return { result, proof };
245
+ }
246
+ async evaluateWithRuleSource(context, ruleSource) {
247
+ try {
248
+ const { config } = await resolveRule(ruleSource);
249
+ return await this.evaluate(context, config);
250
+ } catch (err) {
251
+ return {
252
+ decision: "REJECT",
253
+ code: "RULE_RESOLVE_ERROR",
254
+ reason: err?.message ?? "failed to resolve rule"
255
+ };
256
+ }
257
+ }
258
+ async evaluateAndProveFromSource(params) {
259
+ try {
260
+ const { config } = await resolveRule(params.ruleSource);
261
+ const result = await this.evaluate(params.context, config);
262
+ const proof = await generateDecisionProof({
263
+ payId: params.payId,
264
+ owner: params.owner,
265
+ decision: result.decision,
266
+ context: params.context,
267
+ ruleConfig: config,
268
+ signer: params.signer,
269
+ chainId: params.chainId,
270
+ verifyingContract: params.verifyingContract,
271
+ ttlSeconds: params.ttlSeconds
272
+ });
273
+ return { result, proof };
274
+ } catch (err) {
275
+ return {
276
+ result: {
277
+ decision: "REJECT",
278
+ code: "RULE_RESOLVE_ERROR",
279
+ reason: err?.message ?? "rule resolve failed"
280
+ },
281
+ proof: null
282
+ };
283
+ }
284
+ }
285
+ async evaluateProveAndBuildUserOp(params) {
286
+ const { result, proof } = await this.evaluateAndProveFromSource({
287
+ context: params.context,
288
+ ruleSource: params.ruleSource,
289
+ payId: params.payId,
290
+ owner: params.owner,
291
+ signer: params.signer,
292
+ chainId: params.chainId,
293
+ verifyingContract: params.verifyingContract
294
+ });
295
+ if (result.decision !== "ALLOW" || !proof) {
296
+ return { result, userOp: null, proof };
297
+ }
298
+ const callData = buildPayCallData(
299
+ params.targetContract,
300
+ proof
301
+ );
302
+ const userOp = buildUserOperation({
303
+ sender: params.smartAccount,
304
+ nonce: params.nonce,
305
+ callData,
306
+ gas: params.gas,
307
+ paymasterAndData: params.paymasterAndData
308
+ });
309
+ return { result, userOp, proof };
310
+ }
311
+ };
312
+ export {
313
+ PayID
314
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payid",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -14,15 +14,14 @@
14
14
  "dist"
15
15
  ],
16
16
  "scripts": {
17
- "build:js": "bun build src/index.ts --outdir dist --target node",
18
- "build:build": "tsc -p tsconfig.build.json",
19
- "build": "bun run build:js && bun run build:build"
17
+ "build": "tsup src/index.ts --format esm --dts --clean"
20
18
  },
21
19
  "devDependencies": {
22
- "@types/bun": "latest"
20
+ "@types/bun": "latest",
21
+ "tsup": "^8.5.1"
23
22
  },
24
23
  "peerDependencies": {
25
- "typescript": "^5"
24
+ "typescript": "^5.9.3"
26
25
  },
27
26
  "description": "pay-policy",
28
27
  "repository": {
@@ -1,13 +0,0 @@
1
- import type { DecisionProof } from "./types";
2
- import { ethers } from "ethers";
3
- export declare function generateDecisionProof(params: {
4
- payId: string;
5
- owner: string;
6
- decision: "ALLOW" | "REJECT";
7
- context: any;
8
- ruleConfig: any;
9
- signer: ethers.Signer;
10
- chainId: number;
11
- verifyingContract: string;
12
- ttlSeconds?: number;
13
- }): Promise<DecisionProof>;
@@ -1,20 +0,0 @@
1
- import { randomBytes } from "crypto";
2
- import { hashContext, hashRuleSet } from "./hash";
3
- import { signDecision } from "./sign";
4
- export async function generateDecisionProof(params) {
5
- const issuedAt = Math.floor(Date.now() / 1000);
6
- const expiresAt = issuedAt + (params.ttlSeconds ?? 60);
7
- const payload = {
8
- version: "payid.decision.v1",
9
- payId: params.payId,
10
- owner: params.owner,
11
- decision: params.decision === "ALLOW" ? 1 : 0,
12
- contextHash: hashContext(params.context),
13
- ruleSetHash: hashRuleSet(params.ruleConfig),
14
- issuedAt,
15
- expiresAt,
16
- nonce: `0x${randomBytes(32).toString("hex")}`
17
- };
18
- const signature = await signDecision(params.signer, params.chainId, params.verifyingContract, payload);
19
- return { payload, signature };
20
- }
@@ -1,2 +0,0 @@
1
- export declare function hashContext(context: any): string;
2
- export declare function hashRuleSet(ruleConfig: any): string;
@@ -1,21 +0,0 @@
1
- import { keccak256 } from "ethers";
2
- /**
3
- * NOTE:
4
- * Untuk v1, JSON stringify yang sudah distabilkan
5
- * (keys sorted). WAJIB untuk golden tests.
6
- */
7
- function stableStringify(obj) {
8
- if (Array.isArray(obj)) {
9
- return `[${obj.map(stableStringify).join(",")}]`;
10
- }
11
- if (obj && typeof obj === "object") {
12
- return `{${Object.keys(obj).sort().map(k => `"${k}":${stableStringify(obj[k])}`).join(",")}}`;
13
- }
14
- return JSON.stringify(obj);
15
- }
16
- export function hashContext(context) {
17
- return keccak256(Buffer.from(stableStringify(context)));
18
- }
19
- export function hashRuleSet(ruleConfig) {
20
- return keccak256(Buffer.from(stableStringify(ruleConfig)));
21
- }
@@ -1,3 +0,0 @@
1
- import { ethers } from "ethers";
2
- import type { DecisionPayload } from "./types";
3
- export declare function signDecision(signer: ethers.Signer, chainId: number, verifyingContract: string, payload: DecisionPayload): Promise<string>;
@@ -1,28 +0,0 @@
1
- export async function signDecision(signer, chainId, verifyingContract, payload) {
2
- const domain = {
3
- name: "PAY.ID Decision",
4
- version: "1",
5
- chainId,
6
- verifyingContract
7
- };
8
- const types = {
9
- Decision: [
10
- { name: "version", type: "string" },
11
- { name: "payId", type: "string" },
12
- { name: "owner", type: "address" },
13
- { name: "decision", type: "uint8" },
14
- { name: "contextHash", type: "bytes32" },
15
- { name: "ruleSetHash", type: "bytes32" },
16
- { name: "issuedAt", type: "uint64" },
17
- { name: "expiresAt", type: "uint64" },
18
- { name: "nonce", type: "bytes32" }
19
- ]
20
- };
21
- if (typeof signer.signTypedData === "function") {
22
- return await signer.signTypedData(domain, types, payload);
23
- }
24
- if (typeof signer._signTypedData === "function") {
25
- return await signer._signTypedData(domain, types, payload);
26
- }
27
- throw new Error("Signer does not support EIP-712 signing (signTypedData)");
28
- }
@@ -1,16 +0,0 @@
1
- export type DecisionValue = 0 | 1;
2
- export interface DecisionPayload {
3
- version: "payid.decision.v1";
4
- payId: string;
5
- owner: string;
6
- decision: DecisionValue;
7
- contextHash: string;
8
- ruleSetHash: string;
9
- issuedAt: number;
10
- expiresAt: number;
11
- nonce: string;
12
- }
13
- export interface DecisionProof {
14
- payload: DecisionPayload;
15
- signature: string;
16
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,2 +0,0 @@
1
- import type { DecisionProof } from "../decision-proof/types";
2
- export declare function buildPayCallData(contractAddress: string, proof: DecisionProof): string;
@@ -1,10 +0,0 @@
1
- import { ethers } from "ethers";
2
- export function buildPayCallData(contractAddress, proof) {
3
- const iface = new ethers.Interface([
4
- "function pay(bytes payload, bytes signature)"
5
- ]);
6
- return iface.encodeFunctionData("pay", [
7
- ethers.toUtf8Bytes(JSON.stringify(proof.payload)),
8
- proof.signature
9
- ]);
10
- }
@@ -1,13 +0,0 @@
1
- export interface UserOperation {
2
- sender: string;
3
- nonce: string;
4
- initCode: string;
5
- callData: string;
6
- callGasLimit: string;
7
- verificationGasLimit: string;
8
- preVerificationGas: string;
9
- maxFeePerGas: string;
10
- maxPriorityFeePerGas: string;
11
- paymasterAndData: string;
12
- signature: string;
13
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,15 +0,0 @@
1
- import type { UserOperation } from "./types";
2
- export declare function buildUserOperation(params: {
3
- sender: string;
4
- callData: string;
5
- nonce: string;
6
- gas: {
7
- callGasLimit: string;
8
- verificationGasLimit: string;
9
- preVerificationGas: string;
10
- maxFeePerGas: string;
11
- maxPriorityFeePerGas: string;
12
- };
13
- initCode?: string;
14
- paymasterAndData?: string;
15
- }): UserOperation;
@@ -1,15 +0,0 @@
1
- export function buildUserOperation(params) {
2
- return {
3
- sender: params.sender,
4
- nonce: params.nonce,
5
- initCode: params.initCode ?? "0x",
6
- callData: params.callData,
7
- callGasLimit: params.gas.callGasLimit,
8
- verificationGasLimit: params.gas.verificationGasLimit,
9
- preVerificationGas: params.gas.preVerificationGas,
10
- maxFeePerGas: params.gas.maxFeePerGas,
11
- maxPriorityFeePerGas: params.gas.maxPriorityFeePerGas,
12
- paymasterAndData: params.paymasterAndData ?? "0x",
13
- signature: "0x" // signed later by smart account
14
- };
15
- }
@@ -1,4 +0,0 @@
1
- import type { RuleContext, RuleResult, RuleConfig } from "payid-types";
2
- export declare function evaluate(wasmBinary: Buffer, context: RuleContext, ruleConfig: RuleConfig, options?: {
3
- trustedIssuers?: Set<string>;
4
- }): Promise<RuleResult>;
package/dist/evaluate.js DELETED
@@ -1,46 +0,0 @@
1
- import { executeRule } from "payid-rule-engine";
2
- import { normalizeContext } from "./normalize";
3
- import { preprocessContextV2 } from "payid-rule-engine";
4
- export async function evaluate(wasmBinary, context, ruleConfig, options) {
5
- // ---- basic validation (v1 behavior) ----
6
- if (!context || typeof context !== "object") {
7
- throw new Error("evaluate(): context is required");
8
- }
9
- if (!context.tx) {
10
- throw new Error("evaluate(): context.tx is required");
11
- }
12
- if (!ruleConfig || typeof ruleConfig !== "object") {
13
- throw new Error("evaluate(): ruleConfig is required");
14
- }
15
- let result;
16
- try {
17
- // ---- NEW: preprocess v2 context if enabled ----
18
- const preparedContext = options?.trustedIssuers
19
- ? preprocessContextV2(context, ruleConfig, options.trustedIssuers)
20
- : context;
21
- // ---- existing normalization ----
22
- const normalized = normalizeContext(preparedContext);
23
- // ---- execute WASM rule engine ----
24
- result = await executeRule(wasmBinary, normalized, ruleConfig);
25
- }
26
- catch (err) {
27
- return {
28
- decision: "REJECT",
29
- code: "CONTEXT_OR_ENGINE_ERROR",
30
- reason: err?.message ?? "rule evaluation failed"
31
- };
32
- }
33
- // ---- output validation ----
34
- if (result.decision !== "ALLOW" && result.decision !== "REJECT") {
35
- return {
36
- decision: "REJECT",
37
- code: "INVALID_ENGINE_OUTPUT",
38
- reason: "invalid decision value"
39
- };
40
- }
41
- return {
42
- decision: result.decision,
43
- code: result.code || "UNKNOWN",
44
- reason: result.reason
45
- };
46
- }
@@ -1,2 +0,0 @@
1
- import type { RuleContext } from "payid-types";
2
- export declare function normalizeContext(ctx: RuleContext): RuleContext;
package/dist/normalize.js DELETED
@@ -1,11 +0,0 @@
1
- export function normalizeContext(ctx) {
2
- return {
3
- ...ctx,
4
- tx: {
5
- ...ctx.tx,
6
- sender: ctx.tx.sender?.toLowerCase(),
7
- receiver: ctx.tx.receiver?.toLowerCase(),
8
- asset: ctx.tx.asset.toUpperCase()
9
- }
10
- };
11
- }
package/dist/payid.d.ts DELETED
@@ -1,70 +0,0 @@
1
- import type { RuleContext, RuleResult, RuleConfig } from "payid-types";
2
- import type { RuleSource } from "./resolver/types";
3
- import { ethers } from "ethers";
4
- import type { UserOperation } from "./erc4337/types";
5
- export declare class PayID {
6
- private wasm;
7
- constructor(wasmPath: string);
8
- evaluate(context: RuleContext, ruleConfig: RuleConfig): Promise<RuleResult>;
9
- evaluateAndProve(params: {
10
- context: RuleContext;
11
- ruleConfig: RuleConfig;
12
- payId: string;
13
- owner: string;
14
- signer: ethers.Signer;
15
- chainId: number;
16
- verifyingContract: string;
17
- ttlSeconds?: number;
18
- }): Promise<{
19
- result: RuleResult;
20
- proof: import("./decision-proof/types").DecisionProof;
21
- }>;
22
- evaluateWithRuleSource(context: RuleContext, ruleSource: RuleSource): Promise<RuleResult>;
23
- evaluateAndProveFromSource(params: {
24
- context: RuleContext;
25
- ruleSource: RuleSource;
26
- payId: string;
27
- owner: string;
28
- signer: ethers.Signer;
29
- chainId: number;
30
- verifyingContract: string;
31
- ttlSeconds?: number;
32
- }): Promise<{
33
- result: RuleResult;
34
- proof: import("./decision-proof/types").DecisionProof;
35
- } | {
36
- result: {
37
- decision: string;
38
- code: string;
39
- reason: any;
40
- };
41
- proof: null;
42
- }>;
43
- evaluateProveAndBuildUserOp(params: {
44
- context: RuleContext;
45
- ruleSource: {
46
- uri: string;
47
- hash?: string;
48
- };
49
- payId: string;
50
- owner: string;
51
- signer: ethers.Signer;
52
- smartAccount: string;
53
- targetContract: string;
54
- nonce: string;
55
- gas: {
56
- callGasLimit: string;
57
- verificationGasLimit: string;
58
- preVerificationGas: string;
59
- maxFeePerGas: string;
60
- maxPriorityFeePerGas: string;
61
- };
62
- paymasterAndData?: string;
63
- chainId: number;
64
- verifyingContract: string;
65
- }): Promise<{
66
- result: RuleResult;
67
- userOp: UserOperation | null;
68
- proof: any;
69
- }>;
70
- }
package/dist/payid.js DELETED
@@ -1,93 +0,0 @@
1
- import { evaluate as evaluatePolicy } from "./evaluate";
2
- import { generateDecisionProof } from "./decision-proof/generate";
3
- import { resolveRule } from "./resolver/resolver";
4
- import * as fs from "fs";
5
- import { buildPayCallData } from "./erc4337/build";
6
- import { buildUserOperation } from "./erc4337/userop";
7
- export class PayID {
8
- constructor(wasmPath) {
9
- this.wasm = fs.readFileSync(wasmPath);
10
- }
11
- async evaluate(context, ruleConfig) {
12
- return evaluatePolicy(this.wasm, context, ruleConfig);
13
- }
14
- async evaluateAndProve(params) {
15
- const result = await this.evaluate(params.context, params.ruleConfig);
16
- const proof = await generateDecisionProof({
17
- payId: params.payId,
18
- owner: params.owner,
19
- decision: result.decision,
20
- context: params.context,
21
- ruleConfig: params.ruleConfig,
22
- signer: params.signer,
23
- chainId: params.chainId,
24
- verifyingContract: params.verifyingContract,
25
- ttlSeconds: params.ttlSeconds
26
- });
27
- return { result, proof };
28
- }
29
- async evaluateWithRuleSource(context, ruleSource) {
30
- try {
31
- const { config } = await resolveRule(ruleSource);
32
- return await this.evaluate(context, config);
33
- }
34
- catch (err) {
35
- return {
36
- decision: "REJECT",
37
- code: "RULE_RESOLVE_ERROR",
38
- reason: err?.message ?? "failed to resolve rule"
39
- };
40
- }
41
- }
42
- async evaluateAndProveFromSource(params) {
43
- try {
44
- const { config } = await resolveRule(params.ruleSource);
45
- const result = await this.evaluate(params.context, config);
46
- const proof = await generateDecisionProof({
47
- payId: params.payId,
48
- owner: params.owner,
49
- decision: result.decision,
50
- context: params.context,
51
- ruleConfig: config,
52
- signer: params.signer,
53
- chainId: params.chainId,
54
- verifyingContract: params.verifyingContract,
55
- ttlSeconds: params.ttlSeconds
56
- });
57
- return { result, proof };
58
- }
59
- catch (err) {
60
- return {
61
- result: {
62
- decision: "REJECT",
63
- code: "RULE_RESOLVE_ERROR",
64
- reason: err?.message ?? "rule resolve failed"
65
- },
66
- proof: null
67
- };
68
- }
69
- }
70
- async evaluateProveAndBuildUserOp(params) {
71
- const { result, proof } = await this.evaluateAndProveFromSource({
72
- context: params.context,
73
- ruleSource: params.ruleSource,
74
- payId: params.payId,
75
- owner: params.owner,
76
- signer: params.signer,
77
- chainId: params.chainId,
78
- verifyingContract: params.verifyingContract
79
- });
80
- if (result.decision !== "ALLOW" || !proof) {
81
- return { result: result, userOp: null, proof };
82
- }
83
- const callData = buildPayCallData(params.targetContract, proof);
84
- const userOp = buildUserOperation({
85
- sender: params.smartAccount,
86
- nonce: params.nonce,
87
- callData,
88
- gas: params.gas,
89
- paymasterAndData: params.paymasterAndData
90
- });
91
- return { result, userOp, proof };
92
- }
93
- }
@@ -1 +0,0 @@
1
- export declare function resolveHttpRule(uri: string, expectedHash?: string): Promise<any>;
@@ -1,10 +0,0 @@
1
- import { verifyHash } from "./utils";
2
- export async function resolveHttpRule(uri, expectedHash) {
3
- const res = await fetch(uri);
4
- if (!res.ok) {
5
- throw new Error(`HTTP_RULE_FETCH_FAILED: ${res.status}`);
6
- }
7
- const text = await res.text();
8
- verifyHash(text, expectedHash);
9
- return JSON.parse(text);
10
- }
@@ -1 +0,0 @@
1
- export declare function resolveIpfsRule(uri: string, expectedHash?: string, gateway?: string): Promise<any>;
@@ -1,13 +0,0 @@
1
- import { verifyHash } from "./utils";
2
- const DEFAULT_GATEWAY = "https://ipfs.io/ipfs/";
3
- export async function resolveIpfsRule(uri, expectedHash, gateway = DEFAULT_GATEWAY) {
4
- const cid = uri.replace("ipfs://", "");
5
- const url = `${gateway}${cid}`;
6
- const res = await fetch(url);
7
- if (!res.ok) {
8
- throw new Error(`IPFS_RULE_FETCH_FAILED: ${res.status}`);
9
- }
10
- const text = await res.text();
11
- verifyHash(text, expectedHash);
12
- return JSON.parse(text);
13
- }
@@ -1,2 +0,0 @@
1
- import type { RuleSource, ResolvedRule } from "./types";
2
- export declare function resolveRule(source: RuleSource): Promise<ResolvedRule>;
@@ -1,19 +0,0 @@
1
- import { resolveHttpRule } from "./http";
2
- import { resolveIpfsRule } from "./ipfs";
3
- export async function resolveRule(source) {
4
- const { uri, hash } = source;
5
- let config;
6
- if (uri.startsWith("ipfs://")) {
7
- config = await resolveIpfsRule(uri, hash);
8
- }
9
- else if (uri.startsWith("http://") || uri.startsWith("https://")) {
10
- config = await resolveHttpRule(uri, hash);
11
- }
12
- else {
13
- throw new Error("UNSUPPORTED_RULE_URI");
14
- }
15
- return {
16
- config,
17
- source
18
- };
19
- }
@@ -1,8 +0,0 @@
1
- export interface RuleSource {
2
- uri: string;
3
- hash?: string;
4
- }
5
- export interface ResolvedRule {
6
- config: any;
7
- source: RuleSource;
8
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export declare function verifyHash(content: string, expectedHash?: string): void;
@@ -1,9 +0,0 @@
1
- import { keccak256 } from "ethers";
2
- export function verifyHash(content, expectedHash) {
3
- if (!expectedHash)
4
- return;
5
- const actual = keccak256(Buffer.from(content));
6
- if (actual !== expectedHash) {
7
- throw new Error("RULE_HASH_MISMATCH");
8
- }
9
- }