sol-builder

A solidity contract builder for Ethereum platform

Usage no npm install needed!

<script type="module">
  import solBuilder from 'https://cdn.skypack.dev/sol-builder';
</script>

README

# Solidity Contract Builder

Module for building solidity contracts for the Ethereum platform using javascript.

This is particularly useful when you have to build a contract using parameters specified by your end user. Using sol-builder you can add/remove attributes, change types, change function signatures, etc, without having to manipulate directly the string representing the contract.

After configuring your contract, you get the result code (beautified) using the function getContract().

Install

To install the module, just run

sudo npm install sol-builder -g

Testing

To test this code, run:

npm test

Creating a contract

To create a simple contract, you have to use the function addContract. This function will create a contract with the specified name and parent (if specified). For instance:

var builder = require('sol-builder');

builder.addContract({name: 'Test', is: 'Parent'});

console.log(builder.getContract());

This code will print:

contract Test is Parent {

}

You can add as many contract as you want. For instance, the code;

var builder = require('sol-builder');

builder.addContract({name: 'Test', is: 'Parent'});
builder.addContract({name: 'Other', is: 'Test'});

console.log(builder.getContract());

Will produce:

contract Test is Parent {

}

contract Other is Test {
}

Adding attributes

You can add attributes to your contract by using the function addAttribyte specifying a single object as parameter. This object contains all properties of this attribute. Valid properties are:

  • name: The name of this attributes.
  • type: Type of the attributes.
  • modifier (optional): A modifier for the attribute. Valid values are: public, private or private.
  • value (optional): The initialization value of this attribute.
  • comment (optional): Some comment for this attributes that will live just above its declaration.
  • lineBreak (optional) Whether you want to break line after its declaration.

The code below illustrates the use of the addAttribute function.

builder.addAttribute({
    name: 'attr1',
    type: 'uint',
    modifier: 'public',
    comment: 'Just a simple comment',
    value: 123
});

Assuming the code below is inserted into the previous example shown, the result would be:

contract MyContract is ParentContract {

    // Just a simple comment
    uint public attr1 = 123;
}

Changing properties of attributes

You can change any property of an attribute by using the function changeAttribute and passing an object specifying which property(ies) you would like to change. The property name should always be specified since it's used to find the attribute. For instance:

builder.changeAttribute({
    name: 'attr1',
    value: 999
});

will result:

contract MyContract is ParentContract {

    uint public attr1 = 999;
}

Another example, changing more than one property:

builder.changeAttribute({
    name: 'attr1',
    value: 999,
    comment: 'Just a test comment',
    modifier: undefined
});

Result:

contract MyContract is ParentContract {

    // Just a test comment
    uint attr1 = 999;
}

Removing an attribute

You can easily remove an attribute by using removeAttribute and specifying the attribute name to be removed.

Adding a struct

Structs can be added into your contract using the function addStruct. The object passed as parameter accepts the following properties:

  • name: The name of the struct being built.
  • attributes: List of attributes of the struct. The attribute type accepts all the properties of a contract attribute but the modifier.
  • comment (optional): Any comment you want to add to the struct.

For instance, the code

builder.addStruct({
    name: 'MyStruct',
    comment: 'Just a test…',
    attributes:[
        {
            name: 'attr1',
            type: 'address',
            comment: 'struct attr1'
            lineBreak: true
        },
        {
            name: 'attr2',
            type: 'uint',
            value: 123
        }
    ];
});

will produce:

contract MyContract is ParentContract {

    // Just a test...
    struct MyStruct {
        // struct attr1
        address attr1;

        uint attr2 = 123;
    }
}

Removing and changing a struct

The same logic for contract attributes applies here: use the functions removeStruct(name) and changeStruct(obj).

Adding a mapping

Add a mapping into your contract by using the function addMapping. The object passed as parameter accepts the following properties:

  • name: Mapping name.
  • keyType: Type of the key.
  • valueType: Type of the value.
  • comment (optional): some comment that will show above the mapping declaration.
  • modifier (optional): modifier of the mapping. Valid values are private, public and internal.
  • lineBreak (optional): whether a line break should be inserted below the mapping declaration.

For example, the code

builder.addMapping({
    name: 'records',
    keyType: 'bytes32',
    valueType: 'address',
    comment: 'Just a sample mapping'
});

will produce

contract MyContract is ParentContract {

    // Just a sample mapping
    mapping(bytes32 => address) records;
}

Removing and changing a mapping

The same logic for contract attributes applies here: use the functions removeMapping(name) and changeMapping(obj).

Adding a function

Insert a function into your contract by using the function addFunction. Valid properties for the object passed as parameter for this function are:

  • name: Function's name.
  • parameters (optional): List of function's parameters. Each object should contain name and type.
  • returnType (optional): if this function returns a value, specify its type using this property.
  • body: the actual body of the function. There is no need to indent code here since all the contract code is beautified at the end.
  • lineBreak (optional): whether a line break should be inserted after this function.

Example:

builder.addFunction({
    name: 'myFunc',
    comment: 'Sample function',
    parameters:[
        {
            name: 'param1',
            type: 'address'
        },
        {
            name: 'param2',
            type: 'bytes32'
        }
    ],
    returnType: 'bytes32',
    body: 'some code1; some code2; some code3'
});

Result:

contract MyContract is ParentContract {

    // Sample function
    function (address param1, bytes32 param2) returns(bytes32) {
        some code1;
        some code2;
        some code3;
    }
}

Changing and removing a function

The same logic for contract attributes applies here: use the functions removeFunction(name) and changeFunction(obj).

Placeholders

You can also add some placeholders in your code for later substitution. For instance, you may want to change just a tiny part of a function body instead of replace it all. Using the previous example code, you can set the property body to something like some code1;UI_PLACEHOLDER_1;some code3;. Then you can use:

builder.addReplacement('UI_PLACEHOLDER_1', '');

which would yield the result:

contract MyContract is ParentContract {

    // Sample function
    function (address param1, bytes32 param2) returns(bytes32) {
        some code1;
        some code3;
    }
}

or

builder.addReplacement('UI_PLACEHOLDER_1', 'some code 2.1; some code2.2;');

which would yield the result:

contract MyContract is ParentContract {

    // Sample function
    function (address param1, bytes32 param2) returns(bytes32) {
        some code1;
        some code 2.1;
        some code 2.2;
        some code3;
    }
}

A complete example

var builder = require('sol-builder');

builder.addContract({name: 'MyContract', is: 'ParentContract'});

builder.addAttribute({
    name:     'someNumber',
    type:     'uint8',
    modifier: 'public',                   /* optional */
    value:    16,                         /* optional */
    comment:  'Just a test attribute...', /* optional */
    lineBreak: true                       /* optional */
});

builder.addStruct({
    name: 'Record',
    comment: 'Test struct...', /* optional */
    lineBreak: true,           /* optional */
    attributes: [
        {
            name: 'myNumber',
            type: 'uint32',
            comment: 'Simple comment', /* optional */
            lineBreak: false           /* optional */
        },
        {
            name: 'myAddress',
            type: 'address',
            lineBreak: false /* optional */
        }
    ]
});

builder.addMapping({
    name: 'records',
    keyType: 'address',
    valueType: 'Record',
    modifier: 'public',     /* optional */
    comment: 'test',        /* optional */
    lineBreak: true         /* optional */
});

builder.addFunction({
    name: 'myFunc',
    comment: 'Simple function', /* optional */
    parameters: [
        {
            name: '_number',
            type: 'uint32'
        }
    ],
    body: 'someNumber++;someNumber += _number;'

});

builder.addContract({name: 'OtherContract', is: 'MyContract'});

console.log(builder.getContract());

The code above would produce the following output:

contract MyContract is ParentContract {

    // Just a test attribute...
    uint8 public someNumber = 16;

    // Test struct...
    struct Record {
        // Simple comment
        uint32 myNumber;
        address myAddress;
    }

    // test
    mapping public(address => Record) records;

    // Simple function
    function myFunc(uint32 _number) {
        someNumber++;
        someNumber += _number;
    }
}

contract OtherContract is MyContract {

}