@@ -90,7 +90,7 @@ const { request: transferRequest } = await aliceWallet.simulate({
90
90
value: privateBalance ,
91
91
nonce: 0n
92
92
},
93
- paymentMethod
93
+ paymentMethod ,
94
94
});
95
95
96
96
@@ -171,17 +171,6 @@ This would clear up concerns like:
171
171
// derived ContractInteractions is confusing. We should unify the flow of all ContractInteractions.
172
172
173
173
174
-
175
-
176
-
177
-
178
- Implementing this in the current API would be difficult
179
-
180
- The complexity in the current API is shown elsewhere
181
-
182
-
183
- Further, it is presently difficult to estimate the gas cost of a transaction.
184
-
185
174
### The old UML
186
175
187
176
``` mermaid
@@ -277,63 +266,60 @@ export interface AuthWitnessProvider {
277
266
): Promise <AuthWitness >;
278
267
}
279
268
280
- export interface EntrypointInterface {
281
- /**
282
- * Generates an execution request out of set of function calls.
283
- * @param execution - The execution intents to be run.
284
- * @returns The authenticated transaction execution request.
285
- */
286
- createTxExecutionRequest(execution : ExecutionRequestInit ): Promise <TxExecutionRequest >;
287
- }
288
-
289
269
export class TxExecutionRequest {
290
270
constructor (
291
- /**
292
- * Sender.
293
- */
271
+ // All these are the same:
294
272
public origin : AztecAddress ,
295
- /**
296
- * Selector of the function to call.
297
- */
298
273
public functionSelector : FunctionSelector ,
299
- /**
300
- * The hash of arguments of first call to be executed (usually account entrypoint).
301
- * @dev This hash is a pointer to `argsOfCalls` unordered array.
302
- */
303
274
public firstCallArgsHash : Fr ,
304
- /**
305
- * Transaction context.
306
- */
307
275
public txContext : TxContext ,
308
- /**
309
- * An unordered array of packed arguments for each call in the transaction.
310
- * @dev These arguments are accessed in Noir via oracle and constrained against the args hash. The length of
311
- * the array is equal to the number of function calls in the transaction (1 args per 1 call).
312
- */
313
276
public argsOfCalls : PackedValues [],
277
+ public authWitnesses : AuthWitness [],
278
+ // Add:
314
279
/**
315
- * Transient authorization witnesses for authorizing the execution of one or more actions during this tx.
316
- * These witnesses are not expected to be stored in the local witnesses database of the PXE.
280
+ * Transient capsules needed for this execution.
317
281
*/
318
- public authWitnesses : AuthWitness [],
282
+ public capsules : Fr [] [],
319
283
) {}
320
284
// ...
321
285
}
322
286
323
- export type ExecutionRequestInit = {
324
- /** The function calls to be executed. */
325
- calls: FunctionCall [];
326
- /** Any transient auth witnesses needed for this execution */
327
- authWitnesses? : AuthWitness [];
328
- /** Any transient packed arguments for this execution */
329
- packedArguments? : PackedValues [];
330
- /** Any transient capsules needed for this execution */
331
- capsules? : Fr [][];
332
- /** The fee payment method to use */
333
- paymentMethod: FeePaymentMethod ;
334
- /** The gas settings */
335
- gasSettings: GasSettings ;
336
- };
287
+ export type TxExecutionRequestEntrypoint = Pick <TxExecutionRequest ,
288
+ | ' functionSelector'
289
+ | ' firstCallArgsHash'
290
+ | ' argsOfCalls'
291
+ | ' authWitnesses' >
292
+
293
+ export interface ExecutionRequestInit {
294
+ contractInstance: ContractInstanceWithAddress ;
295
+ functionName: string ;
296
+ args: any ;
297
+ paymentMethod? : FeePaymentMethod ;
298
+ contractArtifact? : ContractArtifact ;
299
+ functionAbi? : FunctionAbi ;
300
+ from? : AztecAddress ;
301
+ simulatePublicFunctions? : boolean ;
302
+ }
303
+
304
+ export interface FeePaymentMethod {
305
+ getSetup(gasSettings : GasSettings ): Promise <{
306
+ functionCalls: FunctionCall [],
307
+ authWitnesses: AuthWitness [],
308
+ }>;
309
+
310
+ getEquivalentAztBalance(): Promise <Fr >;
311
+ }
312
+
313
+
314
+ export interface EntrypointInterface {
315
+ createTxExecutionRequestEntrypoint(
316
+ functionCalls : {
317
+ appFunctionCalls: FunctionCall [];
318
+ setupFunctionCalls: FunctionCall [];
319
+ },
320
+ authWitnessProvider : AuthWitnessProvider
321
+ ): TxExecutionRequestEntrypoint ;
322
+ }
337
323
338
324
```
339
325
@@ -377,32 +363,191 @@ Assume we're dealing with schnorr.
377
363
378
364
Get the contract artifact out of the PXE using the contractInstance's contractClassId.
379
365
380
- Scan its ` FunctionAbi ` s for the one with the given name.
366
+ ``` ts
367
+
368
+ function findFunctionAbi(contractArtifact : ContractArtifact , functionName : string ): FunctionAbi {
369
+ const functionAbi = contractArtifact .abi .find ((abi ) => abi .name === functionName );
370
+ if (! functionAbi ) {
371
+ throw new Error (` Function ${functionName } not found in contract artifact ` );
372
+ }
373
+ return functionAbi ;
374
+ }
375
+
376
+ function makeFunctionCall(
377
+ functionAbi : FunctionAbi ,
378
+ instanceAddress : AztecAddress ,
379
+ args : any ,
380
+ ): FunctionCall {
381
+ return FunctionCall .from ({
382
+ name: functionAbi .name ,
383
+ args: mapArgsObjectToArray (functionAbi .parameters , args ),
384
+ selector: FunctionSelector .fromNameAndParameters (functionAbi .name , functionAbi .parameters ),
385
+ type: functionAbi .functionType ,
386
+ to: instanceAddress ,
387
+ isStatic: functionAbi .isStatic ,
388
+ returnTypes: functionAbi .returnTypes ,
389
+ });
390
+ }
391
+
392
+
393
+ ```
394
+
395
+ Note that since ` gasSettings ` was not provided, we need to determine them.
396
+
397
+ In order to do this, we need to simulate the function call without the FPC to get the gas cost of the app logic, then simulate with the FPC to get the gas cost of the entire transaction.
398
+
399
+ #### Get the ` TxExecutionRequestEntrypoint `
400
+
401
+ ``` ts
402
+ createTxExecutionRequestAccountEntrypoint (
403
+ functionCalls : {
404
+ appFunctionCalls: FunctionCall [];
405
+ setupFunctionCalls : FunctionCall [];
406
+ },
407
+ authWitnessProvider : AuthWitnessProvider
408
+ ): TxExecutionRequestEntrypoint {
409
+ const appPayload = EntrypointPayload .fromFunctionCalls (functionCalls .appFunctionCalls );
410
+ const setupPayload = EntrypointPayload .fromFunctionCalls (functionCalls .setupFunctionCalls );
411
+ const abi = this .getEntrypointAbi ();
412
+ const entrypointPackedArgs = PackedValues .fromValues (encodeArguments (abi , [appPayload , setupPayload ]));
413
+ const firstCallArgsHash = entrypointPackedArgs .hash ();
414
+ const argsOfCalls = [... appPayload .packedArguments , ... feePayload .packedArguments , entrypointPackedArgs ];
415
+ const functionSelector = FunctionSelector .fromNameAndParameters (abi .name , abi .parameters );
416
+
417
+ // Does not insert into PXE
418
+ const appAuthWit = await authWitnessProvider .createAuthWit (appPayload .hash ());
419
+ const setupAuthWit = await authWitnessProvider .createAuthWit (setupPayload .hash ());
420
+
421
+ return {
422
+ functionSelector ,
423
+ firstCallArgsHash ,
424
+ argsOfCalls ,
425
+ authWitnesses: [appAuthWit , setupAuthWit ],
426
+ }
427
+ }
428
+ ```
429
+
430
+ #### Fill in the ` TxExecutionRequest `
381
431
382
- Construct the ` FunctionCall ` as:
383
432
``` ts
384
- {
385
- name : functionAbi .name ,
386
- args ,
387
- selector : FunctionSelector .fromNameAndParameters (functionAbi .name , functionAbi .parameters ),
388
- type : functionAbi .functionType ,
389
- to : contractInstance .address ,
390
- isStatic : functionAbi .isStatic ,
391
- returnTypes : functionAbi .returnTypes ,
433
+ // within alice wallet.
434
+ // This is a low level call that requires us to have resolved the contract artifact and function abi.
435
+ // It is intentionally synchronous.
436
+ #getTxExecutionRequest (requestInit : ExecutionRequestInit ) {
437
+ if (! requestInit .functionAbi ) {
438
+ throw new Error (' Function ABI must be provided' );
439
+ }
440
+
441
+ const builder = new TxExecutionRequestBuilder ();
442
+ builder .setOrigin (this .getAddress ());
443
+
444
+ builder .setTxContext ({
445
+ chainId: this .getChainId (),
446
+ version: this .getVersion (),
447
+ gasSettings: requestInit .gasSettings ,
448
+ });
449
+
450
+ const setup = requestInit .paymentMethod .getSetup (requestInit .gasSettings );
451
+
452
+ builder .addAuthWitnesses (setup .authWitnesses );
453
+
454
+ // Could also allow users to pass the artifact and short-circuit this
455
+ const appFunctionCall = makeFunctionCall (
456
+ requestInit .functionAbi ,
457
+ requestInit .contractInstance .address ,
458
+ requestInit .args
459
+ );
460
+ const entrypointInfo = createTxExecutionRequestAccountEntrypoint ({
461
+ appFunctionCalls: [appFunctionCall ],
462
+ setupFunctionCalls: setup .functionCalls ,
463
+ }, this .account );
464
+
465
+ builder .setFunctionSelector (entrypointInfo .functionSelector );
466
+ builder .setFirstCallArgsHash (entrypointInfo .firstCallArgsHash );
467
+ builder .setArgsOfCalls (entrypointInfo .argsOfCalls );
468
+ builder .addAuthWitnesses (entrypointInfo .authWitnesses );
469
+
470
+ return builder .build ();
471
+
392
472
}
393
473
```
394
474
395
475
396
476
397
477
478
+ #### Define top-level ` Simulate `
479
+
480
+ ``` ts
481
+ // helpers somewhere
398
482
399
- 1 . Extract the "app" EntrypointPayload
483
+ function decodeSimulatedTx(simulatedTx : SimulatedTx , functionAbi : FunctionAbi ): DecodedReturn | [] {
484
+ const rawReturnValues =
485
+ functionAbi .functionType == FunctionType .PRIVATE
486
+ ? simulatedTx .privateReturnValues ?.nested ?.[0 ].values
487
+ : simulatedTx .publicOutput ?.publicReturnValues ?.[0 ].values ;
400
488
401
- 2 . Collect all authwits
402
- 1 . For the app payload
403
- 2 . For the fee payment
404
- 3 . For anything needed by the fee payment method
405
- 3 .
489
+ return rawReturnValues ? decodeReturnValues (functionAbi .returnTypes , rawReturnValues ) : [];
490
+ }
491
+
492
+ // within alice wallet
493
+
494
+ async #simulateInner (requestInit : ExecutionRequestInit ): {
495
+ tx: SimulatedTx ,
496
+ result: DecodedReturn | [],
497
+ request: ExecutionRequestInit ,
498
+ } {
499
+ const txExecutionRequest = this .getTxExecutionRequest (initRequest );
500
+ // Call the PXE
501
+ const simulatedTx = await this .simulateTx (txExecutionRequest , builder .simulatePublicFunctions , builder .from );
502
+ const decodedReturn = decodeSimulatedTx (simulatedTx , builder .functionAbi );
503
+ return {
504
+ tx: simulatedTx ,
505
+ result: decodedReturn ,
506
+ request: initRequest ,
507
+ };
508
+ }
509
+
510
+ async simulate (requestInit : ExecutionRequestInit ): {
511
+ const builder = new ExecutionRequestInitBuilder (requestInit );
512
+
513
+ if (!builder.functionAbi) {
514
+ const contractArtifact = builder .contractArtifact ?? await pxe .getContractArtifact (builder .contractInstance .contractClassId );
515
+ builder .setContractArtifact (contractArtifact );
516
+ const functionAbi = findFunctionAbi (builder .contractArtifact , builder .functionName );
517
+ builder .setFunctionAbi (functionAbi );
518
+ }
519
+
520
+ // If we're not paying, e.g. this is just a read that we don't intend to submit as a TX,
521
+ // set the gas settings to default
522
+ if (!builder.paymentMethod){
523
+ builder .setFeePaymentMethod (new NoFeePaymentMethod ());
524
+ builder .setGasSettings (GasSettings .default ());
525
+ return this .#simulateInner (builder .build ());
526
+ }
527
+ if (builder.gasSettings) {
528
+ return this .#simulateInner (builder .build ());
529
+ }
530
+
531
+ // If we're paying, e.g. in bananas, figure out how much AZT that is.
532
+ // Note: this may call simulate recursively,
533
+ // but it *should* set the payment method to a NoFeePaymentMethod or undefined.
534
+ // perhaps we need a `read` method on the wallet.
535
+ const equivalentAztBalance = await builder .paymentMethod .getEquivalentAztBalance ();
536
+ gasEstimator = GasEstimator .fromAztBalance (equivalentAztBalance );
537
+ builder.setGasSettings(gasEstimator.proposeGasSettings());
538
+
539
+ while (!gasEstimator.isConverged()) {
540
+ const result = await this .#simulateInner (builder .build ());
541
+ gasEstimator .update (result );
542
+ builder .setGasSettings (gasEstimator .proposeGasSettings ());
543
+ }
544
+
545
+ return result;
546
+
547
+ }
548
+
549
+
550
+ ```
406
551
407
552
408
553
0 commit comments