npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@playmint/lallafa

v1.0.1

Published

Gas profiling tool for Ethereum smart contracts

Downloads

4

Readme

NPM Package

Lallafa

An EVM gas profiling tool written in Typescript.

Installation

npm install --save-dev @playmint/lallafa

How it works

Lallafa produces a gas usage report by ingesting a debug trace, along with some compiler input and output. It outputs profiling data which is collated at both the instruction level, and the line-of-code level.

How to use

To profile a transaction, use profile:

function profile(trace: DebugTrace, isDeploymentTransaction: boolean, address: string, contracts: ContractInfoMap)
  • trace - a debug trace generated by a node using debug_traceTransaction
  • isDeploymentTransaction - is the tx the deployment of the contract, determines which bytecode the profiler will use
  • address - address of the contract where the transaction originated, needed for properly profiling re-entrant calls
  • contracts - map of address => ContractInfo
type ContractInfo = {
    // standard input JSON
    input: CompilerInput;

    // standard output JSON
    output?: CompilerOutput;

    // if output is omitted, Lallafa will attempt to compile the contract from input. If you do this
    // then solcVersion needs to be defined
    solcVersion?: string;

    // name of source file which contains the contract definition
    sourceName: string;

    // name of contract within the source file
    contractName: string;
};

profile returns a Profile object, which maps contract address to an object containing an instruction profile and a source profile:

type Profile = {
    [address: string]: {
        instructionsProfile: InstructionProfile[];
        sourcesProfile: SourcesProfile;
    }
};

type InstructionProfile = {
    // gas spent
    gas: number;
    // bytecode hex string 
    bytecode: string;
    // op, arg if any, jumpdest name if relevant
    asm: string;
    // program counter
    pc: number;
    // opcode as string, e.g. ISZERO, PUSH1, etc
    op: string;
    // id of source this instruction was generated from
    sourceId: number;
    sourceRangeStart: number;
    sourceRangeLength: number;
    // line of code where this source range starts (0 being the first line)
    sourceLine: number;
};

type SourcesProfile = {
    [sourceId: number]: {
        name: string;
        content: string;
        lines: {
            // gas spent
            gas: number;
            text: string;
            // instructions generated from this line of code
            instructions: InstructionProfile[]
        }[];
    }
};

You might choose to visualise profiles in a bespoke way, but you can use instructionsProfileToString and sourcesProfileToString to quickly do so.

Example

Check out the test project for detailed examples.

Here's a way to profile a simple contract Storage.sol:

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

contract Storage {
    uint256 _value;

    function setValue(uint256 value) public {
        _value = value;
    }
}

profileStorage.ts:

import hre from "hardhat";
import { Storage__factory } from "../typechain-types";
import fs from "fs";
import { profile, sourcesProfileToString, instructionsProfileToString, ContractInfoMap } from "../../src";


async function main() {
    const storageFactory = new Storage__factory((await hre.ethers.getSigners())[0]);
    const storage = await storageFactory.deploy();
    const tx = await (await storage.setValue(42)).wait();
    const debugTrace = await hre.ethers.provider.send("debug_traceTransaction",
        [tx.transactionHash, {
            "disableStack": true,
            "disableMemory": true,
            "disableStorage": true
        }]);

    const buildInfo = await hre.artifacts.getBuildInfo("contracts/Storage.sol:Storage");
    if (!buildInfo) {
        throw new Error("couldn't find build info");
    }

    const contracts: ContractInfoMap = {}
    contracts[storage.address] = {
        input: buildInfo.input,
        output: buildInfo.output,
        sourceName: "contracts/Storage.sol",
        contractName: "Storage"
    };

    // or if you want Lallafa to compile from input
    /*
    contracts[storage.address] = {
        input: buildInfo.input,
        solcVersion: buildInfo.solcLongVersion,
        sourceName: "contracts/Storage.sol",
        contractName: "Storage"
    };
    */

    const result = await profile(
        debugTrace,
        false, // isDeployment
        storage.address,
        contracts);

    fs.writeFileSync("sources_profile.txt", sourcesProfileToString(result));
    fs.writeFileSync("instruction_profile.txt", instructionsProfileToString(result));
}

main().catch(e => console.error(e));

This script outputs profiles of both the source code and the asm instructions to show what these both look like. sources_profile.txt:

// 5FBDB2315678AFECB367F032D93F642F64180AA3
============================================

// contracts/Storage.sol

Gas   |
---------------------------------------------------------------------------------------------------------------------------------------
97    | //SPDX-License-Identifier: UNLICENSED
0     | pragma solidity ^0.8.0;
0     | 
0     | contract Storage {
0     |     uint256 _value;
0     | 
68    |     function setValue(uint256 value) public {
22114 |         _value = value;
0     |     }
0     | }
0     | 

// #utility.yul

Gas   |
---------------------------------------------------------------------------------------------------------------------------------------
0     | {
0     | 
0     |     function allocate_unbounded() -> memPtr {
0     |         memPtr := mload(64)
0     |     }
0     | 
0     |     function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() {
0     |         revert(0, 0)
0     |     }
0     | 
0     |     function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() {
0     |         revert(0, 0)
0     |     }
0     | 
20    |     function cleanup_t_uint256(value) -> cleaned {
8     |         cleaned := value
0     |     }
0     | 
11    |     function validator_revert_t_uint256(value) {
38    |         if iszero(eq(value, cleanup_t_uint256(value))) { revert(0, 0) }
0     |     }
0     | 
22    |     function abi_decode_t_uint256(offset, end) -> value {
11    |         value := calldataload(offset)
18    |         validator_revert_t_uint256(value)
0     |     }
0     | 
22    |     function abi_decode_tuple_t_uint256(headStart, dataEnd) -> value0 {
32    |         if slt(sub(dataEnd, headStart), 32) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() }
0     | 
2     |         {
0     | 
3     |             let offset := 0
0     | 
32    |             value0 := abi_decode_t_uint256(add(headStart, offset), dataEnd)
0     |         }
0     | 
0     |     }
0     | 
0     | }
0     | 

instructions_profile.txt:

// 5FBDB2315678AFECB367F032D93F642F64180AA3
============================================

Gas   | PC | Asm                                                                                          | Bytecode
------------------------------------------------------------------------------------------------------------------------
3     | 0  | PUSH1 0x80                                                                                   | 0x6080
3     | 2  | PUSH1 0x40                                                                                   | 0x6040
12    | 4  | MSTORE                                                                                       | 0x52
2     | 5  | CALLVALUE                                                                                    | 0x34
3     | 6  | DUP1                                                                                         | 0x80
3     | 7  | ISZERO                                                                                       | 0x15
3     | 8  | PUSH1 0xF                                                                                    | 0x600F
10    | A  | JUMPI [in]                                                                                   | 0x57
0     | B  | PUSH1 0x0                                                                                    | 0x6000
0     | D  | DUP1                                                                                         | 0x80
0     | E  | REVERT                                                                                       | 0xFD
1     | F  | JUMPDEST                                                                                     | 0x5B
2     | 10 | POP                                                                                          | 0x50
3     | 11 | PUSH1 0x4                                                                                    | 0x6004
2     | 13 | CALLDATASIZE                                                                                 | 0x36
3     | 14 | LT                                                                                           | 0x10
3     | 15 | PUSH1 0x28                                                                                   | 0x6028
10    | 17 | JUMPI [in]                                                                                   | 0x57
3     | 18 | PUSH1 0x0                                                                                    | 0x6000
3     | 1A | CALLDATALOAD                                                                                 | 0x35
3     | 1B | PUSH1 0xE0                                                                                   | 0x60E0
3     | 1D | SHR                                                                                          | 0x1C
3     | 1E | DUP1                                                                                         | 0x80
3     | 1F | PUSH4 0x55241077                                                                             | 0x6355241077
3     | 24 | EQ                                                                                           | 0x14
3     | 25 | PUSH1 0x2D [setValue_uint256_0]                                                              | 0x602D
10    | 27 | JUMPI [in]                                                                                   | 0x57
0     | 28 | JUMPDEST                                                                                     | 0x5B
0     | 29 | PUSH1 0x0                                                                                    | 0x6000
0     | 2B | DUP1                                                                                         | 0x80
0     | 2C | REVERT                                                                                       | 0xFD
1     | 2D | JUMPDEST [setValue_uint256_0]                                                                | 0x5B
3     | 2E | PUSH1 0x43                                                                                   | 0x6043
3     | 30 | PUSH1 0x4                                                                                    | 0x6004
3     | 32 | DUP1                                                                                         | 0x80
2     | 33 | CALLDATASIZE                                                                                 | 0x36
3     | 34 | SUB                                                                                          | 0x03
3     | 35 | DUP2                                                                                         | 0x81
3     | 36 | ADD                                                                                          | 0x01
3     | 37 | SWAP1                                                                                        | 0x90
3     | 38 | PUSH1 0x3F                                                                                   | 0x603F
3     | 3A | SWAP2                                                                                        | 0x91
3     | 3B | SWAP1                                                                                        | 0x90
3     | 3C | PUSH1 0x85 [abi_decode_tuple_t_uint256_0]                                                    | 0x6085
8     | 3E | JUMP [in]                                                                                    | 0x56
1     | 3F | JUMPDEST [setValue_uint256_1]                                                                | 0x5B
3     | 40 | PUSH1 0x45 [setValue_uint256_3]                                                              | 0x6045
8     | 42 | JUMP [in]                                                                                    | 0x56
1     | 43 | JUMPDEST [setValue_uint256_2]                                                                | 0x5B
0     | 44 | STOP                                                                                         | 0x00
1     | 45 | JUMPDEST [setValue_uint256_3]                                                                | 0x5B
3     | 46 | DUP1                                                                                         | 0x80
3     | 47 | PUSH1 0x0                                                                                    | 0x6000
3     | 49 | DUP2                                                                                         | 0x81
3     | 4A | SWAP1                                                                                        | 0x90
22100 | 4B | SSTORE                                                                                       | 0x55
2     | 4C | POP                                                                                          | 0x50
2     | 4D | POP                                                                                          | 0x50
8     | 4E | JUMP [out]                                                                                   | 0x56
0     | 4F | JUMPDEST [revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b_0]   | 0x5B
0     | 50 | PUSH1 0x0                                                                                    | 0x6000
0     | 52 | DUP1                                                                                         | 0x80
0     | 53 | REVERT                                                                                       | 0xFD
1     | 54 | JUMPDEST [cleanup_t_uint256_0]                                                               | 0x5B
3     | 55 | PUSH1 0x0                                                                                    | 0x6000
3     | 57 | DUP2                                                                                         | 0x81
3     | 58 | SWAP1                                                                                        | 0x90
2     | 59 | POP                                                                                          | 0x50
3     | 5A | SWAP2                                                                                        | 0x91
3     | 5B | SWAP1                                                                                        | 0x90
2     | 5C | POP                                                                                          | 0x50
8     | 5D | JUMP [out]                                                                                   | 0x56
1     | 5E | JUMPDEST [validator_revert_t_uint256_0]                                                      | 0x5B
3     | 5F | PUSH1 0x65                                                                                   | 0x6065
3     | 61 | DUP2                                                                                         | 0x81
3     | 62 | PUSH1 0x54 [cleanup_t_uint256_0]                                                             | 0x6054
8     | 64 | JUMP [in]                                                                                    | 0x56
1     | 65 | JUMPDEST [validator_revert_t_uint256_1]                                                      | 0x5B
3     | 66 | DUP2                                                                                         | 0x81
3     | 67 | EQ                                                                                           | 0x14
3     | 68 | PUSH1 0x6F [validator_revert_t_uint256_2]                                                    | 0x606F
10    | 6A | JUMPI [in]                                                                                   | 0x57
0     | 6B | PUSH1 0x0                                                                                    | 0x6000
0     | 6D | DUP1                                                                                         | 0x80
0     | 6E | REVERT                                                                                       | 0xFD
1     | 6F | JUMPDEST [validator_revert_t_uint256_2]                                                      | 0x5B
2     | 70 | POP                                                                                          | 0x50
8     | 71 | JUMP [out]                                                                                   | 0x56
1     | 72 | JUMPDEST [abi_decode_t_uint256_0]                                                            | 0x5B
3     | 73 | PUSH1 0x0                                                                                    | 0x6000
3     | 75 | DUP2                                                                                         | 0x81
3     | 76 | CALLDATALOAD                                                                                 | 0x35
3     | 77 | SWAP1                                                                                        | 0x90
2     | 78 | POP                                                                                          | 0x50
3     | 79 | PUSH1 0x7F                                                                                   | 0x607F
3     | 7B | DUP2                                                                                         | 0x81
3     | 7C | PUSH1 0x5E [validator_revert_t_uint256_0]                                                    | 0x605E
8     | 7E | JUMP [in]                                                                                    | 0x56
1     | 7F | JUMPDEST [abi_decode_t_uint256_1]                                                            | 0x5B
3     | 80 | SWAP3                                                                                        | 0x92
3     | 81 | SWAP2                                                                                        | 0x91
2     | 82 | POP                                                                                          | 0x50
2     | 83 | POP                                                                                          | 0x50
8     | 84 | JUMP [out]                                                                                   | 0x56
1     | 85 | JUMPDEST [abi_decode_tuple_t_uint256_0]                                                      | 0x5B
3     | 86 | PUSH1 0x0                                                                                    | 0x6000
3     | 88 | PUSH1 0x20                                                                                   | 0x6020
3     | 8A | DUP3                                                                                         | 0x82
3     | 8B | DUP5                                                                                         | 0x84
3     | 8C | SUB                                                                                          | 0x03
3     | 8D | SLT                                                                                          | 0x12
3     | 8E | ISZERO                                                                                       | 0x15
3     | 8F | PUSH1 0x98 [abi_decode_tuple_t_uint256_2]                                                    | 0x6098
10    | 91 | JUMPI [in]                                                                                   | 0x57
0     | 92 | PUSH1 0x97                                                                                   | 0x6097
0     | 94 | PUSH1 0x4F [revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b_0] | 0x604F
0     | 96 | JUMP [in]                                                                                    | 0x56
0     | 97 | JUMPDEST [abi_decode_tuple_t_uint256_1]                                                      | 0x5B
1     | 98 | JUMPDEST [abi_decode_tuple_t_uint256_2]                                                      | 0x5B
3     | 99 | PUSH1 0x0                                                                                    | 0x6000
3     | 9B | PUSH1 0xA4                                                                                   | 0x60A4
3     | 9D | DUP5                                                                                         | 0x84
3     | 9E | DUP3                                                                                         | 0x82
3     | 9F | DUP6                                                                                         | 0x85
3     | A0 | ADD                                                                                          | 0x01
3     | A1 | PUSH1 0x72 [abi_decode_t_uint256_0]                                                          | 0x6072
8     | A3 | JUMP [in]                                                                                    | 0x56
1     | A4 | JUMPDEST [abi_decode_tuple_t_uint256_3]                                                      | 0x5B
3     | A5 | SWAP2                                                                                        | 0x91
2     | A6 | POP                                                                                          | 0x50
2     | A7 | POP                                                                                          | 0x50
3     | A8 | SWAP3                                                                                        | 0x92
3     | A9 | SWAP2                                                                                        | 0x91
2     | AA | POP                                                                                          | 0x50
2     | AB | POP                                                                                          | 0x50
8     | AC | JUMP [out]                                                                                   | 0x56