add absorb script and upgrade script
This commit is contained in:
420
scripts/handler.ts
Normal file
420
scripts/handler.ts
Normal file
@@ -0,0 +1,420 @@
|
||||
import { ethers, Contract, JsonRpcProvider } from "ethers";
|
||||
import type { EventLog, Log } from "ethers";
|
||||
|
||||
// ==================== 类型定义 ====================
|
||||
|
||||
interface VaultConfig {
|
||||
name: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
interface YTHolderData {
|
||||
address: string;
|
||||
balance: string;
|
||||
}
|
||||
|
||||
interface LPHolderData {
|
||||
address: string;
|
||||
balance: string;
|
||||
share: string;
|
||||
}
|
||||
|
||||
interface LendingSupplierData {
|
||||
address: string;
|
||||
supply: string;
|
||||
borrow: string;
|
||||
net: string;
|
||||
}
|
||||
|
||||
// ==================== 配置 ====================
|
||||
|
||||
const RPC_URL: string = "https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07";
|
||||
|
||||
// 合约配置(包含部署区块号,可以大幅减少查询时间)
|
||||
const YT_VAULTS: VaultConfig[] = [
|
||||
{ name: "YT-A", address: "0x97204190B35D9895a7a47aa7BaC61ac08De3cF05" },
|
||||
{ name: "YT-B", address: "0x181ef4011c35C4a2Fda08eBC5Cf509Ef58E553fF" },
|
||||
{ name: "YT-C", address: "0xE9A5b9f3a2Eda4358f81d4E2eF4f3280A664e5B0" },
|
||||
];
|
||||
|
||||
const YTLP_ADDRESS: string = "0x102e3F25Ef0ad9b0695C8F2daF8A1262437eEfc3";
|
||||
const LENDING_ADDRESS: string = "0xCb4E7B1069F6C26A1c27523ce4c8dfD884552d1D";
|
||||
|
||||
// ==================== 部署区块配置 ====================
|
||||
//
|
||||
// 配置说明:
|
||||
// 1. 查询准确的部署区块号,直接填写
|
||||
|
||||
interface DeploymentConfig {
|
||||
ytVaults: number; // YT 代币部署区块
|
||||
ytlp: number; // ytLP 部署区块
|
||||
lending: number; // Lending 部署区块
|
||||
}
|
||||
|
||||
const DEPLOYMENT_BLOCKS: DeploymentConfig = {
|
||||
ytVaults: 227339300, // YT-A/B/C 部署区块号
|
||||
ytlp: 227230270, // ytLP 部署区块号
|
||||
lending: 227746053, // Lending 部署区块号
|
||||
};
|
||||
|
||||
// ==================== ABIs ====================
|
||||
|
||||
const ERC20_ABI = [
|
||||
"event Transfer(address indexed from, address indexed to, uint256 value)",
|
||||
"function balanceOf(address account) view returns (uint256)",
|
||||
"function totalSupply() view returns (uint256)",
|
||||
] as const;
|
||||
|
||||
const LENDING_ABI = [
|
||||
"event Supply(address indexed from, address indexed dst, uint256 amount)",
|
||||
"function supplyBalanceOf(address account) view returns (uint256)",
|
||||
"function borrowBalanceOf(address account) view returns (uint256)",
|
||||
] as const;
|
||||
|
||||
// ==================== 工具函数 ====================
|
||||
|
||||
/**
|
||||
* 分块查询事件,避免超出 RPC 限制
|
||||
* @param contract 合约实例
|
||||
* @param filter 事件过滤器
|
||||
* @param fromBlock 起始区块
|
||||
* @param toBlock 结束区块
|
||||
* @param batchSize 每批次的区块数量(默认 9999,低于 10000 限制)
|
||||
*/
|
||||
async function queryEventsInBatches(
|
||||
contract: Contract,
|
||||
filter: any,
|
||||
fromBlock: number,
|
||||
toBlock: number,
|
||||
batchSize: number = 9999
|
||||
): Promise<(EventLog | Log)[]> {
|
||||
const allEvents: (EventLog | Log)[] = [];
|
||||
let currentBlock = fromBlock;
|
||||
|
||||
console.log(` 查询区块范围: ${fromBlock} -> ${toBlock} (总共 ${toBlock - fromBlock + 1} 个区块)`);
|
||||
|
||||
while (currentBlock <= toBlock) {
|
||||
const endBlock = Math.min(currentBlock + batchSize, toBlock);
|
||||
|
||||
console.log(` 正在查询区块 ${currentBlock} - ${endBlock}...`);
|
||||
|
||||
try {
|
||||
const events = await contract.queryFilter(filter, currentBlock, endBlock);
|
||||
allEvents.push(...events);
|
||||
console.log(` ✓ 获取到 ${events.length} 个事件`);
|
||||
} catch (error) {
|
||||
console.error(` ✗ 查询区块 ${currentBlock} - ${endBlock} 失败:`, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
currentBlock = endBlock + 1;
|
||||
|
||||
// 添加小延迟,避免触发 RPC 速率限制
|
||||
if (currentBlock <= toBlock) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` 总计获取 ${allEvents.length} 个事件\n`);
|
||||
return allEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前最新区块号
|
||||
*/
|
||||
async function getLatestBlockNumber(provider: JsonRpcProvider, silent: boolean = false): Promise<number> {
|
||||
const blockNumber = await provider.getBlockNumber();
|
||||
if (!silent) {
|
||||
console.log(`当前最新区块: ${blockNumber}\n`);
|
||||
}
|
||||
return blockNumber;
|
||||
}
|
||||
|
||||
// ==================== 主函数 ====================
|
||||
|
||||
// 记录上次扫描的区块号
|
||||
let lastScannedBlock: number = 0;
|
||||
// 标记是否正在扫描,防止并发
|
||||
let isScanning: boolean = false;
|
||||
|
||||
// 全局地址集合,用于追踪所有曾经出现过的地址
|
||||
const allYTAddresses: Map<string, Set<string>> = new Map(); // vault address -> holder addresses
|
||||
const allLPAddresses: Set<string> = new Set();
|
||||
const allLendingAddresses: Set<string> = new Set();
|
||||
|
||||
async function getAllHolders(
|
||||
provider: JsonRpcProvider,
|
||||
fromBlock?: number,
|
||||
toBlock?: number,
|
||||
isInitialScan: boolean = false
|
||||
): Promise<void> {
|
||||
// 获取最新区块号
|
||||
const latestBlock = toBlock || await getLatestBlockNumber(provider, fromBlock !== undefined);
|
||||
|
||||
// 计算起始区块
|
||||
let ytVaultsStartBlock: number;
|
||||
let ytlpStartBlock: number;
|
||||
let lendingStartBlock: number;
|
||||
|
||||
if (fromBlock !== undefined) {
|
||||
// 增量扫描模式
|
||||
ytVaultsStartBlock = ytlpStartBlock = lendingStartBlock = fromBlock;
|
||||
console.log(`\n🔄 增量扫描: 区块 ${fromBlock} -> ${latestBlock}\n`);
|
||||
} else {
|
||||
// 首次扫描:使用部署区块号
|
||||
ytVaultsStartBlock = DEPLOYMENT_BLOCKS.ytVaults;
|
||||
ytlpStartBlock = DEPLOYMENT_BLOCKS.ytlp;
|
||||
lendingStartBlock = DEPLOYMENT_BLOCKS.lending;
|
||||
if (isInitialScan) {
|
||||
console.log(`✨ 首次扫描,从部署区块开始:`);
|
||||
console.log(` YT Vaults 起始区块: ${ytVaultsStartBlock}`);
|
||||
console.log(` ytLP 起始区块: ${ytlpStartBlock}`);
|
||||
console.log(` Lending 起始区块: ${lendingStartBlock}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 获取 YT 代币持有者
|
||||
console.log("1. YT 代币持有者:");
|
||||
|
||||
for (const vault of YT_VAULTS) {
|
||||
console.log(` 正在查询 ${vault.name} (${vault.address})...`);
|
||||
const contract: Contract = new ethers.Contract(vault.address, ERC20_ABI, provider);
|
||||
const filter = contract.filters.Transfer();
|
||||
const events: (EventLog | Log)[] = await queryEventsInBatches(
|
||||
contract,
|
||||
filter,
|
||||
ytVaultsStartBlock,
|
||||
latestBlock
|
||||
);
|
||||
|
||||
// 初始化该 vault 的地址集合(如果不存在)
|
||||
if (!allYTAddresses.has(vault.address)) {
|
||||
allYTAddresses.set(vault.address, new Set<string>());
|
||||
}
|
||||
const vaultAddresses = allYTAddresses.get(vault.address)!;
|
||||
|
||||
// 记录新增地址数量
|
||||
const previousCount = vaultAddresses.size;
|
||||
|
||||
// 添加新发现的地址到全局集合
|
||||
for (const event of events) {
|
||||
if ("args" in event && event.args.to !== ethers.ZeroAddress) {
|
||||
vaultAddresses.add(event.args.to as string);
|
||||
}
|
||||
}
|
||||
|
||||
const newAddressCount = vaultAddresses.size - previousCount;
|
||||
if (newAddressCount > 0) {
|
||||
console.log(` 发现 ${newAddressCount} 个新地址,总共追踪 ${vaultAddresses.size} 个地址`);
|
||||
}
|
||||
|
||||
// 查询所有曾经出现过的地址的当前余额
|
||||
const holders: YTHolderData[] = [];
|
||||
for (const address of vaultAddresses) {
|
||||
const balance: bigint = await contract.balanceOf(address);
|
||||
if (balance > 0n) {
|
||||
holders.push({
|
||||
address,
|
||||
balance: ethers.formatEther(balance),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 按余额降序排序
|
||||
holders.sort((a, b) => parseFloat(b.balance) - parseFloat(a.balance));
|
||||
|
||||
console.log(` ${vault.name}: ${holders.length} 持有者`);
|
||||
if (holders.length > 0) {
|
||||
console.log(` 前 10 名持有者:`);
|
||||
const top10 = holders.slice(0, 10);
|
||||
top10.forEach((h: YTHolderData, index: number) =>
|
||||
console.log(` ${index + 1}. ${h.address}: ${h.balance}`)
|
||||
);
|
||||
} else {
|
||||
console.log(` 暂无持有者`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 获取 LP 代币持有者
|
||||
console.log("\n2. LP 代币持有者 (ytLP):");
|
||||
console.log(` 正在查询 ytLP (${YTLP_ADDRESS})...`);
|
||||
const lpContract: Contract = new ethers.Contract(YTLP_ADDRESS, ERC20_ABI, provider);
|
||||
const lpFilter = lpContract.filters.Transfer();
|
||||
const lpEvents: (EventLog | Log)[] = await queryEventsInBatches(
|
||||
lpContract,
|
||||
lpFilter,
|
||||
ytlpStartBlock,
|
||||
latestBlock
|
||||
);
|
||||
|
||||
// 记录新增地址数量
|
||||
const previousLPCount = allLPAddresses.size;
|
||||
|
||||
// 添加新发现的地址到全局集合
|
||||
for (const event of lpEvents) {
|
||||
if ("args" in event && event.args.to !== ethers.ZeroAddress) {
|
||||
allLPAddresses.add(event.args.to as string);
|
||||
}
|
||||
}
|
||||
|
||||
const newLPAddressCount = allLPAddresses.size - previousLPCount;
|
||||
if (newLPAddressCount > 0) {
|
||||
console.log(` 发现 ${newLPAddressCount} 个新地址,总共追踪 ${allLPAddresses.size} 个地址`);
|
||||
}
|
||||
|
||||
// 查询所有曾经出现过的地址的当前余额
|
||||
const lpHolders: LPHolderData[] = [];
|
||||
const totalSupply: bigint = await lpContract.totalSupply();
|
||||
|
||||
for (const address of allLPAddresses) {
|
||||
const balance: bigint = await lpContract.balanceOf(address);
|
||||
if (balance > 0n) {
|
||||
const share: string = (Number(balance) / Number(totalSupply) * 100).toFixed(4);
|
||||
lpHolders.push({
|
||||
address,
|
||||
balance: ethers.formatEther(balance),
|
||||
share: share + "%",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 按余额降序排序
|
||||
lpHolders.sort((a, b) => parseFloat(b.balance) - parseFloat(a.balance));
|
||||
|
||||
console.log(` 总计: ${lpHolders.length} 持有者`);
|
||||
if (lpHolders.length > 0) {
|
||||
console.log(` 前 10 名持有者:`);
|
||||
const top10 = lpHolders.slice(0, 10);
|
||||
top10.forEach((h: LPHolderData, index: number) =>
|
||||
console.log(` ${index + 1}. ${h.address}: ${h.balance} (${h.share})`)
|
||||
);
|
||||
} else {
|
||||
console.log(` 暂无持有者`);
|
||||
}
|
||||
|
||||
// 3. 获取 Lending 提供者
|
||||
console.log("\n3. Lending 提供者:");
|
||||
console.log(` 正在查询 Lending (${LENDING_ADDRESS})...`);
|
||||
const lendingContract: Contract = new ethers.Contract(LENDING_ADDRESS, LENDING_ABI, provider);
|
||||
const supplyFilter = lendingContract.filters.Supply();
|
||||
const supplyEvents: (EventLog | Log)[] = await queryEventsInBatches(
|
||||
lendingContract,
|
||||
supplyFilter,
|
||||
lendingStartBlock,
|
||||
latestBlock
|
||||
);
|
||||
|
||||
// 记录新增地址数量
|
||||
const previousLendingCount = allLendingAddresses.size;
|
||||
|
||||
// 添加新发现的地址到全局集合
|
||||
for (const event of supplyEvents) {
|
||||
if ("args" in event) {
|
||||
allLendingAddresses.add(event.args.dst as string);
|
||||
}
|
||||
}
|
||||
|
||||
const newLendingAddressCount = allLendingAddresses.size - previousLendingCount;
|
||||
if (newLendingAddressCount > 0) {
|
||||
console.log(` 发现 ${newLendingAddressCount} 个新地址,总共追踪 ${allLendingAddresses.size} 个地址`);
|
||||
}
|
||||
|
||||
// 查询所有曾经出现过的地址的当前余额
|
||||
const suppliers: LendingSupplierData[] = [];
|
||||
for (const address of allLendingAddresses) {
|
||||
const supplyBalance: bigint = await lendingContract.supplyBalanceOf(address);
|
||||
const borrowBalance: bigint = await lendingContract.borrowBalanceOf(address);
|
||||
|
||||
if (supplyBalance > 0n || borrowBalance > 0n) {
|
||||
suppliers.push({
|
||||
address,
|
||||
supply: ethers.formatUnits(supplyBalance, 6),
|
||||
borrow: ethers.formatUnits(borrowBalance, 6),
|
||||
net: ethers.formatUnits(supplyBalance - borrowBalance, 6),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 按净供应额降序排序
|
||||
suppliers.sort((a, b) => parseFloat(b.net) - parseFloat(a.net));
|
||||
|
||||
console.log(` 总计: ${suppliers.length} 参与者`);
|
||||
if (suppliers.length > 0) {
|
||||
console.log(` 前 10 名参与者:`);
|
||||
const top10 = suppliers.slice(0, 10);
|
||||
top10.forEach((s: LendingSupplierData, index: number) =>
|
||||
console.log(
|
||||
` ${index + 1}. ${s.address}: 供应=${s.supply} USDC, 借款=${s.borrow} USDC, 净额=${s.net} USDC`
|
||||
)
|
||||
);
|
||||
} else {
|
||||
console.log(` 暂无参与者`);
|
||||
}
|
||||
|
||||
// 更新上次扫描的区块号
|
||||
lastScannedBlock = latestBlock;
|
||||
console.log(`\n📌 已记录扫描区块: ${lastScannedBlock}`);
|
||||
}
|
||||
|
||||
// ==================== 执行 ====================
|
||||
|
||||
const POLL_INTERVAL_MS = 10000; // 10秒轮询间隔
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const provider: JsonRpcProvider = new ethers.JsonRpcProvider(RPC_URL);
|
||||
|
||||
console.log("=== ytLp 协议 Holder 数据监控 ===\n");
|
||||
console.log(`轮询间隔: ${POLL_INTERVAL_MS / 1000} 秒\n`);
|
||||
|
||||
try {
|
||||
// 首次扫描:从部署区块到当前区块
|
||||
console.log("📊 开始首次扫描...\n");
|
||||
const startTime = Date.now();
|
||||
await getAllHolders(provider, undefined, undefined, true);
|
||||
const endTime = Date.now();
|
||||
const duration = ((endTime - startTime) / 1000).toFixed(2);
|
||||
console.log(`\n✓ 首次扫描完成,耗时 ${duration} 秒`);
|
||||
|
||||
// 启动轮询
|
||||
console.log(`\n⏰ 开始轮询,每 ${POLL_INTERVAL_MS / 1000} 秒检查一次新区块...\n`);
|
||||
|
||||
setInterval(async () => {
|
||||
try {
|
||||
// 如果正在扫描,跳过本次轮询
|
||||
if (isScanning) {
|
||||
console.log(`⏰ [${new Date().toLocaleString()}] 跳过本次轮询(上次扫描仍在进行中)`);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentBlock = await provider.getBlockNumber();
|
||||
|
||||
// 如果有新区块,进行增量扫描
|
||||
if (currentBlock > lastScannedBlock) {
|
||||
isScanning = true; // 标记开始扫描
|
||||
|
||||
console.log(`\n${"=".repeat(60)}`);
|
||||
console.log(`⏰ [${new Date().toLocaleString()}] 发现新区块`);
|
||||
console.log(`${"=".repeat(60)}`);
|
||||
|
||||
const scanStart = Date.now();
|
||||
await getAllHolders(provider, lastScannedBlock + 1, currentBlock, false);
|
||||
const scanDuration = ((Date.now() - scanStart) / 1000).toFixed(2);
|
||||
console.log(`\n✓ 增量扫描完成,耗时 ${scanDuration} 秒`);
|
||||
|
||||
isScanning = false; // 标记扫描完成
|
||||
} else {
|
||||
console.log(`⏰ [${new Date().toLocaleString()}] 暂无新区块 (当前: ${currentBlock})`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`\n✗ 轮询过程中发生错误:`, error);
|
||||
isScanning = false; // 发生错误时也要重置标记
|
||||
}
|
||||
}, POLL_INTERVAL_MS);
|
||||
|
||||
} catch (error) {
|
||||
console.error("\n✗ 发生错误:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
102
scripts/liquidation_bot/index.ts
Normal file
102
scripts/liquidation_bot/index.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import hre from 'hardhat';
|
||||
import { liquidateUnderwaterBorrowers } from './liquidateUnderwaterBorrowers';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
const LOOP_DELAY = 5000; // 5 秒轮询间隔
|
||||
|
||||
/**
|
||||
* 清算机器人主循环
|
||||
*/
|
||||
async function main() {
|
||||
const network = hre.network.name;
|
||||
const chainId = hre.network.config.chainId;
|
||||
|
||||
console.log('\n==========================================');
|
||||
console.log('🤖 YT Lending Liquidation Bot Started');
|
||||
console.log('==========================================');
|
||||
console.log('Network:', network);
|
||||
console.log('Chain ID:', chainId);
|
||||
console.log('Loop Delay:', LOOP_DELAY, 'ms\n');
|
||||
|
||||
// 读取部署信息
|
||||
const deploymentsPath = path.join(__dirname, '../../deployments-lending.json');
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error('deployments-lending.json not found');
|
||||
}
|
||||
|
||||
const deployments = JSON.parse(fs.readFileSync(deploymentsPath, 'utf-8'));
|
||||
const deployment = deployments[chainId?.toString() || '421614'];
|
||||
|
||||
if (!deployment) {
|
||||
throw new Error(`No deployment found for chainId: ${chainId}`);
|
||||
}
|
||||
|
||||
console.log('📋 Contract Addresses:');
|
||||
console.log(' Lending Proxy:', deployment.lendingProxy);
|
||||
console.log(' Price Feed:', deployment.lendingPriceFeedProxy);
|
||||
console.log(' Base Token (USDC):', deployment.usdcAddress);
|
||||
console.log('');
|
||||
|
||||
// 获取签名者
|
||||
const [signer] = await hre.ethers.getSigners();
|
||||
console.log('👤 Liquidator Address:', await signer.getAddress());
|
||||
console.log('💰 Liquidator Balance:', hre.ethers.formatEther(await hre.ethers.provider.getBalance(signer)), 'ETH\n');
|
||||
|
||||
// 初始化合约
|
||||
const lendingContract = await hre.ethers.getContractAt(
|
||||
'Lending',
|
||||
deployment.lendingProxy,
|
||||
signer
|
||||
);
|
||||
|
||||
const priceFeedContract = await hre.ethers.getContractAt(
|
||||
'LendingPriceFeed',
|
||||
deployment.lendingPriceFeedProxy,
|
||||
signer
|
||||
);
|
||||
|
||||
console.log('✅ Contracts initialized\n');
|
||||
console.log('==========================================');
|
||||
console.log('🔄 Starting main loop...\n');
|
||||
|
||||
let lastBlockNumber: number | undefined;
|
||||
|
||||
// Compound V3 风格:while(true) 轮询
|
||||
while (true) {
|
||||
try {
|
||||
const currentBlockNumber = await hre.ethers.provider.getBlockNumber();
|
||||
|
||||
console.log(`[${new Date().toISOString()}] Block: ${currentBlockNumber}`);
|
||||
|
||||
// 检查是否有新区块(每个区块只处理一次)
|
||||
if (currentBlockNumber !== lastBlockNumber) {
|
||||
lastBlockNumber = currentBlockNumber;
|
||||
|
||||
// 执行清算逻辑
|
||||
await liquidateUnderwaterBorrowers(
|
||||
lendingContract,
|
||||
priceFeedContract,
|
||||
signer
|
||||
);
|
||||
|
||||
console.log(''); // 空行分隔
|
||||
} else {
|
||||
console.log(`Block already checked; waiting ${LOOP_DELAY}ms...\n`);
|
||||
}
|
||||
|
||||
// 等待下一次轮询
|
||||
await new Promise(resolve => setTimeout(resolve, LOOP_DELAY));
|
||||
} catch (error) {
|
||||
console.error('❌ Error in main loop:', error);
|
||||
console.log(`Retrying in ${LOOP_DELAY}ms...\n`);
|
||||
await new Promise(resolve => setTimeout(resolve, LOOP_DELAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error('❌ Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
170
scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts
Normal file
170
scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import hre from 'hardhat';
|
||||
import { Signer } from 'ethers';
|
||||
|
||||
const LOOKBACK_BLOCKS = 50000; // 查询最近 50000 个区块
|
||||
const LIQUIDATION_THRESHOLD = 10e6; // $10 (USDC 6 decimals)
|
||||
|
||||
/**
|
||||
* 获取最近活跃的地址(通过多个事件)
|
||||
*/
|
||||
export async function getUniqueAddresses(
|
||||
lendingContract: any
|
||||
): Promise<string[]> {
|
||||
const currentBlock = await hre.ethers.provider.getBlockNumber();
|
||||
const fromBlock = Math.max(currentBlock - LOOKBACK_BLOCKS, 0);
|
||||
|
||||
console.log(`📊 Querying events from block ${fromBlock} to ${currentBlock}...`);
|
||||
|
||||
const uniqueAddresses = new Set<string>();
|
||||
|
||||
// 1. Withdraw 事件(借款/提现)
|
||||
try {
|
||||
const withdrawFilter = lendingContract.filters.Withdraw();
|
||||
const withdrawEvents = await lendingContract.queryFilter(
|
||||
withdrawFilter,
|
||||
fromBlock,
|
||||
currentBlock
|
||||
);
|
||||
for (const event of withdrawEvents) {
|
||||
if (event.args?.src) uniqueAddresses.add(event.args.src);
|
||||
if (event.args?.dst) uniqueAddresses.add(event.args.dst);
|
||||
}
|
||||
console.log(` - Withdraw events: ${withdrawEvents.length}`);
|
||||
} catch (error) {
|
||||
console.error(' ⚠️ Failed to query Withdraw events:', error);
|
||||
}
|
||||
|
||||
// 2. Supply 事件(存款)
|
||||
try {
|
||||
const supplyFilter = lendingContract.filters.Supply();
|
||||
const supplyEvents = await lendingContract.queryFilter(
|
||||
supplyFilter,
|
||||
fromBlock,
|
||||
currentBlock
|
||||
);
|
||||
for (const event of supplyEvents) {
|
||||
if (event.args?.from) uniqueAddresses.add(event.args.from);
|
||||
if (event.args?.dst) uniqueAddresses.add(event.args.dst);
|
||||
}
|
||||
console.log(` - Supply events: ${supplyEvents.length}`);
|
||||
} catch (error) {
|
||||
console.error(' ⚠️ Failed to query Supply events:', error);
|
||||
}
|
||||
|
||||
// 3. SupplyCollateral 事件(抵押品存入)
|
||||
try {
|
||||
const supplyCollateralFilter = lendingContract.filters.SupplyCollateral();
|
||||
const supplyCollateralEvents = await lendingContract.queryFilter(
|
||||
supplyCollateralFilter,
|
||||
fromBlock,
|
||||
currentBlock
|
||||
);
|
||||
for (const event of supplyCollateralEvents) {
|
||||
if (event.args?.from) uniqueAddresses.add(event.args.from);
|
||||
if (event.args?.dst) uniqueAddresses.add(event.args.dst);
|
||||
}
|
||||
console.log(` - SupplyCollateral events: ${supplyCollateralEvents.length}`);
|
||||
} catch (error) {
|
||||
console.error(' ⚠️ Failed to query SupplyCollateral events:', error);
|
||||
}
|
||||
|
||||
// 4. WithdrawCollateral 事件(抵押品提取)
|
||||
try {
|
||||
const withdrawCollateralFilter = lendingContract.filters.WithdrawCollateral();
|
||||
const withdrawCollateralEvents = await lendingContract.queryFilter(
|
||||
withdrawCollateralFilter,
|
||||
fromBlock,
|
||||
currentBlock
|
||||
);
|
||||
for (const event of withdrawCollateralEvents) {
|
||||
if (event.args?.src) uniqueAddresses.add(event.args.src);
|
||||
if (event.args?.to) uniqueAddresses.add(event.args.to);
|
||||
}
|
||||
console.log(` - WithdrawCollateral events: ${withdrawCollateralEvents.length}`);
|
||||
} catch (error) {
|
||||
console.error(' ⚠️ Failed to query WithdrawCollateral events:', error);
|
||||
}
|
||||
|
||||
console.log(`✅ Found ${uniqueAddresses.size} unique addresses from all events`);
|
||||
return Array.from(uniqueAddresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查并清算可清算账户
|
||||
* 参考:comet/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts
|
||||
*/
|
||||
export async function liquidateUnderwaterBorrowers(
|
||||
lendingContract: any,
|
||||
priceFeedContract: any,
|
||||
signer: Signer
|
||||
): Promise<boolean> {
|
||||
// 步骤 1: 获取最近活跃的地址
|
||||
const uniqueAddresses = await getUniqueAddresses(lendingContract);
|
||||
|
||||
if (uniqueAddresses.length === 0) {
|
||||
console.log('ℹ️ No active addresses found');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(`🔍 Checking ${uniqueAddresses.length} addresses for liquidation...`);
|
||||
|
||||
const liquidatableAccounts: string[] = [];
|
||||
|
||||
// 步骤 2: 检查每个地址是否可清算
|
||||
for (const address of uniqueAddresses) {
|
||||
try {
|
||||
// 直接调用合约的 isLiquidatable(),无需自己计算健康因子
|
||||
const isLiquidatable = await lendingContract.isLiquidatable(address);
|
||||
|
||||
if (isLiquidatable) {
|
||||
// 应用清算阈值过滤(防止清算小额账户,gas 成本过高)
|
||||
const borrowBalance = await lendingContract.borrowBalanceOf(address);
|
||||
const baseToken = await lendingContract.baseToken();
|
||||
const basePrice = await priceFeedContract.getPrice(baseToken);
|
||||
|
||||
// debtValue 计算:borrowBalance (6 decimals) * basePrice (30 decimals) / 1e6
|
||||
const debtValue = (BigInt(borrowBalance) * BigInt(basePrice)) / BigInt(10) ** BigInt(6);
|
||||
const debtValueInBaseUnit = Number(debtValue / (BigInt(10) ** BigInt(30))); // 转换为 USDC 单位
|
||||
|
||||
// LIQUIDATION_THRESHOLD 是 6 decimals,需要转换为 30 decimals 来和 debtValue 比较
|
||||
// 10e6 * 1e24 = 10e30 (代表 $10)
|
||||
if (debtValue >= BigInt(LIQUIDATION_THRESHOLD) * BigInt(10) ** BigInt(24)) {
|
||||
console.log(`💰 Liquidatable: ${address}, Debt: $${debtValueInBaseUnit.toFixed(2)}`);
|
||||
liquidatableAccounts.push(address);
|
||||
} else {
|
||||
console.log(`⏭️ Skip (below threshold): ${address}, Debt: $${debtValueInBaseUnit.toFixed(2)}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking ${address}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤 3: 批量清算
|
||||
if (liquidatableAccounts.length > 0) {
|
||||
console.log(`\n🎯 Found ${liquidatableAccounts.length} liquidatable accounts`);
|
||||
console.log('📤 Sending liquidation transaction...');
|
||||
|
||||
try {
|
||||
const liquidatorAddress = await signer.getAddress();
|
||||
const tx = await lendingContract.connect(signer).absorbMultiple(
|
||||
liquidatorAddress,
|
||||
liquidatableAccounts
|
||||
);
|
||||
|
||||
console.log(`🔗 Transaction sent: ${tx.hash}`);
|
||||
const receipt = await tx.wait();
|
||||
console.log(`✅ Liquidation successful!`);
|
||||
console.log(` Gas used: ${receipt.gasUsed.toString()}`);
|
||||
console.log(` Block: ${receipt.blockNumber}`);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ Liquidation transaction failed:', error);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
console.log('✅ No liquidatable accounts found');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
109
scripts/upgrade/upgradeConfigurator.ts
Normal file
109
scripts/upgrade/upgradeConfigurator.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 升级 Configurator 合约
|
||||
* 使用 upgrades.upgradeProxy() 进行 UUPS 升级
|
||||
*/
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log("\n==========================================");
|
||||
console.log("🔄 升级 Configurator 合约");
|
||||
console.log("==========================================");
|
||||
console.log("升级账户:", deployer.address);
|
||||
console.log("账户余额:", ethers.formatEther(await ethers.provider.getBalance(deployer.address)), "ETH\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-lending.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件,请先运行部署脚本");
|
||||
}
|
||||
|
||||
const network = await ethers.provider.getNetwork();
|
||||
const chainId = network.chainId.toString();
|
||||
const allDeployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
const deployments = allDeployments[chainId];
|
||||
|
||||
if (!deployments) {
|
||||
throw new Error(`未找到网络 ${chainId} 的部署信息`);
|
||||
}
|
||||
|
||||
console.log("📋 当前部署的合约:");
|
||||
console.log(" Configurator Proxy:", deployments.configuratorProxy);
|
||||
console.log(" Configurator Impl: ", deployments.configuratorImpl);
|
||||
console.log("");
|
||||
|
||||
// ========== 升级 Configurator ==========
|
||||
console.log("🔄 Phase 1: 升级 Configurator 合约");
|
||||
|
||||
console.log(" 当前 Configurator Proxy:", deployments.configuratorProxy);
|
||||
console.log(" 当前 Configurator Implementation:", deployments.configuratorImpl);
|
||||
|
||||
// 获取新的 Configurator 合约工厂
|
||||
const ConfiguratorV2 = await ethers.getContractFactory("Configurator");
|
||||
|
||||
console.log("\n 正在验证新实现合约...");
|
||||
const upgradedConfigurator = await upgrades.upgradeProxy(
|
||||
deployments.configuratorProxy,
|
||||
ConfiguratorV2,
|
||||
{
|
||||
kind: "uups",
|
||||
// unsafeSkipStorageCheck: true // 跳过存储布局检查(请确保你了解风险)
|
||||
}
|
||||
);
|
||||
await upgradedConfigurator.waitForDeployment();
|
||||
|
||||
console.log(" ✅ Configurator 已升级!");
|
||||
|
||||
// 获取新的实现合约地址
|
||||
const upgradedConfiguratorAddress = await upgradedConfigurator.getAddress();
|
||||
const newConfiguratorImplAddress = await upgrades.erc1967.getImplementationAddress(upgradedConfiguratorAddress);
|
||||
console.log(" 新 Configurator Implementation:", newConfiguratorImplAddress);
|
||||
|
||||
// 验证升级
|
||||
console.log("\n 验证升级结果:");
|
||||
console.log(" Configurator Proxy (不变):", upgradedConfiguratorAddress);
|
||||
console.log(" Owner:", await upgradedConfigurator.owner());
|
||||
|
||||
// 保存升级历史
|
||||
if (!deployments.upgradeHistory) {
|
||||
deployments.upgradeHistory = [];
|
||||
}
|
||||
|
||||
deployments.upgradeHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
contract: "Configurator",
|
||||
oldImplementation: deployments.configuratorImpl,
|
||||
newImplementation: newConfiguratorImplAddress,
|
||||
upgrader: deployer.address
|
||||
});
|
||||
|
||||
// 保存新的实现地址
|
||||
deployments.configuratorImpl = newConfiguratorImplAddress;
|
||||
deployments.configuratorUpgradeTimestamp = new Date().toISOString();
|
||||
|
||||
allDeployments[chainId] = deployments;
|
||||
fs.writeFileSync(deploymentsPath, JSON.stringify(allDeployments, null, 2));
|
||||
|
||||
console.log("\n✅ Configurator 升级完成!");
|
||||
console.log("=====================================");
|
||||
console.log("旧实现:", deployments.upgradeHistory[deployments.upgradeHistory.length - 1].oldImplementation);
|
||||
console.log("新实现:", newConfiguratorImplAddress);
|
||||
console.log("=====================================\n");
|
||||
|
||||
console.log("💾 升级信息已保存到:", deploymentsPath);
|
||||
console.log("");
|
||||
console.log("📌 重要提示:");
|
||||
console.log(" 1. 代理地址保持不变,用户无需更改合约地址");
|
||||
console.log(" 2. 所有状态数据已保留");
|
||||
console.log(" 3. 建议运行验证脚本确认升级成功");
|
||||
console.log(" 4. 建议在测试网充分测试后再升级主网\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
114
scripts/upgrade/upgradeLending.ts
Normal file
114
scripts/upgrade/upgradeLending.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 升级 Lending 合约
|
||||
* 使用 upgrades.upgradeProxy() 进行 UUPS 升级
|
||||
*/
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log("\n==========================================");
|
||||
console.log("🔄 升级 Lending 合约");
|
||||
console.log("==========================================");
|
||||
console.log("升级账户:", deployer.address);
|
||||
console.log("账户余额:", ethers.formatEther(await ethers.provider.getBalance(deployer.address)), "ETH\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-lending.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件,请先运行部署脚本");
|
||||
}
|
||||
|
||||
const network = await ethers.provider.getNetwork();
|
||||
const chainId = network.chainId.toString();
|
||||
const allDeployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
const deployments = allDeployments[chainId];
|
||||
|
||||
if (!deployments) {
|
||||
throw new Error(`未找到网络 ${chainId} 的部署信息`);
|
||||
}
|
||||
|
||||
if (!deployments.lendingProxy) {
|
||||
throw new Error("未找到 Lending Proxy 地址,请先运行配置脚本");
|
||||
}
|
||||
|
||||
console.log("📋 当前部署的合约:");
|
||||
console.log(" Lending Proxy:", deployments.lendingProxy);
|
||||
console.log(" Lending Impl: ", deployments.lendingImpl);
|
||||
console.log("");
|
||||
|
||||
// ========== 升级 Lending ==========
|
||||
console.log("🔄 Phase 1: 升级 Lending 合约");
|
||||
|
||||
console.log(" 当前 Lending Proxy:", deployments.lendingProxy);
|
||||
console.log(" 当前 Lending Implementation:", deployments.lendingImpl);
|
||||
|
||||
// 获取新的 Lending 合约工厂
|
||||
const LendingV2 = await ethers.getContractFactory("Lending");
|
||||
|
||||
console.log("\n 正在验证新实现合约...");
|
||||
// upgrades.upgradeProxy 会自动验证存储布局兼容性
|
||||
const upgradedLending = await upgrades.upgradeProxy(
|
||||
deployments.lendingProxy,
|
||||
LendingV2,
|
||||
{
|
||||
kind: "uups"
|
||||
}
|
||||
);
|
||||
await upgradedLending.waitForDeployment();
|
||||
|
||||
console.log(" ✅ Lending 已升级!");
|
||||
|
||||
// 获取新的实现合约地址
|
||||
const upgradedLendingAddress = await upgradedLending.getAddress();
|
||||
const newLendingImplAddress = await upgrades.erc1967.getImplementationAddress(upgradedLendingAddress);
|
||||
console.log(" 新 Lending Implementation:", newLendingImplAddress);
|
||||
|
||||
// 验证升级
|
||||
console.log("\n 验证升级结果:");
|
||||
console.log(" Lending Proxy (不变):", upgradedLendingAddress);
|
||||
console.log(" Owner:", await upgradedLending.owner());
|
||||
console.log(" Base Token:", await upgradedLending.baseToken());
|
||||
|
||||
// 保存升级历史
|
||||
if (!deployments.upgradeHistory) {
|
||||
deployments.upgradeHistory = [];
|
||||
}
|
||||
|
||||
deployments.upgradeHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
contract: "Lending",
|
||||
oldImplementation: deployments.lendingImpl,
|
||||
newImplementation: newLendingImplAddress,
|
||||
upgrader: deployer.address
|
||||
});
|
||||
|
||||
// 保存新的实现地址
|
||||
deployments.lendingImpl = newLendingImplAddress;
|
||||
deployments.lendingUpgradeTimestamp = new Date().toISOString();
|
||||
|
||||
allDeployments[chainId] = deployments;
|
||||
fs.writeFileSync(deploymentsPath, JSON.stringify(allDeployments, null, 2));
|
||||
|
||||
console.log("\n✅ Lending 升级完成!");
|
||||
console.log("=====================================");
|
||||
console.log("旧实现:", deployments.upgradeHistory[deployments.upgradeHistory.length - 1].oldImplementation);
|
||||
console.log("新实现:", newLendingImplAddress);
|
||||
console.log("=====================================\n");
|
||||
|
||||
console.log("💾 升级信息已保存到:", deploymentsPath);
|
||||
console.log("");
|
||||
console.log("📌 重要提示:");
|
||||
console.log(" 1. 代理地址保持不变,用户无需更改合约地址");
|
||||
console.log(" 2. 所有状态数据已保留");
|
||||
console.log(" 3. 建议运行验证脚本确认升级成功");
|
||||
console.log(" 4. 建议在测试网充分测试后再升级主网\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
117
scripts/upgrade/upgradeLendingPriceFeed.ts
Normal file
117
scripts/upgrade/upgradeLendingPriceFeed.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 升级 LendingPriceFeed 合约
|
||||
* 使用 upgrades.upgradeProxy() 进行 UUPS 升级
|
||||
*/
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log("\n==========================================");
|
||||
console.log("🔄 升级 LendingPriceFeed 合约");
|
||||
console.log("==========================================");
|
||||
console.log("升级账户:", deployer.address);
|
||||
console.log("账户余额:", ethers.formatEther(await ethers.provider.getBalance(deployer.address)), "ETH\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-lending.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件,请先运行部署脚本");
|
||||
}
|
||||
|
||||
const network = await ethers.provider.getNetwork();
|
||||
const chainId = network.chainId.toString();
|
||||
const allDeployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
const deployments = allDeployments[chainId];
|
||||
|
||||
if (!deployments) {
|
||||
throw new Error(`未找到网络 ${chainId} 的部署信息`);
|
||||
}
|
||||
|
||||
if (!deployments.lendingPriceFeed) {
|
||||
throw new Error("未找到 LendingPriceFeed Proxy 地址,请先运行部署脚本");
|
||||
}
|
||||
|
||||
console.log("📋 当前部署的合约:");
|
||||
console.log(" LendingPriceFeed Proxy:", deployments.lendingPriceFeed);
|
||||
if (deployments.lendingPriceFeedImpl) {
|
||||
console.log(" LendingPriceFeed Impl: ", deployments.lendingPriceFeedImpl);
|
||||
}
|
||||
console.log("");
|
||||
|
||||
// ========== 升级 LendingPriceFeed ==========
|
||||
console.log("🔄 Phase 1: 升级 LendingPriceFeed 合约");
|
||||
|
||||
console.log(" 当前 LendingPriceFeed Proxy:", deployments.lendingPriceFeed);
|
||||
if (deployments.lendingPriceFeedImpl) {
|
||||
console.log(" 当前 LendingPriceFeed Implementation:", deployments.lendingPriceFeedImpl);
|
||||
}
|
||||
|
||||
// 获取新的 LendingPriceFeed 合约工厂
|
||||
const LendingPriceFeedV2 = await ethers.getContractFactory("LendingPriceFeed");
|
||||
|
||||
console.log("\n 正在验证新实现合约...");
|
||||
const upgradedPriceFeed = await upgrades.upgradeProxy(
|
||||
deployments.lendingPriceFeed,
|
||||
LendingPriceFeedV2,
|
||||
{
|
||||
kind: "uups"
|
||||
}
|
||||
);
|
||||
await upgradedPriceFeed.waitForDeployment();
|
||||
|
||||
console.log(" ✅ LendingPriceFeed 已升级!");
|
||||
|
||||
// 获取新的实现合约地址
|
||||
const upgradedPriceFeedAddress = await upgradedPriceFeed.getAddress();
|
||||
const newPriceFeedImplAddress = await upgrades.erc1967.getImplementationAddress(upgradedPriceFeedAddress);
|
||||
console.log(" 新 LendingPriceFeed Implementation:", newPriceFeedImplAddress);
|
||||
|
||||
// 验证升级
|
||||
console.log("\n 验证升级结果:");
|
||||
console.log(" LendingPriceFeed Proxy (不变):", upgradedPriceFeedAddress);
|
||||
console.log(" Owner:", await upgradedPriceFeed.owner());
|
||||
console.log(" USDC Address:", await upgradedPriceFeed.usdcAddress());
|
||||
|
||||
// 保存升级历史
|
||||
if (!deployments.upgradeHistory) {
|
||||
deployments.upgradeHistory = [];
|
||||
}
|
||||
|
||||
deployments.upgradeHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
contract: "LendingPriceFeed",
|
||||
oldImplementation: deployments.lendingPriceFeedImpl || "未记录",
|
||||
newImplementation: newPriceFeedImplAddress,
|
||||
upgrader: deployer.address
|
||||
});
|
||||
|
||||
// 保存新的实现地址
|
||||
deployments.lendingPriceFeedImpl = newPriceFeedImplAddress;
|
||||
deployments.lastUpgradeTime = new Date().toISOString();
|
||||
|
||||
allDeployments[chainId] = deployments;
|
||||
fs.writeFileSync(deploymentsPath, JSON.stringify(allDeployments, null, 2));
|
||||
|
||||
console.log("\n✅ LendingPriceFeed 升级完成!");
|
||||
console.log("=====================================");
|
||||
console.log("旧实现:", deployments.upgradeHistory[deployments.upgradeHistory.length - 1].oldImplementation);
|
||||
console.log("新实现:", newPriceFeedImplAddress);
|
||||
console.log("=====================================\n");
|
||||
|
||||
console.log("💾 升级信息已保存到:", deploymentsPath);
|
||||
console.log("");
|
||||
console.log("📌 重要提示:");
|
||||
console.log(" 1. 代理地址保持不变,用户无需更改合约地址");
|
||||
console.log(" 2. 所有状态数据已保留");
|
||||
console.log(" 3. 建议运行验证脚本确认升级成功");
|
||||
console.log(" 4. 建议在测试网充分测试后再升级主网\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
113
scripts/upgrade/upgradeUSDY.ts
Normal file
113
scripts/upgrade/upgradeUSDY.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 升级 USDY 合约
|
||||
* 使用 upgrades.upgradeProxy() 进行 UUPS 升级
|
||||
*/
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log("\n==========================================");
|
||||
console.log("🔄 升级 USDY 合约");
|
||||
console.log("==========================================");
|
||||
console.log("升级账户:", deployer.address);
|
||||
console.log("账户余额:", ethers.formatEther(await ethers.provider.getBalance(deployer.address)), "ETH\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-ytlp.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件,请先运行部署脚本");
|
||||
}
|
||||
|
||||
const deployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
|
||||
if (!deployments.contracts?.USDY?.proxy) {
|
||||
throw new Error("未找到 USDY 部署信息");
|
||||
}
|
||||
|
||||
console.log("📋 当前部署的合约:");
|
||||
console.log(" USDY Proxy: ", deployments.contracts.USDY.proxy);
|
||||
console.log(" USDY Implementation: ", deployments.contracts.USDY.implementation);
|
||||
console.log("");
|
||||
|
||||
// ========== 升级 USDY ==========
|
||||
console.log("🔄 Phase 1: 升级 USDY 代理合约");
|
||||
|
||||
// 获取新的 USDY 合约工厂
|
||||
const USDYV2 = await ethers.getContractFactory("USDY");
|
||||
|
||||
console.log(" 正在验证新实现合约...");
|
||||
const upgradedUSDY = await upgrades.upgradeProxy(
|
||||
deployments.contracts.USDY.proxy,
|
||||
USDYV2,
|
||||
{
|
||||
kind: "uups"
|
||||
}
|
||||
);
|
||||
await upgradedUSDY.waitForDeployment();
|
||||
|
||||
console.log(" ✅ USDY 已升级!");
|
||||
|
||||
// 获取新的实现合约地址
|
||||
const upgradedUSDYAddress = await upgradedUSDY.getAddress();
|
||||
const newUSDYImplAddress = await upgrades.erc1967.getImplementationAddress(upgradedUSDYAddress);
|
||||
console.log(" 新 USDY Implementation:", newUSDYImplAddress);
|
||||
console.log("");
|
||||
|
||||
// ========== 验证升级结果 ==========
|
||||
console.log("🔄 Phase 2: 验证升级结果");
|
||||
|
||||
console.log(" USDY Proxy (不变):", upgradedUSDYAddress);
|
||||
console.log(" Name:", await upgradedUSDY.name());
|
||||
console.log(" Symbol:", await upgradedUSDY.symbol());
|
||||
console.log(" Total Supply:", ethers.formatUnits(await upgradedUSDY.totalSupply(), 6));
|
||||
console.log("");
|
||||
|
||||
// ========== 保存更新的部署信息 ==========
|
||||
if (!deployments.upgradeHistory) {
|
||||
deployments.upgradeHistory = [];
|
||||
}
|
||||
|
||||
deployments.upgradeHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
contract: "USDY",
|
||||
oldImplementation: deployments.contracts.USDY.implementation,
|
||||
newImplementation: newUSDYImplAddress,
|
||||
upgrader: deployer.address
|
||||
});
|
||||
|
||||
deployments.contracts.USDY.implementation = newUSDYImplAddress;
|
||||
deployments.lastUpdate = new Date().toISOString();
|
||||
|
||||
fs.writeFileSync(deploymentsPath, JSON.stringify(deployments, null, 2));
|
||||
console.log("💾 升级信息已保存到:", deploymentsPath);
|
||||
|
||||
// ========== 升级总结 ==========
|
||||
console.log("\n🎉 升级总结:");
|
||||
console.log("=====================================");
|
||||
console.log("旧 USDY Implementation:");
|
||||
console.log(" ", deployments.upgradeHistory[deployments.upgradeHistory.length - 1].oldImplementation);
|
||||
console.log("");
|
||||
console.log("新 USDY Implementation:");
|
||||
console.log(" ", newUSDYImplAddress);
|
||||
console.log("");
|
||||
console.log("USDY Proxy (不变):");
|
||||
console.log(" ", deployments.contracts.USDY.proxy);
|
||||
console.log("=====================================\n");
|
||||
|
||||
console.log("✅ 升级完成!");
|
||||
console.log("");
|
||||
console.log("📌 重要提示:");
|
||||
console.log(" 1. USDY 代理地址保持不变");
|
||||
console.log(" 2. 所有状态数据已保留");
|
||||
console.log(" 3. 建议运行验证脚本确认升级成功");
|
||||
console.log(" 4. 主网升级前务必充分测试\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
125
scripts/upgrade/upgradeYTAssetFactory.ts
Normal file
125
scripts/upgrade/upgradeYTAssetFactory.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 升级 YTAssetFactory 合约
|
||||
*
|
||||
* 升级步骤:
|
||||
* 1. 部署新的 YTAssetFactory 实现合约
|
||||
* 2. 使用 upgrades.upgradeProxy() 进行 UUPS 升级
|
||||
* 3. 验证新功能
|
||||
*
|
||||
* 注意:
|
||||
* - 这是 UUPS 代理升级,代理地址保持不变
|
||||
* - 所有状态数据已保留
|
||||
*/
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log("\n==========================================");
|
||||
console.log("🔄 升级 YTAssetFactory 系统");
|
||||
console.log("==========================================");
|
||||
console.log("升级账户:", deployer.address);
|
||||
console.log("账户余额:", ethers.formatEther(await ethers.provider.getBalance(deployer.address)), "ETH\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-vault-system.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件 deployments-vault-system.json,请先运行部署脚本");
|
||||
}
|
||||
|
||||
const deployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
|
||||
if (!deployments.contracts?.YTAssetFactory?.proxy) {
|
||||
throw new Error("未找到 YTAssetFactory 部署信息");
|
||||
}
|
||||
|
||||
console.log("📋 当前部署的合约:");
|
||||
console.log(" YTAssetFactory Proxy: ", deployments.contracts.YTAssetFactory.proxy);
|
||||
console.log(" YTAssetFactory Implementation: ", deployments.contracts.YTAssetFactory.implementation);
|
||||
console.log("");
|
||||
|
||||
// ========== Phase 1: 升级 YTAssetFactory ==========
|
||||
console.log("🔄 Phase 1: 升级 YTAssetFactory 代理合约");
|
||||
console.log(" 当前 YTAssetFactory Proxy:", deployments.contracts.YTAssetFactory.proxy);
|
||||
console.log(" 当前 YTAssetFactory Implementation:", deployments.contracts.YTAssetFactory.implementation);
|
||||
|
||||
// 获取新的 YTAssetFactory 合约工厂
|
||||
const YTAssetFactoryV2 = await ethers.getContractFactory("YTAssetFactory");
|
||||
|
||||
console.log("\n 正在验证新实现合约...");
|
||||
const upgradedFactory = await upgrades.upgradeProxy(
|
||||
deployments.contracts.YTAssetFactory.proxy,
|
||||
YTAssetFactoryV2,
|
||||
{
|
||||
kind: "uups"
|
||||
}
|
||||
);
|
||||
await upgradedFactory.waitForDeployment();
|
||||
|
||||
console.log(" ✅ YTAssetFactory 已升级!");
|
||||
|
||||
// 获取新的实现合约地址
|
||||
const upgradedFactoryAddress = await upgradedFactory.getAddress();
|
||||
const newFactoryImplAddress = await upgrades.erc1967.getImplementationAddress(upgradedFactoryAddress);
|
||||
console.log(" 新 YTAssetFactory Implementation:", newFactoryImplAddress);
|
||||
console.log("");
|
||||
|
||||
// ========== Phase 2: 验证升级结果 ==========
|
||||
console.log("🔄 Phase 2: 验证升级结果");
|
||||
|
||||
console.log(" YTAssetFactory Proxy (不变):", upgradedFactoryAddress);
|
||||
console.log(" Owner:", await upgradedFactory.owner());
|
||||
console.log(" Vault Implementation:", await upgradedFactory.vaultImplementation());
|
||||
console.log(" USDC Address:", await upgradedFactory.usdcAddress());
|
||||
console.log("");
|
||||
|
||||
// ========== 保存更新的部署信息 ==========
|
||||
// 保存旧的实现地址作为历史记录
|
||||
if (!deployments.upgradeHistory) {
|
||||
deployments.upgradeHistory = [];
|
||||
}
|
||||
|
||||
deployments.upgradeHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
contract: "YTAssetFactory",
|
||||
oldImplementation: deployments.contracts.YTAssetFactory.implementation,
|
||||
newImplementation: newFactoryImplAddress,
|
||||
upgrader: deployer.address
|
||||
});
|
||||
|
||||
// 更新当前实现地址
|
||||
deployments.contracts.YTAssetFactory.implementation = newFactoryImplAddress;
|
||||
deployments.lastUpdate = new Date().toISOString();
|
||||
|
||||
fs.writeFileSync(deploymentsPath, JSON.stringify(deployments, null, 2));
|
||||
console.log("💾 升级信息已保存到:", deploymentsPath);
|
||||
|
||||
// ========== 升级总结 ==========
|
||||
console.log("\n🎉 升级总结:");
|
||||
console.log("=====================================");
|
||||
console.log("旧 YTAssetFactory Implementation:");
|
||||
console.log(" ", deployments.upgradeHistory[deployments.upgradeHistory.length - 1].oldImplementation);
|
||||
console.log("");
|
||||
console.log("新 YTAssetFactory Implementation:");
|
||||
console.log(" ", newFactoryImplAddress);
|
||||
console.log("");
|
||||
console.log("Factory Proxy (不变):");
|
||||
console.log(" ", deployments.contracts.YTAssetFactory.proxy);
|
||||
console.log("=====================================\n");
|
||||
|
||||
console.log("✅ 升级完成!");
|
||||
console.log("");
|
||||
console.log("📌 重要提示:");
|
||||
console.log(" 1. YTAssetFactory 代理地址保持不变");
|
||||
console.log(" 2. 所有状态数据已保留");
|
||||
console.log(" 3. 建议运行验证脚本确认升级成功");
|
||||
console.log(" 4. 主网升级前务必充分测试\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
107
scripts/upgrade/upgradeYTLPToken.ts
Normal file
107
scripts/upgrade/upgradeYTLPToken.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 升级 YTLPToken 合约
|
||||
* 使用 upgrades.upgradeProxy() 进行 UUPS 升级
|
||||
*/
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log("\n==========================================");
|
||||
console.log("🔄 升级 YTLPToken 合约");
|
||||
console.log("==========================================");
|
||||
console.log("升级账户:", deployer.address);
|
||||
console.log("账户余额:", ethers.formatEther(await ethers.provider.getBalance(deployer.address)), "ETH\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-ytlp.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件,请先运行部署脚本");
|
||||
}
|
||||
|
||||
const deployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
|
||||
if (!deployments.contracts?.YTLPToken?.proxy) {
|
||||
throw new Error("未找到 YTLPToken 部署信息");
|
||||
}
|
||||
|
||||
console.log("📋 当前部署的合约:");
|
||||
console.log(" YTLPToken Proxy: ", deployments.contracts.YTLPToken.proxy);
|
||||
console.log(" YTLPToken Implementation: ", deployments.contracts.YTLPToken.implementation);
|
||||
console.log("");
|
||||
|
||||
// ========== 升级 YTLPToken ==========
|
||||
console.log("🔄 Phase 1: 升级 YTLPToken 代理合约");
|
||||
|
||||
// 获取新的 YTLPToken 合约工厂
|
||||
const YTLPTokenV2 = await ethers.getContractFactory("YTLPToken");
|
||||
|
||||
console.log(" 正在验证新实现合约...");
|
||||
const upgradedYTLPToken = await upgrades.upgradeProxy(
|
||||
deployments.contracts.YTLPToken.proxy,
|
||||
YTLPTokenV2,
|
||||
{
|
||||
kind: "uups"
|
||||
}
|
||||
);
|
||||
await upgradedYTLPToken.waitForDeployment();
|
||||
|
||||
console.log(" ✅ YTLPToken 已升级!");
|
||||
|
||||
// 获取新的实现合约地址
|
||||
const upgradedYTLPTokenAddress = await upgradedYTLPToken.getAddress();
|
||||
const newYTLPTokenImplAddress = await upgrades.erc1967.getImplementationAddress(upgradedYTLPTokenAddress);
|
||||
console.log(" 新 YTLPToken Implementation:", newYTLPTokenImplAddress);
|
||||
console.log("");
|
||||
|
||||
// ========== 验证升级结果 ==========
|
||||
console.log("🔄 Phase 2: 验证升级结果");
|
||||
|
||||
console.log(" YTLPToken Proxy (不变):", upgradedYTLPTokenAddress);
|
||||
console.log(" Name:", await upgradedYTLPToken.name());
|
||||
console.log(" Symbol:", await upgradedYTLPToken.symbol());
|
||||
console.log(" Total Supply:", ethers.formatEther(await upgradedYTLPToken.totalSupply()));
|
||||
console.log("");
|
||||
|
||||
// ========== 保存更新的部署信息 ==========
|
||||
if (!deployments.upgradeHistory) {
|
||||
deployments.upgradeHistory = [];
|
||||
}
|
||||
|
||||
deployments.upgradeHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
contract: "YTLPToken",
|
||||
oldImplementation: deployments.contracts.YTLPToken.implementation,
|
||||
newImplementation: newYTLPTokenImplAddress,
|
||||
upgrader: deployer.address
|
||||
});
|
||||
|
||||
deployments.contracts.YTLPToken.implementation = newYTLPTokenImplAddress;
|
||||
deployments.lastUpdate = new Date().toISOString();
|
||||
|
||||
fs.writeFileSync(deploymentsPath, JSON.stringify(deployments, null, 2));
|
||||
console.log("💾 升级信息已保存到:", deploymentsPath);
|
||||
|
||||
// ========== 升级总结 ==========
|
||||
console.log("\n🎉 升级总结:");
|
||||
console.log("=====================================");
|
||||
console.log("旧 YTLPToken Implementation:");
|
||||
console.log(" ", deployments.upgradeHistory[deployments.upgradeHistory.length - 1].oldImplementation);
|
||||
console.log("");
|
||||
console.log("新 YTLPToken Implementation:");
|
||||
console.log(" ", newYTLPTokenImplAddress);
|
||||
console.log("");
|
||||
console.log("YTLPToken Proxy (不变):");
|
||||
console.log(" ", deployments.contracts.YTLPToken.proxy);
|
||||
console.log("=====================================\n");
|
||||
|
||||
console.log("✅ 升级完成!\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
105
scripts/upgrade/upgradeYTPoolManager.ts
Normal file
105
scripts/upgrade/upgradeYTPoolManager.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 升级 YTPoolManager 合约
|
||||
* 使用 upgrades.upgradeProxy() 进行 UUPS 升级
|
||||
*/
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log("\n==========================================");
|
||||
console.log("🔄 升级 YTPoolManager 合约");
|
||||
console.log("==========================================");
|
||||
console.log("升级账户:", deployer.address);
|
||||
console.log("账户余额:", ethers.formatEther(await ethers.provider.getBalance(deployer.address)), "ETH\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-ytlp.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件,请先运行部署脚本");
|
||||
}
|
||||
|
||||
const deployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
|
||||
if (!deployments.contracts?.YTPoolManager?.proxy) {
|
||||
throw new Error("未找到 YTPoolManager 部署信息");
|
||||
}
|
||||
|
||||
console.log("📋 当前部署的合约:");
|
||||
console.log(" YTPoolManager Proxy: ", deployments.contracts.YTPoolManager.proxy);
|
||||
console.log(" YTPoolManager Implementation: ", deployments.contracts.YTPoolManager.implementation);
|
||||
console.log("");
|
||||
|
||||
// ========== 升级 YTPoolManager ==========
|
||||
console.log("🔄 Phase 1: 升级 YTPoolManager 代理合约");
|
||||
|
||||
// 获取新的 YTPoolManager 合约工厂
|
||||
const YTPoolManagerV2 = await ethers.getContractFactory("YTPoolManager");
|
||||
|
||||
console.log(" 正在验证新实现合约...");
|
||||
const upgradedYTPoolManager = await upgrades.upgradeProxy(
|
||||
deployments.contracts.YTPoolManager.proxy,
|
||||
YTPoolManagerV2,
|
||||
{
|
||||
kind: "uups"
|
||||
}
|
||||
);
|
||||
await upgradedYTPoolManager.waitForDeployment();
|
||||
|
||||
console.log(" ✅ YTPoolManager 已升级!");
|
||||
|
||||
// 获取新的实现合约地址
|
||||
const upgradedYTPoolManagerAddress = await upgradedYTPoolManager.getAddress();
|
||||
const newYTPoolManagerImplAddress = await upgrades.erc1967.getImplementationAddress(upgradedYTPoolManagerAddress);
|
||||
console.log(" 新 YTPoolManager Implementation:", newYTPoolManagerImplAddress);
|
||||
console.log("");
|
||||
|
||||
// ========== 验证升级结果 ==========
|
||||
console.log("🔄 Phase 2: 验证升级结果");
|
||||
|
||||
console.log(" YTPoolManager Proxy (不变):", upgradedYTPoolManagerAddress);
|
||||
console.log(" Owner:", await upgradedYTPoolManager.owner());
|
||||
console.log("");
|
||||
|
||||
// ========== 保存更新的部署信息 ==========
|
||||
if (!deployments.upgradeHistory) {
|
||||
deployments.upgradeHistory = [];
|
||||
}
|
||||
|
||||
deployments.upgradeHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
contract: "YTPoolManager",
|
||||
oldImplementation: deployments.contracts.YTPoolManager.implementation,
|
||||
newImplementation: newYTPoolManagerImplAddress,
|
||||
upgrader: deployer.address
|
||||
});
|
||||
|
||||
deployments.contracts.YTPoolManager.implementation = newYTPoolManagerImplAddress;
|
||||
deployments.lastUpdate = new Date().toISOString();
|
||||
|
||||
fs.writeFileSync(deploymentsPath, JSON.stringify(deployments, null, 2));
|
||||
console.log("💾 升级信息已保存到:", deploymentsPath);
|
||||
|
||||
// ========== 升级总结 ==========
|
||||
console.log("\n🎉 升级总结:");
|
||||
console.log("=====================================");
|
||||
console.log("旧 YTPoolManager Implementation:");
|
||||
console.log(" ", deployments.upgradeHistory[deployments.upgradeHistory.length - 1].oldImplementation);
|
||||
console.log("");
|
||||
console.log("新 YTPoolManager Implementation:");
|
||||
console.log(" ", newYTPoolManagerImplAddress);
|
||||
console.log("");
|
||||
console.log("YTPoolManager Proxy (不变):");
|
||||
console.log(" ", deployments.contracts.YTPoolManager.proxy);
|
||||
console.log("=====================================\n");
|
||||
|
||||
console.log("✅ 升级完成!\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
105
scripts/upgrade/upgradeYTPriceFeed.ts
Normal file
105
scripts/upgrade/upgradeYTPriceFeed.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 升级 YTPriceFeed 合约
|
||||
* 使用 upgrades.upgradeProxy() 进行 UUPS 升级
|
||||
*/
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log("\n==========================================");
|
||||
console.log("🔄 升级 YTPriceFeed 合约");
|
||||
console.log("==========================================");
|
||||
console.log("升级账户:", deployer.address);
|
||||
console.log("账户余额:", ethers.formatEther(await ethers.provider.getBalance(deployer.address)), "ETH\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-ytlp.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件,请先运行部署脚本");
|
||||
}
|
||||
|
||||
const deployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
|
||||
if (!deployments.contracts?.YTPriceFeed?.proxy) {
|
||||
throw new Error("未找到 YTPriceFeed 部署信息");
|
||||
}
|
||||
|
||||
console.log("📋 当前部署的合约:");
|
||||
console.log(" YTPriceFeed Proxy: ", deployments.contracts.YTPriceFeed.proxy);
|
||||
console.log(" YTPriceFeed Implementation: ", deployments.contracts.YTPriceFeed.implementation);
|
||||
console.log("");
|
||||
|
||||
// ========== 升级 YTPriceFeed ==========
|
||||
console.log("🔄 Phase 1: 升级 YTPriceFeed 代理合约");
|
||||
|
||||
// 获取新的 YTPriceFeed 合约工厂
|
||||
const YTPriceFeedV2 = await ethers.getContractFactory("YTPriceFeed");
|
||||
|
||||
console.log(" 正在验证新实现合约...");
|
||||
const upgradedYTPriceFeed = await upgrades.upgradeProxy(
|
||||
deployments.contracts.YTPriceFeed.proxy,
|
||||
YTPriceFeedV2,
|
||||
{
|
||||
kind: "uups"
|
||||
}
|
||||
);
|
||||
await upgradedYTPriceFeed.waitForDeployment();
|
||||
|
||||
console.log(" ✅ YTPriceFeed 已升级!");
|
||||
|
||||
// 获取新的实现合约地址
|
||||
const upgradedYTPriceFeedAddress = await upgradedYTPriceFeed.getAddress();
|
||||
const newYTPriceFeedImplAddress = await upgrades.erc1967.getImplementationAddress(upgradedYTPriceFeedAddress);
|
||||
console.log(" 新 YTPriceFeed Implementation:", newYTPriceFeedImplAddress);
|
||||
console.log("");
|
||||
|
||||
// ========== 验证升级结果 ==========
|
||||
console.log("🔄 Phase 2: 验证升级结果");
|
||||
|
||||
console.log(" YTPriceFeed Proxy (不变):", upgradedYTPriceFeedAddress);
|
||||
console.log(" Owner:", await upgradedYTPriceFeed.owner());
|
||||
console.log("");
|
||||
|
||||
// ========== 保存更新的部署信息 ==========
|
||||
if (!deployments.upgradeHistory) {
|
||||
deployments.upgradeHistory = [];
|
||||
}
|
||||
|
||||
deployments.upgradeHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
contract: "YTPriceFeed",
|
||||
oldImplementation: deployments.contracts.YTPriceFeed.implementation,
|
||||
newImplementation: newYTPriceFeedImplAddress,
|
||||
upgrader: deployer.address
|
||||
});
|
||||
|
||||
deployments.contracts.YTPriceFeed.implementation = newYTPriceFeedImplAddress;
|
||||
deployments.lastUpdate = new Date().toISOString();
|
||||
|
||||
fs.writeFileSync(deploymentsPath, JSON.stringify(deployments, null, 2));
|
||||
console.log("💾 升级信息已保存到:", deploymentsPath);
|
||||
|
||||
// ========== 升级总结 ==========
|
||||
console.log("\n🎉 升级总结:");
|
||||
console.log("=====================================");
|
||||
console.log("旧 YTPriceFeed Implementation:");
|
||||
console.log(" ", deployments.upgradeHistory[deployments.upgradeHistory.length - 1].oldImplementation);
|
||||
console.log("");
|
||||
console.log("新 YTPriceFeed Implementation:");
|
||||
console.log(" ", newYTPriceFeedImplAddress);
|
||||
console.log("");
|
||||
console.log("YTPriceFeed Proxy (不变):");
|
||||
console.log(" ", deployments.contracts.YTPriceFeed.proxy);
|
||||
console.log("=====================================\n");
|
||||
|
||||
console.log("✅ 升级完成!\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
105
scripts/upgrade/upgradeYTRewardRouter.ts
Normal file
105
scripts/upgrade/upgradeYTRewardRouter.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 升级 YTRewardRouter 合约
|
||||
* 使用 upgrades.upgradeProxy() 进行 UUPS 升级
|
||||
*/
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log("\n==========================================");
|
||||
console.log("🔄 升级 YTRewardRouter 合约");
|
||||
console.log("==========================================");
|
||||
console.log("升级账户:", deployer.address);
|
||||
console.log("账户余额:", ethers.formatEther(await ethers.provider.getBalance(deployer.address)), "ETH\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-ytlp.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件,请先运行部署脚本");
|
||||
}
|
||||
|
||||
const deployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
|
||||
if (!deployments.contracts?.YTRewardRouter?.proxy) {
|
||||
throw new Error("未找到 YTRewardRouter 部署信息");
|
||||
}
|
||||
|
||||
console.log("📋 当前部署的合约:");
|
||||
console.log(" YTRewardRouter Proxy: ", deployments.contracts.YTRewardRouter.proxy);
|
||||
console.log(" YTRewardRouter Implementation: ", deployments.contracts.YTRewardRouter.implementation);
|
||||
console.log("");
|
||||
|
||||
// ========== 升级 YTRewardRouter ==========
|
||||
console.log("🔄 Phase 1: 升级 YTRewardRouter 代理合约");
|
||||
|
||||
// 获取新的 YTRewardRouter 合约工厂
|
||||
const YTRewardRouterV2 = await ethers.getContractFactory("YTRewardRouter");
|
||||
|
||||
console.log(" 正在验证新实现合约...");
|
||||
const upgradedYTRewardRouter = await upgrades.upgradeProxy(
|
||||
deployments.contracts.YTRewardRouter.proxy,
|
||||
YTRewardRouterV2,
|
||||
{
|
||||
kind: "uups"
|
||||
}
|
||||
);
|
||||
await upgradedYTRewardRouter.waitForDeployment();
|
||||
|
||||
console.log(" ✅ YTRewardRouter 已升级!");
|
||||
|
||||
// 获取新的实现合约地址
|
||||
const upgradedYTRewardRouterAddress = await upgradedYTRewardRouter.getAddress();
|
||||
const newYTRewardRouterImplAddress = await upgrades.erc1967.getImplementationAddress(upgradedYTRewardRouterAddress);
|
||||
console.log(" 新 YTRewardRouter Implementation:", newYTRewardRouterImplAddress);
|
||||
console.log("");
|
||||
|
||||
// ========== 验证升级结果 ==========
|
||||
console.log("🔄 Phase 2: 验证升级结果");
|
||||
|
||||
console.log(" YTRewardRouter Proxy (不变):", upgradedYTRewardRouterAddress);
|
||||
console.log(" Owner:", await upgradedYTRewardRouter.owner());
|
||||
console.log("");
|
||||
|
||||
// ========== 保存更新的部署信息 ==========
|
||||
if (!deployments.upgradeHistory) {
|
||||
deployments.upgradeHistory = [];
|
||||
}
|
||||
|
||||
deployments.upgradeHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
contract: "YTRewardRouter",
|
||||
oldImplementation: deployments.contracts.YTRewardRouter.implementation,
|
||||
newImplementation: newYTRewardRouterImplAddress,
|
||||
upgrader: deployer.address
|
||||
});
|
||||
|
||||
deployments.contracts.YTRewardRouter.implementation = newYTRewardRouterImplAddress;
|
||||
deployments.lastUpdate = new Date().toISOString();
|
||||
|
||||
fs.writeFileSync(deploymentsPath, JSON.stringify(deployments, null, 2));
|
||||
console.log("💾 升级信息已保存到:", deploymentsPath);
|
||||
|
||||
// ========== 升级总结 ==========
|
||||
console.log("\n🎉 升级总结:");
|
||||
console.log("=====================================");
|
||||
console.log("旧 YTRewardRouter Implementation:");
|
||||
console.log(" ", deployments.upgradeHistory[deployments.upgradeHistory.length - 1].oldImplementation);
|
||||
console.log("");
|
||||
console.log("新 YTRewardRouter Implementation:");
|
||||
console.log(" ", newYTRewardRouterImplAddress);
|
||||
console.log("");
|
||||
console.log("YTRewardRouter Proxy (不变):");
|
||||
console.log(" ", deployments.contracts.YTRewardRouter.proxy);
|
||||
console.log("=====================================\n");
|
||||
|
||||
console.log("✅ 升级完成!\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
105
scripts/upgrade/upgradeYTVault.ts
Normal file
105
scripts/upgrade/upgradeYTVault.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 升级 YTVault 合约
|
||||
* 使用 upgrades.upgradeProxy() 进行 UUPS 升级
|
||||
*/
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log("\n==========================================");
|
||||
console.log("🔄 升级 YTVault 合约");
|
||||
console.log("==========================================");
|
||||
console.log("升级账户:", deployer.address);
|
||||
console.log("账户余额:", ethers.formatEther(await ethers.provider.getBalance(deployer.address)), "ETH\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-ytlp.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件,请先运行部署脚本");
|
||||
}
|
||||
|
||||
const deployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
|
||||
if (!deployments.contracts?.YTVault?.proxy) {
|
||||
throw new Error("未找到 YTVault 部署信息");
|
||||
}
|
||||
|
||||
console.log("📋 当前部署的合约:");
|
||||
console.log(" YTVault Proxy: ", deployments.contracts.YTVault.proxy);
|
||||
console.log(" YTVault Implementation: ", deployments.contracts.YTVault.implementation);
|
||||
console.log("");
|
||||
|
||||
// ========== 升级 YTVault ==========
|
||||
console.log("🔄 Phase 1: 升级 YTVault 代理合约");
|
||||
|
||||
// 获取新的 YTVault 合约工厂
|
||||
const YTVaultV2 = await ethers.getContractFactory("YTVault");
|
||||
|
||||
console.log(" 正在验证新实现合约...");
|
||||
const upgradedYTVault = await upgrades.upgradeProxy(
|
||||
deployments.contracts.YTVault.proxy,
|
||||
YTVaultV2,
|
||||
{
|
||||
kind: "uups"
|
||||
}
|
||||
);
|
||||
await upgradedYTVault.waitForDeployment();
|
||||
|
||||
console.log(" ✅ YTVault 已升级!");
|
||||
|
||||
// 获取新的实现合约地址
|
||||
const upgradedYTVaultAddress = await upgradedYTVault.getAddress();
|
||||
const newYTVaultImplAddress = await upgrades.erc1967.getImplementationAddress(upgradedYTVaultAddress);
|
||||
console.log(" 新 YTVault Implementation:", newYTVaultImplAddress);
|
||||
console.log("");
|
||||
|
||||
// ========== 验证升级结果 ==========
|
||||
console.log("🔄 Phase 2: 验证升级结果");
|
||||
|
||||
console.log(" YTVault Proxy (不变):", upgradedYTVaultAddress);
|
||||
console.log(" Owner:", await upgradedYTVault.owner());
|
||||
console.log("");
|
||||
|
||||
// ========== 保存更新的部署信息 ==========
|
||||
if (!deployments.upgradeHistory) {
|
||||
deployments.upgradeHistory = [];
|
||||
}
|
||||
|
||||
deployments.upgradeHistory.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
contract: "YTVault",
|
||||
oldImplementation: deployments.contracts.YTVault.implementation,
|
||||
newImplementation: newYTVaultImplAddress,
|
||||
upgrader: deployer.address
|
||||
});
|
||||
|
||||
deployments.contracts.YTVault.implementation = newYTVaultImplAddress;
|
||||
deployments.lastUpdate = new Date().toISOString();
|
||||
|
||||
fs.writeFileSync(deploymentsPath, JSON.stringify(deployments, null, 2));
|
||||
console.log("💾 升级信息已保存到:", deploymentsPath);
|
||||
|
||||
// ========== 升级总结 ==========
|
||||
console.log("\n🎉 升级总结:");
|
||||
console.log("=====================================");
|
||||
console.log("旧 YTVault Implementation:");
|
||||
console.log(" ", deployments.upgradeHistory[deployments.upgradeHistory.length - 1].oldImplementation);
|
||||
console.log("");
|
||||
console.log("新 YTVault Implementation:");
|
||||
console.log(" ", newYTVaultImplAddress);
|
||||
console.log("");
|
||||
console.log("YTVault Proxy (不变):");
|
||||
console.log(" ", deployments.contracts.YTVault.proxy);
|
||||
console.log("=====================================\n");
|
||||
|
||||
console.log("✅ 升级完成!\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,182 +0,0 @@
|
||||
import { ethers, upgrades } from "hardhat";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 验证 YTAssetVault 升级结果
|
||||
*
|
||||
* 功能:
|
||||
* 1. 检查 Factory 的实现地址是否已更新
|
||||
* 2. 验证现有 Vault 的实现地址
|
||||
* 3. 测试新功能是否可用
|
||||
*/
|
||||
async function main() {
|
||||
console.log("\n==========================================");
|
||||
console.log("🔍 验证 YTAssetVault 升级结果");
|
||||
console.log("==========================================\n");
|
||||
|
||||
// ========== 读取部署信息 ==========
|
||||
const deploymentsPath = path.join(__dirname, "../../deployments-vault-system.json");
|
||||
if (!fs.existsSync(deploymentsPath)) {
|
||||
throw new Error("未找到部署信息文件");
|
||||
}
|
||||
|
||||
const deployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf-8"));
|
||||
const factory = await ethers.getContractAt(
|
||||
"YTAssetFactory",
|
||||
deployments.contracts.YTAssetFactory.proxy
|
||||
);
|
||||
|
||||
// ========== 验证 Factory ==========
|
||||
console.log("📋 验证 Factory 配置");
|
||||
console.log("=====================================");
|
||||
|
||||
const currentImplInFactory = await factory.vaultImplementation();
|
||||
const expectedImpl = deployments.contracts.YTAssetVault.implementation;
|
||||
|
||||
console.log("Factory Proxy: ", deployments.contracts.YTAssetFactory.proxy);
|
||||
console.log("当前 vaultImplementation:", currentImplInFactory);
|
||||
console.log("配置文件中的实现: ", expectedImpl);
|
||||
|
||||
if (currentImplInFactory.toLowerCase() === expectedImpl.toLowerCase()) {
|
||||
console.log("✅ Factory 配置正确!\n");
|
||||
} else {
|
||||
console.log("❌ Factory 配置不匹配!\n");
|
||||
}
|
||||
|
||||
// ========== 验证已部署的 Vaults ==========
|
||||
const vaults = deployments.vaults || [];
|
||||
|
||||
if (vaults.length === 0) {
|
||||
console.log("ℹ️ 没有已部署的 Vault\n");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("📋 验证已部署的 Vaults");
|
||||
console.log("=====================================");
|
||||
console.log(`发现 ${vaults.length} 个 Vault\n`);
|
||||
|
||||
const results: any[] = [];
|
||||
|
||||
for (let i = 0; i < vaults.length; i++) {
|
||||
const vaultInfo = vaults[i];
|
||||
console.log(`[${i + 1}/${vaults.length}] 检查 ${vaultInfo.symbol} (${vaultInfo.address})`);
|
||||
|
||||
try {
|
||||
// 获取实现地址
|
||||
const implAddress = await upgrades.erc1967.getImplementationAddress(vaultInfo.address);
|
||||
const isUpgraded = implAddress.toLowerCase() === expectedImpl.toLowerCase();
|
||||
|
||||
console.log(` 实现地址: ${implAddress}`);
|
||||
console.log(` 状态: ${isUpgraded ? '✅ 已升级' : '⏸️ 未升级'}`);
|
||||
|
||||
// 如果已升级,测试新功能
|
||||
if (isUpgraded) {
|
||||
const vault = await ethers.getContractAt("YTAssetVault", vaultInfo.address);
|
||||
|
||||
try {
|
||||
// 测试新增的状态变量
|
||||
const pendingCount = await vault.pendingRequestsCount();
|
||||
const requestIdCounter = await vault.requestIdCounter();
|
||||
const processedUpToIndex = await vault.processedUpToIndex();
|
||||
|
||||
console.log(` 新功能验证:`);
|
||||
console.log(` - pendingRequestsCount: ${pendingCount}`);
|
||||
console.log(` - requestIdCounter: ${requestIdCounter}`);
|
||||
console.log(` - processedUpToIndex: ${processedUpToIndex}`);
|
||||
|
||||
// 测试新增的查询函数
|
||||
const queueProgress = await vault.getQueueProgress();
|
||||
console.log(` - 队列进度: ${queueProgress[0]}/${queueProgress[1]} (待处理: ${queueProgress[2]})`);
|
||||
|
||||
console.log(` ✅ 新功能工作正常`);
|
||||
|
||||
results.push({
|
||||
index: i,
|
||||
symbol: vaultInfo.symbol,
|
||||
address: vaultInfo.address,
|
||||
upgraded: true,
|
||||
functional: true
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.log(` ⚠️ 新功能测试失败: ${error.message}`);
|
||||
results.push({
|
||||
index: i,
|
||||
symbol: vaultInfo.symbol,
|
||||
address: vaultInfo.address,
|
||||
upgraded: true,
|
||||
functional: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
} else {
|
||||
results.push({
|
||||
index: i,
|
||||
symbol: vaultInfo.symbol,
|
||||
address: vaultInfo.address,
|
||||
upgraded: false,
|
||||
functional: false
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log(` ❌ 检查失败: ${error.message}`);
|
||||
results.push({
|
||||
index: i,
|
||||
symbol: vaultInfo.symbol,
|
||||
address: vaultInfo.address,
|
||||
upgraded: false,
|
||||
functional: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
console.log("");
|
||||
}
|
||||
|
||||
// ========== 验证总结 ==========
|
||||
console.log("📊 验证总结");
|
||||
console.log("=====================================");
|
||||
|
||||
const upgraded = results.filter(r => r.upgraded);
|
||||
const functional = results.filter(r => r.functional);
|
||||
const needsUpgrade = results.filter(r => !r.upgraded);
|
||||
|
||||
console.log(`总 Vaults 数量: ${results.length}`);
|
||||
console.log(`已升级: ${upgraded.length} ✅`);
|
||||
console.log(`功能正常: ${functional.length} ✅`);
|
||||
console.log(`待升级: ${needsUpgrade.length} ${needsUpgrade.length > 0 ? '⏸️' : ''}`);
|
||||
console.log("");
|
||||
|
||||
if (needsUpgrade.length > 0) {
|
||||
console.log("⏸️ 待升级的 Vaults:");
|
||||
needsUpgrade.forEach(v => {
|
||||
console.log(` [${v.index}] ${v.symbol}: ${v.address}`);
|
||||
});
|
||||
console.log("");
|
||||
console.log("💡 升级命令:");
|
||||
console.log(` factory.upgradeVault("vaultAddress", "${expectedImpl}")`);
|
||||
console.log("");
|
||||
}
|
||||
|
||||
// ========== 升级历史 ==========
|
||||
if (deployments.upgradeHistory && deployments.upgradeHistory.length > 0) {
|
||||
console.log("📜 升级历史");
|
||||
console.log("=====================================");
|
||||
deployments.upgradeHistory.forEach((h: any, idx: number) => {
|
||||
console.log(`[${idx + 1}] ${h.timestamp}`);
|
||||
console.log(` 升级者: ${h.upgrader}`);
|
||||
console.log(` 旧实现: ${h.oldImplementation}`);
|
||||
console.log(` 新实现: ${h.newImplementation}`);
|
||||
});
|
||||
console.log("");
|
||||
}
|
||||
|
||||
console.log("✅ 验证完成!\n");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user