add absorb script and upgrade script
This commit is contained in:
@@ -2892,6 +2892,277 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"9a874c5ae80404ee71254240a9dd296c76e6cc0ebf4a68273e9341e65769ce06": {
|
||||
"address": "0x2a6975bC619EDC8399B9Cef9ba9554673e56208a",
|
||||
"txHash": "0xe599b99466f8eb446a9dab93cffd5bfcf366c297fce83dedaafa24d024b8f6d1",
|
||||
"layout": {
|
||||
"solcVersion": "0.8.28",
|
||||
"storage": [
|
||||
{
|
||||
"label": "factory",
|
||||
"offset": 0,
|
||||
"slot": "0",
|
||||
"type": "t_mapping(t_address,t_address)",
|
||||
"contract": "ConfiguratorStorage",
|
||||
"src": "contracts/ytLending/ConfiguratorStorage.sol:12"
|
||||
},
|
||||
{
|
||||
"label": "configuratorParams",
|
||||
"offset": 0,
|
||||
"slot": "1",
|
||||
"type": "t_mapping(t_address,t_struct(Configuration)6548_storage)",
|
||||
"contract": "ConfiguratorStorage",
|
||||
"src": "contracts/ytLending/ConfiguratorStorage.sol:15"
|
||||
},
|
||||
{
|
||||
"label": "__gap",
|
||||
"offset": 0,
|
||||
"slot": "2",
|
||||
"type": "t_array(t_uint256)50_storage",
|
||||
"contract": "Configurator",
|
||||
"src": "contracts/ytLending/Configurator.sol:209"
|
||||
}
|
||||
],
|
||||
"types": {
|
||||
"t_address": {
|
||||
"label": "address",
|
||||
"numberOfBytes": "20"
|
||||
},
|
||||
"t_bool": {
|
||||
"label": "bool",
|
||||
"numberOfBytes": "1"
|
||||
},
|
||||
"t_struct(InitializableStorage)119_storage": {
|
||||
"label": "struct Initializable.InitializableStorage",
|
||||
"members": [
|
||||
{
|
||||
"label": "_initialized",
|
||||
"type": "t_uint64",
|
||||
"offset": 0,
|
||||
"slot": "0"
|
||||
},
|
||||
{
|
||||
"label": "_initializing",
|
||||
"type": "t_bool",
|
||||
"offset": 8,
|
||||
"slot": "0"
|
||||
}
|
||||
],
|
||||
"numberOfBytes": "32"
|
||||
},
|
||||
"t_struct(OwnableStorage)59_storage": {
|
||||
"label": "struct OwnableUpgradeable.OwnableStorage",
|
||||
"members": [
|
||||
{
|
||||
"label": "_owner",
|
||||
"type": "t_address",
|
||||
"offset": 0,
|
||||
"slot": "0"
|
||||
}
|
||||
],
|
||||
"numberOfBytes": "32"
|
||||
},
|
||||
"t_uint64": {
|
||||
"label": "uint64",
|
||||
"numberOfBytes": "8"
|
||||
},
|
||||
"t_array(t_struct(AssetConfig)6517_storage)dyn_storage": {
|
||||
"label": "struct LendingConfiguration.AssetConfig[]",
|
||||
"numberOfBytes": "32"
|
||||
},
|
||||
"t_array(t_uint256)50_storage": {
|
||||
"label": "uint256[50]",
|
||||
"numberOfBytes": "1600"
|
||||
},
|
||||
"t_mapping(t_address,t_address)": {
|
||||
"label": "mapping(address => address)",
|
||||
"numberOfBytes": "32"
|
||||
},
|
||||
"t_mapping(t_address,t_struct(Configuration)6548_storage)": {
|
||||
"label": "mapping(address => struct LendingConfiguration.Configuration)",
|
||||
"numberOfBytes": "32"
|
||||
},
|
||||
"t_struct(AssetConfig)6517_storage": {
|
||||
"label": "struct LendingConfiguration.AssetConfig",
|
||||
"members": [
|
||||
{
|
||||
"label": "asset",
|
||||
"type": "t_address",
|
||||
"offset": 0,
|
||||
"slot": "0"
|
||||
},
|
||||
{
|
||||
"label": "decimals",
|
||||
"type": "t_uint8",
|
||||
"offset": 20,
|
||||
"slot": "0"
|
||||
},
|
||||
{
|
||||
"label": "borrowCollateralFactor",
|
||||
"type": "t_uint64",
|
||||
"offset": 21,
|
||||
"slot": "0"
|
||||
},
|
||||
{
|
||||
"label": "liquidateCollateralFactor",
|
||||
"type": "t_uint64",
|
||||
"offset": 0,
|
||||
"slot": "1"
|
||||
},
|
||||
{
|
||||
"label": "liquidationFactor",
|
||||
"type": "t_uint64",
|
||||
"offset": 8,
|
||||
"slot": "1"
|
||||
},
|
||||
{
|
||||
"label": "supplyCap",
|
||||
"type": "t_uint128",
|
||||
"offset": 16,
|
||||
"slot": "1"
|
||||
}
|
||||
],
|
||||
"numberOfBytes": "64"
|
||||
},
|
||||
"t_struct(Configuration)6548_storage": {
|
||||
"label": "struct LendingConfiguration.Configuration",
|
||||
"members": [
|
||||
{
|
||||
"label": "baseToken",
|
||||
"type": "t_address",
|
||||
"offset": 0,
|
||||
"slot": "0"
|
||||
},
|
||||
{
|
||||
"label": "lendingPriceSource",
|
||||
"type": "t_address",
|
||||
"offset": 0,
|
||||
"slot": "1"
|
||||
},
|
||||
{
|
||||
"label": "supplyKink",
|
||||
"type": "t_uint64",
|
||||
"offset": 20,
|
||||
"slot": "1"
|
||||
},
|
||||
{
|
||||
"label": "supplyPerYearInterestRateSlopeLow",
|
||||
"type": "t_uint64",
|
||||
"offset": 0,
|
||||
"slot": "2"
|
||||
},
|
||||
{
|
||||
"label": "supplyPerYearInterestRateSlopeHigh",
|
||||
"type": "t_uint64",
|
||||
"offset": 8,
|
||||
"slot": "2"
|
||||
},
|
||||
{
|
||||
"label": "supplyPerYearInterestRateBase",
|
||||
"type": "t_uint64",
|
||||
"offset": 16,
|
||||
"slot": "2"
|
||||
},
|
||||
{
|
||||
"label": "borrowKink",
|
||||
"type": "t_uint64",
|
||||
"offset": 24,
|
||||
"slot": "2"
|
||||
},
|
||||
{
|
||||
"label": "borrowPerYearInterestRateSlopeLow",
|
||||
"type": "t_uint64",
|
||||
"offset": 0,
|
||||
"slot": "3"
|
||||
},
|
||||
{
|
||||
"label": "borrowPerYearInterestRateSlopeHigh",
|
||||
"type": "t_uint64",
|
||||
"offset": 8,
|
||||
"slot": "3"
|
||||
},
|
||||
{
|
||||
"label": "borrowPerYearInterestRateBase",
|
||||
"type": "t_uint64",
|
||||
"offset": 16,
|
||||
"slot": "3"
|
||||
},
|
||||
{
|
||||
"label": "storeFrontPriceFactor",
|
||||
"type": "t_uint64",
|
||||
"offset": 24,
|
||||
"slot": "3"
|
||||
},
|
||||
{
|
||||
"label": "baseBorrowMin",
|
||||
"type": "t_uint104",
|
||||
"offset": 0,
|
||||
"slot": "4"
|
||||
},
|
||||
{
|
||||
"label": "targetReserves",
|
||||
"type": "t_uint104",
|
||||
"offset": 13,
|
||||
"slot": "4"
|
||||
},
|
||||
{
|
||||
"label": "assetConfigs",
|
||||
"type": "t_array(t_struct(AssetConfig)6517_storage)dyn_storage",
|
||||
"offset": 0,
|
||||
"slot": "5"
|
||||
}
|
||||
],
|
||||
"numberOfBytes": "192"
|
||||
},
|
||||
"t_uint104": {
|
||||
"label": "uint104",
|
||||
"numberOfBytes": "13"
|
||||
},
|
||||
"t_uint128": {
|
||||
"label": "uint128",
|
||||
"numberOfBytes": "16"
|
||||
},
|
||||
"t_uint256": {
|
||||
"label": "uint256",
|
||||
"numberOfBytes": "32"
|
||||
},
|
||||
"t_uint8": {
|
||||
"label": "uint8",
|
||||
"numberOfBytes": "1"
|
||||
}
|
||||
},
|
||||
"namespaces": {
|
||||
"erc7201:openzeppelin.storage.Ownable": [
|
||||
{
|
||||
"contract": "OwnableUpgradeable",
|
||||
"label": "_owner",
|
||||
"type": "t_address",
|
||||
"src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:24",
|
||||
"offset": 0,
|
||||
"slot": "0"
|
||||
}
|
||||
],
|
||||
"erc7201:openzeppelin.storage.Initializable": [
|
||||
{
|
||||
"contract": "Initializable",
|
||||
"label": "_initialized",
|
||||
"type": "t_uint64",
|
||||
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:69",
|
||||
"offset": 0,
|
||||
"slot": "0"
|
||||
},
|
||||
{
|
||||
"contract": "Initializable",
|
||||
"label": "_initializing",
|
||||
"type": "t_bool",
|
||||
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:73",
|
||||
"offset": 8,
|
||||
"slot": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,11 +272,6 @@
|
||||
"name": "storeFrontPriceFactor",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint64",
|
||||
"name": "trackingIndexScale",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint104",
|
||||
"name": "baseBorrowMin",
|
||||
@@ -387,11 +382,6 @@
|
||||
"name": "storeFrontPriceFactor",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint64",
|
||||
"name": "trackingIndexScale",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint104",
|
||||
"name": "baseBorrowMin",
|
||||
@@ -704,11 +694,6 @@
|
||||
"name": "storeFrontPriceFactor",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint64",
|
||||
"name": "trackingIndexScale",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint104",
|
||||
"name": "baseBorrowMin",
|
||||
@@ -852,11 +837,6 @@
|
||||
"name": "storeFrontPriceFactor",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint64",
|
||||
"name": "trackingIndexScale",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint104",
|
||||
"name": "baseBorrowMin",
|
||||
@@ -1017,11 +997,6 @@
|
||||
"name": "storeFrontPriceFactor",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint64",
|
||||
"name": "trackingIndexScale",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint104",
|
||||
"name": "baseBorrowMin",
|
||||
|
||||
@@ -64,11 +64,6 @@
|
||||
"name": "storeFrontPriceFactor",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint64",
|
||||
"name": "trackingIndexScale",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint104",
|
||||
"name": "baseBorrowMin",
|
||||
|
||||
@@ -315,19 +315,6 @@
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "borrow",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
|
||||
@@ -606,19 +606,6 @@
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "borrow",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
@@ -949,11 +936,6 @@
|
||||
"name": "storeFrontPriceFactor",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint64",
|
||||
"name": "trackingIndexScale",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "uint104",
|
||||
"name": "baseBorrowMin",
|
||||
@@ -1301,19 +1283,6 @@
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "trackingIndexScale",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint64",
|
||||
"name": "",
|
||||
"type": "uint64"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
|
||||
@@ -315,19 +315,6 @@
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "trackingIndexScale",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint64",
|
||||
"name": "",
|
||||
"type": "uint64"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
|
||||
@@ -86,17 +86,6 @@
|
||||
"name": "ReentrancyGuardReentrantCall",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "token",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "SafeERC20FailedOperation",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "UUPSUnauthorizedCallContext",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -28,7 +28,6 @@
|
||||
"symbol": "YT-C",
|
||||
"address": "0xE9A5b9f3a2Eda4358f81d4E2eF4f3280A664e5B0"
|
||||
}
|
||||
],
|
||||
"configTimestamp": "2025-12-26T04:17:01.662Z"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import * as dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
import "hardhat-contract-sizer";
|
||||
import "hardhat-gas-reporter";
|
||||
import "@openZeppelin/hardhat-upgrades";
|
||||
import "@openzeppelin/hardhat-upgrades";
|
||||
import "@typechain/hardhat";
|
||||
|
||||
const accounts =
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
out/build-info/be3a4ea4c5f927a0.json
Normal file
1
out/build-info/be3a4ea4c5f927a0.json
Normal file
@@ -0,0 +1 @@
|
||||
{"id":"be3a4ea4c5f927a0","source_id_to_path":{"0":"contracts/interfaces/ILending.sol","1":"contracts/interfaces/IYTAssetVault.sol","2":"contracts/interfaces/IYTLendingPriceFeed.sol","3":"contracts/ytLending/Configurator.sol","4":"contracts/ytLending/ConfiguratorStorage.sol","5":"contracts/ytLending/Lending.sol","6":"contracts/ytLending/LendingConfiguration.sol","7":"contracts/ytLending/LendingFactory.sol","8":"contracts/ytLending/LendingMath.sol","9":"contracts/ytLending/LendingPriceFeed.sol","10":"contracts/ytLending/LendingStorage.sol","11":"contracts/ytVault/YTAssetFactory.sol","12":"contracts/ytVault/YTAssetVault.sol","13":"lib/forge-std/src/Base.sol","14":"lib/forge-std/src/StdAssertions.sol","15":"lib/forge-std/src/StdChains.sol","16":"lib/forge-std/src/StdCheats.sol","17":"lib/forge-std/src/StdConstants.sol","18":"lib/forge-std/src/StdError.sol","19":"lib/forge-std/src/StdInvariant.sol","20":"lib/forge-std/src/StdJson.sol","21":"lib/forge-std/src/StdMath.sol","22":"lib/forge-std/src/StdStorage.sol","23":"lib/forge-std/src/StdStyle.sol","24":"lib/forge-std/src/StdToml.sol","25":"lib/forge-std/src/StdUtils.sol","26":"lib/forge-std/src/Test.sol","27":"lib/forge-std/src/Vm.sol","28":"lib/forge-std/src/console.sol","29":"lib/forge-std/src/console2.sol","30":"lib/forge-std/src/interfaces/IMulticall3.sol","31":"lib/forge-std/src/safeconsole.sol","32":"node_modules/@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol","33":"node_modules/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol","34":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol","35":"node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol","36":"node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol","37":"node_modules/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol","38":"node_modules/@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol","39":"node_modules/@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol","40":"node_modules/@openzeppelin/contracts/access/Ownable.sol","41":"node_modules/@openzeppelin/contracts/interfaces/IERC1363.sol","42":"node_modules/@openzeppelin/contracts/interfaces/IERC165.sol","43":"node_modules/@openzeppelin/contracts/interfaces/IERC1967.sol","44":"node_modules/@openzeppelin/contracts/interfaces/IERC20.sol","45":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC1822.sol","46":"node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol","47":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol","48":"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol","49":"node_modules/@openzeppelin/contracts/proxy/Proxy.sol","50":"node_modules/@openzeppelin/contracts/proxy/beacon/IBeacon.sol","51":"node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol","52":"node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol","53":"node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol","54":"node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol","55":"node_modules/@openzeppelin/contracts/utils/Address.sol","56":"node_modules/@openzeppelin/contracts/utils/Context.sol","57":"node_modules/@openzeppelin/contracts/utils/Errors.sol","58":"node_modules/@openzeppelin/contracts/utils/StorageSlot.sol","59":"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol","60":"test/YtLending.t.sol"},"language":"Solidity"}
|
||||
4314
package-lock.json
generated
4314
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
||||
"@nomicfoundation/hardhat-network-helpers": "^1.1.0",
|
||||
"@nomicfoundation/hardhat-toolbox": "^6.1.0",
|
||||
"@nomicfoundation/hardhat-verify": "^2.1.1",
|
||||
"@swc/core": "^1.10.1",
|
||||
"@swc/core": "^1.15.8",
|
||||
"@swc/helpers": "^0.5.15",
|
||||
"@typechain/ethers-v6": "^0.5.1",
|
||||
"@typechain/hardhat": "^9.1.0",
|
||||
|
||||
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