@crediolabs/policy-synth 0.3.1 → 0.4.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.
Files changed (51) hide show
  1. package/dist/install/authority-overlap.d.ts +101 -0
  2. package/dist/install/authority-overlap.js +227 -0
  3. package/dist/install/index.d.ts +1 -0
  4. package/dist/install/index.js +4 -0
  5. package/dist/record/index.d.ts +10 -0
  6. package/dist/record/index.js +32 -1
  7. package/dist/record/rpc.d.ts +4 -0
  8. package/dist/record/rpc.js +4 -1
  9. package/dist/registry/identify.d.ts +10 -1
  10. package/dist/registry/identify.js +4 -1
  11. package/dist/registry/on-chain-spec.d.ts +37 -0
  12. package/dist/registry/on-chain-spec.js +152 -0
  13. package/dist/run/index.d.ts +25 -4
  14. package/dist/run/index.js +65 -4
  15. package/dist/run/schemas.d.ts +313 -0
  16. package/dist/run/schemas.js +52 -0
  17. package/dist/synth/declare.d.ts +30 -0
  18. package/dist/synth/declare.js +98 -0
  19. package/dist/synth/index.d.ts +1 -0
  20. package/dist/synth/index.js +1 -0
  21. package/dist-cjs/install/authority-overlap.d.ts +101 -0
  22. package/dist-cjs/install/authority-overlap.js +236 -0
  23. package/dist-cjs/install/index.d.ts +1 -0
  24. package/dist-cjs/install/index.js +13 -2
  25. package/dist-cjs/record/index.d.ts +10 -0
  26. package/dist-cjs/record/index.js +31 -0
  27. package/dist-cjs/record/rpc.d.ts +4 -0
  28. package/dist-cjs/record/rpc.js +7 -3
  29. package/dist-cjs/registry/identify.d.ts +10 -1
  30. package/dist-cjs/registry/identify.js +4 -0
  31. package/dist-cjs/registry/on-chain-spec.d.ts +37 -0
  32. package/dist-cjs/registry/on-chain-spec.js +159 -0
  33. package/dist-cjs/run/index.d.ts +25 -4
  34. package/dist-cjs/run/index.js +65 -2
  35. package/dist-cjs/run/schemas.d.ts +313 -0
  36. package/dist-cjs/run/schemas.js +53 -1
  37. package/dist-cjs/synth/declare.d.ts +30 -0
  38. package/dist-cjs/synth/declare.js +101 -0
  39. package/dist-cjs/synth/index.d.ts +1 -0
  40. package/dist-cjs/synth/index.js +3 -1
  41. package/package.json +1 -1
  42. package/src/install/authority-overlap.ts +312 -0
  43. package/src/install/index.ts +20 -0
  44. package/src/record/index.ts +59 -2
  45. package/src/record/rpc.ts +4 -1
  46. package/src/registry/identify.ts +4 -1
  47. package/src/registry/on-chain-spec.ts +168 -0
  48. package/src/run/index.ts +79 -2
  49. package/src/run/schemas.ts +57 -0
  50. package/src/synth/declare.ts +157 -0
  51. package/src/synth/index.ts +5 -0
@@ -1494,6 +1494,104 @@ export declare const VerifyPolicyInputSchema: z.ZodObject<{
1494
1494
  predicate?: unknown;
1495
1495
  }>;
1496
1496
  export type VerifyPolicyInput = z.infer<typeof VerifyPolicyInputSchema>;
1497
+ /** A rule ALREADY on the account, as the caller observed it. Supplying these
1498
+ * turns on the cross-rule authority scan: a signer belonging to several rules
1499
+ * picks which one applies, so a predicate only constrains a key when the
1500
+ * policed rule is the only rule that key is on. */
1501
+ export declare const ObservedRuleSchema: z.ZodObject<{
1502
+ id: z.ZodNumber;
1503
+ contextType: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
1504
+ kind: z.ZodLiteral<"default">;
1505
+ }, "strip", z.ZodTypeAny, {
1506
+ kind: "default";
1507
+ }, {
1508
+ kind: "default";
1509
+ }>, z.ZodObject<{
1510
+ kind: z.ZodLiteral<"call_contract">;
1511
+ contract: z.ZodString;
1512
+ }, "strip", z.ZodTypeAny, {
1513
+ kind: "call_contract";
1514
+ contract: string;
1515
+ }, {
1516
+ kind: "call_contract";
1517
+ contract: string;
1518
+ }>, z.ZodObject<{
1519
+ kind: z.ZodLiteral<"create_contract">;
1520
+ wasmHash: z.ZodString;
1521
+ }, "strip", z.ZodTypeAny, {
1522
+ kind: "create_contract";
1523
+ wasmHash: string;
1524
+ }, {
1525
+ kind: "create_contract";
1526
+ wasmHash: string;
1527
+ }>]>;
1528
+ signers: z.ZodArray<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
1529
+ kind: z.ZodLiteral<"delegated">;
1530
+ address: z.ZodString;
1531
+ }, "strip", z.ZodTypeAny, {
1532
+ address: string;
1533
+ kind: "delegated";
1534
+ }, {
1535
+ address: string;
1536
+ kind: "delegated";
1537
+ }>, z.ZodObject<{
1538
+ kind: z.ZodLiteral<"external">;
1539
+ verifier: z.ZodString;
1540
+ keyBytes: z.ZodString;
1541
+ }, "strip", z.ZodTypeAny, {
1542
+ kind: "external";
1543
+ verifier: string;
1544
+ keyBytes: string;
1545
+ }, {
1546
+ kind: "external";
1547
+ verifier: string;
1548
+ keyBytes: string;
1549
+ }>]>, "many">;
1550
+ policyAddresses: z.ZodArray<z.ZodString, "many">;
1551
+ predicate: z.ZodOptional<z.ZodType<unknown, z.ZodTypeDef, unknown>>;
1552
+ }, "strip", z.ZodTypeAny, {
1553
+ signers: ({
1554
+ address: string;
1555
+ kind: "delegated";
1556
+ } | {
1557
+ kind: "external";
1558
+ verifier: string;
1559
+ keyBytes: string;
1560
+ })[];
1561
+ id: number;
1562
+ contextType: {
1563
+ kind: "default";
1564
+ } | {
1565
+ kind: "call_contract";
1566
+ contract: string;
1567
+ } | {
1568
+ kind: "create_contract";
1569
+ wasmHash: string;
1570
+ };
1571
+ policyAddresses: string[];
1572
+ predicate?: unknown;
1573
+ }, {
1574
+ signers: ({
1575
+ address: string;
1576
+ kind: "delegated";
1577
+ } | {
1578
+ kind: "external";
1579
+ verifier: string;
1580
+ keyBytes: string;
1581
+ })[];
1582
+ id: number;
1583
+ contextType: {
1584
+ kind: "default";
1585
+ } | {
1586
+ kind: "call_contract";
1587
+ contract: string;
1588
+ } | {
1589
+ kind: "create_contract";
1590
+ wasmHash: string;
1591
+ };
1592
+ policyAddresses: string[];
1593
+ predicate?: unknown;
1594
+ }>;
1497
1595
  /** Pinned interpreter address (testnet).
1498
1596
  * Single source for the MCP layer; do not embed elsewhere. */
1499
1597
  export declare const PINNED_INTERPRETER_TESTNET_ADDRESS = "CCL336TCK2Y5OFNRCMN2M3HVPBCEX4PW5H6EQ5VW5NPMXOCP4ESB5XR4";
@@ -1533,7 +1631,134 @@ export declare const RPC_URL_BY_NETWORK: Record<Network, string>;
1533
1631
  * matching passphrase when the wallet signs (a mismatch yields invalid
1534
1632
  * hashes). */
1535
1633
  export declare const NETWORK_PASSPHRASES: Record<Network, string>;
1634
+ export declare const DeclarePolicyInputSchema: z.ZodObject<{
1635
+ fn: z.ZodString;
1636
+ contract: z.ZodOptional<z.ZodString>;
1637
+ /** Smallest unit, unsigned decimal STRING - an i128 is wider than
1638
+ * Number.MAX_SAFE_INTEGER, so a number here would silently round. */
1639
+ maxAmount: z.ZodOptional<z.ZodString>;
1640
+ amountArgIndex: z.ZodOptional<z.ZodNumber>;
1641
+ recipients: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
1642
+ recipientArgIndex: z.ZodOptional<z.ZodNumber>;
1643
+ allowZeroCap: z.ZodOptional<z.ZodBoolean>;
1644
+ }, "strict", z.ZodTypeAny, {
1645
+ fn: string;
1646
+ contract?: string | undefined;
1647
+ maxAmount?: string | undefined;
1648
+ amountArgIndex?: number | undefined;
1649
+ recipients?: string[] | undefined;
1650
+ recipientArgIndex?: number | undefined;
1651
+ allowZeroCap?: boolean | undefined;
1652
+ }, {
1653
+ fn: string;
1654
+ contract?: string | undefined;
1655
+ maxAmount?: string | undefined;
1656
+ amountArgIndex?: number | undefined;
1657
+ recipients?: string[] | undefined;
1658
+ recipientArgIndex?: number | undefined;
1659
+ allowZeroCap?: boolean | undefined;
1660
+ }>;
1661
+ export type DeclarePolicyInput = z.infer<typeof DeclarePolicyInputSchema>;
1536
1662
  export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
1663
+ /** Rules already on the account. Supplying them turns on the cross-rule
1664
+ * authority scan, which reports every existing rule a signer of this
1665
+ * install could name INSTEAD - including an unpoliced one, against which
1666
+ * the predicate never runs. Absent means the scan is skipped, and the
1667
+ * result says so rather than reporting "no overlaps found". */
1668
+ existingRules: z.ZodOptional<z.ZodArray<z.ZodObject<{
1669
+ id: z.ZodNumber;
1670
+ contextType: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
1671
+ kind: z.ZodLiteral<"default">;
1672
+ }, "strip", z.ZodTypeAny, {
1673
+ kind: "default";
1674
+ }, {
1675
+ kind: "default";
1676
+ }>, z.ZodObject<{
1677
+ kind: z.ZodLiteral<"call_contract">;
1678
+ contract: z.ZodString;
1679
+ }, "strip", z.ZodTypeAny, {
1680
+ kind: "call_contract";
1681
+ contract: string;
1682
+ }, {
1683
+ kind: "call_contract";
1684
+ contract: string;
1685
+ }>, z.ZodObject<{
1686
+ kind: z.ZodLiteral<"create_contract">;
1687
+ wasmHash: z.ZodString;
1688
+ }, "strip", z.ZodTypeAny, {
1689
+ kind: "create_contract";
1690
+ wasmHash: string;
1691
+ }, {
1692
+ kind: "create_contract";
1693
+ wasmHash: string;
1694
+ }>]>;
1695
+ signers: z.ZodArray<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
1696
+ kind: z.ZodLiteral<"delegated">;
1697
+ address: z.ZodString;
1698
+ }, "strip", z.ZodTypeAny, {
1699
+ address: string;
1700
+ kind: "delegated";
1701
+ }, {
1702
+ address: string;
1703
+ kind: "delegated";
1704
+ }>, z.ZodObject<{
1705
+ kind: z.ZodLiteral<"external">;
1706
+ verifier: z.ZodString;
1707
+ keyBytes: z.ZodString;
1708
+ }, "strip", z.ZodTypeAny, {
1709
+ kind: "external";
1710
+ verifier: string;
1711
+ keyBytes: string;
1712
+ }, {
1713
+ kind: "external";
1714
+ verifier: string;
1715
+ keyBytes: string;
1716
+ }>]>, "many">;
1717
+ policyAddresses: z.ZodArray<z.ZodString, "many">;
1718
+ predicate: z.ZodOptional<z.ZodType<unknown, z.ZodTypeDef, unknown>>;
1719
+ }, "strip", z.ZodTypeAny, {
1720
+ signers: ({
1721
+ address: string;
1722
+ kind: "delegated";
1723
+ } | {
1724
+ kind: "external";
1725
+ verifier: string;
1726
+ keyBytes: string;
1727
+ })[];
1728
+ id: number;
1729
+ contextType: {
1730
+ kind: "default";
1731
+ } | {
1732
+ kind: "call_contract";
1733
+ contract: string;
1734
+ } | {
1735
+ kind: "create_contract";
1736
+ wasmHash: string;
1737
+ };
1738
+ policyAddresses: string[];
1739
+ predicate?: unknown;
1740
+ }, {
1741
+ signers: ({
1742
+ address: string;
1743
+ kind: "delegated";
1744
+ } | {
1745
+ kind: "external";
1746
+ verifier: string;
1747
+ keyBytes: string;
1748
+ })[];
1749
+ id: number;
1750
+ contextType: {
1751
+ kind: "default";
1752
+ } | {
1753
+ kind: "call_contract";
1754
+ contract: string;
1755
+ } | {
1756
+ kind: "create_contract";
1757
+ wasmHash: string;
1758
+ };
1759
+ policyAddresses: string[];
1760
+ predicate?: unknown;
1761
+ }>, "many">>;
1537
1762
  /** The smart account contract address (C...) that will receive the rule. */
1538
1763
  smartAccount: z.ZodString;
1539
1764
  /** The signer that authorises the install (G... wallet). Used only for
@@ -1918,6 +2143,28 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
1918
2143
  [k: string]: unknown;
1919
2144
  };
1920
2145
  network?: "mainnet" | "testnet" | undefined;
2146
+ existingRules?: {
2147
+ signers: ({
2148
+ address: string;
2149
+ kind: "delegated";
2150
+ } | {
2151
+ kind: "external";
2152
+ verifier: string;
2153
+ keyBytes: string;
2154
+ })[];
2155
+ id: number;
2156
+ contextType: {
2157
+ kind: "default";
2158
+ } | {
2159
+ kind: "call_contract";
2160
+ contract: string;
2161
+ } | {
2162
+ kind: "create_contract";
2163
+ wasmHash: string;
2164
+ };
2165
+ policyAddresses: string[];
2166
+ predicate?: unknown;
2167
+ }[] | undefined;
1921
2168
  rpcUrl?: string | undefined;
1922
2169
  allowUnpinnedRpcUrl?: boolean | undefined;
1923
2170
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -1955,6 +2202,28 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
1955
2202
  [k: string]: unknown;
1956
2203
  };
1957
2204
  network?: "mainnet" | "testnet" | undefined;
2205
+ existingRules?: {
2206
+ signers: ({
2207
+ address: string;
2208
+ kind: "delegated";
2209
+ } | {
2210
+ kind: "external";
2211
+ verifier: string;
2212
+ keyBytes: string;
2213
+ })[];
2214
+ id: number;
2215
+ contextType: {
2216
+ kind: "default";
2217
+ } | {
2218
+ kind: "call_contract";
2219
+ contract: string;
2220
+ } | {
2221
+ kind: "create_contract";
2222
+ wasmHash: string;
2223
+ };
2224
+ policyAddresses: string[];
2225
+ predicate?: unknown;
2226
+ }[] | undefined;
1958
2227
  rpcUrl?: string | undefined;
1959
2228
  allowUnpinnedRpcUrl?: boolean | undefined;
1960
2229
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -1992,6 +2261,28 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
1992
2261
  [k: string]: unknown;
1993
2262
  };
1994
2263
  network?: "mainnet" | "testnet" | undefined;
2264
+ existingRules?: {
2265
+ signers: ({
2266
+ address: string;
2267
+ kind: "delegated";
2268
+ } | {
2269
+ kind: "external";
2270
+ verifier: string;
2271
+ keyBytes: string;
2272
+ })[];
2273
+ id: number;
2274
+ contextType: {
2275
+ kind: "default";
2276
+ } | {
2277
+ kind: "call_contract";
2278
+ contract: string;
2279
+ } | {
2280
+ kind: "create_contract";
2281
+ wasmHash: string;
2282
+ };
2283
+ policyAddresses: string[];
2284
+ predicate?: unknown;
2285
+ }[] | undefined;
1995
2286
  rpcUrl?: string | undefined;
1996
2287
  allowUnpinnedRpcUrl?: boolean | undefined;
1997
2288
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -2029,6 +2320,28 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
2029
2320
  [k: string]: unknown;
2030
2321
  };
2031
2322
  network?: "mainnet" | "testnet" | undefined;
2323
+ existingRules?: {
2324
+ signers: ({
2325
+ address: string;
2326
+ kind: "delegated";
2327
+ } | {
2328
+ kind: "external";
2329
+ verifier: string;
2330
+ keyBytes: string;
2331
+ })[];
2332
+ id: number;
2333
+ contextType: {
2334
+ kind: "default";
2335
+ } | {
2336
+ kind: "call_contract";
2337
+ contract: string;
2338
+ } | {
2339
+ kind: "create_contract";
2340
+ wasmHash: string;
2341
+ };
2342
+ policyAddresses: string[];
2343
+ predicate?: unknown;
2344
+ }[] | undefined;
2032
2345
  rpcUrl?: string | undefined;
2033
2346
  allowUnpinnedRpcUrl?: boolean | undefined;
2034
2347
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -16,7 +16,7 @@
16
16
  // imports them here so its tool-shape bindings stay in step; the CLI imports
17
17
  // them here so it can build the same args envelope the MCP transport builds.
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.ToolErrorSchema = exports.GetInterpreterInfoInputSchema = exports.RevokePolicyInputSchema = exports.InstallPolicyInputSchema = exports.NETWORK_PASSPHRASES = exports.RPC_URL_BY_NETWORK = exports.PINNED_INTERPRETER_ADDRESS_BY_NETWORK = exports.MAINNET_RPC_URL = exports.TESTNET_RPC_URL = exports.PINNED_INTERPRETER_GRAMMAR_VERSION = exports.PINNED_INTERPRETER_WASM_SHA256 = exports.PINNED_INTERPRETER_MAINNET_ADDRESS = exports.PINNED_INTERPRETER_TESTNET_ADDRESS = exports.VerifyPolicyInputSchema = exports.SimulatePolicyInputSchema = exports.PredicateNodeSchema = exports.PredicateLeafSchema = exports.SynthesizePolicyInputSchema = exports.InterpreterOptionsSchema = exports.RecordTransactionInputSchema = exports.ComposeUserResponsesSchema = exports.RecordedTransactionSchema = exports.ParseConfidenceSchema = exports.OnChainEventSchema = exports.TokenMovementSchema = exports.ContractInvocationSchema = exports.ScValSchema = exports.NetworkSchema = void 0;
19
+ exports.ToolErrorSchema = exports.GetInterpreterInfoInputSchema = exports.RevokePolicyInputSchema = exports.InstallPolicyInputSchema = exports.DeclarePolicyInputSchema = exports.NETWORK_PASSPHRASES = exports.RPC_URL_BY_NETWORK = exports.PINNED_INTERPRETER_ADDRESS_BY_NETWORK = exports.MAINNET_RPC_URL = exports.TESTNET_RPC_URL = exports.PINNED_INTERPRETER_GRAMMAR_VERSION = exports.PINNED_INTERPRETER_WASM_SHA256 = exports.PINNED_INTERPRETER_MAINNET_ADDRESS = exports.PINNED_INTERPRETER_TESTNET_ADDRESS = exports.ObservedRuleSchema = exports.VerifyPolicyInputSchema = exports.SimulatePolicyInputSchema = exports.PredicateNodeSchema = exports.PredicateLeafSchema = exports.SynthesizePolicyInputSchema = exports.InterpreterOptionsSchema = exports.RecordTransactionInputSchema = exports.ComposeUserResponsesSchema = exports.RecordedTransactionSchema = exports.ParseConfidenceSchema = exports.OnChainEventSchema = exports.TokenMovementSchema = exports.ContractInvocationSchema = exports.ScValSchema = exports.NetworkSchema = void 0;
20
20
  const zod_1 = require("zod");
21
21
  const address_ts_1 = require("../synth/address.js");
22
22
  /** Soroban `valid_until` is a u32 ledger sequence; a value above this cannot be
@@ -249,6 +249,26 @@ exports.VerifyPolicyInputSchema = exports.SimulatePolicyInputSchema;
249
249
  * the temporal dead zone (no JS hoisting for `const`). */
250
250
  const MAX_SIGNERS_PER_RULE = 15;
251
251
  const MAX_POLICIES_PER_RULE = 5;
252
+ const SignerDraftSchema = zod_1.z.discriminatedUnion('kind', [
253
+ zod_1.z.object({ kind: zod_1.z.literal('delegated'), address: zod_1.z.string() }),
254
+ zod_1.z.object({ kind: zod_1.z.literal('external'), verifier: zod_1.z.string(), keyBytes: zod_1.z.string() }),
255
+ ]);
256
+ const ContextTypeSchema = zod_1.z.discriminatedUnion('kind', [
257
+ zod_1.z.object({ kind: zod_1.z.literal('default') }),
258
+ zod_1.z.object({ kind: zod_1.z.literal('call_contract'), contract: zod_1.z.string() }),
259
+ zod_1.z.object({ kind: zod_1.z.literal('create_contract'), wasmHash: zod_1.z.string() }),
260
+ ]);
261
+ /** A rule ALREADY on the account, as the caller observed it. Supplying these
262
+ * turns on the cross-rule authority scan: a signer belonging to several rules
263
+ * picks which one applies, so a predicate only constrains a key when the
264
+ * policed rule is the only rule that key is on. */
265
+ exports.ObservedRuleSchema = zod_1.z.object({
266
+ id: zod_1.z.number().int().nonnegative(),
267
+ contextType: ContextTypeSchema,
268
+ signers: zod_1.z.array(SignerDraftSchema),
269
+ policyAddresses: zod_1.z.array(zod_1.z.string()),
270
+ predicate: exports.PredicateNodeSchema.optional(),
271
+ });
252
272
  const ContextRuleDraftSchema = zod_1.z
253
273
  .object({
254
274
  contextRuleType: zod_1.z.discriminatedUnion('kind', [
@@ -338,8 +358,40 @@ exports.NETWORK_PASSPHRASES = {
338
358
  // `sourceAccount` is the signing wallet (G...).
339
359
  const STELLAR_CONTRACT_ADDRESS = /^C[2-7A-Z]{55}$/;
340
360
  const STELLAR_ACCOUNT_ADDRESS = /^G[2-7A-Z]{55}$/;
361
+ // ===== declare_policy =====
362
+ //
363
+ // The declarative front-end: the constraint stated outright, with no
364
+ // transaction to decode. Deliberately NOT a revival of the removed
365
+ // `MandateSpec` - that carried a rolling `spendingLimit` the interpreter
366
+ // cannot evaluate and an `approvalThreshold` needing OZ primitives nobody
367
+ // deployed. Only fields grammar 3 can actually enforce appear here.
368
+ exports.DeclarePolicyInputSchema = zod_1.z
369
+ .object({
370
+ fn: zod_1.z.string().min(1, 'fn must name the method to pin'),
371
+ contract: zod_1.z
372
+ .string()
373
+ .regex(STELLAR_CONTRACT_ADDRESS, 'contract must be a Stellar contract address (C...)')
374
+ .optional(),
375
+ /** Smallest unit, unsigned decimal STRING - an i128 is wider than
376
+ * Number.MAX_SAFE_INTEGER, so a number here would silently round. */
377
+ maxAmount: zod_1.z
378
+ .string()
379
+ .regex(/^[0-9]+$/, 'maxAmount must be an unsigned integer in the smallest unit')
380
+ .optional(),
381
+ amountArgIndex: zod_1.z.number().int().nonnegative().max(U32_MAX).optional(),
382
+ recipients: zod_1.z.array(zod_1.z.string()).min(1, 'recipients must not be empty').optional(),
383
+ recipientArgIndex: zod_1.z.number().int().nonnegative().max(U32_MAX).optional(),
384
+ allowZeroCap: zod_1.z.boolean().optional(),
385
+ })
386
+ .strict();
341
387
  exports.InstallPolicyInputSchema = zod_1.z
342
388
  .object({
389
+ /** Rules already on the account. Supplying them turns on the cross-rule
390
+ * authority scan, which reports every existing rule a signer of this
391
+ * install could name INSTEAD - including an unpoliced one, against which
392
+ * the predicate never runs. Absent means the scan is skipped, and the
393
+ * result says so rather than reporting "no overlaps found". */
394
+ existingRules: zod_1.z.array(exports.ObservedRuleSchema).optional(),
343
395
  /** The smart account contract address (C...) that will receive the rule. */
344
396
  smartAccount: zod_1.z
345
397
  .string()
@@ -0,0 +1,30 @@
1
+ import type { PredicateNode } from '../types.ts';
2
+ export interface PolicyDeclaration {
3
+ /** Method to pin. Required: a predicate with no selector leaf constrains
4
+ * nothing and the contract refuses it at install. */
5
+ fn: string;
6
+ /** Contract to pin, already resolved to a `C...` address. */
7
+ contract?: string;
8
+ /** Upper bound on the call's amount argument, in the token's SMALLEST
9
+ * unit as an unsigned decimal string (25 XLM = "250000000"). */
10
+ maxAmount?: string;
11
+ /** Which argument carries the amount. Defaults to the SEP-41 position. */
12
+ amountArgIndex?: number;
13
+ /** Recipient allowlist. */
14
+ recipients?: string[];
15
+ /** Which argument carries the recipient. Defaults to the SEP-41 position. */
16
+ recipientArgIndex?: number;
17
+ /** A cap of "0" denies every call, so it is refused unless asked for
18
+ * explicitly. A rule that permits nothing is a plausible thing to want and
19
+ * an implausible thing to want by accident. */
20
+ allowZeroCap?: boolean;
21
+ }
22
+ export interface DeclaredPredicate {
23
+ predicate: PredicateNode;
24
+ /** Assumptions the caller should check. Never empty when an argument index
25
+ * was defaulted rather than supplied. */
26
+ warnings: string[];
27
+ }
28
+ /** Lower a declared constraint to a grammar-3 predicate. Pure and total:
29
+ * the same declaration always produces the same predicate. */
30
+ export declare function declarePredicate(d: PolicyDeclaration): DeclaredPredicate;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ // src/synth/declare.ts - build a predicate from a DECLARED constraint.
3
+ //
4
+ // The second synthesis front-end, beside `synthesizeFromRecording`. That one
5
+ // infers a predicate from a transaction that happened; this one takes the
6
+ // constraint the user states outright. No transaction, no decoding, no
7
+ // inference, no RPC: the same declaration always lowers to a byte-identical
8
+ // predicate.
9
+ //
10
+ // This is the surviving half of the removed `MandateSpec`. Two of that type's
11
+ // five fields lowered to things that do not exist - `spendingLimit` became a
12
+ // `window_spent` compare the interpreter cannot evaluate (it is handed one
13
+ // call and keeps no state), and `approvalThreshold` needed OZ built-in policy
14
+ // contracts that were never deployed. Both are deliberately absent here, and
15
+ // the per-call `maxAmount` bound below is the honest replacement for the
16
+ // first: it constrains the amount in THIS call rather than implying a rolling
17
+ // total nothing tracks.
18
+ //
19
+ // What a declaration can say maps one-to-one onto grammar 3:
20
+ // fn -> eq(call_fn, literal_symbol)
21
+ // contract -> eq(call_contract, literal_address)
22
+ // maxAmount -> lte(call_arg(i), literal_i128)
23
+ // recipients -> in(call_arg(j), [literal_address, ...])
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.declarePredicate = declarePredicate;
26
+ const address_ts_1 = require("./address.js");
27
+ /** Argument positions of the SEP-41 `transfer(from, to, amount)` shape. A
28
+ * declaration that names a different method almost certainly has different
29
+ * positions, which is why using either default emits a warning naming the
30
+ * index it assumed - a bound on the wrong argument constrains something the
31
+ * user did not mean and fails silently. */
32
+ const SEP41_RECIPIENT_ARG = 1;
33
+ const SEP41_AMOUNT_ARG = 2;
34
+ /** Lower a declared constraint to a grammar-3 predicate. Pure and total:
35
+ * the same declaration always produces the same predicate. */
36
+ function declarePredicate(d) {
37
+ if (!d.fn || d.fn.trim() === '') {
38
+ throw declareError('SYNTHESIS_ERROR', 'a declaration needs `fn`: the method to pin');
39
+ }
40
+ const warnings = [];
41
+ const children = [
42
+ { op: 'eq', left: { kind: 'call_fn' }, right: { kind: 'literal_symbol', value: d.fn } },
43
+ ];
44
+ if (d.contract !== undefined) {
45
+ if (!(0, address_ts_1.isStellarAddress)(d.contract) || !d.contract.startsWith('C')) {
46
+ throw declareError('SYNTHESIS_ERROR', `contract must be a Stellar contract address (C...), got ${d.contract}`);
47
+ }
48
+ children.push({
49
+ op: 'eq',
50
+ left: { kind: 'call_contract' },
51
+ right: { kind: 'literal_address', value: d.contract },
52
+ });
53
+ }
54
+ if (d.recipients !== undefined) {
55
+ if (d.recipients.length === 0) {
56
+ throw declareError('SYNTHESIS_ERROR', 'recipients was supplied but empty; an empty `in` haystack is refused at decode. Omit it to leave recipients unconstrained.');
57
+ }
58
+ for (const r of d.recipients) {
59
+ if (!(0, address_ts_1.isStellarAddress)(r)) {
60
+ throw declareError('SYNTHESIS_ERROR', `recipient is not a Stellar address: ${r}`);
61
+ }
62
+ }
63
+ const idx = d.recipientArgIndex ?? SEP41_RECIPIENT_ARG;
64
+ if (d.recipientArgIndex === undefined) {
65
+ warnings.push(`recipient allowlist bound to call_arg(${idx}), the SEP-41 \`transfer\` position. If \`${d.fn}\` carries the recipient elsewhere this constrains the wrong argument - pass recipientArgIndex.`);
66
+ }
67
+ children.push({
68
+ op: 'in',
69
+ needle: { kind: 'call_arg', index: idx },
70
+ haystack: d.recipients.map((value) => ({ kind: 'literal_address', value })),
71
+ });
72
+ }
73
+ if (d.maxAmount !== undefined) {
74
+ if (!/^[0-9]+$/.test(d.maxAmount)) {
75
+ throw declareError('SYNTHESIS_ERROR', `maxAmount must be an unsigned integer in the token's smallest unit, got "${d.maxAmount}" (25 XLM = "250000000")`);
76
+ }
77
+ if (d.maxAmount === '0' && d.allowZeroCap !== true) {
78
+ throw declareError('SYNTHESIS_ERROR', 'maxAmount "0" denies every call: no amount satisfies the bound. Set allowZeroCap to declare that deliberately.');
79
+ }
80
+ const idx = d.amountArgIndex ?? SEP41_AMOUNT_ARG;
81
+ if (d.amountArgIndex === undefined) {
82
+ warnings.push(`amount cap bound to call_arg(${idx}), the SEP-41 \`transfer\` position. If \`${d.fn}\` carries the amount elsewhere this caps the wrong argument - pass amountArgIndex.`);
83
+ }
84
+ children.push({
85
+ op: 'lte',
86
+ left: { kind: 'call_arg', index: idx },
87
+ right: { kind: 'literal_i128', value: d.maxAmount },
88
+ });
89
+ }
90
+ // A single conjunct is emitted bare. `and` with one child encodes to
91
+ // different bytes than the child alone, and the extra node buys nothing.
92
+ const predicate = children.length === 1 ? children[0] : { op: 'and', children };
93
+ return { predicate, warnings };
94
+ }
95
+ function declareError(code, message) {
96
+ const err = new Error(message);
97
+ err.code = code;
98
+ err.severity = 'error';
99
+ err.retryable = false;
100
+ throw err;
101
+ }
@@ -1,5 +1,6 @@
1
1
  export { isStellarAddress } from './address.ts';
2
2
  export { type ComposeOptions, type ComposeResult, composeFromRecording, } from './compose-from-recording.ts';
3
+ export { type DeclaredPredicate, declarePredicate, type PolicyDeclaration, } from './declare.ts';
3
4
  export { type IntentFacts, lower } from './lower.ts';
4
5
  export { type DecideScopeOptions, decideScope, type ScopeDecision, scopeToContextRuleType, } from './scope.ts';
5
6
  export { type __TestInterpreterAdapterOptions, type SynthesizeFromRecordingOptions, synthesizeFromRecording, } from './synthesize-from-recording.ts';
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  // src/synth/index.ts - re-export the synthesizer front-ends.
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.synthesizeFromRecording = exports.scopeToContextRuleType = exports.decideScope = exports.lower = exports.composeFromRecording = exports.isStellarAddress = void 0;
4
+ exports.synthesizeFromRecording = exports.scopeToContextRuleType = exports.decideScope = exports.lower = exports.declarePredicate = exports.composeFromRecording = exports.isStellarAddress = void 0;
5
5
  // Exported because `swapRecipientAllowlist` is part of the public synthesis
6
6
  // input: a caller assembling one needs the same validator the schema applies,
7
7
  // and re-deriving it elsewhere means a second address check that can drift.
@@ -9,6 +9,8 @@ var address_ts_1 = require("./address.js");
9
9
  Object.defineProperty(exports, "isStellarAddress", { enumerable: true, get: function () { return address_ts_1.isStellarAddress; } });
10
10
  var compose_from_recording_ts_1 = require("./compose-from-recording.js");
11
11
  Object.defineProperty(exports, "composeFromRecording", { enumerable: true, get: function () { return compose_from_recording_ts_1.composeFromRecording; } });
12
+ var declare_ts_1 = require("./declare.js");
13
+ Object.defineProperty(exports, "declarePredicate", { enumerable: true, get: function () { return declare_ts_1.declarePredicate; } });
12
14
  var lower_ts_1 = require("./lower.js");
13
15
  Object.defineProperty(exports, "lower", { enumerable: true, get: function () { return lower_ts_1.lower; } });
14
16
  var scope_ts_1 = require("./scope.js");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crediolabs/policy-synth",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "license": "MIT",
5
5
  "description": "Off-chain TypeScript synthesis core for the OZ Accounts Policy Builder. Records Soroban transactions, synthesises the minimal policy that permits exactly that flow, verifies it, and returns an unsigned install transaction.",
6
6
  "type": "module",