Giter Site home page Giter Site logo

broxus / locklift-deploy Goto Github PK

View Code? Open in Web Editor NEW
4.0 5.0 2.0 373 KB

Locklift plugin for replicable deployments and easy testing

License: Apache License 2.0

JavaScript 7.42% TypeScript 92.58%
blockchain everscale javascript locklift locklift-plugin smart-contract-tools smart-contracts solidity tvm typescript

locklift-deploy's Introduction

Locklift-deploy

Logo

GitHub npm

Locklift plugin for deployments management and better testing.

Installation

npm i @broxus/locklift-deploy

And add the following statement to your locklift.config.ts:

import "@broxus/locklift-deploy";
import { Deployments } from "@broxus/locklift-deploy";

declare module "locklift" {
    //@ts-ignore
    export interface Locklift {
        deployments: Deployments<FactorySource>;
    }
}

Quickstart

Before going into the details, let's look at the very basic functionality of locklift-deploy.

locklift-deploy allows you to write deploy scripts in the deploy folder. Each of these files that look as follows will be executed in turn when you execute the following task: locklift -n <networkName> deploy

// deploy/00-deploy-sample.ts
export default async () => {
	const signer = await locklift.keystore.getSigner('0');
    await locklift.deployments.deploy({
            deployConfig: {
                contract: "Sample",
                publicKey: signer.publicKey,
                initParams: { _nonce: locklift.utils.getRandomNonce() },
                constructorParams: { _state: 123 },
                value: locklift.utils.toNano(2)
            },
            deploymentName: "Sample1",// user-defined custom name
        },
        true // enable logs
    );
};

export const tag = "sample1";

Furthermore you can also ensure these scripts are executed in test too by calling await deployments.fixture({include: ['sample1']}) in your test.

This is a huge benefit for testing since you are not required to replicate the deployment procedure in your tests. The tag feature (as seen in the script above) and dependencies will also make your life easier when writing complex deployment procedures.

You can even group deploy scripts in different sub folder and ensure they are executed in their logical order.

Furthermore locklift-deploy can also support a multi-chain settings with multiple deploy folder specific to each network.

Configuration

Extra locklift.config networks' options

deploy

The deploy field override the deploy option and let you define a set of sub-folders containing the deploy scripts to be executed for exact network.

You can thus have one network that will be executing mainnet deployment and other networks deployments, etc.

You could also have a folder that deploy contracts that are live on mainnet but that you need to replicate for your test or local network.

{
  networks: {
    local: {
      deploy: [ 'common/', 'deploy-local/']
    },
    test: {
      deploy: ['common/', 'deploy-test/']
    },
    main: {
      deploy: [ 'deploy-main/' ]
    }
  }
}

In this case, the project structure might look like this:

///
deploy
  ├── deploy-local
  │    └── local-migration-1.ts
  │    └── local-migration-2.ts
  ├── common 
  │    └── common-migration.ts
  ├── deploy-test
  │    └── test-migration.ts
  └── deploy-main
       └── main-migration.ts

How to Deploy Contracts

The deploy command

locklift deploy --network <networkName> [options and flags]

This is a new task that the locklift-deploy adds. As the name suggests it deploys contracts. To be exact it will look for files in the folder deploy or whatever was configured in networks.<networkName>.deploy, see config.

It will scan for files in alphabetical order and execute them in turn.

Options

--tags <tags>: only execute deploy scripts with the given tags (separated by whitespaces) and their dependencies (see more info here about tags and dependencies)

Deploy scripts

The deploy scripts need to be of the following type :

export default async () => {
    // out deployment code
};
export const tag = "sample1";
// optional
export const dependencies = ["sample2", "sample3", "sample4"];

The tags is a list of string that when the deploy command is executed with, the script will be executed. In other word if the deploy command is executed with a tag that does not belong to that script, that script will not be executed unless it is a dependency of a script that does get executed.

The dependencies is a list of tag that will be executed if that script is executed. So if the script is executed, every script whose tag match any of the dependencies will be executed first.

These set of fields allow more flexibility to organize the scripts. You are not limited to alphabetical order and you can even organise deploy script in sub folders.

Deploying and retrieving contracts

Contracts could be easily deployed and saved for further usage via locklift.deployments.deploy function:

// deploy/00-deploy-sample.ts
...
    const signer = (await locklift.keystore.getSigner('0'))!;
    await locklift.deployments.deploy({
            // We use same config for regular locklift factory deployments
            deployConfig: {
                contract: "Sample",
                publicKey: signer.publicKey,
                initParams: { _nonce: locklift.utils.getRandomNonce() },
                constructorParams: { _state: 123 },
                value: locklift.utils.toNano(2)
            },
            deploymentName: "Sample1",// user-defined custom name
        },
        true // enable logs
    );
...

All deploy artifacts are saved to disk now, so that you can get instance of deployed contract via deployments.getContract<ContractAbi>(deploymentName) in any other script:

// 01-use-sample.ts
...
const sample = locklift.deployments.getContract<SampleAbi>("Sample1");
...

Deploying and retrieving accounts

Accounts could be easily deployed and saved for further usage via locklift.deployments.deployAccounts function:

// deploy/02-deploy-account.ts
...
// multiple accounts could be deployed at once
await locklift.deployments.deployAccounts([
      {
         deploymentName: "Deployer", // user-defined custom account name
         signerId: "0", // locklift.keystore.getSigner("0") <- id for getting access to the signer
         accountSettings: {
            type: WalletTypes.EverWallet,
            value: locklift.utils.toNano(2),
         },
      },
   ],
   true // enableLogs
);
...

All deploy artifacts are saved to disk now, so that you can get instance of deployed account via deployments.getAccount(deploymentName) in any other script:

// 03-use-account.ts
...
const deployer = locklift.deployments.getAccount("Deployer").account;
...

Saving external contracts to deployments

Sometimes we want to use contract that was deployed outside of our scripts or was deployed by internal message, for example through some kind of "factory" contract. In such a case we can use low-level method for manual saving deployment artifact:

// save arbitrary contract
locklift.deployments.saveContract({
    deploymentName: "FarmingPool_WEVER-USDT",
    contractName: "FarmingPool",
    address: SOME_ADDRESS
});

// save account
locklift.deployments.saveAccount({
    deploymentName: "Owner",
    signerId: "0", // locklift.keystore.getSigner("0") <--
    address: SOME_ADDRESS,
    accountSettings: {
    	type: WalletTypes.EverWallet,
    	// mSigType: "SafeMultisig/multisig2" for multisig types
	}
});

Testing Deployed Contracts

You can continue using the usual test command:

locklift test

Tests can use the locklift.deployments.fixture(config?: { include?: Array<string>; exclude?: Array<string> }) function to run the deployment. You can also choose what tags you want/don't want to execute if you want.

Here is an example of a test:

describe('Token', () => {
  it('testing 1 2 3', async function () {
    // execute only 'token-deploy' tag
    await locklift.deployments.fixture({include: ['token-deploy']});
    const token = await locklift.deployments.getContract<TokenAbi>('Token'); // Token is available because the fixture was executed
    console.log(token.address);
  });
    
  it('testing 4 5 6', async function () {
    // execute all tags except 'token-deploy'
    await locklift.deployments.fixture({exclude: ['token-deploy']});
    ...
  });
});

Tags and Dependencies

It is possible to execute only specific parts of the deployments with locklift deploy --tags <tags>

Tags represent what the deploy script acts on. In general it will be a single string value, the name of the contract it deploys or modifies.

Then if another deploy script has such tag as a dependency, then when this latter deploy script has a specific tag and that tag is requested, the dependency will be executed first.

Here is an example of two deploy scripts :

export default async () => {
   await locklift.deployments.deployAccounts([
          {
             deploymentName: "Deployer",
             signerId: "0",
             accountSettings: {
                type: WalletTypes.EverWallet,
                value: toNano(10),
             },
          },
       ],
       true // enableLogs
   );
};

export const tag = "create-account";
export default async () => {
    const deployer = locklift.deployments.getAccount("Deployer").account;
    await locklift.deployments.deploy({
            deployConfig: {
                contract: "Sample",
                publicKey: deployer.signer.publicKey,
                initParams: { _nonce: locklift.utils.getRandomNonce() },
                constructorParams: { _state: 123 },
                value: locklift.utils.toNano(2)
            },
            deploymentName: "Sample1",// user-defined custom name
        },
        true // enable logs
    );
};

export const tag = "sample1";
// this ensure the 'create-account' script above is executed first, so `deployments.getAccount('Deployer')` succeeds
export const dependencies = ["create-account"]; 

As you can see the second one depends on the first. This is because the second script depends on a tag that the first script registers as using.

locklift-deploy's People

Contributors

30mb1 avatar karneges avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

locklift-deploy's Issues

proposal: show human readable WalletType in logs for account deploy

Current behavior:

logger prints numeric value of enum type:
Account type 0 deployed, address: 0:33f60d7f83aa407cd10bff7eb7e4f0ad44754e76d8d6dc59537a24143006d178, deploymentName: User

Description: It will be more informative to show enum key instead of value for devs

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.