Skip to content

Baliram77/RootVault_Lab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Yul & Huff Contracts on Rootstock

A complete implementation of native RBTC Vault smart contracts written in Yul and Huff, deployable on Rootstock, built with Foundry. No Solidity for contract logic—only low-level EVM assembly.

Overview

This tutorial demonstrates how to write, compile, and deploy Yul and Huff smart contracts on Rootstock. Rootstock is fully EVM-compatible, so standard EVM bytecode runs without modification. While most documentation focuses on Solidity, this project shows that Rootstock executes raw EVM bytecode from low-level languages, enabling gas-optimized and size-critical contracts on Bitcoin-secured infrastructure.

Key Benefits

  • No Solidity Logic: Contract behavior is implemented entirely in Yul (Solidity’s assembly) and Huff (macro-based assembly)
  • Gas & Size Control: Full control over opcodes and bytecode layout for optimization
  • Same Interface: Both implementations expose the same IVault interface (deposit, withdraw, balanceOf)
  • Rootstock Native: Standard EVM bytecode; deploy once to Rootstock Testnet or Mainnet
  • Tested with Foundry: Solidity used only for tests and deployment scripts

Architecture

┌─────────────────┐     ┌─────────────────┐
│   Vault.yul     │     │  Vault.huff     │
│ (strict asm)    │     │ (macros+dispatch)│
└────────┬────────┘     └────────┬────────┘
         │                        │
         │  solc --strict-assembly│  huffc --bin-runtime
         │                        │
         ▼                        ▼
┌─────────────────┐     ┌─────────────────┐
│ vault_yul.bin   │     │ vault_huff.bin  │
│ (creation code) │     │ (creation code) │
└────────┬────────┘     └────────┬────────┘
         │                        │
         └────────────┬───────────┘
                      │
                      │  forge script (--broadcast)
                      ▼
              ┌───────────────┐
              │   Rootstock   │
              │  (Chain ID 31)│
              │  Testnet /    │
              │  Mainnet      │
              └───────────────┘

Project Structure

RootVault_Lab/
├── src/
│   ├── yul/
│   │   └── Vault.yul          # Yul Vault (strict assembly)
│   ├── huff/
│   │   └── Vault.huff         # Huff Vault (macros + manual selector dispatch)
│   └── IVault.sol             # Shared interface for tests and integration
├── script/
│   ├── compile.sh             # Builds out/vault_yul.bin, out/vault_huff.bin
│   ├── DeployYulVault.s.sol   # Deploy Yul Vault to Rootstock
│   └── DeployHuffVault.s.sol   # Deploy Huff Vault to Rootstock
├── test/
│   ├── Deployer.sol            # Deploys raw bytecode (used by tests)
│   ├── YulVault.t.sol          # Full test suite for Yul Vault
│   └── HuffVault.t.sol        # Gas tests for Huff Vault
├── out/                        # Compiled bytecode (generated by compile.sh)
├── foundry.toml                # Foundry config + Rootstock RPC endpoint
└── .env.example                # Environment template for deployment

Prerequisites

  • Foundry installed
  • solc (Solidity compiler) for compiling Yul
  • huffc (Huff compiler) for compiling Huff—see Installing huffc below
  • A Rootstock testnet RPC endpoint (for deployment)

Installation

  1. Clone the repository:
git clone https://github.com/<your-username>/RootVault_Lab.git
cd RootVault_Lab
  1. Install Forge dependencies (if any):
forge build
  1. Compile Yul and Huff to bytecode (required before tests or deploy):
chmod +x script/compile.sh
./script/compile.sh

This produces out/vault_yul.bin, out/vault_yul.hex, out/vault_huff.bin, and out/vault_huff.hex. Yul compilation requires solc on your PATH; Huff requires huffc.

Installing huffc

You need huffc only to compile the Huff Vault and run Huff tests.

Option A — Official installer

curl -L get.huff.sh | bash
source ~/.bashrc   # or source ~/.zshrc
huffup
huffc --help      # verify

Option B — From source (Rust required)

cargo install --git https://github.com/huff-language/huff-rs.git huff_cli --bins --locked

Quick Start

1. Run Tests

After running ./script/compile.sh, test both vaults locally:

forge test

For verbose output:

forge test -vv

With gas report:

forge test --gas-report

2. Deploy to Rootstock Testnet

Set up environment variables:

cp .env.example .env
# Edit .env: set ROOTSTOCK_RPC_URL and PRIVATE_KEY

Get testnet RBTC from the Rootstock Faucet.

Deploy Yul Vault:

source .env
forge script script/DeployYulVault.s.sol:DeployYulVaultScript \
  --rpc-url $ROOTSTOCK_RPC_URL \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --chain-id 31 \
  --legacy

Deploy Huff Vault (after ./script/compile.sh with huffc installed):

source .env
forge script script/DeployHuffVault.s.sol:DeployHuffVaultScript \
  --rpc-url $ROOTSTOCK_RPC_URL \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --chain-id 31 \
  --legacy

Note: Use --legacy because Rootstock testnet does not support EIP-1559 transaction type.

3. Use foundry.toml RPC alias

If ROOTSTOCK_RPC_URL is set in .env, you can use the configured alias:

source .env
forge script script/DeployYulVault.s.sol:DeployYulVaultScript \
  --rpc-url rootstock_testnet \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --chain-id 31 \
  --legacy

Contracts Explained

IVault (Interface)

Shared interface implemented by both Yul and Huff vaults:

  • deposit(): Payable; credits msg.value to msg.sender’s balance
  • withdraw(uint256 amount): Deducts amount from caller’s balance and sends RBTC to caller
  • balanceOf(address user): View; returns the balance of user

Vault.yul (Yul Implementation)

Pure Yul (strict assembly) with manual selector dispatch:

  • Storage: Balances at slot keccak256(address, 0) (mapping at slot 0)
  • Selectors: deposit() = 0xd0e30db0, withdraw(uint256) = 0x2e1a7d4d, balanceOf(address) = 0x70a08231
  • Checks: Revert on zero deposit, insufficient balance, or failed transfer (checks–effects–interactions)

Key flow: Load 4-byte selector from calldata → switch to the correct block → execute logic → stop() or return.

Vault.huff (Huff Implementation)

Huff macros and manual dispatcher at the end of MAIN:

  • Storage: Same layout as Yul—keccak256(abi.encode(key, 0)) for balances (uses sha3 opcode in Huff)
  • Macros: DEPOSIT(), WITHDRAW(), BALANCE_OF(), BALANCE_SLOT_FOR() for slot computation
  • Checks: Same revert conditions as Yul (zero deposit, insufficient balance, failed call)

Key flow: Entry jumps to dispatcher → compare selector → jump to the correct macro block.

Step-by-Step Tutorial

Step 1: Compile Yul Contract

Compile the Yul source to bytecode:

solc --strict-assembly --bin src/yul/Vault.yul

The script compile.sh writes the output to out/vault_yul.hex and out/vault_yul.bin.

Step 2: Compile Huff Contract

Compile the Huff source to runtime bytecode, then wrap it in minimal creation code so deployed code matches the runtime (correct jump targets):

huffc src/huff/Vault.huff --bin-runtime

compile.sh builds the creation wrapper and writes out/vault_huff.bin.

Step 3: Run Tests

Tests deploy the bytecode via a small Deployer contract and call the vault through IVault:

./script/compile.sh   # ensure out/ is up to date
forge test -vv

Step 4: Deploy Yul Vault to Rootstock Testnet

Ensure .env has ROOTSTOCK_RPC_URL and PRIVATE_KEY. Then:

source .env
forge script script/DeployYulVault.s.sol:DeployYulVaultScript \
  --rpc-url $ROOTSTOCK_RPC_URL \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --chain-id 31 \
  --legacy

Save the deployed contract address from the output.

Step 5: Deploy Huff Vault to Rootstock Testnet

Same flow for the Huff bytecode:

forge script script/DeployHuffVault.s.sol:DeployHuffVaultScript \
  --rpc-url $ROOTSTOCK_RPC_URL \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --chain-id 31 \
  --legacy

Step 6: Interact with Deployed Contract

Use Foundry’s cast to call the vault (replace <VAULT_ADDRESS> and <USER_ADDRESS>):

# Check balance
cast call <VAULT_ADDRESS> "balanceOf(address)(uint256)" <USER_ADDRESS> --rpc-url $ROOTSTOCK_RPC_URL

# Deposit (send RBTC with the call)
cast send <VAULT_ADDRESS> "deposit()" --value 1ether --private-key $PRIVATE_KEY --rpc-url $ROOTSTOCK_RPC_URL

# Withdraw
cast send <VAULT_ADDRESS> "withdraw(uint256)" 1000000000000000000 --private-key $PRIVATE_KEY --rpc-url $ROOTSTOCK_RPC_URL

Understanding Storage and Bytecode

Storage Layout

Both implementations use the same layout:

  • Slot 0: Base slot for the balances mapping
  • Balance of user: keccak256(abi.encode(user, 0)) — same as Solidity’s mapping(address => uint256) at slot 0

No inheritance or proxy; storage is straightforward and identical in Yul and Huff.

Rootstock EVM Compatibility

Rootstock executes standard EVM bytecode. There is no special “Rootstock bytecode” format. Contracts use only common opcodes (caller, callvalue, sstore, sload, keccak256, call, revert, etc.), so the same artifacts run on Ethereum, Rootstock Testnet, and Rootstock Mainnet. Use --chain-id 31 for testnet and --legacy because Rootstock testnet does not support EIP-1559.

Testing

The test suite covers:

  • ✅ Deposit increases balance (Yul + Huff)
  • ✅ Withdraw decreases balance and sends RBTC (Yul)
  • ✅ Revert on zero deposit (Yul)
  • ✅ Revert on insufficient balance (Yul)
  • balanceOf for unrelated address returns zero (Yul)
  • ✅ Gas logging for deposit and withdraw (Yul + Huff)

Run tests:

# All tests (requires ./script/compile.sh first)
forge test

# Verbose
forge test -vv

# Gas report
forge test --gas-report

# Only Yul tests
forge test --match-path test/YulVault.t.sol

# Only Huff tests
forge test --match-path test/HuffVault.t.sol

Deployment to Rootstock

Rootstock Testnet

View deployed contracts on the Rootstock Testnet Explorer.

source .env
forge script script/DeployYulVault.s.sol:DeployYulVaultScript \
  --rpc-url $ROOTSTOCK_RPC_URL \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --chain-id 31 \
  --legacy

Rootstock Mainnet

⚠️ WARNING: Only deploy to mainnet after thorough testing and with real RBTC.

# Use mainnet RPC (e.g. https://public-node.rsk.co)
forge script script/DeployYulVault.s.sol:DeployYulVaultScript \
  --rpc-url $ROOTSTOCK_MAINNET_RPC \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --chain-id 30 \
  --legacy

Environment Variables

Create a .env file (use .env.example as template):

# Rootstock Testnet (Chain ID 31)
ROOTSTOCK_RPC_URL=https://public-node.testnet.rsk.co

# Deployer private key (no 0x prefix is fine)
PRIVATE_KEY=your_private_key_here

Get testnet RBTC from the Rootstock Faucet.

Security Considerations

  1. Checks–Effects–Interactions: Both contracts update storage before external call (withdraw).
  2. Input Validation: Revert on zero deposit and on withdraw when balance is insufficient.
  3. Transfer Failure: Revert if the RBTC transfer in withdraw fails.
  4. No Upgradeability: Contracts are not proxy-based; bytecode is immutable after deployment.
  5. Private Key: Never commit .env; keep PRIVATE_KEY secure.

Troubleshooting

Error: "run script/compile.sh first" or "code.length == 0"

  • Run ./script/compile.sh so that out/vault_yul.bin and (if using Huff) out/vault_huff.bin exist.
  • Ensure solc is on PATH for Yul; ensure huffc is installed for Huff.

Error: "solc not found"

  • Install Solidity and add it to your PATH, or use the correct solc version for your platform.

Error: "Huff compiler not found"

  • Install huffc using one of the options in Installing huffc. Run huffc --help to verify.

Error when deploying: "insufficient funds" or "revert"

  • Ensure the deployer account has enough RBTC on the target network (use the faucet on testnet).
  • Use --legacy for Rootstock testnet (EIP-1559 not supported).

Tests pass locally but deployment fails

  • Confirm ROOTSTOCK_RPC_URL and PRIVATE_KEY in .env are correct.
  • For testnet, use --chain-id 31 and --legacy.

Resources

License

MIT

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

Authors

Built for Rootstock using Foundry, with Yul and Huff for low-level EVM development.


Happy Building on Rootstock! 🚀

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors