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 initSelect "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 luxTestnetInteract
npx hardhat console --network luxTestnetconst 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()); // 42Create an ERC-20 Token
Deploy a custom ERC-20 token with minting and burning capabilities.
Install OpenZeppelin
npm install @openzeppelin/contractsWrite 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 testDeploy
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 luxTestnetBuild 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/cliCreate the Subnet
lux subnet create game-chainWhen 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 --localThe 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 deployoutput - 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 localSubnetCross-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 testnetImport on C-Chain
lux transfer import \
--chain c-chain \
--network testnetVerify the Transfer
lux wallet balance --chain c-chain --network testnetCross-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