Files
smom-dbis-138/contracts/bridge/integration/CWReserveVerifier.sol
defiQUG 76aa419320 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
2026-04-07 23:40:52 -07:00

315 lines
12 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../reserve/IReserveSystem.sol";
import "./ICWReserveVerifier.sol";
interface ICWMultiTokenBridgeL1Accounting {
function supportedCanonicalToken(address token) external view returns (bool);
function paused(address token) external view returns (bool);
function lockedBalance(address token) external view returns (uint256);
function totalOutstanding(address token) external view returns (uint256);
function outstandingMinted(address token, uint64 destinationChainSelector) external view returns (uint256);
function maxOutstanding(address token, uint64 destinationChainSelector) external view returns (uint256);
}
interface IOwnableLike {
function owner() external view returns (address);
}
interface IStablecoinReserveVaultLike {
function compliantUSDT() external view returns (address);
function compliantUSDC() external view returns (address);
function getBackingRatio(address token) external view returns (
uint256 reserveBalance,
uint256 tokenSupply,
uint256 backingRatio
);
function checkReserveAdequacy() external view returns (bool usdtAdequate, bool usdcAdequate);
}
/**
* @title CWReserveVerifier
* @notice Verifies bridge escrow and canonical reserve backing before allowing new cW minting.
* @dev Intended for cUSDC/cUSDT -> cWUSDC/cWUSDT hard-peg flows on Chain 138.
*/
contract CWReserveVerifier is AccessControl, ICWReserveVerifier {
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
uint256 public constant HARD_THRESHOLD_BPS = 10000;
struct TokenConfig {
bool enabled;
address reserveAsset;
bool requireVaultBacking;
bool requireReserveSystemBalance;
bool requireTokenOwnerMatchVault;
}
struct VerificationStatus {
bool supportedCanonicalToken;
bool bridgePaused;
bool escrowSufficient;
bool chainCapSufficient;
bool reserveSystemAdequate;
bool vaultAdequate;
bool tokenOwnerMatchesVault;
bool passes;
uint256 lockedBalanceAmount;
uint256 totalOutstandingAmount;
uint256 chainOutstandingAmount;
uint256 maxOutstandingAmount;
uint256 canonicalTotalSupply;
uint256 reserveSystemBalance;
uint256 vaultReserveBalance;
uint256 vaultBackingRatio;
}
ICWMultiTokenBridgeL1Accounting public bridge;
IStablecoinReserveVaultLike public stablecoinReserveVault;
IReserveSystem public reserveSystem;
mapping(address => TokenConfig) public tokenConfigs;
event BridgeUpdated(address indexed newBridge);
event StablecoinReserveVaultUpdated(address indexed newVault);
event ReserveSystemUpdated(address indexed newReserveSystem);
event TokenConfigured(
address indexed canonicalToken,
address indexed reserveAsset,
bool requireVaultBacking,
bool requireReserveSystemBalance,
bool requireTokenOwnerMatchVault
);
event TokenDisabled(address indexed canonicalToken);
error ZeroAddress();
error TokenNotConfigured();
error VaultRequired();
error ReserveSystemRequired();
error UnsupportedCanonicalToken();
error BridgeTokenPaused();
error EscrowInvariantViolation();
error ChainOutstandingCapExceeded();
error TokenOwnerMismatch();
error VaultBackingInsufficient();
error ReserveSystemBackingInsufficient();
constructor(
address admin,
address bridge_,
address stablecoinReserveVault_,
address reserveSystem_
) {
if (admin == address(0) || bridge_ == address(0)) {
revert ZeroAddress();
}
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(OPERATOR_ROLE, admin);
bridge = ICWMultiTokenBridgeL1Accounting(bridge_);
stablecoinReserveVault = IStablecoinReserveVaultLike(stablecoinReserveVault_);
reserveSystem = IReserveSystem(reserveSystem_);
}
function setBridge(address bridge_) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (bridge_ == address(0)) {
revert ZeroAddress();
}
bridge = ICWMultiTokenBridgeL1Accounting(bridge_);
emit BridgeUpdated(bridge_);
}
function setStablecoinReserveVault(address stablecoinReserveVault_) external onlyRole(DEFAULT_ADMIN_ROLE) {
stablecoinReserveVault = IStablecoinReserveVaultLike(stablecoinReserveVault_);
emit StablecoinReserveVaultUpdated(stablecoinReserveVault_);
}
function setReserveSystem(address reserveSystem_) external onlyRole(DEFAULT_ADMIN_ROLE) {
reserveSystem = IReserveSystem(reserveSystem_);
emit ReserveSystemUpdated(reserveSystem_);
}
function configureToken(
address canonicalToken,
address reserveAsset,
bool requireVaultBacking,
bool requireReserveSystemBalance,
bool requireTokenOwnerMatchVault
) external onlyRole(OPERATOR_ROLE) {
if (canonicalToken == address(0)) {
revert ZeroAddress();
}
if ((requireVaultBacking || requireTokenOwnerMatchVault) && address(stablecoinReserveVault) == address(0)) {
revert VaultRequired();
}
if (requireReserveSystemBalance && address(reserveSystem) == address(0)) {
revert ReserveSystemRequired();
}
if (requireReserveSystemBalance && reserveAsset == address(0)) {
revert ZeroAddress();
}
tokenConfigs[canonicalToken] = TokenConfig({
enabled: true,
reserveAsset: reserveAsset,
requireVaultBacking: requireVaultBacking,
requireReserveSystemBalance: requireReserveSystemBalance,
requireTokenOwnerMatchVault: requireTokenOwnerMatchVault
});
emit TokenConfigured(
canonicalToken,
reserveAsset,
requireVaultBacking,
requireReserveSystemBalance,
requireTokenOwnerMatchVault
);
}
function disableToken(address canonicalToken) external onlyRole(OPERATOR_ROLE) {
delete tokenConfigs[canonicalToken];
emit TokenDisabled(canonicalToken);
}
function verifyLock(
address canonicalToken,
uint64 destinationChainSelector,
uint256
) external view override returns (bool verified) {
TokenConfig memory config = tokenConfigs[canonicalToken];
if (!config.enabled) {
revert TokenNotConfigured();
}
VerificationStatus memory status = _buildStatus(canonicalToken, destinationChainSelector, config);
if (!status.supportedCanonicalToken) {
revert UnsupportedCanonicalToken();
}
if (status.bridgePaused) {
revert BridgeTokenPaused();
}
if (!status.escrowSufficient) {
revert EscrowInvariantViolation();
}
if (!status.chainCapSufficient) {
revert ChainOutstandingCapExceeded();
}
if (config.requireTokenOwnerMatchVault && !status.tokenOwnerMatchesVault) {
revert TokenOwnerMismatch();
}
if (config.requireVaultBacking && !status.vaultAdequate) {
revert VaultBackingInsufficient();
}
if (config.requireReserveSystemBalance && !status.reserveSystemAdequate) {
revert ReserveSystemBackingInsufficient();
}
return true;
}
function getVerificationStatus(
address canonicalToken,
uint64 destinationChainSelector
) external view returns (VerificationStatus memory status) {
TokenConfig memory config = tokenConfigs[canonicalToken];
if (!config.enabled) {
revert TokenNotConfigured();
}
return _buildStatus(canonicalToken, destinationChainSelector, config);
}
function _buildStatus(
address canonicalToken,
uint64 destinationChainSelector,
TokenConfig memory config
) internal view returns (VerificationStatus memory status) {
status.supportedCanonicalToken = bridge.supportedCanonicalToken(canonicalToken);
status.bridgePaused = bridge.paused(canonicalToken);
status.lockedBalanceAmount = bridge.lockedBalance(canonicalToken);
status.totalOutstandingAmount = bridge.totalOutstanding(canonicalToken);
status.chainOutstandingAmount = bridge.outstandingMinted(canonicalToken, destinationChainSelector);
status.maxOutstandingAmount = bridge.maxOutstanding(canonicalToken, destinationChainSelector);
status.escrowSufficient = status.lockedBalanceAmount >= status.totalOutstandingAmount;
status.chainCapSufficient = status.maxOutstandingAmount == 0
|| status.chainOutstandingAmount <= status.maxOutstandingAmount;
status.canonicalTotalSupply = IERC20(canonicalToken).totalSupply();
status.reserveSystemAdequate = !config.requireReserveSystemBalance;
if (config.requireReserveSystemBalance) {
status.reserveSystemBalance = reserveSystem.getReserveBalance(config.reserveAsset);
status.reserveSystemAdequate = status.reserveSystemBalance >= status.canonicalTotalSupply;
}
status.vaultAdequate = !config.requireVaultBacking;
status.tokenOwnerMatchesVault = !config.requireTokenOwnerMatchVault;
if (config.requireVaultBacking || config.requireTokenOwnerMatchVault) {
address vaultAddress = address(stablecoinReserveVault);
if (config.requireTokenOwnerMatchVault) {
try IOwnableLike(canonicalToken).owner() returns (address tokenOwner) {
status.tokenOwnerMatchesVault = tokenOwner == vaultAddress;
} catch {
status.tokenOwnerMatchesVault = false;
}
}
if (config.requireVaultBacking) {
bool tokenTrackedByVault;
bool tokenAdequate;
try stablecoinReserveVault.compliantUSDT() returns (address compliantUSDT) {
if (canonicalToken == compliantUSDT) {
tokenTrackedByVault = true;
try stablecoinReserveVault.checkReserveAdequacy() returns (bool usdtAdequate, bool) {
tokenAdequate = usdtAdequate;
} catch {}
}
} catch {}
if (!tokenTrackedByVault) {
try stablecoinReserveVault.compliantUSDC() returns (address compliantUSDC) {
if (canonicalToken == compliantUSDC) {
tokenTrackedByVault = true;
try stablecoinReserveVault.checkReserveAdequacy() returns (bool, bool usdcAdequate) {
tokenAdequate = usdcAdequate;
} catch {}
}
} catch {}
}
if (tokenTrackedByVault) {
try stablecoinReserveVault.getBackingRatio(canonicalToken) returns (
uint256 reserveBalance,
uint256,
uint256 backingRatio
) {
status.vaultReserveBalance = reserveBalance;
status.vaultBackingRatio = backingRatio;
status.vaultAdequate = tokenAdequate
&& backingRatio >= HARD_THRESHOLD_BPS
&& reserveBalance >= status.canonicalTotalSupply;
} catch {
status.vaultAdequate = false;
}
} else {
status.vaultAdequate = false;
}
}
}
status.passes =
status.supportedCanonicalToken
&& !status.bridgePaused
&& status.escrowSufficient
&& status.chainCapSufficient
&& status.tokenOwnerMatchesVault
&& status.vaultAdequate
&& status.reserveSystemAdequate;
}
}