@crediolabs/policy-synth 0.1.15 → 0.1.16
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/run/index.d.ts +18 -7
- package/dist/run/index.js +56 -60
- package/dist/run/schemas.d.ts +49 -8
- package/dist/run/schemas.js +47 -8
- package/dist-cjs/run/index.d.ts +18 -7
- package/dist-cjs/run/index.js +60 -59
- package/dist-cjs/run/schemas.d.ts +49 -8
- package/dist-cjs/run/schemas.js +48 -9
- package/package.json +1 -1
- package/src/run/index.ts +69 -69
- package/src/run/schemas.ts +51 -8
package/src/run/index.ts
CHANGED
|
@@ -52,18 +52,18 @@ import {
|
|
|
52
52
|
type InstallPolicyInput,
|
|
53
53
|
InstallPolicyInputSchema,
|
|
54
54
|
NETWORK_PASSPHRASES,
|
|
55
|
+
PINNED_INTERPRETER_ADDRESS_BY_NETWORK,
|
|
55
56
|
PINNED_INTERPRETER_GRAMMAR_VERSION,
|
|
56
|
-
PINNED_INTERPRETER_TESTNET_ADDRESS,
|
|
57
57
|
PINNED_INTERPRETER_WASM_SHA256,
|
|
58
58
|
type RecordTransactionInput,
|
|
59
59
|
RecordTransactionInputSchema,
|
|
60
60
|
type RevokePolicyInput,
|
|
61
61
|
RevokePolicyInputSchema,
|
|
62
|
+
RPC_URL_BY_NETWORK,
|
|
62
63
|
type SimulatePolicyInput,
|
|
63
64
|
SimulatePolicyInputSchema,
|
|
64
65
|
type SynthesizePolicyInput,
|
|
65
66
|
SynthesizePolicyInputSchema,
|
|
66
|
-
TESTNET_RPC_URL,
|
|
67
67
|
type VerifyPolicyInput,
|
|
68
68
|
VerifyPolicyInputSchema,
|
|
69
69
|
} from './schemas.ts'
|
|
@@ -86,11 +86,14 @@ export {
|
|
|
86
86
|
GetInterpreterInfoInputSchema,
|
|
87
87
|
InstallPolicyInputSchema,
|
|
88
88
|
InterpreterOptionsSchema,
|
|
89
|
+
MAINNET_RPC_URL,
|
|
89
90
|
MandateSpecSchema,
|
|
90
91
|
NetworkSchema,
|
|
91
92
|
OraclePriceFixtureSchema,
|
|
92
93
|
OzAdapterConfigSchema,
|
|
94
|
+
PINNED_INTERPRETER_ADDRESS_BY_NETWORK,
|
|
93
95
|
PINNED_INTERPRETER_GRAMMAR_VERSION,
|
|
96
|
+
PINNED_INTERPRETER_MAINNET_ADDRESS,
|
|
94
97
|
PINNED_INTERPRETER_TESTNET_ADDRESS,
|
|
95
98
|
PINNED_INTERPRETER_WASM_SHA256,
|
|
96
99
|
PredicateLeafSchema,
|
|
@@ -98,8 +101,10 @@ export {
|
|
|
98
101
|
RecordedTransactionSchema,
|
|
99
102
|
RecordTransactionInputSchema,
|
|
100
103
|
RevokePolicyInputSchema,
|
|
104
|
+
RPC_URL_BY_NETWORK,
|
|
101
105
|
SimulatePolicyInputSchema,
|
|
102
106
|
SynthesizePolicyInputSchema,
|
|
107
|
+
TESTNET_RPC_URL,
|
|
103
108
|
ToolErrorSchema,
|
|
104
109
|
VerifyPolicyInputSchema,
|
|
105
110
|
} from './schemas.ts'
|
|
@@ -305,10 +310,12 @@ export async function runVerifyPolicy(raw: unknown): Promise<ToolResponse<true>>
|
|
|
305
310
|
* separate `interpreter.install` call is needed or possible.
|
|
306
311
|
*
|
|
307
312
|
* Default-deny: an interpreter policy address other than the pinned
|
|
308
|
-
*
|
|
309
|
-
* an interpreter the caller controls); the same
|
|
310
|
-
* RPC URL (the auth nonce the wallet signs
|
|
311
|
-
* gates accept an explicit opt-in flag.
|
|
313
|
+
* interpreter for the selected network is REFUSED (the smart account
|
|
314
|
+
* would delegate to an interpreter the caller controls); the same
|
|
315
|
+
* applies to a non-pinned RPC URL (the auth nonce the wallet signs
|
|
316
|
+
* comes from the RPC). Both gates accept an explicit opt-in flag.
|
|
317
|
+
* Pin selection follows `input.network` (defaults to `testnet` so the
|
|
318
|
+
* pre-mainnet callers keep working unchanged). */
|
|
312
319
|
export async function runInstallPolicy(
|
|
313
320
|
raw: unknown
|
|
314
321
|
): Promise<ToolResponse<BuildInstallPolicyResult>> {
|
|
@@ -320,17 +327,24 @@ export async function runInstallPolicy(
|
|
|
320
327
|
}
|
|
321
328
|
}
|
|
322
329
|
const input: InstallPolicyInput = parsed.data
|
|
330
|
+
const network: Network = input.network ?? 'testnet'
|
|
323
331
|
// ---- Pinning gates (default-deny) ----
|
|
324
|
-
const
|
|
332
|
+
const expectedInterpreter = PINNED_INTERPRETER_ADDRESS_BY_NETWORK[network]
|
|
333
|
+
const expectedRpc = RPC_URL_BY_NETWORK[network]
|
|
334
|
+
const pinningError = enforceInterpreterPin(
|
|
335
|
+
input.rule.policies,
|
|
336
|
+
input.allowUnpinnedInterpreter,
|
|
337
|
+
expectedInterpreter
|
|
338
|
+
)
|
|
325
339
|
if (pinningError) {
|
|
326
340
|
return { ok: false, error: pinningError }
|
|
327
341
|
}
|
|
328
|
-
if (input.rpcUrl && input.rpcUrl !==
|
|
342
|
+
if (input.rpcUrl && input.rpcUrl !== expectedRpc && input.allowUnpinnedRpcUrl !== true) {
|
|
329
343
|
return {
|
|
330
344
|
ok: false,
|
|
331
345
|
error: {
|
|
332
346
|
code: 'INSTALL_BUILD_FAILED',
|
|
333
|
-
message: `install_policy: rpcUrl must equal the pinned
|
|
347
|
+
message: `install_policy: rpcUrl must equal the pinned ${expectedRpc} (${network}); set allowUnpinnedRpcUrl: true to opt in to a custom endpoint`,
|
|
334
348
|
severity: 'error',
|
|
335
349
|
retryable: false,
|
|
336
350
|
remediation: { toolCall: { name: 'install_policy', args: {} } },
|
|
@@ -339,7 +353,7 @@ export async function runInstallPolicy(
|
|
|
339
353
|
}
|
|
340
354
|
let rpcClient: InstallRpcClient
|
|
341
355
|
try {
|
|
342
|
-
rpcClient = buildRpcClientFromInput(input.rpcUrl)
|
|
356
|
+
rpcClient = buildRpcClientFromInput(input.rpcUrl, network)
|
|
343
357
|
} catch (e) {
|
|
344
358
|
return {
|
|
345
359
|
ok: false,
|
|
@@ -355,7 +369,7 @@ export async function runInstallPolicy(
|
|
|
355
369
|
const result = await buildInstallPolicyXdr({
|
|
356
370
|
smartAccount: input.smartAccount,
|
|
357
371
|
sourceAccount: input.sourceAccount,
|
|
358
|
-
networkPassphrase: NETWORK_PASSPHRASES
|
|
372
|
+
networkPassphrase: NETWORK_PASSPHRASES[network],
|
|
359
373
|
rule: input.rule,
|
|
360
374
|
installNonce: input.installNonce,
|
|
361
375
|
encodedPredicate,
|
|
@@ -379,7 +393,8 @@ export async function runInstallPolicy(
|
|
|
379
393
|
*
|
|
380
394
|
* Same RPC pin as install: a non-pinned `rpcUrl` is refused unless
|
|
381
395
|
* `allowUnpinnedRpcUrl: true`. Revoke does not carry an interpreter
|
|
382
|
-
* policy payload, so the interpreter pin is not re-checked here.
|
|
396
|
+
* policy payload, so the interpreter pin is not re-checked here.
|
|
397
|
+
* Pin selection follows `input.network` (defaults to `testnet`). */
|
|
383
398
|
export async function runRevokePolicy(
|
|
384
399
|
raw: unknown
|
|
385
400
|
): Promise<ToolResponse<BuildRevokePolicyResult>> {
|
|
@@ -391,12 +406,14 @@ export async function runRevokePolicy(
|
|
|
391
406
|
}
|
|
392
407
|
}
|
|
393
408
|
const input: RevokePolicyInput = parsed.data
|
|
394
|
-
|
|
409
|
+
const network: Network = input.network ?? 'testnet'
|
|
410
|
+
const expectedRpc = RPC_URL_BY_NETWORK[network]
|
|
411
|
+
if (input.rpcUrl && input.rpcUrl !== expectedRpc && input.allowUnpinnedRpcUrl !== true) {
|
|
395
412
|
return {
|
|
396
413
|
ok: false,
|
|
397
414
|
error: {
|
|
398
415
|
code: 'REVOKE_BUILD_FAILED',
|
|
399
|
-
message: `revoke_policy: rpcUrl must equal the pinned
|
|
416
|
+
message: `revoke_policy: rpcUrl must equal the pinned ${expectedRpc} (${network}); set allowUnpinnedRpcUrl: true to opt in to a custom endpoint`,
|
|
400
417
|
severity: 'error',
|
|
401
418
|
retryable: false,
|
|
402
419
|
remediation: { toolCall: { name: 'revoke_policy', args: {} } },
|
|
@@ -405,7 +422,7 @@ export async function runRevokePolicy(
|
|
|
405
422
|
}
|
|
406
423
|
let rpcClient: InstallRpcClient
|
|
407
424
|
try {
|
|
408
|
-
rpcClient = buildRpcClientFromInput(input.rpcUrl)
|
|
425
|
+
rpcClient = buildRpcClientFromInput(input.rpcUrl, network)
|
|
409
426
|
} catch (e) {
|
|
410
427
|
return {
|
|
411
428
|
ok: false,
|
|
@@ -417,7 +434,7 @@ export async function runRevokePolicy(
|
|
|
417
434
|
smartAccount: input.smartAccount,
|
|
418
435
|
sourceAccount: input.sourceAccount,
|
|
419
436
|
ruleId: input.ruleId,
|
|
420
|
-
networkPassphrase: NETWORK_PASSPHRASES
|
|
437
|
+
networkPassphrase: NETWORK_PASSPHRASES[network],
|
|
421
438
|
rpc: rpcClient,
|
|
422
439
|
...(input.baseFee !== undefined ? { baseFee: input.baseFee } : {}),
|
|
423
440
|
})
|
|
@@ -435,7 +452,15 @@ export async function runRevokePolicy(
|
|
|
435
452
|
* `grammar_version()` comparison. The audit field is deliberately
|
|
436
453
|
* OMITTED (phase-04's "audit #44" has no source of truth in the repo -
|
|
437
454
|
* fabricating it would be a lie on a security surface; the live
|
|
438
|
-
* mismatch check is worth MORE).
|
|
455
|
+
* mismatch check is worth MORE).
|
|
456
|
+
*
|
|
457
|
+
* Network-aware: `input.network` selects which interpreter pin and RPC
|
|
458
|
+
* to use. Mainnet was rolled out 2026-08-04 - the same wasm hash was
|
|
459
|
+
* uploaded to mainnet as was exercised on testnet, so a single
|
|
460
|
+
* `PINNED_INTERPRETER_WASM_SHA256` constant backs both networks
|
|
461
|
+
* (DEPLOYMENTS.md:25-27). The address differs because instance ids are
|
|
462
|
+
* network-scoped. UNAUDITED at the time of writing - see
|
|
463
|
+
* DEPLOYMENTS.md:29-32 for what is still pending. */
|
|
439
464
|
export async function runGetInterpreterInfo(
|
|
440
465
|
raw: unknown
|
|
441
466
|
): Promise<ToolResponse<ReturnType<typeof getInterpreterInfo>>> {
|
|
@@ -448,37 +473,15 @@ export async function runGetInterpreterInfo(
|
|
|
448
473
|
}
|
|
449
474
|
const input: GetInterpreterInfoInput = parsed.data
|
|
450
475
|
const network: Network = input.network ?? 'testnet'
|
|
451
|
-
|
|
452
|
-
// 'mainnet'` stamped on it. DEPLOYMENTS.md:3 says plainly "Testnet only.
|
|
453
|
-
// Nothing is deployed to mainnet yet", so that answer described a contract
|
|
454
|
-
// that does not exist at an address that is not on that network. This tool
|
|
455
|
-
// exists to tell a caller what they are installing against; inventing a
|
|
456
|
-
// mainnet deployment is the same failure as inventing an audit reference.
|
|
457
|
-
if (network === 'mainnet') {
|
|
458
|
-
return {
|
|
459
|
-
ok: false,
|
|
460
|
-
error: {
|
|
461
|
-
code: 'RECORDING_FAILED',
|
|
462
|
-
message:
|
|
463
|
-
'get_interpreter_info: no interpreter is deployed to mainnet. The pinned address and wasm hash are testnet-only; mainnet deployment is gated on the interpreter audit.',
|
|
464
|
-
severity: 'error',
|
|
465
|
-
retryable: false,
|
|
466
|
-
remediation: {
|
|
467
|
-
toolCall: { name: 'get_interpreter_info', args: { network: 'testnet' } },
|
|
468
|
-
},
|
|
469
|
-
},
|
|
470
|
-
}
|
|
471
|
-
}
|
|
476
|
+
const pinnedAddress = PINNED_INTERPRETER_ADDRESS_BY_NETWORK[network]
|
|
472
477
|
try {
|
|
473
478
|
let deployedGrammarVersion: number | undefined
|
|
474
479
|
if (input.verifyLive === true) {
|
|
475
|
-
const rpcClient = buildRpcClientFromInput(input.rpcUrl)
|
|
476
|
-
deployedGrammarVersion = await rpcClient.getContractVersion(
|
|
477
|
-
PINNED_INTERPRETER_TESTNET_ADDRESS
|
|
478
|
-
)
|
|
480
|
+
const rpcClient = buildRpcClientFromInput(input.rpcUrl, network)
|
|
481
|
+
deployedGrammarVersion = await rpcClient.getContractVersion(pinnedAddress)
|
|
479
482
|
}
|
|
480
483
|
const info = getInterpreterInfo({
|
|
481
|
-
pinnedAddress
|
|
484
|
+
pinnedAddress,
|
|
482
485
|
pinnedGrammarVersion: PINNED_INTERPRETER_GRAMMAR_VERSION,
|
|
483
486
|
pinnedWasmHash: PINNED_INTERPRETER_WASM_SHA256,
|
|
484
487
|
network,
|
|
@@ -493,48 +496,45 @@ export async function runGetInterpreterInfo(
|
|
|
493
496
|
}
|
|
494
497
|
}
|
|
495
498
|
|
|
496
|
-
/** Build an InstallRpcClient from an optional URL override
|
|
497
|
-
* to the
|
|
498
|
-
*
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
// Default: public testnet RPC. Matches the brief; the mainnet pin would
|
|
507
|
-
// need a separate deploy and is out of scope for this tool.
|
|
508
|
-
//
|
|
499
|
+
/** Build an InstallRpcClient from an optional URL override + selected
|
|
500
|
+
* network, falling back to the pinned RPC for the network. The caller
|
|
501
|
+
* has already been gated against the pinned URL elsewhere, so the
|
|
502
|
+
* fallback here only ever picks from a finite, audited pair. */
|
|
503
|
+
function buildRpcClientFromInput(
|
|
504
|
+
urlOverride: string | undefined,
|
|
505
|
+
network: Network
|
|
506
|
+
): InstallRpcClient {
|
|
507
|
+
const url = urlOverride ?? RPC_URL_BY_NETWORK[network]
|
|
508
|
+
const passphrase = NETWORK_PASSPHRASES[network]
|
|
509
509
|
// NOT `createRpcServer` - that returns an RpcFetcher, a bare
|
|
510
510
|
// `(hash) => Promise<SorobanTxResponse|null>` for the RECORDER. Passing it
|
|
511
511
|
// here produced a client whose `getAccount` was undefined, so every live
|
|
512
512
|
// call died with "server.getAccount is not a function". The install path
|
|
513
513
|
// needs the full Server surface.
|
|
514
|
-
return rpcClientFromServer(
|
|
515
|
-
new rpc.Server(TESTNET_RPC_URL, { allowHttp: false }),
|
|
516
|
-
NETWORK_PASSPHRASES.testnet
|
|
517
|
-
)
|
|
514
|
+
return rpcClientFromServer(new rpc.Server(url, { allowHttp: false }), passphrase)
|
|
518
515
|
}
|
|
519
516
|
|
|
520
517
|
/** Default-deny: refuse any interpreter policy whose address differs from
|
|
521
|
-
* the pinned
|
|
522
|
-
* can permit anything, so the smart account's
|
|
523
|
-
* to the pinned contract unless the caller
|
|
524
|
-
* `allowUnpinnedInterpreter`. OZ built-in
|
|
525
|
-
* and pass through unchanged. Returns a
|
|
526
|
-
* the run-layer envelope, or null when the
|
|
518
|
+
* the pinned interpreter for the selected network. An interpreter the
|
|
519
|
+
* caller controls can permit anything, so the smart account's
|
|
520
|
+
* authorization must bind to the pinned contract unless the caller
|
|
521
|
+
* explicitly opts in via `allowUnpinnedInterpreter`. OZ built-in
|
|
522
|
+
* policies are not interpreters and pass through unchanged. Returns a
|
|
523
|
+
* ToolError to surface through the run-layer envelope, or null when the
|
|
524
|
+
* policies are all pinned. The caller resolves the expected pin per
|
|
525
|
+
* network; this function stays pure so it is easy to test. */
|
|
527
526
|
function enforceInterpreterPin(
|
|
528
527
|
policies: InstallPolicyInput['rule']['policies'],
|
|
529
|
-
allowUnpinned: boolean | undefined
|
|
528
|
+
allowUnpinned: boolean | undefined,
|
|
529
|
+
expectedInterpreterAddress: string
|
|
530
530
|
): ToolError | null {
|
|
531
531
|
for (const p of policies) {
|
|
532
532
|
if (p.kind !== 'interpreter') continue
|
|
533
|
-
if (p.interpreterAddress ===
|
|
533
|
+
if (p.interpreterAddress === expectedInterpreterAddress) continue
|
|
534
534
|
if (allowUnpinned === true) continue
|
|
535
535
|
return {
|
|
536
536
|
code: 'INSTALL_BUILD_FAILED',
|
|
537
|
-
message: `install_policy: interpreter policy address ${p.interpreterAddress} != pinned ${
|
|
537
|
+
message: `install_policy: interpreter policy address ${p.interpreterAddress} != pinned ${expectedInterpreterAddress}; set allowUnpinnedInterpreter: true to opt in to a non-pinned interpreter`,
|
|
538
538
|
severity: 'error',
|
|
539
539
|
retryable: false,
|
|
540
540
|
remediation: { toolCall: { name: 'install_policy', args: {} } },
|
package/src/run/schemas.ts
CHANGED
|
@@ -475,11 +475,20 @@ const MandateSpecSchemaForRule = z
|
|
|
475
475
|
// reference resolves at module init. They are the new tools on top of the
|
|
476
476
|
// existing four.
|
|
477
477
|
|
|
478
|
-
/** Pinned interpreter address (testnet). Mirrors DEPLOYMENTS.md:
|
|
478
|
+
/** Pinned interpreter address (testnet). Mirrors DEPLOYMENTS.md:128.
|
|
479
479
|
* Single source for the MCP layer; do not embed elsewhere. */
|
|
480
480
|
export const PINNED_INTERPRETER_TESTNET_ADDRESS =
|
|
481
481
|
'CDR4NLV22STCXFGZPNKDQTEANWLF7LZ6AJLY6B7CLJXKHDZGYJWIOKGP'
|
|
482
482
|
|
|
483
|
+
/** Pinned interpreter address (mainnet). Mirrors DEPLOYMENTS.md:18. Mainnet
|
|
484
|
+
* has now been deployed (2026-08-04); the mainnet interpreter IS the binary
|
|
485
|
+
* that was exercised on testnet (same wasm sha256, see
|
|
486
|
+
* PINNED_INTERPRETER_WASM_SHA256). The address differs because instance
|
|
487
|
+
* ids are network-scoped. UNAUDITED at the time of writing - see
|
|
488
|
+
* DEPLOYMENTS.md:29-32 for the recorded decision and what is still pending. */
|
|
489
|
+
export const PINNED_INTERPRETER_MAINNET_ADDRESS =
|
|
490
|
+
'CALZAMUPREIRY4TULBEXIK77AUTOEJG63XLCPUWEHHQDOVK6ZVVS7VQ2'
|
|
491
|
+
|
|
483
492
|
/** Pinned interpreter wasm sha256 (hex). Mirrors DEPLOYMENTS.md:156-157. */
|
|
484
493
|
export const PINNED_INTERPRETER_WASM_SHA256 =
|
|
485
494
|
'6e6c13d93e197aa380303a42cd120f5ddb080dd36ef2a343ee1dbd04ca52a443'
|
|
@@ -494,6 +503,25 @@ export const PINNED_INTERPRETER_GRAMMAR_VERSION = 1
|
|
|
494
503
|
* pinned beside the addresses it is used with rather than shared. */
|
|
495
504
|
export const TESTNET_RPC_URL = 'https://soroban-testnet.stellar.org'
|
|
496
505
|
|
|
506
|
+
/** Mainnet Soroban RPC. Pinned beside MAINNET counterparts so the install
|
|
507
|
+
* and get_interpreter_info gates compare against the matching network's
|
|
508
|
+
* RPC rather than always testnet. Public mainnet endpoint, the same one
|
|
509
|
+
* the deploy script hit during the 2026-08-04 mainnet rollout. */
|
|
510
|
+
export const MAINNET_RPC_URL = 'https://mainnet.sorobanrpc.com'
|
|
511
|
+
|
|
512
|
+
/** Pin + RPC lookup for the gate enforcement. The interpreters' wasm sha256
|
|
513
|
+
* is identical across both networks (the same binary was uploaded both
|
|
514
|
+
* places; DEPLOYMENTS.md:25-27), so `PINNED_INTERPRETER_WASM_SHA256` stays
|
|
515
|
+
* a single constant - only the addresses and RPCs are network-scoped. */
|
|
516
|
+
export const PINNED_INTERPRETER_ADDRESS_BY_NETWORK: Record<Network, string> = {
|
|
517
|
+
testnet: PINNED_INTERPRETER_TESTNET_ADDRESS,
|
|
518
|
+
mainnet: PINNED_INTERPRETER_MAINNET_ADDRESS,
|
|
519
|
+
}
|
|
520
|
+
export const RPC_URL_BY_NETWORK: Record<Network, string> = {
|
|
521
|
+
testnet: TESTNET_RPC_URL,
|
|
522
|
+
mainnet: MAINNET_RPC_URL,
|
|
523
|
+
}
|
|
524
|
+
|
|
497
525
|
/** Soroban `valid_until` window (ledgers) added to the latest ledger when
|
|
498
526
|
* building the auth entry. ~25 minutes at 5s/ledger. */
|
|
499
527
|
export const DEFAULT_AUTH_VALID_UNTIL_LEDGERS = 300
|
|
@@ -513,22 +541,34 @@ export const InstallPolicyInputSchema = z
|
|
|
513
541
|
/** The signer that authorises the install (G... wallet). Used only for
|
|
514
542
|
* sequence number + auth nonce simulation; never persisted, never signed. */
|
|
515
543
|
sourceAccount: z.string(),
|
|
544
|
+
/** Target network for the install. Selects which interpreter pin and
|
|
545
|
+
* which RPC URL are valid by default. Defaults to `testnet` so the
|
|
546
|
+
* pre-mainnet callers keep working: they were always pointing at
|
|
547
|
+
* the testnet pin, so defaulting testnet here is the conservative
|
|
548
|
+
* continuation, NOT a silent fall-through that bypasses the gate.
|
|
549
|
+
* A caller that targets mainnet MUST set this to `mainnet` (the
|
|
550
|
+
* pin and RPC pin do not move by themselves). */
|
|
551
|
+
network: NetworkSchema.optional(),
|
|
516
552
|
/** The proposed rule draft. Mirrors the core `ContextRuleDraft` shape. */
|
|
517
553
|
rule: MandateSpecSchemaForRule,
|
|
518
554
|
/** Per-rule install nonce; 1 for a fresh install. */
|
|
519
555
|
installNonce: z.number().int().positive(),
|
|
520
|
-
/** Optional RPC URL override. Defaults to the
|
|
521
|
-
*
|
|
556
|
+
/** Optional RPC URL override. Defaults to the pinned RPC for the
|
|
557
|
+
* selected `network` (testnet by default, mainnet when
|
|
558
|
+
* `network: 'mainnet'`); the override is refused unless
|
|
522
559
|
* `allowUnpinnedRpcUrl: true`, because the auth nonce and
|
|
523
560
|
* rootInvocation in the response come from whichever RPC answered. */
|
|
524
561
|
rpcUrl: z.string().url().optional(),
|
|
525
562
|
/** Opt-in to using a non-pinned RPC URL. Without this flag the tool
|
|
526
|
-
* refuses any `rpcUrl` other than
|
|
527
|
-
* caller's auth-digest binds to whatever the
|
|
563
|
+
* refuses any `rpcUrl` other than the pinned RPC for the selected
|
|
564
|
+
* network because the caller's auth-digest binds to whatever the
|
|
565
|
+
* RPC returned. */
|
|
528
566
|
allowUnpinnedRpcUrl: z.boolean().optional(),
|
|
529
567
|
/** Opt-in to pointing the rule's interpreter policy at any address
|
|
530
|
-
* other than the pinned
|
|
531
|
-
* that controls the interpreter can permit
|
|
568
|
+
* other than the pinned interpreter for the selected network.
|
|
569
|
+
* Default-deny: a caller that controls the interpreter can permit
|
|
570
|
+
* everything. Selecting `network: 'mainnet'` is NOT an opt-in -
|
|
571
|
+
* the mainnet pin is its own deny-by-default anchor. */
|
|
532
572
|
allowUnpinnedInterpreter: z.boolean().optional(),
|
|
533
573
|
/** Base fee in stroops; defaults to BASE_FEE (100). */
|
|
534
574
|
baseFee: z.number().int().positive().optional(),
|
|
@@ -547,9 +587,12 @@ export const RevokePolicyInputSchema = z
|
|
|
547
587
|
* verify, since the account's source is not in this repo. Proven on
|
|
548
588
|
* testnet: the account's deployer can revoke. */
|
|
549
589
|
sourceAccount: z.string(),
|
|
590
|
+
/** Target network for the revoke. Same `testnet`-default as install,
|
|
591
|
+
* so pre-mainnet callers keep working without an explicit flag. */
|
|
592
|
+
network: NetworkSchema.optional(),
|
|
550
593
|
/** The rule id to remove from the smart account + interpreter. */
|
|
551
594
|
ruleId: z.number().int().nonnegative(),
|
|
552
|
-
/** Optional RPC URL override; same pin as install. */
|
|
595
|
+
/** Optional RPC URL override; same per-network pin as install. */
|
|
553
596
|
rpcUrl: z.string().url().optional(),
|
|
554
597
|
/** Opt-in to using a non-pinned RPC URL. Same semantics as install. */
|
|
555
598
|
allowUnpinnedRpcUrl: z.boolean().optional(),
|