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:
defiQUG
2026-04-07 23:40:52 -07:00
parent 0fb7bba07b
commit 76aa419320
289 changed files with 28367 additions and 824 deletions

View 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));
}
}

View 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");
}
}

View File

@@ -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");
}
}

View 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);
}
}

View File

@@ -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));
}
}

View 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();
}
}

View 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))
);
}
}

View 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;
}
}

View 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));
}
}

View 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();
}
}

View 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));
}
}

View 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();
}
}

View File

@@ -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");

View 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));
}
}