github.com/ethereum-optimism/optimism@v1.7.2/packages/web3js-plugin/src/plugin.ts (about)

     1  import Web3, {
     2    type BlockNumberOrTag,
     3    BlockTags,
     4    Contract,
     5    type DataFormat,
     6    DEFAULT_RETURN_FORMAT,
     7    FMT_BYTES,
     8    FMT_NUMBER,
     9    type Transaction,
    10    Web3PluginBase,
    11  } from 'web3'
    12  import { TransactionFactory, type TxData } from 'web3-eth-accounts'
    13  import { estimateGas, formatTransaction } from 'web3-eth'
    14  import {
    15    gasPriceOracleABI,
    16    gasPriceOracleAddress,
    17  } from '@eth-optimism/contracts-ts'
    18  import { RLP } from '@ethereumjs/rlp'
    19  
    20  export class OptimismPlugin extends Web3PluginBase {
    21    public pluginNamespace = 'op'
    22  
    23    private _gasPriceOracleContract:
    24      | Contract<typeof gasPriceOracleABI>
    25      | undefined
    26  
    27    /**
    28     * Retrieves the current L2 base fee
    29     * @param {DataFormat} [returnFormat=DEFAULT_RETURN_FORMAT] - The web3.js format object that specifies how to format number and bytes values
    30     * @returns {Promise<bigint>} - The L2 base fee as a BigInt by default, but {returnFormat} determines type
    31     * @example
    32     * const baseFeeValue: bigint = await web3.op.getBaseFee();
    33     * @example
    34     * const numberFormat = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }
    35     * const baseFeeValue: number = await web3.op.getBaseFee(numberFormat);
    36     */
    37    public async getBaseFee<
    38      ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT
    39    >(returnFormat?: ReturnFormat) {
    40      return Web3.utils.format(
    41        { format: 'uint' },
    42        await this._getPriceOracleContractInstance().methods.baseFee().call(),
    43        returnFormat ?? DEFAULT_RETURN_FORMAT
    44      )
    45    }
    46  
    47    /**
    48     * Retrieves the decimals used in the scalar
    49     * @param {DataFormat} [returnFormat=DEFAULT_RETURN_FORMAT] - The web3.js format object that specifies how to format number and bytes values
    50     * @returns {Promise<Numbers>} - The number of decimals as a BigInt by default, but {returnFormat} determines type
    51     * @example
    52     * const decimalsValue: bigint = await web3.op.getDecimals();
    53     * @example
    54     * const numberFormat = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }
    55     * const decimalsValue: number = await web3.op.getDecimals(numberFormat);
    56     */
    57    public async getDecimals<
    58      ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT
    59    >(returnFormat?: ReturnFormat) {
    60      return Web3.utils.format(
    61        { format: 'uint' },
    62        await this._getPriceOracleContractInstance().methods.decimals().call(),
    63        returnFormat ?? DEFAULT_RETURN_FORMAT
    64      )
    65    }
    66  
    67    /**
    68     * Retrieves the current L2 gas price (base fee)
    69     * @param {DataFormat} [returnFormat=DEFAULT_RETURN_FORMAT] - The web3.js format object that specifies how to format number and bytes values
    70     * @returns {Promise<Numbers>} - The current L2 gas price as a BigInt by default, but {returnFormat} determines type
    71     * @example
    72     * const gasPriceValue: bigint = await web3.op.getGasPrice();
    73     * @example
    74     * const numberFormat = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }
    75     * const gasPriceValue: number = await web3.op.getGasPrice(numberFormat);
    76     */
    77    public async getGasPrice<
    78      ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT
    79    >(returnFormat?: ReturnFormat) {
    80      return Web3.utils.format(
    81        { format: 'uint' },
    82        await this._getPriceOracleContractInstance().methods.gasPrice().call(),
    83        returnFormat ?? DEFAULT_RETURN_FORMAT
    84      )
    85    }
    86  
    87    /**
    88     * Computes the L1 portion of the fee based on the size of the rlp encoded input
    89     * transaction, the current L1 base fee, and the various dynamic parameters
    90     * @param transaction - An unsigned web3.js {Transaction} object
    91     * @param {DataFormat} [returnFormat=DEFAULT_RETURN_FORMAT] - The web3.js format object that specifies how to format number and bytes values
    92     * @returns {Promise<Numbers>} - The fee as a BigInt by default, but {returnFormat} determines type
    93     * @example
    94     * const l1FeeValue: bigint = await getL1Fee(transaction);
    95     * @example
    96     * const numberFormat = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }
    97     * const l1FeeValue: number = await getL1Fee(transaction, numberFormat);
    98     */
    99    public async getL1Fee<
   100      ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT
   101    >(transaction: Transaction, returnFormat?: ReturnFormat) {
   102      return Web3.utils.format(
   103        { format: 'uint' },
   104        await this._getPriceOracleContractInstance()
   105          .methods.getL1Fee(this._serializeTransaction(transaction))
   106          .call(),
   107        returnFormat ?? DEFAULT_RETURN_FORMAT
   108      )
   109    }
   110  
   111    /**
   112     * Computes the amount of L1 gas used for {transaction}. Adds the overhead which
   113     * represents the per-transaction gas overhead of posting the {transaction} and state
   114     * roots to L1. Adds 68 bytes of padding to account for the fact that the input does
   115     * not have a signature.
   116     * @param transaction - An unsigned web3.js {Transaction} object
   117     * @param {DataFormat} [returnFormat=DEFAULT_RETURN_FORMAT] - The web3.js format object that specifies how to format number and bytes values
   118     * @returns {Promise<Numbers>} - The amount gas as a BigInt by default, but {returnFormat} determines type
   119     * @example
   120     * const gasUsedValue: bigint = await getL1GasUsed(transaction);
   121     * @example
   122     * const numberFormat = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }
   123     * const gasUsedValue: number = await getL1GasUsed(transaction, numberFormat);
   124     */
   125    public async getL1GasUsed<
   126      ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT
   127    >(transaction: Transaction, returnFormat?: ReturnFormat) {
   128      return Web3.utils.format(
   129        { format: 'uint' },
   130        await this._getPriceOracleContractInstance()
   131          .methods.getL1GasUsed(this._serializeTransaction(transaction))
   132          .call(),
   133        returnFormat ?? DEFAULT_RETURN_FORMAT
   134      )
   135    }
   136  
   137    /**
   138     * Retrieves the latest known L1 base fee
   139     * @param {DataFormat} [returnFormat=DEFAULT_RETURN_FORMAT] - The web3.js format object that specifies how to format number and bytes values
   140     * @returns {Promise<Numbers>} - The L1 base fee as a BigInt by default, but {returnFormat} determines type
   141     * @example
   142     * const baseFeeValue: bigint = await web3.op.getL1BaseFee();
   143     * @example
   144     * const numberFormat = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }
   145     * const baseFeeValue: number = await web3.op.getL1BaseFee(numberFormat);
   146     */
   147    public async getL1BaseFee<
   148      ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT
   149    >(returnFormat?: ReturnFormat) {
   150      return Web3.utils.format(
   151        { format: 'uint' },
   152        await this._getPriceOracleContractInstance().methods.l1BaseFee().call(),
   153        returnFormat ?? DEFAULT_RETURN_FORMAT
   154      )
   155    }
   156  
   157    /**
   158     * Retrieves the current fee overhead
   159     * @param {DataFormat} [returnFormat=DEFAULT_RETURN_FORMAT] - The web3.js format object that specifies how to format number and bytes values
   160     * @returns {Promise<Numbers>} - The current overhead fee as a BigInt by default, but {returnFormat} determines type
   161     * @example
   162     * const overheadValue: bigint = await web3.op.getOverhead();
   163     * @example
   164     * const numberFormat = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }
   165     * const overheadValue: number = await web3.op.getOverhead(numberFormat);
   166     */
   167    public async getOverhead<
   168      ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT
   169    >(returnFormat?: ReturnFormat) {
   170      return Web3.utils.format(
   171        { format: 'uint' },
   172        await this._getPriceOracleContractInstance().methods.overhead().call(),
   173        returnFormat ?? DEFAULT_RETURN_FORMAT
   174      )
   175    }
   176  
   177    /**
   178     * Retrieves the current fee scalar
   179     * @param {DataFormat} [returnFormat=DEFAULT_RETURN_FORMAT] - The web3.js format object that specifies how to format number and bytes values
   180     * @returns {Promise<Numbers>} - The current scalar fee as a BigInt by default, but {returnFormat} determines type
   181     * @example
   182     * const scalarValue: bigint = await web3.op.getScalar();
   183     * @example
   184     * const numberFormat = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }
   185     * const scalarValue: number = await web3.op.getScalar(numberFormat);
   186     */
   187    public async getScalar<
   188      ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT
   189    >(returnFormat?: ReturnFormat) {
   190      return Web3.utils.format(
   191        { format: 'uint' },
   192        await this._getPriceOracleContractInstance().methods.scalar().call(),
   193        returnFormat ?? DEFAULT_RETURN_FORMAT
   194      )
   195    }
   196  
   197    /**
   198     * Retrieves the full semver version of GasPriceOracle
   199     * @returns {Promise<string>} - The semver version
   200     * @example
   201     * const version = await web3.op.getVersion();
   202     */
   203    public async getVersion() {
   204      return this._getPriceOracleContractInstance().methods.version().call()
   205    }
   206  
   207    /**
   208     * Retrieves the amount of L2 gas estimated to execute {transaction}
   209     * @param transaction - An unsigned web3.js {Transaction} object
   210     * @param {{ blockNumber: BlockNumberOrTag, returnFormat: DataFormat }} [options={blockNumber: BlockTags.LATEST, returnFormat: DEFAULT_RETURN_FORMAT}] -
   211     * An options object specifying what block to use for gas estimates and the web3.js format object that specifies how to format number and bytes values
   212     * @returns {Promise<Numbers>} - The gas estimate as a BigInt by default, but {returnFormat} determines type
   213     * @example
   214     * const l2Fee: bigint = await getL2Fee(transaction);
   215     * @example
   216     * const numberFormat = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }
   217     * const l2Fee: number = await getL2Fee(transaction, numberFormat);
   218     */
   219    public async getL2Fee<
   220      ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT
   221    >(
   222      transaction: Transaction,
   223      options?: {
   224        blockNumber?: BlockNumberOrTag | undefined
   225        returnFormat?: ReturnFormat
   226      }
   227    ) {
   228      const [gasCost, gasPrice] = await Promise.all([
   229        estimateGas(
   230          this,
   231          transaction,
   232          options?.blockNumber ?? BlockTags.LATEST,
   233          DEFAULT_RETURN_FORMAT
   234        ),
   235        this.getGasPrice(),
   236      ])
   237  
   238      return Web3.utils.format(
   239        { format: 'uint' },
   240        gasCost * gasPrice,
   241        options?.returnFormat ?? DEFAULT_RETURN_FORMAT
   242      )
   243    }
   244  
   245    /**
   246     * Computes the total (L1 + L2) fee estimate to execute {transaction}
   247     * @param transaction - An unsigned web3.js {Transaction} object
   248     * @param {DataFormat} [returnFormat=DEFAULT_RETURN_FORMAT] - The web3.js format object that specifies how to format number and bytes values
   249     * @returns {Promise<Numbers>} - The estimated total fee as a BigInt by default, but {returnFormat} determines type
   250     * @example
   251     * const estimatedFees: bigint = await estimateFees(transaction);
   252     * @example
   253     * const numberFormat = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }
   254     * const estimatedFees: number = await estimateFees(transaction, numberFormat);
   255     */
   256    public async estimateFees<
   257      ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT
   258    >(transaction: Transaction, returnFormat?: ReturnFormat) {
   259      const [l1Fee, l2Fee] = await Promise.all([
   260        this.getL1Fee(transaction),
   261        this.getL2Fee(transaction),
   262      ])
   263  
   264      return Web3.utils.format(
   265        { format: 'uint' },
   266        l1Fee + l2Fee,
   267        returnFormat ?? DEFAULT_RETURN_FORMAT
   268      )
   269    }
   270  
   271    /**
   272     * Used to get the web3.js contract instance for gas price oracle contract
   273     * @returns {Contract<typeof gasPriceOracleABI>} - A web.js contract instance with an RPC provider inherited from root {web3} instance
   274     */
   275    private _getPriceOracleContractInstance() {
   276      if (this._gasPriceOracleContract === undefined) {
   277        this._gasPriceOracleContract = new Contract(
   278          gasPriceOracleABI,
   279          gasPriceOracleAddress[420]
   280        )
   281        // This plugin's Web3Context is overridden with main Web3 instance's context
   282        // when the plugin is registered. This overwrites the Contract instance's context
   283        this._gasPriceOracleContract.link(this)
   284      }
   285  
   286      return this._gasPriceOracleContract
   287    }
   288  
   289    /**
   290     * Returns the RLP encoded hex string for {transaction}
   291     * @param transaction - A web3.js {Transaction} object
   292     * @returns {string} - The RLP encoded hex string
   293     */
   294    private _serializeTransaction(transaction: Transaction) {
   295      const ethereumjsTransaction = TransactionFactory.fromTxData(
   296        formatTransaction(transaction, {
   297          number: FMT_NUMBER.HEX,
   298          bytes: FMT_BYTES.HEX,
   299        }) as TxData
   300      )
   301  
   302      return Web3.utils.bytesToHex(
   303        Web3.utils.uint8ArrayConcat(
   304          Web3.utils.hexToBytes(
   305            ethereumjsTransaction.type.toString(16).padStart(2, '0')
   306          ),
   307          // If <transaction> doesn't include a signature,
   308          // <ethereumjsTransaction.raw()> will autofill v, r, and s
   309          // with empty uint8Array. Because L1 fee calculation
   310          // is dependent on the number of bytes, we are removing
   311          // the zero values bytes
   312          RLP.encode(ethereumjsTransaction.raw().slice(0, -3))
   313        )
   314      )
   315    }
   316  }
   317  
   318  // Module Augmentation to add op namespace to root {web3} instance
   319  declare module 'web3' {
   320    interface Web3Context {
   321      op: OptimismPlugin
   322    }
   323  }