@crediolabs/policy-synth 0.2.0 → 0.3.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.
- package/README.md +4 -4
- package/dist/adapters/interpreter/adapter.d.ts +19 -25
- package/dist/adapters/interpreter/adapter.js +54 -487
- package/dist/adapters/interpreter/index.d.ts +1 -1
- package/dist/adapters/interpreter/index.js +1 -1
- package/dist/adapters/oz/adapter.js +1 -15
- package/dist/errors.d.ts +1 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.js +1 -4
- package/dist/install/build-add-context-rule.d.ts +3 -19
- package/dist/install/build-add-context-rule.js +3 -98
- package/dist/install/build-install-policy.d.ts +4 -53
- package/dist/install/build-install-policy.js +44 -123
- package/dist/install/index.d.ts +0 -2
- package/dist/install/index.js +0 -7
- package/dist/ir/index.d.ts +1 -1
- package/dist/ir/types.d.ts +7 -76
- package/dist/ir/types.js +0 -2
- package/dist/predicate/decode.d.ts +1 -1
- package/dist/predicate/decode.js +2 -70
- package/dist/predicate/encode.js +45 -229
- package/dist/predicate/from-json.js +1 -42
- package/dist/record/decode.js +3 -8
- package/dist/review-card/builder.d.ts +4 -3
- package/dist/review-card/builder.js +18 -162
- package/dist/review-card/cross-check.js +2 -105
- package/dist/review-card/render-leaf.d.ts +4 -0
- package/dist/review-card/render-leaf.js +47 -0
- package/dist/run/index.d.ts +36 -139
- package/dist/run/index.js +125 -437
- package/dist/run/schemas.d.ts +168 -1929
- package/dist/run/schemas.js +40 -269
- package/dist/seams/types.d.ts +0 -2
- package/dist/simulate/deny-cases.d.ts +26 -0
- package/dist/simulate/deny-cases.js +282 -0
- package/dist/simulate/evaluate.d.ts +18 -0
- package/dist/simulate/evaluate.js +271 -0
- package/dist/simulate/index.d.ts +4 -0
- package/dist/simulate/index.js +8 -0
- package/dist/synth/compose-from-recording.d.ts +24 -51
- package/dist/synth/compose-from-recording.js +120 -282
- package/dist/synth/deny-cases.d.ts +5 -7
- package/dist/synth/deny-cases.js +23 -373
- package/dist/synth/evaluate.d.ts +0 -21
- package/dist/synth/evaluate.js +56 -336
- package/dist/synth/index.d.ts +0 -5
- package/dist/synth/index.js +0 -5
- package/dist/synth/permit-context.d.ts +2 -9
- package/dist/synth/permit-context.js +1 -82
- package/dist/synth/synthesize-from-recording.d.ts +1 -29
- package/dist/synth/synthesize-from-recording.js +41 -356
- package/dist/types.d.ts +8 -60
- package/dist/types.js +5 -2
- package/dist/verify/simulate.d.ts +1 -14
- package/dist/verify/simulate.js +2 -88
- package/dist/verify/verify.d.ts +1 -7
- package/dist/verify/verify.js +0 -8
- package/dist-cjs/adapters/interpreter/adapter.d.ts +19 -25
- package/dist-cjs/adapters/interpreter/adapter.js +56 -489
- package/dist-cjs/adapters/interpreter/index.d.ts +1 -1
- package/dist-cjs/adapters/interpreter/index.js +1 -2
- package/dist-cjs/adapters/oz/adapter.js +1 -15
- package/dist-cjs/errors.d.ts +1 -1
- package/dist-cjs/index.d.ts +1 -4
- package/dist-cjs/index.js +1 -4
- package/dist-cjs/install/build-add-context-rule.d.ts +3 -19
- package/dist-cjs/install/build-add-context-rule.js +1 -97
- package/dist-cjs/install/build-install-policy.d.ts +4 -53
- package/dist-cjs/install/build-install-policy.js +44 -122
- package/dist-cjs/install/index.d.ts +0 -2
- package/dist-cjs/install/index.js +2 -23
- package/dist-cjs/ir/index.d.ts +1 -1
- package/dist-cjs/ir/types.d.ts +7 -76
- package/dist-cjs/ir/types.js +0 -2
- package/dist-cjs/predicate/decode.d.ts +1 -1
- package/dist-cjs/predicate/decode.js +2 -70
- package/dist-cjs/predicate/encode.js +45 -229
- package/dist-cjs/predicate/from-json.js +1 -42
- package/dist-cjs/record/decode.js +3 -8
- package/dist-cjs/review-card/builder.d.ts +4 -3
- package/dist-cjs/review-card/builder.js +23 -167
- package/dist-cjs/review-card/cross-check.js +7 -110
- package/dist-cjs/review-card/render-leaf.d.ts +4 -0
- package/dist-cjs/review-card/render-leaf.js +52 -0
- package/dist-cjs/run/index.d.ts +36 -139
- package/dist-cjs/run/index.js +125 -444
- package/dist-cjs/run/schemas.d.ts +168 -1929
- package/dist-cjs/run/schemas.js +41 -270
- package/dist-cjs/seams/types.d.ts +0 -2
- package/dist-cjs/simulate/deny-cases.d.ts +26 -0
- package/dist-cjs/simulate/deny-cases.js +286 -0
- package/dist-cjs/simulate/evaluate.d.ts +18 -0
- package/dist-cjs/simulate/evaluate.js +274 -0
- package/dist-cjs/simulate/index.d.ts +4 -0
- package/dist-cjs/simulate/index.js +13 -0
- package/dist-cjs/synth/compose-from-recording.d.ts +24 -51
- package/dist-cjs/synth/compose-from-recording.js +120 -282
- package/dist-cjs/synth/deny-cases.d.ts +5 -7
- package/dist-cjs/synth/deny-cases.js +23 -374
- package/dist-cjs/synth/evaluate.d.ts +0 -21
- package/dist-cjs/synth/evaluate.js +57 -337
- package/dist-cjs/synth/index.d.ts +0 -5
- package/dist-cjs/synth/index.js +1 -11
- package/dist-cjs/synth/permit-context.d.ts +2 -9
- package/dist-cjs/synth/permit-context.js +1 -82
- package/dist-cjs/synth/synthesize-from-recording.d.ts +1 -29
- package/dist-cjs/synth/synthesize-from-recording.js +39 -354
- package/dist-cjs/types.d.ts +8 -60
- package/dist-cjs/types.js +6 -3
- package/dist-cjs/verify/simulate.d.ts +1 -14
- package/dist-cjs/verify/simulate.js +2 -88
- package/dist-cjs/verify/verify.d.ts +1 -7
- package/dist-cjs/verify/verify.js +0 -8
- package/package.json +4 -4
- package/src/adapters/interpreter/adapter.ts +76 -572
- package/src/errors.ts +0 -19
- package/src/index.ts +1 -4
- package/src/install/build-add-context-rule.ts +5 -139
- package/src/install/build-install-policy.ts +93 -214
- package/src/install/index.ts +0 -34
- package/src/predicate/decode.ts +2 -70
- package/src/predicate/encode.ts +49 -261
- package/src/predicate/from-json.ts +1 -43
- package/src/record/decode.ts +3 -8
- package/src/review-card/builder.ts +19 -168
- package/src/review-card/cross-check.ts +3 -105
- package/src/review-card/render-leaf.ts +48 -0
- package/src/run/index.ts +144 -572
- package/src/run/schemas.ts +42 -291
- package/src/simulate/deny-cases.ts +334 -0
- package/src/simulate/evaluate.ts +284 -0
- package/src/simulate/index.ts +11 -0
- package/src/synth/compose-from-recording.ts +148 -347
- package/src/synth/index.ts +0 -13
- package/src/synth/synthesize-from-recording.ts +43 -456
- package/src/types.ts +13 -49
- package/dist/install/authority-overlap.d.ts +0 -134
- package/dist/install/authority-overlap.js +0 -0
- package/dist/install/build-install-predicate.d.ts +0 -96
- package/dist/install/build-install-predicate.js +0 -444
- package/dist/install/build-merge-policy.d.ts +0 -70
- package/dist/install/build-merge-policy.js +0 -130
- package/dist/install/plan-merge-policy.d.ts +0 -49
- package/dist/install/plan-merge-policy.js +0 -86
- package/dist/install/read-account-rules.d.ts +0 -100
- package/dist/install/read-account-rules.js +0 -283
- package/dist-cjs/install/authority-overlap.d.ts +0 -134
- package/dist-cjs/install/authority-overlap.js +0 -0
- package/dist-cjs/install/build-install-predicate.d.ts +0 -96
- package/dist-cjs/install/build-install-predicate.js +0 -479
- package/dist-cjs/install/build-merge-policy.d.ts +0 -70
- package/dist-cjs/install/build-merge-policy.js +0 -134
- package/dist-cjs/install/plan-merge-policy.d.ts +0 -49
- package/dist-cjs/install/plan-merge-policy.js +0 -90
- package/dist-cjs/install/read-account-rules.d.ts +0 -100
- package/dist-cjs/install/read-account-rules.js +0 -296
- package/src/adapters/interpreter/index.ts +0 -8
- package/src/adapters/oz/adapter.ts +0 -376
- package/src/adapters/oz/index.ts +0 -9
- package/src/codegen/compile-gate.ts +0 -167
- package/src/codegen/index.ts +0 -17
- package/src/codegen/template.ts +0 -165
- package/src/install/authority-overlap.ts +0 -0
- package/src/install/build-merge-policy.ts +0 -219
- package/src/install/plan-merge-policy.ts +0 -133
- package/src/install/read-account-rules.ts +0 -376
- package/src/ir/index.ts +0 -13
- package/src/ir/types.ts +0 -132
- package/src/mandate/index.ts +0 -4
- package/src/mandate/to-ir.ts +0 -71
- package/src/mandate/types.ts +0 -21
- package/src/seams/index.ts +0 -11
- package/src/seams/types.ts +0 -81
- package/src/synth/deny-cases.ts +0 -663
- package/src/synth/evaluate.ts +0 -613
- package/src/synth/harness.ts +0 -68
- package/src/synth/minimize.ts +0 -48
- package/src/synth/permit-context.ts +0 -136
- package/src/synth/predicate-literals.ts +0 -27
- package/src/synth/synthesize-from-mandate.ts +0 -82
- package/src/verify/envelope.ts +0 -28
- package/src/verify/index.ts +0 -5
- package/src/verify/simulate.ts +0 -311
- package/src/verify/verify.ts +0 -243
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// expiry.validUntilLedger -> ContextRuleDraft.validUntilLedger
|
|
8
8
|
// window_spent(t,w) <= L -> `spending_limit` primitive
|
|
9
9
|
// approval.threshold -> `simple_threshold` / `weighted_threshold`
|
|
10
|
-
// Anything needing a capability this backend lacks (
|
|
10
|
+
// Anything needing a capability this backend lacks (invocation
|
|
11
11
|
// count, per-arg comparison/allowlist, guard, nested boolean predicate) is NOT
|
|
12
12
|
// emitted: it is named in `uncovered` and `covered` is set false. Nothing is
|
|
13
13
|
// silently dropped.
|
|
@@ -45,8 +45,6 @@ const CAPABILITIES = {
|
|
|
45
45
|
supportsSpendWindow: true,
|
|
46
46
|
supportsThreshold: true,
|
|
47
47
|
supportsTimeExpiry: true,
|
|
48
|
-
supportsOraclePrice: false,
|
|
49
|
-
supportsInvocationCount: false,
|
|
50
48
|
supportsGeneralPredicate: false,
|
|
51
49
|
};
|
|
52
50
|
function createOzAdapter(config) {
|
|
@@ -211,10 +209,6 @@ function matchSpendingLimit(c) {
|
|
|
211
209
|
* cannot express, used to populate `uncovered`. */
|
|
212
210
|
function describeCondition(cond) {
|
|
213
211
|
switch (cond.op) {
|
|
214
|
-
case 'slippage_floor':
|
|
215
|
-
// OZ primitives bound a value against a constant; this bounds one call
|
|
216
|
-
// argument against another, which none of them can express.
|
|
217
|
-
return `slippage floor on arg[${cond.outArgIndex}] (OZ built-ins cannot bound one argument against another)`;
|
|
218
212
|
case 'in':
|
|
219
213
|
return `value allowlist on ${describeSelector(cond.selector)} (arg allowlist)`;
|
|
220
214
|
case 'eq_seq':
|
|
@@ -227,10 +221,6 @@ function describeCondition(cond) {
|
|
|
227
221
|
case 'compare': {
|
|
228
222
|
const s = cond.compare.selector;
|
|
229
223
|
switch (s.kind) {
|
|
230
|
-
case 'oracle_price':
|
|
231
|
-
return `oracle price condition on ${s.asset} (oracle price not supported in week-1)`;
|
|
232
|
-
case 'invocation_count':
|
|
233
|
-
return `invocation-count window (${s.windowSeconds}s) condition (not supported in week-1)`;
|
|
234
224
|
case 'window_spent':
|
|
235
225
|
return `spend-window comparison with operator '${cond.compare.operator}' (only 'lte' lowers to spending_limit)`;
|
|
236
226
|
case 'amount':
|
|
@@ -264,10 +254,6 @@ function describeSelector(s) {
|
|
|
264
254
|
return `amount(${s.token})`;
|
|
265
255
|
case 'window_spent':
|
|
266
256
|
return `window_spent(${s.token})`;
|
|
267
|
-
case 'oracle_price':
|
|
268
|
-
return `oracle_price(${s.asset})`;
|
|
269
|
-
case 'invocation_count':
|
|
270
|
-
return `invocation_count(${s.windowSeconds}s)`;
|
|
271
257
|
case 'calldata':
|
|
272
258
|
return `calldata[${s.offset}:${s.offset + s.length}]`;
|
|
273
259
|
default:
|
package/dist-cjs/errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ErrorCode = 'RECORDING_FAILED' | 'RECORDING_VALIDATION_FAILED' | 'SCOPE_UNRESOLVED' | 'SYNTHESIS_ERROR' | 'MALFORMED_PREDICATE' | 'SIMULATION_ERROR' | 'VERIFICATION_FAILED' | 'DENY_CASE_FAILURE' | 'PERMIT_CASE_FAILED' | 'SUMMARY_DRIFT' | 'INSTALL_BUILD_FAILED' | 'INSTALL_CONFIRM_MISSING' | 'INSTALL_CONFIRM_EXPIRED' | 'REVOKE_BUILD_FAILED' | 'REVOKE_CONFIRM_MISSING' | 'USER_REJECTED_SIGN' | 'WALLET_TIMEOUT' | 'WALLET_UNAVAILABLE' | 'PREDICATE_TOO_LARGE' | 'PREDICATE_TOO_DEEP' | 'TOO_MANY_LEAVES' | 'IN_OPERAND_LIMIT' | '
|
|
1
|
+
export type ErrorCode = 'RECORDING_FAILED' | 'RECORDING_VALIDATION_FAILED' | 'SCOPE_UNRESOLVED' | 'SYNTHESIS_ERROR' | 'MALFORMED_PREDICATE' | 'SIMULATION_ERROR' | 'VERIFICATION_FAILED' | 'DENY_CASE_FAILURE' | 'PERMIT_CASE_FAILED' | 'SUMMARY_DRIFT' | 'INSTALL_BUILD_FAILED' | 'INSTALL_CONFIRM_MISSING' | 'INSTALL_CONFIRM_EXPIRED' | 'REVOKE_BUILD_FAILED' | 'REVOKE_CONFIRM_MISSING' | 'USER_REJECTED_SIGN' | 'WALLET_TIMEOUT' | 'WALLET_UNAVAILABLE' | 'PREDICATE_TOO_LARGE' | 'PREDICATE_TOO_DEEP' | 'TOO_MANY_LEAVES' | 'IN_OPERAND_LIMIT' | 'POLICY_CAP_EXCEEDED' | 'WASM_TOO_LARGE' | 'MASTER_AUTH_REQUIRED' | 'NONCE_REPLAY' | 'VERSION_MISMATCH' | 'ARITHMETIC_OVERFLOW' | 'AMOUNT_OVERFLOW' | 'RULE_SIGNERS_CHANGED' | 'SCOPE_SELF_CALL' | 'ARG_MISMATCH' | 'CONTRACT_SCOPE' | 'UNSUPPORTED_NODE' | 'STATEFUL_BOUND' | 'NOT_IN_ALLOWLIST' | 'COMPILE_OK' | 'COMPILE_GATE_FAILED';
|
|
2
2
|
export interface ToolError {
|
|
3
3
|
code: ErrorCode;
|
|
4
4
|
message: string;
|
package/dist-cjs/index.d.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
export * from './adapters/oz/index.ts';
|
|
2
1
|
export * from './errors.ts';
|
|
3
|
-
export * from './ir/index.ts';
|
|
4
|
-
export * from './mandate/index.ts';
|
|
5
2
|
export * from './predicate/index.ts';
|
|
6
3
|
export * from './record/index.ts';
|
|
7
4
|
export * from './registry/index.ts';
|
|
8
5
|
export * from './review-card/index.ts';
|
|
9
|
-
export * from './
|
|
6
|
+
export * from './simulate/index.ts';
|
|
10
7
|
export * from './synth/index.ts';
|
|
11
8
|
export * from './types.ts';
|
package/dist-cjs/index.js
CHANGED
|
@@ -14,14 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./adapters/oz/index.js"), exports);
|
|
18
17
|
__exportStar(require("./errors.js"), exports);
|
|
19
|
-
__exportStar(require("./ir/index.js"), exports);
|
|
20
|
-
__exportStar(require("./mandate/index.js"), exports);
|
|
21
18
|
__exportStar(require("./predicate/index.js"), exports);
|
|
22
19
|
__exportStar(require("./record/index.js"), exports);
|
|
23
20
|
__exportStar(require("./registry/index.js"), exports);
|
|
24
21
|
__exportStar(require("./review-card/index.js"), exports);
|
|
25
|
-
__exportStar(require("./
|
|
22
|
+
__exportStar(require("./simulate/index.js"), exports);
|
|
26
23
|
__exportStar(require("./synth/index.js"), exports);
|
|
27
24
|
__exportStar(require("./types.js"), exports);
|
|
@@ -8,8 +8,8 @@ export interface BuildAddContextRuleArgs {
|
|
|
8
8
|
* admin session. The deployer signs the install call via the admin rule
|
|
9
9
|
* already on the account, which is separate from this list. */
|
|
10
10
|
signers: SignerDraft[];
|
|
11
|
-
/** The policy(ies) to attach to the new rule.
|
|
12
|
-
*
|
|
11
|
+
/** The policy(ies) to attach to the new rule. `interpreter` is the only
|
|
12
|
+
* kind: the OpenZeppelin built-in backend was removed. */
|
|
13
13
|
policies: PolicyRef[];
|
|
14
14
|
/** Per-policy install nonce; 1 for a fresh install. */
|
|
15
15
|
installNonce: number;
|
|
@@ -19,19 +19,11 @@ export interface BuildAddContextRuleArgs {
|
|
|
19
19
|
encodedPredicate: string;
|
|
20
20
|
/** Hex sha256 of the canonical predicate XDR bytes. */
|
|
21
21
|
predicateHash: string;
|
|
22
|
-
/** Per-policy oracle overrides. Absent on any key uses the wasm default. */
|
|
23
|
-
oracleParams?: {
|
|
24
|
-
maxStalenessSeconds?: number;
|
|
25
|
-
maxDeviationBps?: number;
|
|
26
|
-
/** Bound between the primary and secondary feeds. Tighten-only against
|
|
27
|
-
* the wasm default, like the other two. */
|
|
28
|
-
maxCrossFeedDeviationBps?: number;
|
|
29
|
-
};
|
|
30
22
|
/** Hard pin to the interpreter's wasm grammar. Refusing here fails closed
|
|
31
23
|
* before the chain does; the contract will refuse a mismatch again. */
|
|
32
24
|
grammarVersion?: number;
|
|
33
25
|
}
|
|
34
|
-
export declare const DEFAULT_GRAMMAR_VERSION:
|
|
26
|
+
export declare const DEFAULT_GRAMMAR_VERSION: 3;
|
|
35
27
|
/** The verb `add_context_rule` takes on the wire. */
|
|
36
28
|
export declare const ADD_CONTEXT_RULE_SYMBOL: "add_context_rule";
|
|
37
29
|
/** Tuple of `ScVal` arguments to pass to `Operation.invokeHostFunction` for
|
|
@@ -46,11 +38,3 @@ export type AddContextRuleArgs = readonly [
|
|
|
46
38
|
/** Build the `add_context_rule` invocation args. Throws a `ToolError`-shaped
|
|
47
39
|
* error on limit breaches or malformed input. */
|
|
48
40
|
export declare function buildAddContextRuleArgs(draft: ContextRuleDraft, args: BuildAddContextRuleArgs): AddContextRuleArgs;
|
|
49
|
-
/** The `PolicyInstallParams` ScVal an interpreter policy receives.
|
|
50
|
-
*
|
|
51
|
-
* Exported so the merge remedy can re-install a policy through `add_policy`
|
|
52
|
-
* using the SAME encoder as a fresh install. Re-implementing it would risk
|
|
53
|
-
* exactly the drift this file warns about: the field order is ABI-significant
|
|
54
|
-
* and a differing encoding yields a rule that denies every call. */
|
|
55
|
-
export type PolicyInstallParamArgs = Pick<BuildAddContextRuleArgs, 'encodedPredicate' | 'predicateHash' | 'installNonce' | 'oracleParams' | 'grammarVersion'>;
|
|
56
|
-
export declare function encodePolicyInstallParams(args: PolicyInstallParamArgs): xdr.ScVal;
|
|
@@ -50,11 +50,10 @@
|
|
|
50
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
51
|
exports.ADD_CONTEXT_RULE_SYMBOL = exports.DEFAULT_GRAMMAR_VERSION = void 0;
|
|
52
52
|
exports.buildAddContextRuleArgs = buildAddContextRuleArgs;
|
|
53
|
-
exports.encodePolicyInstallParams = encodePolicyInstallParams;
|
|
54
53
|
const node_crypto_1 = require("node:crypto");
|
|
55
54
|
const stellar_sdk_1 = require("@stellar/stellar-sdk");
|
|
56
55
|
const types_ts_1 = require("../types.js");
|
|
57
|
-
exports.DEFAULT_GRAMMAR_VERSION =
|
|
56
|
+
exports.DEFAULT_GRAMMAR_VERSION = types_ts_1.GRAMMAR_VERSION;
|
|
58
57
|
/** The verb `add_context_rule` takes on the wire. */
|
|
59
58
|
exports.ADD_CONTEXT_RULE_SYMBOL = 'add_context_rule';
|
|
60
59
|
/** Build the `add_context_rule` invocation args. Throws a `ToolError`-shaped
|
|
@@ -70,15 +69,6 @@ function buildAddContextRuleArgs(draft, args) {
|
|
|
70
69
|
if (args.signers.length === 0 && args.policies.length === 0) {
|
|
71
70
|
throw limitError('INSTALL_BUILD_FAILED', 'a rule with no signers and no policies is refused at install (NoSignersAndPolicies)');
|
|
72
71
|
}
|
|
73
|
-
// OZ's spending_limit caps spend on the CONTEXT CONTRACT - it takes no
|
|
74
|
-
// token argument - so it refuses a Default rule on chain with a bare
|
|
75
|
-
// #3227. Say which rule shape it needs here, before the encoding, rather
|
|
76
|
-
// than letting the user read a numeric trap.
|
|
77
|
-
if (args.policies.some((p) => p.kind === 'oz_builtin' && p.primitive.primitive === 'spending_limit')) {
|
|
78
|
-
if (draft.contextRuleType.kind !== 'call_contract') {
|
|
79
|
-
throw limitError('INSTALL_BUILD_FAILED', `spending_limit caps spend on the rule's context contract, so the rule must be call_contract-scoped to that token - a "${draft.contextRuleType.kind}" rule is refused on chain (#3227)`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
72
|
// ---- 2. context_type encoding ----
|
|
83
73
|
const contextType = encodeContextRuleType(draft.contextRuleType);
|
|
84
74
|
// ---- 3. name (string) + valid_until (Option<u32>) ----
|
|
@@ -100,15 +90,6 @@ const POLICY_INSTALL_PARAM_FIELDS = [
|
|
|
100
90
|
'install_nonce',
|
|
101
91
|
'predicate',
|
|
102
92
|
'predicate_hash',
|
|
103
|
-
'oracle_max_staleness_seconds',
|
|
104
|
-
'oracle_max_deviation_bps',
|
|
105
|
-
// Cross-feed divergence bound. A field added to the contract's
|
|
106
|
-
// `PolicyInstallParams` changes the ABI: the host unpacks the map by field
|
|
107
|
-
// count, so omitting one fails the entire install with
|
|
108
|
-
// `Error(Object, UnexpectedSize)` and no indication of which field is
|
|
109
|
-
// missing. The Rust tests build that struct in Rust and never cross this
|
|
110
|
-
// boundary, so only a real install against a deployed contract catches it.
|
|
111
|
-
'oracle_max_xfeed_dev_bps',
|
|
112
93
|
];
|
|
113
94
|
function encodeContextRuleType(rule) {
|
|
114
95
|
switch (rule.kind) {
|
|
@@ -150,75 +131,10 @@ function encodePoliciesMap(args) {
|
|
|
150
131
|
val: encodePolicyInstallParams(args),
|
|
151
132
|
}));
|
|
152
133
|
}
|
|
153
|
-
else if (ref.kind === 'oz_builtin') {
|
|
154
|
-
entries.push(new stellar_sdk_1.xdr.ScMapEntry({
|
|
155
|
-
key: stellar_sdk_1.Address.fromString(ref.instanceAddress).toScVal(),
|
|
156
|
-
val: encodeOzPrimitiveParams(ref.primitive),
|
|
157
|
-
}));
|
|
158
|
-
}
|
|
159
134
|
}
|
|
160
135
|
entries.sort(sortByScValSymbolString);
|
|
161
136
|
return stellar_sdk_1.xdr.ScVal.scvMap(entries);
|
|
162
137
|
}
|
|
163
|
-
/** Encode the install params for one OZ built-in policy.
|
|
164
|
-
*
|
|
165
|
-
* Field names and types are read from the deployed contracts' own spec:
|
|
166
|
-
* SpendingLimitAccountParams { period_ledgers: u32, spending_limit: i128 }
|
|
167
|
-
* SimpleThresholdAccountParams { threshold: u32 }
|
|
168
|
-
* WeightedThresholdAccountParams { signer_weights: map, threshold: u32 }
|
|
169
|
-
* Entries are symbol-string ordered for the same reason the interpreter's
|
|
170
|
-
* are: the host orders by symbol, not by XDR bytes.
|
|
171
|
-
*/
|
|
172
|
-
function encodeOzPrimitiveParams(primitive) {
|
|
173
|
-
const entries = [];
|
|
174
|
-
const push = (name, val) => entries.push(new stellar_sdk_1.xdr.ScMapEntry({ key: stellar_sdk_1.xdr.ScVal.scvSymbol(name), val }));
|
|
175
|
-
const p = primitive.params;
|
|
176
|
-
switch (primitive.primitive) {
|
|
177
|
-
case 'spending_limit': {
|
|
178
|
-
const limit = p.spending_limit;
|
|
179
|
-
const period = p.period_ledgers;
|
|
180
|
-
if (typeof limit !== 'string' || typeof period !== 'number') {
|
|
181
|
-
throw limitError('INSTALL_BUILD_FAILED', 'spending_limit needs { spending_limit: string; period_ledgers: number }');
|
|
182
|
-
}
|
|
183
|
-
push('period_ledgers', stellar_sdk_1.xdr.ScVal.scvU32(period));
|
|
184
|
-
push('spending_limit', encodeI128(limit));
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
case 'simple_threshold': {
|
|
188
|
-
const threshold = p.threshold;
|
|
189
|
-
if (typeof threshold !== 'number') {
|
|
190
|
-
throw limitError('INSTALL_BUILD_FAILED', 'simple_threshold needs { threshold: number }');
|
|
191
|
-
}
|
|
192
|
-
push('threshold', stellar_sdk_1.xdr.ScVal.scvU32(threshold));
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
case 'weighted_threshold': {
|
|
196
|
-
const threshold = p.threshold;
|
|
197
|
-
const weights = p.weights;
|
|
198
|
-
if (typeof threshold !== 'number' || !weights) {
|
|
199
|
-
throw limitError('INSTALL_BUILD_FAILED', 'weighted_threshold needs { threshold: number; weights: Record<address, number> }');
|
|
200
|
-
}
|
|
201
|
-
const weightEntries = Object.entries(weights)
|
|
202
|
-
.map(([addr, w]) => new stellar_sdk_1.xdr.ScMapEntry({
|
|
203
|
-
key: stellar_sdk_1.Address.fromString(addr).toScVal(),
|
|
204
|
-
val: stellar_sdk_1.xdr.ScVal.scvU32(w),
|
|
205
|
-
}))
|
|
206
|
-
.sort((a, b) => (a.key().toXDR('base64') < b.key().toXDR('base64') ? -1 : 1));
|
|
207
|
-
push('signer_weights', stellar_sdk_1.xdr.ScVal.scvMap(weightEntries));
|
|
208
|
-
push('threshold', stellar_sdk_1.xdr.ScVal.scvU32(threshold));
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
entries.sort((a, b) => sortBySymbolString(a.key(), b.key()));
|
|
213
|
-
return stellar_sdk_1.xdr.ScVal.scvMap(entries);
|
|
214
|
-
}
|
|
215
|
-
function encodeI128(value) {
|
|
216
|
-
const v = BigInt(value);
|
|
217
|
-
return stellar_sdk_1.xdr.ScVal.scvI128(new stellar_sdk_1.xdr.Int128Parts({
|
|
218
|
-
hi: new stellar_sdk_1.xdr.Int64(BigInt.asIntN(64, v >> 64n)),
|
|
219
|
-
lo: new stellar_sdk_1.xdr.Uint64(BigInt.asUintN(64, v)),
|
|
220
|
-
}));
|
|
221
|
-
}
|
|
222
138
|
function encodePolicyInstallParams(args) {
|
|
223
139
|
const predicate = Buffer.from(args.encodedPredicate, 'base64');
|
|
224
140
|
const computedHash = (0, node_crypto_1.createHash)('sha256').update(predicate).digest('hex');
|
|
@@ -228,15 +144,11 @@ function encodePolicyInstallParams(args) {
|
|
|
228
144
|
// Per-field value encoders. Each entry returns the ScVal to drop into the
|
|
229
145
|
// map; the key symbol comes from the surrounding loop. Adding a field is
|
|
230
146
|
// one row here plus the symbol in POLICY_INSTALL_PARAM_FIELDS.
|
|
231
|
-
const oracleParams = args.oracleParams;
|
|
232
147
|
const valueFor = {
|
|
233
148
|
grammar_version: () => stellar_sdk_1.xdr.ScVal.scvU32(args.grammarVersion ?? exports.DEFAULT_GRAMMAR_VERSION),
|
|
234
149
|
install_nonce: () => stellar_sdk_1.xdr.ScVal.scvU32(args.installNonce),
|
|
235
150
|
predicate: () => stellar_sdk_1.xdr.ScVal.scvBytes(predicate),
|
|
236
151
|
predicate_hash: () => stellar_sdk_1.xdr.ScVal.scvBytes(Buffer.from(args.predicateHash, 'hex')),
|
|
237
|
-
oracle_max_staleness_seconds: () => encodeOptionU32(oracleParams?.maxStalenessSeconds),
|
|
238
|
-
oracle_max_deviation_bps: () => encodeOptionU32(oracleParams?.maxDeviationBps),
|
|
239
|
-
oracle_max_xfeed_dev_bps: () => encodeOptionU32(oracleParams?.maxCrossFeedDeviationBps),
|
|
240
152
|
};
|
|
241
153
|
const entries = POLICY_INSTALL_PARAM_FIELDS.map((k) => new stellar_sdk_1.xdr.ScMapEntry({ key: stellar_sdk_1.xdr.ScVal.scvSymbol(k), val: valueFor[k]() }));
|
|
242
154
|
// The host orders map entries by the SYMBOL STRING, not by XDR bytes. A
|
|
@@ -246,14 +158,6 @@ function encodePolicyInstallParams(args) {
|
|
|
246
158
|
entries.sort((a, b) => sortBySymbolString(a.key(), b.key()));
|
|
247
159
|
return stellar_sdk_1.xdr.ScVal.scvMap(entries);
|
|
248
160
|
}
|
|
249
|
-
function encodeOptionU32(v) {
|
|
250
|
-
if (v === undefined)
|
|
251
|
-
return stellar_sdk_1.xdr.ScVal.scvVoid();
|
|
252
|
-
if (!Number.isFinite(v) || v < 0 || v > 0xffffffff) {
|
|
253
|
-
throw limitError('INSTALL_BUILD_FAILED', `oracle params value ${v} is outside u32 range`);
|
|
254
|
-
}
|
|
255
|
-
return stellar_sdk_1.xdr.ScVal.scvU32(v);
|
|
256
|
-
}
|
|
257
161
|
/** Sort host-map keys by their symbol-string form. The host orders map
|
|
258
162
|
* entries by the SYMBOL STRING, not by the key's XDR bytes (a length
|
|
259
163
|
* prefix in the encoding would otherwise put `amount` before `address`).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { rpc, type Transaction } from '@stellar/stellar-sdk';
|
|
2
|
+
import type { ContextRuleDraft } from '../types.ts';
|
|
3
3
|
/** Minimal Soroban RPC surface the install pipeline needs. Test seams
|
|
4
4
|
* inject a stub so the builder stays deterministic; production builds
|
|
5
5
|
* the real one via `createRpcServer` from `../record/rpc.ts`. */
|
|
@@ -31,20 +31,14 @@ export interface BuildInstallPolicyArgs {
|
|
|
31
31
|
sourceAccount: string;
|
|
32
32
|
/** Network passphrase - pins the hash the auth digest binds. */
|
|
33
33
|
networkPassphrase: string;
|
|
34
|
-
/** The new rule to install.
|
|
35
|
-
rule:
|
|
34
|
+
/** The new rule to install. */
|
|
35
|
+
rule: ContextRuleDraft;
|
|
36
36
|
/** Per-rule install nonce; 1 for a fresh install. */
|
|
37
37
|
installNonce: number;
|
|
38
38
|
/** Already-encoded (base64) canonical ScVal of the predicate. */
|
|
39
39
|
encodedPredicate: string;
|
|
40
40
|
/** Hex sha256 of the canonical predicate XDR bytes. */
|
|
41
41
|
predicateHash: string;
|
|
42
|
-
/** Per-policy oracle overrides. */
|
|
43
|
-
oracleParams?: {
|
|
44
|
-
maxStalenessSeconds?: number;
|
|
45
|
-
maxDeviationBps?: number;
|
|
46
|
-
maxCrossFeedDeviationBps?: number;
|
|
47
|
-
};
|
|
48
42
|
/** Grammar version override; defaults to 1. */
|
|
49
43
|
grammarVersion?: number;
|
|
50
44
|
/** Base fee in stroops; defaults to BASE_FEE (100). */
|
|
@@ -54,41 +48,6 @@ export interface BuildInstallPolicyArgs {
|
|
|
54
48
|
/** Ledger window (in ledgers) for the auth entry's `validUntil`. */
|
|
55
49
|
authValidUntilLedgers?: number;
|
|
56
50
|
}
|
|
57
|
-
export type BuildInstallPolicyRuleDraft = {
|
|
58
|
-
contextRuleType: {
|
|
59
|
-
kind: 'default';
|
|
60
|
-
} | {
|
|
61
|
-
kind: 'call_contract';
|
|
62
|
-
contract: string;
|
|
63
|
-
} | {
|
|
64
|
-
kind: 'create_contract';
|
|
65
|
-
wasmHash: string;
|
|
66
|
-
};
|
|
67
|
-
name: string;
|
|
68
|
-
validUntilLedger: number | null;
|
|
69
|
-
signers: BuildInstallPolicySignerDraft[];
|
|
70
|
-
policies: BuildInstallPolicyPolicyRef[];
|
|
71
|
-
};
|
|
72
|
-
export type BuildInstallPolicySignerDraft = {
|
|
73
|
-
kind: 'delegated';
|
|
74
|
-
address: string;
|
|
75
|
-
} | {
|
|
76
|
-
kind: 'external';
|
|
77
|
-
verifier: string;
|
|
78
|
-
keyBytes: string;
|
|
79
|
-
};
|
|
80
|
-
export type BuildInstallPolicyPolicyRef = {
|
|
81
|
-
kind: 'interpreter';
|
|
82
|
-
interpreterAddress: string;
|
|
83
|
-
predicateBlobBase64: string;
|
|
84
|
-
} | {
|
|
85
|
-
kind: 'oz_builtin';
|
|
86
|
-
instanceAddress: string;
|
|
87
|
-
primitive: {
|
|
88
|
-
primitive: 'spending_limit' | 'simple_threshold' | 'weighted_threshold';
|
|
89
|
-
params: Record<string, unknown>;
|
|
90
|
-
};
|
|
91
|
-
};
|
|
92
51
|
/** Human-readable description of the install call, decoded FROM the built
|
|
93
52
|
* XDR (not from the input args). The XDR is the source of truth: a
|
|
94
53
|
* human approving a review card needs the description to mirror the bytes
|
|
@@ -134,10 +93,6 @@ export interface InstallCallDescribes {
|
|
|
134
93
|
installNonce: number;
|
|
135
94
|
predicateHash: string;
|
|
136
95
|
predicateSha256OfEmbeddedBytes: string;
|
|
137
|
-
} | {
|
|
138
|
-
kind: 'oz_builtin';
|
|
139
|
-
address: string;
|
|
140
|
-
primitive: 'spending_limit' | 'simple_threshold' | 'weighted_threshold';
|
|
141
96
|
}>;
|
|
142
97
|
/** The install nonce, decoded from the interpreter policy's
|
|
143
98
|
* `install_nonce` field. Echoed at the top level for reviewer convenience;
|
|
@@ -217,7 +172,3 @@ export interface BuildRevokePolicyResult {
|
|
|
217
172
|
authValidUntilLedger: number;
|
|
218
173
|
rootInvocationXdr: string;
|
|
219
174
|
}
|
|
220
|
-
/** Re-export so the run-layer does not need to import from
|
|
221
|
-
* build-add-context-rule.ts (keeps the install/ -> install/ dependency
|
|
222
|
-
* direction intact). */
|
|
223
|
-
export { Contract, DEFAULT_GRAMMAR_VERSION };
|
|
@@ -18,15 +18,12 @@
|
|
|
18
18
|
// forwards each install_param to that policy, so the interpreter stores the
|
|
19
19
|
// predicate document as part of this same transaction.
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.DEFAULT_GRAMMAR_VERSION = exports.Contract = void 0;
|
|
22
21
|
exports.rpcClientFromServer = rpcClientFromServer;
|
|
23
22
|
exports.buildInstallPolicyXdr = buildInstallPolicyXdr;
|
|
24
23
|
exports.buildRevokePolicyXdr = buildRevokePolicyXdr;
|
|
25
24
|
const node_crypto_1 = require("node:crypto");
|
|
26
25
|
const stellar_sdk_1 = require("@stellar/stellar-sdk");
|
|
27
|
-
Object.defineProperty(exports, "Contract", { enumerable: true, get: function () { return stellar_sdk_1.Contract; } });
|
|
28
26
|
const build_add_context_rule_ts_1 = require("./build-add-context-rule.js");
|
|
29
|
-
Object.defineProperty(exports, "DEFAULT_GRAMMAR_VERSION", { enumerable: true, get: function () { return build_add_context_rule_ts_1.DEFAULT_GRAMMAR_VERSION; } });
|
|
30
27
|
const oz_auth_ts_1 = require("./oz-auth.js");
|
|
31
28
|
/** Convert a real rpc.Server into the InstallRpcClient surface. The
|
|
32
29
|
* `getContractVersion` lookup uses `simulateTransaction` against
|
|
@@ -47,7 +44,7 @@ function rpcClientFromServer(server, networkPassphrase) {
|
|
|
47
44
|
// random key does NOT exist on chain - asking the network for it returns
|
|
48
45
|
// 404 and the version probe fails for a reason that has nothing to do
|
|
49
46
|
// with the contract being probed.
|
|
50
|
-
const account = new
|
|
47
|
+
const account = new stellar_sdk_1.Account(stellar_sdk_1.Keypair.random().publicKey(), '0');
|
|
51
48
|
const tx = new stellar_sdk_1.TransactionBuilder(account, {
|
|
52
49
|
fee: stellar_sdk_1.BASE_FEE,
|
|
53
50
|
networkPassphrase,
|
|
@@ -80,73 +77,21 @@ async function buildInstallPolicyXdr(args) {
|
|
|
80
77
|
name: args.rule.name,
|
|
81
78
|
validUntilLedger: args.rule.validUntilLedger,
|
|
82
79
|
signers: args.rule.signers,
|
|
83
|
-
policies: args.rule.policies
|
|
80
|
+
policies: args.rule.policies,
|
|
84
81
|
}, {
|
|
85
82
|
signers: args.rule.signers,
|
|
86
|
-
policies: args.rule.policies
|
|
83
|
+
policies: args.rule.policies,
|
|
87
84
|
installNonce: args.installNonce,
|
|
88
85
|
encodedPredicate: args.encodedPredicate,
|
|
89
86
|
predicateHash: args.predicateHash,
|
|
90
|
-
...(args.oracleParams ? { oracleParams: args.oracleParams } : {}),
|
|
91
87
|
...(args.grammarVersion !== undefined ? { grammarVersion: args.grammarVersion } : {}),
|
|
92
88
|
});
|
|
93
|
-
// 2.
|
|
94
|
-
// bare host call. The recording pass assigns the smart-account auth nonce
|
|
95
|
-
// and root invocation needed to construct OZ's AuthPayload.
|
|
96
|
-
const source = await args.rpc.getAccount(args.sourceAccount);
|
|
97
|
-
const hostFunction = stellar_sdk_1.xdr.HostFunction.hostFunctionTypeInvokeContract(new stellar_sdk_1.xdr.InvokeContractArgs({
|
|
98
|
-
contractAddress: new stellar_sdk_1.Address(args.smartAccount).toScAddress(),
|
|
99
|
-
functionName: 'add_context_rule',
|
|
100
|
-
args: [...callArgs],
|
|
101
|
-
}));
|
|
102
|
-
const makeOperation = (auth = []) => stellar_sdk_1.Operation.invokeHostFunction({ func: hostFunction, auth });
|
|
103
|
-
const baseFee = args.baseFee !== undefined ? String(args.baseFee) : stellar_sdk_1.BASE_FEE;
|
|
104
|
-
const buildTx = (op) => buildUnsignedTx({
|
|
105
|
-
sourceAccount: args.sourceAccount,
|
|
106
|
-
sequence: source.sequenceNumber(),
|
|
107
|
-
fee: baseFee,
|
|
108
|
-
networkPassphrase: args.networkPassphrase,
|
|
109
|
-
op,
|
|
110
|
-
});
|
|
111
|
-
const recordingTx = buildTx(makeOperation());
|
|
112
|
-
// 3. Recording simulation: capture the smart account's nonce and invocation
|
|
113
|
-
// tree. Nothing is signed here.
|
|
114
|
-
const recorded = await args.rpc.simulateTransaction(recordingTx);
|
|
115
|
-
if (stellar_sdk_1.rpc.Api.isSimulationError(recorded)) {
|
|
116
|
-
// Short, stable reason. The full `simulateTransaction` error (which
|
|
117
|
-
// carries host + URL detail) stays in the SDK's own logs - never
|
|
118
|
-
// reflected back into a user-facing message where it would
|
|
119
|
-
// reconnoitre the RPC.
|
|
120
|
-
throw new Error('install_policy: simulateTransaction failed');
|
|
121
|
-
}
|
|
122
|
-
const original = (recorded.result?.auth ?? []).find((entry) => entry.credentials().switch().name === 'sorobanCredentialsAddress' &&
|
|
123
|
-
stellar_sdk_1.Address.fromScAddress(entry.credentials().address().address()).toString() ===
|
|
124
|
-
args.smartAccount);
|
|
125
|
-
if (!original) {
|
|
126
|
-
throw new Error(`install_policy: no Soroban auth entry for smart account ${args.smartAccount}; this call does not route through the smart account`);
|
|
127
|
-
}
|
|
128
|
-
// 4. `add_context_rule` is authorised by the deploy-time admin rule (rule 0).
|
|
89
|
+
// 2. `add_context_rule` is authorised by the deploy-time admin rule (rule 0).
|
|
129
90
|
// The delegated signer uses source-account credentials, so its signature
|
|
130
91
|
// bytes stay empty and the ordinary transaction-envelope signature covers
|
|
131
92
|
// the call when the consumer signs it.
|
|
132
|
-
const validUntilLedger =
|
|
133
|
-
|
|
134
|
-
const contextRuleIds = [0];
|
|
135
|
-
const digest = (0, oz_auth_ts_1.authDigest)((0, oz_auth_ts_1.signaturePayload)(args.networkPassphrase, original.credentials().address().nonce(), validUntilLedger, original.rootInvocation()), contextRuleIds);
|
|
136
|
-
const authEntries = [
|
|
137
|
-
(0, oz_auth_ts_1.accountEntry)(original, validUntilLedger, (0, oz_auth_ts_1.authPayload)([args.sourceAccount], contextRuleIds, () => Buffer.alloc(0))),
|
|
138
|
-
...contextRuleIds.map(() => (0, oz_auth_ts_1.delegatedSignerEntry)(args.smartAccount, digest)),
|
|
139
|
-
];
|
|
140
|
-
// 5. Simulate again with the OZ entries already attached. The SDK preserves
|
|
141
|
-
// existing auth during assembly and adds the simulated Soroban footprint +
|
|
142
|
-
// resource fee, yielding a complete unsigned envelope.
|
|
143
|
-
const txWithAuth = buildTx(makeOperation(authEntries));
|
|
144
|
-
const enforcing = await args.rpc.simulateTransaction(txWithAuth);
|
|
145
|
-
if (stellar_sdk_1.rpc.Api.isSimulationError(enforcing)) {
|
|
146
|
-
throw new Error('install_policy: auth simulateTransaction failed');
|
|
147
|
-
}
|
|
148
|
-
const finalTx = stellar_sdk_1.rpc.assembleTransaction(txWithAuth, enforcing).build();
|
|
149
|
-
// 6. Decode the structured description FROM the final assembled transaction.
|
|
93
|
+
const { finalTx, original, validUntilLedger } = await buildAuthorisedSmartAccountTx(args, 'add_context_rule', [...callArgs], 'install_policy');
|
|
94
|
+
// 3. Decode the structured description FROM the final assembled transaction.
|
|
150
95
|
// The human approval binds to the exact bytes the wallet will sign.
|
|
151
96
|
const describes = decodeInstallCallDescribes(finalTx, args.installNonce);
|
|
152
97
|
return {
|
|
@@ -172,11 +117,39 @@ async function buildInstallPolicyXdr(args) {
|
|
|
172
117
|
* deployer can revoke. This builder does not pre-check the signer: it would
|
|
173
118
|
* be guessing at a contract it cannot read, and the chain is the authority. */
|
|
174
119
|
async function buildRevokePolicyXdr(args) {
|
|
120
|
+
// Removal is master-only, so the deploy-time admin rule authorises it. The
|
|
121
|
+
// recorded rootInvocation still includes remove_context_rule(ruleId), binding
|
|
122
|
+
// the auth payload to the exact rule being removed. As with install,
|
|
123
|
+
// source-account credentials carry the delegated signer entry and the
|
|
124
|
+
// consumer supplies only the ordinary envelope signature.
|
|
125
|
+
const { finalTx, original, validUntilLedger } = await buildAuthorisedSmartAccountTx(args, 'remove_context_rule', [stellar_sdk_1.xdr.ScVal.scvU32(args.ruleId)], 'revoke_policy');
|
|
126
|
+
return {
|
|
127
|
+
unsignedXdr: finalTx.toEnvelope().toXDR().toString('base64'),
|
|
128
|
+
smartAccount: args.smartAccount,
|
|
129
|
+
sourceAccount: args.sourceAccount,
|
|
130
|
+
call: { contract: args.smartAccount, fn: 'remove_context_rule', ruleId: args.ruleId },
|
|
131
|
+
authNonce: original.credentials().address().nonce().toString(),
|
|
132
|
+
authValidUntilLedger: validUntilLedger,
|
|
133
|
+
rootInvocationXdr: original.rootInvocation().toXDR().toString('base64'),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/** ~25 minutes at 5s/ledger. */
|
|
137
|
+
const DEFAULT_AUTH_VALID_UNTIL_LEDGERS = 300;
|
|
138
|
+
// ---- internals ----
|
|
139
|
+
/** Record a bare call to the smart account, attach the deploy-time admin rule's
|
|
140
|
+
* auth entries, and re-simulate to assemble the footprint.
|
|
141
|
+
*
|
|
142
|
+
* Install and revoke differ only in the call they wrap, so they share this
|
|
143
|
+
* spine: the recording pass supplies the auth nonce and root invocation OZ's
|
|
144
|
+
* AuthPayload binds to, and the second simulation adds the Soroban footprint +
|
|
145
|
+
* resource fee. Nothing here is signed. `errorPrefix` names the calling tool in
|
|
146
|
+
* the fail-closed messages so a caller can still tell which call failed. */
|
|
147
|
+
async function buildAuthorisedSmartAccountTx(args, functionName, callArgs, errorPrefix) {
|
|
175
148
|
const source = await args.rpc.getAccount(args.sourceAccount);
|
|
176
149
|
const hostFunction = stellar_sdk_1.xdr.HostFunction.hostFunctionTypeInvokeContract(new stellar_sdk_1.xdr.InvokeContractArgs({
|
|
177
150
|
contractAddress: new stellar_sdk_1.Address(args.smartAccount).toScAddress(),
|
|
178
|
-
functionName
|
|
179
|
-
args:
|
|
151
|
+
functionName,
|
|
152
|
+
args: callArgs,
|
|
180
153
|
}));
|
|
181
154
|
const makeOperation = (auth = []) => stellar_sdk_1.Operation.invokeHostFunction({ func: hostFunction, auth });
|
|
182
155
|
const baseFee = args.baseFee !== undefined ? String(args.baseFee) : stellar_sdk_1.BASE_FEE;
|
|
@@ -193,19 +166,14 @@ async function buildRevokePolicyXdr(args) {
|
|
|
193
166
|
// carries host + URL detail) stays in the SDK's own logs - never
|
|
194
167
|
// reflected back into a user-facing message where it would
|
|
195
168
|
// reconnoitre the RPC.
|
|
196
|
-
throw new Error(
|
|
169
|
+
throw new Error(`${errorPrefix}: simulateTransaction failed`);
|
|
197
170
|
}
|
|
198
171
|
const original = (recorded.result?.auth ?? []).find((entry) => entry.credentials().switch().name === 'sorobanCredentialsAddress' &&
|
|
199
172
|
stellar_sdk_1.Address.fromScAddress(entry.credentials().address().address()).toString() ===
|
|
200
173
|
args.smartAccount);
|
|
201
174
|
if (!original) {
|
|
202
|
-
throw new Error(
|
|
175
|
+
throw new Error(`${errorPrefix}: no Soroban auth entry for smart account ${args.smartAccount}; this call does not route through the smart account`);
|
|
203
176
|
}
|
|
204
|
-
// Removal is master-only, so the deploy-time admin rule authorises it. The
|
|
205
|
-
// recorded rootInvocation still includes remove_context_rule(ruleId), binding
|
|
206
|
-
// the auth payload to the exact rule being removed. As with install,
|
|
207
|
-
// source-account credentials carry the delegated signer entry and the
|
|
208
|
-
// consumer supplies only the ordinary envelope signature.
|
|
209
177
|
const validUntilLedger = (await args.rpc.getLatestLedger()).sequence +
|
|
210
178
|
(args.authValidUntilLedgers ?? DEFAULT_AUTH_VALID_UNTIL_LEDGERS);
|
|
211
179
|
const contextRuleIds = [0];
|
|
@@ -217,38 +185,12 @@ async function buildRevokePolicyXdr(args) {
|
|
|
217
185
|
const txWithAuth = buildTx(makeOperation(authEntries));
|
|
218
186
|
const enforcing = await args.rpc.simulateTransaction(txWithAuth);
|
|
219
187
|
if (stellar_sdk_1.rpc.Api.isSimulationError(enforcing)) {
|
|
220
|
-
throw new Error(
|
|
221
|
-
}
|
|
222
|
-
const finalTx = stellar_sdk_1.rpc.assembleTransaction(txWithAuth, enforcing).build();
|
|
223
|
-
return {
|
|
224
|
-
unsignedXdr: finalTx.toEnvelope().toXDR().toString('base64'),
|
|
225
|
-
smartAccount: args.smartAccount,
|
|
226
|
-
sourceAccount: args.sourceAccount,
|
|
227
|
-
call: { contract: args.smartAccount, fn: 'remove_context_rule', ruleId: args.ruleId },
|
|
228
|
-
authNonce: original.credentials().address().nonce().toString(),
|
|
229
|
-
authValidUntilLedger: validUntilLedger,
|
|
230
|
-
rootInvocationXdr: original.rootInvocation().toXDR().toString('base64'),
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
/** ~25 minutes at 5s/ledger. */
|
|
234
|
-
const DEFAULT_AUTH_VALID_UNTIL_LEDGERS = 300;
|
|
235
|
-
// ---- internals ----
|
|
236
|
-
/** Adapter: turn the install-policy wire `PolicyRef` shape into the core
|
|
237
|
-
* `PolicyRef` shape `buildAddContextRuleArgs` expects. Keeps the wire
|
|
238
|
-
* schema hand-rolled + flat (the strict union would need a recursive
|
|
239
|
-
* schema) while delegating to the proven encoder for the actual bytes. */
|
|
240
|
-
function adaptPolicyRef(p) {
|
|
241
|
-
if (p.kind === 'interpreter') {
|
|
242
|
-
return {
|
|
243
|
-
kind: 'interpreter',
|
|
244
|
-
interpreterAddress: p.interpreterAddress,
|
|
245
|
-
predicateBlobBase64: p.predicateBlobBase64,
|
|
246
|
-
};
|
|
188
|
+
throw new Error(`${errorPrefix}: auth simulateTransaction failed`);
|
|
247
189
|
}
|
|
248
190
|
return {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
191
|
+
finalTx: stellar_sdk_1.rpc.assembleTransaction(txWithAuth, enforcing).build(),
|
|
192
|
+
original,
|
|
193
|
+
validUntilLedger,
|
|
252
194
|
};
|
|
253
195
|
}
|
|
254
196
|
/** Build an unsigned Soroban transaction envelope. The `sequence` is
|
|
@@ -256,7 +198,7 @@ function adaptPolicyRef(p) {
|
|
|
256
198
|
* the SDK accepts). No signing. The returned Transaction is what
|
|
257
199
|
* `simulateTransaction` accepts. */
|
|
258
200
|
function buildUnsignedTx(args) {
|
|
259
|
-
const account = new
|
|
201
|
+
const account = new stellar_sdk_1.Account(args.sourceAccount, args.sequence);
|
|
260
202
|
const tx = new stellar_sdk_1.TransactionBuilder(account, {
|
|
261
203
|
fee: args.fee,
|
|
262
204
|
networkPassphrase: args.networkPassphrase,
|
|
@@ -302,14 +244,13 @@ function decodeInstallCallDescribes(tx, expectedInstallNonce) {
|
|
|
302
244
|
if (scvArgs.length !== 5) {
|
|
303
245
|
throw new Error(`install_policy: built op has ${scvArgs.length} args, expected 5 (context_type, name, valid_until, signers, policies)`);
|
|
304
246
|
}
|
|
305
|
-
const contextType = scvArgs[0];
|
|
306
247
|
const nameScv = scvArgs[1];
|
|
307
248
|
const validUntilScv = scvArgs[2];
|
|
308
249
|
const signersScv = scvArgs[3];
|
|
309
250
|
const policiesScv = scvArgs[4];
|
|
310
251
|
// The length check above pins these as defined; the local references
|
|
311
252
|
// satisfy noUncheckedIndexedAccess on the accessors below.
|
|
312
|
-
if (!nameScv || !validUntilScv || !signersScv || !policiesScv
|
|
253
|
+
if (!nameScv || !validUntilScv || !signersScv || !policiesScv) {
|
|
313
254
|
throw new Error('install_policy: built op args include an undefined slot despite length check');
|
|
314
255
|
}
|
|
315
256
|
// name
|
|
@@ -390,7 +331,6 @@ function decodeInstallCallDescribes(tx, expectedInstallNonce) {
|
|
|
390
331
|
fields.set(fk.sym().toString(), inner.val());
|
|
391
332
|
}
|
|
392
333
|
// Interpreter policy fields carry grammar_version/install_nonce/predicate.
|
|
393
|
-
// OZ primitives carry spending_limit/period_ledgers/threshold/signers.
|
|
394
334
|
if (fields.has('predicate') || fields.has('grammar_version') || fields.has('install_nonce')) {
|
|
395
335
|
const installNonceScv = fields.get('install_nonce');
|
|
396
336
|
if (installNonceScv?.switch().name !== 'scvU32') {
|
|
@@ -419,21 +359,6 @@ function decodeInstallCallDescribes(tx, expectedInstallNonce) {
|
|
|
419
359
|
observedInstallNonce = installNonce;
|
|
420
360
|
continue;
|
|
421
361
|
}
|
|
422
|
-
// OZ built-in primitive. Distinguish by the parameter shape we
|
|
423
|
-
// emitted; matching one of the three primitives exactly pins the
|
|
424
|
-
// kind we built.
|
|
425
|
-
if (fields.has('spending_limit') && fields.has('period_ledgers')) {
|
|
426
|
-
policies.push({ kind: 'oz_builtin', address, primitive: 'spending_limit' });
|
|
427
|
-
continue;
|
|
428
|
-
}
|
|
429
|
-
if (fields.has('threshold') && fields.has('signer_weights')) {
|
|
430
|
-
policies.push({ kind: 'oz_builtin', address, primitive: 'weighted_threshold' });
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
if (fields.has('threshold')) {
|
|
434
|
-
policies.push({ kind: 'oz_builtin', address, primitive: 'simple_threshold' });
|
|
435
|
-
continue;
|
|
436
|
-
}
|
|
437
362
|
throw new Error(`install_policy: policies[${address}] value has an unknown field set; the encoder may have drifted`);
|
|
438
363
|
}
|
|
439
364
|
// `observedInstallNonce` is the nonce baked into whichever interpreter
|
|
@@ -462,6 +387,3 @@ function decodeInstallCallDescribes(tx, expectedInstallNonce) {
|
|
|
462
387
|
installNonce,
|
|
463
388
|
};
|
|
464
389
|
}
|
|
465
|
-
// Local re-import to avoid pulling the class from the SDK module path
|
|
466
|
-
// at the top of the file (avoids the unused-import lint).
|
|
467
|
-
const stellar_sdk_2 = require("@stellar/stellar-sdk");
|
|
@@ -1,3 +1 @@
|
|
|
1
|
-
export { ANY, type AuthorityOverlap, type ContextType, effectiveSelectors, findAuthorityOverlaps, type IntendedInstall, intersectSelectors, type MergeResult, mergeIntoAnd, type ObservedRule, type OverlapSeverity, permittedSelectors, type RuleClass, type Selector, signerKey, } from './authority-overlap.ts';
|
|
2
1
|
export { ADD_CONTEXT_RULE_SYMBOL, type AddContextRuleArgs, type BuildAddContextRuleArgs, buildAddContextRuleArgs, DEFAULT_GRAMMAR_VERSION, } from './build-add-context-rule.ts';
|
|
3
|
-
export { type AccountRuleReader, accountRuleReaderFromServer, type CollectedRules, collectObservedRules, decodeContextRule, decodeContextType, decodeSigner, docLedgerKey, MAX_RULE_ID_SCAN, } from './read-account-rules.ts';
|