Files
smom-dbis-138/contracts/bridge/integration/CWAssetReserveVerifier.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

248 lines
9.9 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 ICWMultiTokenBridgeL1AccountingV2 {
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 IOwnableLikeV2 {
function owner() external view returns (address);
}
interface IBalanceOfAsset {
function balanceOf(address account) external view returns (uint256);
}
/**
* @title CWAssetReserveVerifier
* @notice Generic reserve verifier for canonical c* -> mirrored cW* bridge lanes.
* @dev Uses bridge accounting plus optional vault and reserve-system checks so one verifier
* can cover stable, monetary-unit, and gas-native families behind a single L1 bridge.
*/
contract CWAssetReserveVerifier is AccessControl, ICWReserveVerifier {
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
uint256 public constant HARD_THRESHOLD_BPS = 10_000;
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;
}
ICWMultiTokenBridgeL1AccountingV2 public bridge;
address public assetVault;
IReserveSystem public reserveSystem;
mapping(address => TokenConfig) public tokenConfigs;
event BridgeUpdated(address indexed newBridge);
event AssetVaultUpdated(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 ReserveAssetRequired();
error UnsupportedCanonicalToken();
error BridgeTokenPaused();
error EscrowInvariantViolation();
error ChainOutstandingCapExceeded();
error TokenOwnerMismatch();
error VaultBackingInsufficient();
error ReserveSystemBackingInsufficient();
constructor(
address admin,
address bridge_,
address assetVault_,
address reserveSystem_
) {
if (admin == address(0) || bridge_ == address(0)) revert ZeroAddress();
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(OPERATOR_ROLE, admin);
bridge = ICWMultiTokenBridgeL1AccountingV2(bridge_);
assetVault = assetVault_;
reserveSystem = IReserveSystem(reserveSystem_);
}
function setBridge(address bridge_) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (bridge_ == address(0)) revert ZeroAddress();
bridge = ICWMultiTokenBridgeL1AccountingV2(bridge_);
emit BridgeUpdated(bridge_);
}
function setAssetVault(address assetVault_) external onlyRole(DEFAULT_ADMIN_ROLE) {
assetVault = assetVault_;
emit AssetVaultUpdated(assetVault_);
}
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) && assetVault == address(0)) revert VaultRequired();
if (requireReserveSystemBalance && address(reserveSystem) == address(0)) revert ReserveSystemRequired();
if ((requireVaultBacking || requireReserveSystemBalance) && reserveAsset == address(0)) revert ReserveAssetRequired();
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.requireTokenOwnerMatchVault) {
try IOwnableLikeV2(canonicalToken).owner() returns (address tokenOwner) {
status.tokenOwnerMatchesVault = tokenOwner == assetVault;
} catch {
status.tokenOwnerMatchesVault = false;
}
}
if (config.requireVaultBacking) {
try IBalanceOfAsset(config.reserveAsset).balanceOf(assetVault) returns (uint256 reserveBalance) {
status.vaultReserveBalance = reserveBalance;
if (status.canonicalTotalSupply > 0) {
status.vaultBackingRatio = (reserveBalance * HARD_THRESHOLD_BPS) / status.canonicalTotalSupply;
} else {
status.vaultBackingRatio = HARD_THRESHOLD_BPS;
}
status.vaultAdequate =
status.vaultBackingRatio >= HARD_THRESHOLD_BPS && reserveBalance >= status.canonicalTotalSupply;
} catch {
status.vaultAdequate = false;
}
}
status.passes =
status.supportedCanonicalToken &&
!status.bridgePaused &&
status.escrowSufficient &&
status.chainCapSufficient &&
status.tokenOwnerMatchesVault &&
status.vaultAdequate &&
status.reserveSystemAdequate;
}
}