Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Rohit-KK15/MetaVault-AI/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This guide covers deploying MetaVault AI smart contracts using Hardhat, including local development, testnet deployment, and mainnet deployment.

Prerequisites

Install Dependencies

cd packages/contracts
npm install

Key Dependencies

{
  "dependencies": {
    "@openzeppelin/contracts": "^5.4.0",
    "dotenv": "^17.2.3"
  },
  "devDependencies": {
    "hardhat": "^2.26.5",
    "@nomicfoundation/hardhat-toolbox": "^6.1.0",
    "@nomicfoundation/hardhat-ethers": "^3.1.2",
    "@nomicfoundation/hardhat-verify": "^2.1.3",
    "ethers": "^6.15.0",
    "typescript": "^5.9.3"
  }
}
Source: package.json

Environment Setup

Create a .env file in the contracts directory:
# Infura or Alchemy API key
INFURA_KEY=your_infura_project_id

# Private key for deployment (NEVER commit this!)
PRIVATE_KEY=your_private_key_without_0x

# Etherscan API key for contract verification
ETHERSCAN_API_KEY=your_etherscan_api_key
Never commit your .env file to version control. Add it to .gitignore.

Hardhat Configuration

import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import * as dotenv from "dotenv";
dotenv.config();

const config: HardhatUserConfig = {
  solidity: {
    version: "0.8.28",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200,
      },
      viaIR: true
    }
  },
  networks: {
    hardhat: {
      forking: {
        url: `https://sepolia.infura.io/v3/${process.env.INFURA_KEY}`,
      }
    },
    sepolia: {
      url: `https://sepolia.infura.io/v3/${process.env.INFURA_KEY}`,
      accounts: [process.env.PRIVATE_KEY!]
    },
    localhost: {
      url: "http://127.0.0.1:8545"
    }
  }
};

export default config;
Source: hardhat.config.ts:1-34

Hardhat Commands

Compile Contracts

npx hardhat compile
This compiles all Solidity contracts and generates TypeChain typings.

Run Tests

npx hardhat test

Run Local Node

npx hardhat node
Starts a local Ethereum node with mainnet forking.

Deploy to Network

npx hardhat run scripts/deploy_vault_dashboard.ts --network sepolia

Deployment Script

The main deployment script deploys the Vault and integrates with the web app:
import { ethers, artifacts } from "hardhat";
import * as fs from "fs";
import * as path from "path";

async function main() {
    const [deployer] = await ethers.getSigners();
    console.log("Deploying contracts with the account:", deployer.address);

    // 1. Use Real LINK Token (Sepolia)
    const linkAddress = "0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5";
    console.log("Using existing LINK Token at:", linkAddress);

    // 2. Deploy Vault
    const feeRecipient = deployer.address;
    const performanceFeeBps = 1000; // 10%
    const withdrawFeeBps = 100; // 1%

    const Vault = await ethers.getContractFactory("Vault");
    const vault = await Vault.deploy(
        linkAddress,
        feeRecipient,
        performanceFeeBps,
        withdrawFeeBps
    );
    await vault.waitForDeployment();
    const vaultAddress = await vault.getAddress();
    console.log("Vault deployed to:", vaultAddress);

    // 3. Write to Web App Constants
    const webAppDir = path.join(__dirname, "../../web/defi-portfolio-app");
    const libDir = path.join(webAppDir, "lib");

    if (!fs.existsSync(libDir)) {
        fs.mkdirSync(libDir, { recursive: true });
    }

    const vaultArtifact = artifacts.readArtifactSync("Vault");

    const constantsContent = `
export const VAULT_ADDRESS = "${vaultAddress}";
export const LINK_ADDRESS = "${linkAddress}";

export const VAULT_ABI = ${JSON.stringify(vaultArtifact.abi, null, 2)} as const;
export const LINK_ABI = ${JSON.stringify(linkAbi, null, 2)} as const;
`;

    fs.writeFileSync(path.join(libDir, "constants.ts"), constantsContent);
    console.log("Written constants to web app");
}

main().catch((error) => {
    console.error(error);
    process.exitCode = 1;
});
Source: scripts/deploy_vault_dashboard.ts:1-79

Deployment Steps

1. Local Development

Deploy to local Hardhat network with mainnet forking:
# Terminal 1: Start local node
npx hardhat node

# Terminal 2: Deploy contracts
npx hardhat run scripts/deploy_vault_dashboard.ts --network localhost

2. Testnet Deployment (Sepolia)

# Deploy to Sepolia
npx hardhat run scripts/deploy_vault_dashboard.ts --network sepolia
Sepolia LINK Token: 0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5

3. Full System Deployment

Deploy Vault, Router, and Strategies:
// 1. Deploy Vault
const Vault = await ethers.getContractFactory("Vault");
const vault = await Vault.deploy(
    assetAddress,      // LINK or USDC
    feeRecipient,      // Fee recipient address
    1000,              // 10% performance fee
    100                // 1% withdrawal fee
);
await vault.waitForDeployment();

// 2. Deploy Strategy Router
const StrategyRouter = await ethers.getContractFactory("StrategyRouter");
const router = await StrategyRouter.deploy(
    await vault.getAddress(),
    deployer.address   // Owner
);
await router.waitForDeployment();

// 3. Deploy Aave Strategy
const StrategyAaveV3 = await ethers.getContractFactory("StrategyAaveV3");
const aaveStrategy = await StrategyAaveV3.deploy(
    assetAddress,              // LINK
    await vault.getAddress(),
    await router.getAddress(),
    aavePoolAddress,           // Aave V3 Pool
    dataProviderAddress        // Aave Data Provider
);
await aaveStrategy.waitForDeployment();

// 4. Deploy Leverage Strategy
const StrategyAaveLeverage = await ethers.getContractFactory("StrategyAaveLeverage");
const leverageStrategy = await StrategyAaveLeverage.deploy(
    assetAddress,              // LINK
    await vault.getAddress(),
    await router.getAddress(),
    aavePoolAddress,
    dataProviderAddress,
    swapRouterAddress,         // Uniswap V2 Router
    wethAddress,               // WETH
    oracleAddress              // Price Oracle
);
await leverageStrategy.waitForDeployment();

// 5. Configure Router with Strategies
await router.setStrategies(
    [await aaveStrategy.getAddress(), await leverageStrategy.getAddress()],
    [7000, 3000]  // 70% Aave, 30% Leverage
);

// 6. Connect Vault to Router
await vault.setRouter(await router.getAddress());

Contract Addresses

Sepolia Testnet

ContractAddress
LINK Token0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5
Aave V3 Pool0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951
Aave Data Provider0x3e9708d80f7B3e43118013075F7e95CE3AB31F31

Verify Contracts on Etherscan

npx hardhat verify --network sepolia <CONTRACT_ADDRESS> <CONSTRUCTOR_ARG1> <CONSTRUCTOR_ARG2> ...
Example:
npx hardhat verify --network sepolia 0x1234... \
  "0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5" \
  "0xYourFeeRecipient" \
  1000 \
  100

Post-Deployment Configuration

1. Fund Strategies

// Approve router to move funds
await token.approve(vaultAddress, ethers.parseEther("1000"));

// Deposit into vault
await vault.deposit(ethers.parseEther("1000"));

// Move funds to strategies
await router.moveFundsToStrategy(
    aaveStrategyAddress,
    ethers.parseEther("700")
);

2. Test Operations

// Check strategy balances
const aaveBalance = await aaveStrategy.strategyBalance();
const leverageBalance = await leverageStrategy.strategyBalance();

// Harvest profits
await router.harvestAll();

// Rebalance portfolio
await router.rebalance();

3. Monitor Leverage

// Check leverage state
const { deposited_, borrowed_, netExposure } = 
    await leverageStrategy.getLeverageState();

// Check LTV
const ltv = await leverageStrategy.getLTV();

// Check if at risk
const atRisk = await leverageStrategy.isAtRisk(ethers.parseEther("0.75"));

// Deleverage if needed
if (atRisk) {
    await router.triggerDeleverage(leverageStrategyAddress, 5);
}

Gas Optimization

The contracts use several gas optimization techniques:
  1. Compiler Optimization: 200 runs for balanced optimization
  2. Via IR: Enabled for better optimization
  3. Immutable Variables: Used for addresses that never change
  4. Packed Storage: Careful ordering of state variables
  5. Short-Circuit Logic: Early returns to save gas
solidity: {
  version: "0.8.28",
  settings: {
    optimizer: {
      enabled: true,
      runs: 200,
    },
    viaIR: true
  }
}
Source: hardhat.config.ts:7-15

Testing on Forked Network

Test against real protocols using Hardhat forking:
networks: {
  hardhat: {
    forking: {
      url: `https://sepolia.infura.io/v3/${process.env.INFURA_KEY}`,
    }
  }
}
Source: hardhat.config.ts:18-23 Run tests:
npx hardhat test

Deployment Checklist

  • Compile contracts: npx hardhat compile
  • Run tests: npx hardhat test
  • Configure .env with API keys and private key
  • Deploy Vault contract
  • Deploy StrategyRouter contract
  • Deploy Strategy contracts
  • Configure router with strategies and allocations
  • Connect vault to router
  • Verify contracts on Etherscan
  • Test deposit/withdraw flow
  • Test strategy operations (invest, harvest, rebalance)
  • Monitor gas costs and optimize if needed
  • Document deployed addresses
  • Update frontend with contract addresses and ABIs

Common Issues

Issue: Transaction Reverts

Solution: Check that:
  • Wallet has enough ETH for gas
  • Token approvals are set
  • Constructor arguments are correct
  • Network is correctly configured

Issue: Contract Not Verified

Solution: Run verify command with exact constructor arguments:
npx hardhat verify --network sepolia <ADDRESS> "<ARG1>" "<ARG2>"

Issue: Insufficient Liquidity

Solution: For leverage strategies on testnets:
  • Use smaller amounts
  • Reduce borrowFactor
  • Decrease maxDepth
  • Check pool liquidity before borrowing

Security Considerations

  1. Private Keys: Never commit or share private keys
  2. Constructor Arguments: Double-check all deployment parameters
  3. Access Control: Verify owner addresses are correct
  4. Fee Limits: Set reasonable fee percentages
  5. Strategy Validation: Test strategies thoroughly before deploying
  6. Upgrade Path: Consider proxy patterns for upgradeable contracts
  7. Timelock: Add timelock for sensitive operations on mainnet

Mainnet Deployment

Mainnet deployment requires extra caution. Consider:
  • Professional audit
  • Bug bounty program
  • Gradual rollout with caps
  • Multi-sig for admin functions
  • Emergency pause mechanism

Mainnet Checklist

  • Complete professional security audit
  • Deploy to testnet and run for at least 1 week
  • Set up monitoring and alerting
  • Prepare emergency response plan
  • Use multi-sig wallet for ownership
  • Set initial deposit caps
  • Announce deployment and risk disclosures

Next Steps