Coder Social home page Coder Social logo

hardhat-marmite's Introduction

๐Ÿฅ˜ Marmite

version npm license stars

Hassle-free Hardhat plugin to compare gas cost among different Solidity code snippets.

Demo

๐Ÿงฉ Features

  • ๐Ÿ“Š Compare code snippets directly in your contracts
  • โœ… Compatible with any Solidity versions
  • ๐Ÿ” Checks function calls and contract deployments
  • ๐Ÿ’ฏ Accurate gas cost metrics using code preprocessing
  • ๐Ÿงฐ Supports single contracts and complex scripts

๐Ÿ“ฆ Installation

First thing to do is to install the plugin in your Hardhat project:

# Using yarn
yarn add @primitivefi/hardhat-marmite

# Or using npm
npm i @primitivefi/hardhat-marmite

Next step is simply to include the plugin into your hardhat.config.js or hardhat.config.ts file:

// Using JavaScript
require('@primitivefi/hardhat-marmite');

// Using ES6 or TypeScript
import '@primitivefi/hardhat-marmite';

โ›ฝ๏ธ Usage

Marmite offers 2 different tasks that you can use to compare gas costs:

  • golf:contract for standalone contracts and simple function calls
  • golf:script for all the more complex scenarios

In both cases, the first thing to do is always to write the different snippets that you want to compare, these are called the implementations.

โœ๏ธ Solidity implementations

Declaring an implementation is a piece of cake, just put your Solidity code within the tags @start<Name-of-your-implementation> and @end.

Let's say that you want to know if it's cheaper to check if a variable is "different from 0" or "higher than 0", simply write inside of your contract:

// SPDX-License-Identifier: WTFPL
pragma solidity 0.8.9;

contract Foo {
    uint256 public bar;

    function set(uint256 newBar) external {
        // Declaring our first implementation
        @start<Different-from>
        if (newBar != 0) {
            bar = newBar;
        }
        @end

        // Declaring our second implementation
        @start:<Greater-than>
        if (newBar > 0) {
            bar = newBar;
        }
        @end
    }
}

Marmite will know that it has to compare the gas cost of Different-from and Greater-than. Under the hood, this contract will be compiled 2 times, using each time the code of a different implementation.

๐Ÿ“ƒ golf:contract

Standalone contracts and unique function calls can be quickly gas golf-ed using this task:

npx hardhat golf:contract --contract Foo --func set --params 42

golf:contract

This will tell Marmite to deploy the contract Foo and call the function set with the parameter 42. Note that since no implementation names were specified, Marmite will naively measure all of them.

Here are all the options of the golf:contract task:

  • --contract (mandatory): Name of the contract to deploy, e.g. Foo
  • --ctorParams (optional): Parameters to pass to the constructor during the contract deployment, separated by a comma, e.g. 42,true
  • --func (mandatory): Name of the function to call, e.g. set
  • --params (optional): Parameters to pass with the function call, separated by a comma, e.g. 42,true
  • --impls (optional): Name of the implementations to compare, separated by a comma, note that if this parameter is missing, all the found implementations will be compared, e.g. Different-from,Greater-than

๐Ÿ“ฝ golf:script

Some more complex scenarios require a script to deploy the contracts or to set up a particular environment, this task is especially made to tackle these cases.

Scripts are very similar to the usual Hardhat tasks, scripts or fixtures, but the only difference is that all the logic must happen inside the Marmite context function:

import hre from 'hardhat';
import marmite from '@primitivefi/hardhat-marmite';

async function main() {
  await marmite(hre, async (flag) => {
    const Foo = await hre.ethers.getContractFactory('Foo');
    const foo = await Foo.deploy();

    const tx = await foo.set(42);
    await flag('set function', tx);
  });
}

main();

This example is showcasing the important steps to write a script:

  1. Import the marmite context function
  2. Pass the hre (Hardhat Runtime Environment) variable to the context function, along with a callback function deploying your contracts and executing your transactions
  3. A flag function is provided to your callback function as a parameter, use it to pin a transaction whenever you are interested in having its gas cost compared in the final results

Here is a quick API describing these two functions:

๐Ÿฅ˜ marmite

/**
 * Marmite context function
 * @param hre Hardhat Runtime Environment variable
 * @param callback Function deploying contracts and flagging transactions
 * @param implementations (optional) Array of implementation names to compare, omitting
 *                        this parameter will compare all the found implementations
 */
export default async function marmite(
  hre: HardhatRuntimeEnvironment,
  callback: CallbackFunction,
  implementations: string[] = [],
): Promise<void>

๐Ÿšฉ flag

/**
 * Flags a transaction to display its gas cost in the final results
 * @param name Name of the flag
 * @param tx Transaction to flag
 */
export type FlagFunction = (name: string, tx: ContractTransaction | TransactionResponse) => void;

Once your script is ready, you can run:

npx hardhat golf:script ./path/to/yourScript.ts

golf:script

This task only takes one unnamed parameter, which is the path to your script, e. g. ./examples/scripts/foo.ts.

๐Ÿ”ง Config

Coming soon ๐Ÿ‘€

โ›‘ Help

Feel free to open an issue if you need help or if you encounter a problem! Here are some already known problems though:

  • Naming a flag constructor will create a JS / TS issue
  • Compiling your contracts using npx hardhat compile might not work if Marmite tags are still present in your code

hardhat-marmite's People

Contributors

clemlak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hardhat-marmite's Issues

Cannot place tags in imported contracts from npm registry

hardhat compile behaviour does not resemble the usual node_modules precedence rules. The compilation of contracts in the .gas directory seem to import from node_modules in root directory instead, therefore not removing the tags and leading to compilation error.

Import path resolution of the hardhat compile task needs to be manipulated to import from root/.gs/node_modules instead of root/node_modules

Implementations not being picked up

Doesen't find any implementation if docs from README.md with pattern"

@start:<Greater-than> if (newBar > 0) { bar = newBar; } @end

is used with a ":"

Two possible fixes, modify regex or disallow ":" after start tag in the docs

PRs
#8
#9

unexpected golf:script behaviour

My script:

import hre from 'hardhat';
import marmite from '@primitivefi/hardhat-marmite';

async function main() {
    await marmite(hre, async (flag) => {
        const Complex = await hre.ethers.getContractFactory('Complex');
        const complex = await Complex.deploy(1);

        const tx = await complex.set(42);
        await flag('Set function', tx);
    });
}

main();

contract:

// SPDX-License-Identifier: WTFPL
pragma solidity 0.8.9;

contract Complex {
    uint256 public bar;

    constructor(uint256 initBar) {
        bar = initBar;
    }

    function set(uint256 newBar) external {
        @start<test1>
        if (newBar > 0) {
            bar = newBar;
        }
        @end

        @start<test2>
        if (newBar != 0) {
            bar = newBar;
        }
        @end
    }
}

executing: npx hardhat golf:script ./scripts/marmite.ts

returns:

image

Greater-than โ”‚ Different-from are from another contract in the same folder than Complex

I would expect return only from the contract I am running in the script

Passing arrays as args

This is probably due to my lack of understanding of bash and/or zsh.

npx hardhat golf:contract --contract Foo --func mintManyToSender --params ("value"),1,"0x0" 

// returns
zsh: no matches found: (value),1,0x0

this fails because ("value") gets interpreted as a string. This doesn't even compile

If I set a reference above it doesn't pass the reference:

array=(value)
npx hardhat golf:contract --contract Foo --func mintManyToSender --params array,1,"0x0" 

This compiles BUT errors out with this:

  reason: 'invalid value for array',
  code: 'INVALID_ARGUMENT',
  argument: 'value',
  value: 'array'

Any ideas? I'm sure this will be an issue with others as well but again don't think it's necessarily related to this package just understanding of bash ๐Ÿ˜„

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.