@pushchain/core 6.0.27-alpha.3 → 6.0.27-alpha.4
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/AGW.md +27 -6
- package/CHANGELOG.md +7 -0
- package/MARKETPLACE.md +206 -0
- package/package.json +2 -1
- package/src/lib/agentic/codec/rules.js +74 -69
- package/src/lib/agentic/codec/universal.js +15 -3
- package/src/lib/agentic/constants.d.ts +36 -1
- package/src/lib/agentic/constants.js +14 -1
- package/src/lib/agentic/contracts/reader.d.ts +21 -0
- package/src/lib/agentic/contracts/reader.js +3 -0
- package/src/lib/agentic/error-messages.d.ts +3 -0
- package/src/lib/agentic/error-messages.js +125 -0
- package/src/lib/agentic/errors.d.ts +12 -3
- package/src/lib/agentic/errors.js +76 -0
- package/src/lib/agentic/management/common.js +6 -0
- package/src/lib/agentic/management/destination.d.ts +5 -0
- package/src/lib/agentic/management/destination.js +32 -0
- package/src/lib/agentic/management/prepare.js +80 -55
- package/src/lib/agentic/management/svm-public.js +13 -4
- package/src/lib/agentic/presets/index.js +7 -8
- package/src/lib/agentic/presets/routes.d.ts +1 -1
- package/src/lib/agentic/presets/routes.js +6 -7
- package/src/lib/agentic/presets/types.d.ts +1 -6
- package/src/lib/agentic/public-rule.js +13 -2
- package/src/lib/constants/index.d.ts +35 -0
- package/src/lib/index.d.ts +4 -0
- package/src/lib/index.js +6 -2
- package/src/lib/job/job.d.ts +35 -0
- package/src/lib/job/job.js +382 -0
- package/src/lib/job/types.d.ts +94 -0
- package/src/lib/job/types.js +3 -0
- package/src/lib/job/watch.d.ts +3 -0
- package/src/lib/job/watch.js +169 -0
- package/src/lib/job-spec/codec.d.ts +18 -0
- package/src/lib/job-spec/codec.js +348 -0
- package/src/lib/job-spec/constants.d.ts +32 -0
- package/src/lib/job-spec/constants.js +36 -0
- package/src/lib/job-spec/helpers.d.ts +19 -0
- package/src/lib/job-spec/helpers.js +138 -0
- package/src/lib/job-spec/index.d.ts +14 -0
- package/src/lib/job-spec/index.js +17 -0
- package/src/lib/job-spec/preview.d.ts +8 -0
- package/src/lib/job-spec/preview.js +298 -0
- package/src/lib/job-spec/read.d.ts +20 -0
- package/src/lib/job-spec/read.js +224 -0
- package/src/lib/job-spec/schema.d.ts +290 -0
- package/src/lib/job-spec/schema.js +70 -0
- package/src/lib/job-spec/types.d.ts +139 -0
- package/src/lib/job-spec/types.js +3 -0
- package/src/lib/market/contracts/AgenticCommerce.d.ts +1026 -0
- package/src/lib/market/contracts/AgenticCommerce.js +1317 -0
- package/src/lib/market/contracts/RulesBindingHook.d.ts +235 -0
- package/src/lib/market/contracts/RulesBindingHook.js +314 -0
- package/src/lib/market/contracts/UniversalMarketplace.d.ts +1770 -0
- package/src/lib/market/contracts/UniversalMarketplace.js +2295 -0
- package/src/lib/market/contracts/UniversalMarketplaceErrors.d.ts +309 -0
- package/src/lib/market/contracts/UniversalMarketplaceErrors.js +416 -0
- package/src/lib/market/contracts/UniversalMarketplaceRules.d.ts +241 -0
- package/src/lib/market/contracts/UniversalMarketplaceRules.js +323 -0
- package/src/lib/market/data.d.ts +8 -0
- package/src/lib/market/data.js +60 -0
- package/src/lib/market/deployment.d.ts +14 -0
- package/src/lib/market/deployment.js +23 -0
- package/src/lib/market/deposit.d.ts +24 -0
- package/src/lib/market/deposit.js +271 -0
- package/src/lib/market/errors.d.ts +13 -0
- package/src/lib/market/errors.js +81 -0
- package/src/lib/market/history.d.ts +6 -0
- package/src/lib/market/history.js +83 -0
- package/src/lib/market/index.d.ts +4 -0
- package/src/lib/market/index.js +12 -0
- package/src/lib/market/kernel-wire.d.ts +15 -0
- package/src/lib/market/kernel-wire.js +41 -0
- package/src/lib/market/market.d.ts +55 -0
- package/src/lib/market/market.js +635 -0
- package/src/lib/market/rules.d.ts +106 -0
- package/src/lib/market/rules.js +293 -0
- package/src/lib/market/runtime.d.ts +35 -0
- package/src/lib/market/runtime.js +130 -0
- package/src/lib/market/types.d.ts +180 -0
- package/src/lib/market/types.js +3 -0
- package/src/lib/orchestrator/internals/errors.d.ts +9 -2
- package/src/lib/orchestrator/internals/errors.js +9 -2
- package/src/lib/orchestrator/internals/push-chain-tx.js +17 -3
- package/src/lib/push-chain/push-chain.d.ts +44 -1
- package/src/lib/push-chain/push-chain.js +23 -3
- package/src/lib/read-state/read-params.d.ts +9 -0
- package/src/lib/read-state/read-params.js +5 -1
- package/src/lib/utils.d.ts +15 -0
- package/src/lib/utils.js +4 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MUTABILITIES = exports.LOGIC_KINDS = exports.OPERATIONS = exports.EVALUATIONS = exports.PRINCIPAL = exports.JOB_VOCABULARY = void 0;
|
|
4
|
+
exports.JOB_VOCABULARY = Object.freeze({
|
|
5
|
+
EVALUATION: Object.freeze({
|
|
6
|
+
BOOLEAN: 'BOOLEAN',
|
|
7
|
+
COMPARE: 'COMPARE',
|
|
8
|
+
NUMBER: 'NUMBER',
|
|
9
|
+
PERCENTAGE: 'PERCENTAGE',
|
|
10
|
+
}),
|
|
11
|
+
OPERATION: Object.freeze({
|
|
12
|
+
EQUAL: 'EQUAL',
|
|
13
|
+
NOT_EQUAL: 'NOT_EQUAL',
|
|
14
|
+
GREATER_THAN: 'GREATER_THAN',
|
|
15
|
+
GREATER_OR_EQUAL: 'GREATER_OR_EQUAL',
|
|
16
|
+
LESS_THAN: 'LESS_THAN',
|
|
17
|
+
LESS_OR_EQUAL: 'LESS_OR_EQUAL',
|
|
18
|
+
}),
|
|
19
|
+
LOGIC: Object.freeze({
|
|
20
|
+
ALL: 'ALL',
|
|
21
|
+
ANY: 'ANY',
|
|
22
|
+
AT_LEAST: 'AT_LEAST',
|
|
23
|
+
}),
|
|
24
|
+
ALLOWED_MUTABILITY: Object.freeze({
|
|
25
|
+
NONE: 'NONE',
|
|
26
|
+
CLIENT: 'CLIENT',
|
|
27
|
+
PROVIDER: 'PROVIDER',
|
|
28
|
+
BOTH: 'BOTH',
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
31
|
+
exports.PRINCIPAL = 'agentic:principal';
|
|
32
|
+
exports.EVALUATIONS = Object.values(exports.JOB_VOCABULARY.EVALUATION);
|
|
33
|
+
exports.OPERATIONS = Object.values(exports.JOB_VOCABULARY.OPERATION);
|
|
34
|
+
exports.LOGIC_KINDS = Object.values(exports.JOB_VOCABULARY.LOGIC);
|
|
35
|
+
exports.MUTABILITIES = Object.values(exports.JOB_VOCABULARY.ALLOWED_MUTABILITY);
|
|
36
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Address, type Hex } from 'viem';
|
|
2
|
+
import type { JobSpec, JobSpecTemplate } from './types';
|
|
3
|
+
export declare function resolveJobSpec(template: JobSpecTemplate, ctx: {
|
|
4
|
+
principal: bigint;
|
|
5
|
+
params: bigint[];
|
|
6
|
+
accounts: Record<string, Address>;
|
|
7
|
+
origin: Hex;
|
|
8
|
+
executeBy: number;
|
|
9
|
+
settleWindow: number;
|
|
10
|
+
}): JobSpec;
|
|
11
|
+
export declare function describeJobSpec(spec: JobSpec | JobSpecTemplate, options?: {
|
|
12
|
+
principal?: bigint;
|
|
13
|
+
decimals?: Record<Address, number>;
|
|
14
|
+
}): {
|
|
15
|
+
checks: string[];
|
|
16
|
+
logic: string;
|
|
17
|
+
timing: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function specOrigin(marketplace: Address, cardId: bigint, version: bigint, principal: bigint): Hex;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveJobSpec = resolveJobSpec;
|
|
4
|
+
exports.describeJobSpec = describeJobSpec;
|
|
5
|
+
exports.specOrigin = specOrigin;
|
|
6
|
+
const viem_1 = require("viem");
|
|
7
|
+
const codec_1 = require("./codec");
|
|
8
|
+
const constants_1 = require("./constants");
|
|
9
|
+
const errors_1 = require("../market/errors");
|
|
10
|
+
const read_1 = require("./read");
|
|
11
|
+
function resolveJobSpec(template, ctx) {
|
|
12
|
+
(0, codec_1.encodeJobSpec)(template);
|
|
13
|
+
if (ctx.params.length !== template.params.length ||
|
|
14
|
+
ctx.params.some((n, i) => (0, codec_1.int256)(n) < template.params[i].min || n > template.params[i].max))
|
|
15
|
+
throw new errors_1.JobSpecError('PARAM_OUT_OF_RANGE', 'The job parameters do not match the template bounds.');
|
|
16
|
+
const account = (read) => {
|
|
17
|
+
const r = template.reads[read];
|
|
18
|
+
const a = ctx.accounts[`${r.chainNamespace}:${r.chainId}`];
|
|
19
|
+
if (!a)
|
|
20
|
+
throw new errors_1.JobSpecError('UNFILLED_PLACEHOLDER', 'Resolve the wallet account on every read chain.');
|
|
21
|
+
return (0, viem_1.getAddress)(a);
|
|
22
|
+
};
|
|
23
|
+
const reads = template.reads.map((r, i) => {
|
|
24
|
+
let args = r.args;
|
|
25
|
+
for (const f of r.fills ?? []) {
|
|
26
|
+
const value = f.source === 'WALLET'
|
|
27
|
+
? BigInt(account(i))
|
|
28
|
+
: BigInt.asUintN(256, ctx.params[f.param ?? 0]);
|
|
29
|
+
const at = 2 + f.word * 64;
|
|
30
|
+
args = (args.slice(0, at) +
|
|
31
|
+
(0, viem_1.toHex)(value, { size: 32 }).slice(2) +
|
|
32
|
+
args.slice(at + 64));
|
|
33
|
+
}
|
|
34
|
+
const read = { ...r };
|
|
35
|
+
delete read.fills;
|
|
36
|
+
const resolved = { ...read, args };
|
|
37
|
+
(0, read_1.inheritReadLabel)(r, resolved);
|
|
38
|
+
return resolved;
|
|
39
|
+
});
|
|
40
|
+
const checks = template.checks.map((c) => {
|
|
41
|
+
const t = c.target;
|
|
42
|
+
const target = typeof t === 'bigint'
|
|
43
|
+
? t
|
|
44
|
+
: t === constants_1.PRINCIPAL
|
|
45
|
+
? ctx.principal
|
|
46
|
+
: t === 'agentic:wallet'
|
|
47
|
+
? BigInt(account(c.read))
|
|
48
|
+
: 'param' in t
|
|
49
|
+
? ctx.params[t.param]
|
|
50
|
+
: (() => {
|
|
51
|
+
const product = ctx.principal * t.principalBps;
|
|
52
|
+
if (product >= BigInt(1) << BigInt(256))
|
|
53
|
+
throw new errors_1.JobSpecError('TARGET_OVERFLOW', 'The principal-based target overflows uint256.');
|
|
54
|
+
return product / BigInt(10000);
|
|
55
|
+
})();
|
|
56
|
+
return { ...c, target: (0, codec_1.int256)(target) };
|
|
57
|
+
});
|
|
58
|
+
const spec = {
|
|
59
|
+
reads,
|
|
60
|
+
checks,
|
|
61
|
+
logic: template.logic,
|
|
62
|
+
allowedMutability: template.allowedMutability,
|
|
63
|
+
origin: ctx.origin,
|
|
64
|
+
executeBy: ctx.executeBy,
|
|
65
|
+
failFinalAt: ctx.executeBy + ctx.settleWindow,
|
|
66
|
+
};
|
|
67
|
+
(0, codec_1.encodeJobSpec)(spec);
|
|
68
|
+
return spec;
|
|
69
|
+
}
|
|
70
|
+
const words = {
|
|
71
|
+
EQUAL: 'equals',
|
|
72
|
+
NOT_EQUAL: 'does not equal',
|
|
73
|
+
GREATER_THAN: 'is greater than',
|
|
74
|
+
GREATER_OR_EQUAL: 'is at least',
|
|
75
|
+
LESS_THAN: 'is less than',
|
|
76
|
+
LESS_OR_EQUAL: 'is at most',
|
|
77
|
+
};
|
|
78
|
+
function describeJobSpec(spec, options) {
|
|
79
|
+
(0, codec_1.encodeJobSpec)(spec);
|
|
80
|
+
if (options?.principal !== undefined && (0, codec_1.int256)(options.principal) < BigInt(0))
|
|
81
|
+
throw new errors_1.JobSpecError('INVALID_DESCRIPTION_OPTIONS', 'Principal must be nonnegative.');
|
|
82
|
+
const decimals = new Map();
|
|
83
|
+
for (const [address, value] of Object.entries(options?.decimals ?? {})) {
|
|
84
|
+
if (!Number.isInteger(value) || value < 0 || value > 255)
|
|
85
|
+
throw new errors_1.JobSpecError('INVALID_DESCRIPTION_OPTIONS', 'Token decimals must be integers from 0 to 255.');
|
|
86
|
+
try {
|
|
87
|
+
decimals.set((0, viem_1.getAddress)(address).toLowerCase(), value);
|
|
88
|
+
}
|
|
89
|
+
catch (cause) {
|
|
90
|
+
throw new errors_1.JobSpecError('INVALID_DESCRIPTION_OPTIONS', 'Decimals must be keyed by token address.', undefined, cause);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function logic(t) {
|
|
94
|
+
return `${t.kind === 'ALL' ? 'All' : t.kind === 'ANY' ? 'Any' : `At least ${t.k}`} of (${t.of
|
|
95
|
+
.map((x) => (typeof x === 'number' ? `check ${x + 1}` : logic(x)))
|
|
96
|
+
.join(', ')})`;
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
checks: spec.checks.map((c) => {
|
|
100
|
+
const r = spec.reads[c.read];
|
|
101
|
+
const t = c.target;
|
|
102
|
+
const resolvedTarget = typeof t === 'bigint'
|
|
103
|
+
? t
|
|
104
|
+
: t === constants_1.PRINCIPAL
|
|
105
|
+
? options?.principal ?? 'principal'
|
|
106
|
+
: t === 'agentic:wallet'
|
|
107
|
+
? 'the wallet on this chain'
|
|
108
|
+
: 'param' in t
|
|
109
|
+
? `parameter ${t.param + 1}`
|
|
110
|
+
: options?.principal !== undefined
|
|
111
|
+
? (options.principal * t.principalBps) / BigInt(10000)
|
|
112
|
+
: `${t.principalBps} basis points of principal`;
|
|
113
|
+
const precision = decimals.get(r.target.toLowerCase());
|
|
114
|
+
const target = typeof resolvedTarget === 'bigint'
|
|
115
|
+
? c.evaluation === 'PERCENTAGE'
|
|
116
|
+
? `${(0, viem_1.formatUnits)(resolvedTarget, 2)}% (${resolvedTarget} bps)`
|
|
117
|
+
: precision !== undefined && c.evaluation !== 'BOOLEAN'
|
|
118
|
+
? `${(0, viem_1.formatUnits)(resolvedTarget, precision)} (${resolvedTarget} raw units; ${precision} decimals)`
|
|
119
|
+
: `${resolvedTarget} (raw units)`
|
|
120
|
+
: `${resolvedTarget}${c.evaluation === 'PERCENTAGE' ? ' bps' : ' (raw units)'}`;
|
|
121
|
+
const name = (0, read_1.jobReadLabel)(r);
|
|
122
|
+
return `Read ${c.read + 1}: ${r.target} ${name ? name + ' ' : ''}${r.selector} on ${r.chainNamespace}:${r.chainId}, field [${r.field.join(',')}], ${c.evaluation} ${words[c.operation]} ${target}.`;
|
|
123
|
+
}),
|
|
124
|
+
logic: logic(spec.logic),
|
|
125
|
+
timing: 'executeBy' in spec
|
|
126
|
+
? `Execute by ${new Date(spec.executeBy * 1000).toISOString()}; failure is final after ${new Date(spec.failFinalAt * 1000).toISOString()}.`
|
|
127
|
+
: '',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function specOrigin(marketplace, cardId, version, principal) {
|
|
131
|
+
// Imported here to keep the public helpers independent of client state.
|
|
132
|
+
return (0, viem_1.keccak256)(('0x' +
|
|
133
|
+
[
|
|
134
|
+
marketplace.slice(2).padStart(64, '0'),
|
|
135
|
+
...[cardId, version, principal].map((n) => (0, viem_1.toHex)(n, { size: 32 }).slice(2)),
|
|
136
|
+
].join('')));
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { buildJobSpec, buildJobSpecTemplate, encodeJobSpec, decodeJobSpec } from './codec';
|
|
2
|
+
import { describeJobSpec } from './helpers';
|
|
3
|
+
import { prepareJobRead } from './read';
|
|
4
|
+
import { previewJobSpec } from './preview';
|
|
5
|
+
export declare const jobSpecUtils: Readonly<{
|
|
6
|
+
buildJobSpec: typeof buildJobSpec;
|
|
7
|
+
buildJobSpecTemplate: typeof buildJobSpecTemplate;
|
|
8
|
+
encodeJobSpec: typeof encodeJobSpec;
|
|
9
|
+
decodeJobSpec: typeof decodeJobSpec;
|
|
10
|
+
describeJobSpec: typeof describeJobSpec;
|
|
11
|
+
prepareRead: typeof prepareJobRead;
|
|
12
|
+
previewJobSpec: typeof previewJobSpec;
|
|
13
|
+
}>;
|
|
14
|
+
export type * from './types';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jobSpecUtils = void 0;
|
|
4
|
+
const codec_1 = require("./codec");
|
|
5
|
+
const helpers_1 = require("./helpers");
|
|
6
|
+
const read_1 = require("./read");
|
|
7
|
+
const preview_1 = require("./preview");
|
|
8
|
+
exports.jobSpecUtils = Object.freeze({
|
|
9
|
+
buildJobSpec: codec_1.buildJobSpec,
|
|
10
|
+
buildJobSpecTemplate: codec_1.buildJobSpecTemplate,
|
|
11
|
+
encodeJobSpec: codec_1.encodeJobSpec,
|
|
12
|
+
decodeJobSpec: codec_1.decodeJobSpec,
|
|
13
|
+
describeJobSpec: helpers_1.describeJobSpec,
|
|
14
|
+
prepareRead: read_1.prepareJobRead,
|
|
15
|
+
previewJobSpec: preview_1.previewJobSpec,
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Address, type Hex } from 'viem';
|
|
2
|
+
import type { CHAIN } from '../constants/enums';
|
|
3
|
+
import type { JobRead, JobSpec, JobSpecTemplate, JobSpecPreview, PreviewJobSpecOptions } from './types';
|
|
4
|
+
/** @internal Bind the real client's configured CEA resolver without exposing its runtime. */
|
|
5
|
+
export declare function bindJobPreviewAccounts(universal: object, resolve: (wallet: Address, chain: CHAIN) => Promise<Address>): void;
|
|
6
|
+
/** Follow the deployed V2 type codes; uint values above int256.max cannot be compared. */
|
|
7
|
+
export declare function decodeJobAnswer(read: JobRead, data: Hex): bigint;
|
|
8
|
+
export declare function previewJobSpec(subject: JobSpec | JobSpecTemplate, options: PreviewJobSpecOptions): Promise<JobSpecPreview>;
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bindJobPreviewAccounts = bindJobPreviewAccounts;
|
|
4
|
+
exports.decodeJobAnswer = decodeJobAnswer;
|
|
5
|
+
exports.previewJobSpec = previewJobSpec;
|
|
6
|
+
const viem_1 = require("viem");
|
|
7
|
+
const read_state_types_1 = require("../read-state/read-state.types");
|
|
8
|
+
const envelopes_1 = require("../read-state/envelopes");
|
|
9
|
+
const errors_1 = require("../market/errors");
|
|
10
|
+
const codec_1 = require("./codec");
|
|
11
|
+
const helpers_1 = require("./helpers");
|
|
12
|
+
const read_1 = require("./read");
|
|
13
|
+
const constants_1 = require("./constants");
|
|
14
|
+
const resolvers = new WeakMap();
|
|
15
|
+
/** @internal Bind the real client's configured CEA resolver without exposing its runtime. */
|
|
16
|
+
function bindJobPreviewAccounts(universal, resolve) {
|
|
17
|
+
resolvers.set(universal, resolve);
|
|
18
|
+
}
|
|
19
|
+
/** Follow the deployed V2 type codes; uint values above int256.max cannot be compared. */
|
|
20
|
+
function decodeJobAnswer(read, data) {
|
|
21
|
+
const bytes = BigInt((data.length - 2) / 2);
|
|
22
|
+
const invalid = () => {
|
|
23
|
+
throw new errors_1.JobSpecError('INVALID_READ_RESULT', 'The consensus bytes do not contain the selected JobSpec field.');
|
|
24
|
+
};
|
|
25
|
+
const word = (at) => {
|
|
26
|
+
if (at < BigInt(0) || at + BigInt(32) > bytes)
|
|
27
|
+
return invalid();
|
|
28
|
+
return BigInt('0x' + data.slice(2 + Number(at) * 2, 66 + Number(at) * 2));
|
|
29
|
+
};
|
|
30
|
+
const dynamic = (n) => [6, 7, 9].includes(n.code) || n.children.some(dynamic);
|
|
31
|
+
const head = (n) => dynamic(n)
|
|
32
|
+
? BigInt(1)
|
|
33
|
+
: n.code === 8
|
|
34
|
+
? n.children.reduce((sum, c) => sum + head(c), BigInt(0))
|
|
35
|
+
: n.code === 10
|
|
36
|
+
? BigInt(n.size ?? 0) * head(n.children[0])
|
|
37
|
+
: BigInt(1);
|
|
38
|
+
let node = (0, read_1.parseOutputs)(read.outputs);
|
|
39
|
+
let base = BigInt(0);
|
|
40
|
+
for (const index of read.field) {
|
|
41
|
+
let child;
|
|
42
|
+
let start = base;
|
|
43
|
+
let slot;
|
|
44
|
+
if (node.code === 8) {
|
|
45
|
+
child = node.children[index];
|
|
46
|
+
if (!child)
|
|
47
|
+
return invalid();
|
|
48
|
+
slot =
|
|
49
|
+
base +
|
|
50
|
+
BigInt(32) *
|
|
51
|
+
node.children
|
|
52
|
+
.slice(0, index)
|
|
53
|
+
.reduce((sum, c) => sum + head(c), BigInt(0));
|
|
54
|
+
}
|
|
55
|
+
else if (node.code === 9 || node.code === 10) {
|
|
56
|
+
if (BigInt(index) >= (node.code === 9 ? word(base) : BigInt(node.size ?? 0)))
|
|
57
|
+
return invalid();
|
|
58
|
+
child = node.children[0];
|
|
59
|
+
start += node.code === 9 ? BigInt(32) : BigInt(0);
|
|
60
|
+
slot = start + BigInt(32) * BigInt(index) * head(child);
|
|
61
|
+
}
|
|
62
|
+
else
|
|
63
|
+
return invalid();
|
|
64
|
+
base = dynamic(child) ? start + word(slot) : slot;
|
|
65
|
+
if (base > bytes)
|
|
66
|
+
return invalid();
|
|
67
|
+
node = child;
|
|
68
|
+
}
|
|
69
|
+
const value = word(base);
|
|
70
|
+
if (node.code === 6 || node.code === 7) {
|
|
71
|
+
if (base + BigInt(32) + value > bytes)
|
|
72
|
+
return invalid();
|
|
73
|
+
return BigInt.asIntN(256, BigInt((0, viem_1.keccak256)(('0x' +
|
|
74
|
+
data.slice(2 + Number(base + BigInt(32)) * 2, 2 + Number(base + BigInt(32) + value) * 2)))));
|
|
75
|
+
}
|
|
76
|
+
if (node.code === 9)
|
|
77
|
+
return BigInt.asIntN(256, value);
|
|
78
|
+
if ((node.code === 1 && value >= BigInt(1) << BigInt(255)) ||
|
|
79
|
+
(node.code === 3 && value > BigInt(1)) ||
|
|
80
|
+
(node.code === 4 && value >= BigInt(1) << BigInt(160)))
|
|
81
|
+
return invalid();
|
|
82
|
+
return BigInt.asIntN(256, value);
|
|
83
|
+
}
|
|
84
|
+
function compare(value, op, target) {
|
|
85
|
+
switch (op) {
|
|
86
|
+
case 'EQUAL':
|
|
87
|
+
return value === target;
|
|
88
|
+
case 'NOT_EQUAL':
|
|
89
|
+
return value !== target;
|
|
90
|
+
case 'GREATER_THAN':
|
|
91
|
+
return value > target;
|
|
92
|
+
case 'GREATER_OR_EQUAL':
|
|
93
|
+
return value >= target;
|
|
94
|
+
case 'LESS_THAN':
|
|
95
|
+
return value < target;
|
|
96
|
+
case 'LESS_OR_EQUAL':
|
|
97
|
+
return value <= target;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function combine(tree, checks) {
|
|
101
|
+
const results = tree.of.map((x) => typeof x === 'number' ? checks[x].pass : combine(x, checks));
|
|
102
|
+
const yes = results.filter((v) => v === true).length;
|
|
103
|
+
const unknown = results.filter((v) => v === null).length;
|
|
104
|
+
const required = tree.kind === 'ALL'
|
|
105
|
+
? results.length
|
|
106
|
+
: tree.kind === 'ANY'
|
|
107
|
+
? 1
|
|
108
|
+
: tree.k ?? 0;
|
|
109
|
+
return yes >= required ? true : yes + unknown < required ? false : null;
|
|
110
|
+
}
|
|
111
|
+
async function previewJobSpec(subject, options) {
|
|
112
|
+
// Validate and copy before asynchronous work; later caller mutations must not change paid criteria.
|
|
113
|
+
const input = (0, codec_1.decodeJobSpec)((0, codec_1.encodeJobSpec)(subject), {
|
|
114
|
+
format: 'params' in subject ? 'template' : 'spec',
|
|
115
|
+
});
|
|
116
|
+
options = {
|
|
117
|
+
...options,
|
|
118
|
+
params: options.params && [...options.params],
|
|
119
|
+
baseline: options.baseline && [...options.baseline],
|
|
120
|
+
accounts: { ...options.accounts },
|
|
121
|
+
resume: options.resume && [...options.resume],
|
|
122
|
+
};
|
|
123
|
+
if (options.maxTotalFee !== undefined &&
|
|
124
|
+
(typeof options.maxTotalFee !== 'bigint' || options.maxTotalFee < BigInt(0)))
|
|
125
|
+
throw new errors_1.JobSpecError('INVALID_AMOUNT', 'maxTotalFee must be a nonnegative bigint.');
|
|
126
|
+
if (options.baseline) {
|
|
127
|
+
if (options.baseline.length !== input.reads.length)
|
|
128
|
+
throw new errors_1.JobSpecError('INVALID_BASELINE', 'Provide one baseline entry per read; use null for unknown entries.');
|
|
129
|
+
options.baseline.forEach((value) => {
|
|
130
|
+
if (value !== null)
|
|
131
|
+
(0, codec_1.int256)(value);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
let spec;
|
|
135
|
+
if ('params' in input) {
|
|
136
|
+
if (input.checks.some((c) => c.target === constants_1.PRINCIPAL ||
|
|
137
|
+
(typeof c.target === 'object' && 'principalBps' in c.target)) &&
|
|
138
|
+
options.principal === undefined)
|
|
139
|
+
throw new errors_1.JobSpecError('UNFILLED_PLACEHOLDER', 'Supply principal to resolve principal-based preview targets.');
|
|
140
|
+
const accounts = { ...options.accounts };
|
|
141
|
+
const resolve = resolvers.get(options.client.universal);
|
|
142
|
+
for (const [index, read] of input.reads.entries()) {
|
|
143
|
+
const chain = `${read.chainNamespace}:${read.chainId}`;
|
|
144
|
+
const needed = read.fills?.some((f) => f.source === 'WALLET') ||
|
|
145
|
+
input.checks.some((c) => c.read === index && c.target === 'agentic:wallet');
|
|
146
|
+
if (needed && !accounts[chain] && options.wallet && resolve)
|
|
147
|
+
accounts[chain] = await resolve((0, viem_1.getAddress)(options.wallet), chain);
|
|
148
|
+
}
|
|
149
|
+
spec = (0, helpers_1.resolveJobSpec)(input, {
|
|
150
|
+
principal: options.principal ?? BigInt(0),
|
|
151
|
+
params: options.params ?? [],
|
|
152
|
+
accounts,
|
|
153
|
+
origin: viem_1.zeroHash,
|
|
154
|
+
executeBy: 0,
|
|
155
|
+
settleWindow: 1,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else
|
|
159
|
+
spec = input;
|
|
160
|
+
const waitOptions = {
|
|
161
|
+
timeoutMs: options.advanced?.timeout,
|
|
162
|
+
pollingIntervalMs: options.advanced?.pollingIntervalMs,
|
|
163
|
+
resultShape: { kind: 'raw' },
|
|
164
|
+
};
|
|
165
|
+
let responses;
|
|
166
|
+
let quoted = BigInt(0);
|
|
167
|
+
let newlyPaid = BigInt(0);
|
|
168
|
+
if (options.resume) {
|
|
169
|
+
if (options.resume.length !== spec.reads.length ||
|
|
170
|
+
options.resume.some((id) => !/^0x[\da-f]{64}$/i.test(id)) ||
|
|
171
|
+
new Set(options.resume.map((x) => x.toLowerCase())).size !==
|
|
172
|
+
options.resume.length)
|
|
173
|
+
throw new errors_1.JobSpecError('INVALID_PREVIEW_RESUME', 'Provide one distinct request ID per read, in read order.');
|
|
174
|
+
responses = [];
|
|
175
|
+
for (const [i, id] of options.resume.entries()) {
|
|
176
|
+
const response = await options.client.universal.trackRead({ requestId: id }, { resultShape: { kind: 'raw' }, progressHook: options.progressHook });
|
|
177
|
+
const read = spec.reads[i];
|
|
178
|
+
let matched = false;
|
|
179
|
+
try {
|
|
180
|
+
const actual = response.request.spec;
|
|
181
|
+
const envelope = (0, envelopes_1.decodeEvmQueryEnvelope)(actual.query);
|
|
182
|
+
const [target, calldata] = (0, viem_1.decodeAbiParameters)([{ type: 'address' }, { type: 'bytes' }], envelope.payload);
|
|
183
|
+
matched =
|
|
184
|
+
response.requestId.toLowerCase() === id.toLowerCase() &&
|
|
185
|
+
actual.account.chainNamespace === read.chainNamespace &&
|
|
186
|
+
actual.account.chainId === read.chainId &&
|
|
187
|
+
actual.minConfirmations >= read.minConfirmations &&
|
|
188
|
+
envelope.queryType === 1 &&
|
|
189
|
+
envelope.refType === 0 &&
|
|
190
|
+
envelope.blockNumber === actual.blockNumber &&
|
|
191
|
+
(0, viem_1.getAddress)(target) === (0, viem_1.getAddress)(read.target) &&
|
|
192
|
+
calldata.toLowerCase() ===
|
|
193
|
+
(read.selector + read.args.slice(2)).toLowerCase();
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
/* A request from another schema/query cannot stand in for this logical read. */
|
|
197
|
+
}
|
|
198
|
+
if (!matched)
|
|
199
|
+
throw new errors_1.JobSpecError('PREVIEW_REQUEST_MISMATCH', 'The recorded request does not match this resolved JobSpec read.', { read: i, requestId: id });
|
|
200
|
+
responses.push(response);
|
|
201
|
+
}
|
|
202
|
+
quoted = responses.reduce((sum, response) => sum + response.fees.paid, BigInt(0));
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const prepared = [];
|
|
206
|
+
for (const read of spec.reads) {
|
|
207
|
+
const readOptions = {
|
|
208
|
+
chain: `${read.chainNamespace}:${read.chainId}`,
|
|
209
|
+
callData: (read.selector + read.args.slice(2)),
|
|
210
|
+
minConfirmations: read.minConfirmations,
|
|
211
|
+
expiryBlocks: options.expiryBlocks,
|
|
212
|
+
refundTo: options.refundTo,
|
|
213
|
+
callback: options.callback,
|
|
214
|
+
};
|
|
215
|
+
prepared.push(await options.client.universal.prepareRead(read.target, readOptions));
|
|
216
|
+
}
|
|
217
|
+
quoted = prepared.reduce((sum, read) => sum + read.value, BigInt(0));
|
|
218
|
+
if (options.maxTotalFee !== undefined && quoted > options.maxTotalFee)
|
|
219
|
+
throw new errors_1.JobSpecError('PREVIEW_FEE_EXCEEDED', 'The prepared read fees exceed maxTotalFee. No read was submitted.', { quoted, maxTotalFee: options.maxTotalFee });
|
|
220
|
+
let batch;
|
|
221
|
+
try {
|
|
222
|
+
batch = await options.client.universal.executeReads(prepared, {
|
|
223
|
+
progressHook: options.progressHook,
|
|
224
|
+
advanced: options.advanced,
|
|
225
|
+
waitForCompletion: false,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
catch (cause) {
|
|
229
|
+
const error = cause;
|
|
230
|
+
throw new errors_1.JobSpecError('PREVIEW_INCOMPLETE', 'Read submission could not be confirmed. Track the recorded transactions before requesting more reads.', {
|
|
231
|
+
txHash: error?.txHash,
|
|
232
|
+
transactionHashes: error?.transactionHashes,
|
|
233
|
+
pendingTransactionHash: error?.pendingTransactionHash,
|
|
234
|
+
}, cause);
|
|
235
|
+
}
|
|
236
|
+
responses = batch.reads;
|
|
237
|
+
newlyPaid = quoted;
|
|
238
|
+
}
|
|
239
|
+
const requestIds = responses.map((r) => r.requestId);
|
|
240
|
+
try {
|
|
241
|
+
responses = await Promise.all(responses.map((r) => r.wait(waitOptions)));
|
|
242
|
+
}
|
|
243
|
+
catch (cause) {
|
|
244
|
+
throw new errors_1.JobSpecError('PREVIEW_INCOMPLETE', 'The paid reads are still recoverable. Resume with these request IDs instead of paying again.', { requestIds, txHashes: responses.map((r) => r.txHash) }, cause);
|
|
245
|
+
}
|
|
246
|
+
const reads = responses.map((response, i) => {
|
|
247
|
+
const value = response.raw?.resultData ?? null;
|
|
248
|
+
if (response.outcome !== read_state_types_1.READ_OUTCOME.SUCCESS || value === null)
|
|
249
|
+
return {
|
|
250
|
+
value,
|
|
251
|
+
decoded: null,
|
|
252
|
+
error: response.errorMsg || 'The paid read did not complete successfully.',
|
|
253
|
+
response,
|
|
254
|
+
};
|
|
255
|
+
try {
|
|
256
|
+
return {
|
|
257
|
+
value,
|
|
258
|
+
decoded: decodeJobAnswer(spec.reads[i], value),
|
|
259
|
+
response,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
return { value, decoded: null, error: e.message, response };
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
const checks = spec.checks.map((check) => {
|
|
267
|
+
let value = reads[check.read].decoded;
|
|
268
|
+
if (value === null)
|
|
269
|
+
return { pass: null, value, reason: 'READ_UNAVAILABLE' };
|
|
270
|
+
if (check.evaluation === 'NUMBER' || check.evaluation === 'PERCENTAGE') {
|
|
271
|
+
const before = options.baseline?.[check.read];
|
|
272
|
+
if (before === undefined || before === null)
|
|
273
|
+
return { pass: null, value: null, reason: 'BASELINE_REQUIRED' };
|
|
274
|
+
if (check.evaluation === 'PERCENTAGE' && before === BigInt(0))
|
|
275
|
+
return { pass: null, value: null, reason: 'ZERO_BASELINE' };
|
|
276
|
+
try {
|
|
277
|
+
value = (0, codec_1.int256)(value - before);
|
|
278
|
+
if (check.evaluation === 'PERCENTAGE')
|
|
279
|
+
value = (0, codec_1.int256)(value * BigInt(10000)) / before;
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return { pass: null, value: null, reason: 'ARITHMETIC_OVERFLOW' };
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return { pass: compare(value, check.operation, check.target), value };
|
|
286
|
+
});
|
|
287
|
+
return {
|
|
288
|
+
reads,
|
|
289
|
+
checks,
|
|
290
|
+
pass: combine(spec.logic, checks),
|
|
291
|
+
fees: {
|
|
292
|
+
quoted,
|
|
293
|
+
paid: reads.reduce((sum, r) => sum + r.response.fees.paid, BigInt(0)),
|
|
294
|
+
newlyPaid,
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=preview.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type AbiParameter, type Hex } from 'viem';
|
|
2
|
+
import type { PrepareJobReadOptions, PreparedJobRead, ExecuteJobReadOptions } from './types';
|
|
3
|
+
import type { ReadOptions } from '../read-state/read-params';
|
|
4
|
+
export declare function inheritReadLabel(source: PreparedJobRead, target: PreparedJobRead): void;
|
|
5
|
+
export declare function jobReadLabel(read: PreparedJobRead): string | undefined;
|
|
6
|
+
/** Same logical call as a JobSpec read, through the normal paid universal-read path. */
|
|
7
|
+
export declare function jobReadOptions(read: PreparedJobRead, options?: ExecuteJobReadOptions): ReadOptions;
|
|
8
|
+
export declare function outputTypes(params: readonly AbiParameter[]): Hex;
|
|
9
|
+
/** Pure ABI preparation, independent of read fees, RPCs and the request preflight. */
|
|
10
|
+
export declare function prepareJobRead(options: PrepareJobReadOptions): PreparedJobRead;
|
|
11
|
+
export interface OutputNode {
|
|
12
|
+
code: number;
|
|
13
|
+
children: OutputNode[];
|
|
14
|
+
size?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function parseOutputs(outputs: Hex): OutputNode;
|
|
17
|
+
export declare function validateJobRead(r: PreparedJobRead & {
|
|
18
|
+
field?: number[];
|
|
19
|
+
minConfirmations?: number;
|
|
20
|
+
}): void;
|