feat: bridges, PMM, flash workflow, token-aggregation, and deployment docs
- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
This commit is contained in:
27
script/DeployCCIPRelayRouterOnly.s.sol
Normal file
27
script/DeployCCIPRelayRouterOnly.s.sol
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {CCIPRelayRouter} from "../contracts/relay/CCIPRelayRouter.sol";
|
||||
|
||||
contract DeployCCIPRelayRouterOnly is Script {
|
||||
function run() external {
|
||||
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(deployerPrivateKey);
|
||||
address relayer = vm.envAddress("RELAYER_ADDRESS");
|
||||
|
||||
console.log("Deploying CCIPRelayRouter with deployer:", deployer);
|
||||
console.log("Relayer:", relayer);
|
||||
|
||||
vm.startBroadcast(deployerPrivateKey);
|
||||
|
||||
CCIPRelayRouter relayRouter = new CCIPRelayRouter();
|
||||
if (relayer != address(0)) {
|
||||
relayRouter.grantRelayerRole(relayer);
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("CCIPRelayRouter deployed at:", address(relayRouter));
|
||||
}
|
||||
}
|
||||
61
script/DeployCWAssetReserveVerifier.s.sol
Normal file
61
script/DeployCWAssetReserveVerifier.s.sol
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {CWAssetReserveVerifier} from "../contracts/bridge/integration/CWAssetReserveVerifier.sol";
|
||||
|
||||
interface ICWMultiTokenBridgeL1AdminV2 {
|
||||
function setReserveVerifier(address newVerifier) external;
|
||||
}
|
||||
|
||||
/**
|
||||
* @title DeployCWAssetReserveVerifier
|
||||
* @notice Deploy a generic reserve verifier for stable, monetary-unit, or gas-native c* lanes.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY (required)
|
||||
* CW_L1_BRIDGE or CHAIN138_L1_BRIDGE (required)
|
||||
* CW_ASSET_RESERVE_VAULT (optional)
|
||||
* CW_ASSET_RESERVE_SYSTEM (optional)
|
||||
* CW_ATTACH_VERIFIER_TO_L1=1 (optional, default 0)
|
||||
*/
|
||||
contract DeployCWAssetReserveVerifier is Script {
|
||||
function run() external {
|
||||
uint256 privateKey = vm.envUint("PRIVATE_KEY");
|
||||
|
||||
address l1Bridge = vm.envOr("CW_L1_BRIDGE", address(0));
|
||||
if (l1Bridge == address(0)) {
|
||||
l1Bridge = vm.envOr("CHAIN138_L1_BRIDGE", address(0));
|
||||
}
|
||||
if (l1Bridge == address(0)) {
|
||||
l1Bridge = vm.envOr("CW_L1_BRIDGE_CHAIN138", address(0));
|
||||
}
|
||||
|
||||
address assetReserveVault = vm.envOr("CW_ASSET_RESERVE_VAULT", address(0));
|
||||
address reserveSystem = vm.envOr("CW_ASSET_RESERVE_SYSTEM", address(0));
|
||||
bool attachVerifierToBridge = vm.envOr("CW_ATTACH_VERIFIER_TO_L1", uint256(0)) == 1;
|
||||
|
||||
require(l1Bridge != address(0), "CW_L1_BRIDGE or CHAIN138_L1_BRIDGE required");
|
||||
|
||||
vm.startBroadcast(privateKey);
|
||||
|
||||
CWAssetReserveVerifier verifier = new CWAssetReserveVerifier(
|
||||
vm.addr(privateKey),
|
||||
l1Bridge,
|
||||
assetReserveVault,
|
||||
reserveSystem
|
||||
);
|
||||
|
||||
if (attachVerifierToBridge) {
|
||||
ICWMultiTokenBridgeL1AdminV2(l1Bridge).setReserveVerifier(address(verifier));
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("CWAssetReserveVerifier:", address(verifier));
|
||||
console.log(" L1 bridge:", l1Bridge);
|
||||
console.log(" attached to bridge:", attachVerifierToBridge);
|
||||
console.log(" asset reserve vault:", assetReserveVault);
|
||||
console.log(" reserve system:", reserveSystem);
|
||||
}
|
||||
}
|
||||
20
script/DeployCWMultiTokenBridgeL1.s.sol
Normal file
20
script/DeployCWMultiTokenBridgeL1.s.sol
Normal file
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {CWMultiTokenBridgeL1} from "../contracts/bridge/CWMultiTokenBridgeL1.sol";
|
||||
|
||||
contract DeployCWMultiTokenBridgeL1 is Script {
|
||||
function run() external {
|
||||
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
||||
address sendRouter = vm.envAddress("CW_SEND_ROUTER");
|
||||
address receiveRouter = vm.envAddress("CW_RECEIVE_ROUTER");
|
||||
address feeToken = vm.envOr("CW_FEE_TOKEN", address(0));
|
||||
|
||||
vm.startBroadcast(deployerPrivateKey);
|
||||
CWMultiTokenBridgeL1 bridge = new CWMultiTokenBridgeL1(sendRouter, receiveRouter, feeToken);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("CWMultiTokenBridgeL1:", address(bridge));
|
||||
}
|
||||
}
|
||||
20
script/DeployCWMultiTokenBridgeL2.s.sol
Normal file
20
script/DeployCWMultiTokenBridgeL2.s.sol
Normal file
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {CWMultiTokenBridgeL2} from "../contracts/bridge/CWMultiTokenBridgeL2.sol";
|
||||
|
||||
contract DeployCWMultiTokenBridgeL2 is Script {
|
||||
function run() external {
|
||||
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
||||
address sendRouter = vm.envAddress("CW_SEND_ROUTER");
|
||||
address receiveRouter = vm.envAddress("CW_RECEIVE_ROUTER");
|
||||
address feeToken = vm.envOr("CW_FEE_TOKEN", address(0));
|
||||
|
||||
vm.startBroadcast(deployerPrivateKey);
|
||||
CWMultiTokenBridgeL2 bridge = new CWMultiTokenBridgeL2(sendRouter, receiveRouter, feeToken);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("CWMultiTokenBridgeL2:", address(bridge));
|
||||
}
|
||||
}
|
||||
99
script/DeployCWReserveVerifier.s.sol
Normal file
99
script/DeployCWReserveVerifier.s.sol
Normal file
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {CWReserveVerifier} from "../contracts/bridge/integration/CWReserveVerifier.sol";
|
||||
|
||||
interface ICWMultiTokenBridgeL1Admin {
|
||||
function setReserveVerifier(address newVerifier) external;
|
||||
}
|
||||
|
||||
/**
|
||||
* @title DeployCWReserveVerifier
|
||||
* @notice Deploy and optionally attach/configure the cW canonical reserve verifier.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY (required)
|
||||
* CW_L1_BRIDGE (required)
|
||||
* CW_STABLECOIN_RESERVE_VAULT (optional)
|
||||
* CW_RESERVE_SYSTEM (optional)
|
||||
* CW_ATTACH_VERIFIER_TO_L1=1 (optional, default 1)
|
||||
* CW_CANONICAL_USDT / CW_CANONICAL_USDC (optional)
|
||||
* CW_USDT_RESERVE_ASSET / CW_USDC_RESERVE_ASSET (optional, used for reserve-system balance checks)
|
||||
* CW_REQUIRE_VAULT_BACKING=1 (optional, defaults to 1 when vault address is set)
|
||||
* CW_REQUIRE_RESERVE_SYSTEM_BALANCE=1 (optional, defaults to 1 when reserve system address is set)
|
||||
* CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT=1 (optional, defaults to 1 when vault address is set)
|
||||
*/
|
||||
contract DeployCWReserveVerifier is Script {
|
||||
function run() external {
|
||||
uint256 privateKey = vm.envUint("PRIVATE_KEY");
|
||||
address admin = vm.addr(privateKey);
|
||||
|
||||
address l1Bridge = vm.envAddress("CW_L1_BRIDGE");
|
||||
address stablecoinReserveVault = vm.envOr("CW_STABLECOIN_RESERVE_VAULT", address(0));
|
||||
address reserveSystem = vm.envOr("CW_RESERVE_SYSTEM", address(0));
|
||||
|
||||
bool attachVerifierToBridge = vm.envOr("CW_ATTACH_VERIFIER_TO_L1", uint256(1)) == 1;
|
||||
|
||||
bool defaultRequireVaultBacking = stablecoinReserveVault != address(0);
|
||||
bool defaultRequireReserveSystemBalance = reserveSystem != address(0);
|
||||
bool defaultRequireTokenOwnerMatchVault = stablecoinReserveVault != address(0);
|
||||
|
||||
bool requireVaultBacking = vm.envOr("CW_REQUIRE_VAULT_BACKING", defaultRequireVaultBacking ? uint256(1) : uint256(0)) == 1;
|
||||
bool requireReserveSystemBalance =
|
||||
vm.envOr("CW_REQUIRE_RESERVE_SYSTEM_BALANCE", defaultRequireReserveSystemBalance ? uint256(1) : uint256(0)) == 1;
|
||||
bool requireTokenOwnerMatchVault =
|
||||
vm.envOr("CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT", defaultRequireTokenOwnerMatchVault ? uint256(1) : uint256(0)) == 1;
|
||||
|
||||
address canonicalUSDT = vm.envOr("CW_CANONICAL_USDT", address(0));
|
||||
address canonicalUSDC = vm.envOr("CW_CANONICAL_USDC", address(0));
|
||||
address usdtReserveAsset = vm.envOr("CW_USDT_RESERVE_ASSET", address(0));
|
||||
address usdcReserveAsset = vm.envOr("CW_USDC_RESERVE_ASSET", address(0));
|
||||
|
||||
vm.startBroadcast(privateKey);
|
||||
|
||||
CWReserveVerifier verifier = new CWReserveVerifier(
|
||||
admin,
|
||||
l1Bridge,
|
||||
stablecoinReserveVault,
|
||||
reserveSystem
|
||||
);
|
||||
|
||||
if (attachVerifierToBridge) {
|
||||
ICWMultiTokenBridgeL1Admin(l1Bridge).setReserveVerifier(address(verifier));
|
||||
}
|
||||
|
||||
if (canonicalUSDT != address(0)) {
|
||||
verifier.configureToken(
|
||||
canonicalUSDT,
|
||||
usdtReserveAsset,
|
||||
requireVaultBacking,
|
||||
requireReserveSystemBalance && usdtReserveAsset != address(0),
|
||||
requireTokenOwnerMatchVault
|
||||
);
|
||||
}
|
||||
|
||||
if (canonicalUSDC != address(0)) {
|
||||
verifier.configureToken(
|
||||
canonicalUSDC,
|
||||
usdcReserveAsset,
|
||||
requireVaultBacking,
|
||||
requireReserveSystemBalance && usdcReserveAsset != address(0),
|
||||
requireTokenOwnerMatchVault
|
||||
);
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("CWReserveVerifier:", address(verifier));
|
||||
console.log(" L1 bridge:", l1Bridge);
|
||||
console.log(" attached to bridge:", attachVerifierToBridge);
|
||||
console.log(" stablecoin reserve vault:", stablecoinReserveVault);
|
||||
console.log(" reserve system:", reserveSystem);
|
||||
console.log(" requireVaultBacking:", requireVaultBacking);
|
||||
console.log(" requireReserveSystemBalance:", requireReserveSystemBalance);
|
||||
console.log(" requireTokenOwnerMatchVault:", requireTokenOwnerMatchVault);
|
||||
console.log(" configured cUSDT:", canonicalUSDT);
|
||||
console.log(" configured cUSDC:", canonicalUSDC);
|
||||
}
|
||||
}
|
||||
141
script/bridge/trustless/DeployChain138PilotDexVenues.s.sol
Normal file
141
script/bridge/trustless/DeployChain138PilotDexVenues.s.sol
Normal file
@@ -0,0 +1,141 @@
|
||||
// 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";
|
||||
import "../../../contracts/bridge/trustless/pilot/Chain138PilotDexVenues.sol";
|
||||
|
||||
contract DeployChain138PilotDexVenues is Script {
|
||||
address constant CHAIN138_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||
address constant CHAIN138_USDT = 0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1;
|
||||
address constant CHAIN138_USDC = 0x71D6687F38b93CCad569Fa6352c876eea967201b;
|
||||
address constant LIVE_ROUTER_V2 = 0xF1c93F54A5C2fc0d7766Ccb0Ad8f157DFB4C99Ce;
|
||||
|
||||
uint24 constant UNISWAP_FEE = 3000;
|
||||
uint256 constant BALANCER_FEE_BPS = 30;
|
||||
uint256 constant CURVE_FEE_BPS = 4;
|
||||
uint256 constant ONE_INCH_FEE_BPS = 35;
|
||||
|
||||
bytes32 constant BALANCER_WETH_USDT_POOL_ID = keccak256("chain138-pilot-balancer-weth-usdt");
|
||||
bytes32 constant BALANCER_WETH_USDC_POOL_ID = keccak256("chain138-pilot-balancer-weth-usdc");
|
||||
|
||||
uint256 constant WETH_PAIR_WETH_AMOUNT = 100 ether;
|
||||
uint256 constant WETH_PAIR_STABLE_AMOUNT = 210_000 * 1e6;
|
||||
uint256 constant CURVE_STABLE_AMOUNT = 500_000 * 1e6;
|
||||
|
||||
function run() external {
|
||||
require(block.chainid == 138, "DeployChain138PilotDexVenues: Chain 138 only");
|
||||
|
||||
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(deployerPrivateKey);
|
||||
address payable routerV2 = payable(vm.envOr("ENHANCED_SWAP_ROUTER_V2_ADDRESS", LIVE_ROUTER_V2));
|
||||
|
||||
vm.startBroadcast(deployerPrivateKey);
|
||||
|
||||
Chain138PilotUniswapV3Router uniswapRouter = new Chain138PilotUniswapV3Router();
|
||||
Chain138PilotBalancerVault balancerVault = new Chain138PilotBalancerVault();
|
||||
Chain138PilotCurve3Pool curve3Pool = new Chain138PilotCurve3Pool(CHAIN138_USDT, CHAIN138_USDC, address(0), CURVE_FEE_BPS);
|
||||
Chain138PilotOneInchAggregationRouter oneInchRouter = new Chain138PilotOneInchAggregationRouter();
|
||||
|
||||
uint256 totalUsdtRequired = (WETH_PAIR_STABLE_AMOUNT * 3) + CURVE_STABLE_AMOUNT;
|
||||
uint256 totalUsdcRequired = (WETH_PAIR_STABLE_AMOUNT * 3) + CURVE_STABLE_AMOUNT;
|
||||
uint256 totalWethRequired = WETH_PAIR_WETH_AMOUNT * 6;
|
||||
|
||||
_ensureMintedStable(CHAIN138_USDT, deployer, totalUsdtRequired);
|
||||
_ensureMintedStable(CHAIN138_USDC, deployer, totalUsdcRequired);
|
||||
_ensureWrappedWeth(deployer, totalWethRequired);
|
||||
|
||||
_approveToken(CHAIN138_WETH, address(uniswapRouter), WETH_PAIR_WETH_AMOUNT * 2);
|
||||
_approveToken(CHAIN138_USDT, address(uniswapRouter), WETH_PAIR_STABLE_AMOUNT);
|
||||
_approveToken(CHAIN138_USDC, address(uniswapRouter), WETH_PAIR_STABLE_AMOUNT);
|
||||
uniswapRouter.seedPair(CHAIN138_WETH, CHAIN138_USDT, UNISWAP_FEE, WETH_PAIR_WETH_AMOUNT, WETH_PAIR_STABLE_AMOUNT);
|
||||
uniswapRouter.seedPair(CHAIN138_WETH, CHAIN138_USDC, UNISWAP_FEE, WETH_PAIR_WETH_AMOUNT, WETH_PAIR_STABLE_AMOUNT);
|
||||
|
||||
_approveToken(CHAIN138_WETH, address(balancerVault), WETH_PAIR_WETH_AMOUNT * 2);
|
||||
_approveToken(CHAIN138_USDT, address(balancerVault), WETH_PAIR_STABLE_AMOUNT);
|
||||
_approveToken(CHAIN138_USDC, address(balancerVault), WETH_PAIR_STABLE_AMOUNT);
|
||||
balancerVault.seedPool(BALANCER_WETH_USDT_POOL_ID, CHAIN138_WETH, CHAIN138_USDT, WETH_PAIR_WETH_AMOUNT, WETH_PAIR_STABLE_AMOUNT, BALANCER_FEE_BPS);
|
||||
balancerVault.seedPool(BALANCER_WETH_USDC_POOL_ID, CHAIN138_WETH, CHAIN138_USDC, WETH_PAIR_WETH_AMOUNT, WETH_PAIR_STABLE_AMOUNT, BALANCER_FEE_BPS);
|
||||
|
||||
_approveToken(CHAIN138_USDT, address(curve3Pool), CURVE_STABLE_AMOUNT);
|
||||
_approveToken(CHAIN138_USDC, address(curve3Pool), CURVE_STABLE_AMOUNT);
|
||||
curve3Pool.fund(CURVE_STABLE_AMOUNT, CURVE_STABLE_AMOUNT, 0);
|
||||
|
||||
_approveToken(CHAIN138_WETH, address(oneInchRouter), WETH_PAIR_WETH_AMOUNT * 2);
|
||||
_approveToken(CHAIN138_USDT, address(oneInchRouter), WETH_PAIR_STABLE_AMOUNT);
|
||||
_approveToken(CHAIN138_USDC, address(oneInchRouter), WETH_PAIR_STABLE_AMOUNT);
|
||||
oneInchRouter.seedRoute(CHAIN138_WETH, CHAIN138_USDT, WETH_PAIR_WETH_AMOUNT, WETH_PAIR_STABLE_AMOUNT, ONE_INCH_FEE_BPS);
|
||||
oneInchRouter.seedRoute(CHAIN138_WETH, CHAIN138_USDC, WETH_PAIR_WETH_AMOUNT, WETH_PAIR_STABLE_AMOUNT, ONE_INCH_FEE_BPS);
|
||||
|
||||
if (routerV2 != address(0)) {
|
||||
EnhancedSwapRouterV2 router = EnhancedSwapRouterV2(routerV2);
|
||||
bytes memory uniswapProviderData = abi.encode(bytes(""), UNISWAP_FEE, address(uniswapRouter), false);
|
||||
bytes memory balancerUsdtProviderData = abi.encode(BALANCER_WETH_USDT_POOL_ID);
|
||||
bytes memory balancerUsdcProviderData = abi.encode(BALANCER_WETH_USDC_POOL_ID);
|
||||
bytes memory curveProviderData = abi.encode(int128(0), int128(1), false);
|
||||
bytes memory oneInchProviderData = abi.encode(address(oneInchRouter), address(oneInchRouter), bytes(""));
|
||||
|
||||
_setBidirectionalRoute(router, CHAIN138_WETH, CHAIN138_USDT, RouteTypesV2.Provider.UniswapV3, address(uniswapRouter), uniswapProviderData);
|
||||
_setBidirectionalRoute(router, CHAIN138_WETH, CHAIN138_USDC, RouteTypesV2.Provider.UniswapV3, address(uniswapRouter), uniswapProviderData);
|
||||
|
||||
_setBidirectionalRoute(router, CHAIN138_WETH, CHAIN138_USDT, RouteTypesV2.Provider.Balancer, address(balancerVault), balancerUsdtProviderData);
|
||||
_setBidirectionalRoute(router, CHAIN138_WETH, CHAIN138_USDC, RouteTypesV2.Provider.Balancer, address(balancerVault), balancerUsdcProviderData);
|
||||
|
||||
_setBidirectionalRoute(router, CHAIN138_USDT, CHAIN138_USDC, RouteTypesV2.Provider.Curve, address(curve3Pool), curveProviderData);
|
||||
|
||||
_setBidirectionalRoute(router, CHAIN138_WETH, CHAIN138_USDT, RouteTypesV2.Provider.OneInch, address(oneInchRouter), oneInchProviderData);
|
||||
_setBidirectionalRoute(router, CHAIN138_WETH, CHAIN138_USDC, RouteTypesV2.Provider.OneInch, address(oneInchRouter), oneInchProviderData);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.OneInch, true);
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("CHAIN138_PILOT_UNISWAP_V3_ROUTER:", address(uniswapRouter));
|
||||
console.log("CHAIN138_PILOT_UNISWAP_V3_QUOTER:", address(uniswapRouter));
|
||||
console.log("CHAIN138_PILOT_BALANCER_VAULT:", address(balancerVault));
|
||||
console.log("CHAIN138_PILOT_BALANCER_WETH_USDT_POOL_ID:");
|
||||
console.logBytes32(BALANCER_WETH_USDT_POOL_ID);
|
||||
console.log("CHAIN138_PILOT_BALANCER_WETH_USDC_POOL_ID:");
|
||||
console.logBytes32(BALANCER_WETH_USDC_POOL_ID);
|
||||
console.log("CHAIN138_PILOT_CURVE_3POOL:", address(curve3Pool));
|
||||
console.log("CHAIN138_PILOT_ONEINCH_ROUTER:", address(oneInchRouter));
|
||||
}
|
||||
|
||||
function _ensureMintedStable(address token, address deployer, uint256 requiredBalance) internal {
|
||||
uint256 currentBalance = IERC20(token).balanceOf(deployer);
|
||||
if (currentBalance >= requiredBalance) {
|
||||
return;
|
||||
}
|
||||
uint256 delta = requiredBalance - currentBalance;
|
||||
(bool ok,) = token.call(abi.encodeWithSignature("mint(address,uint256)", deployer, delta));
|
||||
require(ok, "DeployChain138PilotDexVenues: stable mint failed");
|
||||
}
|
||||
|
||||
function _ensureWrappedWeth(address deployer, uint256 requiredBalance) internal {
|
||||
uint256 currentBalance = IERC20(CHAIN138_WETH).balanceOf(deployer);
|
||||
if (currentBalance >= requiredBalance) {
|
||||
return;
|
||||
}
|
||||
uint256 delta = requiredBalance - currentBalance;
|
||||
(bool ok,) = CHAIN138_WETH.call{value: delta}(abi.encodeWithSignature("deposit()"));
|
||||
require(ok, "DeployChain138PilotDexVenues: WETH deposit failed");
|
||||
}
|
||||
|
||||
function _approveToken(address token, address spender, uint256 amount) internal {
|
||||
IERC20(token).approve(spender, 0);
|
||||
IERC20(token).approve(spender, amount);
|
||||
}
|
||||
|
||||
function _setBidirectionalRoute(
|
||||
EnhancedSwapRouterV2 router,
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
RouteTypesV2.Provider provider,
|
||||
address target,
|
||||
bytes memory providerData
|
||||
) internal {
|
||||
router.setProviderRoute(tokenA, tokenB, provider, target, providerData, true);
|
||||
router.setProviderRoute(tokenB, tokenA, provider, target, providerData, true);
|
||||
}
|
||||
}
|
||||
@@ -29,14 +29,14 @@ contract DeployEnhancedSwapRouter is Script {
|
||||
address constant CHAIN138_USDC = 0x71D6687F38b93CCad569Fa6352c876eea967201b;
|
||||
address constant CHAIN138_DAI_PLACEHOLDER = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
|
||||
|
||||
// Chain 138 live DODO pool map (2026-03-26)
|
||||
// Chain 138 live DODO pool map (2026-04-02 corrected stable stack)
|
||||
address constant CHAIN138_cUSDT = 0x93E66202A11B1772E55407B32B44e5Cd8eda7f22;
|
||||
address constant CHAIN138_cUSDC = 0xf22258f57794CC8E06237084b353Ab30fFfa640b;
|
||||
address constant CHAIN138_cEURT = 0xdf4b71c61E5912712C1Bdd451416B9aC26949d72;
|
||||
address constant CHAIN138_cXAUC = 0x290E52a8819A4fbD0714E517225429aA2B70EC6b;
|
||||
address constant CHAIN138_POOL_CUSDTCUSDC = 0xff8d3b8fDF7B112759F076B69f4271D4209C0849;
|
||||
address constant CHAIN138_POOL_CUSDTUSDT = 0x6fc60DEDc92a2047062294488539992710b99D71;
|
||||
address constant CHAIN138_POOL_CUSDCUSDC = 0x0309178Ae30302D83C76d6DD402a684ef3160eeC;
|
||||
address constant CHAIN138_POOL_CUSDTCUSDC = 0x9e89bAe009adf128782E19e8341996c596ac40dC;
|
||||
address constant CHAIN138_POOL_CUSDTUSDT = 0x866Cb44b59303d8dc5f4F9E3E7A8e8b0bf238d66;
|
||||
address constant CHAIN138_POOL_CUSDCUSDC = 0xc39B7D0F40838cbFb54649d327f49a6DAC964062;
|
||||
address constant CHAIN138_POOL_CUSDT_XAU_PUBLIC = 0x1AA55E2001E5651349aFf5a63FD7a7ae44f0f1b0;
|
||||
address constant CHAIN138_POOL_CUSDC_XAU_PUBLIC = 0xEa9AC6357CaCB42a83b9082B870610363b177CbA;
|
||||
address constant CHAIN138_POOL_CEURT_XAU_PUBLIC = 0xba99bc1eAac164569d5aca96c806934dDaf970CF;
|
||||
@@ -171,6 +171,8 @@ contract DeployEnhancedSwapRouter is Script {
|
||||
_registerPair(router, CHAIN138_cUSDT, CHAIN138_cXAUC, CHAIN138_POOL_CUSDT_XAU_PUBLIC);
|
||||
_registerPair(router, CHAIN138_cUSDC, CHAIN138_cXAUC, CHAIN138_POOL_CUSDC_XAU_PUBLIC);
|
||||
_registerPair(router, CHAIN138_cEURT, CHAIN138_cXAUC, CHAIN138_POOL_CEURT_XAU_PUBLIC);
|
||||
_registerOptionalEnvPool(router, "CHAIN138_POOL_WETH_USDT", CHAIN138_WETH, CHAIN138_USDT);
|
||||
_registerOptionalEnvPool(router, "CHAIN138_POOL_WETH_USDC", CHAIN138_WETH, CHAIN138_USDC);
|
||||
|
||||
if (dodoPmmProvider != address(0)) {
|
||||
router.setDodoLiquidityProvider(dodoPmmProvider);
|
||||
@@ -205,6 +207,20 @@ contract DeployEnhancedSwapRouter is Script {
|
||||
console.log("WARNING: current Chain 138 DODO initialization is primarily for token-to-token pair mappings via swapTokenToToken().");
|
||||
}
|
||||
|
||||
function _registerOptionalEnvPool(
|
||||
EnhancedSwapRouter router,
|
||||
string memory envKey,
|
||||
address tokenA,
|
||||
address tokenB
|
||||
) internal {
|
||||
address pool = vm.envOr(envKey, address(0));
|
||||
if (pool == address(0)) {
|
||||
console.log("Optional pool env not set:", envKey);
|
||||
return;
|
||||
}
|
||||
_registerPair(router, tokenA, tokenB, pool);
|
||||
}
|
||||
|
||||
function _registerPair(
|
||||
EnhancedSwapRouter router,
|
||||
address tokenA,
|
||||
|
||||
219
script/bridge/trustless/DeployEnhancedSwapRouterV2.s.sol
Normal file
219
script/bridge/trustless/DeployEnhancedSwapRouterV2.s.sol
Normal file
@@ -0,0 +1,219 @@
|
||||
// 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/IntentBridgeCoordinatorV2.sol";
|
||||
import "../../../contracts/bridge/trustless/RouteTypesV2.sol";
|
||||
import "../../../contracts/bridge/trustless/adapters/DodoRouteExecutorAdapter.sol";
|
||||
import "../../../contracts/bridge/trustless/adapters/DodoV3RouteExecutorAdapter.sol";
|
||||
import "../../../contracts/bridge/trustless/adapters/UniswapV3RouteExecutorAdapter.sol";
|
||||
import "../../../contracts/bridge/trustless/adapters/BalancerRouteExecutorAdapter.sol";
|
||||
import "../../../contracts/bridge/trustless/adapters/CurveRouteExecutorAdapter.sol";
|
||||
import "../../../contracts/bridge/trustless/adapters/OneInchRouteExecutorAdapter.sol";
|
||||
|
||||
contract DeployEnhancedSwapRouterV2 is Script {
|
||||
address constant CHAIN138_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||
address constant CHAIN138_WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
|
||||
address constant CHAIN138_USDT = 0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1;
|
||||
address constant CHAIN138_USDC = 0x71D6687F38b93CCad569Fa6352c876eea967201b;
|
||||
address constant CHAIN138_DAI_PLACEHOLDER = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
|
||||
|
||||
address constant CHAIN138_cUSDT = 0x93E66202A11B1772E55407B32B44e5Cd8eda7f22;
|
||||
address constant CHAIN138_cUSDC = 0xf22258f57794CC8E06237084b353Ab30fFfa640b;
|
||||
address constant CHAIN138_cEURT = 0xdf4b71c61E5912712C1Bdd451416B9aC26949d72;
|
||||
address constant CHAIN138_cXAUC = 0x290E52a8819A4fbD0714E517225429aA2B70EC6b;
|
||||
|
||||
address constant CHAIN138_POOL_CUSDTCUSDC = 0x9e89bAe009adf128782E19e8341996c596ac40dC;
|
||||
address constant CHAIN138_POOL_CUSDTUSDT = 0x866Cb44b59303d8dc5f4F9E3E7A8e8b0bf238d66;
|
||||
address constant CHAIN138_POOL_CUSDCUSDC = 0xc39B7D0F40838cbFb54649d327f49a6DAC964062;
|
||||
address constant CHAIN138_POOL_CUSDT_XAU_PUBLIC = 0x1AA55E2001E5651349aFf5a63FD7a7ae44f0f1b0;
|
||||
address constant CHAIN138_POOL_CUSDC_XAU_PUBLIC = 0xEa9AC6357CaCB42a83b9082B870610363b177CbA;
|
||||
address constant CHAIN138_POOL_CEURT_XAU_PUBLIC = 0xba99bc1eAac164569d5aca96c806934dDaf970CF;
|
||||
address constant CHAIN138_D3_PROXY = 0xc9a11abB7C63d88546Be24D58a6d95e3762cB843;
|
||||
address constant CHAIN138_D3_MM = 0x6550A3a59070061a262a893A1D6F3F490afFDBDA;
|
||||
|
||||
function run() external {
|
||||
require(block.chainid == 138, "DeployEnhancedSwapRouterV2: Chain 138 only");
|
||||
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(deployerPrivateKey);
|
||||
|
||||
address dodoProvider = vm.envOr("DODO_PMM_PROVIDER_ADDRESS", address(0));
|
||||
if (dodoProvider == address(0)) {
|
||||
dodoProvider = vm.envOr("DODO_PMM_PROVIDER", address(0));
|
||||
}
|
||||
require(dodoProvider != address(0), "DeployEnhancedSwapRouterV2: DODO PMM provider required");
|
||||
|
||||
address uniswapRouter = vm.envOr("UNISWAP_V3_ROUTER", address(0));
|
||||
address uniswapQuoter = vm.envOr("UNISWAP_QUOTER_ADDRESS", address(0));
|
||||
address balancerVault = vm.envOr("BALANCER_VAULT", address(0));
|
||||
address curvePool = vm.envOr("CURVE_3POOL", address(0));
|
||||
address oneInchRouter = vm.envOr("ONEINCH_ROUTER", address(0));
|
||||
address wethUsdtPool = vm.envOr("CHAIN138_POOL_WETH_USDT", address(0));
|
||||
address wethUsdcPool = vm.envOr("CHAIN138_POOL_WETH_USDC", address(0));
|
||||
uint24 uniswapWethUsdtFee = uint24(vm.envOr("UNISWAP_V3_WETH_USDT_FEE", uint256(3000)));
|
||||
uint24 uniswapWethUsdcFee = uint24(vm.envOr("UNISWAP_V3_WETH_USDC_FEE", uint256(3000)));
|
||||
bytes32 balancerWethUsdtPoolId = vm.envOr("BALANCER_WETH_USDT_POOL_ID", bytes32(0));
|
||||
bytes32 balancerWethUsdcPoolId = vm.envOr("BALANCER_WETH_USDC_POOL_ID", bytes32(0));
|
||||
address d3Proxy = vm.envOr("CHAIN138_D3_PROXY_ADDRESS", CHAIN138_D3_PROXY);
|
||||
address d3Pool = vm.envOr("CHAIN138_D3_MM_ADDRESS", CHAIN138_D3_MM);
|
||||
|
||||
console.log("=== EnhancedSwapRouterV2 Deployment ===");
|
||||
console.log("Deployer:", deployer);
|
||||
console.log("DODO PMM provider:", dodoProvider);
|
||||
console.log("UNISWAP_V3_ROUTER:", uniswapRouter);
|
||||
console.log("UNISWAP_QUOTER_ADDRESS:", uniswapQuoter);
|
||||
console.log("BALANCER_VAULT:", balancerVault);
|
||||
console.log("CURVE_3POOL:", curvePool);
|
||||
console.log("ONEINCH_ROUTER:", oneInchRouter);
|
||||
console.log("CHAIN138_POOL_WETH_USDT:", wethUsdtPool);
|
||||
console.log("CHAIN138_POOL_WETH_USDC:", wethUsdcPool);
|
||||
console.log("CHAIN138_D3_PROXY_ADDRESS:", d3Proxy);
|
||||
console.log("CHAIN138_D3_MM_ADDRESS:", d3Pool);
|
||||
|
||||
vm.startBroadcast(deployerPrivateKey);
|
||||
|
||||
DodoRouteExecutorAdapter dodoAdapter = new DodoRouteExecutorAdapter();
|
||||
DodoV3RouteExecutorAdapter dodoV3Adapter = new DodoV3RouteExecutorAdapter();
|
||||
UniswapV3RouteExecutorAdapter uniswapAdapter = new UniswapV3RouteExecutorAdapter();
|
||||
BalancerRouteExecutorAdapter balancerAdapter = new BalancerRouteExecutorAdapter();
|
||||
CurveRouteExecutorAdapter curveAdapter = new CurveRouteExecutorAdapter();
|
||||
OneInchRouteExecutorAdapter oneInchAdapter = new OneInchRouteExecutorAdapter();
|
||||
|
||||
EnhancedSwapRouterV2 router = new EnhancedSwapRouterV2(
|
||||
CHAIN138_WETH,
|
||||
CHAIN138_USDT,
|
||||
CHAIN138_USDC,
|
||||
CHAIN138_DAI_PLACEHOLDER
|
||||
);
|
||||
IntentBridgeCoordinatorV2 coordinator = new IntentBridgeCoordinatorV2(address(router));
|
||||
|
||||
router.setProviderAdapter(RouteTypesV2.Provider.Dodo, address(dodoAdapter));
|
||||
router.setProviderAdapter(RouteTypesV2.Provider.DodoV3, address(dodoV3Adapter));
|
||||
router.setProviderAdapter(RouteTypesV2.Provider.UniswapV3, address(uniswapAdapter));
|
||||
router.setProviderAdapter(RouteTypesV2.Provider.Balancer, address(balancerAdapter));
|
||||
router.setProviderAdapter(RouteTypesV2.Provider.Curve, address(curveAdapter));
|
||||
router.setProviderAdapter(RouteTypesV2.Provider.OneInch, address(oneInchAdapter));
|
||||
|
||||
_setDodoPair(router, CHAIN138_cUSDT, CHAIN138_cUSDC, dodoProvider, CHAIN138_POOL_CUSDTCUSDC);
|
||||
_setDodoPair(router, CHAIN138_cUSDT, CHAIN138_USDT, dodoProvider, CHAIN138_POOL_CUSDTUSDT);
|
||||
_setDodoPair(router, CHAIN138_cUSDC, CHAIN138_USDC, dodoProvider, CHAIN138_POOL_CUSDCUSDC);
|
||||
_setDodoPair(router, CHAIN138_cUSDT, CHAIN138_cXAUC, dodoProvider, CHAIN138_POOL_CUSDT_XAU_PUBLIC);
|
||||
_setDodoPair(router, CHAIN138_cUSDC, CHAIN138_cXAUC, dodoProvider, CHAIN138_POOL_CUSDC_XAU_PUBLIC);
|
||||
_setDodoPair(router, CHAIN138_cEURT, CHAIN138_cXAUC, dodoProvider, CHAIN138_POOL_CEURT_XAU_PUBLIC);
|
||||
|
||||
if (wethUsdtPool != address(0)) {
|
||||
_setDodoPair(router, CHAIN138_WETH, CHAIN138_USDT, dodoProvider, wethUsdtPool);
|
||||
}
|
||||
if (wethUsdcPool != address(0)) {
|
||||
_setDodoPair(router, CHAIN138_WETH, CHAIN138_USDC, dodoProvider, wethUsdcPool);
|
||||
}
|
||||
|
||||
if (d3Proxy != address(0) && d3Pool != address(0)) {
|
||||
_setDodoV3Pair(router, CHAIN138_WETH10, CHAIN138_USDT, d3Proxy, d3Pool);
|
||||
} else {
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.DodoV3, false);
|
||||
}
|
||||
|
||||
if (uniswapRouter != address(0) && uniswapQuoter != address(0)) {
|
||||
_setUniswapPair(router, CHAIN138_WETH, CHAIN138_USDT, uniswapRouter, uniswapQuoter, uniswapWethUsdtFee);
|
||||
_setUniswapPair(router, CHAIN138_WETH, CHAIN138_USDC, uniswapRouter, uniswapQuoter, uniswapWethUsdcFee);
|
||||
} else {
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.UniswapV3, false);
|
||||
}
|
||||
|
||||
if (balancerVault != address(0) && balancerWethUsdtPoolId != bytes32(0)) {
|
||||
_setBalancerPair(router, CHAIN138_WETH, CHAIN138_USDT, balancerVault, balancerWethUsdtPoolId);
|
||||
}
|
||||
if (balancerVault != address(0) && balancerWethUsdcPoolId != bytes32(0)) {
|
||||
_setBalancerPair(router, CHAIN138_WETH, CHAIN138_USDC, balancerVault, balancerWethUsdcPoolId);
|
||||
}
|
||||
if (balancerVault == address(0) || (balancerWethUsdtPoolId == bytes32(0) && balancerWethUsdcPoolId == bytes32(0))) {
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Balancer, false);
|
||||
}
|
||||
|
||||
if (curvePool != address(0)) {
|
||||
_setCurvePair(router, CHAIN138_USDT, CHAIN138_USDC, curvePool, 0, 1, false);
|
||||
} else {
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Curve, false);
|
||||
}
|
||||
|
||||
if (oneInchRouter == address(0)) {
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.OneInch, false);
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("EnhancedSwapRouterV2:", address(router));
|
||||
console.log("IntentBridgeCoordinatorV2:", address(coordinator));
|
||||
console.log("DodoRouteExecutorAdapter:", address(dodoAdapter));
|
||||
console.log("DodoV3RouteExecutorAdapter:", address(dodoV3Adapter));
|
||||
console.log("UniswapV3RouteExecutorAdapter:", address(uniswapAdapter));
|
||||
console.log("BalancerRouteExecutorAdapter:", address(balancerAdapter));
|
||||
console.log("CurveRouteExecutorAdapter:", address(curveAdapter));
|
||||
console.log("OneInchRouteExecutorAdapter:", address(oneInchAdapter));
|
||||
}
|
||||
|
||||
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 _setDodoV3Pair(
|
||||
EnhancedSwapRouterV2 router,
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
address target,
|
||||
address pool
|
||||
) internal {
|
||||
bytes memory providerData = abi.encode(pool);
|
||||
router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.DodoV3, target, providerData, true);
|
||||
router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.DodoV3, 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);
|
||||
}
|
||||
}
|
||||
36
script/deploy/DeployAaveQuotePushFlashReceiver.s.sol
Normal file
36
script/deploy/DeployAaveQuotePushFlashReceiver.s.sol
Normal file
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {AaveQuotePushFlashReceiver} from "../../contracts/flash/AaveQuotePushFlashReceiver.sol";
|
||||
|
||||
/**
|
||||
* @title DeployAaveQuotePushFlashReceiver
|
||||
* @notice Deploy the Aave V3 quote-push flash receiver.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY required
|
||||
* AAVE_POOL_ADDRESS optional; defaults to Aave V3 mainnet Pool
|
||||
*
|
||||
* Usage:
|
||||
* forge script script/deploy/DeployAaveQuotePushFlashReceiver.s.sol:DeployAaveQuotePushFlashReceiver \
|
||||
* --rpc-url $ETHEREUM_MAINNET_RPC --broadcast -vvvv
|
||||
*/
|
||||
contract DeployAaveQuotePushFlashReceiver 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);
|
||||
|
||||
console.log("Deployer:", deployer);
|
||||
console.log("Aave Pool:", pool);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
AaveQuotePushFlashReceiver receiver = new AaveQuotePushFlashReceiver(pool);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("AaveQuotePushFlashReceiver:", address(receiver));
|
||||
}
|
||||
}
|
||||
146
script/deploy/DeployAndStageCompliantFiatTokensV2ForChain.s.sol
Normal file
146
script/deploy/DeployAndStageCompliantFiatTokensV2ForChain.s.sol
Normal file
@@ -0,0 +1,146 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {CompliantUSDTTokenV2} from "../../contracts/tokens/CompliantUSDTTokenV2.sol";
|
||||
import {CompliantUSDCTokenV2} from "../../contracts/tokens/CompliantUSDCTokenV2.sol";
|
||||
import {UniversalAssetRegistry} from "../../contracts/registry/UniversalAssetRegistry.sol";
|
||||
|
||||
/**
|
||||
* @title DeployAndStageCompliantFiatTokensV2ForChain
|
||||
* @notice Deploy fresh source-aligned cUSDT V2 / cUSDC V2 contracts, optionally wire governance/disclosure metadata,
|
||||
* and stage them in UniversalAssetRegistry as version-aware GRU assets.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY (required)
|
||||
* INITIAL_OPERATOR (optional; default deployer)
|
||||
* ADMIN (optional; default deployer / OWNER alias)
|
||||
* OWNER (optional alias for ADMIN when ADMIN unset)
|
||||
* INITIAL_SUPPLY (optional; default 0 for safe promotion)
|
||||
* FORWARD_CANONICAL (optional; default true for promotion flow)
|
||||
* GOVERNANCE_CONTROLLER (optional; when set, calls setGovernanceController on fresh deployments)
|
||||
* UNIVERSAL_ASSET_REGISTRY (optional; when set and REGISTER_IN_GRU != 0, registers V2 assets)
|
||||
* REGISTER_IN_GRU (optional; default 1)
|
||||
* TOKEN_URI (optional generic fallback)
|
||||
* REGULATORY_DISCLOSURE_URI (optional)
|
||||
* REPORTING_URI (optional)
|
||||
* CUSDT_V2_TOKEN_URI / CUSDC_V2_TOKEN_URI (optional; per-token overrides)
|
||||
* CUSDT_V2_REGULATORY_DISCLOSURE_URI / CUSDC_V2_REGULATORY_DISCLOSURE_URI (optional; per-token overrides)
|
||||
* CUSDT_V2_REPORTING_URI / CUSDC_V2_REPORTING_URI (optional; per-token overrides)
|
||||
* DEPLOY_CUSDT_V2 / DEPLOY_CUSDC_V2 (optional; default both 1)
|
||||
*/
|
||||
contract DeployAndStageCompliantFiatTokensV2ForChain is Script {
|
||||
uint256 internal constant DEFAULT_INITIAL_SUPPLY = 0;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address initialOperator = vm.envOr("INITIAL_OPERATOR", deployer);
|
||||
address ownerAlias = vm.envOr("OWNER", deployer);
|
||||
address admin = vm.envOr("ADMIN", ownerAlias);
|
||||
uint256 initialSupply = vm.envOr("INITIAL_SUPPLY", DEFAULT_INITIAL_SUPPLY);
|
||||
bool forwardCanonical = vm.envOr("FORWARD_CANONICAL", true);
|
||||
address governanceController = vm.envOr("GOVERNANCE_CONTROLLER", address(0));
|
||||
address registryAddr = vm.envOr("UNIVERSAL_ASSET_REGISTRY", address(0));
|
||||
bool registerInGru = vm.envOr("REGISTER_IN_GRU", uint256(1)) != 0;
|
||||
string memory genericTokenURI = vm.envOr("TOKEN_URI", string(""));
|
||||
string memory disclosureURI = vm.envOr("REGULATORY_DISCLOSURE_URI", string(""));
|
||||
string memory reportingURI = vm.envOr("REPORTING_URI", string(""));
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
if (vm.envOr("DEPLOY_CUSDT_V2", uint256(1)) != 0) {
|
||||
CompliantUSDTTokenV2 cusdtV2 =
|
||||
new CompliantUSDTTokenV2(initialOperator, admin, initialSupply, forwardCanonical);
|
||||
_postDeploy(
|
||||
address(cusdtV2),
|
||||
vm.envOr("CUSDT_V2_TOKEN_URI", genericTokenURI),
|
||||
"cUSDT",
|
||||
governanceController,
|
||||
vm.envOr("CUSDT_V2_REGULATORY_DISCLOSURE_URI", disclosureURI),
|
||||
vm.envOr("CUSDT_V2_REPORTING_URI", reportingURI),
|
||||
registryAddr,
|
||||
registerInGru,
|
||||
"Tether USD (Compliant V2)",
|
||||
"cUSDT.v2"
|
||||
);
|
||||
console.log("cUSDT_V2", address(cusdtV2));
|
||||
}
|
||||
|
||||
if (vm.envOr("DEPLOY_CUSDC_V2", uint256(1)) != 0) {
|
||||
CompliantUSDCTokenV2 cusdcV2 =
|
||||
new CompliantUSDCTokenV2(initialOperator, admin, initialSupply, forwardCanonical);
|
||||
_postDeploy(
|
||||
address(cusdcV2),
|
||||
vm.envOr("CUSDC_V2_TOKEN_URI", genericTokenURI),
|
||||
"cUSDC",
|
||||
governanceController,
|
||||
vm.envOr("CUSDC_V2_REGULATORY_DISCLOSURE_URI", disclosureURI),
|
||||
vm.envOr("CUSDC_V2_REPORTING_URI", reportingURI),
|
||||
registryAddr,
|
||||
registerInGru,
|
||||
"USD Coin (Compliant V2)",
|
||||
"cUSDC.v2"
|
||||
);
|
||||
console.log("cUSDC_V2", address(cusdcV2));
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
|
||||
function _postDeploy(
|
||||
address token,
|
||||
string memory tokenURI,
|
||||
string memory symbolDisplay,
|
||||
address governanceController,
|
||||
string memory disclosureURI,
|
||||
string memory reportingURI,
|
||||
address registryAddr,
|
||||
bool registerInGru,
|
||||
string memory name,
|
||||
string memory versionedSymbol
|
||||
) internal {
|
||||
if (bytes(tokenURI).length > 0 || bytes(symbolDisplay).length > 0) {
|
||||
_setPresentationMetadata(token, tokenURI, symbolDisplay);
|
||||
}
|
||||
if (governanceController != address(0)) {
|
||||
_setGovernanceController(token, governanceController);
|
||||
}
|
||||
if (bytes(disclosureURI).length > 0 || bytes(reportingURI).length > 0) {
|
||||
_setDisclosureMetadata(token, disclosureURI, reportingURI);
|
||||
}
|
||||
if (registerInGru && registryAddr != address(0)) {
|
||||
UniversalAssetRegistry(registryAddr).registerGRUCompliantAsset(token, name, versionedSymbol, 6, "International");
|
||||
}
|
||||
}
|
||||
|
||||
function _setPresentationMetadata(address token, string memory tokenURI, string memory symbolDisplay) internal {
|
||||
(bool ok,) = token.call(
|
||||
abi.encodeWithSignature(
|
||||
"emergencySetPresentationMetadata(bool,string,string)",
|
||||
true,
|
||||
tokenURI,
|
||||
symbolDisplay
|
||||
)
|
||||
);
|
||||
require(ok, "emergencySetPresentationMetadata failed");
|
||||
}
|
||||
|
||||
function _setGovernanceController(address token, address governanceController) internal {
|
||||
(bool ok,) = token.call(
|
||||
abi.encodeWithSignature("setGovernanceController(address)", governanceController)
|
||||
);
|
||||
require(ok, "setGovernanceController failed");
|
||||
}
|
||||
|
||||
function _setDisclosureMetadata(address token, string memory disclosureURI, string memory reportingURI) internal {
|
||||
(bool ok,) = token.call(
|
||||
abi.encodeWithSignature(
|
||||
"emergencySetDisclosureMetadata(string,string)",
|
||||
disclosureURI,
|
||||
reportingURI
|
||||
)
|
||||
);
|
||||
require(ok, "emergencySetDisclosureMetadata failed");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {CompliantFiatTokenV2} from "../../contracts/tokens/CompliantFiatTokenV2.sol";
|
||||
import {UniversalAssetRegistry} from "../../contracts/registry/UniversalAssetRegistry.sol";
|
||||
|
||||
/**
|
||||
* @title DeployAndStageGenericCompliantFiatTokenV2ForChain
|
||||
* @notice Deploy a generic GRU c* V2 asset, optionally wire governance / disclosure metadata,
|
||||
* and stage it in UniversalAssetRegistry.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY (required)
|
||||
* TOKEN_NAME (required)
|
||||
* TOKEN_SYMBOL (required)
|
||||
* CURRENCY_CODE (required)
|
||||
* TOKEN_DECIMALS (optional; default 6)
|
||||
* VERSION_TAG (optional; default "2")
|
||||
* INITIAL_OPERATOR (optional; default deployer)
|
||||
* ADMIN / OWNER (optional; default deployer)
|
||||
* INITIAL_SUPPLY (optional; default 0)
|
||||
* FORWARD_CANONICAL (optional; default true)
|
||||
* GOVERNANCE_CONTROLLER (optional)
|
||||
* UNIVERSAL_ASSET_REGISTRY (optional; when set and REGISTER_IN_GRU != 0, registers the asset)
|
||||
* REGISTER_IN_GRU (optional; default 1)
|
||||
* REGISTRY_NAME (optional; default TOKEN_NAME)
|
||||
* REGISTRY_SYMBOL (optional; default TOKEN_SYMBOL.VERSION_TAG lower-suffix style is not enforced)
|
||||
* TOKEN_URI / REGULATORY_DISCLOSURE_URI / REPORTING_URI (optional)
|
||||
*/
|
||||
contract DeployAndStageGenericCompliantFiatTokenV2ForChain is Script {
|
||||
uint256 internal constant DEFAULT_INITIAL_SUPPLY = 0;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address initialOperator = vm.envOr("INITIAL_OPERATOR", deployer);
|
||||
address ownerAlias = vm.envOr("OWNER", deployer);
|
||||
address admin = vm.envOr("ADMIN", ownerAlias);
|
||||
uint256 initialSupply = vm.envOr("INITIAL_SUPPLY", DEFAULT_INITIAL_SUPPLY);
|
||||
bool forwardCanonical = vm.envOr("FORWARD_CANONICAL", true);
|
||||
address governanceController = vm.envOr("GOVERNANCE_CONTROLLER", address(0));
|
||||
address registryAddr = vm.envOr("UNIVERSAL_ASSET_REGISTRY", address(0));
|
||||
bool registerInGru = vm.envOr("REGISTER_IN_GRU", uint256(1)) != 0;
|
||||
|
||||
string memory tokenName = vm.envString("TOKEN_NAME");
|
||||
string memory tokenSymbol = vm.envString("TOKEN_SYMBOL");
|
||||
string memory currencyCode = vm.envString("CURRENCY_CODE");
|
||||
uint8 tokenDecimals = uint8(vm.envOr("TOKEN_DECIMALS", uint256(6)));
|
||||
string memory versionTag = vm.envOr("VERSION_TAG", string("2"));
|
||||
string memory registryName = vm.envOr("REGISTRY_NAME", tokenName);
|
||||
string memory registrySymbol = vm.envOr("REGISTRY_SYMBOL", tokenSymbol);
|
||||
string memory tokenURI = vm.envOr("TOKEN_URI", string(""));
|
||||
string memory disclosureURI = vm.envOr("REGULATORY_DISCLOSURE_URI", string(""));
|
||||
string memory reportingURI = vm.envOr("REPORTING_URI", string(""));
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
CompliantFiatTokenV2 token = new CompliantFiatTokenV2(
|
||||
tokenName,
|
||||
tokenSymbol,
|
||||
tokenDecimals,
|
||||
currencyCode,
|
||||
versionTag,
|
||||
initialOperator,
|
||||
admin,
|
||||
initialSupply,
|
||||
forwardCanonical
|
||||
);
|
||||
|
||||
if (bytes(tokenURI).length > 0 || bytes(tokenSymbol).length > 0) {
|
||||
_setPresentationMetadata(address(token), tokenURI, tokenSymbol);
|
||||
}
|
||||
if (governanceController != address(0)) {
|
||||
_setGovernanceController(address(token), governanceController);
|
||||
}
|
||||
if (bytes(disclosureURI).length > 0 || bytes(reportingURI).length > 0) {
|
||||
_setDisclosureMetadata(address(token), disclosureURI, reportingURI);
|
||||
}
|
||||
if (registerInGru && registryAddr != address(0)) {
|
||||
UniversalAssetRegistry(registryAddr).registerGRUCompliantAsset(
|
||||
address(token),
|
||||
registryName,
|
||||
registrySymbol,
|
||||
tokenDecimals,
|
||||
"International"
|
||||
);
|
||||
}
|
||||
|
||||
console.log("generic_cstar_v2", address(token));
|
||||
console.log("token_symbol", tokenSymbol);
|
||||
console.log("currency_code", currencyCode);
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
|
||||
function _setPresentationMetadata(address token, string memory tokenURI, string memory symbolDisplay) internal {
|
||||
(bool ok,) = token.call(
|
||||
abi.encodeWithSignature(
|
||||
"emergencySetPresentationMetadata(bool,string,string)",
|
||||
true,
|
||||
tokenURI,
|
||||
symbolDisplay
|
||||
)
|
||||
);
|
||||
require(ok, "emergencySetPresentationMetadata failed");
|
||||
}
|
||||
|
||||
function _setGovernanceController(address token, address governanceController) internal {
|
||||
(bool ok,) = token.call(
|
||||
abi.encodeWithSignature("setGovernanceController(address)", governanceController)
|
||||
);
|
||||
require(ok, "setGovernanceController failed");
|
||||
}
|
||||
|
||||
function _setDisclosureMetadata(address token, string memory disclosureURI, string memory reportingURI) internal {
|
||||
(bool ok,) = token.call(
|
||||
abi.encodeWithSignature(
|
||||
"emergencySetDisclosureMetadata(string,string)",
|
||||
disclosureURI,
|
||||
reportingURI
|
||||
)
|
||||
);
|
||||
require(ok, "emergencySetDisclosureMetadata failed");
|
||||
}
|
||||
}
|
||||
53
script/deploy/DeployCAUSDT.s.sol
Normal file
53
script/deploy/DeployCAUSDT.s.sol
Normal file
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import {CREATE2Factory} from "../../contracts/utils/CREATE2Factory.sol";
|
||||
import {CompliantFiatToken} from "../../contracts/tokens/CompliantFiatToken.sol";
|
||||
|
||||
/**
|
||||
* @title DeployCAUSDT
|
||||
* @notice Deterministically deploy the Chain 138 cAUSDT contract via CREATE2.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY (required)
|
||||
* CREATE2_FACTORY_ADDRESS (required; CREATE2_FACTORY accepted as fallback)
|
||||
* OWNER / ADMIN (optional; default deployer)
|
||||
* INITIAL_SUPPLY_CAUSDT (optional; defaults to 1_000_000e6)
|
||||
*/
|
||||
contract DeployCAUSDT is Script {
|
||||
uint8 constant DECIMALS = 6;
|
||||
string constant SYMBOL = "cAUSDT";
|
||||
string constant NAME = "Alltra USD Token (Compliant)";
|
||||
string constant CURRENCY_CODE = "USD";
|
||||
|
||||
function run() external returns (address deployed) {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address owner = vm.envOr("OWNER", deployer);
|
||||
address admin = vm.envOr("ADMIN", deployer);
|
||||
uint256 initialSupply = vm.envOr("INITIAL_SUPPLY_CAUSDT", uint256(1_000_000 * 10**6));
|
||||
|
||||
address factoryAddr = vm.envOr("CREATE2_FACTORY_ADDRESS", vm.envAddress("CREATE2_FACTORY"));
|
||||
require(factoryAddr != address(0), "CREATE2 factory required");
|
||||
CREATE2Factory factory = CREATE2Factory(factoryAddr);
|
||||
|
||||
uint256 salt = uint256(keccak256(abi.encodePacked("CompliantFiatToken.", SYMBOL)));
|
||||
bytes memory bytecode = abi.encodePacked(
|
||||
type(CompliantFiatToken).creationCode,
|
||||
abi.encode(NAME, SYMBOL, uint8(DECIMALS), CURRENCY_CODE, owner, admin, initialSupply)
|
||||
);
|
||||
|
||||
deployed = factory.computeAddress(bytecode, salt);
|
||||
if (deployed.code.length > 0) {
|
||||
console.log("cAUSDT already deployed", deployed);
|
||||
return deployed;
|
||||
}
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
deployed = factory.deploy(bytecode, salt);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("cAUSDT", deployed);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,10 @@ import {CompliantWrappedToken} from "../../contracts/tokens/CompliantWrappedToke
|
||||
* Env:
|
||||
* PRIVATE_KEY (required)
|
||||
* CW_BRIDGE_ADDRESS (required) — address that can mint/burn (e.g. CCIP receiver or custom bridge)
|
||||
* DEPLOY_CWUSDT=1, DEPLOY_CWUSDC=1, DEPLOY_CWEURC=1, ... (default all 1; set 0 to skip a token)
|
||||
* CW_STRICT_MODE=1 (optional) — revoke deployer MINTER/BURNER after bridge grant
|
||||
* CW_GOVERNANCE_ADMIN=0x... (optional) — grant DEFAULT_ADMIN_ROLE to governance; if strict, revoke deployer admin when governance is set
|
||||
* CW_FREEZE_OPERATIONAL_ROLES=1 (optional) — freeze future MINTER/BURNER changes after setup
|
||||
* DEPLOY_CWUSDT=1, DEPLOY_CWUSDC=1, DEPLOY_CWUSDW=1, DEPLOY_CWEURC=1, ... (default all 1; set 0 to skip a token)
|
||||
*/
|
||||
contract DeployCWTokens is Script {
|
||||
uint8 constant DECIMALS = 6;
|
||||
@@ -21,22 +24,27 @@ contract DeployCWTokens is Script {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address bridge = vm.envAddress("CW_BRIDGE_ADDRESS");
|
||||
bool strictMode = vm.envOr("CW_STRICT_MODE", uint256(0)) == 1;
|
||||
bool freezeOperationalRoles = vm.envOr("CW_FREEZE_OPERATIONAL_ROLES", uint256(0)) == 1;
|
||||
address governanceAdmin = vm.envOr("CW_GOVERNANCE_ADMIN", address(0));
|
||||
require(bridge != address(0), "CW_BRIDGE_ADDRESS required");
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
_deployOne(deployer, "Wrapped cUSDT", "cWUSDT", "DEPLOY_CWUSDT", bridge);
|
||||
_deployOne(deployer, "Wrapped cUSDC", "cWUSDC", "DEPLOY_CWUSDC", bridge);
|
||||
_deployOne(deployer, "Wrapped cEURC", "cWEURC", "DEPLOY_CWEURC", bridge);
|
||||
_deployOne(deployer, "Wrapped cEURT", "cWEURT", "DEPLOY_CWEURT", bridge);
|
||||
_deployOne(deployer, "Wrapped cGBPC", "cWGBPC", "DEPLOY_CWGBPC", bridge);
|
||||
_deployOne(deployer, "Wrapped cGBPT", "cWGBPT", "DEPLOY_CWGBPT", bridge);
|
||||
_deployOne(deployer, "Wrapped cAUDC", "cWAUDC", "DEPLOY_CWAUDC", bridge);
|
||||
_deployOne(deployer, "Wrapped cJPYC", "cWJPYC", "DEPLOY_CWJPYC", bridge);
|
||||
_deployOne(deployer, "Wrapped cCHFC", "cWCHFC", "DEPLOY_CWCHFC", bridge);
|
||||
_deployOne(deployer, "Wrapped cCADC", "cWCADC", "DEPLOY_CWCADC", bridge);
|
||||
_deployOne(deployer, "Wrapped cXAUC", "cWXAUC", "DEPLOY_CWXAUC", bridge);
|
||||
_deployOne(deployer, "Wrapped cXAUT", "cWXAUT", "DEPLOY_CWXAUT", bridge);
|
||||
_deployOne(deployer, "Wrapped cUSDT", "cWUSDT", "DEPLOY_CWUSDT", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cUSDC", "cWUSDC", "DEPLOY_CWUSDC", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cAUSDT", "cWAUSDT", "DEPLOY_CWAUSDT", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cUSDW", "cWUSDW", "DEPLOY_CWUSDW", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cEURC", "cWEURC", "DEPLOY_CWEURC", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cEURT", "cWEURT", "DEPLOY_CWEURT", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cGBPC", "cWGBPC", "DEPLOY_CWGBPC", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cGBPT", "cWGBPT", "DEPLOY_CWGBPT", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cAUDC", "cWAUDC", "DEPLOY_CWAUDC", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cJPYC", "cWJPYC", "DEPLOY_CWJPYC", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cCHFC", "cWCHFC", "DEPLOY_CWCHFC", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cCADC", "cWCADC", "DEPLOY_CWCADC", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cXAUC", "cWXAUC", "DEPLOY_CWXAUC", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
_deployOne(deployer, "Wrapped cXAUT", "cWXAUT", "DEPLOY_CWXAUT", bridge, strictMode, governanceAdmin, freezeOperationalRoles);
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
@@ -46,12 +54,40 @@ contract DeployCWTokens is Script {
|
||||
string memory name,
|
||||
string memory symbol,
|
||||
string memory envKey,
|
||||
address bridge
|
||||
address bridge,
|
||||
bool strictMode,
|
||||
address governanceAdmin,
|
||||
bool freezeOperationalRoles
|
||||
) internal {
|
||||
if (vm.envOr(envKey, uint256(1)) == 0) return;
|
||||
CompliantWrappedToken t = new CompliantWrappedToken(name, symbol, DECIMALS, admin);
|
||||
t.grantRole(t.MINTER_ROLE(), bridge);
|
||||
t.grantRole(t.BURNER_ROLE(), bridge);
|
||||
|
||||
if (strictMode) {
|
||||
t.revokeRole(t.MINTER_ROLE(), admin);
|
||||
t.revokeRole(t.BURNER_ROLE(), admin);
|
||||
}
|
||||
|
||||
if (governanceAdmin != address(0) && governanceAdmin != admin) {
|
||||
t.grantRole(t.DEFAULT_ADMIN_ROLE(), governanceAdmin);
|
||||
}
|
||||
|
||||
if (freezeOperationalRoles) {
|
||||
t.freezeOperationalRoles();
|
||||
}
|
||||
|
||||
if (strictMode && governanceAdmin != address(0) && governanceAdmin != admin) {
|
||||
t.revokeRole(t.DEFAULT_ADMIN_ROLE(), admin);
|
||||
}
|
||||
|
||||
console.log(symbol, address(t));
|
||||
console.log(" strictMode", strictMode);
|
||||
console.log(" governanceAdmin", governanceAdmin);
|
||||
console.log(" operationalRolesFrozen", t.operationalRolesFrozen());
|
||||
console.log(" deployerHasMinter", t.hasRole(t.MINTER_ROLE(), admin));
|
||||
console.log(" deployerHasBurner", t.hasRole(t.BURNER_ROLE(), admin));
|
||||
console.log(" bridgeHasMinter", t.hasRole(t.MINTER_ROLE(), bridge));
|
||||
console.log(" bridgeHasBurner", t.hasRole(t.BURNER_ROLE(), bridge));
|
||||
}
|
||||
}
|
||||
|
||||
56
script/deploy/DeployCompliantFiatTokensV2ForChain.s.sol
Normal file
56
script/deploy/DeployCompliantFiatTokensV2ForChain.s.sol
Normal file
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {CompliantUSDTTokenV2} from "../../contracts/tokens/CompliantUSDTTokenV2.sol";
|
||||
import {CompliantUSDCTokenV2} from "../../contracts/tokens/CompliantUSDCTokenV2.sol";
|
||||
|
||||
/**
|
||||
* @title DeployCompliantFiatTokensV2ForChain
|
||||
* @notice Deploy canonical cUSDT V2 / cUSDC V2 contracts to the current chain.
|
||||
* @dev Defaults to safe pre-cutover posture: new addresses with forwardCanonical disabled unless env overrides it.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY (required)
|
||||
* INITIAL_OPERATOR (optional; default deployer)
|
||||
* ADMIN (optional; default deployer)
|
||||
* OWNER (optional alias for ADMIN when ADMIN unset)
|
||||
* INITIAL_SUPPLY (optional; default 1_000_000e6)
|
||||
* FORWARD_CANONICAL=1 to mark deployed V2 as forward canonical immediately
|
||||
* DEPLOY_CUSDT_V2=1 / DEPLOY_CUSDC_V2=1 (default both 1)
|
||||
*/
|
||||
contract DeployCompliantFiatTokensV2ForChain is Script {
|
||||
uint256 internal constant DEFAULT_INITIAL_SUPPLY = 1_000_000 * 10 ** 6;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address initialOperator = vm.envOr("INITIAL_OPERATOR", deployer);
|
||||
address ownerAlias = vm.envOr("OWNER", deployer);
|
||||
address admin = vm.envOr("ADMIN", ownerAlias);
|
||||
uint256 initialSupply = vm.envOr("INITIAL_SUPPLY", DEFAULT_INITIAL_SUPPLY);
|
||||
bool forwardCanonical = vm.envOr("FORWARD_CANONICAL", false);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
if (vm.envOr("DEPLOY_CUSDT_V2", uint256(1)) != 0) {
|
||||
CompliantUSDTTokenV2 cusdtV2 =
|
||||
new CompliantUSDTTokenV2(initialOperator, admin, initialSupply, forwardCanonical);
|
||||
console.log("cUSDT_V2", address(cusdtV2));
|
||||
console.log("cUSDT_V2_admin", admin);
|
||||
console.log("cUSDT_V2_initialOperator", initialOperator);
|
||||
console.log("cUSDT_V2_forwardCanonical", forwardCanonical);
|
||||
}
|
||||
|
||||
if (vm.envOr("DEPLOY_CUSDC_V2", uint256(1)) != 0) {
|
||||
CompliantUSDCTokenV2 cusdcV2 =
|
||||
new CompliantUSDCTokenV2(initialOperator, admin, initialSupply, forwardCanonical);
|
||||
console.log("cUSDC_V2", address(cusdcV2));
|
||||
console.log("cUSDC_V2_admin", admin);
|
||||
console.log("cUSDC_V2_initialOperator", initialOperator);
|
||||
console.log("cUSDC_V2_forwardCanonical", forwardCanonical);
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
||||
77
script/deploy/DeployCrossChainFlashInfrastructure.s.sol
Normal file
77
script/deploy/DeployCrossChainFlashInfrastructure.s.sol
Normal file
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {UniversalCCIPFlashBridgeAdapter} from "../../contracts/flash/UniversalCCIPFlashBridgeAdapter.sol";
|
||||
import {CrossChainFlashRepayReceiver} from "../../contracts/flash/CrossChainFlashRepayReceiver.sol";
|
||||
import {CrossChainFlashVaultCreditReceiver} from "../../contracts/flash/CrossChainFlashVaultCreditReceiver.sol";
|
||||
|
||||
/**
|
||||
* @title DeployCrossChainFlashInfrastructure
|
||||
* @notice Deploys the Chain 138 cross-chain flash adapter plus both CCIP receivers.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY required
|
||||
* FLASH_UNIVERSAL_CCIP_BRIDGE optional; fallback UNIVERSAL_CCIP_BRIDGE
|
||||
* FLASH_CCIP_ROUTER optional default router for both receivers
|
||||
* FLASH_REPAY_RECEIVER_ROUTER optional; fallback FLASH_CCIP_ROUTER / CCIP_ROUTER*
|
||||
* FLASH_VAULT_CREDIT_ROUTER optional; fallback FLASH_CCIP_ROUTER / CCIP_ROUTER*
|
||||
* CCIP_ROUTER optional fallback
|
||||
* CCIP_ROUTER_ADDRESS optional fallback
|
||||
* CCIP_ROUTER_CHAIN138 optional fallback
|
||||
*
|
||||
* Usage:
|
||||
* forge script script/deploy/DeployCrossChainFlashInfrastructure.s.sol:DeployCrossChainFlashInfrastructure \
|
||||
* --rpc-url $RPC_URL_138 --broadcast --with-gas-price 1000000000 -vvvv
|
||||
*/
|
||||
contract DeployCrossChainFlashInfrastructure is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
|
||||
address universalBridge = vm.envOr("FLASH_UNIVERSAL_CCIP_BRIDGE", address(0));
|
||||
if (universalBridge == address(0)) {
|
||||
universalBridge = vm.envOr("UNIVERSAL_CCIP_BRIDGE", address(0));
|
||||
}
|
||||
require(universalBridge != address(0), "FLASH_UNIVERSAL_CCIP_BRIDGE or UNIVERSAL_CCIP_BRIDGE not set");
|
||||
|
||||
address router = vm.envOr("FLASH_CCIP_ROUTER", address(0));
|
||||
if (router == address(0)) {
|
||||
router = vm.envOr("CCIP_ROUTER", address(0));
|
||||
}
|
||||
if (router == address(0)) {
|
||||
router = vm.envOr("CCIP_ROUTER_ADDRESS", address(0));
|
||||
}
|
||||
if (router == address(0)) {
|
||||
router = vm.envOr("CCIP_ROUTER_CHAIN138", address(0));
|
||||
}
|
||||
require(router != address(0), "FLASH_CCIP_ROUTER or CCIP_ROUTER* not set");
|
||||
|
||||
address repayReceiverRouter = vm.envOr("FLASH_REPAY_RECEIVER_ROUTER", router);
|
||||
address vaultCreditRouter = vm.envOr("FLASH_VAULT_CREDIT_ROUTER", router);
|
||||
|
||||
console.log("Deployer:", deployer);
|
||||
console.log("UniversalCCIPBridge:", universalBridge);
|
||||
console.log("Repay receiver router:", repayReceiverRouter);
|
||||
console.log("Vault credit receiver router:", vaultCreditRouter);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
UniversalCCIPFlashBridgeAdapter adapter = new UniversalCCIPFlashBridgeAdapter(universalBridge);
|
||||
CrossChainFlashRepayReceiver repayReceiver = new CrossChainFlashRepayReceiver(repayReceiverRouter);
|
||||
CrossChainFlashVaultCreditReceiver vaultCreditReceiver =
|
||||
new CrossChainFlashVaultCreditReceiver(vaultCreditRouter);
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("UniversalCCIPFlashBridgeAdapter:", address(adapter));
|
||||
console.log("CrossChainFlashRepayReceiver:", address(repayReceiver));
|
||||
console.log("CrossChainFlashVaultCreditReceiver:", address(vaultCreditReceiver));
|
||||
console.log("Export: CROSS_CHAIN_FLASH_BRIDGE_ADAPTER=%s", vm.toString(address(adapter)));
|
||||
console.log("Export: CROSS_CHAIN_FLASH_REPAY_RECEIVER=%s", vm.toString(address(repayReceiver)));
|
||||
console.log(
|
||||
"Export: CROSS_CHAIN_FLASH_VAULT_CREDIT_RECEIVER=%s",
|
||||
vm.toString(address(vaultCreditReceiver))
|
||||
);
|
||||
}
|
||||
}
|
||||
85
script/deploy/DeployGasCanonicalTokens.s.sol
Normal file
85
script/deploy/DeployGasCanonicalTokens.s.sol
Normal file
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import {CompliantFiatToken} from "../../contracts/tokens/CompliantFiatToken.sol";
|
||||
|
||||
/**
|
||||
* @title DeployGasCanonicalTokens
|
||||
* @notice Deploy Wave 1 gas-native canonical c* tokens on Chain 138.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY (required)
|
||||
* GAS_FAMILY (optional) - deploy one family only: eth_mainnet, eth_l2, bnb, pol, avax, cro, xdai, celo, wemix
|
||||
* GAS_INITIAL_OWNER (optional, defaults to deployer)
|
||||
* GAS_ADMIN (optional, defaults to deployer)
|
||||
* GAS_INITIAL_SUPPLY (optional, defaults to 0)
|
||||
* DEPLOY_GAS_<FAMILY>=0 to skip a family when GAS_FAMILY is unset
|
||||
*/
|
||||
contract DeployGasCanonicalTokens is Script {
|
||||
uint8 internal constant DECIMALS = 18;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address owner = vm.envOr("GAS_INITIAL_OWNER", deployer);
|
||||
address admin = vm.envOr("GAS_ADMIN", deployer);
|
||||
uint256 initialSupply = vm.envOr("GAS_INITIAL_SUPPLY", uint256(0));
|
||||
string memory targetFamily = vm.envOr("GAS_FAMILY", string(""));
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
_deployOne(owner, admin, initialSupply, targetFamily, "eth_mainnet", "DEPLOY_GAS_ETH_MAINNET", "Ethereum Mainnet Gas (Compliant)", "cETH", "ETH");
|
||||
_deployOne(owner, admin, initialSupply, targetFamily, "eth_l2", "DEPLOY_GAS_ETH_L2", "Ethereum L2 Gas (Compliant)", "cETHL2", "ETH");
|
||||
_deployOne(owner, admin, initialSupply, targetFamily, "bnb", "DEPLOY_GAS_BNB", "BNB Gas (Compliant)", "cBNB", "BNB");
|
||||
_deployOne(owner, admin, initialSupply, targetFamily, "pol", "DEPLOY_GAS_POL", "Polygon Gas (Compliant)", "cPOL", "POL");
|
||||
_deployOne(owner, admin, initialSupply, targetFamily, "avax", "DEPLOY_GAS_AVAX", "Avalanche Gas (Compliant)", "cAVAX", "AVAX");
|
||||
_deployOne(owner, admin, initialSupply, targetFamily, "cro", "DEPLOY_GAS_CRO", "Cronos Gas (Compliant)", "cCRO", "CRO");
|
||||
_deployOne(owner, admin, initialSupply, targetFamily, "xdai", "DEPLOY_GAS_XDAI", "Gnosis Gas (Compliant)", "cXDAI", "XDAI");
|
||||
_deployOne(owner, admin, initialSupply, targetFamily, "celo", "DEPLOY_GAS_CELO", "Celo Gas (Compliant)", "cCELO", "CELO");
|
||||
_deployOne(owner, admin, initialSupply, targetFamily, "wemix", "DEPLOY_GAS_WEMIX", "Wemix Gas (Compliant)", "cWEMIX", "WEMIX");
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
|
||||
function _deployOne(
|
||||
address owner,
|
||||
address admin,
|
||||
uint256 initialSupply,
|
||||
string memory targetFamily,
|
||||
string memory familyKey,
|
||||
string memory envFlag,
|
||||
string memory name,
|
||||
string memory symbol,
|
||||
string memory currencyCode
|
||||
) internal {
|
||||
if (!_shouldDeploy(targetFamily, familyKey, envFlag)) return;
|
||||
|
||||
CompliantFiatToken token = new CompliantFiatToken(
|
||||
name,
|
||||
symbol,
|
||||
DECIMALS,
|
||||
currencyCode,
|
||||
owner,
|
||||
admin,
|
||||
initialSupply
|
||||
);
|
||||
|
||||
console.log(symbol, address(token));
|
||||
console.log(" familyKey", familyKey);
|
||||
console.log(" owner", owner);
|
||||
console.log(" admin", admin);
|
||||
console.log(" initialSupply", initialSupply);
|
||||
}
|
||||
|
||||
function _shouldDeploy(
|
||||
string memory targetFamily,
|
||||
string memory familyKey,
|
||||
string memory envFlag
|
||||
) internal view returns (bool) {
|
||||
if (bytes(targetFamily).length != 0) {
|
||||
return keccak256(bytes(targetFamily)) == keccak256(bytes(familyKey));
|
||||
}
|
||||
return vm.envOr(envFlag, uint256(1)) != 0;
|
||||
}
|
||||
}
|
||||
34
script/deploy/DeployQuotePushFlashWorkflowBorrower.s.sol
Normal file
34
script/deploy/DeployQuotePushFlashWorkflowBorrower.s.sol
Normal file
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {QuotePushFlashWorkflowBorrower} from "../../contracts/flash/QuotePushFlashWorkflowBorrower.sol";
|
||||
|
||||
/**
|
||||
* @title DeployQuotePushFlashWorkflowBorrower
|
||||
* @notice Deploy the ERC-3156 quote-push borrower against a trusted flash lender.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY required
|
||||
* QUOTE_PUSH_FLASH_LENDER required
|
||||
*
|
||||
* Usage:
|
||||
* forge script script/deploy/DeployQuotePushFlashWorkflowBorrower.s.sol:DeployQuotePushFlashWorkflowBorrower \
|
||||
* --rpc-url <RPC> --broadcast -vvvv
|
||||
*/
|
||||
contract DeployQuotePushFlashWorkflowBorrower is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address lender = vm.envAddress("QUOTE_PUSH_FLASH_LENDER");
|
||||
address deployer = vm.addr(pk);
|
||||
|
||||
console.log("Deployer:", deployer);
|
||||
console.log("Trusted lender:", lender);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
QuotePushFlashWorkflowBorrower borrower = new QuotePushFlashWorkflowBorrower(lender);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("QuotePushFlashWorkflowBorrower:", address(borrower));
|
||||
}
|
||||
}
|
||||
59
script/deploy/DeploySimpleERC3156FlashVault.s.sol
Normal file
59
script/deploy/DeploySimpleERC3156FlashVault.s.sol
Normal file
@@ -0,0 +1,59 @@
|
||||
// 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 {SimpleERC3156FlashVault} from "../../contracts/flash/SimpleERC3156FlashVault.sol";
|
||||
|
||||
/**
|
||||
* @title DeploySimpleERC3156FlashVault
|
||||
* @notice Deploy ERC-3156 flash vault for Chain 138; optional USDT whitelist + seed transfer from deployer.
|
||||
*
|
||||
* Env (broadcast):
|
||||
* PRIVATE_KEY — required
|
||||
* FLASH_VAULT_OWNER — optional; default: deployer
|
||||
* FLASH_VAULT_FEE_BPS — optional; default: 5 (0.05%)
|
||||
* FLASH_VAULT_TOKEN — optional; token to whitelist (default: official USDT Chain 138)
|
||||
* FLASH_VAULT_SEED_AMOUNT — optional; raw token units to transfer from deployer into vault after deploy (0 = skip)
|
||||
*
|
||||
* Usage:
|
||||
* forge script script/deploy/DeploySimpleERC3156FlashVault.s.sol:DeploySimpleERC3156FlashVault \
|
||||
* --rpc-url $RPC_URL_138 --broadcast -vvvv
|
||||
*/
|
||||
contract DeploySimpleERC3156FlashVault is Script {
|
||||
/// @dev Canonical official USDT (Chain 138) per project config / explorer.
|
||||
address internal constant DEFAULT_USDT_138 = 0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address vaultOwner = vm.envOr("FLASH_VAULT_OWNER", deployer);
|
||||
uint256 feeBps = vm.envOr("FLASH_VAULT_FEE_BPS", uint256(5));
|
||||
address token = vm.envOr("FLASH_VAULT_TOKEN", DEFAULT_USDT_138);
|
||||
uint256 seedAmount = vm.envOr("FLASH_VAULT_SEED_AMOUNT", uint256(0));
|
||||
|
||||
console.log("Deployer:", deployer);
|
||||
console.log("Vault owner:", vaultOwner);
|
||||
console.log("feeBps:", feeBps);
|
||||
console.log("Whitelist token:", token);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
SimpleERC3156FlashVault vault = new SimpleERC3156FlashVault(vaultOwner, feeBps);
|
||||
console.log("SimpleERC3156FlashVault:", address(vault));
|
||||
|
||||
if (vaultOwner == deployer) {
|
||||
vault.setTokenSupported(token, true);
|
||||
console.log("setTokenSupported: true");
|
||||
if (seedAmount > 0) {
|
||||
IERC20(token).transfer(address(vault), seedAmount);
|
||||
console.log("Seeded vault (raw units):", seedAmount);
|
||||
console.log("Vault balance:", IERC20(token).balanceOf(address(vault)));
|
||||
}
|
||||
} else {
|
||||
console.log("Owner != deployer: owner must call setTokenSupported + seed separately.");
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
||||
44
script/deploy/DeploySingleCWToken.s.sol
Normal file
44
script/deploy/DeploySingleCWToken.s.sol
Normal file
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import {CompliantWrappedToken} from "../../contracts/tokens/CompliantWrappedToken.sol";
|
||||
|
||||
/**
|
||||
* @title DeploySingleCWToken
|
||||
* @notice Deploy exactly one CompliantWrappedToken and grant MINTER/BURNER to the bridge.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY (required)
|
||||
* CW_BRIDGE_ADDRESS (required)
|
||||
* CW_TOKEN_NAME (required)
|
||||
* CW_TOKEN_SYMBOL (required)
|
||||
* CW_TOKEN_DECIMALS (optional, default 6)
|
||||
*/
|
||||
contract DeploySingleCWToken is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address admin = vm.addr(pk);
|
||||
address bridge = vm.envAddress("CW_BRIDGE_ADDRESS");
|
||||
string memory tokenName = vm.envString("CW_TOKEN_NAME");
|
||||
string memory tokenSymbol = vm.envString("CW_TOKEN_SYMBOL");
|
||||
uint8 decimals_ = uint8(vm.envOr("CW_TOKEN_DECIMALS", uint256(6)));
|
||||
|
||||
require(bridge != address(0), "CW_BRIDGE_ADDRESS required");
|
||||
require(bytes(tokenName).length != 0, "CW_TOKEN_NAME required");
|
||||
require(bytes(tokenSymbol).length != 0, "CW_TOKEN_SYMBOL required");
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
CompliantWrappedToken token = new CompliantWrappedToken(tokenName, tokenSymbol, decimals_, admin);
|
||||
token.grantRole(token.MINTER_ROLE(), bridge);
|
||||
token.grantRole(token.BURNER_ROLE(), bridge);
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log(tokenSymbol, address(token));
|
||||
console.log(" bridge", bridge);
|
||||
console.log(" bridgeHasMinter", token.hasRole(token.MINTER_ROLE(), bridge));
|
||||
console.log(" bridgeHasBurner", token.hasRole(token.BURNER_ROLE(), bridge));
|
||||
}
|
||||
}
|
||||
73
script/deploy/DeployUSDWPublicWrapVault.s.sol
Normal file
73
script/deploy/DeployUSDWPublicWrapVault.s.sol
Normal file
@@ -0,0 +1,73 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {CompliantWrappedToken} from "../../contracts/tokens/CompliantWrappedToken.sol";
|
||||
import {USDWPublicWrapVault} from "../../contracts/bridge/integration/USDWPublicWrapVault.sol";
|
||||
|
||||
/**
|
||||
* @title DeployUSDWPublicWrapVault
|
||||
* @notice Deploy the native USDW <-> cWUSDW wrap vault for a public chain.
|
||||
* @dev Use with an existing cWUSDW deployment on BSC or a newly deployed cWUSDW on Polygon.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY (required)
|
||||
* USDW_NATIVE_ADDRESS (required) // e.g. dwinUsdWinPublic.chains.56.usdwCurrent
|
||||
* CWUSDW_ADDRESS (required) // cWUSDW contract on the current public chain
|
||||
* USDW_WRAP_ADMIN (optional) // additional admin to grant after deployment
|
||||
* USDW_WRAP_OPERATOR (optional) // reserve seeding operator; default admin/deployer
|
||||
* USDW_WRAP_EMERGENCY_ADMIN (optional)
|
||||
* USDW_WRAP_GRANT_TOKEN_ROLES=1 // grant MINTER_ROLE and BURNER_ROLE on cWUSDW to the vault
|
||||
* USDW_WRAP_STRICT_ADMIN=1 // revoke deployer DEFAULT_ADMIN_ROLE after additional grants
|
||||
*/
|
||||
contract DeployUSDWPublicWrapVault is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address nativeUsdw = vm.envAddress("USDW_NATIVE_ADDRESS");
|
||||
address wrappedUsdw = vm.envAddress("CWUSDW_ADDRESS");
|
||||
address admin = vm.envOr("USDW_WRAP_ADMIN", deployer);
|
||||
address reserveOperator = vm.envOr("USDW_WRAP_OPERATOR", admin);
|
||||
address emergencyAdmin = vm.envOr("USDW_WRAP_EMERGENCY_ADMIN", admin);
|
||||
bool grantTokenRoles = vm.envOr("USDW_WRAP_GRANT_TOKEN_ROLES", uint256(0)) == 1;
|
||||
bool strictAdmin = vm.envOr("USDW_WRAP_STRICT_ADMIN", uint256(0)) == 1;
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
USDWPublicWrapVault vault = new USDWPublicWrapVault(deployer, nativeUsdw, wrappedUsdw);
|
||||
|
||||
if (admin != deployer) {
|
||||
vault.grantRole(vault.DEFAULT_ADMIN_ROLE(), admin);
|
||||
vault.grantRole(vault.RESERVE_OPERATOR_ROLE(), admin);
|
||||
vault.grantRole(vault.EMERGENCY_ADMIN_ROLE(), admin);
|
||||
}
|
||||
if (reserveOperator != admin && reserveOperator != deployer) {
|
||||
vault.grantRole(vault.RESERVE_OPERATOR_ROLE(), reserveOperator);
|
||||
}
|
||||
if (emergencyAdmin != admin && emergencyAdmin != deployer) {
|
||||
vault.grantRole(vault.EMERGENCY_ADMIN_ROLE(), emergencyAdmin);
|
||||
}
|
||||
|
||||
if (grantTokenRoles) {
|
||||
CompliantWrappedToken token = CompliantWrappedToken(wrappedUsdw);
|
||||
token.grantRole(token.MINTER_ROLE(), address(vault));
|
||||
token.grantRole(token.BURNER_ROLE(), address(vault));
|
||||
}
|
||||
|
||||
if (strictAdmin && admin != deployer) {
|
||||
vault.revokeRole(vault.DEFAULT_ADMIN_ROLE(), deployer);
|
||||
}
|
||||
|
||||
console.log("USDWPublicWrapVault", address(vault));
|
||||
console.log(" nativeUsdw", nativeUsdw);
|
||||
console.log(" wrappedUsdw", wrappedUsdw);
|
||||
console.log(" deployer", deployer);
|
||||
console.log(" admin", admin);
|
||||
console.log(" reserveOperator", reserveOperator);
|
||||
console.log(" emergencyAdmin", emergencyAdmin);
|
||||
console.log(" grantTokenRoles", grantTokenRoles);
|
||||
console.log(" strictAdmin", strictAdmin);
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ contract RegisterGRUCompliantTokens is Script {
|
||||
|
||||
_register(registry, vm.envOr("CUSDT_ADDRESS_138", address(0)), "Tether USD (Compliant)", "cUSDT");
|
||||
_register(registry, vm.envOr("CUSDC_ADDRESS_138", address(0)), "USD Coin (Compliant)", "cUSDC");
|
||||
_register(registry, vm.envOr("CAUSDT_ADDRESS_138", address(0)), "Alltra USD Token (Compliant)", "cAUSDT");
|
||||
_register(registry, vm.envOr("CEURC_ADDRESS_138", address(0)), "Euro Coin (Compliant)", "cEURC");
|
||||
_register(registry, vm.envOr("CEURT_ADDRESS_138", address(0)), "Tether EUR (Compliant)", "cEURT");
|
||||
_register(registry, vm.envOr("CGBPC_ADDRESS_138", address(0)), "Pound Sterling (Compliant)", "cGBPC");
|
||||
|
||||
50
script/deploy/RegisterGRUCompliantTokensV2.s.sol
Normal file
50
script/deploy/RegisterGRUCompliantTokensV2.s.sol
Normal file
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import {UniversalAssetRegistry} from "../../contracts/registry/UniversalAssetRegistry.sol";
|
||||
|
||||
/**
|
||||
* @title RegisterGRUCompliantTokensV2
|
||||
* @notice Stage deployed c* V2 contracts in UniversalAssetRegistry using version-aware symbols.
|
||||
* @dev Keeps live V1 symbols untouched while allowing indexers/operators to discover V2 addresses.
|
||||
* Env: UNIVERSAL_ASSET_REGISTRY; optional CUSDT_V2_ADDRESS_138, CUSDC_V2_ADDRESS_138.
|
||||
*/
|
||||
contract RegisterGRUCompliantTokensV2 is Script {
|
||||
function run() external {
|
||||
address registryAddr = vm.envAddress("UNIVERSAL_ASSET_REGISTRY");
|
||||
UniversalAssetRegistry registry = UniversalAssetRegistry(registryAddr);
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
_register(
|
||||
registry,
|
||||
vm.envOr("CUSDT_V2_ADDRESS_138", address(0)),
|
||||
"Tether USD (Compliant V2)",
|
||||
"cUSDT.v2"
|
||||
);
|
||||
_register(
|
||||
registry,
|
||||
vm.envOr("CUSDC_V2_ADDRESS_138", address(0)),
|
||||
"USD Coin (Compliant V2)",
|
||||
"cUSDC.v2"
|
||||
);
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
|
||||
function _register(
|
||||
UniversalAssetRegistry registry,
|
||||
address tokenAddr,
|
||||
string memory name,
|
||||
string memory symbol
|
||||
) internal {
|
||||
if (tokenAddr == address(0)) return;
|
||||
if (registry.isAssetActive(tokenAddr)) {
|
||||
console.log("Skip (already registered):", symbol, vm.toString(tokenAddr));
|
||||
return;
|
||||
}
|
||||
registry.registerGRUCompliantAsset(tokenAddr, name, symbol, 6, "International");
|
||||
console.log("Registered GRU V2:", symbol, vm.toString(tokenAddr));
|
||||
}
|
||||
}
|
||||
56
script/dex/AddLiquidityCUSDWCUSDCV2PoolChain138.s.sol
Normal file
56
script/dex/AddLiquidityCUSDWCUSDCV2PoolChain138.s.sol
Normal file
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {DODOPMMIntegration} from "../../contracts/dex/DODOPMMIntegration.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
/**
|
||||
* @title AddLiquidityCUSDWCUSDCV2PoolChain138
|
||||
* @notice Add liquidity to the Chain 138 cUSDW / cUSDC_V2 DODO PMM pool.
|
||||
* @dev Env: PRIVATE_KEY, DODO_PMM_INTEGRATION or DODO_PMM_INTEGRATION_ADDRESS,
|
||||
* CUSDW_ADDRESS_138, CUSDC_V2_ADDRESS_138, POOL_CUSDWCUSDCV2,
|
||||
* ADD_LIQUIDITY_CUSDWCUSDCV2_BASE, ADD_LIQUIDITY_CUSDWCUSDCV2_QUOTE.
|
||||
* Optional fallback amounts:
|
||||
* - ADD_LIQUIDITY_BASE_AMOUNT
|
||||
* - ADD_LIQUIDITY_QUOTE_AMOUNT
|
||||
* Optional: NEXT_NONCE.
|
||||
*/
|
||||
contract AddLiquidityCUSDWCUSDCV2PoolChain138 is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address integrationAddr = vm.envAddress("DODO_PMM_INTEGRATION");
|
||||
if (integrationAddr == address(0)) {
|
||||
integrationAddr = vm.envAddress("DODO_PMM_INTEGRATION_ADDRESS");
|
||||
}
|
||||
require(integrationAddr != address(0), "DODO_PMM_INTEGRATION not set");
|
||||
|
||||
address cusdw = vm.envAddress("CUSDW_ADDRESS_138");
|
||||
address cusdcV2 = vm.envAddress("CUSDC_V2_ADDRESS_138");
|
||||
address pool = vm.envAddress("POOL_CUSDWCUSDCV2");
|
||||
require(cusdw != address(0), "CUSDW_ADDRESS_138 not set");
|
||||
require(cusdcV2 != address(0), "CUSDC_V2_ADDRESS_138 not set");
|
||||
require(pool != address(0), "POOL_CUSDWCUSDCV2 not set");
|
||||
|
||||
uint256 defaultBase = vm.envOr("ADD_LIQUIDITY_BASE_AMOUNT", uint256(0));
|
||||
uint256 defaultQuote = vm.envOr("ADD_LIQUIDITY_QUOTE_AMOUNT", uint256(0));
|
||||
uint256 baseAmount = vm.envOr("ADD_LIQUIDITY_CUSDWCUSDCV2_BASE", defaultBase);
|
||||
uint256 quoteAmount = vm.envOr("ADD_LIQUIDITY_CUSDWCUSDCV2_QUOTE", defaultQuote);
|
||||
require(baseAmount > 0, "ADD_LIQUIDITY_CUSDWCUSDCV2_BASE not set");
|
||||
require(quoteAmount > 0, "ADD_LIQUIDITY_CUSDWCUSDCV2_QUOTE not set");
|
||||
|
||||
address deployer = vm.addr(pk);
|
||||
uint64 nextNonce = uint64(vm.envOr("NEXT_NONCE", uint256(0)));
|
||||
if (nextNonce > 0) {
|
||||
vm.setNonce(deployer, nextNonce);
|
||||
}
|
||||
|
||||
DODOPMMIntegration integration = DODOPMMIntegration(integrationAddr);
|
||||
vm.startBroadcast(pk);
|
||||
IERC20(cusdw).approve(address(integration), type(uint256).max);
|
||||
IERC20(cusdcV2).approve(address(integration), type(uint256).max);
|
||||
integration.addLiquidity(pool, baseAmount, quoteAmount);
|
||||
console.log("Added liquidity to cUSDW/cUSDC_V2 pool:", pool);
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,10 @@ import {DODOPMMIntegration} from "../../contracts/dex/DODOPMMIntegration.sol";
|
||||
* @dev Requires caller to have POOL_MANAGER_ROLE. Run with --broadcast.
|
||||
*/
|
||||
contract CreateCUSDCUSDCPool is Script {
|
||||
uint256 constant LP_FEE_BPS = 3;
|
||||
uint256 constant INITIAL_PRICE_1E18 = 1e18;
|
||||
uint256 constant K_50PCT = 0.5e18;
|
||||
bool constant USE_TWAP = true;
|
||||
uint256 constant DEFAULT_LP_FEE_BPS = 10;
|
||||
uint256 constant DEFAULT_INITIAL_PRICE_1E18 = 1e18;
|
||||
uint256 constant DEFAULT_K_0PCT = 0;
|
||||
bool constant DEFAULT_USE_TWAP = false;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
@@ -30,12 +30,16 @@ contract CreateCUSDCUSDCPool is Script {
|
||||
}
|
||||
|
||||
DODOPMMIntegration integration = DODOPMMIntegration(integrationAddr);
|
||||
uint256 lpFeeBps = vm.envOr("DODO_LP_FEE_BPS", DEFAULT_LP_FEE_BPS);
|
||||
uint256 initialPrice = vm.envOr("DODO_INITIAL_PRICE_1E18", DEFAULT_INITIAL_PRICE_1E18);
|
||||
uint256 k = vm.envOr("DODO_K_FACTOR_1E18", DEFAULT_K_0PCT);
|
||||
bool useTwap = vm.envOr("DODO_ENABLE_TWAP", DEFAULT_USE_TWAP);
|
||||
vm.startBroadcast(pk);
|
||||
address pool = integration.createCUSDCUSDCPool(
|
||||
LP_FEE_BPS,
|
||||
INITIAL_PRICE_1E18,
|
||||
K_50PCT,
|
||||
USE_TWAP
|
||||
lpFeeBps,
|
||||
initialPrice,
|
||||
k,
|
||||
useTwap
|
||||
);
|
||||
console.log("cUSDC/USDC pool created at:", pool);
|
||||
vm.stopBroadcast();
|
||||
|
||||
@@ -10,10 +10,10 @@ import {DODOPMMIntegration} from "../../contracts/dex/DODOPMMIntegration.sol";
|
||||
* @dev Requires caller to have POOL_MANAGER_ROLE. Run with --broadcast.
|
||||
*/
|
||||
contract CreateCUSDTCUSDCPool is Script {
|
||||
uint256 constant LP_FEE_BPS = 3;
|
||||
uint256 constant INITIAL_PRICE_1E18 = 1e18;
|
||||
uint256 constant K_50PCT = 0.5e18;
|
||||
bool constant USE_TWAP = true;
|
||||
uint256 constant DEFAULT_LP_FEE_BPS = 10;
|
||||
uint256 constant DEFAULT_INITIAL_PRICE_1E18 = 1e18;
|
||||
uint256 constant DEFAULT_K_0PCT = 0;
|
||||
bool constant DEFAULT_USE_TWAP = false;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
@@ -30,12 +30,16 @@ contract CreateCUSDTCUSDCPool is Script {
|
||||
}
|
||||
|
||||
DODOPMMIntegration integration = DODOPMMIntegration(integrationAddr);
|
||||
uint256 lpFeeBps = vm.envOr("DODO_LP_FEE_BPS", DEFAULT_LP_FEE_BPS);
|
||||
uint256 initialPrice = vm.envOr("DODO_INITIAL_PRICE_1E18", DEFAULT_INITIAL_PRICE_1E18);
|
||||
uint256 k = vm.envOr("DODO_K_FACTOR_1E18", DEFAULT_K_0PCT);
|
||||
bool useTwap = vm.envOr("DODO_ENABLE_TWAP", DEFAULT_USE_TWAP);
|
||||
vm.startBroadcast(pk);
|
||||
address pool = integration.createCUSDTCUSDCPool(
|
||||
LP_FEE_BPS,
|
||||
INITIAL_PRICE_1E18,
|
||||
K_50PCT,
|
||||
USE_TWAP
|
||||
lpFeeBps,
|
||||
initialPrice,
|
||||
k,
|
||||
useTwap
|
||||
);
|
||||
console.log("cUSDT/cUSDC pool created at:", pool);
|
||||
vm.stopBroadcast();
|
||||
|
||||
@@ -10,10 +10,10 @@ import {DODOPMMIntegration} from "../../contracts/dex/DODOPMMIntegration.sol";
|
||||
* @dev Requires caller to have POOL_MANAGER_ROLE. Run with --broadcast.
|
||||
*/
|
||||
contract CreateCUSDTUSDTPool is Script {
|
||||
uint256 constant LP_FEE_BPS = 3;
|
||||
uint256 constant INITIAL_PRICE_1E18 = 1e18;
|
||||
uint256 constant K_50PCT = 0.5e18;
|
||||
bool constant USE_TWAP = true;
|
||||
uint256 constant DEFAULT_LP_FEE_BPS = 10;
|
||||
uint256 constant DEFAULT_INITIAL_PRICE_1E18 = 1e18;
|
||||
uint256 constant DEFAULT_K_0PCT = 0;
|
||||
bool constant DEFAULT_USE_TWAP = false;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
@@ -30,12 +30,16 @@ contract CreateCUSDTUSDTPool is Script {
|
||||
}
|
||||
|
||||
DODOPMMIntegration integration = DODOPMMIntegration(integrationAddr);
|
||||
uint256 lpFeeBps = vm.envOr("DODO_LP_FEE_BPS", DEFAULT_LP_FEE_BPS);
|
||||
uint256 initialPrice = vm.envOr("DODO_INITIAL_PRICE_1E18", DEFAULT_INITIAL_PRICE_1E18);
|
||||
uint256 k = vm.envOr("DODO_K_FACTOR_1E18", DEFAULT_K_0PCT);
|
||||
bool useTwap = vm.envOr("DODO_ENABLE_TWAP", DEFAULT_USE_TWAP);
|
||||
vm.startBroadcast(pk);
|
||||
address pool = integration.createCUSDTUSDTPool(
|
||||
LP_FEE_BPS,
|
||||
INITIAL_PRICE_1E18,
|
||||
K_50PCT,
|
||||
USE_TWAP
|
||||
lpFeeBps,
|
||||
initialPrice,
|
||||
k,
|
||||
useTwap
|
||||
);
|
||||
console.log("cUSDT/USDT pool created at:", pool);
|
||||
vm.stopBroadcast();
|
||||
|
||||
63
script/dex/CreateCUSDWCUSDCV2Pool.s.sol
Normal file
63
script/dex/CreateCUSDWCUSDCV2Pool.s.sol
Normal file
@@ -0,0 +1,63 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {DODOPMMIntegration} from "../../contracts/dex/DODOPMMIntegration.sol";
|
||||
|
||||
/**
|
||||
* @title CreateCUSDWCUSDCV2Pool
|
||||
* @notice Create a Chain 138 DODO PMM pool for cUSDW / cUSDC_V2 using the generic createPool path.
|
||||
* @dev Assumes repo-native D-WIN-aligned cUSDW on Chain 138 and staged cUSDC V2 as the quote leg.
|
||||
* Env: PRIVATE_KEY, DODO_PMM_INTEGRATION or DODO_PMM_INTEGRATION_ADDRESS,
|
||||
* CUSDW_ADDRESS_138, CUSDC_V2_ADDRESS_138.
|
||||
* Optional params:
|
||||
* - DODO_LP_FEE_BPS (default 3)
|
||||
* - DODO_INITIAL_PRICE_1E18 (default 1e18)
|
||||
* - DODO_K_FACTOR_1E18 (default 0.5e18)
|
||||
* - DODO_ENABLE_TWAP (default true)
|
||||
* - NEXT_NONCE
|
||||
*/
|
||||
contract CreateCUSDWCUSDCV2Pool is Script {
|
||||
uint256 constant DEFAULT_LP_FEE_BPS = 3;
|
||||
uint256 constant DEFAULT_INITIAL_PRICE_1E18 = 1e18;
|
||||
uint256 constant DEFAULT_K_50PCT = 0.5e18;
|
||||
bool constant DEFAULT_USE_TWAP = true;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address integrationAddr = vm.envAddress("DODO_PMM_INTEGRATION");
|
||||
if (integrationAddr == address(0)) {
|
||||
integrationAddr = vm.envAddress("DODO_PMM_INTEGRATION_ADDRESS");
|
||||
}
|
||||
require(integrationAddr != address(0), "DODO_PMM_INTEGRATION not set");
|
||||
|
||||
address cusdw = vm.envAddress("CUSDW_ADDRESS_138");
|
||||
address cusdcV2 = vm.envAddress("CUSDC_V2_ADDRESS_138");
|
||||
require(cusdw != address(0), "CUSDW_ADDRESS_138 not set");
|
||||
require(cusdcV2 != address(0), "CUSDC_V2_ADDRESS_138 not set");
|
||||
|
||||
uint256 lpFeeBps = vm.envOr("DODO_LP_FEE_BPS", DEFAULT_LP_FEE_BPS);
|
||||
uint256 initialPrice = vm.envOr("DODO_INITIAL_PRICE_1E18", DEFAULT_INITIAL_PRICE_1E18);
|
||||
uint256 kFactor = vm.envOr("DODO_K_FACTOR_1E18", DEFAULT_K_50PCT);
|
||||
bool useTwap = vm.envOr("DODO_ENABLE_TWAP", DEFAULT_USE_TWAP);
|
||||
|
||||
address deployer = vm.addr(pk);
|
||||
uint64 nextNonce = uint64(vm.envOr("NEXT_NONCE", uint256(0)));
|
||||
if (nextNonce > 0) {
|
||||
vm.setNonce(deployer, nextNonce);
|
||||
}
|
||||
|
||||
DODOPMMIntegration integration = DODOPMMIntegration(integrationAddr);
|
||||
vm.startBroadcast(pk);
|
||||
address pool = integration.createPool(
|
||||
cusdw,
|
||||
cusdcV2,
|
||||
lpFeeBps,
|
||||
initialPrice,
|
||||
kFactor,
|
||||
useTwap
|
||||
);
|
||||
console.log("cUSDW/cUSDC_V2 pool created at:", pool);
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
||||
64
script/flash/TestOneUSDTFlash.s.sol
Normal file
64
script/flash/TestOneUSDTFlash.s.sol
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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 {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
|
||||
import {SimpleERC3156FlashVault} from "../../contracts/flash/SimpleERC3156FlashVault.sol";
|
||||
import {MinimalERC3156FlashBorrower} from "../../contracts/flash/MinimalERC3156FlashBorrower.sol";
|
||||
|
||||
/**
|
||||
* @title TestOneUSDTFlash
|
||||
* @notice Live / fork: deploy minimal borrower, pre-fund fee, execute 1 USDT flash against an existing vault.
|
||||
*
|
||||
* Env:
|
||||
* PRIVATE_KEY — deployer (pays gas; seeds fee to borrower)
|
||||
* FLASH_VAULT — deployed SimpleERC3156FlashVault
|
||||
* FLASH_VAULT_TOKEN — optional; default official USDT Chain 138
|
||||
* FLASH_TEST_AMOUNT — optional raw amount (default 1e6 = 1 USDT with 6 decimals)
|
||||
*
|
||||
* Usage:
|
||||
* forge script script/flash/TestOneUSDTFlash.s.sol:TestOneUSDTFlash --rpc-url $RPC_URL_138 --broadcast -vvvv
|
||||
*/
|
||||
contract TestOneUSDTFlash is Script {
|
||||
address internal constant DEFAULT_USDT_138 = 0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address vaultAddr = vm.envAddress("FLASH_VAULT");
|
||||
address token = vm.envOr("FLASH_VAULT_TOKEN", DEFAULT_USDT_138);
|
||||
uint256 amount = vm.envOr("FLASH_TEST_AMOUNT", uint256(1_000_000)); // 1 USDT (6 dp)
|
||||
|
||||
SimpleERC3156FlashVault vault = SimpleERC3156FlashVault(vaultAddr);
|
||||
require(!vault.borrowerAllowlistEnabled() || vault.owner() == deployer, "allowlist on: deployer must be vault owner");
|
||||
|
||||
uint256 fee = vault.previewFlashFee(token, amount);
|
||||
uint256 vaultBal = IERC20(token).balanceOf(vaultAddr);
|
||||
|
||||
console.log("Deployer:", deployer);
|
||||
console.log("Vault:", vaultAddr);
|
||||
console.log("Token:", token);
|
||||
console.log("Amount (raw):", amount);
|
||||
console.log("Fee (raw):", fee);
|
||||
console.log("Vault token balance (raw):", vaultBal);
|
||||
require(vaultBal >= amount, "vault liquidity < amount");
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
MinimalERC3156FlashBorrower borrower = new MinimalERC3156FlashBorrower(vaultAddr);
|
||||
console.log("MinimalERC3156FlashBorrower:", address(borrower));
|
||||
|
||||
if (vault.borrowerAllowlistEnabled()) {
|
||||
vault.setBorrowerApproved(address(borrower), true);
|
||||
}
|
||||
|
||||
// Callback must return amount+fee; vault only credits `amount` before callback — need `fee` pre-funded on borrower.
|
||||
IERC20(token).transfer(address(borrower), fee);
|
||||
vault.flashLoan(IERC3156FlashBorrower(address(borrower)), token, amount, "");
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("Done. Vault balance after (raw):", IERC20(token).balanceOf(vaultAddr));
|
||||
}
|
||||
}
|
||||
69
script/flash/TestScaledFlash.s.sol
Normal file
69
script/flash/TestScaledFlash.s.sol
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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 {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
|
||||
import {SimpleERC3156FlashVault} from "../../contracts/flash/SimpleERC3156FlashVault.sol";
|
||||
import {MinimalERC3156FlashBorrower} from "../../contracts/flash/MinimalERC3156FlashBorrower.sol";
|
||||
|
||||
/**
|
||||
* @title TestScaledFlash
|
||||
* @notice Sequential flashes: 1, 10, 100, 1000 USDT (6 decimals) against FLASH_VAULT. One borrower, fee prefunded per step.
|
||||
*
|
||||
* Env: PRIVATE_KEY, FLASH_VAULT; optional FLASH_VAULT_TOKEN (default official USDT 138).
|
||||
*
|
||||
* If vault.borrowerAllowlistEnabled(), deployer must be vault owner so the script can setBorrowerApproved.
|
||||
*
|
||||
* Usage:
|
||||
* forge script script/flash/TestScaledFlash.s.sol:TestScaledFlash --rpc-url $RPC_URL_138 --broadcast -vvvv
|
||||
*/
|
||||
contract TestScaledFlash is Script {
|
||||
address internal constant DEFAULT_USDT_138 = 0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1;
|
||||
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address vaultAddr = vm.envAddress("FLASH_VAULT");
|
||||
address token = vm.envOr("FLASH_VAULT_TOKEN", DEFAULT_USDT_138);
|
||||
|
||||
SimpleERC3156FlashVault vault = SimpleERC3156FlashVault(vaultAddr);
|
||||
require(!vault.borrowerAllowlistEnabled() || vault.owner() == deployer, "allowlist on: deployer must be vault owner");
|
||||
|
||||
uint256[4] memory amounts = [
|
||||
uint256(1_000_000),
|
||||
uint256(10_000_000),
|
||||
uint256(100_000_000),
|
||||
uint256(1_000_000_000)
|
||||
];
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
|
||||
MinimalERC3156FlashBorrower borrower = new MinimalERC3156FlashBorrower(vaultAddr);
|
||||
console.log("Borrower:", address(borrower));
|
||||
|
||||
if (vault.borrowerAllowlistEnabled()) {
|
||||
vault.setBorrowerApproved(address(borrower), true);
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < amounts.length; i++) {
|
||||
uint256 amount = amounts[i];
|
||||
uint256 fee = vault.previewFlashFee(token, amount);
|
||||
uint256 vb = IERC20(token).balanceOf(vaultAddr);
|
||||
require(vb >= amount, "vault liquidity < amount");
|
||||
|
||||
console.log("--- step", i + 1);
|
||||
console.log("amount (raw):", amount);
|
||||
console.log("fee (raw):", fee);
|
||||
console.log("vault before (raw):", vb);
|
||||
|
||||
IERC20(token).transfer(address(borrower), fee);
|
||||
vault.flashLoan(IERC3156FlashBorrower(address(borrower)), token, amount, "");
|
||||
|
||||
console.log("vault after (raw):", IERC20(token).balanceOf(vaultAddr));
|
||||
}
|
||||
|
||||
vm.stopBroadcast();
|
||||
console.log("Scaled ladder complete.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user