Skip to content

Commit

Permalink
Merge pull request #34 from CrocSwap/scroll-l1gas
Browse files Browse the repository at this point in the history
Add utils to estimate Scroll L1 gas
  • Loading branch information
Mister-Meeseeks authored Feb 27, 2024
2 parents 6bf3354 + 9672a54 commit d58b264
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 2 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@crocswap-libs/sdk",
"version": "0.2.89",
"version": "0.3.0",
"description": "🛠🐊🛠 An SDK for building applications on top of CrocSwap",
"author": "Ben Wolski <[email protected]>",
"repository": "https://github.com/CrocSwap/sdk.git",
Expand Down
1 change: 1 addition & 0 deletions src/abis/external/L1GasPriceOracle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const L1_GAS_PRICE_ORACLE_ABI = [{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"l1BaseFee","type":"uint256"}],"name":"L1BaseFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"overhead","type":"uint256"}],"name":"OverheadUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"scalar","type":"uint256"}],"name":"ScalarUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldWhitelist","type":"address"},{"indexed":false,"internalType":"address","name":"_newWhitelist","type":"address"}],"name":"UpdateWhitelist","type":"event"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"getL1Fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"getL1GasUsed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l1BaseFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"overhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"scalar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_l1BaseFee","type":"uint256"}],"name":"setL1BaseFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_overhead","type":"uint256"}],"name":"setOverhead","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_scalar","type":"uint256"}],"name":"setScalar","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newWhitelist","type":"address"}],"name":"updateWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"whitelist","outputs":[{"internalType":"contract IWhitelist","name":"","type":"address"}],"stateMutability":"view","type":"function"}] as const;
22 changes: 21 additions & 1 deletion src/swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BigNumber, ContractFunction } from "ethers";
import { TransactionResponse } from '@ethersproject/providers';
import { CrocContext } from './context';
import { CrocPoolView } from './pool';
import { decodeCrocPrice } from './utils';
import { decodeCrocPrice, getUnsignedRawTransaction } from './utils';
import { CrocEthView, CrocTokenView, sortBaseQuoteViews, TokenQty } from './tokens';
import { AddressZero } from '@ethersproject/constants';
import { CrocSurplusFlags, decodeSurplusFlag, encodeSurplusArg } from "./encoding/flags";
Expand Down Expand Up @@ -139,6 +139,26 @@ export class CrocSwapPlan {
return base.userCmd(HOT_PROXY_IDX, cmd, await this.buildTxArgs(surplusFlags, args.gasEst))
}

/**
* Utility function to generate a "signed" raw transaction for a swap, used for L1 gas estimation on L2's like Scroll.
* Extra 0xFF...F is appended to the unsigned raw transaction to simulate the signature and other missing fields.
*
* Note: This function is only intended for L1 gas estimation, and does not generate valid signed transactions.
*/
async getFauxRawTx (args: CrocSwapExecOpts = { }): Promise<`0x${string}`> {
const TIP = 0
const surplusFlags = this.maskSurplusArgs(args)

const unsignedTx = await (await this.context).dex.populateTransaction.swap
(this.baseToken.tokenAddr, this.quoteToken.tokenAddr, (await this.context).chain.poolIndex,
this.sellBase, this.qtyInBase, await this.qty, TIP,
await this.calcLimitPrice(), await this.calcSlipQty(), surplusFlags,
await this.buildTxArgs(surplusFlags))

// append 160 'f's to the end of the raw transaction to simulate the signature and other missing fields
return getUnsignedRawTransaction(unsignedTx) + "f".repeat(160) as `0x${string}`
}

async calcImpact(): Promise<CrocImpact> {
const TIP = 0
const limitPrice = this.sellBase ? MAX_SQRT_PRICE : MIN_SQRT_PRICE
Expand Down
66 changes: 66 additions & 0 deletions src/utils/gas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { BigNumber, Contract, PopulatedTransaction, Transaction, utils } from "ethers";
import { CrocEnv } from "../croc";
import { L1_GAS_PRICE_ORACLE_ABI } from "../abis/external/L1GasPriceOracle";

/**
* Compute the raw transaction data for a given transaction.
*
* ref: https://docs.ethers.org/v5/cookbook/transactions/#cookbook--compute-raw-transaction
*/
export function getRawTransaction(tx: Transaction) {
function addKey(accum: any, key: keyof Transaction) {
if (tx[key]) { accum[key] = tx[key]; }
return accum;
}

// Extract the relevant parts of the transaction and signature
const txFields = ["accessList","chainId","data","gasPrice","gasLimit","maxFeePerGas","maxPriorityFeePerGas","nonce","to","type","value"] as const;
const sigFields = ["v","r","s"] as const;

// Serialize the signed transaction
const raw = utils.serializeTransaction(txFields.reduce(addKey, { }), sigFields.reduce(addKey, { }));

// Double check things went well
if (utils.keccak256(raw) !== tx.hash) { throw new Error("serializing failed!"); }

return raw as `0x${string}`;
}

/**
* Compute the raw transaction data for a given transaction without the signature.
*
* ref: https://docs.ethers.org/v5/cookbook/transactions/#cookbook--compute-raw-transaction
*/
export function getUnsignedRawTransaction(tx: PopulatedTransaction) {
function addKey(accum: any, key: keyof PopulatedTransaction) {
if (tx[key]) { accum[key] = tx[key]; }
return accum;
}

// Extract the relevant parts of the transaction and signature
const txFields = ["accessList","chainId","data","gasPrice","gasLimit","maxFeePerGas","maxPriorityFeePerGas","nonce","to","type","value"] as const;

// Serialize the signed transaction
const raw = utils.serializeTransaction(txFields.reduce(addKey, { }));

return raw as `0x${string}`;
}

/**
* Estimates the additional L1 gas on Scroll for any data which is a RLP-encoded transaction with signature.
*/
export async function estimateScrollL1Gas(crocEnv: CrocEnv, rawTransaction: `0x${string}`): Promise<BigNumber> {
const crocContext = await crocEnv.context;
const chainId = crocContext.chain.chainId;
const isScroll = chainId === "0x82750" || chainId === "0x8274f";
if (!isScroll) {
return BigNumber.from(0);
}

const L1_GAS_PRICE_ORACLE_ADDRESS = "0x5300000000000000000000000000000000000002";
const l1GasPriceOracle = new Contract(L1_GAS_PRICE_ORACLE_ADDRESS, L1_GAS_PRICE_ORACLE_ABI, crocContext.provider);

// function getL1Fee(bytes memory _data) external view override returns (uint256);
const l1Gas = await l1GasPriceOracle.getL1Fee(rawTransaction) as BigNumber;
return l1Gas;
}
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./math";
export * from "./price";
export * from "./token";
export * from "./liquidity";
export * from "./gas";

0 comments on commit d58b264

Please sign in to comment.