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

@ricmoo/rooted

v0.0.1

Published

The CLI and code for Rooted Ethereum deployments.

Downloads

1

Readme

Rooted: Far-Too-Easy-To-Upgrade Contracts

The goal of Rooted is to simplify making upgradable Ethereum contracts.

With Rooted, any contract deployed by the same account will be deployed to the same Rooted Address, everytime.

So, to upgrade a contract, have it self-destruct itself (in some safe, gaurded manner) and then redeploy using the same account originally used.

How To Deploy

Since any given EOA will have exactly one Rooted Address, it is recommended you create a new EOA for each contract you wish Rooted. Otherwise, complication seeps back in, as you need contracts to dispatch deployment calls.

  1. Create a new EOA for your Contract
  2. Fund that account
  3. Deploy your contract as usual, except: instead of an empty to address, use v0-beta-0.rooted.eth.

That's it! When you wish to upgrade your contract, simply use whatever method exists on your contract to self-destruct it and then follow the above steps again, with the new contract bytecode.

Command-Line Interface

The command-line interface has the same options as the other ethers CLI utilities. To install, use npm install @ricmoo/rooted, and can then be used with the following usage:

Usage:
   rooted FILENAME [ OPTIONS ]

OPTIONS
  --contract                  specify the contract to deploy
  --args                      specify JSON encoded constructor args
  --no-optimize               do not run the optimizer

Example:

These operations were run on ropsten (the --network ropsten is omitted for brevity), so each transaction hash can be looked up on Etherscan.

/home/ricmoo> npm install -g @ricmoo/rooted

/home/ricmoo> cat Test1.sol
contract Test1 {
    address public owner = msg.sender;
    string public value;

    constructor(string _value) public {
        value = _value;
    }

    function die() public {
        require(msg.sender == owner);
        selfdestruct(owner);
    }
}

/home/ricmoo> cat Test2.sol
contract Test2 {
    address payable public owner = msg.sender;

    function value() public view returns (string memory) {
        return "The cat came back...";
    }

    function die() public {
        require(msg.sender == owner);
        selfdestruct(owner);
    }
}

# Deploy
/home/ricmoo> rooted --account wallet.json Test1.sol --args '[ "Hello World" ]'
Deploy: contracts/tests/test1.sol
  Contract Address:  0x266bBB07e802890024eBd03512FbED1E3c961d83
Response:
  Hash:  0x35317670d0edf57c4eb8c75c0fa080c81f8ab30cde27e7d2db30b50b96be7293
  
/home/ricmoo> export ADDR="0x266bBB07e802890024eBd03512FbED1E3c961d83"

/home/ricmoo> ethers --account wallet.json eval 'provider.getCode(process.env.ADDR)'
"0x608060405234801561001057600080fd5b50600436106100415760003560e01c806335f46994146100465780633fa4f245146100505780638da5cb5b146100cd575b600080fd5b61004e6100f1565b005b610058610116565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561009257818101518382015260200161007a565b50505050905090810190601f1680156100bf5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100d56101a3565b604080516001600160a01b039092168252519081900360200190f35b6000546001600160a01b0316331461010857600080fd5b6000546001600160a01b0316ff5b60018054604080516020600284861615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561019b5780601f106101705761010080835404028352916020019161019b565b820191906000526020600020905b81548152906001019060200180831161017e57829003601f168201915b505050505081565b6000546001600160a01b03168156fea2646970667358221220eb669057926d1e6d788f7eb4df1322e2d04c3e49790744f9c31fdca1a8e3074f64736f6c63430006040033"

/home/ricmoo> ethers eval '(new Contract(process.env.ADDR, [ "function value() view returns (string)" ], provider)).value()'
Hello World


# Destroy
/home/ricmoo> ethers --account wallet.json eval '(new Contract(process.env.ADDR, [ "function die()" ], accounts[0])).die()'
Response:
  Hash:  0xd74a31f8fb0ec36bcbae174a665e1f183d269bf122605a44355dfc6d0b7235ca

/home/ricmoo> ethers --account wallet.json eval 'provider.getCode(process.env.ADDR)'
"0x"


# Redeploy (same address, new code)
/home/ricmoo> rooted --account wallet.json Test2.sol
Deploy: contracts/tests/test2.sol
  Contract Address:  0x266bBB07e802890024eBd03512FbED1E3c961d83
Response:
  Hash:  0x89cb3165cb8a27d08094b198dbe8279147c226de293b69b86ccca91fedf621f1

/home/ricmoo> ethers --account wallet.json eval 'provider.getCode(process.env.ADDR)'
"0x608060405234801561001057600080fd5b50600436106100415760003560e01c806335f46994146100465780633fa4f245146100505780638da5cb5b146100cd575b600080fd5b61004e6100f1565b005b610058610116565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561009257818101518382015260200161007a565b50505050905090810190601f1680156100bf5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100d5610144565b604080516001600160a01b039092168252519081900360200190f35b6000546001600160a01b0316331461010857600080fd5b6000546001600160a01b0316ff5b6040805180820190915260148152732a34329031b0ba1031b0b6b2903130b1b597171760611b602082015290565b6000546001600160a01b03168156fea26469706673582212205320b56a7ffb50e87057e9097f43c461aeeda9bd441fa781680ac5a46535cea164736f6c63430006040033"

/home/ricmoo> ethers --account wallet.json eval '(new Contract(process.env.ADDR, [ "function value() view returns (string)" ], accounts[0])).value()'
The cat came back...

Persistent Storage

Since a self-destructed contract loses all its storage, there is an external Storage contract available. To use it, simply specify the interface you'd like (the selector is ignored) with keys which must fit within a bytes32 and values which must also fit within a bytes32.

For example:

interface StorageUint256 {
    function get(uint256 key) view external returns (uint256);
    function set(uint256 key, uint256 value) external;
}        

interface StorageBytes32 {
    function get(bytes32 key) view external returns (bytes32);
    function set(bytes32 key, bytes32 value) external;
}        

interface StorageMishMash {
    function get(uint8 key) view external returns (bytes32);
    function set(uint8 key, bytes32 value) external;
}        

And in your contract, you can use:

function persistentValue() view public returns (uint256) {
    return StorageUint256(0x760158D4613e8851D0C5Ae906a81698da89f903a).get(0);
}

function setPersistentValue(uint256 _value) public {
    StorageUint256(0x760158D4613e8851D0C5Ae906a81698da89f903a).set(0, _value);
}                            

Notes:

  • The slots are shared per-caller, so make sure the keys do no collide; for example, bytes1(0x05) and uint8(5) would both occupy the same storage.
  • Accessing values in an external contract will incur addition gas costs; cache values and limit calls if possible

How does it work?

It works by using a combination of two of our previous Hackathon projects, Wisps and Lurch.

Wisps allow using create2 to deploy contracts with different bytecode to the same address, although it was intended to only live within a single transaction, this allows contracts to live as long as is desired.

Lurch allows execution of EVM bytecode within an Ethereum contract, which allows for hooks to alter the environment during runtime. In this case, opcodes like codecopy and caller are hijacked so the executing initcode thinks it is being deployed normally.

Caveats

  • This shold be thought of as fairly experimental at this point; please use it responsibly
  • On average Lurch costs quite a bit more to run a contract through, around 300k gas more

License

MIT License.