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

181 lines
6.9 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IRouterClient} from "../../contracts/ccip/IRouterClient.sol";
import {CWMultiTokenBridgeL1} from "../../contracts/bridge/CWMultiTokenBridgeL1.sol";
import {CWAssetReserveVerifier} from "../../contracts/bridge/integration/CWAssetReserveVerifier.sol";
contract MockCanonicalAssetToken is ERC20 {
address internal _owner;
constructor(string memory name_, string memory symbol_, address initialOwner) ERC20(name_, symbol_) {
_owner = initialOwner;
}
function decimals() public pure override returns (uint8) {
return 18;
}
function owner() external view returns (address) {
return _owner;
}
function setOwner(address newOwner) external {
_owner = newOwner;
}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
}
contract MockReserveAsset is ERC20 {
constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {}
function decimals() public pure override returns (uint8) {
return 18;
}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
function burn(address from, uint256 amount) external {
_burn(from, amount);
}
}
contract MockRouterAssetVerifier is IRouterClient {
function ccipSend(
uint64,
EVM2AnyMessage memory
) external payable returns (bytes32 messageId, uint256 fees) {
return (bytes32(0), fees);
}
function getFee(uint64, EVM2AnyMessage memory) external pure returns (uint256) {
return 0;
}
function getSupportedTokens(uint64) external pure returns (address[] memory tokens) {
tokens = new address[](0);
}
}
contract MockReserveSystemAssetVerifier {
mapping(address => uint256) internal reserveBalances;
function setReserveBalance(address asset, uint256 amount) external {
reserveBalances[asset] = amount;
}
function getReserveBalance(address asset) external view returns (uint256) {
return reserveBalances[asset];
}
}
contract CWAssetReserveVerifierTest is Test {
uint64 internal constant MAINNET_SELECTOR = 5009297550715157269;
uint64 internal constant BSC_SELECTOR = 11344663589394136015;
address internal user = address(0xBEEF);
address internal receiveRouter = address(0x138138);
address internal strictPeerBridge = address(0x010101);
address internal hybridPeerBridge = address(0x020202);
address internal assetVault = address(0xCAFE);
MockRouterAssetVerifier internal router;
MockCanonicalAssetToken internal strictCanonical;
MockCanonicalAssetToken internal hybridCanonical;
MockReserveAsset internal strictReserveAsset;
MockReserveAsset internal hybridReserveAsset;
MockReserveSystemAssetVerifier internal reserveSystem;
CWMultiTokenBridgeL1 internal l1Bridge;
CWAssetReserveVerifier internal verifier;
function setUp() public {
router = new MockRouterAssetVerifier();
strictCanonical = new MockCanonicalAssetToken("Ethereum Mainnet Gas (Compliant)", "cETH", assetVault);
hybridCanonical = new MockCanonicalAssetToken("BNB Gas (Compliant)", "cBNB", address(this));
strictReserveAsset = new MockReserveAsset("Wrapped Ether", "WETH");
hybridReserveAsset = new MockReserveAsset("Wrapped BNB", "WBNB");
reserveSystem = new MockReserveSystemAssetVerifier();
l1Bridge = new CWMultiTokenBridgeL1(address(router), receiveRouter, address(0));
verifier = new CWAssetReserveVerifier(address(this), address(l1Bridge), assetVault, address(reserveSystem));
l1Bridge.configureSupportedCanonicalToken(address(strictCanonical), true);
l1Bridge.configureDestination(address(strictCanonical), MAINNET_SELECTOR, strictPeerBridge, true);
l1Bridge.configureSupportedCanonicalToken(address(hybridCanonical), true);
l1Bridge.configureDestination(address(hybridCanonical), BSC_SELECTOR, hybridPeerBridge, true);
l1Bridge.setReserveVerifier(address(verifier));
verifier.configureToken(address(strictCanonical), address(strictReserveAsset), true, false, true);
verifier.configureToken(address(hybridCanonical), address(hybridReserveAsset), false, true, false);
strictCanonical.mint(user, 100e18);
hybridCanonical.mint(user, 75e18);
strictReserveAsset.mint(assetVault, strictCanonical.totalSupply());
reserveSystem.setReserveBalance(address(hybridReserveAsset), hybridCanonical.totalSupply());
}
function testStrictVerifierAllowsLockWhenVaultBalanceMatchesSupply() public {
uint256 amount = 10e18;
vm.startPrank(user);
strictCanonical.approve(address(l1Bridge), amount);
l1Bridge.lockAndSend(address(strictCanonical), MAINNET_SELECTOR, user, amount);
vm.stopPrank();
assertEq(l1Bridge.lockedBalance(address(strictCanonical)), amount);
assertEq(l1Bridge.totalOutstanding(address(strictCanonical)), amount);
}
function testStrictVerifierBlocksLockWhenVaultBalanceFallsShort() public {
uint256 amount = 10e18;
strictReserveAsset.burn(assetVault, 1);
vm.startPrank(user);
strictCanonical.approve(address(l1Bridge), amount);
vm.expectRevert(CWAssetReserveVerifier.VaultBackingInsufficient.selector);
l1Bridge.lockAndSend(address(strictCanonical), MAINNET_SELECTOR, user, amount);
vm.stopPrank();
}
function testStrictVerifierBlocksLockWhenTokenOwnerDoesNotMatchVault() public {
uint256 amount = 10e18;
strictCanonical.setOwner(address(0xDEAD));
vm.startPrank(user);
strictCanonical.approve(address(l1Bridge), amount);
vm.expectRevert(CWAssetReserveVerifier.TokenOwnerMismatch.selector);
l1Bridge.lockAndSend(address(strictCanonical), MAINNET_SELECTOR, user, amount);
vm.stopPrank();
}
function testHybridVerifierAllowsLockWhenReserveSystemBalanceMatchesSupply() public {
uint256 amount = 10e18;
vm.startPrank(user);
hybridCanonical.approve(address(l1Bridge), amount);
l1Bridge.lockAndSend(address(hybridCanonical), BSC_SELECTOR, user, amount);
vm.stopPrank();
assertEq(l1Bridge.lockedBalance(address(hybridCanonical)), amount);
assertEq(l1Bridge.totalOutstanding(address(hybridCanonical)), amount);
}
function testHybridVerifierBlocksLockWhenReserveSystemFallsShort() public {
uint256 amount = 10e18;
reserveSystem.setReserveBalance(address(hybridReserveAsset), hybridCanonical.totalSupply() - 1);
vm.startPrank(user);
hybridCanonical.approve(address(l1Bridge), amount);
vm.expectRevert(CWAssetReserveVerifier.ReserveSystemBackingInsufficient.selector);
l1Bridge.lockAndSend(address(hybridCanonical), BSC_SELECTOR, user, amount);
vm.stopPrank();
}
}