Initialize Contract
The 1st step is to initialize your new contract. xSuite makes initialization super simple.
To create a new blank contract:
xsuite new --dir <DIR>
To create a new contract from a starter contract (audited by Arda):
xsuite new --starter <STARTER> --dir <DIR>
Starter contracts are a great starting point for your new smart contract. They will save you significant time setting up the codebase and writing the initial logic. They are audited by Arda. Available starter contracts:
-
blank (opens in a new tab): An empty contract that comes fully set up with tests and blockchain interactions.
xsuite new --starter blank --dir <DIR>
-
vested-transfers (opens in a new tab): A contract for vested transfers, with tests and blockchain interactions.
xsuite new --starter vested-transfers --dir <DIR>
Manual setup
You can also setup your new contract without using the CLI.
Create a new directory and init a package there:
mkdir my-contract
cd my-contract
npm init -y
Add xsuite
package and we also recommend adding TypeScript (typescript
to type-check and tsx
to run .ts
files):
npm i -D xsuite typescript tsx
Building and testing
Add vitest
package to run tests:
npm i -D vitest
Add the following scripts
in the package.json
file:
"scripts": {
"build": "xsuite build",
"test": "vitest run",
"typecheck": "tsc --noEmit"
},
Create the tests
directory:
mkdir tests
Create the first test file under tests/contract.test.ts
, with the following content:
import { test, beforeEach, afterEach } from "vitest";
import { assertAccount, LSWorld, LSWallet, LSContract } from "xsuite";
let world: LSWorld;
let deployer: LSWallet;
let contract: LSContract;
beforeEach(async () => {
world = await LSWorld.start();
deployer = await world.createWallet();
({ contract } = await deployer.deployContract({
code: "file:output/contract.wasm",
codeMetadata: [],
gasLimit: 10_000_000,
}));
});
afterEach(async () => {
world.terminate();
});
test("Test", async () => {
assertAccount(await contract.getAccount(), {
balance: 0n,
kvs: [],
});
});
At this point, you can build the contract using npm run build
and run tests with npm run test
.
Interacting
Interactions use commander
package to easily declare CLI commands:
npm i -D commander
Add the following scripts in the scripts
of the package.json
file:
"interact:devnet": "CHAIN=devnet tsx interact/index.ts",
"interact:testnet": "CHAIN=testnet tsx interact/index.ts",
"interact:mainnet": "CHAIN=mainnet tsx interact/index.ts",
Create the interact/data.json
file with the following content:
{
"code": "file:output/contract.wasm",
"address": {
"devnet": "",
"testnet": "",
"mainnet": ""
}
}
Create the interact/index.ts
file with the following content:
import { Command } from "commander";
import { envChain, World } from "xsuite";
import data from "./data.json";
const world = World.new({
chainId: envChain.id(),
});
const loadWallet = () => world.newWalletFromFile("wallet.json");
const program = new Command();
program.command("deploy").action(async () => {
const wallet = await loadWallet();
const result = await wallet.deployContract({
code: data.code,
codeMetadata: ["upgradeable"],
gasLimit: 20_000_000,
});
console.log("Result:", result);
});
program.command("upgrade").action(async () => {
const wallet = await loadWallet();
const result = await wallet.upgradeContract({
callee: envChain.select(data.address),
code: data.code,
codeMetadata: ["upgradeable"],
gasLimit: 20_000_000,
});
console.log("Result:", result);
});
program.command("ClaimDeveloperRewards").action(async () => {
const wallet = await loadWallet();
const result = await wallet.callContract({
callee: envChain.select(data.address),
funcName: "ClaimDeveloperRewards",
gasLimit: 10_000_000,
});
console.log("Result:", result);
});
program.parse(process.argv);
At this point, you should be able to run interactions. For example, if you want to deploy the contract on devnet: npm run interact:devnet deploy
.
Note: The interactions use a wallet.json
file which can be generated using xsuite new-wallet --wallet wallet.json
.