@crediolabs/policy-synth 0.4.0 → 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.
Files changed (68) hide show
  1. package/dist/adapters/interpreter/adapter.d.ts +2 -2
  2. package/dist/adapters/interpreter/adapter.js +11 -3
  3. package/dist/errors.d.ts +6 -1
  4. package/dist/install/authority-overlap.js +12 -0
  5. package/dist/install/build-add-context-rule.d.ts +1 -1
  6. package/dist/install/read-account-rules.d.ts +79 -0
  7. package/dist/install/read-account-rules.js +241 -0
  8. package/dist/predicate/decode.js +22 -1
  9. package/dist/predicate/encode.js +52 -5
  10. package/dist/predicate/from-json.js +21 -1
  11. package/dist/review-card/builder.js +40 -0
  12. package/dist/review-card/cross-check.js +34 -0
  13. package/dist/review-card/render-leaf.d.ts +1 -1
  14. package/dist/review-card/render-leaf.js +7 -0
  15. package/dist/run/index.d.ts +2 -2
  16. package/dist/run/index.js +52 -9
  17. package/dist/run/schemas.d.ts +64 -14
  18. package/dist/run/schemas.js +70 -6
  19. package/dist/simulate/deny-cases.js +11 -0
  20. package/dist/simulate/evaluate.js +86 -5
  21. package/dist/synth/declare.d.ts +13 -0
  22. package/dist/synth/declare.js +34 -5
  23. package/dist/synth/synthesize-from-recording.js +1 -1
  24. package/dist/types.d.ts +21 -1
  25. package/dist/types.js +1 -1
  26. package/dist-cjs/adapters/interpreter/adapter.d.ts +2 -2
  27. package/dist-cjs/adapters/interpreter/adapter.js +11 -3
  28. package/dist-cjs/errors.d.ts +6 -1
  29. package/dist-cjs/install/authority-overlap.js +12 -0
  30. package/dist-cjs/install/build-add-context-rule.d.ts +1 -1
  31. package/dist-cjs/install/read-account-rules.d.ts +79 -0
  32. package/dist-cjs/install/read-account-rules.js +252 -0
  33. package/dist-cjs/predicate/decode.js +22 -1
  34. package/dist-cjs/predicate/encode.js +52 -5
  35. package/dist-cjs/predicate/from-json.js +21 -1
  36. package/dist-cjs/review-card/builder.js +40 -0
  37. package/dist-cjs/review-card/cross-check.js +34 -0
  38. package/dist-cjs/review-card/render-leaf.d.ts +1 -1
  39. package/dist-cjs/review-card/render-leaf.js +7 -0
  40. package/dist-cjs/run/index.d.ts +2 -2
  41. package/dist-cjs/run/index.js +54 -9
  42. package/dist-cjs/run/schemas.d.ts +64 -14
  43. package/dist-cjs/run/schemas.js +71 -7
  44. package/dist-cjs/simulate/deny-cases.js +11 -0
  45. package/dist-cjs/simulate/evaluate.js +86 -5
  46. package/dist-cjs/synth/declare.d.ts +13 -0
  47. package/dist-cjs/synth/declare.js +34 -5
  48. package/dist-cjs/synth/synthesize-from-recording.js +1 -1
  49. package/dist-cjs/types.d.ts +21 -1
  50. package/dist-cjs/types.js +1 -1
  51. package/package.json +1 -1
  52. package/src/adapters/interpreter/adapter.ts +13 -5
  53. package/src/errors.ts +5 -0
  54. package/src/install/authority-overlap.ts +11 -0
  55. package/src/install/read-account-rules.ts +313 -0
  56. package/src/predicate/decode.ts +22 -1
  57. package/src/predicate/encode.ts +55 -5
  58. package/src/predicate/from-json.ts +21 -1
  59. package/src/review-card/builder.ts +45 -2
  60. package/src/review-card/cross-check.ts +35 -1
  61. package/src/review-card/render-leaf.ts +8 -1
  62. package/src/run/index.ts +57 -8
  63. package/src/run/schemas.ts +83 -6
  64. package/src/simulate/deny-cases.ts +12 -1
  65. package/src/simulate/evaluate.ts +101 -9
  66. package/src/synth/declare.ts +54 -5
  67. package/src/synth/synthesize-from-recording.ts +1 -1
  68. package/src/types.ts +16 -1
@@ -15,11 +15,12 @@
15
15
  // first: it constrains the amount in THIS call rather than implying a rolling
16
16
  // total nothing tracks.
17
17
  //
18
- // What a declaration can say maps one-to-one onto grammar 3:
19
- // fn -> eq(call_fn, literal_symbol)
20
- // contract -> eq(call_contract, literal_address)
21
- // maxAmount -> lte(call_arg(i), literal_i128)
22
- // recipients -> in(call_arg(j), [literal_address, ...])
18
+ // What a declaration can say maps one-to-one onto grammar 4:
19
+ // fn -> eq(call_fn, literal_symbol)
20
+ // contract -> eq(call_contract, literal_address)
21
+ // maxAmount -> lte(call_arg(i), literal_i128)
22
+ // recipients -> in(call_arg(j), [literal_address, ...])
23
+ // minOutputRatio-> gte(call_arg(out), call_arg_scaled(in, num, den))
23
24
 
24
25
  import type { ToolError } from '../errors.ts'
25
26
  import type { PredicateLeaf, PredicateNode } from '../types.ts'
@@ -52,6 +53,14 @@ export interface PolicyDeclaration {
52
53
  * explicitly. A rule that permits nothing is a plausible thing to want and
53
54
  * an implausible thing to want by accident. */
54
55
  allowZeroCap?: boolean
56
+ /** Minimum output as a ratio of the call's own input: the output argument
57
+ * must be at least `input * num / den`.
58
+ *
59
+ * A swap's acceptable output depends on the size of the trade, so a fixed
60
+ * floor would pin the policy to one trade size. The ratio is DECLARED, never
61
+ * inferred from a recording: a recorded rate is a price at one moment, and
62
+ * freezing it as policy would deny ordinary trades later. */
63
+ minOutputRatio?: { num: string; den: string; inputArgIndex: number; outputArgIndex: number }
55
64
  }
56
65
 
57
66
  export interface DeclaredPredicate {
@@ -137,6 +146,46 @@ export function declarePredicate(d: PolicyDeclaration): DeclaredPredicate {
137
146
  })
138
147
  }
139
148
 
149
+ if (d.minOutputRatio !== undefined) {
150
+ const { num, den, inputArgIndex, outputArgIndex } = d.minOutputRatio
151
+ if (!/^[0-9]+$/.test(num) || !/^[0-9]+$/.test(den)) {
152
+ throw declareError(
153
+ 'SYNTHESIS_ERROR',
154
+ `minOutputRatio num/den must be unsigned integers, got "${num}"/"${den}" (a 1% slippage tolerance is num "99", den "100")`
155
+ )
156
+ }
157
+ // Both are refused on chain at install (INVALID_SCALED_RATIO). Refusing
158
+ // here too means the caller learns before a transaction is built.
159
+ if (den === '0') {
160
+ throw declareError('SYNTHESIS_ERROR', 'minOutputRatio.den is zero: the ratio has no value')
161
+ }
162
+ if (num === '0') {
163
+ throw declareError(
164
+ 'SYNTHESIS_ERROR',
165
+ 'minOutputRatio.num is zero: the floor would be zero, which constrains nothing. Omit it instead.'
166
+ )
167
+ }
168
+ if (inputArgIndex === outputArgIndex) {
169
+ throw declareError(
170
+ 'SYNTHESIS_ERROR',
171
+ `minOutputRatio bounds arg[${inputArgIndex}] against itself, which is true for any ratio at or below 1 and false above it - never a slippage floor. Pass the distinct input and output positions.`
172
+ )
173
+ }
174
+ if (BigInt(num) > BigInt(den)) {
175
+ // Demanding MORE out than went in is not slippage protection; it is a
176
+ // rule that denies every honest trade. Loud beats a policy that never
177
+ // permits.
178
+ warnings.push(
179
+ `minOutputRatio ${num}/${den} is above 1: it requires the output to EXCEED the input, which no ordinary swap satisfies. Check the ratio is not inverted.`
180
+ )
181
+ }
182
+ children.push({
183
+ op: 'gte',
184
+ left: { kind: 'call_arg', index: outputArgIndex },
185
+ right: { kind: 'call_arg_scaled', index: inputArgIndex, num, den },
186
+ })
187
+ }
188
+
140
189
  // A single conjunct is emitted bare. `and` with one child encodes to
141
190
  // different bytes than the child alone, and the extra node buys nothing.
142
191
  const predicate: PredicateNode =
@@ -1,7 +1,7 @@
1
1
  // src/synth/synthesize-from-recording.ts - recording-path orchestrator.
2
2
  //
3
3
  // `synthesizeFromRecording` INFERS a bounded policy from a `RecordedTransaction`
4
- // via the `PolicyIR` + interpreter adapter pair.
4
+ // via the composer + interpreter adapter pair.
5
5
  // Flow: validate -> parseConfidence gate -> lower -> decideScope -> composeFromRecording
6
6
  // -> interpreter compile + self-verify.
7
7
  // Same (tx, opts) -> byte-identical ProposedPolicy (no randomness, clock, globals).
package/src/types.ts CHANGED
@@ -105,7 +105,7 @@ export type PolicyRef = {
105
105
  * `contracts/policy-interpreter/src/version.rs`. Every value this package puts on
106
106
  * the wire derives from here; `grammar-version-parity.test.ts` pins it to the
107
107
  * contract so a skew cannot pass a green test run. */
108
- export const GRAMMAR_VERSION = 3 as const
108
+ export const GRAMMAR_VERSION = 4 as const
109
109
 
110
110
  /** A serialised predicate document stored against a (smart_account, rule_id) pair. */
111
111
  export interface PolicyDocument {
@@ -128,8 +128,15 @@ export interface PolicyDocument {
128
128
  /** The versioned predicate AST (interpreter side). Tagged union. */
129
129
  export type PredicateNode =
130
130
  | { op: 'and'; children: PredicateNode[] }
131
+ // Permits when ANY child permits. Children are canonically sorted like
132
+ // `and`, so authoring order is NOT preserved; when every branch denies, the
133
+ // contract reports the first branch's reason in wire order.
134
+ | { op: 'or'; children: PredicateNode[] }
131
135
  | { op: 'eq'; left: PredicateLeaf; right: PredicateLeaf }
136
+ | { op: 'lt'; left: PredicateLeaf; right: PredicateLeaf }
132
137
  | { op: 'lte'; left: PredicateLeaf; right: PredicateLeaf }
138
+ | { op: 'gt'; left: PredicateLeaf; right: PredicateLeaf }
139
+ | { op: 'gte'; left: PredicateLeaf; right: PredicateLeaf }
133
140
  | {
134
141
  op: 'in'
135
142
  needle: PredicateLeaf
@@ -150,6 +157,14 @@ export type PredicateLeaf =
150
157
  // recorded ScVal type. A non-vec arg, missing element, missing field, or
151
158
  // type mismatch all DENY (fail-closed).
152
159
  | { kind: 'call_arg_field'; index: number; element: number; field: string }
160
+ // `args[index] * num / den`, truncating toward zero. The one leaf whose
161
+ // value is COMPUTED from the call rather than read from it, and the only
162
+ // selector allowed on the right of a comparison - which is what lets a swap
163
+ // bound its output against its own input instead of against a constant that
164
+ // would pin the policy to one trade size. `num`/`den` are i128 decimal
165
+ // strings. Install refuses a zero or non-positive ratio (214), because a
166
+ // negative ratio inverts the comparison.
167
+ | { kind: 'call_arg_scaled'; index: number; num: string; den: string }
153
168
  // Literal leaves: bare ScVal on the wire (no selector-tuple wrapper). Right-hand side of
154
169
  // comparisons, elements of `in` haystacks, and operands to future arithmetic nodes.
155
170
  | { kind: 'literal_address'; value: string }