@keelcodes/policy 0.1.0

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.
@@ -0,0 +1,341 @@
1
+ import { encodeAbiParameters, getAddress, isAddress, keccak256 } from 'viem';
2
+ import { EnvelopeError, PolicyError } from './errors.js';
3
+ // ============================================================================
4
+ // ERC-8312 — Bounded Agent Actions (off-chain mirror).
5
+ //
6
+ // ERC-8312 defines a narrow on-chain *envelope*: a principal, an immutable
7
+ // capability commitment (`capabilityRoot`), a mutable aggregate-state
8
+ // commitment (`cursorRoot`), an expiry and a lifecycle status. The standard is
9
+ // deliberately explicit that the interface does **accounting, not enforcement**
10
+ // ("counting ≠ enforcing"): non-bypassability is a property of the *substrate*
11
+ // that owns the assets or gates the account's only execution path.
12
+ //
13
+ // This module is the off-chain half of Keel's substrate: it normalises and
14
+ // commits a capability, commits cursor state, applies the budget invariant
15
+ // `spent <= cap`, models the aggregate (delegated) profile's conservation rule,
16
+ // and drives the status state machine including contest/resolve. The on-chain
17
+ // registry recomputes the same commitments from the same canonical encodings,
18
+ // so the two layers cannot disagree about what a capability means.
19
+ //
20
+ // ERC-8312 leaves `capabilityRoot` opaque on purpose, so everything *inside*
21
+ // the capability below — the trust tier, the release gate, the approval set —
22
+ // is Keel's own profile, not a claim of standard conformance. What is standard
23
+ // is the envelope shape, the cursor, the budget profile (`spent <= cap`), the
24
+ // aggregate profile's conservation/attenuation rules, and the status machine.
25
+ // Aligned with the draft of 2026-05-09 (requires ERC-165); see
26
+ // docs/BOUNDED_ACTIONS.md for the exact revision this mirrors.
27
+ // ============================================================================
28
+ /** Schema version mixed into `capabilityRoot`, so a future profile change can never collide with an older one. */
29
+ export const CAPABILITY_VERSION = 1;
30
+ /**
31
+ * Lifecycle status of an envelope, matching the standard's `Status` enum
32
+ * (including `None`, the "unknown id" result of `getStatus`).
33
+ */
34
+ export var EnvelopeStatus;
35
+ (function (EnvelopeStatus) {
36
+ EnvelopeStatus[EnvelopeStatus["None"] = 0] = "None";
37
+ EnvelopeStatus[EnvelopeStatus["Active"] = 1] = "Active";
38
+ EnvelopeStatus[EnvelopeStatus["Completed"] = 2] = "Completed";
39
+ EnvelopeStatus[EnvelopeStatus["Contested"] = 3] = "Contested";
40
+ EnvelopeStatus[EnvelopeStatus["Revoked"] = 4] = "Revoked";
41
+ EnvelopeStatus[EnvelopeStatus["Expired"] = 5] = "Expired";
42
+ })(EnvelopeStatus || (EnvelopeStatus = {}));
43
+ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
44
+ function normalizeAddress(value, field) {
45
+ if (!isAddress(value))
46
+ throw new PolicyError(`${field}: invalid address "${value}"`);
47
+ return getAddress(value).toLowerCase();
48
+ }
49
+ function normalizeNonNegative(value, field) {
50
+ if (value < 0n)
51
+ throw new PolicyError(`${field}: must be >= 0, got ${value}`);
52
+ return value;
53
+ }
54
+ function normalizeTier(value, field) {
55
+ const tier = value ?? 0;
56
+ if (!Number.isInteger(tier) || tier < 0 || tier > 3) {
57
+ throw new PolicyError(`${field}: must be an integer 0..3, got ${tier}`);
58
+ }
59
+ return tier;
60
+ }
61
+ /**
62
+ * Validates an authored capability and returns its normalised form. Addresses
63
+ * are lower-cased and every optional field is filled with its default, so
64
+ * semantically identical capabilities always commit to the same root.
65
+ */
66
+ export function normalizeCapability(input) {
67
+ const asset = normalizeAddress(input.asset, 'asset');
68
+ const cap = normalizeNonNegative(input.cap ?? 0n, 'cap');
69
+ const trustTier = normalizeTier(input.trustTier, 'trustTier');
70
+ const notBefore = normalizeNonNegative(input.notBefore ?? 0n, 'notBefore');
71
+ const threshold = input.approvals?.threshold ?? 0;
72
+ if (!Number.isInteger(threshold) || threshold < 0) {
73
+ throw new PolicyError(`approvals.threshold: must be a non-negative integer, got ${threshold}`);
74
+ }
75
+ const approvers = (input.approvals?.approvers ?? []).map((value, i) => normalizeAddress(value, `approvals.approvers[${i}]`));
76
+ if (new Set(approvers).size !== approvers.length) {
77
+ throw new PolicyError('approvals.approvers: duplicate approver');
78
+ }
79
+ if (threshold > 0) {
80
+ if (threshold > approvers.length) {
81
+ throw new PolicyError(`approvals.threshold (${threshold}) exceeds approver count (${approvers.length})`);
82
+ }
83
+ }
84
+ else if (approvers.length > 0) {
85
+ throw new PolicyError('approvals.approvers: configured with a zero threshold (dead config)');
86
+ }
87
+ return Object.freeze({
88
+ version: CAPABILITY_VERSION,
89
+ asset,
90
+ cap,
91
+ trustTier,
92
+ notBefore,
93
+ approvals: Object.freeze({ threshold, approvers: Object.freeze(approvers) }),
94
+ delegate: input.delegate ?? false,
95
+ });
96
+ }
97
+ // Canonical ABI shapes. The on-chain substrate re-encodes the same tuples, so
98
+ // the commitments below are byte-for-byte reproducible in Solidity.
99
+ const CAPABILITY_PARAMETERS = [
100
+ { name: 'version', type: 'uint256' },
101
+ { name: 'asset', type: 'address' },
102
+ { name: 'cap', type: 'uint256' },
103
+ { name: 'trustTier', type: 'uint8' },
104
+ { name: 'notBefore', type: 'uint256' },
105
+ { name: 'delegate', type: 'bool' },
106
+ { name: 'threshold', type: 'uint256' },
107
+ { name: 'approvers', type: 'address[]' },
108
+ ];
109
+ const CURSOR_PARAMETERS = [
110
+ { name: 'spent', type: 'uint256' },
111
+ { name: 'draws', type: 'uint256' },
112
+ { name: 'lastAdvance', type: 'uint256' },
113
+ ];
114
+ /** Canonical ABI encoding of a normalised capability. */
115
+ export function encodeCapability(capability) {
116
+ return encodeAbiParameters(CAPABILITY_PARAMETERS, [
117
+ BigInt(capability.version),
118
+ capability.asset,
119
+ capability.cap,
120
+ BigInt(capability.trustTier),
121
+ capability.notBefore,
122
+ capability.delegate,
123
+ BigInt(capability.approvals.threshold),
124
+ [...capability.approvals.approvers],
125
+ ]);
126
+ }
127
+ /** `capabilityRoot = keccak256(encodeCapability(capability))`. */
128
+ export function capabilityCommitment(capability) {
129
+ return keccak256(encodeCapability(capability));
130
+ }
131
+ /** Canonical ABI encoding of a cursor. */
132
+ export function encodeCursor(cursor) {
133
+ return encodeAbiParameters(CURSOR_PARAMETERS, [
134
+ cursor.spent,
135
+ BigInt(cursor.draws),
136
+ cursor.lastAdvance,
137
+ ]);
138
+ }
139
+ /** `cursorRoot = keccak256(encodeCursor(cursor))`. */
140
+ export function cursorCommitment(cursor) {
141
+ return keccak256(encodeCursor(cursor));
142
+ }
143
+ /** The cursor of an envelope that has never been drawn against. */
144
+ export const ZERO_CURSOR = Object.freeze({ spent: 0n, draws: 0, lastAdvance: 0n });
145
+ /**
146
+ * Deterministic envelope id, per the standard's recommended derivation
147
+ * `keccak256(abi.encode(registry, principal, capabilityRoot, salt))`. A
148
+ * registry may precompute it before registering so a reference can be embedded
149
+ * upstream.
150
+ */
151
+ export function envelopeId(params) {
152
+ return keccak256(encodeAbiParameters([
153
+ { name: 'registry', type: 'address' },
154
+ { name: 'principal', type: 'address' },
155
+ { name: 'capabilityRoot', type: 'bytes32' },
156
+ { name: 'salt', type: 'bytes32' },
157
+ ], [
158
+ normalizeAddress(params.registry, 'registry'),
159
+ normalizeAddress(params.principal, 'principal'),
160
+ params.capabilityRoot,
161
+ params.salt,
162
+ ]));
163
+ }
164
+ /** Remaining headroom under a capability's cap. Never negative. */
165
+ export function remaining(capability, cursor) {
166
+ return cursor.spent >= capability.cap ? 0n : capability.cap - cursor.spent;
167
+ }
168
+ /**
169
+ * The budget invariant, `spent + amount <= cap`. Exposed separately so a
170
+ * caller can check a prospective draw without constructing a full context.
171
+ */
172
+ export function withinCap(capability, cursor, amount) {
173
+ if (amount < 0n)
174
+ return false;
175
+ return cursor.spent + amount <= capability.cap;
176
+ }
177
+ /** Whether the collected approvals satisfy the capability's M-of-N gate. */
178
+ export function approvalsSatisfied(capability, approvals) {
179
+ const { threshold, approvers } = capability.approvals;
180
+ if (threshold === 0)
181
+ return true;
182
+ const collected = new Set(approvals.map((value) => value.toLowerCase()));
183
+ let seen = 0;
184
+ for (const approver of approvers)
185
+ if (collected.has(approver))
186
+ seen += 1;
187
+ return seen >= threshold;
188
+ }
189
+ /**
190
+ * Evaluates a prospective draw. Mirrors the on-chain substrate's gates in the
191
+ * same order — status, release time, trust tier, approvals, then the budget
192
+ * invariant — so an off-chain denial is never more permissive than the chain.
193
+ */
194
+ export function canDraw(capability, cursor, amount, context) {
195
+ if (context.status !== EnvelopeStatus.Active)
196
+ return { allowed: false, reason: 'envelope-not-active' };
197
+ if (capability.notBefore > 0n && context.now < capability.notBefore) {
198
+ return { allowed: false, reason: 'not-yet-released' };
199
+ }
200
+ if ((context.minTier ?? 0) > capability.trustTier) {
201
+ return { allowed: false, reason: 'trust-tier-too-low' };
202
+ }
203
+ if (!approvalsSatisfied(capability, context.approvals ?? [])) {
204
+ return { allowed: false, reason: 'approval-required' };
205
+ }
206
+ if (!withinCap(capability, cursor, amount))
207
+ return { allowed: false, reason: 'cap-exceeded' };
208
+ return { allowed: true };
209
+ }
210
+ /**
211
+ * Advances the cursor by one accepted draw. The caller must have obtained an
212
+ * allowed {@link canDraw} first; this function does not re-check the cap.
213
+ */
214
+ export function advanceCursor(cursor, amount, now) {
215
+ if (amount < 0n)
216
+ throw new EnvelopeError(`amount: must be >= 0, got ${amount}`);
217
+ return Object.freeze({
218
+ spent: cursor.spent + amount,
219
+ draws: cursor.draws + 1,
220
+ lastAdvance: now,
221
+ });
222
+ }
223
+ /** Derived status: an envelope past its expiry reads as `Expired` while it is not terminal. */
224
+ export function effectiveStatus(envelope, now) {
225
+ if (envelope.status === EnvelopeStatus.Active && envelope.expiresAt !== 0n && now > envelope.expiresAt) {
226
+ return EnvelopeStatus.Expired;
227
+ }
228
+ return envelope.status;
229
+ }
230
+ // ============================================================================
231
+ // Aggregate (delegated) profile.
232
+ //
233
+ // A root envelope can allocate sub-budgets to child envelopes. Two rules from
234
+ // the standard's aggregate profile are load-bearing and encoded here:
235
+ // * conservation — the leaf allocations may not sum to more than the root
236
+ // cap, so a tree can never conjure headroom it was not granted; and
237
+ // * attenuation — a child's capability must be no wider than its parent's,
238
+ // and an attenuated node cannot itself delegate.
239
+ // ============================================================================
240
+ /** Throws unless `allocations` sum to at most `rootCap` (conservation). */
241
+ export function assertConservation(rootCap, allocations) {
242
+ let sum = 0n;
243
+ for (const allocation of allocations) {
244
+ if (allocation < 0n)
245
+ throw new PolicyError(`allocation: must be >= 0, got ${allocation}`);
246
+ sum += allocation;
247
+ }
248
+ if (sum > rootCap) {
249
+ throw new PolicyError(`aggregate profile: allocations sum to ${sum} > root cap ${rootCap}`);
250
+ }
251
+ }
252
+ /**
253
+ * Derives an attenuated child capability from a parent.
254
+ *
255
+ * Narrowing only: the child's cap and trust tier may not exceed the parent's,
256
+ * its release time may not be earlier, its approval gate may not be weaker
257
+ * (threshold no lower, approvers a subset of the parent's) and its asset must
258
+ * match. A parent that is not allowed to delegate (or is itself attenuated) may
259
+ * not spawn a child at all. The child is always barred from delegating further,
260
+ * which is what makes attenuation non-transitive widening impossible.
261
+ */
262
+ export function attenuate(parent, child, options = {}) {
263
+ if (!parent.delegate || options.parentAttenuated) {
264
+ throw new EnvelopeError('capability: this envelope may not delegate');
265
+ }
266
+ const next = normalizeCapability(child);
267
+ if (next.asset !== parent.asset)
268
+ throw new EnvelopeError('attenuation: asset must match the parent');
269
+ if (next.cap > parent.cap)
270
+ throw new EnvelopeError('attenuation: cap may not exceed the parent');
271
+ if (next.trustTier > parent.trustTier) {
272
+ throw new EnvelopeError('attenuation: trust tier may not exceed the parent');
273
+ }
274
+ if (next.notBefore < parent.notBefore) {
275
+ throw new EnvelopeError('attenuation: release time may not be earlier than the parent');
276
+ }
277
+ if (next.approvals.threshold < parent.approvals.threshold) {
278
+ throw new EnvelopeError('attenuation: approval gate may not be weaker than the parent');
279
+ }
280
+ // The gate is identity-based, so a non-weaker threshold is not enough: every
281
+ // child approver must already be one of the parent's, or the delegate could
282
+ // swap in an approver set of its own choosing.
283
+ const parentApprovers = new Set(parent.approvals.approvers);
284
+ for (const approver of next.approvals.approvers) {
285
+ if (!parentApprovers.has(approver)) {
286
+ throw new EnvelopeError('attenuation: approvers must be a subset of the parent');
287
+ }
288
+ }
289
+ return normalizeCapability({ ...next, delegate: false });
290
+ }
291
+ // ============================================================================
292
+ // Status state machine.
293
+ //
294
+ // Active -> {Completed, Contested, Revoked, Expired}
295
+ // Contested -> {Active (dismissed), Revoked (upheld)}
296
+ // Completed / Revoked / Expired are terminal; None is the "unknown id" sink.
297
+ // ============================================================================
298
+ const TRANSITIONS = Object.freeze({
299
+ [EnvelopeStatus.None]: [],
300
+ [EnvelopeStatus.Active]: [
301
+ EnvelopeStatus.Completed,
302
+ EnvelopeStatus.Contested,
303
+ EnvelopeStatus.Revoked,
304
+ EnvelopeStatus.Expired,
305
+ ],
306
+ [EnvelopeStatus.Contested]: [EnvelopeStatus.Active, EnvelopeStatus.Revoked],
307
+ [EnvelopeStatus.Completed]: [],
308
+ [EnvelopeStatus.Revoked]: [],
309
+ [EnvelopeStatus.Expired]: [],
310
+ });
311
+ /** Whether `to` is a legal successor of `from`. */
312
+ export function canSetStatus(from, to) {
313
+ return TRANSITIONS[from].includes(to);
314
+ }
315
+ /** Applies a status transition, throwing {@link EnvelopeError} when it is illegal. */
316
+ export function applyStatus(envelope, to) {
317
+ if (!canSetStatus(envelope.status, to)) {
318
+ throw new EnvelopeError(`status: illegal transition ${EnvelopeStatus[envelope.status]} -> ${EnvelopeStatus[to]}`);
319
+ }
320
+ return Object.freeze({ ...envelope, status: to });
321
+ }
322
+ /** Whether a status is terminal (no successor). */
323
+ export function isTerminal(status) {
324
+ return TRANSITIONS[status].length === 0 && status !== EnvelopeStatus.None;
325
+ }
326
+ /** Opens a contest window of `windowSeconds` from `now`. */
327
+ export function openContest(now, windowSeconds) {
328
+ if (windowSeconds <= 0n)
329
+ throw new EnvelopeError('contest window must be > 0');
330
+ return Object.freeze({ contestedAt: now, resolutionDeadline: now + windowSeconds });
331
+ }
332
+ /** Whether the window has elapsed at `now`, letting any caller resolve to the default. */
333
+ export function contestExpired(window, now) {
334
+ return now > window.resolutionDeadline;
335
+ }
336
+ /** The documented default resolution: a still-open contest lapses back to Active. */
337
+ export function defaultResolution() {
338
+ return EnvelopeStatus.Active;
339
+ }
340
+ export { ZERO_ADDRESS as ZERO_ASSET };
341
+ //# sourceMappingURL=bounded.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded.js","sourceRoot":"","sources":["../src/bounded.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import type { Hex, Policy } from './types.js';
2
+ /** ABI shape of a normalised policy; shared with the on-chain carrier. */
3
+ export declare const POLICY_ABI_PARAMETERS: readonly [{
4
+ readonly name: "version";
5
+ readonly type: "uint256";
6
+ }, {
7
+ readonly name: "validAfter";
8
+ readonly type: "uint256";
9
+ }, {
10
+ readonly name: "validUntil";
11
+ readonly type: "uint256";
12
+ }, {
13
+ readonly name: "rules";
14
+ readonly type: "tuple[]";
15
+ readonly components: readonly [{
16
+ readonly name: "target";
17
+ readonly type: "address";
18
+ }, {
19
+ readonly name: "selectors";
20
+ readonly type: "bytes4[]";
21
+ }, {
22
+ readonly name: "maxPerTx";
23
+ readonly type: "uint256";
24
+ }, {
25
+ readonly name: "maxDaily";
26
+ readonly type: "uint256";
27
+ }, {
28
+ readonly name: "maxCalls";
29
+ readonly type: "uint256";
30
+ }, {
31
+ readonly name: "tokenLimits";
32
+ readonly type: "tuple[]";
33
+ readonly components: readonly [{
34
+ readonly name: "token";
35
+ readonly type: "address";
36
+ }, {
37
+ readonly name: "maxPerTx";
38
+ readonly type: "uint256";
39
+ }, {
40
+ readonly name: "maxDaily";
41
+ readonly type: "uint256";
42
+ }];
43
+ }];
44
+ }];
45
+ /**
46
+ * Canonical ABI encoding of a normalised policy. Prefer
47
+ * {@link policyCommitment} unless you need the raw payload (e.g. to recompute
48
+ * the hash on-chain or in a test).
49
+ */
50
+ export declare function encodePolicy(policy: Policy): Hex;
51
+ /** The policy commitment hash: `keccak256(encodePolicy(policy))`. */
52
+ export declare function policyCommitment(policy: Policy): Hex;
53
+ /** ABI shape of the hook install payload, i.e. `(bytes32 sessionId, bytes policyData)`. */
54
+ export declare const INSTALL_ABI_PARAMETERS: readonly [{
55
+ readonly name: "sessionId";
56
+ readonly type: "bytes32";
57
+ }, {
58
+ readonly name: "policyData";
59
+ readonly type: "bytes";
60
+ }];
61
+ /**
62
+ * The ERC-7579 hook install payload for one session:
63
+ * `abi.encode(bytes32 sessionId, bytes policyData)`, where `policyData` is
64
+ * {@link encodePolicy}. The hook stores `keccak256(policyData)` as the session's
65
+ * commitment, so it equals {@link policyCommitment} — the layers cannot disagree.
66
+ */
67
+ export declare function encodeInstallData(sessionId: Hex, policy: Policy): Hex;
68
+ //# sourceMappingURL=commitment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commitment.d.ts","sourceRoot":"","sources":["../src/commitment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAiC9C,0EAA0E;AAC1E,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKxB,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAkBhD;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAEpD;AAED,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB;;;;;;EAGzB,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,CAErE"}
@@ -0,0 +1,80 @@
1
+ import { encodeAbiParameters, keccak256 } from 'viem';
2
+ // ============================================================================
3
+ // Commitment hash.
4
+ //
5
+ // The commitment is `keccak256(abi.encode(version, validAfter, validUntil, rules))`
6
+ // over the **normalised** policy. The on-chain carrier (the ERC-7579 hook module)
7
+ // recomputes the same value from the calldata it decodes and stores it, so an
8
+ // agent's session key commits to a policy it cannot widen later: every signature
9
+ // covers the commitment, and the account's stored hash must match.
10
+ //
11
+ // Decoding calldata into memory arrays and re-encoding (Solidity `abi.encode`)
12
+ // yields the canonical encoding, byte-for-byte identical to the client encoding
13
+ // below — the same property the existing infraX module relies on.
14
+ // ============================================================================
15
+ const RULE_COMPONENTS = [
16
+ { name: 'target', type: 'address' },
17
+ { name: 'selectors', type: 'bytes4[]' },
18
+ { name: 'maxPerTx', type: 'uint256' },
19
+ { name: 'maxDaily', type: 'uint256' },
20
+ { name: 'maxCalls', type: 'uint256' },
21
+ {
22
+ name: 'tokenLimits',
23
+ type: 'tuple[]',
24
+ components: [
25
+ { name: 'token', type: 'address' },
26
+ { name: 'maxPerTx', type: 'uint256' },
27
+ { name: 'maxDaily', type: 'uint256' },
28
+ ],
29
+ },
30
+ ];
31
+ /** ABI shape of a normalised policy; shared with the on-chain carrier. */
32
+ export const POLICY_ABI_PARAMETERS = [
33
+ { name: 'version', type: 'uint256' },
34
+ { name: 'validAfter', type: 'uint256' },
35
+ { name: 'validUntil', type: 'uint256' },
36
+ { name: 'rules', type: 'tuple[]', components: RULE_COMPONENTS },
37
+ ];
38
+ /**
39
+ * Canonical ABI encoding of a normalised policy. Prefer
40
+ * {@link policyCommitment} unless you need the raw payload (e.g. to recompute
41
+ * the hash on-chain or in a test).
42
+ */
43
+ export function encodePolicy(policy) {
44
+ return encodeAbiParameters(POLICY_ABI_PARAMETERS, [
45
+ BigInt(policy.version),
46
+ policy.validAfter,
47
+ policy.validUntil,
48
+ policy.rules.map((rule) => ({
49
+ target: rule.target,
50
+ selectors: [...rule.selectors],
51
+ maxPerTx: rule.maxPerTx,
52
+ maxDaily: rule.maxDaily,
53
+ maxCalls: BigInt(rule.maxCalls),
54
+ tokenLimits: rule.tokenLimits.map((limit) => ({
55
+ token: limit.token,
56
+ maxPerTx: limit.maxPerTx,
57
+ maxDaily: limit.maxDaily,
58
+ })),
59
+ })),
60
+ ]);
61
+ }
62
+ /** The policy commitment hash: `keccak256(encodePolicy(policy))`. */
63
+ export function policyCommitment(policy) {
64
+ return keccak256(encodePolicy(policy));
65
+ }
66
+ /** ABI shape of the hook install payload, i.e. `(bytes32 sessionId, bytes policyData)`. */
67
+ export const INSTALL_ABI_PARAMETERS = [
68
+ { name: 'sessionId', type: 'bytes32' },
69
+ { name: 'policyData', type: 'bytes' },
70
+ ];
71
+ /**
72
+ * The ERC-7579 hook install payload for one session:
73
+ * `abi.encode(bytes32 sessionId, bytes policyData)`, where `policyData` is
74
+ * {@link encodePolicy}. The hook stores `keccak256(policyData)` as the session's
75
+ * commitment, so it equals {@link policyCommitment} — the layers cannot disagree.
76
+ */
77
+ export function encodeInstallData(sessionId, policy) {
78
+ return encodeAbiParameters(INSTALL_ABI_PARAMETERS, [sessionId, encodePolicy(policy)]);
79
+ }
80
+ //# sourceMappingURL=commitment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commitment.js","sourceRoot":"","sources":["../src/commitment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { Call, Hex } from './types.js';
2
+ /** Selectors of the standard ERC-20 methods Keel understands. */
3
+ export declare const ERC20_SELECTOR: {
4
+ readonly transfer: "0xa9059cbb";
5
+ readonly approve: "0x095ea7b3";
6
+ readonly transferFrom: "0x23b872dd";
7
+ };
8
+ /** Outcome of reading an ERC-20 amount from a call. */
9
+ export type Erc20Read =
10
+ /** Selector is not one of the standard value-moving methods. */
11
+ {
12
+ readonly kind: 'not-erc20';
13
+ }
14
+ /** A standard method whose call data is too short to carry an amount. */
15
+ | {
16
+ readonly kind: 'malformed';
17
+ readonly selector: Hex;
18
+ }
19
+ /** A standard method with an amount as its final 32-byte word. */
20
+ | {
21
+ readonly kind: 'amount';
22
+ readonly selector: Hex;
23
+ readonly value: bigint;
24
+ };
25
+ /**
26
+ * Reads the amount from a standard ERC-20 `transfer` / `approve` /
27
+ * `transferFrom` call. In all three the amount is the final `uint256` word, so
28
+ * the same slice works for each (68-byte and 100-byte call data respectively).
29
+ */
30
+ export declare function readErc20Amount(call: Call): Erc20Read;
31
+ //# sourceMappingURL=erc20.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erc20.d.ts","sourceRoot":"","sources":["../src/erc20.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAc5C,iEAAiE;AACjE,eAAO,MAAM,cAAc;;;;CAIa,CAAC;AAEzC,uDAAuD;AACvD,MAAM,MAAM,SAAS;AACnB,gEAAgE;AAC9D;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE;AAChC,yEAAyE;GACvE;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAA;CAAE;AACxD,kEAAkE;GAChE;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAUhF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAKrD"}
package/dist/erc20.js ADDED
@@ -0,0 +1,36 @@
1
+ // ============================================================================
2
+ // Standard ERC-20 call shapes.
3
+ //
4
+ // Token-level ceilings can only be enforced by reading the amount out of the
5
+ // token's own call data, so Keel recognises the three standard value-moving
6
+ // methods. Everything else is left to the rule's selector whitelist.
7
+ //
8
+ // `transferFrom` is recognised but *blocked* when a token limit is configured
9
+ // (see evaluate.ts): bounding it is ambiguous enough that the conservative
10
+ // choice — mirroring the existing infraX module — is to refuse it outright.
11
+ // ============================================================================
12
+ /** Selectors of the standard ERC-20 methods Keel understands. */
13
+ export const ERC20_SELECTOR = {
14
+ transfer: '0xa9059cbb',
15
+ approve: '0x095ea7b3',
16
+ transferFrom: '0x23b872dd',
17
+ };
18
+ function isStandardSelector(selector) {
19
+ return (selector === ERC20_SELECTOR.transfer ||
20
+ selector === ERC20_SELECTOR.approve ||
21
+ selector === ERC20_SELECTOR.transferFrom);
22
+ }
23
+ /**
24
+ * Reads the amount from a standard ERC-20 `transfer` / `approve` /
25
+ * `transferFrom` call. In all three the amount is the final `uint256` word, so
26
+ * the same slice works for each (68-byte and 100-byte call data respectively).
27
+ */
28
+ export function readErc20Amount(call) {
29
+ if (!isStandardSelector(call.selector))
30
+ return { kind: 'not-erc20' };
31
+ // Selector (4 bytes) + at least two words (recipient + amount).
32
+ if (call.data.length < 10 + 64)
33
+ return { kind: 'malformed', selector: call.selector };
34
+ return { kind: 'amount', selector: call.selector, value: BigInt(`0x${call.data.slice(-64)}`) };
35
+ }
36
+ //# sourceMappingURL=erc20.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erc20.js","sourceRoot":"","sources":["../src/erc20.ts"],"names":[],"mappings":"AAEA,+EAA+E;AAC/E,+BAA+B;AAC/B,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,qEAAqE;AACrE,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,4EAA4E;AAC5E,+EAA+E;AAE/E,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,QAAQ,EAAE,YAAY;IACtB,OAAO,EAAE,YAAY;IACrB,YAAY,EAAE,YAAY;CACY,CAAC;AAWzC,SAAS,kBAAkB,CAAC,QAAa;IACvC,OAAO,CACL,QAAQ,KAAK,cAAc,CAAC,QAAQ;QACpC,QAAQ,KAAK,cAAc,CAAC,OAAO;QACnC,QAAQ,KAAK,cAAc,CAAC,YAAY,CACzC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAU;IACxC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IACrE,gEAAgE;IAChE,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,EAAE;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;IACtF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AACjG,CAAC"}
@@ -0,0 +1,13 @@
1
+ /** Raised when an authored policy is malformed. */
2
+ export declare class PolicyError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ /** Raised when a session lifecycle operation is invalid (unknown / duplicate / already revoked). */
6
+ export declare class SessionError extends Error {
7
+ constructor(message: string);
8
+ }
9
+ /** Raised when an ERC-8312 envelope lifecycle or attenuation operation is invalid. */
10
+ export declare class EnvelopeError extends Error {
11
+ constructor(message: string);
12
+ }
13
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED,oGAAoG;AACpG,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED,sFAAsF;AACtF,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI5B"}
package/dist/errors.js ADDED
@@ -0,0 +1,22 @@
1
+ /** Raised when an authored policy is malformed. */
2
+ export class PolicyError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = 'PolicyError';
6
+ }
7
+ }
8
+ /** Raised when a session lifecycle operation is invalid (unknown / duplicate / already revoked). */
9
+ export class SessionError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = 'SessionError';
13
+ }
14
+ }
15
+ /** Raised when an ERC-8312 envelope lifecycle or attenuation operation is invalid. */
16
+ export class EnvelopeError extends Error {
17
+ constructor(message) {
18
+ super(message);
19
+ this.name = 'EnvelopeError';
20
+ }
21
+ }
22
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,oGAAoG;AACpG,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,sFAAsF;AACtF,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF"}