Skip to content

2. GettingStarted

Levy Santiago edited this page Apr 25, 2023 · 14 revisions

Components you can import from @gifflar/solgen:

  • createGifflarContract
  • createGifflarLibrary
  • createGifflarInterface
  • createGifflarManager

Index

Quickstart

Gifflar Contract

Gifflar Contract is the creationg of a smart contract.

Modeling Contract

First you can model the contract definitions.

import { createGifflarContract } from "@gifflar/solgen";

const myContract = createGifflarContract("MyContract");

// Creating a contract variable
myContract.createVariable({ regularType: "address" }, "owner", "public");

// Creating event
gContract.createEvent("transferedOwnership", [
  { name: "oldOwner", type: { regularType: "address" } },
  { name: "newOwner", type: { regularType: "address" } },
]);

// Creating contract function
myContract
  .createFunction("transferOwnership", "public")
  .setInput({ regularType: "address" }, "_newOwner")
  .setRequire("msg.sender == owner", "Forbidden")
  .setRequire("_newOwner != address(0)", "Invalid address!")
  .setVariable({regularType: "address"}, "_oldOwner", { customExpression: "owner" });
  .setAssignment("owner", { customExpression: "_newOwner" });
  .setEventCall("transferedOwnership", ["_oldOwner", "_newOwner"]);

The contract is constructed using a JSON model, which can be obtained as follows:

// Javascript object
const json = myContract.toJson();

// Stringifying json if needed
const stringifyedJson = mContract.toString();

The result is:

{
  "contract": {
    "name": "MyContract",
    "inherits": [],
    "usings": [],
    "structs": [],
    "variables": [
      {
        "type": "address",
        "name": "owner",
        "scope": "public"
      }
    ],
    "mappings": [],
    "events": [
      {
        "name": "transferedOwnership",
        "inputs": [
          { "name": "oldOwner", "type": "address" },
          { "name": "newOwner", "type": "address" }
        ]
      }
    ],
    "modifiers": [],
    "functions": [
      {
        "name": "transferOwnership",
        "scope": "public",
        "isConstructor": false,
        "inputs": [{ "name": "_newOwner", "type": "address" }],
        "outputs": [],
        "modifiers": [],
        "content": [
          {
            "statement": "require",
            "condition": "msg.sender == owner",
            "errorMessage": "Forbidden"
          },
          {
            "statement": "require",
            "condition": "_newOwner != address(0)",
            "errorMessage": "Invalid address!"
          },
          {
            "statement": "variable",
            "type": "address",
            "name": "_oldOwner",
            "expressionValue": {
              "customExpression": "owner"
            }
          },
          {
            "statement": "assignment",
            "variable": "owner",
            "expressionValue": {
              "customExpression": "_newOwner"
            }
          },
          {
            "statement": "event_call",
            "name": "transferedOwnership",
            "variables": ["_oldOwner", "_newOwner"]
          }
        ]
      }
    ]
  }
}

Writing Contract

Then you can generate the code by calling the .write() function:

myContract.write();

or you can create the contract model directly using the JSON, then sending it to .write() function. You can pass a list of contract to be written. This means you can create the JSON in another system, and pass the JSON contract model as request to an API that uses Gifflar in background. Amazing right?

myContract.write([json]);

The result is:

pragma solidity 0.6.0;

contract MyContract {
// VARIABLES
address owner;

// EVENTS
event transferedOwnership(address oldOwner, address newOwner);

// FUNCTIONS
function transferOwnership(address _newOwner) public{
require(msg.sender == owner, "Forbidden");
require(_newOwner == owner, "Invalid address!");
address _oldOwner = owner;
owner = _newOwner;
emit transferedOwnership(_oldOwner, _newOwner);
}

}

Compiling Contract

For contract compilation, you can just run the method below and you will get the contract compilation JSON that contains the ABI, bytecodes and others metadata. If the compiler throws any errors, they can be accessed by passing a callback function to .compile().

const compiledJson = myContract.compile((errors) => {
  if (errors) console.log(errors);
});

Deploying Contract

Before deploying the contract you might want to first define the deployConfig and signer after contract model creation. So this will be saved in the Gifflar Contract object for next deploys. The setDeployConfig will also create a Web3 object if any was previously created.

If you'd like to create your own web3 object separately, you must later config the Gifflar Contract deployer to use this web3. For example:

import Web3 from "web3";

// Creating my own web3
const web3 = new Web3(new HttpProvider("http://localhost:8545"));

// Configuring deployer to use my web3
myContract.setWeb3(web3);

// Obtaining the web3 used by the deployer (must be your web3)
const deployerWeb3 = myContract.getWeb3();

Then you can define the deploy configurations and default signer:

// Defining default deploy config
myContract.setDeployConfig({
  key: "local_network",
  networkId: 0,
  gas: 3000000,
  gasPrice: "10000000000",
  nodeLink: "http://localhost:8545",
});

// Defining default signer
myContract.addSigner(accountPrivateKey);

To deploy the contract you just created, you run the method .deploy() passing the necessary inputs for the deployer, and opitionally the signer private key, you can omit them if you already defined the deployConfig and signer. The .deploy method returns the web3 contract instance in the blockchain.

const instance = myContract.deploy(
  {
    args: [],
    from: "0x123123123...", // opitional
    gas: 3000000, // opitional
    gasPrice: "10000000000", // opitional
    nonce: 0, // opitional
  },
  "0x321321321..." // opitional
);

Retrieving Contract Data

You can also retrieve the generated contract content with the methods: .written(), .compiled(), .deployed().

const code = myContract.written();
const compiledJson = myContract.compiled();
const instance = myContract.deployed();

Gifflar Library

Gifflar Library is the creationg of a smart contract library.

Modeling Library

The library modelation works almost the same as the Contract (see Modeling Contract), it has the following differences:

  • Libraries don't have constructors;
  • Libraries don't have fallback and receive functions;
  • Libraries may not contain non-constant state variables;
  • Libraries are not allowed to inherit;
  • Gifflar Library don't export the function deploy, since is just a library. You can only model, write and compile it.

For more info, see the Solidity Grammar.

This means the Gifflar Library model doesn't contain the methods createConstructor, setInheritance, createFallback nor createReceive. Also, you must define the value of every state variable created with createVariable function.

import { createGifflarLibrary } from "@gifflar/solgen";

const myLibrary = createGifflarLibrary("MyLibrary");

// Creating a library variable. The variable expression value must me set
myLibrary.createVariable({ regularType: "address" }, "owner", "public", {
  customExpression: "0x123123123...",
});

Writing Library

Same as Gifflar Contract (see Writing Contract).

myLibrary.write();

Compiling Library

Same as Gifflar Contract (see Compiling Contract).

const compiledJson = myLibrary.compile((errors) => {
  if (errors) console.log(errors);
});

Retrieving Library Data

const code = myLibrary.written();
const compiledJson = myLibrary.compiled();

Gifflar Interface

Gifflar Interface is the creationg of a smart contract interface.

Modeling Interface

The interface modelation works almost the same as the Contract (see Modeling Contract), it has the following differences:

  • Interfaces don't contain state variables;
  • Interfaces don't have constructors;
  • Interfaces don't have library definition to a elementary type (e.g. using MyLibrary for uint;);
  • None interface function has a body content.

For more info, see the Solidity Grammar.

This means the Gifflar Library model doesn't contain the methods createConstructor, createUsing, createVariable, createMapping, createFallback nor createReceive. Also, the createFunction scope must be "external".

import { createGifflarInterface } from "@gifflar/solgen";

const myInterface = createGifflarInterface("MyInterface");

// Creating event
myInterface.createEvent("transferedOwnership", [
  { name: "oldOwner", type: { regularType: "address" } },
  { name: "newOwner", type: { regularType: "address" } },
]);

// Creating interface function
myInterface
  .createFunction("transferOwnership", "external") // scope here is optional
  .setInput({ regularType: "address" }, "_newOwner");

Writing Interface

Same as Gifflar Contract (see Writing Contract).

myInterface.write();

Compiling Interface

Same as Gifflar Contract (see Compiling Contract).

const compiledJson = myInterface.compile((errors) => {
  if (errors) console.log(errors);
});

Retrieving Interface Data

const code = myInterface.written();
const compiledJson = myInterface.compiled();

Gifflar Manager

Gifflar Manager allows you to create and manage many smart contracts, libraries and interfaces.

Managing contracts, libraries and interfaces

Instead of creating through createGifflar... function, you can create contracts, libraries and interfaces through the Gifflar Manager. We can call them as top level components. This way you can manage them all by only one object. When creating using Gifflar Manager, it will save every component inside it, so later you can retrieve the components or execute an operation for all components inside the manager.

import { createGifflarManager } from "@gifflar/solgen";

const gifflarManager = createGifflarManager();

// Creating library with manager
const myLibrary = gifflarManager.newLibrary("MyLibrary");

// Creating contract with manager
const myInterface = gifflarManager.newInterface("MyInterface");

// Creating contract with manager
const myContract = gifflarManager.newContract("MyContract");

// Creating a second contract with manager
const mySecondContract = gifflarManager.newContract("MySecondContract");

The modelation phase works the same way as written in Modeling Contract, Modeling Library and Modeling Interface. The difference is that every updates you make in a isolated component, its been saved to the manager, as this component was created by it.

Writing TopLevel Components

You have two options to write the components with Gifflar Manager.

In the first one, you can write using the .writeAll() function. This function will write one single code file containing the code of all components. Note that it will follow the creation order.

const code = gifflarManager.writeAll();

The second option is to give another writing order by sending the list of components to the .write() function. Note that you can send a list of Gifflar Contracts, Gifflar Libraries or Gifflar Interfaces, since the manager treats them generically as Top Level Components.

const code = gifflarManager.write([
  myInterface,
  myLibrary,
  mySecondContract,
  myContract,
]);

Note that in the first option the order will be: myLibrary, myInterface, myContract, mySecondContract. In the second option the order will be: myInterface, myLibrary, mySecondContract, myContract.

Once you wrote the components models with Gifflar Manager, automatically the manager sets each individual code to the respective component. This means, for example, that you should be able to retrieve the myContract code with myContract.written() even if you didn't run myContract.write(), the same for the other written components.

Compiling TopLevel Components

You also have two options when compiling components with Gifflar Manager.

The first option is to compile the code you generated with the Gifflar Manager using the .compileAll method.

const compiledJson = gifflarManager.compileAll((errors) => {
  if (errors) console.log(errors);
});

The second option is to compile the code of only one contract using the .compile method. In this case you must pass the name of the component code to be compiled.

const compiledJson = gifflarManager.compile("MyContract", () => {
  if (errors) console.log(errors);
});

As it happens in Writing TopLevel Components, you should be able, for example, to obtain the myContract compiledJson by running myContract.compiled(), the same for the other compiled components.

Deploying TopLevel Components

The deploy with Gifflar Manager is only individual and works basically the same way as explained in Deploying Contract.

// Define your web3 if needed
gifflarManager.setWeb3(web3);

// Defining default deploy config
gifflarManager.setDeployConfig({
  key: "local_network",
  networkId: 0,
  gas: 3000000,
  gasPrice: "10000000000",
  nodeLink: "http://localhost:8545",
});

// Defining default signer
gifflarManager.addSigner(accountPrivateKey);

Then you run deploy function passing the contract identifier name and the deploy data:

const instance = gifflarManager.deploy(
  "MyContract",
  {
    args: [],
    from: "0x123123123...", // opitional
    gas: 3000000, // opitional
    gasPrice: "10000000000", // opitional
    nonce: 0, // opitional
  },
  "0x321321321..." // opitional
);

Retrieving TopLevel Components Data

You can also retrieve generated data from the Gifflar Manager. You can retrieve complete data or individual data in .written and .compiled cases. In .deployed case, makes sense to only retrieve an individual instance of an already deployed contract.

// Whole manager written code
const code = gifflarManager.written();

// Single component written code
const myContractCode = gifflarManager.written("MyContract");

// Whole compiled json
const compiledJson = gifflarManager.compiled();

// Single component compiled json
const myContractCompiledJson = gifflarManager.compiled("MyContract");

// Instance of a component
const instance = gifflarManager.deployed("MyContract");