@@ -320,6 +320,7 @@ export interface SimulationOutput {
320320 executionResult: ExecutionResult ;
321321 publicOutput: PublicSimulationOutput ;
322322 privateOutput: NestedProcessReturnValues ;
323+ error? : SimulationError ;
323324}
324325
325326export interface UserAPI {
@@ -665,7 +666,7 @@ async simulate(userRequest: UserRequest): {
665666 // If we're paying, e.g. in bananas, figure out how much AZT that is.
666667 // Note: paymentMethod.getEquivalentAztBalance() may call `read` internally.
667668 const equivalentAztBalance = await builder .paymentMethod .getEquivalentAztBalance ();
668- gasEstimator = GasEstimator . fromAztBalance (equivalentAztBalance );
669+ gasEstimator = new BinarySearchGasEstimator (equivalentAztBalance );
669670 builder .setGasSettings (gasEstimator .proposeGasSettings ());
670671
671672 while (! gasEstimator .isConverged ()) {
@@ -889,6 +890,136 @@ const { request: deployAliceAccountRequest } = await aliceDappWrappedWallet.simu
889890```
890891
891892
893+ ### Gas Estimation
894+
895+ ``` ts
896+
897+
898+ export interface GasEstimator {
899+ proposeGasSettings(): GasSettings ;
900+ isConverged(): boolean ;
901+ update(simulationOutput : SimulationOutput ): void ;
902+ }
903+
904+ interface SearchRatios {
905+ daToL2: number ;
906+ daTearDown: number ;
907+ l2TearDown: number ;
908+ }
909+
910+ // Finds a balance that is enough to cover the gas costs of the simulation.
911+ // Marks as converged if the simulation did not run out of gas,
912+ // or if it is not possible to increase the balance further.
913+ export class BinarySearchGasEstimator implements GasEstimator {
914+ // keep the initial balance
915+ // and the ratios of daGas to l2Gas
916+ // as well as the ratios to teardownGas
917+ private balance: number
918+
919+ // The upper and lower bounds of the search space.
920+ // We start at the midpoint of these bounds.
921+ // The search space is the balance, and the ratios of daGas to l2Gas
922+ // as well as the ratios to teardownGas.
923+ // The goal is to find a balance that is enough to cover the gas costs of the simulation.
924+ // Then we can just use the true gas costs for the actual transaction.
925+ private upperBounds: SearchRatios ;
926+ private lowerBounds: SearchRatios ;
927+ private current: SearchRatios ;
928+ private mostRecentSimulation? : SimulationOutput ;
929+ private iterations: number = 0 ;
930+ private maxIterations: number = 10 ;
931+ private epsilon: number = 0.01 ;
932+
933+
934+
935+ constructor (private balance : number ) {
936+ this .lowerBounds = {
937+ daToL2: 0 ,
938+ daTearDown: 0 ,
939+ l2TearDown: 0 ,
940+ };
941+ this .upperBounds = {
942+ daToL2: 1 ,
943+ daTearDown: 1 ,
944+ l2TearDown: 1 ,
945+ };
946+ this .current = {
947+ daToL2: .5 ,
948+ daTearDown: .1 ,
949+ l2TearDown: .1 ,
950+ };
951+ }
952+
953+ update(simulationOutput : SimulationOutput ): void {
954+ // If the simulation ran out of DA gas
955+ const oog = simulationOutput .getOutOfGas ();
956+ if (! oog ) {
957+ return
958+ } else if (oog === ' da' ) {
959+ // increase the balance
960+ this .lowerBounds .daToL2 = this .current .daToL2 ;
961+ } else if (oog === ' l2' ) {
962+ // increase the balance
963+ this .lowerBounds .daToL2 = this .current .daToL2 ;
964+ } else if (oog === ' da_teardown' ) {
965+ // increase the balance
966+ this .lowerBounds .daTearDown = this .current .daTearDown ;
967+ } else if (oog === ' l2_teardown' ) {
968+ // increase the balance
969+ this .lowerBounds .l2TearDown = this .current .l2TearDown ;
970+ }
971+ // update the current balance
972+ this .current .daToL2 = (this .lowerBounds .daToL2 + this .upperBounds .daToL2 ) / 2 ;
973+ this .current .daTearDown = (this .lowerBounds .daTearDown + this .upperBounds .daTearDown ) / 2 ;
974+ this .current .l2TearDown = (this .lowerBounds .l2TearDown + this .upperBounds .l2TearDown ) / 2 ;
975+ }
976+
977+ proposeGasSettings(): GasSettings {
978+ return GasSettings .from ({
979+ gasLimits: {
980+ daGas: this .balance * this .current .daToL2 ,
981+ l2Gas: this .balance * (1 - this .current .daToL2 ),
982+ },
983+ teardownGasLimits: {
984+ daGas: this .balance * this .current .daToL2 * this .current .daTearDown ,
985+ l2Gas: this .balance * (1 - this .current .daToL2 ) * this .current .l2TearDown ,
986+ },
987+ // This should actually be informed somehow
988+ maxFeesPerGas: { daGas: 1 , l2Gas: 1 },
989+ inclusionFee: 0
990+ });
991+ }
992+
993+ //
994+ isConverged(): boolean {
995+ // If the simulation did not run out of gas, we are converged.
996+ if (! this .mostRecentSimulation ?.getOutOfGas ()) {
997+ return true ;
998+ }
999+
1000+ // If we have reached the maximum number of iterations, we are converged.
1001+ if (this .iterations >= this .maxIterations ) {
1002+ return true ;
1003+ }
1004+
1005+ // If the search space is too small, we are converged.
1006+ if (this .upperBounds .daToL2 - this .lowerBounds .daToL2 < this .epsilon ) {
1007+ return true ;
1008+ }
1009+ if (this .upperBounds .daTearDown - this .lowerBounds .daTearDown < this .epsilon ) {
1010+ return true ;
1011+ }
1012+ if (this .upperBounds .l2TearDown - this .lowerBounds .l2TearDown < this .epsilon ) {
1013+ return true ;
1014+ }
1015+
1016+ return false ;
1017+
1018+ }
1019+
1020+ }
1021+
1022+ ```
8921023
8931024### Concerns
8941025
0 commit comments