WIP: Chain138 deployment scripts, flash receivers, HYBX OMNL recovery
This commit is contained in:
@@ -117,9 +117,8 @@
|
||||
"NONFUNGIBLE_POSITION_MANAGER_CHAIN138_DODO": "0x31b68BE5af4Df565Ce261dfe53D529005D947B48",
|
||||
"UNISWAP_V3_ROUTER_CHAIN138_DODO": "0xde9cD8ee2811E6E64a41D5F68Be315d33995975E",
|
||||
"DODO_TEAM_MULTISIG": "0x4A666F96fC8764181194447A7dFdb7d471b301C8",
|
||||
"DODO_OPTIONAL_NOT_DEPLOYED": "GSPFactory,FeeRouteProxy1/2,LimitOrder,D3,DODOStarterProxy,DODONFTPoolProxy — see docs/04-configuration/dodo/DODO_CHAIN138_OPTIONAL_DEFERRED.md",
|
||||
"DODO_VENDING_MACHINE_ADDRESS": "0xB16c3D48A111714B1795E58341FeFDd643Ab01ab",
|
||||
"DODO_VENDING_MACHINE_NOTE": "Legacy DBIS route-executor stub (~1kB), not DODOV2Proxy02 — run deploy-dodo-full-stack-chain138.sh for native proxy",
|
||||
"DODO_VENDING_MACHINE_NOTE": "Legacy DBIS route-executor stub (~1kB), not DODOV2Proxy02 \u2014 run deploy-dodo-full-stack-chain138.sh for native proxy",
|
||||
"DODO_PMM_INTEGRATION": "0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895",
|
||||
"DODO_PMM_PROVIDER": "0x3f729632E9553EBacCdE2e9b4c8F2B285b014F2e",
|
||||
"CAUSDT_ADDRESS_138": "0x5fdDF65733e3d590463F68f93Cf16E8c04081271",
|
||||
@@ -139,14 +138,66 @@
|
||||
"DODO_PMM_INTEGRATION_ADDRESS": "0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895",
|
||||
"DODOEX_ROUTER": "0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895",
|
||||
"DODO_PMM_PROVIDER_ADDRESS": "0x3f729632E9553EBacCdE2e9b4c8F2B285b014F2e",
|
||||
"POOL_CUSDT_XAU_PUBLIC": "0x1AA55E2001E5651349aFf5a63FD7a7ae44f0f1b0",
|
||||
"POOL_CUSDC_XAU_PUBLIC": "0xEa9AC6357CaCB42a83b9082B870610363b177CbA",
|
||||
"POOL_CEURT_XAU_PUBLIC": "0xba99bc1eAac164569d5aca96c806934dDaf970CF",
|
||||
"POOL_CUSDT_XAU_PUBLIC": "0x800baB6037390B44708EEbd408447686F5fEf904",
|
||||
"POOL_CUSDC_XAU_PUBLIC": "0xDC4968F0B665ccDffBba6eB23902e95b5b3B097B",
|
||||
"POOL_CEURT_XAU_PUBLIC": "0x495162bf25Ff85C5537cBf7950c7A79BA9f4e066",
|
||||
"CHAIN138_POOL_WETH_USDT": "0xe227f6c0520c0c6e8786fe56fa76c4914f861533",
|
||||
"CHAIN138_POOL_WETH_USDC": "0xb53a0508940b1ff90f1aad4f6cb50a7012fe5593",
|
||||
"POOL_WETH_CUSDC": "0xaae68830a55767722618e869882c6ed064cc1eb2",
|
||||
"RWA_TOKEN_REGISTRY": "0xdc4Fff7c1C037242623663d2970DB7ECc80714Dd",
|
||||
"RWA_TOKEN_FACTORY": "0xb2Da9c8f3F9f794bD243e30Aa9Df94a8414EC80B"
|
||||
"RWA_TOKEN_REGISTRY": "0x9c83430A1b3A9ac8e807acCF6cCbC6CaBd0afFa7",
|
||||
"RWA_TOKEN_FACTORY": "0xb2da9c8f3f9f794bd243e30aa9df94a8414ec80b",
|
||||
"LIXAU_TOKEN_138": "0x1Ef5579Bed4a99301943aF5B9aC9a0C1b00ddB91",
|
||||
"LIXAU_TOKEN_138_LEGACY": "0x2fab9847da83cb88018611d32271eb8e73d01ad2",
|
||||
"M00_DIAMOND_HUB": "0x557efc7f5b93edc8A5A36Cf3E8363cB5bC6D7C43",
|
||||
"M00_MAINNET_BRIDGE_FACET": "0x8d622510EDAFeA0196F7eC2B2f1A081E1C4FA5aA",
|
||||
"M00_ACCESS_FACET": "0x1c69828B70E8291959e38D38cBFF50F1357576e4",
|
||||
"M00_RWA_INSTRUMENT_FACET": "0xf2bf51091410Dc0010e4926dc92091D0B4FE8c5E",
|
||||
"M00_RWA_DOCUMENT_FACET": "0xcDE36eC826e6d41754AAC578F197c9051EdfCa12",
|
||||
"M00_RWA_STANDARDS_FACET": "0xbAb0C3F6f96c0B6D0dDfE5b392EC47cF1126DCCa",
|
||||
"UNIVERSAL_ASSET_REGISTRY_IMPL_RWA": "0x93630589eec0FA7795DefB0a5DDc1C4eA1e5aedd",
|
||||
"IndexFacet": "0xa975f5c394a30d8c3c63ef395ee8b6fa7b69bcc7",
|
||||
"MonetaryFacet": "0xe95e4d51dfcca3fe72ee5a21e1f46e46e34d52bd",
|
||||
"GovernanceFacet": "0x7fbd1c9fa949d6499c9d8861f7412681a9916e2c",
|
||||
"LIPMG_TOKEN_138": "0xD920da2D8A9c1Cd31f4853969F1492C0B6527d9b",
|
||||
"LIPMG_TOKEN_138_LEGACY": "0xf9e82712be806216a6eaa871e33942b39bed00c9",
|
||||
"LIBMG1_TOKEN_138": "0x60e9001881fe5966567e842b91C6dDB63C12616D",
|
||||
"LIBMG1_TOKEN_138_LEGACY": "0x25489b432cb53135baa08cc0d649def6748f7641",
|
||||
"LIBMG2_TOKEN_138": "0xAF2c8050C93F6BD4c39Ac41013aD9EAe35683140",
|
||||
"LIBMG2_TOKEN_138_LEGACY": "0x3933315b1dd095e761fcf76ecfd8fd9ba44648de",
|
||||
"LIBMG3_TOKEN_138": "0x47d46acC0B849d9C0EFb9CFF96cfD84a905951b1",
|
||||
"LIBMG3_TOKEN_138_LEGACY": "0x75d0e18fbb4d5c8fab19aa216595f4a6e085d493",
|
||||
"LIXAU_ADDRESS_138": "0x1Ef5579Bed4a99301943aF5B9aC9a0C1b00ddB91",
|
||||
"LIPMG_ADDRESS_138": "0xD920da2D8A9c1Cd31f4853969F1492C0B6527d9b",
|
||||
"LIBMG1_ADDRESS_138": "0x60e9001881fe5966567e842b91C6dDB63C12616D",
|
||||
"LIBMG2_ADDRESS_138": "0xAF2c8050C93F6BD4c39Ac41013aD9EAe35683140",
|
||||
"LIBMG3_ADDRESS_138": "0x47d46acC0B849d9C0EFb9CFF96cfD84a905951b1",
|
||||
"DODO_D3_ORACLE": "0x994A737B0D39D686e3d94A611455bB34724c6eab",
|
||||
"DODO_D3_RATE_MANAGER": "0x01340C18fD67878c74286FFD54e288551201AA7f",
|
||||
"DODO_D3_LIQUIDATION_ROUTER": "0xc0F6ccCEBA3dDF3c1c8dBC6AF5B9E17dFFDf99c3",
|
||||
"DODO_D3_VAULT": "0x085F04C0A283Ef56010EF8E22a4D9510483834FC",
|
||||
"DODO_D3_POOL_QUOTA": "0xa0FcE9f0582481B0f82614f05bd6a9868Ea7cB4E",
|
||||
"DODO_D3_MOCK_ROUTER": "0x5B73492927387b82844E845a283bd774bb05b9eC",
|
||||
"DODO_STARTER_FACTORY": "0xa114524eceCBd2d184B17A6b85b9127E91cde07C",
|
||||
"DODO_STARTER_PROXY": "0x55D9678725BB11173789dcf78b8F7C792Dd37Ad5",
|
||||
"DODO_NFT_POOL_PROXY": "0xbe884727cA88b324c0B0B3f40b39eD14a871FF0D",
|
||||
"DODO_NFT_APPROVE": "0x7703C49073cE2dcd38A39F0cD17A9e1EE21CD89C",
|
||||
"DODO_D3_MM": "0xdb68a9728bfbaf874c47077c849847fd7fcee258",
|
||||
"DODO_D3_USER_QUOTA": "0x5aed9F96c728cfDF0762E7C6c42aA0879D113Fdf",
|
||||
"DODO_D3_MM_FACTORY": "0xca01e43290D57Af7B371209f73D0c0c9456bA891",
|
||||
"DODO_D3_PROXY": "0x20d030e6F0270859cbA04886333f6B83D9Ad6f1a",
|
||||
"DODO_D3_FEE_RATE_MODEL": "0x8b1FeC1cf6f492E109d8a27Fd2A41a6F6C604cCa",
|
||||
"DODO_OPTIONAL_NOT_DEFERRED": "none \u2014 see dodoDeferredChecklist upstream items",
|
||||
"DODO_GSP_CUSDT_CUSDC": "0xc8a9b51983364d2753B09ad6eA07a8232f5d45c7",
|
||||
"DODO_D3_ORACLE_FEEDS": {
|
||||
"cUSDT": "0x9386FCF39962A3c6e2fF69e03b792b8cEb5Cae88",
|
||||
"cUSDC": "0xaB36862e8d07Aa92844049bf6E64A311b7Cc2d07",
|
||||
"WETH10": "0x8b622084d0109b3aEA130ec4440346Da0338645A",
|
||||
"DODO": "0xFeb97b7Ea96C849bdF9729bB3D9b7D85c1f95e51"
|
||||
},
|
||||
"DODO_FEE_ROUTE_PROXY1": "0xD6840208B7B3A1edc2C619d3db454c15CF10dB91",
|
||||
"DODO_FEE_ROUTE_PROXY2": "0x1B138C77d92eC81fA8e7E60f4f67febeFD845E4B",
|
||||
"DODO_LIMIT_ORDER": "0x7d0205F888170B1769e91b6A187c8F9a33b42cA5",
|
||||
"DODO_LIMIT_ORDER_BOT": "0xf5babe17f7A7b2209f4816C517084fd54FC1f5b5"
|
||||
},
|
||||
"mainnetAttestation": {
|
||||
"CHAIN138_MAINNET_CHECKPOINT_PROXY": "0xe2D6B908FE2535C39C79257FAAa2A52457673ba9",
|
||||
|
||||
@@ -134,10 +134,30 @@
|
||||
"glCode": "52100",
|
||||
"name": "Unrealized FX loss (P&L)",
|
||||
"fineractType": "EXPENSE",
|
||||
"usage": "Unrealized FX loss / ECL expense bucket",
|
||||
"usage": "Unrealized FX loss on revaluation",
|
||||
"ipsasStandards": ["IPSAS 9"],
|
||||
"ifrsRefs": ["IAS 21", "IFRS 9"],
|
||||
"roles": ["fx_loss_unrealized", "ecl_expense"]
|
||||
"roles": ["fx_loss_unrealized"]
|
||||
},
|
||||
{
|
||||
"glCode": "11040",
|
||||
"name": "M00 Gold-Backed Asset Inventory (3FR M00)",
|
||||
"fineractType": "ASSET",
|
||||
"usage": "Face-value M00 from 3FR discounted exchange (T-3FR-001/002)",
|
||||
"ipsasStandards": ["IPSAS 28", "IPSAS 29"],
|
||||
"ifrsRefs": ["IFRS 9", "IFRS 13"],
|
||||
"usGaapRefs": ["ASC 820", "ASC 860"],
|
||||
"roles": ["m00_inventory", "gold_backed"]
|
||||
},
|
||||
{
|
||||
"glCode": "32200",
|
||||
"name": "M00 Discount / FVR — 3FR Exchange",
|
||||
"fineractType": "EQUITY",
|
||||
"usage": "Discount, haircut, monetization reserve / fair value reconciliation",
|
||||
"ipsasStandards": ["IPSAS 1", "IPSAS 29"],
|
||||
"ifrsRefs": ["IFRS 13", "IAS 1"],
|
||||
"usGaapRefs": ["ASC 820"],
|
||||
"roles": ["fvr_adjustment", "revaluation_deficit"]
|
||||
}
|
||||
],
|
||||
"allowedJournalPairs": [
|
||||
@@ -150,6 +170,28 @@
|
||||
{ "debitGlCode": "52100", "creditGlCode": "23010", "ipsasRef": "IPSAS 19", "memo": "IAS37-PROVISION", "ifrsRef": "IAS 37" },
|
||||
{ "debitGlCode": "13010", "creditGlCode": "2000", "ipsasRef": "IPSAS 28", "memo": "SETTLE-NOSTRO", "ifrsRef": "IFRS 7 settlement" }
|
||||
],
|
||||
"compoundJournalEntries": [
|
||||
{
|
||||
"memo": "T-3FR-001",
|
||||
"debitGlCodes": ["11040", "32200"],
|
||||
"creditGlCodes": ["2000"],
|
||||
"totalAmountUsd": 900000000000,
|
||||
"ipsasRef": "IPSAS 3, 28, 29",
|
||||
"ifrsRef": "IFRS 9, IFRS 13, IAS 32",
|
||||
"usGaapRef": "ASC 820, ASC 860, ASC 606 excluded",
|
||||
"narrative": "3FR M00 substance-only compound — replaces T-001"
|
||||
},
|
||||
{
|
||||
"memo": "T-3FR-002",
|
||||
"debitGlCodes": ["11040", "32200"],
|
||||
"creditGlCodes": ["1000"],
|
||||
"totalAmountUsd": 900000000000,
|
||||
"ipsasRef": "IPSAS 28, 29",
|
||||
"ifrsRef": "IFRS 9, IFRS 13",
|
||||
"usGaapRef": "ASC 820, ASC 860",
|
||||
"narrative": "Substance reclass after T-001 settlement receipt — full compliant path"
|
||||
}
|
||||
],
|
||||
"monetaryLayerHints": {
|
||||
"m0_reserve": { "primaryGlCodes": ["1050"], "ipsasNarrative": "Treasury / M0 reserve assets (IPSAS 28, 29)" },
|
||||
"m1_liability": { "primaryGlCodes": ["2000", "2100"], "ipsasNarrative": "Financial liabilities — deposits (IPSAS 28, 29)" },
|
||||
|
||||
32
deployments/chain138/wormhole-pending.json
Normal file
32
deployments/chain138/wormhole-pending.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"schemaVersion": "1.0.0",
|
||||
"status": "pending_wormhole_foundation_ack",
|
||||
"evmChainId": 138,
|
||||
"wormholeChainId": null,
|
||||
"updated": "2026-05-27",
|
||||
"note": "Populate after Guardian ACK + deploy. Do not treat as canonical until verified on explorer.",
|
||||
"contracts": {
|
||||
"WormholeCore": {
|
||||
"address": null,
|
||||
"deployTx": null
|
||||
},
|
||||
"WormholeSetup": {
|
||||
"address": null
|
||||
},
|
||||
"WormholeImplementation": {
|
||||
"address": null
|
||||
},
|
||||
"TokenBridge": {
|
||||
"address": null,
|
||||
"deployTx": null
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"repo": "https://github.com/wormhole-foundation/wormhole",
|
||||
"ref": "v2.56.0",
|
||||
"deployScripts": [
|
||||
"ethereum/sh/deployCoreBridge.sh",
|
||||
"ethereum/sh/deployTokenBridge.sh"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,9 @@ test = "test"
|
||||
out = "out"
|
||||
libs = ["../lib"]
|
||||
solc = "0.8.20"
|
||||
skip = [
|
||||
"test/AaveQuotePushFlashReceiverMainnetFork.t.sol",
|
||||
]
|
||||
optimizer = true
|
||||
optimizer_runs = 1
|
||||
via_ir = true
|
||||
|
||||
92
forkproof/test/SeedMainnetCwStablePoolPermanentFork.t.sol
Normal file
92
forkproof/test/SeedMainnetCwStablePoolPermanentFork.t.sol
Normal file
@@ -0,0 +1,92 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Test, console} from "forge-std/Test.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
|
||||
interface IDODOPMMIntegrationSeedFork {
|
||||
function isRegisteredPool(address pool) external view returns (bool);
|
||||
function addLiquidity(address pool, uint256 baseAmount, uint256 quoteAmount)
|
||||
external
|
||||
returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare);
|
||||
}
|
||||
|
||||
interface IDODOPMMPoolSeedFork {
|
||||
function _BASE_TOKEN_() external view returns (address);
|
||||
function _QUOTE_TOKEN_() external view returns (address);
|
||||
function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve);
|
||||
function getMidPrice() external view returns (uint256);
|
||||
}
|
||||
|
||||
/// @notice Mainnet fork validation for permanent cWUSDC/USDC and cWUSDT/USDT DODO seed paths.
|
||||
contract SeedMainnetCwStablePoolPermanentForkTest is Test {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
address internal constant INTEGRATION = 0xa9F284eD010f4F7d7F8F201742b49b9f58e29b84;
|
||||
address internal constant POOL_USDC = 0x69776fc607e9edA8042e320e7e43f54d06c68f0E;
|
||||
address internal constant POOL_USDT = 0x79156F6B7bf71a1B72D78189B540A89A6C13F6FC;
|
||||
address internal constant CWUSDC = 0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a;
|
||||
address internal constant CWUSDT = 0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE;
|
||||
address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
|
||||
address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
|
||||
|
||||
uint256 internal constant SEED_RAW = 50_000_000_000;
|
||||
|
||||
function setUp() public {
|
||||
string memory rpc = vm.envOr("ETHEREUM_MAINNET_RPC", string("https://eth.llamarpc.com"));
|
||||
vm.createSelectFork(rpc);
|
||||
}
|
||||
|
||||
function test_fork_seed_cWUSDC_USDC_addLiquidity_50k() public {
|
||||
_seedRail(POOL_USDC, CWUSDC, USDC, "USDC");
|
||||
}
|
||||
|
||||
function test_fork_seed_cWUSDT_USDT_addLiquidity_50k() public {
|
||||
_seedRail(POOL_USDT, CWUSDT, USDT, "USDT");
|
||||
}
|
||||
|
||||
function _ensurePoolRegistered(address pool) internal view {
|
||||
assertTrue(IDODOPMMIntegrationSeedFork(INTEGRATION).isRegisteredPool(pool), "pool not registered on integration");
|
||||
}
|
||||
|
||||
function _seedRail(address pool, address baseToken, address quoteToken, string memory label) internal {
|
||||
address user = makeAddr("seedUser");
|
||||
deal(baseToken, user, SEED_RAW);
|
||||
deal(quoteToken, user, SEED_RAW);
|
||||
|
||||
IDODOPMMPoolSeedFork poolView = IDODOPMMPoolSeedFork(pool);
|
||||
assertEq(poolView._BASE_TOKEN_(), baseToken, "base token");
|
||||
assertEq(poolView._QUOTE_TOKEN_(), quoteToken, "quote token");
|
||||
|
||||
_ensurePoolRegistered(pool);
|
||||
|
||||
(uint256 baseBefore, uint256 quoteBefore) = poolView.getVaultReserve();
|
||||
uint256 midBefore = poolView.getMidPrice();
|
||||
console.log(string.concat(label, " baseReserveBefore"), baseBefore);
|
||||
console.log(string.concat(label, " quoteReserveBefore"), quoteBefore);
|
||||
console.log(string.concat(label, " midBefore"), midBefore);
|
||||
|
||||
vm.startPrank(user);
|
||||
IERC20(baseToken).forceApprove(INTEGRATION, SEED_RAW);
|
||||
IERC20(quoteToken).forceApprove(INTEGRATION, SEED_RAW);
|
||||
(uint256 baseShare, uint256 quoteShare, uint256 lpShare) =
|
||||
IDODOPMMIntegrationSeedFork(INTEGRATION).addLiquidity(pool, SEED_RAW, SEED_RAW);
|
||||
vm.stopPrank();
|
||||
|
||||
(uint256 baseAfter, uint256 quoteAfter) = poolView.getVaultReserve();
|
||||
uint256 midAfter = poolView.getMidPrice();
|
||||
console.log(string.concat(label, " baseShare"), baseShare);
|
||||
console.log(string.concat(label, " quoteShare"), quoteShare);
|
||||
console.log(string.concat(label, " lpShare"), lpShare);
|
||||
console.log(string.concat(label, " baseReserveAfter"), baseAfter);
|
||||
console.log(string.concat(label, " quoteReserveAfter"), quoteAfter);
|
||||
console.log(string.concat(label, " midAfter"), midAfter);
|
||||
|
||||
assertGt(baseShare, 0, "base share");
|
||||
assertGt(quoteShare, 0, "quote share");
|
||||
assertGt(lpShare, 0, "lp share");
|
||||
assertGt(baseAfter, baseBefore, "base reserve increased");
|
||||
assertGt(quoteAfter, quoteBefore, "quote reserve increased");
|
||||
}
|
||||
}
|
||||
32
foundry.toml
32
foundry.toml
@@ -23,9 +23,11 @@ auto_detect_remappings = false
|
||||
# Fork tests execute live mainnet bytecode; Cancun matches post-Dencun execution (MCOPY, etc.).
|
||||
evm_version = "cancun"
|
||||
fs_permissions = [
|
||||
{ access = "read", path = "./config" }
|
||||
{ access = "read", path = "./config" },
|
||||
{ access = "read", path = "../reports" }
|
||||
]
|
||||
remappings = [
|
||||
"@chainlink/contracts-ccip/=node_modules/@chainlink/contracts-ccip/",
|
||||
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
|
||||
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
|
||||
"forge-std/=lib/forge-std/src/",
|
||||
@@ -83,6 +85,7 @@ fs_permissions = [
|
||||
{ access = "read", path = "../config" }
|
||||
]
|
||||
remappings = [
|
||||
"@chainlink/contracts-ccip/=node_modules/@chainlink/contracts-ccip/",
|
||||
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
|
||||
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
|
||||
"forge-std/=lib/forge-std/src/",
|
||||
@@ -106,6 +109,33 @@ via_ir = true
|
||||
# Backwards-compatible alias for older scripts; prefer profile.chain138.
|
||||
evm_version = "paris"
|
||||
|
||||
# BSC CompliantWrappedToken — matches verified cWUSDT on BscScan (chain 56).
|
||||
# Use with scoped build: FOUNDRY_SRC=contracts/tokens FOUNDRY_OUT=out/scopes/tokens
|
||||
# bash scripts/forge/scope.sh build tokens
|
||||
# EVM london (not cancun); solc 0.8.20; optimizer 200; via_ir required (stack depth).
|
||||
[profile.bsc_tokens_verify]
|
||||
src = "contracts/tokens,contracts/interfaces"
|
||||
out = "out/scopes/tokens"
|
||||
cache_path = "cache/scopes/tokens"
|
||||
solc = "0.8.20"
|
||||
optimizer = true
|
||||
optimizer_runs = 200
|
||||
via_ir = true
|
||||
evm_version = "london"
|
||||
auto_detect_remappings = false
|
||||
remappings = [
|
||||
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
|
||||
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
|
||||
"forge-std/=lib/forge-std/src/",
|
||||
"ds-test/=lib/forge-std/lib/ds-test/src/",
|
||||
"@emoney/=contracts/emoney/",
|
||||
"@emoney-scripts/=script/emoney/",
|
||||
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
|
||||
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
|
||||
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
|
||||
"@gru/=lib/gru-contracts/"
|
||||
]
|
||||
|
||||
# Mainnet checkpoint hub — minimize runtime bytecode (EIP-170 24 KiB).
|
||||
[profile.mainnet-checkpoint]
|
||||
optimizer = true
|
||||
|
||||
74
script/DeployCWReserveSettlementStack.s.sol
Normal file
74
script/DeployCWReserveSettlementStack.s.sol
Normal file
@@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {CWNavOracle} from "../contracts/cw-settlement/CWNavOracle.sol";
|
||||
import {CWRedemptionQueue} from "../contracts/cw-settlement/CWRedemptionQueue.sol";
|
||||
import {CWStabilityFund} from "../contracts/cw-settlement/CWStabilityFund.sol";
|
||||
import {CWBuybackExecutor} from "../contracts/cw-settlement/CWBuybackExecutor.sol";
|
||||
import {CWProtocolTreasury} from "../contracts/cw-settlement/CWProtocolTreasury.sol";
|
||||
import {CWReserveVerifier} from "../contracts/bridge/integration/CWReserveVerifier.sol";
|
||||
|
||||
interface ICWMultiTokenBridgeL1Admin {
|
||||
function setReserveVerifier(address newVerifier) external;
|
||||
}
|
||||
|
||||
/**
|
||||
* @title DeployCWReserveSettlementStack
|
||||
* @notice Deploy NAV oracle, redemption queue, stability fund, buyback, treasury, and reserve verifier on Chain 138.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY, RPC_URL_138
|
||||
* CW_L1_BRIDGE (default 0x152ed3e9912161b76bdfd368d0c84b7c31c10de7)
|
||||
* CW_RESERVE_SYSTEM (default 0x607e97cD626f209facfE48c1464815DDE15B5093)
|
||||
* CW_CANONICAL_USDT / CW_CANONICAL_USDC
|
||||
* CW_ATTACH_VERIFIER_TO_L1=1
|
||||
*/
|
||||
contract DeployCWReserveSettlementStack is Script {
|
||||
function run() external {
|
||||
uint256 privateKey = vm.envUint("PRIVATE_KEY");
|
||||
address admin = vm.addr(privateKey);
|
||||
|
||||
address l1Bridge = vm.envOr("CW_L1_BRIDGE", address(0x152eD3e9912161b76BDFd368D0C84B7C31C10dE7));
|
||||
address reserveSystem = vm.envOr("CW_RESERVE_SYSTEM", address(0x607e97cD626f209facfE48c1464815DDE15B5093));
|
||||
address canonicalUSDT = vm.envOr("CW_CANONICAL_USDT", address(0x93E66202A11B1772E55407B32B44e5Cd8eda7f22));
|
||||
address canonicalUSDC = vm.envOr("CW_CANONICAL_USDC", address(0xf22258f57794CC8E06237084b353Ab30fFfa640b));
|
||||
bool attachVerifier = vm.envOr("CW_ATTACH_VERIFIER_TO_L1", uint256(0)) == 1;
|
||||
bool skipVerifierDeploy = vm.envOr("CW_SKIP_RESERVE_VERIFIER", uint256(1)) == 1;
|
||||
|
||||
vm.startBroadcast(privateKey);
|
||||
|
||||
address verifierAddr;
|
||||
if (skipVerifierDeploy) {
|
||||
verifierAddr = address(0);
|
||||
} else {
|
||||
CWReserveVerifier verifier = new CWReserveVerifier(admin, l1Bridge, address(0), reserveSystem);
|
||||
verifierAddr = address(verifier);
|
||||
if (attachVerifier) {
|
||||
ICWMultiTokenBridgeL1Admin(l1Bridge).setReserveVerifier(verifierAddr);
|
||||
}
|
||||
verifier.configureToken(canonicalUSDT, address(0), false, false, false);
|
||||
verifier.configureToken(canonicalUSDC, address(0), false, false, false);
|
||||
}
|
||||
|
||||
CWNavOracle navOracle = new CWNavOracle(admin, l1Bridge, reserveSystem);
|
||||
CWRedemptionQueue redemptionQueue = new CWRedemptionQueue(admin);
|
||||
CWStabilityFund stabilityFund = new CWStabilityFund(admin);
|
||||
CWProtocolTreasury treasury = new CWProtocolTreasury(admin, address(0));
|
||||
CWBuybackExecutor buyback = new CWBuybackExecutor(admin, verifierAddr, address(treasury));
|
||||
|
||||
treasury.setBuybackExecutor(address(buyback));
|
||||
|
||||
navOracle.configureToken(canonicalUSDT, address(0));
|
||||
navOracle.configureToken(canonicalUSDC, address(0));
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("CWReserveVerifier:", verifierAddr);
|
||||
console.log("CWNavOracle:", address(navOracle));
|
||||
console.log("CWRedemptionQueue:", address(redemptionQueue));
|
||||
console.log("CWStabilityFund:", address(stabilityFund));
|
||||
console.log("CWProtocolTreasury:", address(treasury));
|
||||
console.log("CWBuybackExecutor:", address(buyback));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import "../../../contracts/bridge/trustless/EnhancedSwapRouterV2.sol";
|
||||
import "../../../contracts/bridge/trustless/RouteTypesV2.sol";
|
||||
|
||||
/// @notice Enable UniV3, Balancer, and Curve routes on live EnhancedSwapRouterV2 (Chain 138).
|
||||
/// Adapters must already be set via initial deploy; this script only enables providers + routes.
|
||||
contract ConfigureEnhancedSwapRouterV2MultiVenue is Script {
|
||||
address constant DEFAULT_ROUTER_V2 = 0xa421706768aEB7fafA2D912C5E10824eF3437ad4;
|
||||
|
||||
address constant CHAIN138_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||
address constant CHAIN138_USDT = 0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1;
|
||||
address constant CHAIN138_USDC = 0x71D6687F38b93CCad569Fa6352c876eea967201b;
|
||||
address constant CHAIN138_cUSDT = 0x93E66202A11B1772E55407B32B44e5Cd8eda7f22;
|
||||
address constant CHAIN138_cUSDC = 0xf22258f57794CC8E06237084b353Ab30fFfa640b;
|
||||
|
||||
address constant CHAIN138_DODO_PROVIDER = 0x3f729632E9553EBacCdE2e9b4c8F2B285b014F2e;
|
||||
address constant CHAIN138_POOL_CUSDTCUSDC = 0x9e89bAe009adf128782E19e8341996c596ac40dC;
|
||||
address constant CHAIN138_POOL_CUSDTUSDT = 0x866Cb44b59303d8dc5f4F9E3E7A8e8b0bf238d66;
|
||||
address constant CHAIN138_POOL_CUSDCUSDC = 0xc39B7D0F40838cbFb54649d327f49a6DAC964062;
|
||||
address constant CHAIN138_POOL_WETH_USDT = 0xe227F6C0520c0c6E8786fE56Fa76c4914F861533;
|
||||
address constant CHAIN138_POOL_WETH_USDC = 0xb53A0508940b1Ff90F1AAD4f6cb50a7012Fe5593;
|
||||
|
||||
address constant UNISWAP_V3_ROUTER = 0xde9cD8ee2811E6E64a41D5F68Be315d33995975E;
|
||||
address constant UNISWAP_QUOTER = 0x6abbB1CEb2468e748a03A00CD6aA9BFE893AFa1f;
|
||||
address constant BALANCER_VAULT = 0x96423d7C1727698D8a25EbFB88131e9422d1a3C3;
|
||||
bytes32 constant BALANCER_WETH_USDT_POOL_ID =
|
||||
0x877cd220759e8c94b82f55450c85d382ae06856c426b56d93092a420facbc324;
|
||||
bytes32 constant BALANCER_WETH_USDC_POOL_ID =
|
||||
0xd8dfb18a6baf9b29d8c2dbd74639db87ac558af120df5261dab8e2a5de69013b;
|
||||
address constant CURVE_3POOL = 0xE440Ec15805BE4C7BabCD17A63B8C8A08a492e0f;
|
||||
|
||||
function run() external {
|
||||
require(block.chainid == 138, "ConfigureEnhancedSwapRouterV2MultiVenue: Chain 138 only");
|
||||
|
||||
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
||||
address routerAddress = vm.envOr("ENHANCED_SWAP_ROUTER_V2_ADDRESS", DEFAULT_ROUTER_V2);
|
||||
EnhancedSwapRouterV2 router = EnhancedSwapRouterV2(payable(routerAddress));
|
||||
|
||||
uint24 wethUsdtFee = uint24(vm.envOr("UNISWAP_V3_WETH_USDT_FEE", uint256(3000)));
|
||||
uint24 wethUsdcFee = uint24(vm.envOr("UNISWAP_V3_WETH_USDC_FEE", uint256(500)));
|
||||
|
||||
vm.startBroadcast(deployerPrivateKey);
|
||||
|
||||
_setDodoPair(router, CHAIN138_cUSDT, CHAIN138_cUSDC, CHAIN138_DODO_PROVIDER, CHAIN138_POOL_CUSDTCUSDC);
|
||||
_setDodoPair(router, CHAIN138_cUSDT, CHAIN138_USDT, CHAIN138_DODO_PROVIDER, CHAIN138_POOL_CUSDTUSDT);
|
||||
_setDodoPair(router, CHAIN138_cUSDC, CHAIN138_USDC, CHAIN138_DODO_PROVIDER, CHAIN138_POOL_CUSDCUSDC);
|
||||
_setDodoPair(router, CHAIN138_WETH, CHAIN138_USDT, CHAIN138_DODO_PROVIDER, CHAIN138_POOL_WETH_USDT);
|
||||
_setDodoPair(router, CHAIN138_WETH, CHAIN138_USDC, CHAIN138_DODO_PROVIDER, CHAIN138_POOL_WETH_USDC);
|
||||
|
||||
_setUniswapPair(router, CHAIN138_WETH, CHAIN138_USDT, UNISWAP_V3_ROUTER, UNISWAP_QUOTER, wethUsdtFee);
|
||||
_setUniswapPair(router, CHAIN138_WETH, CHAIN138_USDC, UNISWAP_V3_ROUTER, UNISWAP_QUOTER, wethUsdcFee);
|
||||
|
||||
_setBalancerPair(router, CHAIN138_WETH, CHAIN138_USDT, BALANCER_VAULT, BALANCER_WETH_USDT_POOL_ID);
|
||||
_setBalancerPair(router, CHAIN138_WETH, CHAIN138_USDC, BALANCER_VAULT, BALANCER_WETH_USDC_POOL_ID);
|
||||
|
||||
_setCurvePair(router, CHAIN138_USDT, CHAIN138_USDC, CURVE_3POOL, 0, 1, false);
|
||||
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Dodo, true);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.UniswapV3, true);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Balancer, true);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Curve, true);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.OneInch, false);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Partner, false);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.DodoV3, false);
|
||||
|
||||
RouteTypesV2.Provider[] memory providers = new RouteTypesV2.Provider[](4);
|
||||
providers[0] = RouteTypesV2.Provider.Dodo;
|
||||
providers[1] = RouteTypesV2.Provider.UniswapV3;
|
||||
providers[2] = RouteTypesV2.Provider.Balancer;
|
||||
providers[3] = RouteTypesV2.Provider.Curve;
|
||||
router.setRoutingConfig(0, providers);
|
||||
router.setRoutingConfig(1, providers);
|
||||
router.setRoutingConfig(2, providers);
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("EnhancedSwapRouterV2 multi-venue configured:", routerAddress);
|
||||
}
|
||||
|
||||
function _setDodoPair(EnhancedSwapRouterV2 router, address tokenA, address tokenB, address target, address pool)
|
||||
internal
|
||||
{
|
||||
bytes memory providerData = abi.encode(pool);
|
||||
router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.Dodo, target, providerData, true);
|
||||
router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.Dodo, target, providerData, true);
|
||||
}
|
||||
|
||||
function _setUniswapPair(
|
||||
EnhancedSwapRouterV2 router,
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
address target,
|
||||
address quoter,
|
||||
uint24 fee
|
||||
) internal {
|
||||
bytes memory providerData = abi.encode(bytes(""), fee, quoter, false);
|
||||
router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.UniswapV3, target, providerData, true);
|
||||
router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.UniswapV3, target, providerData, true);
|
||||
}
|
||||
|
||||
function _setBalancerPair(
|
||||
EnhancedSwapRouterV2 router,
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
address target,
|
||||
bytes32 poolId
|
||||
) internal {
|
||||
bytes memory providerData = abi.encode(poolId);
|
||||
router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.Balancer, target, providerData, true);
|
||||
router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.Balancer, target, providerData, true);
|
||||
}
|
||||
|
||||
function _setCurvePair(
|
||||
EnhancedSwapRouterV2 router,
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
address target,
|
||||
int128 i,
|
||||
int128 j,
|
||||
bool useUnderlying
|
||||
) internal {
|
||||
bytes memory providerData = abi.encode(i, j, useUnderlying);
|
||||
router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.Curve, target, providerData, true);
|
||||
router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.Curve, target, providerData, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {AaveUniV2CwStableRebalanceFlashReceiver} from "../../contracts/flash/AaveUniV2CwStableRebalanceFlashReceiver.sol";
|
||||
|
||||
/**
|
||||
* @title DeployAaveUniV2CwStableRebalanceFlashReceiver
|
||||
* @notice Deploy the UniV2 rebalance + remove Aave flash receiver.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY
|
||||
* AAVE_POOL_ADDRESS optional; default Aave V3 mainnet Pool
|
||||
* UNIV2_FLASH_REBALANCE_OWNER optional; default deployer
|
||||
*/
|
||||
contract DeployAaveUniV2CwStableRebalanceFlashReceiver is Script {
|
||||
address internal constant DEFAULT_AAVE_POOL_MAINNET = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address pool = vm.envOr("AAVE_POOL_ADDRESS", DEFAULT_AAVE_POOL_MAINNET);
|
||||
address deployer = vm.addr(pk);
|
||||
address owner = vm.envOr("UNIV2_FLASH_REBALANCE_OWNER", deployer);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
AaveUniV2CwStableRebalanceFlashReceiver receiver =
|
||||
new AaveUniV2CwStableRebalanceFlashReceiver(pool, owner);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("AaveUniV2CwStableRebalanceFlashReceiver", address(receiver));
|
||||
console.log("owner", owner);
|
||||
console.log("aavePool", pool);
|
||||
}
|
||||
}
|
||||
27
script/deploy/bridge/DeployZedxionAdapter.s.sol
Normal file
27
script/deploy/bridge/DeployZedxionAdapter.s.sol
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import "../../../contracts/bridge/adapters/evm/ZedxionAdapter.sol";
|
||||
|
||||
/**
|
||||
* @notice Deploy ZedxionAdapter on Chain 138.
|
||||
* Env: PRIVATE_KEY, ADMIN (optional), ZEDXION_TRANSPORT (optional — call setZedxionTransport after)
|
||||
*/
|
||||
contract DeployZedxionAdapter is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address admin = vm.envOr("ADMIN", vm.addr(pk));
|
||||
vm.startBroadcast(pk);
|
||||
ZedxionAdapter adapter = new ZedxionAdapter(admin);
|
||||
vm.stopBroadcast();
|
||||
console2.log("ZedxionAdapter", address(adapter));
|
||||
address transport = vm.envOr("ZEDXION_TRANSPORT", address(0));
|
||||
if (transport != address(0)) {
|
||||
vm.startBroadcast(pk);
|
||||
adapter.setZedxionTransport(transport);
|
||||
vm.stopBroadcast();
|
||||
console2.log("ZedxionTransport wired", transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
script/deploy/bridge/DeployZedxionCustomBridge.s.sol
Normal file
22
script/deploy/bridge/DeployZedxionCustomBridge.s.sol
Normal file
@@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import "../../../contracts/bridge/ZedxionCustomBridge.sol";
|
||||
|
||||
/**
|
||||
* @notice Deploy ZedxionCustomBridge on Chain 138 and/or ZEDXION (83872).
|
||||
* @dev For CREATE2 same-address deploy, use DeployDeterministicCore pattern with salt keccak256("ZedxionCustomBridge").
|
||||
* Env: PRIVATE_KEY, ADMIN (defaults msg.sender)
|
||||
*/
|
||||
contract DeployZedxionCustomBridge is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address admin = vm.envOr("ADMIN", vm.addr(pk));
|
||||
vm.startBroadcast(pk);
|
||||
ZedxionCustomBridge bridge = new ZedxionCustomBridge(admin);
|
||||
vm.stopBroadcast();
|
||||
console2.log("ZedxionCustomBridge", address(bridge));
|
||||
console2.log("Admin", admin);
|
||||
}
|
||||
}
|
||||
27
script/deploy/rwa/GrantUarRegistrarRWA138.s.sol
Normal file
27
script/deploy/rwa/GrantUarRegistrarRWA138.s.sol
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import {UniversalAssetRegistry} from "../../../contracts/registry/UniversalAssetRegistry.sol";
|
||||
|
||||
/**
|
||||
* @title GrantUarRegistrarRWA138
|
||||
* @notice Grant REGISTRAR_ROLE on UAR to broadcaster (for RegisterRWAIndicesInUAR138).
|
||||
* @dev Broadcaster must hold DEFAULT_ADMIN_ROLE on UAR.
|
||||
*/
|
||||
contract GrantUarRegistrarRWA138 is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address grantee = vm.envOr("UAR_REGISTRAR_GRANTEE", vm.addr(pk));
|
||||
address uarAddr = vm.envAddress("UNIVERSAL_ASSET_REGISTRY");
|
||||
|
||||
UniversalAssetRegistry uar = UniversalAssetRegistry(uarAddr);
|
||||
bytes32 role = uar.REGISTRAR_ROLE();
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
uar.grantRole(role, grantee);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("REGISTRAR_ROLE granted to", grantee, "on UAR", uarAddr);
|
||||
}
|
||||
}
|
||||
147
script/deploy/rwa/RedeployLiIndexPublisher138.s.sol
Normal file
147
script/deploy/rwa/RedeployLiIndexPublisher138.s.sol
Normal file
@@ -0,0 +1,147 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import {RWATokenRegistry} from "../../../contracts/rwa/RWATokenRegistry.sol";
|
||||
import {RWATokenFactory} from "../../../contracts/rwa/RWATokenFactory.sol";
|
||||
import {IRWATokenFactory} from "../../../contracts/rwa/IRWATokenFactory.sol";
|
||||
import {RWAToken} from "../../../contracts/rwa/RWAToken.sol";
|
||||
|
||||
/**
|
||||
* @title RedeployLiIndexPublisher138
|
||||
* @notice Operator recovery when Gnosis Safe co-signer keys are unavailable:
|
||||
* deactivate Li* in registry, redeploy with deployer as INDEX_PUBLISHER, publish index level.
|
||||
*
|
||||
* Env: LI_TICKER (LiXAU|LiPMG|LiBMG1|LiBMG2|LiBMG3), PRIVATE_KEY, RWA_TOKEN_REGISTRY, RWA_TOKEN_FACTORY
|
||||
* REBASE_INDEX_VALUE (optional), OWNER, COMPLIANCE_ADMIN, INDEX_PUBLISHER, RWA_METHODOLOGY_HASH
|
||||
*/
|
||||
contract RedeployLiIndexPublisher138 is Script {
|
||||
struct LiProduct {
|
||||
string indexTicker;
|
||||
string name;
|
||||
string symbol;
|
||||
string assetGroup;
|
||||
string instrumentType;
|
||||
string underlyingAsset;
|
||||
uint256 defaultIndexValue;
|
||||
}
|
||||
|
||||
function run() external {
|
||||
string memory ticker = vm.envString("LI_TICKER");
|
||||
LiProduct memory p = _product(ticker);
|
||||
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address registryAddr = vm.envAddress("RWA_TOKEN_REGISTRY");
|
||||
address factoryAddr = vm.envAddress("RWA_TOKEN_FACTORY");
|
||||
address owner = vm.envOr("OWNER", vm.envOr("OMNL_COMPLIANCE_MULTISIG", deployer));
|
||||
address compliance = vm.envOr("COMPLIANCE_ADMIN", vm.envOr("OMNL_GNOSIS_SAFE_ADMIN", deployer));
|
||||
address publisher = vm.envOr("INDEX_PUBLISHER", deployer);
|
||||
uint256 rebaseValue = vm.envOr("REBASE_INDEX_VALUE", p.defaultIndexValue);
|
||||
bytes32 methodologyHash = vm.parseBytes32(
|
||||
vm.envOr("RWA_METHODOLOGY_HASH", string("0x6b6e599d0ba31d048b49302e263a12f0c59502f67a35100ad5c65b503b1d4b82"))
|
||||
);
|
||||
|
||||
RWATokenRegistry registry = RWATokenRegistry(registryAddr);
|
||||
RWATokenFactory factory = RWATokenFactory(factoryAddr);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
if (registry.isRegistered(p.indexTicker)) {
|
||||
registry.deactivateIndex(p.indexTicker);
|
||||
console.log("Deactivated", p.indexTicker, "in registry");
|
||||
}
|
||||
|
||||
address token = factory.deployRWAIndex(
|
||||
IRWATokenFactory.RWAProductConfig({
|
||||
indexTicker: p.indexTicker,
|
||||
name: p.name,
|
||||
symbol: p.symbol,
|
||||
decimals: 6,
|
||||
assetClass: "Commodities",
|
||||
assetGroup: p.assetGroup,
|
||||
instrumentType: p.instrumentType,
|
||||
underlyingAsset: p.underlyingAsset,
|
||||
gruLayer: "M00",
|
||||
jurisdiction: "International",
|
||||
initialOwner: owner,
|
||||
complianceAdmin: compliance,
|
||||
indexPublisher: publisher,
|
||||
initialIndexValue: p.defaultIndexValue,
|
||||
initialSupply: 0,
|
||||
methodologyDocumentHash: methodologyHash,
|
||||
registerInUniversalAssetRegistry: false
|
||||
})
|
||||
);
|
||||
|
||||
if (rebaseValue != p.defaultIndexValue) {
|
||||
RWAToken(token).updateIndexValue(rebaseValue);
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("Li* redeployed", p.indexTicker, token);
|
||||
console.log("indexValue", RWAToken(token).indexValue());
|
||||
console.log("indexPublisher", publisher);
|
||||
}
|
||||
|
||||
function _product(string memory ticker) internal pure returns (LiProduct memory p) {
|
||||
bytes32 h = keccak256(bytes(ticker));
|
||||
if (h == keccak256("LiXAU")) {
|
||||
return LiProduct({
|
||||
indexTicker: "LiXAU",
|
||||
name: "XAU Liquidity Index (M00)",
|
||||
symbol: "LiXAU",
|
||||
assetGroup: "Precious Metals",
|
||||
instrumentType: "Commodity Index",
|
||||
underlyingAsset: "Gold",
|
||||
defaultIndexValue: 1_000_000
|
||||
});
|
||||
}
|
||||
if (h == keccak256("LiPMG")) {
|
||||
return LiProduct({
|
||||
indexTicker: "LiPMG",
|
||||
name: "Precious Metals Group Index (M00)",
|
||||
symbol: "LiPMG",
|
||||
assetGroup: "Precious Metals",
|
||||
instrumentType: "Basket Index",
|
||||
underlyingAsset: "Precious Metals",
|
||||
defaultIndexValue: 1_000_000
|
||||
});
|
||||
}
|
||||
if (h == keccak256("LiBMG1")) {
|
||||
return LiProduct({
|
||||
indexTicker: "LiBMG1",
|
||||
name: "Base Metals Group Index 1 (M00)",
|
||||
symbol: "LiBMG1",
|
||||
assetGroup: "Industrial Metals",
|
||||
instrumentType: "Basket Index",
|
||||
underlyingAsset: "Base Metals",
|
||||
defaultIndexValue: 1_000_000
|
||||
});
|
||||
}
|
||||
if (h == keccak256("LiBMG2")) {
|
||||
return LiProduct({
|
||||
indexTicker: "LiBMG2",
|
||||
name: "Base Metals Group Index 2 (M00)",
|
||||
symbol: "LiBMG2",
|
||||
assetGroup: "Industrial Metals",
|
||||
instrumentType: "Basket Index",
|
||||
underlyingAsset: "Battery Metals",
|
||||
defaultIndexValue: 1_000_000
|
||||
});
|
||||
}
|
||||
if (h == keccak256("LiBMG3")) {
|
||||
return LiProduct({
|
||||
indexTicker: "LiBMG3",
|
||||
name: "Base Metals Group Index 3 (M00)",
|
||||
symbol: "LiBMG3",
|
||||
assetGroup: "Industrial Metals",
|
||||
instrumentType: "Basket Index",
|
||||
underlyingAsset: "Building Metals",
|
||||
defaultIndexValue: 1_000_000
|
||||
});
|
||||
}
|
||||
revert("RedeployLiIndexPublisher138: unsupported LI_TICKER");
|
||||
}
|
||||
}
|
||||
71
script/deploy/rwa/RedeployLiXauPublisher138.s.sol
Normal file
71
script/deploy/rwa/RedeployLiXauPublisher138.s.sol
Normal file
@@ -0,0 +1,71 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import {RWATokenRegistry} from "../../../contracts/rwa/RWATokenRegistry.sol";
|
||||
import {RWATokenFactory} from "../../../contracts/rwa/RWATokenFactory.sol";
|
||||
import {IRWATokenFactory} from "../../../contracts/rwa/IRWATokenFactory.sol";
|
||||
import {RWAToken} from "../../../contracts/rwa/RWAToken.sol";
|
||||
|
||||
/**
|
||||
* @title RedeployLiXauPublisher138
|
||||
* @notice Operator recovery when Gnosis Safe co-signer keys are unavailable:
|
||||
* deactivate LiXAU in registry, redeploy with deployer as INDEX_PUBLISHER, publish rebase level.
|
||||
*
|
||||
* Env: PRIVATE_KEY, RWA_TOKEN_REGISTRY, RWA_TOKEN_FACTORY, REBASE_INDEX_VALUE (default 1885856)
|
||||
* OWNER (default OMNL multisig), COMPLIANCE_ADMIN (default Gnosis Safe admin)
|
||||
* INDEX_PUBLISHER (default deployer), RWA_METHODOLOGY_HASH
|
||||
*/
|
||||
contract RedeployLiXauPublisher138 is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address registryAddr = vm.envAddress("RWA_TOKEN_REGISTRY");
|
||||
address factoryAddr = vm.envAddress("RWA_TOKEN_FACTORY");
|
||||
address owner = vm.envOr("OWNER", vm.envOr("OMNL_COMPLIANCE_MULTISIG", deployer));
|
||||
address compliance = vm.envOr("COMPLIANCE_ADMIN", vm.envOr("OMNL_GNOSIS_SAFE_ADMIN", deployer));
|
||||
address publisher = vm.envOr("INDEX_PUBLISHER", deployer);
|
||||
uint256 rebaseValue = vm.envOr("REBASE_INDEX_VALUE", uint256(1_885_856));
|
||||
bytes32 methodologyHash = vm.parseBytes32(vm.envOr("RWA_METHODOLOGY_HASH", string("0x6b6e599d0ba31d048b49302e263a12f0c59502f67a35100ad5c65b503b1d4b82")));
|
||||
|
||||
RWATokenRegistry registry = RWATokenRegistry(registryAddr);
|
||||
RWATokenFactory factory = RWATokenFactory(factoryAddr);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
if (registry.isRegistered("LiXAU")) {
|
||||
registry.deactivateIndex("LiXAU");
|
||||
console.log("Deactivated LiXAU in registry");
|
||||
}
|
||||
|
||||
address token = factory.deployRWAIndex(
|
||||
IRWATokenFactory.RWAProductConfig({
|
||||
indexTicker: "LiXAU",
|
||||
name: "XAU Liquidity Index (M00)",
|
||||
symbol: "LiXAU",
|
||||
decimals: 6,
|
||||
assetClass: "Commodities",
|
||||
assetGroup: "Precious Metals",
|
||||
instrumentType: "Commodity Index",
|
||||
underlyingAsset: "Gold",
|
||||
gruLayer: "M00",
|
||||
jurisdiction: "International",
|
||||
initialOwner: owner,
|
||||
complianceAdmin: compliance,
|
||||
indexPublisher: publisher,
|
||||
initialIndexValue: 1_000_000,
|
||||
initialSupply: 0,
|
||||
methodologyDocumentHash: methodologyHash,
|
||||
registerInUniversalAssetRegistry: false
|
||||
})
|
||||
);
|
||||
|
||||
RWAToken(token).updateIndexValue(rebaseValue);
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("LiXAU redeployed", token);
|
||||
console.log("indexValue", RWAToken(token).indexValue());
|
||||
console.log("indexPublisher", publisher);
|
||||
}
|
||||
}
|
||||
125
script/flash/RunMainnetUniV2CwusdcUsdcFlashRebalanceRemove.s.sol
Normal file
125
script/flash/RunMainnetUniV2CwusdcUsdcFlashRebalanceRemove.s.sol
Normal file
@@ -0,0 +1,125 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {
|
||||
AaveUniV2CwStableRebalanceFlashReceiver
|
||||
} from "../../contracts/flash/AaveUniV2CwStableRebalanceFlashReceiver.sol";
|
||||
|
||||
/**
|
||||
* @title RunMainnetUniV2CwusdcUsdcFlashRebalanceRemove
|
||||
* @notice Simulate or broadcast flash rebalance + LP remove on mainnet cWUSDC/USDC UniV2.
|
||||
*
|
||||
* Prerequisite: run plan-mainnet-cwusdc-usdc-univ2-flash-rebalance-remove.py and transfer LP
|
||||
* to the receiver (or set UNIV2_FLASH_PULL_LP=1 with prior LP approval to receiver).
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY
|
||||
* ETHEREUM_MAINNET_RPC
|
||||
* UNIV2_FLASH_REBALANCE_RECEIVER_MAINNET
|
||||
* UNIV2_FLASH_REBALANCE_PLAN_JSON optional path to planner JSON
|
||||
*/
|
||||
contract RunMainnetUniV2CwusdcUsdcFlashRebalanceRemove is Script {
|
||||
address internal constant DEFAULT_PAIR = 0xC28706F899266b36BC43cc072b3a921BDf2C48D9;
|
||||
address internal constant DEFAULT_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
|
||||
address internal constant DEFAULT_CW = 0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a;
|
||||
address internal constant DEFAULT_USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address receiver = vm.envAddress("UNIV2_FLASH_REBALANCE_RECEIVER_MAINNET");
|
||||
string memory planPath = vm.envOr(
|
||||
"UNIV2_FLASH_REBALANCE_PLAN_JSON",
|
||||
string("reports/status/mainnet-cwusdc-usdc-univ2-flash-rebalance-plan-latest.json")
|
||||
);
|
||||
|
||||
(
|
||||
address pair,
|
||||
address router,
|
||||
address cw,
|
||||
address usdc,
|
||||
address lpHolder,
|
||||
address recipient,
|
||||
uint256 lpAmount,
|
||||
uint256 flashStableIn,
|
||||
uint256 minCwRebalance,
|
||||
uint256 minCwRemove,
|
||||
uint256 minStableRemove,
|
||||
uint256 cwToSell,
|
||||
uint256 minStableRepay
|
||||
) = _loadPlan(planPath);
|
||||
|
||||
pair = pair == address(0) ? DEFAULT_PAIR : pair;
|
||||
router = router == address(0) ? DEFAULT_ROUTER : router;
|
||||
cw = cw == address(0) ? DEFAULT_CW : cw;
|
||||
usdc = usdc == address(0) ? DEFAULT_USDC : usdc;
|
||||
|
||||
console.log("receiver", receiver);
|
||||
console.log("pair", pair);
|
||||
console.log("lpHolder", lpHolder);
|
||||
console.log("lpAmount", lpAmount);
|
||||
console.log("flashStableIn", flashStableIn);
|
||||
console.log("receiverLpBefore", IERC20(pair).balanceOf(receiver));
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
if (vm.envOr("UNIV2_FLASH_PULL_LP", uint256(0)) == 1) {
|
||||
AaveUniV2CwStableRebalanceFlashReceiver(receiver).pullLpFrom(pair, lpHolder, lpAmount);
|
||||
}
|
||||
AaveUniV2CwStableRebalanceFlashReceiver.RebalanceRemoveParams memory p =
|
||||
AaveUniV2CwStableRebalanceFlashReceiver.RebalanceRemoveParams({
|
||||
router: router,
|
||||
pair: pair,
|
||||
cwToken: cw,
|
||||
stableToken: usdc,
|
||||
lpAmount: lpAmount,
|
||||
rebalanceStableIn: flashStableIn,
|
||||
minCwFromRebalance: minCwRebalance,
|
||||
minStableFromRemove: minStableRemove,
|
||||
minCwFromRemove: minCwRemove,
|
||||
cwToSellForRepay: cwToSell,
|
||||
minStableFromRepaySwap: minStableRepay,
|
||||
recipient: recipient
|
||||
});
|
||||
AaveUniV2CwStableRebalanceFlashReceiver(receiver).runRebalanceRemove(usdc, flashStableIn, p);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("recipientStableAfter", IERC20(usdc).balanceOf(recipient));
|
||||
console.log("recipientCwAfter", IERC20(cw).balanceOf(recipient));
|
||||
}
|
||||
|
||||
function _loadPlan(string memory path)
|
||||
internal
|
||||
view
|
||||
returns (
|
||||
address pair,
|
||||
address router,
|
||||
address cw,
|
||||
address usdc,
|
||||
address lpHolder,
|
||||
address recipient,
|
||||
uint256 lpAmount,
|
||||
uint256 flashStableIn,
|
||||
uint256 minCwRebalance,
|
||||
uint256 minCwRemove,
|
||||
uint256 minStableRemove,
|
||||
uint256 cwToSell,
|
||||
uint256 minStableRepay
|
||||
)
|
||||
{
|
||||
string memory json = vm.readFile(path);
|
||||
pair = vm.parseJsonAddress(json, ".pair");
|
||||
router = vm.parseJsonAddress(json, ".router");
|
||||
cw = vm.parseJsonAddress(json, ".cwToken");
|
||||
usdc = vm.parseJsonAddress(json, ".stableToken");
|
||||
lpHolder = vm.parseJsonAddress(json, ".lpHolder");
|
||||
recipient = vm.parseJsonAddress(json, ".recipient");
|
||||
lpAmount = vm.parseJsonUint(json, ".lpAmountRaw");
|
||||
flashStableIn = vm.parseJsonUint(json, ".flashLoan.stableBorrowRaw");
|
||||
minCwRebalance = vm.parseJsonUint(json, ".rebalanceSwap.minCwOutRaw");
|
||||
minCwRemove = vm.parseJsonUint(json, ".removeLiquidity.minCwOutRaw");
|
||||
minStableRemove = vm.parseJsonUint(json, ".removeLiquidity.minStableOutRaw");
|
||||
cwToSell = vm.parseJsonUint(json, ".repaySwap.cwToSellRaw");
|
||||
minStableRepay = vm.parseJsonUint(json, ".repaySwap.minStableOutRaw");
|
||||
}
|
||||
}
|
||||
101
script/flash/RunManagedMainnetAaveCwusdtUsdtQuotePushCycle.s.sol
Normal file
101
script/flash/RunManagedMainnetAaveCwusdtUsdtQuotePushCycle.s.sol
Normal file
@@ -0,0 +1,101 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {AaveQuotePushFlashReceiver} from "../../contracts/flash/AaveQuotePushFlashReceiver.sol";
|
||||
import {QuotePushTreasuryManager} from "../../contracts/flash/QuotePushTreasuryManager.sol";
|
||||
|
||||
interface IDODOPMMPoolQuoteManagedUsdt {
|
||||
function querySellQuote(address trader, uint256 payQuoteAmount) external view returns (uint256 receiveBaseAmount, uint256 mtFee);
|
||||
}
|
||||
|
||||
/// @notice USDT rail mirror of RunManagedMainnetAaveCwusdcUsdcQuotePushCycle.
|
||||
contract RunManagedMainnetAaveCwusdtUsdtQuotePushCycle is Script {
|
||||
address internal constant DEFAULT_POOL = 0x79156F6B7bf71a1B72D78189B540A89A6C13F6FC;
|
||||
address internal constant DEFAULT_CWUSDT = 0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE;
|
||||
address internal constant DEFAULT_USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
|
||||
uint256 internal constant DEFENDED_SAFE_CAP_RAW = 2_964_298;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address receiver = vm.envAddress("AAVE_QUOTE_PUSH_RECEIVER_MAINNET");
|
||||
address managerAddr = vm.envAddress("QUOTE_PUSH_TREASURY_MANAGER_MAINNET");
|
||||
address pool = vm.envOr("POOL_CWUSDT_USDT_MAINNET", DEFAULT_POOL);
|
||||
address integration = vm.envAddress("DODO_PMM_INTEGRATION_MAINNET");
|
||||
address baseToken = vm.envOr("CWUSDT_MAINNET", DEFAULT_CWUSDT);
|
||||
address usdt = vm.envOr("USDT_MAINNET", DEFAULT_USDT);
|
||||
address unwinder = vm.envAddress("QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET");
|
||||
uint256 amount = vm.envUint("FLASH_QUOTE_AMOUNT_RAW");
|
||||
uint256 localCap = vm.envOr("MAX_FLASH_QUOTE_AMOUNT_RAW", DEFENDED_SAFE_CAP_RAW);
|
||||
bool harvest = vm.envOr("QUOTE_PUSH_TREASURY_HARVEST", uint256(1)) == 1;
|
||||
uint256 gasHoldbackTargetRaw = vm.envOr("QUOTE_PUSH_TREASURY_GAS_HOLDBACK_TARGET_RAW", uint256(0));
|
||||
|
||||
require(pool == DEFAULT_POOL, "defended pool only");
|
||||
require(localCap <= DEFENDED_SAFE_CAP_RAW, "local cap exceeds defended safe cap");
|
||||
require(amount <= localCap, "flash amount exceeds cap");
|
||||
|
||||
QuotePushTreasuryManager manager = QuotePushTreasuryManager(managerAddr);
|
||||
AaveQuotePushFlashReceiver.QuotePushParams memory p =
|
||||
_loadQuotePushParams(receiver, pool, integration, baseToken, unwinder, amount);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
(uint256 harvested, uint256 gasAmount, uint256 recycleAmount) =
|
||||
manager.runManagedCycle(usdt, amount, p, harvest, gasHoldbackTargetRaw);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("managedCycleHarvestedRaw", harvested);
|
||||
console.log("managedCycleGasDistributionRaw", gasAmount);
|
||||
console.log("managedCycleRecycleDistributionRaw", recycleAmount);
|
||||
}
|
||||
|
||||
function _loadQuotePushParams(
|
||||
address receiver,
|
||||
address pool,
|
||||
address integration,
|
||||
address baseToken,
|
||||
address unwinder,
|
||||
uint256 amount
|
||||
) internal view returns (AaveQuotePushFlashReceiver.QuotePushParams memory p) {
|
||||
uint256 minPmmNum = vm.envOr("MIN_OUT_PMM_NUM", uint256(985));
|
||||
uint256 minPmmDen = vm.envOr("MIN_OUT_PMM_DEN", uint256(1000));
|
||||
uint256 minOutPmm = vm.envOr("MIN_OUT_PMM", uint256(0));
|
||||
if (minOutPmm == 0) {
|
||||
(uint256 baseOut,) = IDODOPMMPoolQuoteManagedUsdt(pool).querySellQuote(receiver, amount);
|
||||
minOutPmm = (baseOut * minPmmNum) / minPmmDen;
|
||||
}
|
||||
uint256 premiumBps = vm.envOr("AAVE_FLASH_PREMIUM_BPS", uint256(5));
|
||||
uint256 buf = vm.envOr("MIN_OUT_UNWIND_BUFFER_RAW", uint256(5000));
|
||||
uint256 premium = (amount * premiumBps) / 10000;
|
||||
uint256 minOutUnwind = vm.envOr("MIN_OUT_UNWIND", amount + premium + buf);
|
||||
uint256 unwindMode = vm.envOr("UNWIND_MODE", uint256(1));
|
||||
bytes memory unwindData;
|
||||
if (unwindMode == 1) {
|
||||
address dodoPool = vm.envOr("UNWIND_DODO_POOL", pool);
|
||||
unwindData = abi.encode(dodoPool);
|
||||
} else {
|
||||
revert("USDT rail: UNWIND_MODE=1 (DODO) required for now");
|
||||
}
|
||||
p = AaveQuotePushFlashReceiver.QuotePushParams({
|
||||
integration: integration,
|
||||
pmmPool: pool,
|
||||
baseToken: baseToken,
|
||||
externalUnwinder: unwinder,
|
||||
minOutPmm: minOutPmm,
|
||||
minOutUnwind: minOutUnwind,
|
||||
unwindData: unwindData,
|
||||
atomicBridge: AaveQuotePushFlashReceiver.AtomicBridgeParams({
|
||||
coordinator: address(0),
|
||||
sourceChain: 0,
|
||||
destinationChain: 0,
|
||||
destinationAsset: address(0),
|
||||
bridgeAmount: 0,
|
||||
minDestinationAmount: 0,
|
||||
destinationRecipient: address(0),
|
||||
destinationDeadline: 0,
|
||||
routeId: bytes32(0),
|
||||
settlementMode: bytes32(0),
|
||||
submitCommitment: false
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
90
script/flash/SeedMainnetCwStablePoolPermanent.s.sol
Normal file
90
script/flash/SeedMainnetCwStablePoolPermanent.s.sol
Normal file
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
|
||||
interface IDODOPMMIntegrationSeed {
|
||||
function isRegisteredPool(address pool) external view returns (bool);
|
||||
function addLiquidity(address pool, uint256 baseAmount, uint256 quoteAmount)
|
||||
external
|
||||
returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare);
|
||||
}
|
||||
|
||||
interface IDODOPMMPoolSeed {
|
||||
function _BASE_TOKEN_() external view returns (address);
|
||||
function _QUOTE_TOKEN_() external view returns (address);
|
||||
function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve);
|
||||
}
|
||||
|
||||
/// @notice Permanently seed defended mainnet cWUSDC/USDC or cWUSDT/USDT DODO pools via addLiquidity.
|
||||
/// @dev Uses deployer inventory — not flash (flash must repay same block). Pair with Aave borrow + vault deposit off-chain.
|
||||
contract SeedMainnetCwStablePoolPermanent is Script {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
address internal constant DEFAULT_INTEGRATION = 0xa9F284eD010f4F7d7F8F201742b49b9f58e29b84;
|
||||
address internal constant POOL_CWUSDC_USDC = 0x69776fc607e9edA8042e320e7e43f54d06c68f0E;
|
||||
/// @dev Registered on DODOPMMIntegration.pools(cWUSDT, USDT); 0x99d012… is an unregistered duplicate.
|
||||
address internal constant POOL_CWUSDT_USDT = 0x79156F6B7bf71a1B72D78189B540A89A6C13F6FC;
|
||||
address internal constant CWUSDC = 0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a;
|
||||
address internal constant CWUSDT = 0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE;
|
||||
address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
|
||||
address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
string memory rail = vm.envOr("CW_STABLE_RAIL", string("USDC"));
|
||||
uint256 baseAmount = vm.envUint("SEED_BASE_AMOUNT_RAW");
|
||||
uint256 quoteAmount = vm.envUint("SEED_QUOTE_AMOUNT_RAW");
|
||||
address integration = vm.envOr("DODO_PMM_INTEGRATION_MAINNET", DEFAULT_INTEGRATION);
|
||||
|
||||
(address pool, address baseToken, address quoteToken) = _resolveRail(rail);
|
||||
|
||||
IDODOPMMPoolSeed poolView = IDODOPMMPoolSeed(pool);
|
||||
require(poolView._BASE_TOKEN_() == baseToken, "base mismatch");
|
||||
require(poolView._QUOTE_TOKEN_() == quoteToken, "quote mismatch");
|
||||
require(IDODOPMMIntegrationSeed(integration).isRegisteredPool(pool), "pool not registered on integration");
|
||||
|
||||
(uint256 baseBefore, uint256 quoteBefore) = poolView.getVaultReserve();
|
||||
console.log("deployer", deployer);
|
||||
console.log("rail", rail);
|
||||
console.log("pool", pool);
|
||||
console.log("baseAmount", baseAmount);
|
||||
console.log("quoteAmount", quoteAmount);
|
||||
console.log("baseReserveBefore", baseBefore);
|
||||
console.log("quoteReserveBefore", quoteBefore);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
IERC20(baseToken).forceApprove(integration, baseAmount);
|
||||
IERC20(quoteToken).forceApprove(integration, quoteAmount);
|
||||
(uint256 baseShare, uint256 quoteShare, uint256 lpShare) =
|
||||
IDODOPMMIntegrationSeed(integration).addLiquidity(pool, baseAmount, quoteAmount);
|
||||
IERC20(baseToken).forceApprove(integration, 0);
|
||||
IERC20(quoteToken).forceApprove(integration, 0);
|
||||
vm.stopBroadcast();
|
||||
|
||||
(uint256 baseAfter, uint256 quoteAfter) = poolView.getVaultReserve();
|
||||
console.log("baseShare", baseShare);
|
||||
console.log("quoteShare", quoteShare);
|
||||
console.log("lpShare", lpShare);
|
||||
console.log("baseReserveAfter", baseAfter);
|
||||
console.log("quoteReserveAfter", quoteAfter);
|
||||
}
|
||||
|
||||
function _resolveRail(string memory rail)
|
||||
internal
|
||||
pure
|
||||
returns (address pool, address baseToken, address quoteToken)
|
||||
{
|
||||
bytes32 key = keccak256(bytes(rail));
|
||||
if (key == keccak256(bytes("USDC"))) {
|
||||
return (POOL_CWUSDC_USDC, CWUSDC, USDC);
|
||||
}
|
||||
if (key == keccak256(bytes("USDT"))) {
|
||||
return (POOL_CWUSDT_USDT, CWUSDT, USDT);
|
||||
}
|
||||
revert("CW_STABLE_RAIL must be USDC or USDT");
|
||||
}
|
||||
}
|
||||
48
script/hybx-omnl/RecoverOMNLReserveVaultSafe.s.sol
Normal file
48
script/hybx-omnl/RecoverOMNLReserveVaultSafe.s.sol
Normal file
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console2} from "forge-std/Script.sol";
|
||||
import {ReserveCommitmentStore} from "../../contracts/hybx-omnl/ReserveCommitmentStore.sol";
|
||||
|
||||
/// @notice Deploy reserve store with deployer bootstrap, enable notary gate, migrate commitment, hand DEFAULT_ADMIN to vault Safe.
|
||||
contract RecoverOMNLReserveVaultSafe is Script {
|
||||
bytes32 private constant DEFAULT_ADMIN_ROLE = 0x00;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address vaultSafe = vm.envAddress("OMNL_VAULT_SAFE");
|
||||
address legacyStore = vm.envAddress("OMNL_RESERVE_STORE_138");
|
||||
address notary = vm.envAddress("OMNL_NOTARY_REGISTRY");
|
||||
bytes32 lineId = vm.envBytes32("OMNL_HEALTH_LINE_ID");
|
||||
|
||||
bytes32 jurId = keccak256(bytes(vm.envOr("OMNL_JURISDICTION_ID", string("ID"))));
|
||||
bytes32 matrixId = keccak256(bytes(vm.envOr("OMNL_MATRIX_CONTROL_ID", string("ID-OMNL-001"))));
|
||||
uint256 attTh = vm.envOr("OMNL_RESERVE_ATTESTATION_THRESHOLD", uint256(3));
|
||||
|
||||
ReserveCommitmentStore legacy = ReserveCommitmentStore(legacyStore);
|
||||
ReserveCommitmentStore.Commitment memory c = legacy.getCommitment(lineId);
|
||||
address mirror = legacy.mirrorReceiver();
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
ReserveCommitmentStore storeV3 = new ReserveCommitmentStore(deployer);
|
||||
storeV3.configureNotaryGate(notary, true, jurId, matrixId);
|
||||
storeV3.setAttestationThreshold(attTh);
|
||||
if (mirror != address(0)) {
|
||||
storeV3.setMirrorReceiver(mirror);
|
||||
}
|
||||
if (c.R != 0) {
|
||||
storeV3.commitReserve(lineId, c.R, c.validUntil, c.evidenceHash, c.merkleRoot);
|
||||
}
|
||||
storeV3.grantRole(DEFAULT_ADMIN_ROLE, vaultSafe);
|
||||
storeV3.revokeRole(DEFAULT_ADMIN_ROLE, deployer);
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console2.log("ReserveCommitmentStoreV3", address(storeV3));
|
||||
console2.log("vaultSafe", vaultSafe);
|
||||
console2.log("requireNotarizedEvidence", storeV3.requireNotarizedEvidence());
|
||||
console2.log("legacyStore", legacyStore);
|
||||
}
|
||||
}
|
||||
@@ -103,13 +103,14 @@ contract DeployM00DiamondHub138 is Script {
|
||||
}
|
||||
|
||||
function _accessSelectors() internal pure returns (bytes4[] memory s) {
|
||||
s = new bytes4[](6);
|
||||
s = new bytes4[](7);
|
||||
s[0] = AccessFacet.grantRoles.selector;
|
||||
s[1] = AccessFacet.revokeRoles.selector;
|
||||
s[2] = AccessFacet.ROLE_UPGRADE_BIT.selector;
|
||||
s[3] = AccessFacet.ROLE_GOVERNANCE_BIT.selector;
|
||||
s[4] = AccessFacet.ROLE_INDEX_BIT.selector;
|
||||
s[5] = AccessFacet.ROLE_MONETARY_BIT.selector;
|
||||
s[2] = AccessFacet.hasRoles.selector;
|
||||
s[3] = AccessFacet.ROLE_UPGRADE_BIT.selector;
|
||||
s[4] = AccessFacet.ROLE_GOVERNANCE_BIT.selector;
|
||||
s[5] = AccessFacet.ROLE_INDEX_BIT.selector;
|
||||
s[6] = AccessFacet.ROLE_MONETARY_BIT.selector;
|
||||
}
|
||||
|
||||
function _bridgeSelectors() internal pure returns (bytes4[] memory s) {
|
||||
@@ -137,15 +138,19 @@ contract DeployM00DiamondHub138 is Script {
|
||||
}
|
||||
|
||||
function _rwaDocSelectors() internal pure returns (bytes4[] memory s) {
|
||||
s = new bytes4[](2);
|
||||
s = new bytes4[](4);
|
||||
s[0] = RWADocumentFacet.anchorDocument.selector;
|
||||
s[1] = RWADocumentFacet.setPrimaryContentHash.selector;
|
||||
s[2] = RWADocumentFacet.documentCount.selector;
|
||||
s[3] = RWADocumentFacet.getDocument.selector;
|
||||
}
|
||||
|
||||
function _rwaStdSelectors() internal pure returns (bytes4[] memory s) {
|
||||
s = new bytes4[](3);
|
||||
s = new bytes4[](5);
|
||||
s[0] = RWAStandardsRegistryFacet.enableStandard.selector;
|
||||
s[1] = RWAStandardsRegistryFacet.disableStandard.selector;
|
||||
s[2] = RWAStandardsRegistryFacet.bindAssetStandardFacet.selector;
|
||||
s[3] = RWAStandardsRegistryFacet.isStandardEnabled.selector;
|
||||
s[4] = RWAStandardsRegistryFacet.assetStandardFacet.selector;
|
||||
}
|
||||
}
|
||||
|
||||
32
script/m00-diamond/SeedM00LiIndexWeights138.s.sol
Normal file
32
script/m00-diamond/SeedM00LiIndexWeights138.s.sol
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import {IndexFacet} from "@gru/facets/IndexFacet.sol";
|
||||
import {IERC173} from "@gru/interfaces/IERC173.sol";
|
||||
|
||||
/// @notice Seed unit weights (1e18) for each Li* index on the M00 diamond hub.
|
||||
contract SeedM00LiIndexWeights138 is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address diamond = vm.envAddress("M00_DIAMOND_HUB");
|
||||
uint256 chainId = 138;
|
||||
|
||||
string[5] memory tickers = ["LiXAU", "LiPMG", "LiBMG1", "LiBMG2", "LiBMG3"];
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
for (uint256 i = 0; i < tickers.length; i++) {
|
||||
bytes32 indexId = keccak256(abi.encode(tickers[i], chainId));
|
||||
bytes32[] memory keys = new bytes32[](1);
|
||||
keys[0] = keccak256(abi.encode(tickers[i]));
|
||||
uint256[] memory weights = new uint256[](1);
|
||||
weights[0] = 1e18;
|
||||
IndexFacet(diamond).setWeights(indexId, keys, weights, 1, keccak256("M00_LI_v1"));
|
||||
}
|
||||
address newOwner = vm.envOr("M00_DIAMOND_OWNER", vm.envOr("GOVERNANCE_CONTROLLER", address(0)));
|
||||
if (newOwner != address(0)) {
|
||||
IERC173(diamond).transferOwnership(newOwner);
|
||||
}
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
||||
133
script/m00-diamond/UpgradeM00DiamondHubComplete138.s.sol
Normal file
133
script/m00-diamond/UpgradeM00DiamondHubComplete138.s.sol
Normal file
@@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import {IDiamondCut} from "@gru/interfaces/IDiamondCut.sol";
|
||||
import {IndexFacet} from "@gru/facets/IndexFacet.sol";
|
||||
import {MonetaryFacet} from "@gru/facets/MonetaryFacet.sol";
|
||||
import {GovernanceFacet} from "@gru/facets/GovernanceFacet.sol";
|
||||
import {AccessFacet} from "@gru/facets/AccessFacet.sol";
|
||||
import {IAccess} from "@gru/interfaces/IAccess.sol";
|
||||
import {IGovernance} from "@gru/interfaces/IGovernance.sol";
|
||||
import {RWAInstrumentFacet} from "../../contracts/rwa/diamond/facets/RWAInstrumentFacet.sol";
|
||||
import {RWADocumentFacet} from "../../contracts/rwa/diamond/facets/RWADocumentFacet.sol";
|
||||
import {RWAStandardsRegistryFacet} from "../../contracts/rwa/diamond/facets/RWAStandardsRegistryFacet.sol";
|
||||
|
||||
/**
|
||||
* @title UpgradeM00DiamondHubComplete138
|
||||
* @notice Add GRC Index/Monetary/Governance facets, RWA read selectors, AccessFacet.hasRoles; grant GRC roles.
|
||||
*/
|
||||
contract UpgradeM00DiamondHubComplete138 is Script {
|
||||
uint256 internal constant ROLE_ALL_GRC = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3);
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address diamond = vm.envAddress("M00_DIAMOND_HUB");
|
||||
address governance = vm.envOr("GOVERNANCE_CONTROLLER", deployer);
|
||||
address accessFacet = vm.envAddress("M00_ACCESS_FACET");
|
||||
address rwaInst = vm.envAddress("M00_RWA_INSTRUMENT_FACET");
|
||||
address rwaDoc = vm.envAddress("M00_RWA_DOCUMENT_FACET");
|
||||
address rwaStd = vm.envAddress("M00_RWA_STANDARDS_FACET");
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
IndexFacet index = new IndexFacet();
|
||||
MonetaryFacet monetary = new MonetaryFacet();
|
||||
GovernanceFacet govFacet = new GovernanceFacet();
|
||||
|
||||
IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](7);
|
||||
cut[0] = _add(address(index), _indexSelectors());
|
||||
cut[1] = _add(address(monetary), _monetarySelectors());
|
||||
cut[2] = _add(address(govFacet), _governanceSelectors());
|
||||
cut[3] = _add(accessFacet, _accessExtraSelectors());
|
||||
cut[4] = _add(rwaInst, _rwaInstViewSelectors());
|
||||
cut[5] = _add(rwaDoc, _rwaDocViewSelectors());
|
||||
cut[6] = _add(rwaStd, _rwaStdViewSelectors());
|
||||
|
||||
IDiamondCut(diamond).diamondCut(cut, address(0), "");
|
||||
|
||||
IAccess(diamond).grantRoles(governance, ROLE_ALL_GRC);
|
||||
if (governance != deployer) {
|
||||
IAccess(diamond).grantRoles(deployer, ROLE_ALL_GRC);
|
||||
}
|
||||
|
||||
IGovernance(diamond).setGovernanceParams(86_400, 5_000);
|
||||
|
||||
console.log("M00Diamond", diamond);
|
||||
console.log("IndexFacet", address(index));
|
||||
console.log("MonetaryFacet", address(monetary));
|
||||
console.log("GovernanceFacet", address(govFacet));
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
|
||||
function _add(address facet, bytes4[] memory sels)
|
||||
internal
|
||||
pure
|
||||
returns (IDiamondCut.FacetCut memory)
|
||||
{
|
||||
return IDiamondCut.FacetCut({
|
||||
facetAddress: facet,
|
||||
action: IDiamondCut.FacetCutAction.Add,
|
||||
functionSelectors: sels
|
||||
});
|
||||
}
|
||||
|
||||
function _indexSelectors() internal pure returns (bytes4[] memory s) {
|
||||
s = new bytes4[](8);
|
||||
s[0] = IndexFacet.setWeights.selector;
|
||||
s[1] = IndexFacet.getIndex.selector;
|
||||
s[2] = IndexFacet.recalcLiCRI.selector;
|
||||
s[3] = IndexFacet.recalcLiCRIWeighted.selector;
|
||||
s[4] = IndexFacet.setDashboardComposite.selector;
|
||||
s[5] = IndexFacet.getLiCRI.selector;
|
||||
s[6] = IndexFacet.setIndexValue.selector;
|
||||
s[7] = IndexFacet.getIndexValue.selector;
|
||||
}
|
||||
|
||||
function _monetarySelectors() internal pure returns (bytes4[] memory s) {
|
||||
s = new bytes4[](6);
|
||||
s[0] = MonetaryFacet.mintM1.selector;
|
||||
s[1] = MonetaryFacet.burnM1.selector;
|
||||
s[2] = MonetaryFacet.issueM0.selector;
|
||||
s[3] = MonetaryFacet.redeemM0.selector;
|
||||
s[4] = MonetaryFacet.setScalarS.selector;
|
||||
s[5] = MonetaryFacet.getLayers.selector;
|
||||
}
|
||||
|
||||
function _governanceSelectors() internal pure returns (bytes4[] memory s) {
|
||||
s = new bytes4[](9);
|
||||
s[0] = GovernanceFacet.proposeCut.selector;
|
||||
s[1] = GovernanceFacet.queueCut.selector;
|
||||
s[2] = GovernanceFacet.executeCut.selector;
|
||||
s[3] = GovernanceFacet.emergencyBrake.selector;
|
||||
s[4] = GovernanceFacet.timelock.selector;
|
||||
s[5] = GovernanceFacet.quorumBps.selector;
|
||||
s[6] = GovernanceFacet.eta.selector;
|
||||
s[7] = GovernanceFacet.proposer.selector;
|
||||
s[8] = GovernanceFacet.setGovernanceParams.selector;
|
||||
}
|
||||
|
||||
function _accessExtraSelectors() internal pure returns (bytes4[] memory s) {
|
||||
s = new bytes4[](1);
|
||||
s[0] = AccessFacet.hasRoles.selector;
|
||||
}
|
||||
|
||||
function _rwaInstViewSelectors() internal pure returns (bytes4[] memory s) {
|
||||
s = new bytes4[](2);
|
||||
s[0] = RWAInstrumentFacet.getIssuanceMode.selector;
|
||||
s[1] = RWAInstrumentFacet.getTokenPointer.selector;
|
||||
}
|
||||
|
||||
function _rwaDocViewSelectors() internal pure returns (bytes4[] memory s) {
|
||||
s = new bytes4[](2);
|
||||
s[0] = RWADocumentFacet.documentCount.selector;
|
||||
s[1] = RWADocumentFacet.getDocument.selector;
|
||||
}
|
||||
|
||||
function _rwaStdViewSelectors() internal pure returns (bytes4[] memory s) {
|
||||
s = new bytes4[](2);
|
||||
s[0] = RWAStandardsRegistryFacet.isStandardEnabled.selector;
|
||||
s[1] = RWAStandardsRegistryFacet.assetStandardFacet.selector;
|
||||
}
|
||||
}
|
||||
168
script/reserve/WireChain138OraclePegs138.s.sol
Normal file
168
script/reserve/WireChain138OraclePegs138.s.sol
Normal file
@@ -0,0 +1,168 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {Aggregator} from "../../contracts/oracle/Aggregator.sol";
|
||||
|
||||
interface IOraclePriceFeedWire {
|
||||
function aggregators(address asset) external view returns (address);
|
||||
function setAggregator(address asset, address aggregator, uint256 multiplier) external;
|
||||
function updatePriceFeed(address asset) external;
|
||||
function setUpdateInterval(uint256 interval) external;
|
||||
function updateInterval() external view returns (uint256);
|
||||
}
|
||||
|
||||
interface IPriceFeedKeeperWire {
|
||||
function isTracked(address asset) external view returns (bool);
|
||||
function trackAsset(address asset) external;
|
||||
function maxUpdatesPerCall() external view returns (uint256);
|
||||
function setMaxUpdatesPerCall(uint256 max) external;
|
||||
}
|
||||
|
||||
interface IDODOPMMIntegrationWire {
|
||||
function setReserveSystem(address reserveSystem_) external;
|
||||
function reserveSystem() external view returns (address);
|
||||
}
|
||||
|
||||
interface IReserveSystemWire {
|
||||
function updatePriceFeed(address asset, uint256 price, uint256 timestamp) external;
|
||||
}
|
||||
|
||||
interface IAggregatorWire {
|
||||
function addTransmitter(address transmitter) external;
|
||||
function updateAnswer(uint256 answer) external;
|
||||
function isTransmitter(address transmitter) external view returns (bool);
|
||||
function latestRoundData()
|
||||
external
|
||||
view
|
||||
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
|
||||
}
|
||||
|
||||
/// @notice Wire canonical Chain 138 assets to Chainlink-mirrored repo aggregators + keeper + reserve.
|
||||
contract WireChain138OraclePegs138 is Script {
|
||||
uint256 internal constant MULT = 1e10;
|
||||
|
||||
address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||
address internal constant WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
|
||||
address internal constant LINK = 0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03;
|
||||
address internal constant CUSDT = 0x93E66202A11B1772E55407B32B44e5Cd8eda7f22;
|
||||
address internal constant CUSDC = 0xf22258f57794CC8E06237084b353Ab30fFfa640b;
|
||||
address internal constant CEURC = 0x8085961F9cF02b4d800A3c6d386D31da4B34266a;
|
||||
address internal constant CEURT = 0xdf4b71c61E5912712C1Bdd451416B9aC26949d72;
|
||||
address internal constant CGBPC = 0x003960f16D9d34F2e98d62723B6721Fb92074aD2;
|
||||
address internal constant CGBPT = 0x350f54e4D23795f86A9c03988c7135357CCaD97c;
|
||||
address internal constant CAUDC = 0xD51482e567c03899eecE3CAe8a058161FD56069D;
|
||||
address internal constant CJPYC = 0xEe269e1226a334182aace90056EE4ee5Cc8A6770;
|
||||
address internal constant CCHFC = 0x873990849DDa5117d7C644f0aF24370797C03885;
|
||||
address internal constant CCADC = 0x54dBd40cF05e15906A2C21f600937e96787f5679;
|
||||
address internal constant CXAUC = 0x290E52a8819A4fbD0714E517225429aA2B70EC6b;
|
||||
address internal constant CXAUT = 0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E;
|
||||
address internal constant CBTC = 0xe94260c555aC1d9D3CC9E1632883452ebDf0082E;
|
||||
|
||||
function run() external {
|
||||
require(block.chainid == 138, "ChainID 138 only");
|
||||
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
|
||||
address opf = vm.envAddress("ORACLE_PRICE_FEED");
|
||||
address keeperAddr = vm.envAddress("PRICE_FEED_KEEPER_ADDRESS");
|
||||
address rs = vm.envAddress("RESERVE_SYSTEM");
|
||||
address ethAgg = vm.envAddress("AGGREGATOR_ADDRESS");
|
||||
address dodo = vm.envAddress("CHAIN_138_DODO_PMM_INTEGRATION");
|
||||
uint256 ethSeed8 = vm.envOr("FEED_ETH_USD_8", uint256(0));
|
||||
|
||||
IOraclePriceFeedWire op = IOraclePriceFeedWire(opf);
|
||||
IPriceFeedKeeperWire keeper = IPriceFeedKeeperWire(keeperAddr);
|
||||
|
||||
console.log("=== Wire Chain 138 oracle pegs ===");
|
||||
console.log("Deployer:", deployer);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
if (op.updateInterval() != 86400) {
|
||||
try op.setUpdateInterval(86400) {} catch {}
|
||||
}
|
||||
|
||||
if (ethSeed8 > 0) {
|
||||
IAggregatorWire eth = IAggregatorWire(ethAgg);
|
||||
if (!eth.isTransmitter(deployer)) {
|
||||
try eth.addTransmitter(deployer) {} catch {}
|
||||
}
|
||||
try eth.updateAnswer(ethSeed8) {} catch {}
|
||||
}
|
||||
|
||||
if (keeper.maxUpdatesPerCall() < 20) {
|
||||
keeper.setMaxUpdatesPerCall(20);
|
||||
}
|
||||
|
||||
address usdAgg = _deployMirror("USD/USD Chainlink mirror", vm.envUint("FEED_USD_USD_8"));
|
||||
address linkAgg = _deployMirror("LINK/USD Chainlink mirror", vm.envUint("FEED_LINK_USD_8"));
|
||||
address btcAgg = _deployMirror("BTC/USD Chainlink mirror", vm.envUint("FEED_BTC_USD_8"));
|
||||
address xauAgg = _deployMirror("XAU/USD Chainlink mirror", vm.envUint("FEED_XAU_USD_8"));
|
||||
address eurAgg = _deployMirror("EUR/USD Chainlink mirror", vm.envUint("FEED_EUR_USD_8"));
|
||||
address gbpAgg = _deployMirror("GBP/USD Chainlink mirror", vm.envUint("FEED_GBP_USD_8"));
|
||||
address audAgg = _deployMirror("AUD/USD Chainlink mirror", vm.envUint("FEED_AUD_USD_8"));
|
||||
address jpyAgg = _deployMirror("JPY/USD Chainlink mirror", vm.envUint("FEED_JPY_USD_8"));
|
||||
address chfAgg = _deployMirror("CHF/USD Chainlink mirror", vm.envUint("FEED_CHF_USD_8"));
|
||||
address cadAgg = _deployMirror("CAD/USD Chainlink mirror", vm.envUint("FEED_CAD_USD_8"));
|
||||
|
||||
_wire(op, keeper, rs, WETH9, ethAgg);
|
||||
_wire(op, keeper, rs, WETH10, ethAgg);
|
||||
_wire(op, keeper, rs, LINK, linkAgg);
|
||||
_wire(op, keeper, rs, CUSDT, usdAgg);
|
||||
_wire(op, keeper, rs, CUSDC, usdAgg);
|
||||
_wire(op, keeper, rs, CEURC, eurAgg);
|
||||
_wire(op, keeper, rs, CEURT, eurAgg);
|
||||
_wire(op, keeper, rs, CGBPC, gbpAgg);
|
||||
_wire(op, keeper, rs, CGBPT, gbpAgg);
|
||||
_wire(op, keeper, rs, CAUDC, audAgg);
|
||||
_wire(op, keeper, rs, CJPYC, jpyAgg);
|
||||
_wire(op, keeper, rs, CCHFC, chfAgg);
|
||||
_wire(op, keeper, rs, CCADC, cadAgg);
|
||||
_wire(op, keeper, rs, CXAUC, xauAgg);
|
||||
_wire(op, keeper, rs, CXAUT, xauAgg);
|
||||
_wire(op, keeper, rs, CBTC, btcAgg);
|
||||
|
||||
IDODOPMMIntegrationWire dodoInt = IDODOPMMIntegrationWire(dodo);
|
||||
if (dodoInt.reserveSystem() != rs) {
|
||||
dodoInt.setReserveSystem(rs);
|
||||
console.log("DODO reserveSystem wired:", rs);
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("=== Done ===");
|
||||
}
|
||||
|
||||
function _deployMirror(string memory description, uint256 price8) internal returns (address) {
|
||||
require(price8 > 0, "missing seed price");
|
||||
Aggregator agg = new Aggregator(description, msg.sender, 3600, 50);
|
||||
agg.addTransmitter(msg.sender);
|
||||
agg.updateAnswer(price8);
|
||||
console.log("Deployed feed:", description);
|
||||
console.log(" aggregator:", address(agg));
|
||||
return address(agg);
|
||||
}
|
||||
|
||||
function _wire(
|
||||
IOraclePriceFeedWire op,
|
||||
IPriceFeedKeeperWire keeper,
|
||||
address rs,
|
||||
address token,
|
||||
address agg
|
||||
) internal {
|
||||
require(agg != address(0), "zero aggregator");
|
||||
if (op.aggregators(token) == address(0)) {
|
||||
op.setAggregator(token, agg, MULT);
|
||||
}
|
||||
if (!keeper.isTracked(token)) {
|
||||
keeper.trackAsset(token);
|
||||
}
|
||||
try op.updatePriceFeed(token) {} catch {
|
||||
(, int256 ans,, uint256 updatedAt,) = IAggregatorWire(agg).latestRoundData();
|
||||
require(ans > 0, "bad agg price");
|
||||
IReserveSystemWire(rs).updatePriceFeed(token, uint256(ans) * MULT, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ cd ..
|
||||
# Deploy Admin Dashboard
|
||||
echo ""
|
||||
echo "--- Building Admin Dashboard ---"
|
||||
cd ../dbis_core/frontend
|
||||
cd /home/intlc/projects/dbis_core/frontend
|
||||
|
||||
if [ -f "package.json" ]; then
|
||||
npm install
|
||||
|
||||
180
scripts/deployment/export-bsc-cw-verification-artifacts.sh
Executable file
180
scripts/deployment/export-bsc-cw-verification-artifacts.sh
Executable file
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env bash
|
||||
# Export BSC cW* verification artifacts: pinned profile build + Standard JSON for BscScan manual upload.
|
||||
#
|
||||
# Usage (from smom-dbis-138/):
|
||||
# ./scripts/deployment/export-bsc-cw-verification-artifacts.sh
|
||||
# ./scripts/deployment/export-bsc-cw-verification-artifacts.sh --verify # also submit forge verify (cWBNB may fail)
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
REPO_ROOT="$(cd "$PROJECT_ROOT/.." && pwd)"
|
||||
OUT_DIR="$REPO_ROOT/reports/status/bsc-cw-verification"
|
||||
VERIFY=0
|
||||
[[ "${1:-}" == "--verify" ]] && VERIFY=1
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
if [[ -f "$SCRIPT_DIR/../lib/deployment/dotenv.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$SCRIPT_DIR/../lib/deployment/dotenv.sh"
|
||||
load_deployment_env --repo-root "$PROJECT_ROOT"
|
||||
fi
|
||||
|
||||
[[ -n "${ETHERSCAN_API_KEY:-}" ]] || { echo "ETHERSCAN_API_KEY required" >&2; exit 1; }
|
||||
|
||||
ADMIN="${CW_VERIFY_ADMIN:-}"
|
||||
if [[ -z "$ADMIN" && -n "${PRIVATE_KEY:-}" ]]; then
|
||||
ADMIN="$(cast wallet address --private-key "$PRIVATE_KEY")"
|
||||
fi
|
||||
[[ -n "$ADMIN" ]] || { echo "CW_VERIFY_ADMIN or PRIVATE_KEY required" >&2; exit 1; }
|
||||
|
||||
CWBNB="${CWBNB_BSC:-0x179034a08ac2c9c35d2e41239f68c79dca6f18fa}"
|
||||
CONTRACT_PATH="contracts/tokens/CompliantWrappedToken.sol:CompliantWrappedToken"
|
||||
CTOR="$(cast abi-encode 'constructor(string,string,uint8,address)' \
|
||||
'Wrapped BNB Gas (Compliant)' 'cWBNB' 18 "$ADMIN")"
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
echo "==> Pinned profile: bsc_tokens_verify (see foundry.toml + config/bsc-cw-verify-profile.v1.json)"
|
||||
echo "==> Scoped build: scripts/forge/scope.sh build tokens"
|
||||
export FOUNDRY_PROFILE=bsc_tokens_verify
|
||||
export FOUNDRY_SRC="contracts/tokens,contracts/interfaces"
|
||||
export FOUNDRY_OUT="out/scopes/tokens"
|
||||
export FOUNDRY_CACHE_PATH="cache/scopes/tokens"
|
||||
bash "$PROJECT_ROOT/scripts/forge/scope.sh" build tokens
|
||||
|
||||
echo "==> Export Standard JSON Input (upload at https://bscscan.com/verifyContract solidity-standard-json-input)"
|
||||
JSON_OUT="$OUT_DIR/CompliantWrappedToken_cWBNB_standard_input.json"
|
||||
# forge may print ERROR log lines before the JSON object on stdout — keep only the JSON line
|
||||
forge verify-contract \
|
||||
--chain bsc \
|
||||
--num-of-optimizations 200 \
|
||||
--via-ir \
|
||||
--evm-version london \
|
||||
--compiler-version "0.8.20+commit.a1b79de6" \
|
||||
--constructor-args "$CTOR" \
|
||||
--show-standard-json-input \
|
||||
"$CWBNB" \
|
||||
"$CONTRACT_PATH" 2>/dev/null | awk '/^\{/{buf=$0} END{if(buf) print buf}' | jq -c . > "$JSON_OUT"
|
||||
[[ -s "$JSON_OUT" ]] || { echo "Failed to write Standard JSON (forge output empty?)" >&2; exit 5; }
|
||||
echo " wrote $JSON_OUT"
|
||||
|
||||
echo "==> Export flattened source (reference only; prefer Standard-JSON for via-ir)"
|
||||
forge flatten contracts/tokens/CompliantWrappedToken.sol > "$OUT_DIR/CompliantWrappedToken_cWBNB_flattened.sol" 2>/dev/null
|
||||
echo " wrote $OUT_DIR/CompliantWrappedToken_cWBNB_flattened.sol"
|
||||
|
||||
RPC="${BSC_RPC_URL:-${BSC_MAINNET_RPC:-}}"
|
||||
if [[ -n "$RPC" ]]; then
|
||||
ONCHAIN="$(cast code "$CWBNB" --rpc-url "$RPC" | sed 's/^0x//' | tr '[:upper:]' '[:lower:]')"
|
||||
BUILT="$(jq -r .deployedBytecode.object "$FOUNDRY_OUT/CompliantWrappedToken.sol/CompliantWrappedToken.json" | sed 's/^0x//' | tr '[:upper:]' '[:lower:]')"
|
||||
MATCH=false
|
||||
[[ "$ONCHAIN" == "$BUILT" ]] && MATCH=true
|
||||
FIRST_DIFF_OFFSET=""
|
||||
if [[ "$MATCH" == false ]]; then
|
||||
FIRST_DIFF_OFFSET="$(python3 -c "
|
||||
on, b = '''$ONCHAIN''', '''$BUILT'''
|
||||
n = min(len(on), len(b)) // 2
|
||||
for i in range(n):
|
||||
if on[2*i:2*i+2] != b[2*i:2*i+2]:
|
||||
print(i)
|
||||
break
|
||||
else:
|
||||
print(n if len(on)//2 != len(b)//2 else '')
|
||||
" 2>/dev/null || true)"
|
||||
fi
|
||||
jq -n \
|
||||
--arg generatedAt "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
--arg address "$CWBNB" \
|
||||
--argjson onChainRuntimeBytes $(( ${#ONCHAIN} / 2 )) \
|
||||
--argjson pinnedBuildRuntimeBytes $(( ${#BUILT} / 2 )) \
|
||||
--argjson runtimeBytecodeMatch "$([[ "$MATCH" == true ]] && echo true || echo false)" \
|
||||
--arg firstRuntimeDiffOffset "${FIRST_DIFF_OFFSET:-null}" \
|
||||
--arg constructorArgs "$CTOR" \
|
||||
--arg foundryProfile "bsc_tokens_verify" \
|
||||
--arg standardJson "$JSON_OUT" \
|
||||
'{
|
||||
generatedAt: $generatedAt,
|
||||
address: $address,
|
||||
onChainRuntimeBytes: $onChainRuntimeBytes,
|
||||
pinnedBuildRuntimeBytes: $pinnedBuildRuntimeBytes,
|
||||
runtimeBytecodeMatch: $runtimeBytecodeMatch,
|
||||
firstRuntimeDiffOffset: (if $firstRuntimeDiffOffset == "" or $firstRuntimeDiffOffset == "null" then null else ($firstRuntimeDiffOffset|tonumber) end),
|
||||
constructorArgs: $constructorArgs,
|
||||
foundryProfile: $foundryProfile,
|
||||
standardJson: $standardJson
|
||||
}' > "$OUT_DIR/cWBNB_bytecode_compare.json"
|
||||
echo " wrote $OUT_DIR/cWBNB_bytecode_compare.json"
|
||||
fi
|
||||
|
||||
cat > "$OUT_DIR/README.md" <<EOF
|
||||
# BSC cWBNB verification artifacts
|
||||
|
||||
Generated by \`smom-dbis-138/scripts/deployment/export-bsc-cw-verification-artifacts.sh\`.
|
||||
|
||||
## Pinned compiler (matches verified BSC cWUSDT)
|
||||
|
||||
| Setting | Value |
|
||||
|---------|--------|
|
||||
| Profile | \`bsc_tokens_verify\` in \`foundry.toml\` |
|
||||
| solc | \`0.8.20+commit.a1b79de6\` |
|
||||
| Optimizer | enabled, **200** runs |
|
||||
| via-ir | **true** (required — stack too deep without) |
|
||||
| EVM | **london** (not cancun) |
|
||||
| Scope | \`FOUNDRY_SRC=contracts/tokens,contracts/interfaces\`, \`FOUNDRY_OUT=out/scopes/tokens\` |
|
||||
|
||||
## Manual BscScan steps
|
||||
|
||||
1. Open https://bscscan.com/verifyContract
|
||||
2. Contract address: \`$CWBNB\`
|
||||
3. Compiler type: **Solidity (Standard-Json-Input)**
|
||||
4. Upload \`CompliantWrappedToken_cWBNB_standard_input.json\`
|
||||
5. Constructor arguments ABI-encoded (hex): see \`cWBNB_bytecode_compare.json\` or run:
|
||||
|
||||
\`\`\`bash
|
||||
cast abi-encode 'constructor(string,string,uint8,address)' 'Wrapped BNB Gas (Compliant)' 'cWBNB' 18 <ADMIN>
|
||||
\`\`\`
|
||||
|
||||
## If verification still fails
|
||||
|
||||
On-chain runtime bytecode is **~45 bytes longer** than the current pinned scoped build (see \`cWBNB_bytecode_compare.json\`). That indicates deploy used a slightly different artifact (solc patch, source revision, or metadata), not just constructor naming. Options:
|
||||
|
||||
- Locate the deploy transaction and pin the exact \`solc\` commit from that date.
|
||||
- Compare \`git log contracts/tokens/CompliantWrappedToken.sol\` at deploy block.
|
||||
- Redeploy cWBNB only if a new address is acceptable.
|
||||
|
||||
## Automated verify (may fail until bytecode pin found)
|
||||
|
||||
\`\`\`bash
|
||||
cd smom-dbis-138
|
||||
FOUNDRY_PROFILE=bsc_tokens_verify \\
|
||||
FOUNDRY_SRC=contracts/tokens FOUNDRY_OUT=out/scopes/tokens \\
|
||||
forge verify-contract --chain bsc --num-of-optimizations 200 --via-ir --evm-version london \\
|
||||
--compiler-version 0.8.20+commit.a1b79de6 \\
|
||||
--etherscan-api-key "\$ETHERSCAN_API_KEY" \\
|
||||
--constructor-args "\$CTOR" \\
|
||||
$CWBNB $CONTRACT_PATH
|
||||
\`\`\`
|
||||
EOF
|
||||
echo " wrote $OUT_DIR/README.md"
|
||||
|
||||
if [[ "$VERIFY" -eq 1 ]]; then
|
||||
echo "==> forge verify-contract (automated)"
|
||||
set +e
|
||||
forge verify-contract \
|
||||
--chain bsc \
|
||||
--num-of-optimizations 200 \
|
||||
--via-ir \
|
||||
--evm-version london \
|
||||
--compiler-version "0.8.20+commit.a1b79de6" \
|
||||
--etherscan-api-key "${ETHERSCAN_API_KEY}" \
|
||||
--constructor-args "$CTOR" \
|
||||
--watch --rpc-timeout 120 \
|
||||
"$CWBNB" \
|
||||
"$CONTRACT_PATH"
|
||||
set -e
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Done. Artifacts: $OUT_DIR/"
|
||||
@@ -33,8 +33,8 @@ fi
|
||||
echo ""
|
||||
echo "--- Building Admin Dashboard ---"
|
||||
|
||||
if [ -d "../dbis_core/frontend" ] && [ -f "../dbis_core/frontend/package.json" ]; then
|
||||
cd ../dbis_core/frontend
|
||||
if [ -d "/home/intlc/projects/dbis_core/frontend" ] && [ -f "/home/intlc/projects/dbis_core/frontend/package.json" ]; then
|
||||
cd /home/intlc/projects/dbis_core/frontend
|
||||
echo "Installing dependencies..."
|
||||
npm install
|
||||
echo "Building..."
|
||||
|
||||
@@ -22,6 +22,7 @@ declare -A ROOT_SCRIPT_SCOPE_ALIASES=(
|
||||
["DeployCompliantUSDT.s.sol"]="tokens"
|
||||
["DeployCWAssetReserveVerifier.s.sol"]="bridge/integration"
|
||||
["DeployCWReserveVerifier.s.sol"]="bridge/integration"
|
||||
["DeployCWReserveSettlementStack.s.sol"]="cw-settlement"
|
||||
["DeployFeeCollector.s.sol"]="utils"
|
||||
["DeployGenericStateChannelManager.s.sol"]="channels"
|
||||
["DeployMainnetTether.s.sol"]="tether"
|
||||
@@ -40,6 +41,10 @@ declare -A ROOT_SCRIPT_SCOPE_ALIASES=(
|
||||
["DeployRWATokenFactory138.s.sol"]="rwa"
|
||||
["DeployM00DiamondHub138.s.sol"]="m00-diamond"
|
||||
["UpgradeM00DiamondAcl138.s.sol"]="m00-diamond"
|
||||
["UpgradeM00DiamondHubComplete138.s.sol"]="m00-diamond"
|
||||
["SeedM00LiIndexWeights138.s.sol"]="m00-diamond"
|
||||
["WireChain138OraclePegs138.s.sol"]="oracle"
|
||||
["GrantUarRegistrarRWA138.s.sol"]="rwa"
|
||||
["FundBridgeLinkViaCcip138.s.sol"]="ccip"
|
||||
["FundBridgeLinkViaCcipMainnet.s.sol"]="ccip"
|
||||
["DeployCCIPRelayBridgeLINK.s.sol"]="relay"
|
||||
@@ -240,9 +245,13 @@ prepare_scope_env() {
|
||||
|
||||
if [[ "$scope" == "rwa" ]]; then
|
||||
export FOUNDRY_SRC="contracts/rwa,contracts/compliance"
|
||||
export FOUNDRY_PROFILE="${FOUNDRY_PROFILE:-chain138}"
|
||||
elif [[ "$scope" == "m00-diamond" ]]; then
|
||||
export FOUNDRY_SRC="contracts/m00-diamond,contracts/rwa"
|
||||
export FOUNDRY_PROFILE="${FOUNDRY_PROFILE:-m00-diamond}"
|
||||
elif [[ "$scope" == "oracle" ]]; then
|
||||
export FOUNDRY_SRC="$src_dir"
|
||||
export FOUNDRY_PROFILE="${FOUNDRY_PROFILE:-chain138}"
|
||||
else
|
||||
export FOUNDRY_SRC="$src_dir"
|
||||
fi
|
||||
@@ -261,6 +270,8 @@ prepare_scope_env() {
|
||||
local script_dir="script/$scope"
|
||||
if [[ -d "$REPO_ROOT/$script_dir" ]]; then
|
||||
export FOUNDRY_SCRIPT="$script_dir"
|
||||
elif [[ "$scope" == "oracle" && -d "$REPO_ROOT/script/reserve" ]]; then
|
||||
export FOUNDRY_SCRIPT="script/reserve"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/bash
|
||||
# Complete Keeper Deployment Script
|
||||
# Deploys all keeper components and sets up monitoring
|
||||
|
||||
@@ -8,10 +8,23 @@ set -e
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
DRY_RUN="${DRY_RUN:-0}"
|
||||
EXPECTED_KEEPER="0xD3AD6831aacB5386B8A25BB8D8176a6C8a026f04"
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Load environment variables
|
||||
if [ -f .env ]; then
|
||||
# shellcheck disable=SC1091
|
||||
source .env
|
||||
fi
|
||||
|
||||
@@ -20,15 +33,94 @@ RPC_URL="${RPC_URL_138:-https://rpc.d-bis.org}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY}"
|
||||
KEEPER_ADDRESS="${PRICE_FEED_KEEPER_ADDRESS}"
|
||||
|
||||
echo -e "${GREEN}=== Complete Keeper Deployment ===${NC}\n"
|
||||
normalize_addr() {
|
||||
echo "$1" | tr '[:upper:]' '[:lower:]'
|
||||
}
|
||||
|
||||
# Step 1: Deploy PriceFeedKeeper
|
||||
echo -e "${YELLOW}Step 1: Deploying PriceFeedKeeper...${NC}"
|
||||
if [ -z "$KEEPER_ADDRESS" ]; then
|
||||
forge script script/reserve/DeployKeeper.s.sol:DeployKeeper \
|
||||
validate_keeper_env() {
|
||||
if [ -z "$KEEPER_ADDRESS" ]; then
|
||||
echo -e "${RED}ERROR: PRICE_FEED_KEEPER_ADDRESS is not set in .env${NC}"
|
||||
echo -e "${YELLOW}Expected: PRICE_FEED_KEEPER_ADDRESS=${EXPECTED_KEEPER}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(normalize_addr "$KEEPER_ADDRESS")" != "$(normalize_addr "$EXPECTED_KEEPER")" ]; then
|
||||
echo -e "${RED}ERROR: PRICE_FEED_KEEPER_ADDRESS mismatch${NC}"
|
||||
echo -e " .env: ${KEEPER_ADDRESS}"
|
||||
echo -e " expected: ${EXPECTED_KEEPER}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
dry_run_msg() {
|
||||
echo -e "${CYAN}[DRY RUN]${NC} $*"
|
||||
}
|
||||
|
||||
run_forge_broadcast() {
|
||||
local script_path="$1"
|
||||
forge script "$script_path" \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--broadcast \
|
||||
--verify
|
||||
}
|
||||
|
||||
print_forge_would_run() {
|
||||
local label="$1"
|
||||
local script_path="$2"
|
||||
dry_run_msg "$label"
|
||||
echo " forge script $script_path --rpc-url \"$RPC_URL\" --broadcast --verify"
|
||||
}
|
||||
|
||||
if [ "$DRY_RUN" = "1" ]; then
|
||||
echo -e "${CYAN}=== Complete Keeper Deployment (DRY RUN) ===${NC}\n"
|
||||
validate_keeper_env
|
||||
echo -e "${GREEN}PRICE_FEED_KEEPER_ADDRESS (.env):${NC} $KEEPER_ADDRESS"
|
||||
echo -e "${GREEN}RPC_URL:${NC} $RPC_URL"
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}Step 1: PriceFeedKeeper${NC}"
|
||||
dry_run_msg "Skip deploy; keeper already configured at $KEEPER_ADDRESS"
|
||||
echo ""
|
||||
|
||||
if [ "$DEPLOY_CHAINLINK" = "true" ]; then
|
||||
echo -e "${YELLOW}Step 2: ChainlinkKeeperCompatible (DEPLOY_CHAINLINK=true)${NC}"
|
||||
print_forge_would_run "Would deploy ChainlinkKeeperCompatible" \
|
||||
"script/reserve/DeployChainlinkKeeper.s.sol:DeployChainlinkKeeper"
|
||||
dry_run_msg "Would append CHAINLINK_KEEPER_COMPATIBLE_ADDRESS to .env (skipped)"
|
||||
dry_run_msg "Follow-up: node scripts/reserve/chainlink-keeper-setup.js"
|
||||
echo ""
|
||||
else
|
||||
echo -e "${YELLOW}Step 2: ChainlinkKeeperCompatible${NC}"
|
||||
dry_run_msg "Skipped (set DEPLOY_CHAINLINK=true to include)"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ "$DEPLOY_GELATO" = "true" ]; then
|
||||
echo -e "${YELLOW}Step 3: GelatoKeeperCompatible (DEPLOY_GELATO=true)${NC}"
|
||||
print_forge_would_run "Would deploy GelatoKeeperCompatible" \
|
||||
"script/reserve/DeployGelatoKeeper.s.sol:DeployGelatoKeeper"
|
||||
dry_run_msg "Would append GELATO_KEEPER_COMPATIBLE_ADDRESS to .env (skipped)"
|
||||
dry_run_msg "Follow-up: node scripts/reserve/gelato-keeper-setup.js"
|
||||
echo ""
|
||||
else
|
||||
echo -e "${YELLOW}Step 3: GelatoKeeperCompatible${NC}"
|
||||
dry_run_msg "Skipped (set DEPLOY_GELATO=true to include)"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}Step 4: Verify deployment${NC}"
|
||||
dry_run_msg "Would run read-only upkeep check (no --broadcast):"
|
||||
echo " forge script script/reserve/CheckUpkeep.s.sol:CheckUpkeep --rpc-url \"$RPC_URL\""
|
||||
echo ""
|
||||
echo -e "${GREEN}=== Dry Run Complete (no transactions broadcast, .env unchanged) ===${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}=== Complete Keeper Deployment ===${NC}\n"
|
||||
|
||||
# Step 1: Deploy PriceFeedKeepe
|
||||
echo -e "${YELLOW}Step 1: Deploying PriceFeedKeeper...${NC}"
|
||||
if [ -z "$KEEPER_ADDRESS" ]; then
|
||||
run_forge_broadcast "script/reserve/DeployKeeper.s.sol:DeployKeeper"
|
||||
|
||||
# Extract keeper address from output
|
||||
KEEPER_ADDRESS=$(forge script script/reserve/DeployKeeper.s.sol:DeployKeeper \
|
||||
@@ -43,10 +135,7 @@ fi
|
||||
# Step 2: Deploy Chainlink Keeper Compatible (optional)
|
||||
if [ "$DEPLOY_CHAINLINK" = "true" ]; then
|
||||
echo -e "${YELLOW}Step 2: Deploying ChainlinkKeeperCompatible...${NC}"
|
||||
forge script script/reserve/DeployChainlinkKeeper.s.sol:DeployChainlinkKeeper \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--broadcast \
|
||||
--verify
|
||||
run_forge_broadcast "script/reserve/DeployChainlinkKeeper.s.sol:DeployChainlinkKeeper"
|
||||
|
||||
CHAINLINK_KEEPER=$(forge script script/reserve/DeployChainlinkKeeper.s.sol:DeployChainlinkKeeper \
|
||||
--rpc-url "$RPC_URL" 2>&1 | grep "ChainlinkKeeperCompatible deployed at:" | awk '{print $NF}')
|
||||
@@ -62,10 +151,7 @@ fi
|
||||
# Step 3: Deploy Gelato Keeper Compatible (optional)
|
||||
if [ "$DEPLOY_GELATO" = "true" ]; then
|
||||
echo -e "${YELLOW}Step 3: Deploying GelatoKeeperCompatible...${NC}"
|
||||
forge script script/reserve/DeployGelatoKeeper.s.sol:DeployGelatoKeeper \
|
||||
--rpc-url "$RPC_URL" \
|
||||
--broadcast \
|
||||
--verify
|
||||
run_forge_broadcast "script/reserve/DeployGelatoKeeper.s.sol:DeployGelatoKeeper"
|
||||
|
||||
GELATO_KEEPER=$(forge script script/reserve/DeployGelatoKeeper.s.sol:DeployGelatoKeeper \
|
||||
--rpc-url "$RPC_URL" 2>&1 | grep "GelatoKeeperCompatible deployed at:" | awk '{print $NF}')
|
||||
@@ -99,5 +185,4 @@ echo " docker-compose -f docker/docker-compose.keeper.yml up -d"
|
||||
echo ""
|
||||
echo "4. Or use systemd:"
|
||||
echo " sudo systemctl enable price-feed-keeper"
|
||||
echo " sudo systemctl start price-feed-keeper"
|
||||
|
||||
echo " sudo systemctl start price-feed-keeper"
|
||||
|
||||
@@ -4,7 +4,9 @@ pragma solidity ^0.8.19;
|
||||
import {Test} from "forge-std/Test.sol";
|
||||
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
import {IRouterClient} from "../../contracts/ccip/IRouterClient.sol";
|
||||
import {IRouterClient} from "@chainlink/contracts-ccip/contracts/interfaces/IRouterClient.sol";
|
||||
import {Client} from "@chainlink/contracts-ccip/contracts/libraries/Client.sol";
|
||||
import {IRouterClient as LegacyRouterClient} from "../../contracts/ccip/IRouterClient.sol";
|
||||
import {CWMultiTokenBridgeL1} from "../../contracts/bridge/CWMultiTokenBridgeL1.sol";
|
||||
import {CWMultiTokenBridgeL2} from "../../contracts/bridge/CWMultiTokenBridgeL2.sol";
|
||||
import {CompliantWrappedToken} from "../../contracts/tokens/CompliantWrappedToken.sol";
|
||||
@@ -21,7 +23,7 @@ contract MockCanonicalToken is ERC20 {
|
||||
contract MockRouter is IRouterClient {
|
||||
uint256 public fee;
|
||||
bytes32 public nextMessageId = keccak256("message");
|
||||
EVM2AnyMessage internal _lastMessage;
|
||||
Client.EVM2AnyMessage internal _lastMessage;
|
||||
uint64 public lastDestinationChainSelector;
|
||||
|
||||
function setFee(uint256 newFee) external {
|
||||
@@ -30,31 +32,19 @@ contract MockRouter is IRouterClient {
|
||||
|
||||
function ccipSend(
|
||||
uint64 destinationChainSelector,
|
||||
EVM2AnyMessage memory message
|
||||
) external payable returns (bytes32 messageId, uint256 fees) {
|
||||
fees = fee;
|
||||
Client.EVM2AnyMessage memory message
|
||||
) external payable returns (bytes32 messageId) {
|
||||
if (message.feeToken == address(0)) {
|
||||
require(msg.value >= fees, "native fee");
|
||||
require(msg.value >= fee, "native fee");
|
||||
}
|
||||
|
||||
lastDestinationChainSelector = destinationChainSelector;
|
||||
_lastMessage = message;
|
||||
|
||||
emit MessageSent(
|
||||
nextMessageId,
|
||||
destinationChainSelector,
|
||||
msg.sender,
|
||||
message.receiver,
|
||||
message.data,
|
||||
message.tokenAmounts,
|
||||
message.feeToken,
|
||||
message.extraArgs
|
||||
);
|
||||
|
||||
return (nextMessageId, fees);
|
||||
messageId = nextMessageId;
|
||||
return messageId;
|
||||
}
|
||||
|
||||
function getFee(uint64, EVM2AnyMessage memory) external view returns (uint256) {
|
||||
function getFee(uint64, Client.EVM2AnyMessage memory) external view returns (uint256) {
|
||||
return fee;
|
||||
}
|
||||
|
||||
@@ -145,7 +135,9 @@ contract CWMultiTokenBridgeTest is Test {
|
||||
assertEq(l2Bridge.mintedTotal(address(wrapped)), amount);
|
||||
assertEq(l2Bridge.burnedTotal(address(wrapped)), amount);
|
||||
|
||||
(, bytes memory returnData,,) = routerAvax.lastMessage();
|
||||
(, bytes memory returnData, address feeToken, bytes memory extraArgs) = routerAvax.lastMessage();
|
||||
assertTrue(extraArgs.length > 0, "missing CCIP extraArgs");
|
||||
assertEq(feeToken, address(0));
|
||||
vm.prank(address(0x138138));
|
||||
l1Bridge.ccipReceive(_message(returnMessageId, AVALANCHE_SELECTOR, address(l2Bridge), returnData));
|
||||
|
||||
@@ -308,8 +300,8 @@ contract CWMultiTokenBridgeTest is Test {
|
||||
relayRouter.authorizeBridge(address(receiveBridge));
|
||||
relayRouter.grantRelayerRole(address(this));
|
||||
|
||||
IRouterClient.TokenAmount[] memory noTokens = new IRouterClient.TokenAmount[](0);
|
||||
IRouterClient.Any2EVMMessage memory message = IRouterClient.Any2EVMMessage({
|
||||
LegacyRouterClient.TokenAmount[] memory noTokens = new LegacyRouterClient.TokenAmount[](0);
|
||||
LegacyRouterClient.Any2EVMMessage memory message = LegacyRouterClient.Any2EVMMessage({
|
||||
messageId: keccak256("three-field"),
|
||||
sourceChainSelector: CHAIN138_SELECTOR,
|
||||
sender: abi.encode(address(0xCAFE)),
|
||||
@@ -326,14 +318,13 @@ contract CWMultiTokenBridgeTest is Test {
|
||||
uint64 sourceChainSelector,
|
||||
address sender,
|
||||
bytes memory data
|
||||
) internal pure returns (IRouterClient.Any2EVMMessage memory message) {
|
||||
IRouterClient.TokenAmount[] memory noTokens = new IRouterClient.TokenAmount[](0);
|
||||
message = IRouterClient.Any2EVMMessage({
|
||||
) internal pure returns (Client.Any2EVMMessage memory message) {
|
||||
message = Client.Any2EVMMessage({
|
||||
messageId: messageId,
|
||||
sourceChainSelector: sourceChainSelector,
|
||||
sender: abi.encode(sender),
|
||||
data: data,
|
||||
tokenAmounts: noTokens
|
||||
destTokenAmounts: new Client.EVMTokenAmount[](0)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ pragma solidity ^0.8.20;
|
||||
import {Test} from "forge-std/Test.sol";
|
||||
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
import {IRouterClient} from "../../contracts/ccip/IRouterClient.sol";
|
||||
import {IRouterClient} from "@chainlink/contracts-ccip/contracts/interfaces/IRouterClient.sol";
|
||||
import {Client} from "@chainlink/contracts-ccip/contracts/libraries/Client.sol";
|
||||
import {CWMultiTokenBridgeL1} from "../../contracts/bridge/CWMultiTokenBridgeL1.sol";
|
||||
import {CWMultiTokenBridgeL2} from "../../contracts/bridge/CWMultiTokenBridgeL2.sol";
|
||||
import {CompliantWrappedToken} from "../../contracts/tokens/CompliantWrappedToken.sol";
|
||||
@@ -23,18 +24,18 @@ contract MockCanonicalBTC is ERC20 {
|
||||
|
||||
contract MockRouterBTC is IRouterClient {
|
||||
bytes32 public nextMessageId = keccak256("btc-message");
|
||||
EVM2AnyMessage internal _lastMessage;
|
||||
Client.EVM2AnyMessage internal _lastMessage;
|
||||
|
||||
function ccipSend(
|
||||
uint64 destinationChainSelector,
|
||||
EVM2AnyMessage memory message
|
||||
) external payable returns (bytes32 messageId, uint256 fees) {
|
||||
Client.EVM2AnyMessage memory message
|
||||
) external payable returns (bytes32 messageId) {
|
||||
destinationChainSelector;
|
||||
_lastMessage = message;
|
||||
return (nextMessageId, fees);
|
||||
return nextMessageId;
|
||||
}
|
||||
|
||||
function getFee(uint64, EVM2AnyMessage memory) external pure returns (uint256) {
|
||||
function getFee(uint64, Client.EVM2AnyMessage memory) external pure returns (uint256) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -122,14 +123,13 @@ contract CWMultiTokenBridgeBTCTest is Test {
|
||||
uint64 sourceChainSelector,
|
||||
address sender,
|
||||
bytes memory data
|
||||
) internal pure returns (IRouterClient.Any2EVMMessage memory message) {
|
||||
IRouterClient.TokenAmount[] memory noTokens = new IRouterClient.TokenAmount[](0);
|
||||
message = IRouterClient.Any2EVMMessage({
|
||||
) internal pure returns (Client.Any2EVMMessage memory message) {
|
||||
message = Client.Any2EVMMessage({
|
||||
messageId: messageId,
|
||||
sourceChainSelector: sourceChainSelector,
|
||||
sender: abi.encode(sender),
|
||||
data: data,
|
||||
tokenAmounts: noTokens
|
||||
destTokenAmounts: new Client.EVMTokenAmount[](0)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ pragma solidity ^0.8.20;
|
||||
|
||||
import {Test} from "forge-std/Test.sol";
|
||||
|
||||
import {IRouterClient} from "../../contracts/ccip/IRouterClient.sol";
|
||||
import {IRouterClient} from "@chainlink/contracts-ccip/contracts/interfaces/IRouterClient.sol";
|
||||
import {Client} from "@chainlink/contracts-ccip/contracts/libraries/Client.sol";
|
||||
import {OfficialStableMirrorToken} from "../../contracts/tokens/OfficialStableMirrorToken.sol";
|
||||
import {CompliantUSDCTokenV2} from "../../contracts/tokens/CompliantUSDCTokenV2.sol";
|
||||
import {CompliantUSDTTokenV2} from "../../contracts/tokens/CompliantUSDTTokenV2.sol";
|
||||
@@ -16,19 +17,19 @@ import {CompliantWrappedToken} from "../../contracts/tokens/CompliantWrappedToke
|
||||
contract MockRouterVaultVerifierV2 is IRouterClient {
|
||||
uint256 public fee;
|
||||
bytes32 public nextMessageId = keccak256("cw-reserve-vault-v2-message");
|
||||
EVM2AnyMessage internal _lastMessage;
|
||||
Client.EVM2AnyMessage internal _lastMessage;
|
||||
uint64 public lastDestinationChainSelector;
|
||||
|
||||
function ccipSend(
|
||||
uint64 destinationChainSelector,
|
||||
EVM2AnyMessage memory message
|
||||
) external payable returns (bytes32 messageId, uint256 fees) {
|
||||
Client.EVM2AnyMessage memory message
|
||||
) external payable returns (bytes32 messageId) {
|
||||
lastDestinationChainSelector = destinationChainSelector;
|
||||
_lastMessage = message;
|
||||
return (nextMessageId, fee);
|
||||
return nextMessageId;
|
||||
}
|
||||
|
||||
function getFee(uint64, EVM2AnyMessage memory) external view returns (uint256) {
|
||||
function getFee(uint64, Client.EVM2AnyMessage memory) external view returns (uint256) {
|
||||
return fee;
|
||||
}
|
||||
|
||||
@@ -182,14 +183,13 @@ contract CWReserveVerifierVaultV2IntegrationTest is Test {
|
||||
uint64 sourceChainSelector,
|
||||
address sender,
|
||||
bytes memory data
|
||||
) internal pure returns (IRouterClient.Any2EVMMessage memory message) {
|
||||
IRouterClient.TokenAmount[] memory noTokens = new IRouterClient.TokenAmount[](0);
|
||||
message = IRouterClient.Any2EVMMessage({
|
||||
) internal pure returns (Client.Any2EVMMessage memory message) {
|
||||
message = Client.Any2EVMMessage({
|
||||
messageId: messageId,
|
||||
sourceChainSelector: sourceChainSelector,
|
||||
sender: abi.encode(sender),
|
||||
data: data,
|
||||
tokenAmounts: noTokens
|
||||
destTokenAmounts: new Client.EVMTokenAmount[](0)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
28
test/compliance/MonetaryFormulas.t.sol
Normal file
28
test/compliance/MonetaryFormulas.t.sol
Normal file
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "../../contracts/compliance/libraries/MonetaryFormulas.sol";
|
||||
|
||||
contract MonetaryFormulasTest is Test {
|
||||
function test_MoneySupplyCD() public pure {
|
||||
assertEq(MonetaryFormulas.moneySupplyCD(100, 50), 150);
|
||||
}
|
||||
|
||||
function test_SimpleMultiplier() public pure {
|
||||
assertEq(MonetaryFormulas.simpleMultiplier(1000), 10e18);
|
||||
}
|
||||
|
||||
function test_CoverageRatioBps() public pure {
|
||||
assertEq(MonetaryFormulas.coverageRatioBps(120, 100), 12000);
|
||||
}
|
||||
|
||||
function test_GruFanout() public pure {
|
||||
assertEq(MonetaryFormulas.gruM00ToM1Fanout(), 25);
|
||||
}
|
||||
|
||||
function test_CoverageWeightedVelocity() public pure {
|
||||
uint256 v = MonetaryFormulas.coverageWeightedVelocity(2e18, 12000);
|
||||
assertEq(v, 2e18);
|
||||
}
|
||||
}
|
||||
140
test/cw-settlement/CWReserveSettlementStack.t.sol
Normal file
140
test/cw-settlement/CWReserveSettlementStack.t.sol
Normal file
@@ -0,0 +1,140 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Test} from "forge-std/Test.sol";
|
||||
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
import {CWNavOracle} from "../../contracts/cw-settlement/CWNavOracle.sol";
|
||||
import {CWRedemptionQueue} from "../../contracts/cw-settlement/CWRedemptionQueue.sol";
|
||||
import {CWStabilityFund} from "../../contracts/cw-settlement/CWStabilityFund.sol";
|
||||
|
||||
contract MockNavBridge {
|
||||
mapping(address => uint256) internal _locked;
|
||||
mapping(address => uint256) internal _outstanding;
|
||||
|
||||
function setLocked(address token, uint256 amount) external {
|
||||
_locked[token] = amount;
|
||||
}
|
||||
|
||||
function lockedBalance(address token) external view returns (uint256) {
|
||||
return _locked[token];
|
||||
}
|
||||
|
||||
function totalOutstanding(address token) external view returns (uint256) {
|
||||
return _outstanding[token];
|
||||
}
|
||||
}
|
||||
|
||||
contract MockReserveSystem {
|
||||
mapping(address => uint256) internal _balances;
|
||||
|
||||
function setBalance(address asset, uint256 amount) external {
|
||||
_balances[asset] = amount;
|
||||
}
|
||||
|
||||
function getReserveBalance(address asset) external view returns (uint256) {
|
||||
return _balances[asset];
|
||||
}
|
||||
}
|
||||
|
||||
contract MockERC20Six is ERC20 {
|
||||
constructor() ERC20("Mock", "MOCK") {}
|
||||
|
||||
function mint(address to, uint256 amount) external {
|
||||
_mint(to, amount);
|
||||
}
|
||||
|
||||
function decimals() public pure override returns (uint8) {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
contract CWNavOracleTest is Test {
|
||||
MockNavBridge internal bridge;
|
||||
MockReserveSystem internal reserveSystem;
|
||||
CWNavOracle internal navOracle;
|
||||
MockERC20Six internal canonical;
|
||||
|
||||
function setUp() public {
|
||||
bridge = new MockNavBridge();
|
||||
reserveSystem = new MockReserveSystem();
|
||||
navOracle = new CWNavOracle(address(this), address(bridge), address(reserveSystem));
|
||||
canonical = new MockERC20Six();
|
||||
navOracle.configureToken(address(canonical), address(canonical));
|
||||
}
|
||||
|
||||
function test_publishNav_computesBackingAndNavPerShare() public {
|
||||
bridge.setLocked(address(canonical), 120 * 1e6);
|
||||
reserveSystem.setBalance(address(canonical), 30 * 1e6);
|
||||
|
||||
navOracle.publishNav(
|
||||
address(canonical),
|
||||
100 * 1e6,
|
||||
8200,
|
||||
1500,
|
||||
300,
|
||||
keccak256("attestation-v1")
|
||||
);
|
||||
|
||||
CWNavOracle.NavSnapshot memory snap = navOracle.getNavSnapshot(address(canonical));
|
||||
assertEq(snap.totalLockedAssets, 120 * 1e6);
|
||||
assertEq(snap.totalReserveAssets, 30 * 1e6);
|
||||
assertEq(snap.totalCwSupply, 100 * 1e6);
|
||||
assertEq(snap.backingRatioBps, 15000);
|
||||
assertEq(snap.navPerShare, 15e17);
|
||||
}
|
||||
}
|
||||
|
||||
contract CWRedemptionQueueTest is Test {
|
||||
CWRedemptionQueue internal queue;
|
||||
MockERC20Six internal token;
|
||||
|
||||
function setUp() public {
|
||||
queue = new CWRedemptionQueue(address(this));
|
||||
token = new MockERC20Six();
|
||||
queue.grantRole(queue.BRIDGE_ROLE(), address(this));
|
||||
}
|
||||
|
||||
function test_instantTier_claimableImmediately() public {
|
||||
token.mint(address(this), 1_000 * 1e6);
|
||||
token.approve(address(queue), 1_000 * 1e6);
|
||||
|
||||
uint256 requestId = queue.fundAndEnqueue(address(this), address(token), 1_000 * 1e6);
|
||||
queue.claim(requestId);
|
||||
assertEq(token.balanceOf(address(this)), 1_000 * 1e6);
|
||||
}
|
||||
|
||||
function test_standardTier_requiresDelay() public {
|
||||
uint256 requestId = queue.enqueueRedemption(address(this), address(token), 10_000 * 1e6);
|
||||
vm.expectRevert();
|
||||
queue.claim(requestId);
|
||||
vm.warp(block.timestamp + 24 hours);
|
||||
queue.processDueRequests(_arr(requestId));
|
||||
vm.expectRevert();
|
||||
queue.claim(requestId);
|
||||
}
|
||||
|
||||
function _arr(uint256 id) internal pure returns (uint256[] memory ids) {
|
||||
ids = new uint256[](1);
|
||||
ids[0] = id;
|
||||
}
|
||||
}
|
||||
|
||||
contract CWStabilityFundTest is Test {
|
||||
CWStabilityFund internal fund;
|
||||
MockERC20Six internal token;
|
||||
|
||||
function setUp() public {
|
||||
fund = new CWStabilityFund(address(this));
|
||||
token = new MockERC20Six();
|
||||
fund.setSupportedAsset(address(token), true);
|
||||
}
|
||||
|
||||
function test_depositAndEmergencyWithdraw() public {
|
||||
token.mint(address(this), 100e6);
|
||||
token.approve(address(fund), 100e6);
|
||||
fund.deposit(address(token), 100e6);
|
||||
fund.emergencyWithdraw(address(token), address(this), 50e6, keccak256("depeg"));
|
||||
assertEq(token.balanceOf(address(this)), 50e6);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Test} from "forge-std/Test.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {
|
||||
AaveUniV2CwStableRebalanceFlashReceiver
|
||||
} from "../../contracts/flash/AaveUniV2CwStableRebalanceFlashReceiver.sol";
|
||||
|
||||
contract AaveUniV2CwStableRebalanceFlashReceiverMainnetForkTest is Test {
|
||||
address constant AAVE_POOL = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2;
|
||||
address constant PAIR = 0xC28706F899266b36BC43cc072b3a921BDf2C48D9;
|
||||
address constant ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
|
||||
address constant CWUSDC = 0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a;
|
||||
address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
|
||||
address constant TOKEN_JAR = 0xf38521f130fcCF29dB1961597bc5d2B60F995f85;
|
||||
|
||||
bool internal forkAvailable;
|
||||
AaveUniV2CwStableRebalanceFlashReceiver internal receiver;
|
||||
|
||||
modifier skipIfNoFork() {
|
||||
if (!forkAvailable) return;
|
||||
_;
|
||||
}
|
||||
|
||||
function setUp() public {
|
||||
string memory rpcUrl = vm.envOr("ETHEREUM_MAINNET_RPC", string(""));
|
||||
if (bytes(rpcUrl).length == 0) {
|
||||
forkAvailable = false;
|
||||
return;
|
||||
}
|
||||
try vm.createSelectFork(rpcUrl) {
|
||||
forkAvailable = true;
|
||||
} catch {
|
||||
forkAvailable = false;
|
||||
return;
|
||||
}
|
||||
receiver = new AaveUniV2CwStableRebalanceFlashReceiver(AAVE_POOL, address(this));
|
||||
}
|
||||
|
||||
function testFork_flashRebalanceRemove_withTokenJarLp() public skipIfNoFork {
|
||||
uint256 lpBal = IERC20(PAIR).balanceOf(TOKEN_JAR);
|
||||
if (lpBal == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
(uint112 r0, uint112 r1,) = _reserves();
|
||||
uint256 baseRaw = address(IERC20(CWUSDC)) < address(IERC20(USDC)) ? r0 : r1;
|
||||
uint256 quoteRaw = address(IERC20(CWUSDC)) < address(IERC20(USDC)) ? r1 : r0;
|
||||
if (quoteRaw == 0 || baseRaw <= quoteRaw) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint256 flashIn = _quoteInToEqualize(baseRaw, quoteRaw);
|
||||
uint256 premium = (flashIn * 5 + 9999) / 10000;
|
||||
uint256 lpUse = lpBal;
|
||||
|
||||
vm.prank(TOKEN_JAR);
|
||||
IERC20(PAIR).transfer(address(receiver), lpUse);
|
||||
|
||||
uint256 minCwRebalance = 1;
|
||||
uint256 minCwRemove = 1;
|
||||
uint256 minStableRemove = 1;
|
||||
uint256 cwToSell = flashIn + premium + 50_000;
|
||||
uint256 minStableRepay = flashIn + premium;
|
||||
|
||||
address recipient = address(0xBEEF);
|
||||
receiver.runRebalanceRemove(
|
||||
USDC,
|
||||
flashIn,
|
||||
AaveUniV2CwStableRebalanceFlashReceiver.RebalanceRemoveParams({
|
||||
router: ROUTER,
|
||||
pair: PAIR,
|
||||
cwToken: CWUSDC,
|
||||
stableToken: USDC,
|
||||
lpAmount: lpUse,
|
||||
rebalanceStableIn: flashIn,
|
||||
minCwFromRebalance: minCwRebalance,
|
||||
minStableFromRemove: minStableRemove,
|
||||
minCwFromRemove: minCwRemove,
|
||||
cwToSellForRepay: cwToSell,
|
||||
minStableFromRepaySwap: minStableRepay,
|
||||
recipient: recipient
|
||||
})
|
||||
);
|
||||
|
||||
assertEq(IERC20(PAIR).balanceOf(address(receiver)), 0, "LP consumed");
|
||||
assertGt(IERC20(USDC).balanceOf(recipient) + IERC20(CWUSDC).balanceOf(recipient), 0, "recipient funded");
|
||||
}
|
||||
|
||||
function _reserves() internal view returns (uint112 r0, uint112 r1, uint32 ts) {
|
||||
(r0, r1, ts) = _getReserves(PAIR);
|
||||
}
|
||||
|
||||
function _getReserves(address pair) internal view returns (uint112, uint112, uint32) {
|
||||
(bool ok, bytes memory data) = pair.staticcall(abi.encodeWithSignature("getReserves()"));
|
||||
require(ok, "getReserves failed");
|
||||
return abi.decode(data, (uint112, uint112, uint32));
|
||||
}
|
||||
|
||||
function _quoteInToEqualize(uint256 baseRaw, uint256 quoteRaw) internal pure returns (uint256) {
|
||||
uint256 y = quoteRaw;
|
||||
uint256 x = baseRaw;
|
||||
// Integer approximation of closed-form quote-in (matches planner within rounding).
|
||||
uint256 xy = x * y;
|
||||
uint256 target = _isqrt(xy);
|
||||
if (target <= y) return y + 1;
|
||||
uint256 need = target - y;
|
||||
return (need * 1005) / 997 + 1;
|
||||
}
|
||||
|
||||
function _isqrt(uint256 n) internal pure returns (uint256) {
|
||||
if (n == 0) return 0;
|
||||
uint256 x = n;
|
||||
uint256 z = (x + 1) / 2;
|
||||
while (z < x) {
|
||||
x = z;
|
||||
z = (x + n / x) / 2;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
}
|
||||
72
test/rwa/GruMonetaryPolicyGate.t.sol
Normal file
72
test/rwa/GruMonetaryPolicyGate.t.sol
Normal file
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "../../contracts/rwa/RWAToken.sol";
|
||||
import "../../contracts/rwa/GruMonetaryPolicyGate.sol";
|
||||
import "../../contracts/rwa/IGruMonetaryPolicyGate.sol";
|
||||
|
||||
contract GruMonetaryPolicyGateTest is Test {
|
||||
GruMonetaryPolicyGate gate;
|
||||
RWAToken liToken;
|
||||
address admin = address(0xA11CE);
|
||||
|
||||
function setUp() public {
|
||||
vm.startPrank(admin);
|
||||
gate = new GruMonetaryPolicyGate(admin, 12000, 11800, 25);
|
||||
liToken = new RWAToken(
|
||||
"LiXAU Test",
|
||||
"LiXAU",
|
||||
6,
|
||||
"LiXAU",
|
||||
"Commodity",
|
||||
"Gold",
|
||||
"Index",
|
||||
"XAU",
|
||||
"M00",
|
||||
admin,
|
||||
admin,
|
||||
admin,
|
||||
1_000_000,
|
||||
0,
|
||||
keccak256("methodology-test")
|
||||
);
|
||||
gate.setTokenGate(address(liToken), true);
|
||||
liToken.setPolicyGate(address(gate));
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_AllowsMintWhenGreen() public {
|
||||
vm.prank(admin);
|
||||
gate.updateMetrics(15000, 10, IGruMonetaryPolicyGate.VelocityZone.Green, false);
|
||||
vm.prank(admin);
|
||||
liToken.mint(admin, 1000e6);
|
||||
assertEq(liToken.totalSupply(), 1000e6);
|
||||
}
|
||||
|
||||
function test_BlocksMintOnCoverageAlert() public {
|
||||
vm.prank(admin);
|
||||
gate.updateMetrics(11700, 10, IGruMonetaryPolicyGate.VelocityZone.Green, false);
|
||||
vm.prank(admin);
|
||||
vm.expectRevert("RWAToken: policy gate");
|
||||
liToken.mint(admin, 1000e6);
|
||||
}
|
||||
|
||||
function test_BlocksMintOnUtilization() public {
|
||||
vm.prank(admin);
|
||||
gate.updateMetrics(15000, 30, IGruMonetaryPolicyGate.VelocityZone.Green, false);
|
||||
vm.prank(admin);
|
||||
vm.expectRevert("RWAToken: policy gate");
|
||||
liToken.mint(admin, 1000e6);
|
||||
}
|
||||
|
||||
function test_SkipGateWhenTokenNotEnabled() public {
|
||||
vm.prank(admin);
|
||||
gate.setTokenGate(address(liToken), false);
|
||||
vm.prank(admin);
|
||||
gate.updateMetrics(10000, 99, IGruMonetaryPolicyGate.VelocityZone.Red, true);
|
||||
vm.prank(admin);
|
||||
liToken.mint(admin, 1000e6);
|
||||
assertEq(liToken.totalSupply(), 1000e6);
|
||||
}
|
||||
}
|
||||
84
test/rwa/LiIndexFlashVault.t.sol
Normal file
84
test/rwa/LiIndexFlashVault.t.sol
Normal file
@@ -0,0 +1,84 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "../../contracts/rwa/RWAToken.sol";
|
||||
import "../../contracts/rwa/LiIndexFlashVault.sol";
|
||||
import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
contract MockFlashBorrower is IERC3156FlashBorrower {
|
||||
bytes32 private constant _RETURN = keccak256("ERC3156FlashBorrower.onFlashLoan");
|
||||
|
||||
function onFlashLoan(
|
||||
address,
|
||||
address token,
|
||||
uint256 amount,
|
||||
uint256 fee,
|
||||
bytes calldata
|
||||
) external returns (bytes32) {
|
||||
IERC20(token).approve(msg.sender, amount + fee);
|
||||
return _RETURN;
|
||||
}
|
||||
}
|
||||
|
||||
contract LiIndexFlashVaultTest is Test {
|
||||
LiIndexFlashVault vault;
|
||||
RWAToken liToken;
|
||||
MockFlashBorrower borrower;
|
||||
address admin = address(0xA11CE);
|
||||
|
||||
function setUp() public {
|
||||
vm.startPrank(admin);
|
||||
liToken = new RWAToken(
|
||||
"LiXAU Test",
|
||||
"LiXAU",
|
||||
6,
|
||||
"LiXAU",
|
||||
"Commodity",
|
||||
"Gold",
|
||||
"Index",
|
||||
"XAU",
|
||||
"M00",
|
||||
admin,
|
||||
admin,
|
||||
admin,
|
||||
1_000_000,
|
||||
0,
|
||||
keccak256("methodology-test")
|
||||
);
|
||||
vault = new LiIndexFlashVault(
|
||||
admin,
|
||||
8000,
|
||||
5000,
|
||||
5,
|
||||
250,
|
||||
86400,
|
||||
2400_00000000
|
||||
);
|
||||
vault.setSupportedToken(address(liToken), true);
|
||||
borrower = new MockFlashBorrower();
|
||||
liToken.mint(admin, 1_000_000e6);
|
||||
liToken.approve(address(vault), type(uint256).max);
|
||||
vault.deposit(address(liToken), 500_000e6);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_MaxFlashLoanWithinLtv() public view {
|
||||
uint256 max = vault.maxFlashLoan(address(liToken));
|
||||
assertEq(max, 250_000e6);
|
||||
}
|
||||
|
||||
function test_FlashLoanRepays() public {
|
||||
vm.prank(admin);
|
||||
vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(liToken), 100_000e6, "");
|
||||
assertEq(vault.outstandingFlash(address(liToken)), 0);
|
||||
}
|
||||
|
||||
function test_BlocksWhenUtilizationExceeded() public {
|
||||
vm.startPrank(admin);
|
||||
vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(liToken), 400_000e6, "");
|
||||
vm.stopPrank();
|
||||
assertEq(vault.maxFlashLoan(address(liToken)), 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user