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
This compiles all Solidity contracts and generates TypeChain typings.
Run Tests
Run Local 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
| Contract | Address |
|---|
| LINK Token | 0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5 |
| Aave V3 Pool | 0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951 |
| Aave Data Provider | 0x3e9708d80f7B3e43118013075F7e95CE3AB31F31 |
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:
- Compiler Optimization: 200 runs for balanced optimization
- Via IR: Enabled for better optimization
- Immutable Variables: Used for addresses that never change
- Packed Storage: Careful ordering of state variables
- 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:
Deployment Checklist
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
- Private Keys: Never commit or share private keys
- Constructor Arguments: Double-check all deployment parameters
- Access Control: Verify owner addresses are correct
- Fee Limits: Set reasonable fee percentages
- Strategy Validation: Test strategies thoroughly before deploying
- Upgrade Path: Consider proxy patterns for upgradeable contracts
- 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
Next Steps