diff --git a/package.json b/package.json index eabd229..867f09a 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,6 @@ "lodash": "^4.17.21", "markdown-table": "2.0.0", "sha1": "^1.1.1", - "viem": "^2.7.14" + "viem": "2.7.14" } } diff --git a/src/constants.ts b/src/constants.ts index 8502ee7..886482d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -5,14 +5,14 @@ export const TABLE_NAME_MARKDOWN = "markdown"; export const DEFAULT_CURRENCY = "USD"; export const DEFAULT_CURRENCY_DISPLAY_PRECISION = 2; export const DEFAULT_JSON_OUTPUT_FILE = "./gasReporterOutput.json"; -export const DEFAULT_GAS_PRICE_PRECISION = 7; +export const DEFAULT_GAS_PRICE_PRECISION = 5; export const DEFAULT_GET_BLOCK_API_ARGS = "action=eth_getBlockByNumber&tag=latest&boolean=false" export const DEFAULT_GAS_PRICE_API_ARGS = "action=eth_gasPrice" export const DEFAULT_API_KEY_ARGS = "&apikey=" export const DEFAULT_COINMARKET_BASE_URL = "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/" -export const DEFAULT_OPTIMISM_HARDFORK = "bedrock"; +export const DEFAULT_OPTIMISM_HARDFORK = "ecotone"; export const DEFAULT_ARBITRUM_HARDFORK = "arbOS11"; export const TOOLCHAIN_HARDHAT = "hardhat"; @@ -29,8 +29,54 @@ export const OPTIMISM_BEDROCK_DYNAMIC_OVERHEAD = 0.684; // These params are configured by node operators and may vary // Values are suggested default values from: // https://docs.optimism.io/builders/chain-operators/management/blobs -export const OPTIMISM_ECOTONE_BASE_FEE_SCALAR = 11000 -export const OPTIMISM_ECOTONE_BLOB_BASE_FEE_SCALAR = 1087000 +export const OPTIMISM_ECOTONE_BASE_FEE_SCALAR = 1368 +export const OPTIMISM_ECOTONE_BLOB_BASE_FEE_SCALAR = 810949 export const UNICODE_CIRCLE = "◯"; export const UNICODE_TRIANGLE = "△" + +export const OPTIMISM_GAS_ORACLE_ADDRESS = "0xb528d11cc114e026f138fe568744c6d45ce6da7a"; +export const OPTIMISM_GAS_ORACLE_ABI_PARTIAL = [ +{ + constant: true, + inputs: [], + name: "blobBaseFee", + outputs: [ + { + name: "", + type: "uint256", + }, + ], + payable: false, + stateMutability: "view", + type: "function", +}, +{ + constant: true, + inputs: [], + name: "baseFeeScalar", + outputs: [ + { + name: "", + type: "uint32", + }, + ], + payable: false, + stateMutability: "view", + type: "function", +}, +{ + constant: true, + inputs: [], + name: "blobBaseFeeScalar", + outputs: [ + { + name: "", + type: "uint32", + }, + ], + payable: false, + stateMutability: "view", + type: "function", +}]; + diff --git a/src/utils/gas.ts b/src/utils/gas.ts index 42dd093..b7fbef2 100644 --- a/src/utils/gas.ts +++ b/src/utils/gas.ts @@ -79,12 +79,12 @@ export function getOptimismBedrockL1Cost(txDataGas: number, baseFee: number): nu */ /** - * Gets compressed transaction calldata gas usage (an input into the cost function below) + * Gets transaction calldata gas usage (an input into the cost function below) * @param tx JSONRPC formatted getTransaction response * @returns */ export function getOptimismEcotoneL1Gas(tx: JsonRpcTx) { - return Math.floor(getSerializedTxDataGas(tx) / 16); + return getSerializedTxDataGas(tx); } /** @@ -93,15 +93,28 @@ export function getOptimismEcotoneL1Gas(tx: JsonRpcTx) { * @param baseFee * @param blobBaseFee * @returns + * + * Source: https://github.com/ethereum-optimism/optimism/blob/e57787ea7d0b9782cea5f32bcb92d0fdeb7bd870/ + + * packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L88-L92 + * + * DECIMALS = 6 + * + * function _getL1FeeEcotone(bytes memory _data) internal view returns (uint256) { + * uint256 l1GasUsed = _getCalldataGas(_data); + * uint256 scaledBaseFee = baseFeeScalar() * 16 * l1BaseFee(); + * uint256 scaledBlobBaseFee = blobBaseFeeScalar() * blobBaseFee(); + * uint256 fee = l1GasUsed * (scaledBaseFee + scaledBlobBaseFee); + * return fee / (16 * 10 ** DECIMALS); + * } */ export function getOptimismEcotoneL1Cost( - txCompressed: number, + txSerialized: number, baseFee: number, blobBaseFee: number ): number { const weightedBaseFee = 16 * OPTIMISM_ECOTONE_BASE_FEE_SCALAR * baseFee; const weightedBlobBaseFee = OPTIMISM_ECOTONE_BLOB_BASE_FEE_SCALAR * blobBaseFee; - return txCompressed * (weightedBaseFee + weightedBlobBaseFee); + return (txSerialized * (weightedBaseFee + weightedBlobBaseFee)) / 16000000; } // ========================== @@ -339,14 +352,13 @@ export function hexWeiToIntGwei(val: string): number { return hexToDecimal(val) / Math.pow(10, 9); } -export function normalizeTxType(_type: string) { +export function normalizeTxType(_type: string): ("legacy" | "eip1559" | "eip2930" | "eip4844") { switch(hexToDecimal(_type)) { case 0: return 'legacy'; - case 1: return 'eip2930;' + case 1: return 'eip2930'; case 2: return 'eip1559'; - - // This will error within viem.serializeTransaction - default: return _type; + case 3: return 'eip4844'; + default: return 'legacy'; } } diff --git a/src/utils/prices.ts b/src/utils/prices.ts index 27f19af..aa9d5b4 100644 --- a/src/utils/prices.ts +++ b/src/utils/prices.ts @@ -11,7 +11,6 @@ import { import { hexWeiToIntGwei } from "./gas"; import { getTokenForChain, getGasPriceUrlForChain, getBlockUrlForChain } from "./chains"; - /** * Fetches gas, base, & blob fee rates from etherscan as well as current market value of * network token in nation state currency specified by the options from coinmarketcap @@ -101,24 +100,64 @@ export async function setGasAndPriceRates(options: GasReporterOptions): Promise< } } - // blobBaseFee data: etherscan (or `getBlockAPI`) - if (options.L2 && !options.blobBaseFee) { - options.blobBaseFee = 0; - - // TODO: DENCUN - /* if (block === undefined) { - try { - block = await axiosInstance.get(blockUrl); - checkForEtherscanError(block.data.result); - } catch (error) { - options.blobBaseFee = 0; - warnings.push(warnBlobBaseFeeRemoteCallFailed(error, blockUrl)); - return; - } - } - options.baseFee = Math.round( - parseInt(block.data.result.blobBaseFeePerGas, 16) / Math.pow(10, 9) - );*/ + // blobBaseFee data: alchemy or infura call to Optimism's gas oracle on L2 + if ( + options.L2 === "optimism" && + options.optimismHardfork === "ecotone" && + !options.blobBaseFee + ) { + options.blobBaseFee = .1; + + // TODO: Check the GasOracle value against the eth_blobBaseFee value once + // it becomes available and then make a decision about how to + // fetch the data.... + // + // At the moment oracle fee comes back as `1`, which seems fake/wrong and + // produces numbers that are 10% too high. `.1` gets the + // calculations in the right ballpark. + + /* + import { OPTIMISM_GAS_ORACLE_ABI_PARTIAL, OPTIMISM_GAS_ORACLE_ADDRESS } from "../constants"; + import { createPublicClient, http } from "viem"; + import { optimism } from 'viem/chains' + import { AbiCoder, Interface } from "@ethersproject/abi"; + import { BytesLike } from "@ethersproject/bytes"; + + const iface = new Interface(OPTIMISM_GAS_ORACLE_ABI_PARTIAL); + const blobBaseFeeData = iface.encodeFunctionData("blobBaseFee()", []); + const baseFeeScalarData = iface.encodeFunctionData("baseFeeScalar()", []); + const blobBaseFeeScalarData = iface.encodeFunctionData("blobBaseFeeScalar()", []); + + // check that transport url exists.... + const client = createPublicClient({ + chain: optimism, + transport: http(process.env.ALCHEMY_OPTIMISM_URL) + }); + + const blobBaseFeeResponse = await client.call({ + data: blobBaseFeeData as hexString, + to: OPTIMISM_GAS_ORACLE_ADDRESS as hexString, + }) + + const baseFeeScalarResponse = await client.call({ + data: baseFeeScalarData as hexString, + to: OPTIMISM_GAS_ORACLE_ADDRESS as hexString, + }); + + const blobBaseFeeScalarResponse = await client.call({ + data: blobBaseFeeScalarData as hexString, + to: OPTIMISM_GAS_ORACLE_ADDRESS as hexString, + }); + + const abiCoder = new AbiCoder(); + const blobBaseFee = abiCoder.decode(["uint256"], blobBaseFeeResponse.data as BytesLike ); + const baseFeeScalar = abiCoder.decode(["uint32"], baseFeeScalarResponse.data as BytesLike ); + const blobBaseFeeScalar = abiCoder.decode(["uint32"], blobBaseFeeScalarResponse.data as BytesLike); + + console.log("blobBaseFee: " + blobBaseFee); + console.log("baseFeeScalar: " + baseFeeScalar); + console.log("blobBaseFeeScalar: " + blobBaseFeeScalar); + */ } return warnings; diff --git a/test/integration/options.e.ts b/test/integration/options.e.ts index 4c31bd1..d0331f4 100644 --- a/test/integration/options.e.ts +++ b/test/integration/options.e.ts @@ -50,6 +50,7 @@ describe("Options E", function () { after(() => execSync(`rm ${outputPath}`)); it("auto-configures options correctly", function () { + assert.equal(options.optimismHardfork, "ecotone"); assert.isDefined(options.gasPrice) assert.isBelow(options.gasPrice!, 1); diff --git a/test/unit/cases/optimism.ts b/test/unit/cases/optimism.ts index 4845980..4982021 100644 --- a/test/unit/cases/optimism.ts +++ b/test/unit/cases/optimism.ts @@ -83,8 +83,7 @@ export const cases = { "v":"0x0", "r":"0x6accd4e509882c1904bd09e809bc496b80289db28a23a03d4680516851bc0810", "s":"0x64f49f78b860dd7b1e2ebdfa0471b1a5dacbcd33ebb9482cf5fa08e8bc8e8ea2", - "yParity": - "0x0" + "yParity": "0x0" }, // Etherscan l1GasUsed: 47_980, @@ -93,6 +92,94 @@ export const cases = { l1BaseFee: 25.239617906, // actually gasPrice txFeeETH: 0.000830501226549222, }, - ecotoneFunction: {}, - ecotoneDeployment: {} -} \ No newline at end of file + // mainnet: txHash: 0x8625293b98e70070dc460189b4e840f98e2c5c69471b82e98e29716c87cfc041 + ecotoneFunction_1: { + tx: { + "blockHash":"0xbe94322d7c361ae828f27b060f8ab84bc97831216098385d3c2ea93c8ee5703d", + "blockNumber":"0x6ff87ae", + "from":"0x47d312c7604b1751f691011aed4ce32231bff4d1", + "gas":"0xd264", + "gasPrice":"0x7fa9bf", + "maxFeePerGas":"0xf4c2f2", + "maxPriorityFeePerGas":"0x8bd9d", + "hash":"0x8625293b98e70070dc460189b4e840f98e2c5c69471b82e98e29716c87cfc041", + "input":"0x095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000000000000000000000000000bf14adc", + "nonce":"0x586","to":"0x7f5c764cbc14f9669b88837ca1490cca17c31607", + "transactionIndex":"0x9", + "value":"0x0", + "type":"0x2", + "accessList":[], + "chainId":"0xa", + "v":"0x0", + "r":"0x819ddde0817e5b000c568d4046717c4b98904e96abbcd1fbc00f97503c7fbe56", + "s":"0x31c0c8436ed0b04cf5547b0067a40a7173bd5cf90691f1e638ac48e11807e15b", + "yParity":"0x0" + }, + // Etherscan + l1GasUsed: 2_344, + l2GasUsed: 53_067, + l2GasPrice: 0.008366527, + l1BlobBaseFee: .1, + l1BaseFee: 46.105691136, // actually gasPrice + txFeeETH: 0.000000591828628778, + }, + // mainnet: txHash: 0x41a05280362cdf2495f40a2fff9be20e9178890b627b2bb0461215e8678d8925 + ecotoneFunction_2: { + tx: { + "blockHash":"0xcf9ec494d82b138b2f78b977d8348693c21fb42f6b9f533ab5bf561ccff2c3f0", + "blockNumber":"0x6ff891e", + "from":"0x2827e0a180cca0706dbdd96f3ea260008d22f30c", + "gas":"0x81ee4b", + "gasPrice":"0x964077", + "maxFeePerGas":"0x119ead2", + "maxPriorityFeePerGas":"0x105fc4", + "hash":"0x41a05280362cdf2495f40a2fff9be20e9178890b627b2bb0461215e8678d8925", + "input":"0x82ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000001ae000000000000000000000000000000000000000000000000000000000000034c0000000000000000000000000087000a300de7200382b55d40045000000e5d60e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000019e482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000062000000000000000000000000000000000000000000000000000000000000008e00000000000000000000000000000000000000000000000000000000000000ba00000000000000000000000000000000000000000000000000000000000000e60000000000000000000000000000000000000000000000000000000000000108000000000000000000000000000000000000000000000000000000000000012a000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000016e00000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022439f87dfc0000000000000000000000000000000000000000000000000000000065b972490000000000000000000000001a0521a0970165f45355e85cff2e916555de211c20619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec0a8cb1dee1061a526d1dc6ad2e3d07afdc0c85a103542b2dbfa2f537997180fc19b10b9ee7db96eb34303442ed0c4dc9ccd02518f9585d6b65896b2b73f01a8d039dc63b964dd089a0b9a831c18b6491a3aacca536a1c05a69b1f57f3fd9c69f1ff029dd6b992fca7b71c9e0a521bcb16d08e3828e060cc89b3c72d19b1b4264103efcfd749452c7a00d3d4c12f9d5c4939f43466da1b67fcc279d9ef0a561bf2ae7582cf878c8b0be6f1863b6db126e999c1b58b2a00b19f9194f3eb81714662926df7d5e675295800879bd2ef6aa4dea296073f74cca251c2b6f0c42d76e76219dc41e832a640098fc4b00c2795e9b0aa90ee60689620738dcc7af3816d291098296d65d6d46279c65f8cb5e14b3910ecc5f8b6a8e04506fae22bf967b893e00000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000041284c50a01a2f6cd1b9cb38254ecd9bf4c5abf2fada49c1a2febca1aacb09d67e319341c71a1bceaf5f2816f6cfa3c9fd688b592fb5831797c792a9c1731fa9101b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d0000000000000000000000006f6364b9295cff74298971a3ca2c6a55758adaf820619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec292b994ea3f395512495b0dab2f0ff9c86c40e3824002b4b74bdc24567033c5b13b4dd5bf45efa8b1751d79e2bf4385d2bfbdc04b6cf6ead7cc92e4497b09ed523a1365a66821e99aa382cc39ec73b1a4306a037b88485823760780aeb415d5d24a704aba91f590e696a340763d6dc4a1eb369f91121a9e17fcbd9d45fe61e772c08d31873efaacfcdfee62dc8b3f454fe3d93bdb23445af9b8cd52b3be416b00ea6ab3db3fbcdd946ccc7c8b46e4f2c7634ec7bce24336e8734f44fd1840c650199ffc63e3d34332d91e04ab7771b31a8a1ba7f3e34d74fdab8ec8e873675ce066902c808f831d8808014ccffabe7794f1ca5e1028735f0d8c3d890530f09f8262591dbd65193957a070f3b73f66e5dcee52fe0fd2bff2c2b24ea6b31b9d605000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022439f87dfc0000000000000000000000000000000000000000000000000000000065da9df00000000000000000000000000031952f55f6ebb6ce44d71fcd92f6dcf4beae7220619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec1a1f53ab522271eac87626b8e6f25b82802c6f38dd353fd84706c35977580f880f6844ed931fd7ae69397c76be80e9b7f516566a381c39b21823cfb3fe7d0bc4127305969ae547cf0a90b3c5822f04c32333ab15d5aa5bad4f8a7b224bd9191f230279b9e135a0a8d0003a307caf4cd78f5c305a8b70e2337f128518e93a0bc8115dfda0c47568e4f336f9e6895ba7900984eb21082a817e39cc7aea500f71142f6c9fc2b8f4e4353a712d72506be28fd289b904819ee084f11b947353b70307117293f9e3ec42d4399b41ef58bc9492778979d11a1003fe9de5b0f2141f4ab30e0887961f2aacaeb98403ca0d9af008f108b805a31467c8ce264cb9f897b2ff305f99911101d49113cfb8d52ed9f66411cf98607348206d4adfd0056cbe579b00000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000041f0361f0405c3ab1cb6bde94e888e7b2f6e4da9d9c0d25fd8fa92510e8c82a9ff25f53ed5a45198b458e06fe357a9a245321f0629e9615e4bb41a4e0e98e046291c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022439f87dfc00000000000000000000000000000000000000000000000000000000655bbf00000000000000000000000000e0e3da5b35f8a338d4acd7dd839cafc6220bb92120619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec2c14c885fbfec1b9f2cc7a4a475d1859b13b8f1a26c7aac5a057d34d2ac9c534048a1b6dff3b7219d900c46858df93e6698dff1506c24a31e5ab18bae262ba81177b6fbb7d014be78e97b706e92ff911af87eed03e75bff3647d71825126540d0b1c4dba625e4b898de1d83b84f4f99fe6e626aa8eb5d9f60ed12a4ab79b02891e2d70a887731ba658d7f3fc9c47ccfb50ad3b03c98bc4022c383306d79cd79602fcc614d3c8a50726a58b5a7eb32405a57a38b2852037cee6adc61ee71bf9e9233505fc7f40c5fae14179cd8f4132370c784d52b16545d585571fadbec5393112fee5e66b52ab1731160bca7381caf54466d7305125aa0f61b9c9bbb5e2dd110d43b200f49daf5eef81f89d9cc82f25cc88661d3fa701d46ee45e270b9e83da00000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000041e27c3ef669464e1d2a619a1f61f4e3efc51fa53b47b4d673844df57a586fe5a934f4c317d1619e434d87f230519e853cc815dbece7e58aca670643521fa490e71c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022439f87dfc0000000000000000000000000000000000000000000000000000000065ca23aa0000000000000000000000001a0521a0970165f45355e85cff2e916555de211c20619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec1fa9ed79543cfde11b1763eb1ea08d9929bae64ea811994488132a21e0e7540c2ada4ff7523723c7ea76e652b06c99b2955896b3580182437028b4e6bb30233f13648c39708c33de57eee5b2220b50938467033a9cfbc1c161831636deff5f9929f38d82d338e68c59fb3fe75483ad0da8a205f35e22197fa35e30349dda1642210432c061f2cafb475db1f331cb21fcf5d5ee4c03d0ea7b72c520c7bfe9ab8f11bc66a989d94043d68f91274ea4258ddfada6f85087d23bc26a69d6f234fa4808bf55ca6f8a6620fd37158f24a44a5384cd17f5d8a7aefad835ab47da8a1f4909f20380f98f39af61867e5454bc58576c6e46d5268c7925abbb307c645f777e1676fc0759bb8811d67382dce2291ac48ad9750d93af347acfd2fff7becd9e0b00000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000004100fe6d6433fd1f342014007d2698e928fde1164ebf0d548c6ef9d1ed4451457c5a28b37f361d8088321e61730464423b433ad55912780d92b2401f59bc0b0c0b1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d0000000000000000000000001112de50be806b60c31efd7a09ee4cb6520c543f20619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec2c9b7ba7c57ff89182e58c6d5093fdd95f242227024a4ef9d5a2800f6b2f35e01c57790c11e4f71731f22fdd7c36a05d087a9d24e8c7a61dce4c44de5dd41a5c098bb265d3e5c6c42ea64b48d3d8e94695ab7e12ff40ff3f6109e9d1b51af97c26b763544d043c5cd09948a446d490d23deb3f0c35782dd830ef4d93e7a6f9fe17250cea5a0eac460d183894126f8179849a786401739f2cfb343abd9f0c4cf6199ef2cc4fd284d9d94de5f7201f39200eae3a51b5b296378567d2bf2d872fce20634c71cfce2cba455297100de23bdbf064f08702b0e660f00069f4584c9ba503845ca4bd827c6d26bf72d68915a0594b79130c0675e8123883469255313ca516aee89d3af4c89993e660c26f01367ab77a440dbeb6d5e4e5fa4eb4a62c7ae4000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000c4486674c6d71c6aeed9ab204b8008d9b09e70f120619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec30141e98ed2879d8d9c9fa5e354125de57a97334a371b66996a0dd03e1bd42412912aa592fe8057b2562ec1993654dbffe78404e96b3a3330fd0b945cb39aaca1bceda2d9d0fdeab2673d6abca11960267cd4b6795b8b2b0e55334232daac9a01c6a78a4da41f4cf2af07f81d6ce3b5a35e9144b11f0b03b418a7fce2d33504c01b24129b85a1612bfcf8d69f2027fa92d7e5eb3ef0f9a658c03d1abd10064d012aeea59be40fc97fe2ac9ff6840d4b5ea370a2620f01a890d728179d4e6e4380c860462bc670cb2fba6284abb7c00b86e70deb55bf98af5c3544291a724da5707bc9bbc7d3043930f59a98ed87c78adcf9c216f9f05a6bf47e7ce78a65062fe2683fb8abd4ebbd3b6fcdb4a12e36e793afbc3525df0155a9a11ba4510e3b92c000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d0000000000000000000000008c42a6077cc3b71fa042e52fdef3e7e18cdffa9d20619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec0a707df9db713f92c5eb564bccf8ecfb8dca2067cc63f0fb4043234aaf43593e2a3e71b2f9204a758f558fcf6c526528f0793b3cf060127b0b26884cc6a77623072e8396d59e7fb7c403182f23b1ff1b080b9b54787f5e227424844c2bb6fe2915aa0e31ceeb92109a5067e5288008ae2cd69304a143e6b9901d86e0211aae502e8c8280f143818cb48cda51acca5c8a896094885b498e4f6eacd456db0865ac06e4a3215a17e73eddbecdb5ef9d4e332bac2495156e88f66cd29d8d33f974b10bd0dc58949ec04ae6db2c4b83db456b848ac2cda87ddbdf67164a768f5f166401948500b8ec087e07456610b0b5b6894a8d11d83a5540f5e8854e5c3d6c3f9e1e567d3e2a19c14581fe833b17c025fb3214842eb648818ba84cabf7a1f8b13e000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000d9044ea132f5c1ef9d4c69301e38016508a1c01020619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec1b850a38b4adb6fe33b1a1466c44ae1bd902e2df7d7ac00e3824c3197d30cc26009bcaa65909041d1301488052fae768dd975701798f289dd6ed4380dd1f82492df3dfdb37c49fb525aecbae6d3e5c2685caeacb443cb2ac7da90000b93afdf6165b9b10810ed17ee97358c82227d50421a68bc2d432d3e0828b2c643d33bf9703694b50edfc698f0eb57c771de44d1573e66c762857703cc9c17c222b2ac7a116f7a5a65610d517a2f08c33e0745690b593098ee8c2a01a4ea50c17fe39e19026c277041eeccb3dff061b408db49a33d3013b2bb34149ad274fd5ae65618129218ae1618adfdb62d50a9a70b872d81c2003768dbc49b2d4e51f7b09c1ce58082102d919d2ef71422e6f11c66084dd7d33d9f1e33f6530dd00eebdaec7304c5e000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022439f87dfc0000000000000000000000000000000000000000000000000000000065711ad8000000000000000000000000e0e3da5b35f8a338d4acd7dd839cafc6220bb92120619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec07ae15b0f24c8cbed2ebca0993b709b81385737a3aa89c07099cc69a92946bb5027da9957e8368b38eed56af3c328b6bf501364b622fe58946643e5d82933110145a0a581380658edc270b0a443bd51cca29f07c51594c2f470a6b2150703c812f5fb75af4de7d15efc3b1f30b9fe5c214d8963a9d8dc09d4b647f555fbfd11a2ef8b8d4ca8bd8ad5a1e2ecfd1806f76d1d5694bbd99b1f0f497053f485bbeb915a4abef80cf1808f021cf7ba365f7296e43e9c9e79f755985a5b02c9c34c8501b73f489cb03c605f60e6de2b7b21538c5704da82a7e06984fb9271ff2ca1f1a29db10f2abe910dcef74f4c11dc7cf31c9e7ec8e45d2ac945b6a61413bca9d090893c0bde6fd4c4089c03eacf8320f39be5347838bcbe431f3b4513fbc4cbc0f00000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000041ddf9a950dcd49b448e9cce9f25c93e85527ae880a61f07d099b030134e17fa403ed503cb205addcfeecce6518fb3694d6d99623346c2f94f3281c59da108eff21b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000087000a300de7200382b55d40045000000e5d60e00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000194482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000062000000000000000000000000000000000000000000000000000000000000008e00000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d200000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000016e00000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022439f87dfc0000000000000000000000000000000000000000000000000000000065cbfffb000000000000000000000000ec3fd63436979afc3a30fc5afe14926534f7fcab20619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec07defe0faf8c7604f92664853de3810edc6f5a08a0b6e8c34d88d16613cf446d2af8ff4b620a17cf4a62c7704b779f8392e863849d4d7a7bc9820c7f914f7a1a285c869ab6e266ba4fe50344bcc4d2609dbb6509ee8de575f73a441fb7fde33e236b769a278d88409edbf02cabec2d6503bd54fc37b358808ff5c84fd76f188d2a3bed0bb9ee016943f68d4da9c5b17473e530f49029f1fa79679f0bed6997b32dde69832156976c96646da458aa98534cef6fc41a54ac8364ef41e7725465fa1f051d0e071e577a7d794dafab10ab5f2a5a9f584ec0855efb724c2a5fe570420616ef7c647e9031a47443293aee1fd96368d713c76275ced2e3a29510a7b22f1028f8caff269439d69c6843d71474e96b1eaca777c70387740ffd688a773c2e00000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000041c390c47885ef60b2e5390cf28da81663dceb2197d086f0beb791040090f94b8b3b998ae77beaa959e0b0ad2dd06de84a395bbcf38fd68a888fa202c31efc6a631c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d0000000000000000000000002959ba5927b5000819f35da2db98230e4164fa4a20619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec07c1c8dab4a0b512b3bd9e8ffaa239121d47d3011079f7c2e05cade2c33153870d891b427f9d068ec0bbe329b10b7799afde16eecd4ef17143742c01e9f6a0fe2bbdeea80c2158ac014785dca9620beabe18d0d4e2b73c84008f75055edde7630149fb70ebd1c9596366af12dfedcb33d71cd17bc34eaa3fcfcf957990e61bec1a7c0cc8acb2412ef581f7aee1816b4de05ee2ecefa144464f96222250c4b3db1e9ef8ded3fa70d2505abaf4f49be69940e5e445e819b9348910a03e25debf0d300f50ddd893ff59a4f644bf56e04f4ea5ef7ec47479101f47c3eaa01e28a29722fcede7ee3720ba6267b8eeab4f952dea452913322b500cabffa1518fd29e1d0338f8bc45f8a9bb42ff24e453f23f4a53e59d88bb47e00c906062b6ceb65c2e000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022439f87dfc00000000000000000000000000000000000000000000000000000000655f84b50000000000000000000000002350d4a976b30c346bcb22c57bdef7f5dd1c73c820619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec29c2aecff9c0259a4da4b94687a7b0e7af3e7f5cc6aa4d673755cf15d5c7e93d25fc1d298c4f85d36af1dce8e999ade16c069774622061e13081c9508eb7b12a2d415d681126d6475db2799000e34e88452e14fe298c70ffab5e7ce6dfff567906f3b9473b426f9dd5cedb9a08b2e1165428ede1b6c78a4e6ff3f584df2f84712e2266943cf6e403f57345041d2f953911ade83da37f9308315cb221ef9102d32e3843a033dc646f26034e95b58e3f78a193cae851f181b8db9555add51ed2301110ac9f734dab5dda146838b8ba010c03b343aa4ce8accfdc991a9c344770e5217db629981e9fe7ecf92ebeef92cbedcb04c77f72780053b44598e9c17794872cfe032ae827c97a0d340f80a77ab63284066b3f708bf1e1d41b5a2ab06d840b00000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000041f0cbe02c04390cb99e167e5617066b7174ea35d7c1ae1a88aa2026f1aa537e4d3a0d95fda329805903decfc6c8f12a50b77ea1ea98bae45a5208ce4a5b1f6a2b1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d00000000000000000000000011766531fa9d15e565bdf95737eb647aaf873d1820619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec0bd900ef2c6db28cb05a44d38e20a40b66d3466cbdbd7ec687c3f957beef4619159d155fe9fd69289636af453b61c0c12f575f09509a98582fbd5aece402e9961c4ff8870235897db1d85eea92fabaa8165dcfd7a10c5469186f04301d8c7dea15d0a0fe2c0d40ffbda7df5f610c83889899b8d5ab3bdca2465b5cdbf95ad8b319fda3c2f04b2166785c9d5c315872ebf1c920a1009edfec1c140f804e7da5b31a4467a74adf40a799f4b32f42d82e2617ad44ac6b42f42acdc6ae65f1c802e408cf4da964f88ba686937df1b084261ba48423ff3c9ed277042bab5f586ed9681d1909b88638aab61d5db519fa571affb9387e07920be1d11af2b4801cec798105409e508a10bad32db342174e7efc7616a40e44bad70a6537cccd72ae3d37f7000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000525a99247949d35355ef66ef198ec14624765de620619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec20e10bb146153f8dbbab2cf0bce97c66d3ebe7be2dc38b55647c8cf6bfba2f6029e841956e9712459eabb4350a30f8e4dfbd2ec62ba01c84aef758595d1d9bd31dd4940c7a918d9c15147c110c2a7997c8f5b00f1c3d8ac3d85caec587b686b000210dd73b1c678188d717e14d72db80cbfe21b507c881ddd50d66d96de339c52f1f541a37c6d65e631d73dfdb73ed1ea0542e678c8d99f5fbe11fe9b944e3bf2ce588f53b6ce66462a03af55211301c9e9ed3b0c6a600c45d6a9346aa02ae92264ea64b22080b17edac07c021df75d9d4f4796433f1addbed24c2b3458624310f3976efb439a67c11004d7086aa554581444549af2793ce66e5008f5522c9a82241b495a3f6d8e477b903969d4e43c4a431c5904cf9ed6dd1abf01c0661de49000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d0000000000000000000000000e834913f14c924238ee21f2cd06fceb569a5e3920619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec27b094971b3e7b3502a97b0a5d82ab2532ba9f0f3438d46eed48870322c7cd6e04d92a5915a8c70740f6829a28f2a19de67c5045319cefd13b444385ed6e4654069480647633dda74f116f8e191a1e6b39f4cfeef8f330a89ae795f795e8e605247f33941f64d99bfe6a3540224eadf427fabfa45be00cfec2520074a173495c02c124abdb2d289f300fa3435e16991881d3af20087370b597e28daedc34499a0dc01713b2f36d1eb4197c77a274f58ff37c15e6d524c5489e4a3aa7a403ede306215c44376db89a4c18d5b3feb91ec056ce192c88617607e262e464b355a7ed1b08229b8937101610f3d0d7b13fb36059e72dde2b01535b2e517c27e617c7e627dcb4481db75c132f9c2be0979306854c393a1c0e12467a8465681c9192c22d000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022439f87dfc0000000000000000000000000000000000000000000000000000000065d94398000000000000000000000000aacfd04638d35c35da7cd90f650a2116876365d820619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec133f6cbb6f3cc22c35c9cf5fd3304d5c09f4b1dd35d736edde822adaa61074bd0f70e1cdad67d1ab282e0e2114aea735503fd05472507467f05d955f09b998bf0348d7f2b444b4ddcde6f47bb280192409142a2a5e8df674a15e4f6c9c2c732a19c50e90d3b57beaf67fa2b3b8423f9fd93f18306c5f0961597ebbcde741d0650d433bbb428fd60aa1a81cca29f9dae5bc357b63d353808e260d89b17595ce3719181a41eb2d97839370a80e5ec277d3c680e049d6dcf6aa87c7ccafd295746719686cc1c680701e0d5fde50530ba0b22fcafb63807ab5886a0185ae8ecf810a011e992f05b85f0f04e3bc0ba2246bc0f5a007fb42d44538287f4364fd245869131bf5a9e88f3296628f16d1c273149b0133cd83a9f74b7cb80b0cfcac56cf9c00000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000041cf764727e1384321c66ba6098ec9c7e566eb168ddf2ff8ca06bfdd64468b23f071dd9685c0a552bf3f05cfb0c2b0b66853af374de90ae1026587cfb38561ec781c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022439f87dfc0000000000000000000000000000000000000000000000000000000065c78cc6000000000000000000000000b2dc12d18825efb268a7da40e7163f14669426b520619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec212788b59074f49d832625269287f5f0e6781058ac7c41f92363316cd98a205220780f0e0bc69b5b949ccf67a28bef980d3ec7d183d2b56f313720c3fe13294d112a4aa170336e07a7019cbac922a4e1c36ec9e7ae916fa36f9a21adeafdb3e524d880e15dca1ab60605735fc45da8f0d2e9ef4b54292f1c8a35e81df491216f0e4637c737d390e95f98d0285505509ad383f7283120413c12ecbd3ddd08388c0e39dfa9a0f7c6e35519b27c5acf21e7df131f066c15b8eb347b155affcfde08138fa693e4d6146b653b749c4bba780bf94c8482c2d4131f9085372ddf3e83fb037cb013962bfcbd92d9fe0c800c0efbbfc09125fca657edf4cbcc520b6ba0610fa6c88d10a83a0031277891033b0fb0ec441a29b64122856db829b72baaf00a00000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000412db7ccf0f84d4482b318444de6eb95f8a9cccb86345dd232f9874bbd478875325303092df9048b49c47fb9b30890ccfbb04a167e261f78c46988a78a167ac7471b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000d50505c8536df45f0ea46f06d4fc76cf0175eed320619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec0a26b287d4bc0820d3975fbd4ae6c91156eef3f3204ddc63f028de59b0912c09193433b59c97b3e272e1db0df405177043280cc7b88bff659b2075a4b62605e809b1026900f12462324e9751c4120cfcff591b511051a9ae63810eef2b72a8e20e24bf63b8b52873f160cac5cc9e4da9ffa3a9e35bb227b11a02550b2e8c246606a639636cf96218e1f8ea75434a073b7ac3ffe7ba2d497e9f28c9c71510286729535665e363eaf7f7de3dd952ef059811996517631b57ae9a6689a06b7ef4e90154354616660f010bd5b61ba09d1a6b7af5631b3725a7a58ad9ef5a582c54320dbfaa597f482532008c6b0215eaa7ebf5591cde155c40c583c40d08472f85f50f428e7876d5b241a10f5510c5af6c26bb317543b220573f9605c8385e9cdaa9000000000000000000000000000000000000000000000000000000000000000000000000000000007b46ffbc976db2f94c3b3cdd9ebbe4ab50e3d77d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184a41e6ceb000000000000000000000000000000000000000000000000000000000000001d0000000000000000000000003a9d72e15d1731cf1aa212352fa5db5a77df628e20619e25408bbaabc62f2de402722b9326ad30c088708db3ec2b0f6ad629b2ec2ada14cfae6c9fb68e09f5f8cceb07d22f3a57b9e144fef3b95db07e7cf84e7d2cc93c7d06cb8fd36c4add133e245c24ef42f33185d406ee71ef9eedcb4870cd20fd4a5a89abcd246ec57fd60818f764dee1ad8dc2f6d8f4ff181af03195e39903a1b0fcc79c9eb68953193af17e4255d0e9c1bc9a912943d2bf1e3e2a5793632de0b776591cbf334900191a7ec6a6cc9531d2328fa9e9a664921b8896c5a68e0dfdff15ad29c6b2ff8740e99f04a69071e001bd713dc458d99bbd2c09aeacc609c3efb6147baedf7e60c30e56867be508ca9f7a3417174576666618750901bc0ad2324bd133ed4c386952f5208dcd58dc0e8880cd13b0231add47944b3324b200485167029eda3cbb1570888d14d3a65143c756afba9ff366a3c3dae8e418950000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000561d8419c30093009fe70082e10a00007b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003050fb8acc3d4bb6c7b402278fd95f584624e67b73cf8d6e10201000000000000000000000842000000000000b1b93f68538a6e09e9214ec9d28af6f21eda4823538cc6820b5d645c45fd40b35b275c1dd148cf670094bf48236daba398f0ba6e5f80210eea812462c3e71b6d8089454b2f2a27de8b04c7ed96ce3e6f79486aec2335191f356f00010000000029a2674d37dbb726000000000000000001a1a00004b5d6f8df27285ce0701a0049c1c81143f2749e825c0484f547d1473d23f08caf5e45495fae7e4a1e0ef0cdefd55f9bc39f92eeb2c1c5660929173f1025f328688d688891fa6564b18144e9fc8dbd73c7ab2781000600000000334fe25c5730d5be00000000000000000200d933a7c977c8f85521093c42e6c394268ced01b13258863c4561692c30238425fbdffeba449437884f98391f5c8343f7cba809a3655133a7b72f89e4378346e324849da1a6ae639e0893db8696d717fbe5ed1bc97d63000600000000fd9069965237ac8f000000000000000009e63517afed6aa3bb83d4e2e2da1f203adcb661d230daaa3d7bb8972cdddeab38dd07d227da8ac98e0fc1c6dbcc31ce296da864eaf1f2a68af8c46417a9e10c618b0a1cc21305680f714c8a027b3ca257be9b099dcff8b0000b000000001127efdd8315c64c000000000000000000ab5c8f47120214447707c289305b5658b54f4f118161bc4818f34226a969151b94ef4ea6cfc5ec4f1ca4152850036a2c677417ddf15178b7901a831ad2fe9f2550e1c037c92072b4fefe3bee30f0cd7a73f8fb1130f74c050b000000000000000000989a680000000000081770761440780e2c71ec9cf8a62f1081ee6af243d687f1a4dd40bbcd3757e3029d6d018c1f4d79924899210dbfff05e451d15f768558e24a135ee3ad7bad7e6c6a133ae741e077d912583d6eeb4ce810451df07d3e3b49af9311030e00000000000000000046d9150000000000000000000017a7d16f8a1f990b802694a1e92980368f455efb1839c38a912d3ffe1be0ad60a3be7a64d72202de02c2bb8726a24600e6d3ed575cc4b0d0848627af263dd363b9c0000000000000000000000000000000000000000000000000000000", + "nonce":"0x5e5ed", + "to":"0x087000a300de7200382b55d40045000000e5d60e", + "transactionIndex":"0xc", + "value":"0x0", + "type":"0x2", + "accessList":[], + "chainId":"0xa", + "v":"0x1", + "r":"0xefe4519d55ff08799694e7203da12de33ca6df51229353acfca62c72d9957d2", + "s":"0x55c287356459ce98e5ae22219a016f0f50d28606de48d864803e73d43e24f53e", + "yParity":"0x1" + }, + // Etherscan + l1GasUsed: 164_864, + l2GasUsed: 7_001_681, + l2GasPrice: 0.009846903, + l1BaseFee: 43.842615066, // actually gasPrice + l1BlobBaseFee: .1, + txFeeETH: 0.000078832871894148, + }, + ecotoneFunction_3: { + tx: { + "blockHash":"0x373d89d1edcbfcd0e482511b42911bfde54eec50adc6604d1b012dd30130370c","blockNumber":"0x6ffb991","from":"0x0e118783aec093d97a4b7b4bb6023e0fd2e2dfeb", + "gas":"0x989680", + "gasPrice": + "0x7a308f9", + "maxFeePerGas":"0x2faf0800", + "maxPriorityFeePerGas":"0x42b3663","hash":"0xe15f385b91dc6d22b86510a61a6a00a2d0e9ac5e92c15a0c0647c663191a918d","input":"0xdfa723cc000000000000000000000000eaaa8ba17e546063b90e3f00d66eb870713614d900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004e3504e41550100000003b801000000030d02197dfe4fb80b9d0ebaaf37e258190cd6d036c17b941f195bd712eafdc551513f2a519246cdecf7ef56427cdec76f64fbb78d4a67770e26c58b90d967f946bdce010341be5924cc2f233e9d7b81b321916ea4b55cbcd3a8280b3f8e9277dc38b079e610dc1f75af35f3f1ef3e92d856414e4d9a3b93497e431a6bb2c821f9d315eef600043ebc719a36c80b43578fb032071dfb05ad4b00eb7f7573d6b7e9bad12dd629de656acfebc0f62a276a7ae08f25f7ccb7dc1a6af0a27ccb6ce934a178289dd7b701062eb0bcdd6c117150a084118e9227fc613a5906877f8ed6847ea97c2e3efe54fe04964dbb54b3b30add4a98b2ef69664d0bcb0f75ea004c9c28ed14a098c8640f01076489c71040401c7372c58de00935da282296234e3d6f17bc4aebddab7b0cbb824941b9a6c46833b97bcef816ff9136b514c920a6bff811d957652c9f8a57f10c0108f44ab3bd031b5aaa1599fb76aa30e12555e9fc0a116e0b3655a2b154d80833dd17635dc8597903f6d3e95ee6ba86d89dd13b2095e03e611c6c8b9da18ee55883010a58350b8c6d9c07bb8b0bf3ca958ce0b35938ca30b224f058815fa7325ebffe097813b5e6bba75ed4578901dc55ebfdf1c32f525a65975935b2d040cf91aec3e0010bc1b090cebd14a8ae46796071645fbc4f5a919c2e4e8e0cbc9e6fdd8f7264158260e5c251cd9d4ac80d379f9d390fd7762a0919a453859191f51f340f463d941a000cd67df0be41230e4e3006777405b8f42730f187bf40657f1f3281f52c0acb394140330265fe1887f600210643976202d66fa269fe30016608e38692048493b9bc010d6a07b112114b53162863a9580a87a3ae4e1df0b32986b56e781e579f78a0117b4b255d7c92adb2e55fa554759337a5faf11ae690585b3a52fa9953b60237b8cf000e12fbedcdd673fabaf2eedf934a78b5dd41f0ee6cc402448a860b7515027783906b924a7d443f4800106ffde20457e1abc639a7922809fccd9f0b6500bacca35a0110ae275291f378622a4b2948ddb3aa315ee12f71730815823770e5b7aa4cd3f60e1c453998de32aeee5d1f562d25619aa65f2171c01f1afe3424910bb452fc8025001200fac5f0ec2d42ecfbc1ecfb590e3b75ae902b13ac50b60b6f1b246efbaa4f464ee6448a00c25b48d3e32fdaf14770c1d4c13760580b24f846b22ecc326ba5e80065f34cd800000000001ae101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa710000000002b07c420141555756000000000007b98530000027105ae83475165c3c80e9f1f642a333f2ee11e89daa01005500d6835ad1f773de4a378115eb6824bd0c0e42d84d1c84d9750e853fb6b6c7794a000000003a9028dd000000000011af23fffffff80000000065f34cd80000000065f34cd7000000003afb2fe00000000000114c7d0a6a1c7371dc184427b8297410f22819be7b8dd7ee3783de7cc01ea2b65d268138d2ee222f9823f9f4d533f464d0947bae1b7e1c044b3af3af430e8b3ee2bb8b364074928a0c45880c383386b35fd572d1c66566330bd11373575617a1ee5345551daee6c619e44a3972f0ff321924e7ca0e6192186b9f2c65d9e7d3ee84eb6c27d2f0bdee1166a0330def2fd1027ce2222d9a44338b7fb326c33a6c7a656082da3dbf11458818b7e949ddadac5ce07f8d48d8fe35e1da43f01ff18524afad8050e3ea7942cfd6148b0000000000000000000000000000000000000000000000000000000000", + "nonce":"0x5112", + "to":"0x77da808032dcdd48077fa7c57afbf088713e09ad", + "transactionIndex":"0x1", + "value":"0x1", + "type":"0x2", + "accessList":[], + "chainId":"0xa", + "v":"0x1", + "r":"0x683f27529de90aa206c70d0adb557f353fb1a8dae405a3771230828b9fab1ebc","s":"0x6304acd47f049f14978b6ff45978505e91f24988d17fb5723587e904eebe25b4", + "yParity":"0x1" + }, + // Etherscan + l1GasUsed: 22_208, + l2GasUsed: 1_025_865, + l2GasPrice: 0.128125177, + l1BaseFee: 68.251814695, // actually gasPrice + l1BlobBaseFee: .1, + txFeeETH: 0.000133512661963651, + } +} diff --git a/test/unit/gas.ts b/test/unit/gas.ts index b09510b..73498e2 100644 --- a/test/unit/gas.ts +++ b/test/unit/gas.ts @@ -53,7 +53,6 @@ describe("EVM L1: gasToCost", function() { describe("Optimism: getCalldataCostForNetwork", function () { const options: GasReporterOptions = { L2: "optimism", - optimismHardfork: "bedrock", tokenPrice: "1", currencyDisplayPrecision: 8, } @@ -62,6 +61,7 @@ describe("Optimism: getCalldataCostForNetwork", function () { const fn = optimismCases.bedrockFunction_1; options.gasPrice = fn.l2GasPrice; options.baseFee = fn.l1BaseFee; + options.optimismHardfork = "bedrock"; const gas = getCalldataGasForNetwork(options, fn.tx); const cost = gasToCost(fn.l2GasUsed, gas, options); @@ -75,6 +75,7 @@ describe("Optimism: getCalldataCostForNetwork", function () { const fn = optimismCases.bedrockFunction_2; options.gasPrice = fn.l2GasPrice; options.baseFee = fn.l1BaseFee; + options.optimismHardfork = "bedrock"; const gas = getCalldataGasForNetwork(options, fn.tx); const cost = gasToCost(fn.l2GasUsed, gas, options); @@ -88,6 +89,7 @@ describe("Optimism: getCalldataCostForNetwork", function () { const fn = optimismCases.bedrockDeployment; options.gasPrice = fn.l2GasPrice; options.baseFee = fn.l1BaseFee; + options.optimismHardfork = "bedrock"; const gas = getCalldataGasForNetwork(options, fn.tx); const cost = gasToCost(fn.l2GasUsed, gas, options); @@ -96,4 +98,48 @@ describe("Optimism: getCalldataCostForNetwork", function () { // Actual < 0.06% assert(diff < .01); }); + it("calculates gas cost for small function call tx (ecotone)", function () { + const fn = optimismCases.ecotoneFunction_1; + options.gasPrice = fn.l2GasPrice; + options.baseFee = fn.l1BaseFee; + options.blobBaseFee = fn.l1BlobBaseFee; + options.optimismHardfork = "ecotone"; + + const gas = getCalldataGasForNetwork(options, fn.tx); + const cost = gasToCost(fn.l2GasUsed, gas, options); + const diff = getPercentDiff(parseFloat(cost), fn.txFeeETH); + + // actual 0.013 + assert(diff < .015); + }); + + it("calculates gas cost for large function call tx (ecotone) (I)", function () { + const fn = optimismCases.ecotoneFunction_2; + options.gasPrice = fn.l2GasPrice; + options.baseFee = fn.l1BaseFee; + options.blobBaseFee = fn.l1BlobBaseFee; + options.optimismHardfork = "ecotone"; + + const gas = getCalldataGasForNetwork(options, fn.tx);; + const cost = gasToCost(fn.l2GasUsed, gas, options); + const diff = getPercentDiff(parseFloat(cost), fn.txFeeETH); + + // actual 0.0105 + assert(diff < .015); + }); + + it("calculates gas cost for large function call tx (ecotone) (II)", function () { + const fn = optimismCases.ecotoneFunction_3; + options.gasPrice = fn.l2GasPrice; + options.baseFee = fn.l1BaseFee; + options.blobBaseFee = fn.l1BlobBaseFee; + options.optimismHardfork = "ecotone"; + + const gas = getCalldataGasForNetwork(options, fn.tx); + const cost = gasToCost(fn.l2GasUsed, gas, options); + const diff = getPercentDiff(parseFloat(cost), fn.txFeeETH); + + // actual 0.0008 + assert(diff < .015); + }); }); diff --git a/test/unit/prices.ts b/test/unit/prices.ts index 4c3cedf..e41d392 100644 --- a/test/unit/prices.ts +++ b/test/unit/prices.ts @@ -6,7 +6,6 @@ import { import { getDefaultOptions } from "../../src/lib/options"; import { GasReporterOptions } from "../types"; - describe("setGasAndPriceRates", function(){ let options: GasReporterOptions; @@ -94,11 +93,12 @@ describe("setGasAndPriceRates", function(){ assert.typeOf(options.baseFee, "number"); }); - it ("when tokenPrice, gasPrice and baseFee are set but blobBaseFee is not set", async function(){ + it("when tokenPrice, gasPrice and baseFee are set but blobBaseFee is not set", async function(){ options.tokenPrice = "1"; options.gasPrice = 1; options.baseFee = 1; options.L2 = 'optimism'; + options.optimismHardfork = "ecotone"; options.coinmarketcap = process.env.CMC_API_KEY; options.L2Etherscan = process.env.OPTIMISTIC_API_KEY; diff --git a/yarn.lock b/yarn.lock index 969a50c..3da2d90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4128,7 +4128,7 @@ uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" -viem@^2.7.14: +viem@2.7.14: version "2.7.14" resolved "https://registry.yarnpkg.com/viem/-/viem-2.7.14.tgz#347d316cb5400f0b896b2205b1bc8073aa5e27e0" dependencies: