Lux Docs
Build

Tutorials

Step-by-step guides for building on Lux Network

Tutorials

Hands-on guides to get you building on Lux Network. Each tutorial walks through a complete workflow from setup to deployment.

Deploy Your First Contract

Deploy a simple storage contract to Lux C-Chain testnet using Hardhat.

Set Up the Project

mkdir lux-hello && cd lux-hello
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init

Select "Create a JavaScript project" when prompted.

Write the Contract

Replace contracts/Lock.sol with contracts/Storage.sol:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Storage {
    uint256 private value;

    event ValueChanged(uint256 newValue);

    function store(uint256 newValue) public {
        value = newValue;
        emit ValueChanged(newValue);
    }

    function retrieve() public view returns (uint256) {
        return value;
    }
}

Configure Lux Testnet

Update hardhat.config.js:

require("@nomicfoundation/hardhat-toolbox");

module.exports = {
  solidity: "0.8.24",
  networks: {
    luxTestnet: {
      url: "https://api.testnet.lux.network/ext/bc/C/rpc",
      chainId: 96368,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};

Get Testnet LUX

Request tokens from the testnet faucet:

lux faucet request --network testnet --address <your-c-chain-address>

Deploy

Create scripts/deploy.js:

const { ethers } = require("hardhat");

async function main() {
  const Storage = await ethers.getContractFactory("Storage");
  const storage = await Storage.deploy();
  await storage.waitForDeployment();
  console.log("Storage deployed to:", await storage.getAddress());
}

main().catch(console.error);
npx hardhat run scripts/deploy.js --network luxTestnet

Interact

npx hardhat console --network luxTestnet
const Storage = await ethers.getContractFactory("Storage");
const storage = Storage.attach("0xYourDeployedAddress");

await storage.store(42);
const value = await storage.retrieve();
console.log("Stored value:", value.toString()); // 42

Create an ERC-20 Token

Deploy a custom ERC-20 token with minting and burning capabilities.

Install OpenZeppelin

npm install @openzeppelin/contracts

Write the Token Contract

Create contracts/LuxToken.sol:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract LuxToken is ERC20, ERC20Burnable, Ownable {
    constructor(
        string memory name,
        string memory symbol,
        uint256 initialSupply
    ) ERC20(name, symbol) Ownable(msg.sender) {
        _mint(msg.sender, initialSupply * 10 ** decimals());
    }

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}

Write Tests

Create test/LuxToken.test.js:

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("LuxToken", function () {
  let token, owner, addr1;

  beforeEach(async function () {
    [owner, addr1] = await ethers.getSigners();
    const LuxToken = await ethers.getContractFactory("LuxToken");
    token = await LuxToken.deploy("LuxToken", "LUXT", 1000000);
  });

  it("should assign total supply to owner", async function () {
    const balance = await token.balanceOf(owner.address);
    expect(await token.totalSupply()).to.equal(balance);
  });

  it("should allow owner to mint", async function () {
    await token.mint(addr1.address, 1000);
    expect(await token.balanceOf(addr1.address)).to.equal(1000);
  });

  it("should allow token burning", async function () {
    const amount = ethers.parseEther("100");
    await token.burn(amount);
    const supply = await token.totalSupply();
    expect(supply).to.equal(ethers.parseEther("999900"));
  });
});
npx hardhat test

Deploy

Create scripts/deployToken.js:

const { ethers } = require("hardhat");

async function main() {
  const LuxToken = await ethers.getContractFactory("LuxToken");
  const token = await LuxToken.deploy("MyToken", "MTK", 1000000);
  await token.waitForDeployment();
  console.log("Token deployed to:", await token.getAddress());
}

main().catch(console.error);
npx hardhat run scripts/deployToken.js --network luxTestnet

Build a Simple DeFi Vault

Create a vault contract that accepts LUX deposits and tracks shares.

Write the Vault Contract

Create contracts/Vault.sol:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Vault is ERC20 {
    IERC20 public immutable token;

    constructor(IERC20 _token) ERC20("Vault Shares", "vSHARE") {
        token = _token;
    }

    function deposit(uint256 amount) external {
        uint256 shares;
        if (totalSupply() == 0) {
            shares = amount;
        } else {
            shares = (amount * totalSupply()) / token.balanceOf(address(this));
        }
        token.transferFrom(msg.sender, address(this), amount);
        _mint(msg.sender, shares);
    }

    function withdraw(uint256 shares) external {
        uint256 amount = (shares * token.balanceOf(address(this))) / totalSupply();
        _burn(msg.sender, shares);
        token.transfer(msg.sender, amount);
    }
}

Test the Vault

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Vault", function () {
  it("should mint shares on deposit", async function () {
    const [owner] = await ethers.getSigners();

    const Token = await ethers.getContractFactory("LuxToken");
    const token = await Token.deploy("Test", "TST", 1000000);

    const Vault = await ethers.getContractFactory("Vault");
    const vault = await Vault.deploy(await token.getAddress());

    const amount = ethers.parseEther("1000");
    await token.approve(await vault.getAddress(), amount);
    await vault.deposit(amount);

    expect(await vault.balanceOf(owner.address)).to.equal(amount);
  });
});

Create a Custom Subnet

Deploy your own subnet with a custom gas token and fee structure.

Install the CLI

npm install -g @luxfi/cli

Create the Subnet

lux subnet create game-chain

When prompted, configure:

  • VM: Subnet-EVM
  • Chain ID: 99999 (or any unique ID)
  • Gas token symbol: GAME
  • Initial supply: 1,000,000 GAME
  • Airdrop address: Your wallet address

Deploy Locally

lux subnet deploy game-chain --local

The CLI will output your subnet's RPC URL and chain ID.

Connect with MetaMask

Add the custom network to MetaMask:

  • RPC URL: Provided by lux subnet deploy output
  • Chain ID: 99999
  • Symbol: GAME

Deploy Contracts to Your Subnet

Update your Hardhat config with the local subnet RPC and deploy as usual:

npx hardhat run scripts/deploy.js --network localSubnet

Cross-Chain Asset Transfer

Move LUX between the X-Chain, P-Chain, and C-Chain.

Export from X-Chain to C-Chain

lux transfer export \
  --source x-chain \
  --destination c-chain \
  --amount 10 \
  --network testnet

Import on C-Chain

lux transfer import \
  --chain c-chain \
  --network testnet

Verify the Transfer

lux wallet balance --chain c-chain --network testnet

Cross-chain transfers require a small fee on each chain. The transfer is atomic -- it either completes on both chains or neither.

Further Reading

  • Smart Contracts - Contract development reference
  • SDKs - Interact with contracts programmatically
  • Subnets - Full subnet configuration reference

On this page