@aztec/txe 5.0.0-private.20260319 → 5.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/AuthRegistry-CPGFQR26.js +3 -0
- package/dest/AuthRegistry-CPGFQR26.js.map +7 -0
- package/dest/ContractClassRegistry-EHVIHGEK.js +3 -0
- package/dest/ContractClassRegistry-EHVIHGEK.js.map +7 -0
- package/dest/ContractInstanceRegistry-DWZDXHRG.js +3 -0
- package/dest/ContractInstanceRegistry-DWZDXHRG.js.map +7 -0
- package/dest/FeeJuice-MI32ZO7B.js +3 -0
- package/dest/FeeJuice-MI32ZO7B.js.map +7 -0
- package/dest/HandshakeRegistry-3KSP3ITH.js +3 -0
- package/dest/HandshakeRegistry-3KSP3ITH.js.map +7 -0
- package/dest/MultiCallEntrypoint-IU7HYFYE.js +3 -0
- package/dest/MultiCallEntrypoint-IU7HYFYE.js.map +7 -0
- package/dest/SchnorrAccount-6TUE7JX4.js +3 -0
- package/dest/SchnorrAccount-6TUE7JX4.js.map +7 -0
- package/dest/SchnorrInitializerlessAccount-S3DU2DJK.js +3 -0
- package/dest/SchnorrInitializerlessAccount-S3DU2DJK.js.map +7 -0
- package/dest/bin/check_txe_oracle_version.d.ts +2 -0
- package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
- package/dest/bin/check_txe_oracle_version.js +61 -0
- package/dest/bin/index.js +3 -30
- package/dest/bin/index.js.map +7 -0
- package/dest/bin/oracle_test_server.d.ts +3 -0
- package/dest/bin/oracle_test_server.d.ts.map +1 -0
- package/dest/bin/oracle_test_server.js +41 -0
- package/dest/chunk-5U25VAFR.js +265 -0
- package/dest/chunk-5U25VAFR.js.map +7 -0
- package/dest/chunk-BJVAAXNA.js +3 -0
- package/dest/chunk-BJVAAXNA.js.map +7 -0
- package/dest/chunk-UPW55EJX.js +304 -0
- package/dest/chunk-UPW55EJX.js.map +7 -0
- package/dest/constants.d.ts +5 -1
- package/dest/constants.d.ts.map +1 -1
- package/dest/constants.js +8 -0
- package/dest/dispatcher_pool.d.ts +67 -0
- package/dest/dispatcher_pool.d.ts.map +1 -0
- package/dest/dispatcher_pool.js +286 -0
- package/dest/index.d.ts +51 -7
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +70 -190
- package/dest/metafile.json +38829 -0
- package/dest/msgpackr_fr_extension.d.ts +2 -0
- package/dest/msgpackr_fr_extension.d.ts.map +1 -0
- package/dest/msgpackr_fr_extension.js +21 -0
- package/dest/oracle/interfaces.d.ts +33 -8
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/test-resolver/fixtures.d.ts +43 -0
- package/dest/oracle/test-resolver/fixtures.d.ts.map +1 -0
- package/dest/oracle/test-resolver/fixtures.js +39 -0
- package/dest/oracle/test-resolver/index.d.ts +9 -0
- package/dest/oracle/test-resolver/index.d.ts.map +1 -0
- package/dest/oracle/test-resolver/index.js +33 -0
- package/dest/oracle/test-resolver/resolver.d.ts +34 -0
- package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
- package/dest/oracle/test-resolver/resolver.js +114 -0
- package/dest/oracle/txe_oracle_public_context.d.ts +26 -2
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +43 -1
- package/dest/oracle/txe_oracle_registry.d.ts +14 -0
- package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_registry.js +562 -0
- package/dest/oracle/txe_oracle_top_level_context.d.ts +33 -20
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +151 -58
- package/dest/oracle/txe_oracle_version.d.ts +17 -0
- package/dest/oracle/txe_oracle_version.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_version.js +14 -0
- package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
- package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
- package/dest/oracle/txe_private_execution_oracle.js +15 -0
- package/dest/rpc_server.d.ts +14 -0
- package/dest/rpc_server.d.ts.map +1 -0
- package/dest/rpc_server.js +78 -0
- package/dest/rpc_translator.d.ts +103 -233
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +695 -636
- package/dest/server.bundle.js +3 -0
- package/dest/server.bundle.js.map +7 -0
- package/dest/state_machine/archiver.d.ts +4 -3
- package/dest/state_machine/archiver.d.ts.map +1 -1
- package/dest/state_machine/archiver.js +26 -15
- package/dest/state_machine/dummy_p2p_client.d.ts +14 -7
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +19 -4
- package/dest/state_machine/global_variable_builder.d.ts +9 -4
- package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
- package/dest/state_machine/global_variable_builder.js +9 -3
- package/dest/state_machine/index.d.ts +4 -2
- package/dest/state_machine/index.d.ts.map +1 -1
- package/dest/state_machine/index.js +11 -3
- package/dest/state_machine/mock_epoch_cache.d.ts +1 -2
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
- package/dest/state_machine/mock_epoch_cache.js +0 -3
- package/dest/state_machine/synchronizer.js +1 -1
- package/dest/txe_session.d.ts +86 -19
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +244 -45
- package/dest/utils/encoding.d.ts +191 -0
- package/dest/utils/encoding.d.ts.map +1 -0
- package/dest/{util → utils}/encoding.js +7 -2
- package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
- package/dest/utils/expected_failure_error.d.ts.map +1 -0
- package/dest/{util → utils}/txe_account_store.d.ts +1 -1
- package/dest/utils/txe_account_store.d.ts.map +1 -0
- package/dest/utils/txe_artifact_resolver.d.ts +37 -0
- package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
- package/dest/utils/txe_artifact_resolver.js +161 -0
- package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
- package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
- package/dest/{util → utils}/txe_public_contract_data_source.js +1 -3
- package/dest/worker.bundle.js +3 -0
- package/dest/worker.bundle.js.map +7 -0
- package/dest/worker.d.ts +2 -0
- package/dest/worker.d.ts.map +1 -0
- package/dest/worker.js +92 -0
- package/package.json +38 -21
- package/src/bin/check_txe_oracle_version.ts +70 -0
- package/src/bin/index.ts +11 -2
- package/src/bin/oracle_test_server.ts +51 -0
- package/src/constants.ts +10 -0
- package/src/dispatcher_pool.ts +317 -0
- package/src/index.ts +97 -227
- package/src/msgpackr_fr_extension.ts +23 -0
- package/src/oracle/interfaces.ts +29 -7
- package/src/oracle/test-resolver/fixtures.ts +84 -0
- package/src/oracle/test-resolver/index.ts +45 -0
- package/src/oracle/test-resolver/resolver.ts +165 -0
- package/src/oracle/txe_oracle_public_context.ts +60 -0
- package/src/oracle/txe_oracle_registry.ts +401 -0
- package/src/oracle/txe_oracle_top_level_context.ts +185 -67
- package/src/oracle/txe_oracle_version.ts +17 -0
- package/src/oracle/txe_private_execution_oracle.ts +30 -0
- package/src/rpc_server.ts +87 -0
- package/src/rpc_translator.ts +765 -913
- package/src/state_machine/archiver.ts +38 -16
- package/src/state_machine/dummy_p2p_client.ts +35 -11
- package/src/state_machine/global_variable_builder.ts +18 -3
- package/src/state_machine/index.ts +17 -5
- package/src/state_machine/mock_epoch_cache.ts +0 -4
- package/src/state_machine/synchronizer.ts +1 -1
- package/src/txe_session.ts +434 -57
- package/src/{util → utils}/encoding.ts +8 -2
- package/src/utils/txe_artifact_resolver.ts +217 -0
- package/src/{util → utils}/txe_public_contract_data_source.ts +0 -2
- package/src/worker.ts +98 -0
- package/dest/util/encoding.d.ts +0 -720
- package/dest/util/encoding.d.ts.map +0 -1
- package/dest/util/expected_failure_error.d.ts.map +0 -1
- package/dest/util/txe_account_store.d.ts.map +0 -1
- package/dest/util/txe_public_contract_data_source.d.ts +0 -20
- package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
- /package/dest/{util → utils}/expected_failure_error.js +0 -0
- /package/dest/{util → utils}/txe_account_store.js +0 -0
- /package/src/{util → utils}/expected_failure_error.ts +0 -0
- /package/src/{util → utils}/txe_account_store.ts +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
3
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { withHexPrefix, withoutHexPrefix } from '@aztec/foundation/string';
|
|
5
|
+
import type { InputSlot, OracleRegistryEntry } from '@aztec/pxe/simulator';
|
|
6
|
+
|
|
7
|
+
import type { ForeignCallArgs, ForeignCallResult } from '../../utils/encoding.js';
|
|
8
|
+
import type { OracleTestScenario } from './fixtures.js';
|
|
9
|
+
|
|
10
|
+
/** Name of the meta-oracle that Noir tests call to select a fixture scenario by name. */
|
|
11
|
+
const SET_SCENARIO_ORACLE = 'aztec_oracle_test_set_scenario';
|
|
12
|
+
|
|
13
|
+
export type OracleTestCallInput = {
|
|
14
|
+
session_id: number;
|
|
15
|
+
function: string;
|
|
16
|
+
root_path: string;
|
|
17
|
+
package_name: string;
|
|
18
|
+
inputs: ForeignCallArgs;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Oracle resolver for roundtrip tests. Selects a fixture scenario, verifies that the received inputs match, and
|
|
23
|
+
* returns the corresponding fixture output.
|
|
24
|
+
*
|
|
25
|
+
* For oracles with a single fixture, it is selected automatically. For oracles with multiple fixtures, Noir tests
|
|
26
|
+
* must call the `oracle_test_set_scenario` meta-oracle to select a specific scenario by name before calling the
|
|
27
|
+
* real oracle.
|
|
28
|
+
*/
|
|
29
|
+
export class OracleTestResolver {
|
|
30
|
+
private readonly calledOracles = new Set<string>();
|
|
31
|
+
private readonly pendingScenario = new Map<number, string>();
|
|
32
|
+
private readonly logger: Logger;
|
|
33
|
+
|
|
34
|
+
constructor(
|
|
35
|
+
private readonly registry: Record<string, OracleRegistryEntry>,
|
|
36
|
+
private readonly fixtures: Partial<Record<string, OracleTestScenario[]>>,
|
|
37
|
+
logger?: Logger,
|
|
38
|
+
) {
|
|
39
|
+
this.logger = logger ?? createLogger('txe:test-resolver');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// eslint-disable-next-line require-await
|
|
43
|
+
async resolve_foreign_call(callData: OracleTestCallInput): Promise<ForeignCallResult> {
|
|
44
|
+
const oracleName = callData.function;
|
|
45
|
+
this.logger.debug('Resolving oracle', { oracleName });
|
|
46
|
+
|
|
47
|
+
if (oracleName === SET_SCENARIO_ORACLE) {
|
|
48
|
+
return this.#handleSetScenario(callData);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.calledOracles.add(oracleName);
|
|
52
|
+
|
|
53
|
+
if (!(oracleName in this.registry)) {
|
|
54
|
+
throw new Error(`Oracle '${oracleName}' not found in registry`);
|
|
55
|
+
}
|
|
56
|
+
const entry = this.registry[oracleName];
|
|
57
|
+
|
|
58
|
+
const scenarios = this.fixtures[oracleName];
|
|
59
|
+
if (!scenarios || scenarios.length === 0) {
|
|
60
|
+
throw new Error(`No fixture defined for oracle '${oracleName}'`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const match = this.#selectScenario(callData.session_id, oracleName, scenarios);
|
|
64
|
+
this.#verifyInputs(callData.inputs, entry, match, oracleName);
|
|
65
|
+
|
|
66
|
+
this.logger.debug('Verified scenario for oracle', { oracleName });
|
|
67
|
+
|
|
68
|
+
const outputSlots = entry.serializeReturn(match.output);
|
|
69
|
+
return {
|
|
70
|
+
values: outputSlots.map(slot => (Array.isArray(slot) ? slot.map(withoutHexPrefix) : withoutHexPrefix(slot))),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Returns oracles that have a fixture defined but were never called during testing. */
|
|
75
|
+
getUncalledFixtures(): string[] {
|
|
76
|
+
return Object.keys(this.fixtures).filter(name => !this.calledOracles.has(name));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Returns oracles in the registry that have no fixture defined at all. */
|
|
80
|
+
getMissingFixtures(): string[] {
|
|
81
|
+
return Object.keys(this.registry).filter(name => !(name in this.fixtures));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#handleSetScenario(callData: { session_id: number; inputs: ForeignCallArgs }): ForeignCallResult {
|
|
85
|
+
const charCodes = callData.inputs[0] as string[];
|
|
86
|
+
const scenarioName = charCodes
|
|
87
|
+
.map(hex => Number(BigInt(hex.startsWith('0x') ? hex : `0x${hex}`)))
|
|
88
|
+
.map(code => String.fromCharCode(code))
|
|
89
|
+
.join('');
|
|
90
|
+
|
|
91
|
+
this.logger.debug('Setting scenario for next oracle call', { sessionId: callData.session_id, scenarioName });
|
|
92
|
+
this.pendingScenario.set(callData.session_id, scenarioName);
|
|
93
|
+
|
|
94
|
+
return { values: [] };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Selects a scenario by pending name (for multi-fixture oracles) or returns the single fixture. */
|
|
98
|
+
#selectScenario(sessionId: number, oracleName: string, fixtures: OracleTestScenario[]): OracleTestScenario {
|
|
99
|
+
const selectedName = this.pendingScenario.get(sessionId);
|
|
100
|
+
|
|
101
|
+
if (selectedName !== undefined) {
|
|
102
|
+
this.pendingScenario.delete(sessionId);
|
|
103
|
+
const matches = fixtures.filter(s => s.scenario === selectedName);
|
|
104
|
+
if (matches.length === 0) {
|
|
105
|
+
const available = fixtures.map(s => s.scenario).join(', ');
|
|
106
|
+
throw new Error(`No scenario named '${selectedName}' for oracle '${oracleName}'. Available: ${available}`);
|
|
107
|
+
}
|
|
108
|
+
if (matches.length > 1) {
|
|
109
|
+
throw new Error(`Duplicate scenario name '${selectedName}' for oracle '${oracleName}'`);
|
|
110
|
+
}
|
|
111
|
+
return matches[0];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (fixtures.length === 1) {
|
|
115
|
+
return fixtures[0];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Oracle '${oracleName}' has ${fixtures.length} fixture scenarios. ` +
|
|
120
|
+
`Use #[oracle_test("scenario_name")] to select one.`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Deserializes actual inputs and verifies they match the scenario's expected inputs. */
|
|
125
|
+
#verifyInputs(
|
|
126
|
+
rawInputs: ForeignCallArgs,
|
|
127
|
+
entry: OracleRegistryEntry,
|
|
128
|
+
scenario: OracleTestScenario,
|
|
129
|
+
oracleName: string,
|
|
130
|
+
): void {
|
|
131
|
+
const expectedInputs = scenario.inputs as Record<string, unknown>;
|
|
132
|
+
if (Object.keys(expectedInputs).length === 0) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const normalized: InputSlot[] = rawInputs.map(v =>
|
|
137
|
+
Array.isArray(v) ? (v as string[]).map(withHexPrefix) : [withHexPrefix(v as string)],
|
|
138
|
+
);
|
|
139
|
+
const named = entry.deserializeParams(normalized);
|
|
140
|
+
|
|
141
|
+
for (const param of named) {
|
|
142
|
+
if (!(param.name in expectedInputs)) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Unexpected param '${param.name}' for oracle '${oracleName}'. ` +
|
|
145
|
+
`Expected: ${Object.keys(expectedInputs).join(', ')}`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
if (!valuesEqual(param.value, expectedInputs[param.name])) {
|
|
149
|
+
const scenarioLabel = scenario.scenario ? ` (scenario '${scenario.scenario}')` : '';
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Input mismatch for oracle '${oracleName}'${scenarioLabel}: ` +
|
|
152
|
+
`param '${param.name}' expected ${String(expectedInputs[param.name])} ` +
|
|
153
|
+
`but got ${String(param.value)}`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function valuesEqual(actual: unknown, expected: unknown): boolean {
|
|
161
|
+
if (Array.isArray(actual) && Array.isArray(expected)) {
|
|
162
|
+
return actual.length === expected.length && actual.every((v, i) => valuesEqual(v, expected[i]));
|
|
163
|
+
}
|
|
164
|
+
return String(actual) === String(expected);
|
|
165
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
|
+
import type { ContractStore } from '@aztec/pxe/server';
|
|
4
5
|
import { PublicDataWrite } from '@aztec/stdlib/avm';
|
|
5
6
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
6
7
|
import type { L2Block } from '@aztec/stdlib/block';
|
|
8
|
+
import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
7
9
|
import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/stdlib/hash';
|
|
8
10
|
import {
|
|
9
11
|
MerkleTreeId,
|
|
@@ -29,6 +31,7 @@ export class TXEOraclePublicContext implements IAvmExecutionOracle {
|
|
|
29
31
|
private forkedWorldTrees: MerkleTreeWriteOperations,
|
|
30
32
|
private txRequestHash: Fr,
|
|
31
33
|
private globalVariables: GlobalVariables,
|
|
34
|
+
private contractStore: ContractStore,
|
|
32
35
|
) {
|
|
33
36
|
this.logger = createLogger('txe:public_context');
|
|
34
37
|
|
|
@@ -122,6 +125,63 @@ export class TXEOraclePublicContext implements IAvmExecutionOracle {
|
|
|
122
125
|
return value;
|
|
123
126
|
}
|
|
124
127
|
|
|
128
|
+
getContractInstanceDeployer(address: AztecAddress): Promise<{ member: Fr; exists: boolean }> {
|
|
129
|
+
return this.getContractInstanceMember(address, i => i.deployer.toField());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getContractInstanceClassId(address: AztecAddress): Promise<{ member: Fr; exists: boolean }> {
|
|
133
|
+
return this.getContractInstanceMember(address, i => i.currentContractClassId);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getContractInstanceInitializationHash(address: AztecAddress): Promise<{ member: Fr; exists: boolean }> {
|
|
137
|
+
return this.getContractInstanceMember(address, i => i.initializationHash);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getContractInstanceImmutablesHash(address: AztecAddress): Promise<{ member: Fr; exists: boolean }> {
|
|
141
|
+
return this.getContractInstanceMember(address, i => i.immutablesHash);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private async getContractInstanceMember(
|
|
145
|
+
address: AztecAddress,
|
|
146
|
+
accessor: (instance: ContractInstanceWithAddress) => Fr,
|
|
147
|
+
): Promise<{ member: Fr; exists: boolean }> {
|
|
148
|
+
const instance = await this.contractStore.getContractInstance(address);
|
|
149
|
+
if (!instance) {
|
|
150
|
+
return { member: Fr.ZERO, exists: false };
|
|
151
|
+
}
|
|
152
|
+
return { member: accessor(instance), exists: true };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
returndataSize(): Promise<Fr> {
|
|
156
|
+
throw new Error(
|
|
157
|
+
'Contract calls are forbidden inside a `TestEnvironment::public_context`, use `public_call` instead',
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
returndataCopy(_rdOffset: number, _copySize: number): Promise<Fr[]> {
|
|
162
|
+
throw new Error(
|
|
163
|
+
'Contract calls are forbidden inside a `TestEnvironment::public_context`, use `public_call` instead',
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
call(_l2Gas: number, _daGas: number, _address: AztecAddress, _argsLength: number, _args: Fr[]): Promise<Fr[]> {
|
|
168
|
+
throw new Error(
|
|
169
|
+
'Contract calls are forbidden inside a `TestEnvironment::public_context`, use `public_call` instead',
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
staticCall(_l2Gas: number, _daGas: number, _address: AztecAddress, _argsLength: number, _args: Fr[]): Promise<Fr[]> {
|
|
174
|
+
throw new Error(
|
|
175
|
+
'Contract calls are forbidden inside a `TestEnvironment::public_context`, use `public_call` instead',
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
successCopy(): Promise<Fr> {
|
|
180
|
+
throw new Error(
|
|
181
|
+
'Contract calls are forbidden inside a `TestEnvironment::public_context`, use `public_call` instead',
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
125
185
|
async close(): Promise<L2Block> {
|
|
126
186
|
this.logger.debug('Exiting Public Context, building block with collected side effects', {
|
|
127
187
|
blockNumber: this.globalVariables.blockNumber,
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import {
|
|
3
|
+
GAS_SETTINGS_LENGTH,
|
|
4
|
+
MAX_NOTE_HASHES_PER_TX,
|
|
5
|
+
MAX_NULLIFIERS_PER_TX,
|
|
6
|
+
MAX_PRIVATE_LOGS_PER_TX,
|
|
7
|
+
PRIVATE_LOG_SIZE_IN_FIELDS,
|
|
8
|
+
} from '@aztec/constants';
|
|
9
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
10
|
+
import { withHexPrefix, withoutHexPrefix } from '@aztec/foundation/string';
|
|
11
|
+
import {
|
|
12
|
+
ARRAY,
|
|
13
|
+
AZTEC_ADDRESS,
|
|
14
|
+
BIGINT,
|
|
15
|
+
BLOCK_NUMBER,
|
|
16
|
+
BOOL,
|
|
17
|
+
FIELD,
|
|
18
|
+
FUNCTION_SELECTOR,
|
|
19
|
+
type InputSlot,
|
|
20
|
+
type MaybePromise,
|
|
21
|
+
OPTION,
|
|
22
|
+
ORACLE_REGISTRY,
|
|
23
|
+
type OracleRegistryEntry,
|
|
24
|
+
type ParamTypes,
|
|
25
|
+
STR,
|
|
26
|
+
type TypeMapping,
|
|
27
|
+
U32,
|
|
28
|
+
makeEntry,
|
|
29
|
+
} from '@aztec/pxe/simulator';
|
|
30
|
+
import { EventSelector } from '@aztec/stdlib/abi';
|
|
31
|
+
import { CompleteAddress } from '@aztec/stdlib/contract';
|
|
32
|
+
import { GasSettings } from '@aztec/stdlib/gas';
|
|
33
|
+
import { PrivateContextInputs } from '@aztec/stdlib/kernel';
|
|
34
|
+
import type { PrivateLog } from '@aztec/stdlib/logs';
|
|
35
|
+
import type { TxHash } from '@aztec/stdlib/tx';
|
|
36
|
+
|
|
37
|
+
import {
|
|
38
|
+
MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY,
|
|
39
|
+
MAX_OFFCHAIN_EFFECT_LEN,
|
|
40
|
+
MAX_PRIVATE_EVENTS_PER_TXE_QUERY,
|
|
41
|
+
MAX_PRIVATE_EVENT_LEN,
|
|
42
|
+
} from '../constants.js';
|
|
43
|
+
import type { ForeignCallArgs, ForeignCallResult } from '../utils/encoding.js';
|
|
44
|
+
|
|
45
|
+
const GAS_SETTINGS: TypeMapping<GasSettings> = {
|
|
46
|
+
serialization: { fn: v => v.toFields() },
|
|
47
|
+
deserialization: {
|
|
48
|
+
fn: ([reader]) => GasSettings.fromFields(reader.readFieldArray(GAS_SETTINGS_LENGTH)),
|
|
49
|
+
slots: 1,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const PRIVATE_CONTEXT_INPUTS: TypeMapping<PrivateContextInputs> = {
|
|
54
|
+
serialization: { fn: v => v.toFields() },
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const COMPLETE_ADDRESS: TypeMapping<CompleteAddress> = {
|
|
58
|
+
serialization: { fn: v => [v.address.toField(), ...v.publicKeys.toFields()] },
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const TXE_TX_EFFECTS: TypeMapping<{
|
|
62
|
+
txHash: TxHash;
|
|
63
|
+
noteHashes: Fr[];
|
|
64
|
+
nullifiers: Fr[];
|
|
65
|
+
privateLogs: PrivateLog[];
|
|
66
|
+
}> = {
|
|
67
|
+
serialization: {
|
|
68
|
+
fn: ({ txHash, noteHashes, nullifiers, privateLogs }) => {
|
|
69
|
+
const emittedLogs = privateLogs.map(log => log.getEmittedFields());
|
|
70
|
+
const rawLogStorage = emittedLogs
|
|
71
|
+
.map(fields => fields.concat(Array(PRIVATE_LOG_SIZE_IN_FIELDS - fields.length).fill(Fr.ZERO)))
|
|
72
|
+
.concat(
|
|
73
|
+
Array(MAX_PRIVATE_LOGS_PER_TX - emittedLogs.length).fill(Array(PRIVATE_LOG_SIZE_IN_FIELDS).fill(Fr.ZERO)),
|
|
74
|
+
)
|
|
75
|
+
.flat();
|
|
76
|
+
const logLengths = emittedLogs
|
|
77
|
+
.map(fields => new Fr(fields.length))
|
|
78
|
+
.concat(Array(MAX_PRIVATE_LOGS_PER_TX - emittedLogs.length).fill(Fr.ZERO));
|
|
79
|
+
const paddedNoteHashes = noteHashes.concat(Array(MAX_NOTE_HASHES_PER_TX - noteHashes.length).fill(Fr.ZERO));
|
|
80
|
+
const paddedNullifiers = nullifiers.concat(Array(MAX_NULLIFIERS_PER_TX - nullifiers.length).fill(Fr.ZERO));
|
|
81
|
+
|
|
82
|
+
return [
|
|
83
|
+
txHash.hash,
|
|
84
|
+
paddedNoteHashes,
|
|
85
|
+
new Fr(noteHashes.length),
|
|
86
|
+
paddedNullifiers,
|
|
87
|
+
new Fr(nullifiers.length),
|
|
88
|
+
rawLogStorage,
|
|
89
|
+
logLengths,
|
|
90
|
+
new Fr(emittedLogs.length),
|
|
91
|
+
] as (Fr | Fr[])[];
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const TXE_OFFCHAIN_EFFECTS: TypeMapping<{ effects: Fr[][] }> = {
|
|
97
|
+
serialization: {
|
|
98
|
+
fn: ({ effects }) => {
|
|
99
|
+
const rawArrayStorage = effects
|
|
100
|
+
.map(e => e.concat(Array(MAX_OFFCHAIN_EFFECT_LEN - e.length).fill(Fr.ZERO)))
|
|
101
|
+
.concat(
|
|
102
|
+
Array(MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY - effects.length).fill(Array(MAX_OFFCHAIN_EFFECT_LEN).fill(Fr.ZERO)),
|
|
103
|
+
)
|
|
104
|
+
.flat();
|
|
105
|
+
const effectLengths = effects
|
|
106
|
+
.map(e => new Fr(e.length))
|
|
107
|
+
.concat(Array(MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY - effects.length).fill(Fr.ZERO));
|
|
108
|
+
|
|
109
|
+
return [rawArrayStorage, effectLengths, new Fr(effects.length)] as (Fr | Fr[])[];
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const TXE_CALL_CONTEXT: TypeMapping<{ txHash: Fr; anchorBlockTimestamp: bigint }> = {
|
|
115
|
+
serialization: {
|
|
116
|
+
fn: ({ txHash, anchorBlockTimestamp }) => {
|
|
117
|
+
const isSome = txHash.isZero() ? 0 : 1;
|
|
118
|
+
return [new Fr(isSome), txHash, new Fr(anchorBlockTimestamp)];
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const CONTRACT_INSTANCE_MEMBER: TypeMapping<{ member: Fr; exists: boolean }> = {
|
|
124
|
+
serialization: { fn: ({ member, exists }) => [member, new Fr(exists)] },
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const EVENT_SELECTOR: TypeMapping<EventSelector> = {
|
|
128
|
+
serialization: { fn: v => [v.toField()] },
|
|
129
|
+
deserialization: { fn: ([reader]) => EventSelector.fromField(reader.readField()), slots: 1 },
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const TXE_PRIVATE_EVENTS: TypeMapping<Fr[][]> = {
|
|
133
|
+
serialization: {
|
|
134
|
+
fn: events => {
|
|
135
|
+
const rawArrayStorage = events
|
|
136
|
+
.map(e => e.concat(Array(MAX_PRIVATE_EVENT_LEN - e.length).fill(Fr.ZERO)))
|
|
137
|
+
.concat(
|
|
138
|
+
Array(MAX_PRIVATE_EVENTS_PER_TXE_QUERY - events.length).fill(Array(MAX_PRIVATE_EVENT_LEN).fill(Fr.ZERO)),
|
|
139
|
+
)
|
|
140
|
+
.flat();
|
|
141
|
+
const eventLengths = events
|
|
142
|
+
.map(e => new Fr(e.length))
|
|
143
|
+
.concat(Array(MAX_PRIVATE_EVENTS_PER_TXE_QUERY - events.length).fill(Fr.ZERO));
|
|
144
|
+
return [rawArrayStorage, eventLengths, new Fr(events.length)] as (Fr | Fr[])[];
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export const TXE_ORACLE_REGISTRY: Record<string, OracleRegistryEntry> = {
|
|
150
|
+
...ORACLE_REGISTRY,
|
|
151
|
+
|
|
152
|
+
aztec_txe_assertCompatibleOracleVersion: makeEntry({
|
|
153
|
+
params: [
|
|
154
|
+
{ name: 'major', type: U32 },
|
|
155
|
+
{ name: 'minor', type: U32 },
|
|
156
|
+
],
|
|
157
|
+
}),
|
|
158
|
+
|
|
159
|
+
aztec_txe_setTopLevelTXEContext: makeEntry(),
|
|
160
|
+
|
|
161
|
+
aztec_txe_setPrivateTXEContext: makeEntry({
|
|
162
|
+
params: [
|
|
163
|
+
{ name: 'contractAddress', type: OPTION(AZTEC_ADDRESS) },
|
|
164
|
+
{ name: 'anchorBlockNumber', type: OPTION(BLOCK_NUMBER) },
|
|
165
|
+
{ name: 'gasSettings', type: GAS_SETTINGS },
|
|
166
|
+
],
|
|
167
|
+
returnType: PRIVATE_CONTEXT_INPUTS,
|
|
168
|
+
}),
|
|
169
|
+
|
|
170
|
+
aztec_txe_setPublicTXEContext: makeEntry({
|
|
171
|
+
params: [{ name: 'contractAddress', type: OPTION(AZTEC_ADDRESS) }],
|
|
172
|
+
}),
|
|
173
|
+
|
|
174
|
+
aztec_txe_setUtilityTXEContext: makeEntry({
|
|
175
|
+
params: [{ name: 'contractAddress', type: OPTION(AZTEC_ADDRESS) }],
|
|
176
|
+
}),
|
|
177
|
+
|
|
178
|
+
aztec_txe_getDefaultAddress: makeEntry({ returnType: AZTEC_ADDRESS }),
|
|
179
|
+
|
|
180
|
+
aztec_txe_getNextBlockNumber: makeEntry({ returnType: BLOCK_NUMBER }),
|
|
181
|
+
|
|
182
|
+
aztec_txe_getNextBlockTimestamp: makeEntry({ returnType: BIGINT }),
|
|
183
|
+
|
|
184
|
+
aztec_txe_advanceBlocksBy: makeEntry({
|
|
185
|
+
params: [{ name: 'blocks', type: U32 }],
|
|
186
|
+
}),
|
|
187
|
+
|
|
188
|
+
aztec_txe_advanceTimestampBy: makeEntry({
|
|
189
|
+
params: [{ name: 'duration', type: BIGINT }],
|
|
190
|
+
}),
|
|
191
|
+
|
|
192
|
+
aztec_txe_deploy: makeEntry({
|
|
193
|
+
params: [
|
|
194
|
+
{ name: 'contractPath', type: STR },
|
|
195
|
+
{ name: 'initializer', type: STR },
|
|
196
|
+
{ name: 'argsLength', type: U32 },
|
|
197
|
+
{ name: 'args', type: ARRAY(FIELD) },
|
|
198
|
+
{ name: 'secret', type: FIELD },
|
|
199
|
+
{ name: 'salt', type: FIELD },
|
|
200
|
+
{ name: 'deployer', type: AZTEC_ADDRESS },
|
|
201
|
+
],
|
|
202
|
+
returnType: ARRAY(FIELD),
|
|
203
|
+
}),
|
|
204
|
+
|
|
205
|
+
aztec_txe_createAccount: makeEntry({
|
|
206
|
+
params: [{ name: 'secret', type: FIELD }],
|
|
207
|
+
returnType: COMPLETE_ADDRESS,
|
|
208
|
+
}),
|
|
209
|
+
|
|
210
|
+
aztec_txe_addAccount: makeEntry({
|
|
211
|
+
params: [{ name: 'secret', type: FIELD }],
|
|
212
|
+
returnType: COMPLETE_ADDRESS,
|
|
213
|
+
}),
|
|
214
|
+
|
|
215
|
+
aztec_txe_addAuthWitness: makeEntry({
|
|
216
|
+
params: [
|
|
217
|
+
{ name: 'address', type: AZTEC_ADDRESS },
|
|
218
|
+
{ name: 'messageHash', type: FIELD },
|
|
219
|
+
],
|
|
220
|
+
}),
|
|
221
|
+
|
|
222
|
+
aztec_txe_getLastBlockTimestamp: makeEntry({
|
|
223
|
+
returnType: BIGINT,
|
|
224
|
+
}),
|
|
225
|
+
|
|
226
|
+
aztec_txe_getLastTxEffects: makeEntry({ returnType: TXE_TX_EFFECTS }),
|
|
227
|
+
aztec_txe_getLastCallOffchainEffects: makeEntry({ returnType: TXE_OFFCHAIN_EFFECTS }),
|
|
228
|
+
aztec_txe_getLastCallContext: makeEntry({ returnType: TXE_CALL_CONTEXT }),
|
|
229
|
+
|
|
230
|
+
aztec_txe_getPrivateEvents: makeEntry({
|
|
231
|
+
params: [
|
|
232
|
+
{ name: 'selector', type: EVENT_SELECTOR },
|
|
233
|
+
{ name: 'contractAddress', type: AZTEC_ADDRESS },
|
|
234
|
+
{ name: 'scope', type: AZTEC_ADDRESS },
|
|
235
|
+
],
|
|
236
|
+
returnType: TXE_PRIVATE_EVENTS,
|
|
237
|
+
}),
|
|
238
|
+
|
|
239
|
+
aztec_txe_privateCallNewFlow: makeEntry({
|
|
240
|
+
params: [
|
|
241
|
+
{ name: 'from', type: OPTION(AZTEC_ADDRESS) },
|
|
242
|
+
{ name: 'targetContractAddress', type: AZTEC_ADDRESS },
|
|
243
|
+
{ name: 'functionSelector', type: FUNCTION_SELECTOR },
|
|
244
|
+
{ name: 'args', type: ARRAY(FIELD) },
|
|
245
|
+
{ name: 'argsHash', type: FIELD },
|
|
246
|
+
{ name: 'isStaticCall', type: BOOL },
|
|
247
|
+
{ name: 'additionalScopes', type: ARRAY(AZTEC_ADDRESS) },
|
|
248
|
+
{ name: 'authorizedUtilityCallTargets', type: ARRAY(AZTEC_ADDRESS) },
|
|
249
|
+
{ name: 'gasSettings', type: GAS_SETTINGS },
|
|
250
|
+
],
|
|
251
|
+
returnType: ARRAY(FIELD),
|
|
252
|
+
}),
|
|
253
|
+
|
|
254
|
+
aztec_txe_executeUtilityFunction: makeEntry({
|
|
255
|
+
params: [
|
|
256
|
+
{ name: 'targetContractAddress', type: AZTEC_ADDRESS },
|
|
257
|
+
{ name: 'functionSelector', type: FUNCTION_SELECTOR },
|
|
258
|
+
{ name: 'args', type: ARRAY(FIELD) },
|
|
259
|
+
{ name: 'authorizedUtilityCallTargets', type: ARRAY(AZTEC_ADDRESS) },
|
|
260
|
+
],
|
|
261
|
+
returnType: ARRAY(FIELD),
|
|
262
|
+
}),
|
|
263
|
+
|
|
264
|
+
aztec_txe_publicCallNewFlow: makeEntry({
|
|
265
|
+
params: [
|
|
266
|
+
{ name: 'from', type: OPTION(AZTEC_ADDRESS) },
|
|
267
|
+
{ name: 'address', type: AZTEC_ADDRESS },
|
|
268
|
+
{ name: 'calldata', type: ARRAY(FIELD) },
|
|
269
|
+
{ name: 'isStaticCall', type: BOOL },
|
|
270
|
+
{ name: 'gasSettings', type: GAS_SETTINGS },
|
|
271
|
+
],
|
|
272
|
+
returnType: ARRAY(FIELD),
|
|
273
|
+
}),
|
|
274
|
+
|
|
275
|
+
aztec_avm_address: makeEntry({ returnType: AZTEC_ADDRESS }),
|
|
276
|
+
|
|
277
|
+
aztec_avm_sender: makeEntry({ returnType: AZTEC_ADDRESS }),
|
|
278
|
+
|
|
279
|
+
aztec_avm_blockNumber: makeEntry({ returnType: BLOCK_NUMBER }),
|
|
280
|
+
|
|
281
|
+
aztec_avm_timestamp: makeEntry({ returnType: BIGINT }),
|
|
282
|
+
|
|
283
|
+
aztec_avm_isStaticCall: makeEntry({ returnType: BOOL }),
|
|
284
|
+
|
|
285
|
+
aztec_avm_chainId: makeEntry({ returnType: FIELD }),
|
|
286
|
+
|
|
287
|
+
aztec_avm_version: makeEntry({ returnType: FIELD }),
|
|
288
|
+
|
|
289
|
+
aztec_avm_emitNullifier: makeEntry({
|
|
290
|
+
params: [{ name: 'nullifier', type: FIELD }],
|
|
291
|
+
}),
|
|
292
|
+
|
|
293
|
+
aztec_avm_emitNoteHash: makeEntry({
|
|
294
|
+
params: [{ name: 'noteHash', type: FIELD }],
|
|
295
|
+
}),
|
|
296
|
+
|
|
297
|
+
aztec_avm_nullifierExists: makeEntry({
|
|
298
|
+
params: [{ name: 'siloedNullifier', type: FIELD }],
|
|
299
|
+
returnType: BOOL,
|
|
300
|
+
}),
|
|
301
|
+
|
|
302
|
+
aztec_avm_storageRead: makeEntry({
|
|
303
|
+
params: [
|
|
304
|
+
{ name: 'slot', type: FIELD },
|
|
305
|
+
{ name: 'contractAddress', type: AZTEC_ADDRESS },
|
|
306
|
+
],
|
|
307
|
+
returnType: FIELD,
|
|
308
|
+
}),
|
|
309
|
+
|
|
310
|
+
aztec_avm_storageWrite: makeEntry({
|
|
311
|
+
params: [
|
|
312
|
+
{ name: 'slot', type: FIELD },
|
|
313
|
+
{ name: 'value', type: FIELD },
|
|
314
|
+
],
|
|
315
|
+
}),
|
|
316
|
+
|
|
317
|
+
aztec_avm_emitPublicLog: makeEntry({
|
|
318
|
+
params: [{ name: 'message', type: ARRAY(FIELD) }],
|
|
319
|
+
}),
|
|
320
|
+
|
|
321
|
+
aztec_avm_returndataSize: makeEntry({ returnType: FIELD }),
|
|
322
|
+
|
|
323
|
+
aztec_avm_returndataCopy: makeEntry({
|
|
324
|
+
params: [
|
|
325
|
+
{ name: 'rdOffset', type: U32 },
|
|
326
|
+
{ name: 'copySize', type: U32 },
|
|
327
|
+
],
|
|
328
|
+
returnType: ARRAY(FIELD),
|
|
329
|
+
}),
|
|
330
|
+
|
|
331
|
+
aztec_avm_call: makeEntry({
|
|
332
|
+
params: [
|
|
333
|
+
{ name: 'l2Gas', type: U32 },
|
|
334
|
+
{ name: 'daGas', type: U32 },
|
|
335
|
+
{ name: 'address', type: AZTEC_ADDRESS },
|
|
336
|
+
{ name: 'argsLength', type: U32 },
|
|
337
|
+
{ name: 'args', type: ARRAY(FIELD) },
|
|
338
|
+
],
|
|
339
|
+
returnType: ARRAY(FIELD),
|
|
340
|
+
}),
|
|
341
|
+
|
|
342
|
+
aztec_avm_staticCall: makeEntry({
|
|
343
|
+
params: [
|
|
344
|
+
{ name: 'l2Gas', type: U32 },
|
|
345
|
+
{ name: 'daGas', type: U32 },
|
|
346
|
+
{ name: 'address', type: AZTEC_ADDRESS },
|
|
347
|
+
{ name: 'argsLength', type: U32 },
|
|
348
|
+
{ name: 'args', type: ARRAY(FIELD) },
|
|
349
|
+
],
|
|
350
|
+
returnType: ARRAY(FIELD),
|
|
351
|
+
}),
|
|
352
|
+
|
|
353
|
+
aztec_avm_successCopy: makeEntry({ returnType: FIELD }),
|
|
354
|
+
|
|
355
|
+
aztec_avm_getContractInstanceDeployer: makeEntry({
|
|
356
|
+
params: [{ name: 'address', type: AZTEC_ADDRESS }],
|
|
357
|
+
returnType: CONTRACT_INSTANCE_MEMBER,
|
|
358
|
+
}),
|
|
359
|
+
aztec_avm_getContractInstanceClassId: makeEntry({
|
|
360
|
+
params: [{ name: 'address', type: AZTEC_ADDRESS }],
|
|
361
|
+
returnType: CONTRACT_INSTANCE_MEMBER,
|
|
362
|
+
}),
|
|
363
|
+
aztec_avm_getContractInstanceInitializationHash: makeEntry({
|
|
364
|
+
params: [{ name: 'address', type: AZTEC_ADDRESS }],
|
|
365
|
+
returnType: CONTRACT_INSTANCE_MEMBER,
|
|
366
|
+
}),
|
|
367
|
+
aztec_avm_getContractInstanceImmutablesHash: makeEntry({
|
|
368
|
+
params: [{ name: 'address', type: AZTEC_ADDRESS }],
|
|
369
|
+
returnType: CONTRACT_INSTANCE_MEMBER,
|
|
370
|
+
}),
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Deserializes oracle inputs, calls the handler with typed params, serializes the result, and wraps
|
|
375
|
+
* it in a `ForeignCallResult`. Normalizes `ForeignCallArgs` (which may contain bare strings) into
|
|
376
|
+
* `InputSlot[]` (always arrays) before deserialization.
|
|
377
|
+
*/
|
|
378
|
+
export async function callTxeHandler<K extends keyof typeof TXE_ORACLE_REGISTRY>({
|
|
379
|
+
oracle,
|
|
380
|
+
inputs,
|
|
381
|
+
handler,
|
|
382
|
+
}: {
|
|
383
|
+
oracle: K;
|
|
384
|
+
inputs: ForeignCallArgs;
|
|
385
|
+
handler: (
|
|
386
|
+
params: ParamTypes<ReturnType<(typeof TXE_ORACLE_REGISTRY)[K]['deserializeParams']>>,
|
|
387
|
+
) => MaybePromise<Parameters<(typeof TXE_ORACLE_REGISTRY)[K]['serializeReturn']>[0]>;
|
|
388
|
+
}): Promise<ForeignCallResult> {
|
|
389
|
+
const entry = TXE_ORACLE_REGISTRY[oracle] as OracleRegistryEntry;
|
|
390
|
+
// TXE foreign calls use bare hex strings, but Fr.fromString requires a 0x prefix to parse as hex.
|
|
391
|
+
const normalized: InputSlot[] = inputs.map(v =>
|
|
392
|
+
Array.isArray(v) ? (v as string[]).map(withHexPrefix) : [withHexPrefix(v as string)],
|
|
393
|
+
);
|
|
394
|
+
const named = entry.deserializeParams(normalized);
|
|
395
|
+
const positional = named.map((p: { value: unknown }) => p.value);
|
|
396
|
+
const result = await handler(positional as any);
|
|
397
|
+
const outputSlots = entry.serializeReturn(result);
|
|
398
|
+
return {
|
|
399
|
+
values: outputSlots.map(slot => (Array.isArray(slot) ? slot.map(withoutHexPrefix) : withoutHexPrefix(slot))),
|
|
400
|
+
};
|
|
401
|
+
}
|