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

156 lines
5.3 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 {CWReserveVerifier} from "../../contracts/bridge/integration/CWReserveVerifier.sol";
contract MockCanonicalBTCWithOwner is ERC20 {
address internal _owner;
constructor(address initialOwner) ERC20("Bitcoin (Compliant)", "cBTC") {
_owner = initialOwner;
}
function decimals() public pure override returns (uint8) {
return 8;
}
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 MockRouterVerifierBTC 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 MockReserveVaultBTC {
address internal trackedToken;
mapping(address => uint256) public reserveBalance;
mapping(address => uint256) public backingRatio;
bool public tokenAdequate = true;
constructor(address trackedToken_) {
trackedToken = trackedToken_;
}
function compliantUSDT() external view returns (address) {
return trackedToken;
}
function compliantUSDC() external pure returns (address) {
return address(0xCAFE);
}
function setAdequacy(bool tokenAdequate_) external {
tokenAdequate = tokenAdequate_;
}
function setBacking(address token, uint256 reserveBalance_, uint256 backingRatio_) external {
reserveBalance[token] = reserveBalance_;
backingRatio[token] = backingRatio_;
}
function getBackingRatio(address token) external view returns (uint256, uint256, uint256) {
return (reserveBalance[token], ERC20(token).totalSupply(), backingRatio[token]);
}
function checkReserveAdequacy() external view returns (bool, bool) {
return (tokenAdequate, true);
}
}
contract MockReserveSystemBTC {
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 CWReserveVerifierBTCTest is Test {
uint64 internal constant ETHEREUM_SELECTOR = 5009297550715157269;
address internal user = address(0xBEEF);
address internal receiveRouter = address(0x138138);
address internal peerBridge = address(0x010101);
address internal reserveAsset = address(0x0B7C);
MockRouterVerifierBTC internal router;
MockCanonicalBTCWithOwner internal canonical;
CWMultiTokenBridgeL1 internal l1Bridge;
MockReserveVaultBTC internal reserveVault;
MockReserveSystemBTC internal reserveSystem;
CWReserveVerifier internal verifier;
function setUp() public {
router = new MockRouterVerifierBTC();
canonical = new MockCanonicalBTCWithOwner(address(this));
l1Bridge = new CWMultiTokenBridgeL1(address(router), receiveRouter, address(0));
reserveVault = new MockReserveVaultBTC(address(canonical));
reserveSystem = new MockReserveSystemBTC();
verifier = new CWReserveVerifier(address(this), address(l1Bridge), address(reserveVault), address(reserveSystem));
canonical.setOwner(address(reserveVault));
l1Bridge.configureSupportedCanonicalToken(address(canonical), true);
l1Bridge.configureDestination(address(canonical), ETHEREUM_SELECTOR, peerBridge, true);
l1Bridge.setReserveVerifier(address(verifier));
verifier.configureToken(address(canonical), reserveAsset, true, true, true);
canonical.mint(user, 10_000_000_000);
reserveVault.setBacking(address(canonical), canonical.totalSupply(), 10_000);
reserveVault.setAdequacy(true);
reserveSystem.setReserveBalance(reserveAsset, canonical.totalSupply());
}
function testVerifierAllowsBTCLockAtEightDecimals() public {
vm.startPrank(user);
canonical.approve(address(l1Bridge), 250_000_000);
l1Bridge.lockAndSend(address(canonical), ETHEREUM_SELECTOR, user, 250_000_000);
vm.stopPrank();
assertEq(l1Bridge.totalOutstanding(address(canonical)), 250_000_000);
assertEq(l1Bridge.lockedBalance(address(canonical)), 250_000_000);
}
function testVerifierBlocksBTCLockWhenReserveFallsShort() public {
reserveSystem.setReserveBalance(reserveAsset, canonical.totalSupply() - 1);
vm.startPrank(user);
canonical.approve(address(l1Bridge), 100_000_000);
vm.expectRevert(CWReserveVerifier.ReserveSystemBackingInsufficient.selector);
l1Bridge.lockAndSend(address(canonical), ETHEREUM_SELECTOR, user, 100_000_000);
vm.stopPrank();
}
}