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

web3-plugin-craftsman

v0.1.1

Published

web3.js plugin that allows instantiating contracts objects directly from Solidity source code

Downloads

53

Readme

web3.js Plugin: web3-plugin-craftsman

Smoothly interact with a Smart Contract directly from its Solidity Code, or save its compilation for later use.

Description

The web3-plugin-craftsman allows instantiating contracts directly from Solidity source code or a Solidity file. This is because it compiles the Solidity source code and generates the bytecode and the ABI. This is to enable creating and interacting with smart contracts directly from source without pre-compiling.

So, you can pass the solidity source code or a file path to the the ExtendedContract class constructor; And then use it like a normal web3.js Contract object. And you can also save the generated contract's ABI and Bytecode to a TypeScript, or a JavaScript, file that is compatible and easily readable by a normal web3.js Contract.

Compatibility

This plugin is compatible with:

  • web3.js v4.x
  • Node.js 16+
  • Browsers (untested but should work via Webpack/Rollup/Browserify)

Features

  • Compile Solidity source code that is provided to the ExtendedContract constructor, as mentioned in Creating an instance of the ExtendedContract object.

  • The bytecode and ABI are available to read after the compilation finishes, as mentioned in Waiting for the compilation result.

  • The possibility to save the ABI and the Bytecode, as mentioned at Save the compilation result. And this ABI can be used later to enable IntelliSense for the smart contract methods.

  • When deploying the Contract, the generated bytecode is used internally automatically (no need to provide it), as mentioned in Deploying and interacting with your Contract.

  • The rest is exactly as you would interact with the Contract instance, according to the official web3.js documentation (https://docs.web3js.org/).

  • Do you need more or something else? Try to open an issue and we will discuss it... Are you interested in developing a web3.js plugin for your network, platform, or project? reach out using email to discuss.

Installation

yarn add web3-plugin-craftsman

or

npm install web3-plugin-craftsman

Usage

Basically, you just need to create an object of ExtendedContract by passing the solidity source code.

After that, you have to call await contract.compilationResult to be able to wait for the compilation to finish.

Creating an instance of the ExtendedContract object

However, there is 3 ways to create an object of ExtendedContract:

  • First alternative is to instantiate from the ExtendedContract class

    import { ExtendedContract } from 'web3-plugin-craftsman';
    
    // Using ExtendedContract directly
    const contract = new ExtendedContract(sourceCodeOrPath);
    
    // You need to set the provider only if you need to deploy or execute methods
    // You do not need it incase you only need to compile the source code,
    //  and save the ABI and bytecode for later usage.
    contract.provider = 'http://localhost:8545';
  • Alternatively, you can create the contract using an instance of ExtendedWeb3 that is basically the same as if you created an object of Web3. But it has .eth.ExtendedContract that is equivalent to .eth.Contract but with the ability to accept a smart contract source code.

    import { ExtendedWeb3 } from 'web3-plugin-craftsman';
    
    // Using ExtendedWeb3
    const web3 = new ExtendedWeb3('http://localhost:8545');
    
    const contract = new web3.eth.ExtendedContract(sourceCodeOrPath);
  • As a third alternative, you can use the functionality as a plugin to Web3. This is helpful if you like to using this functionality on an existing Web3 object.

    import { ExtendedWeb3 } from 'web3-plugin-craftsman';
    
    const web3 = new Web3('http://localhost:8545'); // your Web3 object
    
    // Using ExtendedWeb3 as a plugin
    web3.registerPlugin(new ExtendedWeb3());
    
    const contract = new web3.craftsman.ExtendedContract(sourceCodeOrPath);

After you have your contract object created, the rest is the same regardless of the alternative you chose.

Waiting for the compilation result

You need to wait for the compilation to finish before you can go further. This is a one call that you need to await as below:

// Note: After initializing the contract using one of the 3 alternatives
//  mentioned above, the rest is the same regardless of the initialization.

// Wait for the contract compilation and handle compilation errors if any
try {
  const compilationResult = await contract.compilationResult;
  // the compilationResult will consists of:
  // {
  //     abi: ContractAbi,
  //     bytecodeString: string,
  //     contractName: string,
  // }
  // Note: if you did not provide a source code or a file to
  //  the constructor, the compilationResult will be `undefined`. 
  //  This might be useful to know that your contract does not need
  //  to be waited for compilation. But that would also mean you need
  //  to provide the ABI and the bytecode manually.
} catch (e) {
  console.log(e);
}

Note: The contract object will not recognize the ABI. However, To make it do so, save and then use the ABI, as mentioned in a later section named Save the compilation result.

You can detect if the compilation had finished by checking the boolean contract.hadFinishedCompilation. It will be false if it is still compiling, true if it had finished, and undefined if there was no code or file path provided.

Deploying and interacting with your Contract

// get the accounts provided by your Ethereum node (like Ganache). 
const accounts = await web3.eth.getAccounts();
fromAccount = accounts[0];

// Deploy contract
const deployed = await contract
  .deploy({ arguments: [1000] })
  .send({ from: fromAccount });

// Call a method
const myNumber = await deployed.methods.myNumber().call();

// Send a transaction
await deployed.methods.setMyNumber(100).send({ from: fromAccount });
// If you are using TypeScript you need to ask similar to the following:
//  await(deployed.methods.setMyNumber(100) as any)
//    .send({ from: fromAccount });

// Call a method
const myNumberUpdated = await deployed.methods.myNumber().call();

The constructor options

Note that, you can pass one of the following, as the first argument, to the extended contract contractor. (This is what you can enter to replace the variable sourceCodeOrPath mentioned above):

  • A string containing a Smart Contract source code
  • A string containing a Smart Contract file path
  • An object containing the source code and the Smart Contract name:
    {
      sourceCode: string;
      // The name must be specified if there are multiple contracts
      contractName: string;
    }
  • An object containing the path of the file (path/file.sol), or the paths, and the Smart Contract name:
    {
      // Pass an array, if the contract inherits from other contracts 
      //  that exist at other files
      path: string | string[];
      // The name must be specified if there are multiple contracts
      contractName: string;
    }

Examples for the constructor options

And here is an example for passing the file path (the recommended way):

const contract = new ExtendedContract(
  './test/smart_contracts/simple-contract.sol'
);

Or as the following when you need to pass multiple files:

const contract = new ExtendedContract({
  path: [
    './test/smart_contracts/simple-contract.sol',
    './test/smart_contracts/child-contract.sol',
  ],
  contractName: 'ChildContract',
});

And here is an example for passing the solidity code in-line (not recommended):

const contract = new ExtendedContract('
  // SPDX-License-Identifier: MIT
  pragma solidity 0.8.0;

  contract SimpleContract {
      uint256 public myNumber;

      constructor(uint256 _myNumber) {
          myNumber = _myNumber;
      }

      function setMyNumber(uint256 _myNumber) public {
          myNumber = _myNumber;
      }
  }
'
);

Save the compilation result

Saving the compilation result is very handy. Because you do not need to compile the contract every time you interact with your smart contract. The plugin can take care of saving both the ABI and the Bytecode and assign them to exported variables.

// If you provide a directory name. The file name will be automatically generated as `[Smart Contract Name at Solidity code]-artifacts.ts`.
// However, you can specify a file that must end with either `.ts` or `.js`.
await contract.saveCompilationResult('../compilation_output_dir');

The above will create a file named according to your smart contract. And here is the file content for the smart contract named SimpleContract that is mentioned in the previous section:

export const SimpleContractAbi = [
  {
    inputs: [
      {
        internalType: 'uint256',
        name: '_myNumber',
        type: 'uint256'
      }
    ],
    stateMutability: 'nonpayable',
    type: 'constructor',
    signature: ''
  },
  {
    inputs: [],
    name: 'myNumber',
    outputs: [
      {
        internalType: 'uint256',
        name: '',
        type: 'uint256'
      }
    ],
    stateMutability: 'view',
    type: 'function',
    signature: '0x23fd0e40',
    constant: true,
    payable: false
  },
  {
    inputs: [
      {
        internalType: 'uint256',
        name: '_myNumber',
        type: 'uint256'
      }
    ],
    name: 'setMyNumber',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
    signature: '0x6ffd773c',
    constant: false,
    payable: false
  }
] as const;

export const SimpleContractBytecode = '608060405234801561000f575f80fd5b506040516101d23803806101d283398181016040528101906100319190610074565b805f819055505061009f565b5f80fd5b5f819050919050565b61005381610041565b811461005d575f80fd5b50565b5f8151905061006e8161004a565b92915050565b5f602082840312156100895761008861003d565b5b5f61009684828501610060565b91505092915050565b610126806100ac5f395ff3fe6080604052348015600e575f80fd5b50600436106030575f3560e01c806323fd0e401460345780636ffd773c14604e575b5f80fd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f80fd5b60ac816074565b811460b5575f80fd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212206e0456eccefdc957c99835d074d2cbebb77403b8d6ac9c68b75079b173f5ac6664736f6c63430008140033';

Note that the auto-generated file name for the above contract compilation result will be: SimpleContract-artifacts.ts. And the variables names are also auto generated according to your Smart Contract name. Additionally, to make the intellisense for the smart contracts object works according to the ABI, the syntax as const is added to the ABI variable.

And you can use the above ABI and Bytecode simply by:

import {
  SimpleContractAbi,
  SimpleContractBytecode,
} from '../compilation_output_dir/SimpleContract-artifacts.ts';
// you use those constants when interacting with your smart contract object, 
//  as usual, according to web3.js documentation (https://docs.web3js.org/)

Contributing

Please include relevant test cases to cover any new functionality.

Compatibility

This plugin is compatible with:

  • web3.js version 4.x
  • Node.js 16+
  • Browsers (via Webpack/Rollup/Browserify)

Troubleshooting

Just try to run your code again, if you faced the following error:

EACCES: permission denied, open '/home/maltabba/repos/experiments/web3-plugin-craftsman/node_modules/solc-typed-ast/.compiler_cache/wasm/soljson-v0.8.20+commit.a1b79de6.js'

And if the error persist, you may open an issue at: https://github.com/ConsenSys/solc-typed-ast.


If you like to contribute to the development of this package, you may check the CONTRIBUTION.md file.

Tutorials

If you are new in the space and would like to follow a tutorial, here is one: Interacting with Ethereum Smart Contracts, Directly from Solidity Source Code`