MCPcopy Index your code
hub / github.com/broxus/locklift

github.com/broxus/locklift @v2.9.6

Chat with this repo
repository ↗ · DeepWiki ↗ · release v2.9.6 ↗ · + Follow
300 symbols 697 edges 84 files 1 documented · 0%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

locklift logo

Development environment for TVM blockchain.

    <a href="https://github.com/broxus/locklift/raw/v2.9.6/LICENSE">
        <img alt="GitHub" src="https://img.shields.io/badge/license-Apache--2.0-orange" />
    </a>
    <a href="https://www.npmjs.com/package/locklift">
        <img alt="npm" src="https://img.shields.io/npm/v/locklift">
    </a>

Locklift is a development environment aiming to help you with TVM contracts development. With Locklift, you get:

  • Network management for working with any networks (main, test, local, ...)
  • Automated contract testing with Mocha
  • Handy wrapper around TVM smart contract
  • Custom givers support
  • Keys management
  • External script runner that executes scripts within a specified environment

Quick start

To install Locklift you need node 14 or later. Go to an empty folder, initialize an npm project (i.e. npm init), and run

npm install --save-dev locklift

Once it's installed you can initialize the project

// initialize in current directory
npx locklift init -f
// or specify new one
npx locklift init --path amazing-locklift-project

Get version

npx locklift --version

CLI docs

This section describes the set of commands, supported by the locklift package.

Initialize Locklift package

npx locklift init --path amazing-locklift-project
# New Locklift project initialized in amazing-locklift-project

This command initializes new Locklift project, filled with samples:

├── contracts
│   └── Sample.sol
├── locklift.config.ts
├── scripts
│   └── 1-deploy-sample.ts
└── test
    └── sample-test.ts

Other flags

-f, --force - force run the init command (in case you have any files in the target directory);

Configuration

By default, the configuration file is called locklift.config.ts. Here's the basic layout:

const LOCAL_NETWORK_ENDPOINT = "http://localhost/graphql";

const config: LockliftConfig = {
  compiler: {
    // Specify path to your TON-Solidity-Compiler
    // path: "/mnt/o/projects/broxus/TON-Solidity-Compiler/build/solc/solc",

    // Or specify version of compiler
    version: "0.61.2",

    // Specify config for extarnal contracts as in exapmple
    // This filed for generating types only
    // externalContracts: {
    //   "node_modules/broxus-ton-tokens-contracts/build": ['TokenRoot', 'TokenWallet']
    // }

    // Additional comiler params can be added via this field
    // compilerParams: ["--tvm-version ton"],
  },
  linker: {
    // Specify path to your stdlib
    // lib: "/mnt/o/projects/broxus/TON-Solidity-Compiler/lib/stdlib_sol.tvm",
    // // Specify path to your Linker
    // path: "/mnt/o/projects/broxus/TVM-linker/target/release/tvm_linker",

    // Or specify version of linker
    version: "0.15.48",
  },
  networks: {
    locklift: {
      blockchainConfig: "TON", // or other presets, or we can provide our custom config by {cunstom: "MY BLOCKCHAIN CONFIG"}
      connection: {
        id: 1001,
        // @ts-ignore
        type: "proxy",
        // @ts-ignore
        data: {},
      },
      keys: {
        // Use everdev to generate your phrase
        // !!! Never commit it in your repos !!!
        // phrase: "action inject penalty envelope rabbit element slim tornado dinner pizza off blood",
        amount: 20,
      },
    },
    local: {
      // Specify connection settings for https://github.com/broxus/everscale-standalone-client/
      connection: {
        group: "localnet",
        type: "graphql",
        data: {
          endpoints: [LOCAL_NETWORK_ENDPOINT],
          local: true,
        },
      },
      // This giver is the default local-node giverV2
      giver: {
        // Check if you need to provide a custom giver
        // giverFactory: (ever, keyPair, address) => new SimpleGiver(ever, keyPair, address),
        address: "0:ece57bcc6c530283becbbd8a3b24d3c5987cdddc3c8b7b33be6e4a6312490415",
        key: "172af540e43a524763dd53b26a066d472a97c4de37d5498170564510608250c3",
      },
      tracing: {
        endpoint: LOCAL_NETWORK_ENDPOINT,
      },
      keys: {
        // Use everdev to generate your phrase
        // !!! Never commit it in your repos !!!
        // phrase: "action inject penalty envelope rabbit element slim tornado dinner pizza off blood",
        amount: 20,
      },
    },
    mainnet: {
      // Specify connection settings for https://github.com/broxus/everscale-standalone-client/
      connection: "mainnet",
      // Here, default SafeMultisig wallet is used as a giver
      giver: {
        address: "0:ece57bcc6c530283becbbd8a3b24d3c5987cdddc3c8b7b33be6e4a6312490415",
        // you can use bip39 phrase instead of key
        phrase: "action inject penalty envelope rabbit element slim tornado dinner pizza off blood",
        accountId: 0,
      },
      keys: {
        // Use everdev to generate your phrase
        // !!! Never commit it in your repos !!!
        // phrase: "action inject penalty envelope rabbit element slim tornado dinner pizza off blood",
        amount: 20,
      },
    },
  },
  // you can use any settings that mocha framework support
  mocha: {
    timeout: 2000000,
  },
};

Note about networks.locklift.blockchainConfig

This field is used to specify the blockchain config. It can be one of the following:

blockchainConfig: "EVER" | "TON" | { custom: string } | undefined;

For example we can get our blockchain config from explorers. Lets see an example of tycho config

Note about Giver settings:

Let's look at networks.giver this is giver settings filed. All known wallets and givers will be detected automatically e.g. EverWallet or GiverV2 (from local node). You only need to provide giver credentials - (address, secret key, or phrase with account id). But if you want to use something custom you will need to provide giverFactory callback for networks.giver.giverFactory that callback should return something that implements Giver interface

Getting code of contract

This command will show you the contract code

npx locklift code -c <contract name>

Getting storage fee of contract

This command will show you storage fee for the given period of time

npx locklift fee -a <contract address> -n main

Build contracts

This command uses the specified TON Solidity compiler and TVM linker to build all project contracts.

npx locklift build
Found 1 sources
Building contracts/Sample.sol
Compiled contracts/Sample.sol
Linked contracts/Sample.sol

Test contracts

This command runs the project Mocha tests, test folder by default. The locklift object will be set up and included automatically, you don't need to import it manually.

npx locklift test --network local
  Test Sample contract
    Contracts
      ✓ Load contract factory
      ✓ Deploy contract (1491ms)
      ✓ Interact with contract (1110ms)


  3 passing (3s)

Debugging

You can print to console in contracts with special library:

import "locklift/src/console.sol";

contract Sample {
  function testFunc(uint input) external {
    tvm.accept();

    console.log(format("You called testFunc with input = {}", input));
  }
}

Note: console.log functionality working only with tracing e.g:

await lockLift.tracing.trace(sampleContract.testFunc({ input: 10 }).sendExternal({ pubkey: keyPair.publicKey }));

And then you will see this in your terminal:

You called testFunc with input = 10

Note the console.log is just an event, so if your message dropped on the computed phase (e.g. required didn't pass), you will not see the log message.

Tracing

The tracing module scans the message tree, determines which contracts have been deployed, and decodes all method calls. In case of an error in some section of the execution graph, tracing will show the chain of calls that led to the error, as well as the error itself.

Note: If you want to use tracing be sure to provide a tracing endpoint to the config that supports graphql

// trace deploy
const {contract: deployedContractInstance, tx} = await locklift.tracing.trace(locklift.factory.deployContract(...))
// trace simple transaction
const changeStateTransaction = await locklift.tracing.trace(MyContract.methods.changeCounterState({newState: 10}).sendExternal({publicKey: signer.publicKey}))
// trace runTarget
const accountTransaction = await locklift.tracing.trace(myAccount.runTarget(...))

example with tracing output

npx locklift test -n local
...

 #1 action out of 1
Addr: 0:785ea492db0bc46e370d9ef3a0cc23fb86f7a734ac7948bb50e25b51b2455de0
MsgId: 963a963f227d69f2845265335ecee99052411204b767be441755796cc28482f4
-----------------------------------------------------------------
TokenWallet.transfer{value: 4.998, bounce: true}(
    amount: 100
    recipient: 0:5d0075f4d3b14edb87f78c5928fbaff7aa769a49eedc7368c33c95a6d63bbf17
    deployWalletValue: 0
    remainingGasTo: 0:bb0e7143ca4c16a717733ff4a943767efcb4796dd1d808e027f39e7712745efc
    notify: true
    payload: te6ccgEBAQEAKAAAS4AXvOIJRF0kuLdJrf7QNzLzvROSLywJoUpcj6w7WfXqVCAAAAAQ
)
        ⬇
        ⬇
    #1 action out of 1
Addr: 0:b00ef94c1a23a48e14cdd12a689a3f942e8b616d061d74a017385f6edc704588
MsgId: bcbe2fb9efd98efe02a6cb6452f38f3dce364b5480b7352000a32f7bdfde949a
-----------------------------------------------------------------
TokenWallet.acceptTransfer{value: 4.978, bounce: true}(
    amount: 100
    sender: 0:bb0e7143ca4c16a717733ff4a943767efcb4796dd1d808e027f39e7712745efc
    remainingGasTo: 0:bb0e7143ca4c16a717733ff4a943767efcb4796dd1d808e027f39e7712745efc
    notify: true
    payload: te6ccgEBAQEAKAAAS4AXvOIJRF0kuLdJrf7QNzLzvROSLywJoUpcj6w7WfXqVCAAAAAQ
)
        ⬇
        ⬇
    #1 action out of 1
Addr: 0:5d0075f4d3b14edb87f78c5928fbaff7aa769a49eedc7368c33c95a6d63bbf17
MsgId: 99034783340906fb5b9eb9a379e1fcb08887992ed0183da78e363ef694ba7c52
-----------------------------------------------------------------
EverFarmPool.onAcceptTokensTransfer{value: 4.952, bounce: false}(
    tokenRoot: 0:c87f8def8ff9ab121eeeb533dc813908ec69e420101bda70d64e33e359f13e75
    amount: 100
    sender: 0:bb0e7143ca4c16a717733ff4a943767efcb4796dd1d808e027f39e7712745efc
    senderWallet: 0:785ea492db0bc46e370d9ef3a0cc23fb86f7a734ac7948bb50e25b51b2455de0
    remainingGasTo: 0:bb0e7143ca4c16a717733ff4a943767efcb4796dd1d808e027f39e7712745efc
    payload: te6ccgEBAQEAKAAAS4AXvOIJRF0kuLdJrf7QNzLzvROSLywJoUpcj6w7WfXqVCAAAAAQ
)
 !!! Reverted with 1233 error code on compute phase !!!

Ignoring errors

By default, tracing will throw an error on any non-zero code in the execution graph, but sometimes in contract, we can expect some specific errors that could be processed later with bounced msgs. In this cases, we don't want tracing to throw errors, because such behavior is expected. We can tell tracing to ignore specific errors on compute or action phases.

We can ignore errors on specific call:

// Tracing will ignore all 51 and 60 errors on compute phase + 30 error on action phase
// Here 51 compute and 30 action errors will be ignored for all transacions in msg chain and 60 compute error
// will be ignored only on specific address
const transaction = await locklift.tracing.trace(
  tokenRoot.methods.sendTokens({ walletOwner: "" }).sendExternal({ publicKey: signer.publicKey }),
  {
    allowedCodes: {
      //compute or action phase for all contracts
      compute: [40],
      //also you can specify allowed codes for specific contract
      contracts: {
        [someAddress.toString()]: {
          action: [52, 60],
        },
      },
    },
  },
);

Or set ignoring by default for all further calls:

// ignore compute(or acton) phase errors for all transactions
locklift.tracing.setAllowedCodes({ compute: [52, 60] });
// ignore more errors for specific address
locklift.tracing.setAllowedCodesForAddress(SOME_ADDRESS, { compute: [123], action: [111] });

// remove code from a default list of ignored errors so that only 51 errors will be ignored
// this affects only global rules, per-address rules are not modified
locklift.tracing.removeAllowedCodes({ compute: [60] });
// remove code from deault list of ignored errors for specific address
locklift.tracing.removeAllowedCodesForAddress(SOME_ADDRESS, { compute: [123] });

Tracing features (EXPERIMENTAL FEATURES)

For using this feature first of all need to wrap the transaction by tracing, and make sure that tracing is enabled. Otherwise, the traceTree will be undefined

example

const { traceTree } = await locklift.tracing.trace(myContract.method.myMethod().send());

tracingTree methods

  1. Beauty print

example

await traceTree?.beautyPrint();

example output

```shell CALL Wallet.sendTransaction{valueReceive: 0,valueSent: 3.9, rest: -3.910662⮯, totalFees: 0.010662}(dest="StEverVault(0:5db...a9bcf)", value="3900000000", bounce=true, flags="3", payload="te6c...pQ==") CALL StEverVault.startEmergencyProcess{valueReceive: 3.9,valueSent: 3.867427, rest: 0⮬, totalFees: 0.032573}(_poofNonce="50341") CALL StEverAccount.onStartEmergency{valueReceive: 3.867427,valueSent: 3.857346, rest: 0⮬, totalFees: 0.010081}(_proofNonce="50341") CALL StEverVault.startEmergencyRejected{valueReceive: 3.857346,valueSent: 3.831589, rest: 0⮬, totalFees: 0.025757}(_user="Wallet(0:ae6...10a56)", errcode="2004") EVENT StEverVault.EmergencyProcessRejectedByAccount(e

Extension points exported contracts — how you extend this code

TracingTransportConnection (Interface)
(no doc) [7 implementers]
src/internal/tracing/types.ts
Assertion (Interface)
(no doc)
src/chaiPlugin/types.ts
Giver (Interface)
(no doc) [4 implementers]
src/internal/factory/giver.ts
AsyncAssertion (Interface)
(no doc)
src/chaiPlugin/types.ts
SourceFetcher (Interface)
(no doc) [4 implementers]
src/internal/network/forkService/stateFetchers.ts
EmitAssertion (Interface)
(no doc)
src/chaiPlugin/types.ts
LockliftConfig (Interface)
(no doc)
src/internal/config/index.ts
NetworkValue (Interface)
(no doc)
src/internal/config/index.ts

Core symbols most depended-on inside this repo

printInfo
called by 21
src/internal/logger/index.ts
printTracingLog
called by 20
src/internal/logger/index.ts
loadConfig
called by 14
src/internal/config/index.ts
printError
called by 12
src/internal/logger/index.ts
printWarn
called by 9
src/internal/logger/index.ts
replaceDots
called by 7
src/internal/compilerComponentsStore/utils.ts
convert
called by 7
src/internal/tracing/utils.ts
extractStringAddress
called by 7
src/internal/tracing/utils.ts

Shape

Function 119
Method 105
Class 58
Enum 10
Interface 8

Languages

TypeScript100%

Modules by API surface

src/utils/index.ts15 symbols
src/internal/tracing/utils.ts15 symbols
src/internal/compilerComponentsStore/utils.ts15 symbols
src/internal/buildCache/index.ts15 symbols
src/internal/tracing/tracingInternal.ts14 symbols
src/internal/network/forkService/stateFetchers.ts12 symbols
src/internal/cli/builder/utils.ts11 symbols
src/index.ts11 symbols
src/internal/logger/index.ts9 symbols
src/internal/tracing/transport/index.ts8 symbols
src/internal/tracing/trace/trace.ts8 symbols
src/internal/giver/giverOptions.ts8 symbols

For agents

$ claude mcp add locklift \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact