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

196 lines
8.3 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol";
import {IRouterClient} from "../../contracts/ccip/IRouterClient.sol";
import {OfficialStableMirrorToken} from "../../contracts/tokens/OfficialStableMirrorToken.sol";
import {CompliantUSDCTokenV2} from "../../contracts/tokens/CompliantUSDCTokenV2.sol";
import {CompliantUSDTTokenV2} from "../../contracts/tokens/CompliantUSDTTokenV2.sol";
import {StablecoinReserveVault} from "../../contracts/reserve/StablecoinReserveVault.sol";
import {CWMultiTokenBridgeL1} from "../../contracts/bridge/CWMultiTokenBridgeL1.sol";
import {CWMultiTokenBridgeL2} from "../../contracts/bridge/CWMultiTokenBridgeL2.sol";
import {CWReserveVerifier} from "../../contracts/bridge/integration/CWReserveVerifier.sol";
import {CompliantWrappedToken} from "../../contracts/tokens/CompliantWrappedToken.sol";
contract MockRouterVaultVerifierV2 is IRouterClient {
uint256 public fee;
bytes32 public nextMessageId = keccak256("cw-reserve-vault-v2-message");
EVM2AnyMessage internal _lastMessage;
uint64 public lastDestinationChainSelector;
function ccipSend(
uint64 destinationChainSelector,
EVM2AnyMessage memory message
) external payable returns (bytes32 messageId, uint256 fees) {
lastDestinationChainSelector = destinationChainSelector;
_lastMessage = message;
return (nextMessageId, fee);
}
function getFee(uint64, EVM2AnyMessage memory) external view returns (uint256) {
return fee;
}
function getSupportedTokens(uint64) external pure returns (address[] memory tokens) {
tokens = new address[](0);
}
function lastMessage()
external
view
returns (bytes memory receiver, bytes memory data, address feeToken, bytes memory extraArgs)
{
return (_lastMessage.receiver, _lastMessage.data, _lastMessage.feeToken, _lastMessage.extraArgs);
}
}
contract CWReserveVerifierVaultV2IntegrationTest is Test {
uint64 internal constant AVALANCHE_SELECTOR = 6433500567565415381;
address internal admin = address(0xABCD);
address internal user = address(0xBEEF);
address internal receiveRouterL1 = address(0x138138);
address internal receiveRouterL2 = address(0x4311443114);
address internal peerBridge = address(0x4311443114);
MockRouterVaultVerifierV2 internal router;
OfficialStableMirrorToken internal officialUsdt;
OfficialStableMirrorToken internal officialUsdc;
CompliantUSDTTokenV2 internal compliantUsdtV2;
CompliantUSDCTokenV2 internal compliantUsdcV2;
StablecoinReserveVault internal vault;
CWMultiTokenBridgeL1 internal l1Bridge;
CWMultiTokenBridgeL2 internal l2Bridge;
CWReserveVerifier internal verifier;
CompliantWrappedToken internal wrappedUsdc;
function setUp() public {
router = new MockRouterVaultVerifierV2();
officialUsdt = new OfficialStableMirrorToken("Tether USD (Chain 138)", "USDT", 6, address(this), 0);
officialUsdc = new OfficialStableMirrorToken("USD Coin (Chain 138)", "USDC", 6, address(this), 0);
compliantUsdtV2 = new CompliantUSDTTokenV2(address(this), admin, 1_000_000 * 10 ** 6, true);
compliantUsdcV2 = new CompliantUSDCTokenV2(address(this), admin, 1_000_000 * 10 ** 6, true);
vault = new StablecoinReserveVault(
admin,
address(officialUsdt),
address(officialUsdc),
address(compliantUsdtV2),
address(compliantUsdcV2)
);
l1Bridge = new CWMultiTokenBridgeL1(address(router), receiveRouterL1, address(0));
l2Bridge = new CWMultiTokenBridgeL2(address(router), receiveRouterL2, address(0));
verifier = new CWReserveVerifier(admin, address(l1Bridge), address(vault), address(0));
wrappedUsdc = new CompliantWrappedToken("Wrapped cUSDC", "cWUSDC", 6, address(this));
uint256 canonicalSupply = compliantUsdcV2.totalSupply();
officialUsdc.mint(admin, canonicalSupply);
vm.prank(admin);
officialUsdc.approve(address(vault), canonicalSupply);
vm.prank(admin);
vault.seedUSDCReserve(canonicalSupply);
vm.startPrank(admin);
compliantUsdtV2.grantRole(compliantUsdtV2.MINTER_ROLE(), address(vault));
compliantUsdtV2.grantRole(compliantUsdtV2.PAUSER_ROLE(), address(vault));
compliantUsdtV2.transferOwnership(address(vault));
compliantUsdcV2.grantRole(compliantUsdcV2.MINTER_ROLE(), address(vault));
compliantUsdcV2.grantRole(compliantUsdcV2.PAUSER_ROLE(), address(vault));
compliantUsdcV2.transferOwnership(address(vault));
vm.stopPrank();
vm.prank(address(this));
compliantUsdcV2.transfer(user, 250e6);
l1Bridge.configureSupportedCanonicalToken(address(compliantUsdcV2), true);
l1Bridge.configureDestination(address(compliantUsdcV2), AVALANCHE_SELECTOR, address(l2Bridge), true);
l1Bridge.setReserveVerifier(address(verifier));
l2Bridge.configureDestination(138, address(l1Bridge), true);
l2Bridge.configureTokenPair(address(compliantUsdcV2), address(wrappedUsdc));
wrappedUsdc.grantRole(wrappedUsdc.MINTER_ROLE(), address(l2Bridge));
wrappedUsdc.grantRole(wrappedUsdc.BURNER_ROLE(), address(l2Bridge));
vm.prank(admin);
verifier.configureToken(
address(compliantUsdcV2),
address(0),
true,
false,
true
);
}
function testVerifierAllowsLockForV2CanonicalTokenBackedByVault() public {
uint256 amount = 25e6;
vm.startPrank(user);
compliantUsdcV2.approve(address(l1Bridge), amount);
l1Bridge.lockAndSend(address(compliantUsdcV2), AVALANCHE_SELECTOR, user, amount);
vm.stopPrank();
assertEq(l1Bridge.lockedBalance(address(compliantUsdcV2)), amount);
assertEq(l1Bridge.totalOutstanding(address(compliantUsdcV2)), amount);
assertEq(l1Bridge.outstandingMinted(address(compliantUsdcV2), AVALANCHE_SELECTOR), amount);
}
function testV2CanonicalTokenCompletesFullTransportRoundTrip() public {
uint256 amount = 25e6;
vm.startPrank(user);
compliantUsdcV2.approve(address(l1Bridge), amount);
bytes32 outboundMessageId =
l1Bridge.lockAndSend(address(compliantUsdcV2), AVALANCHE_SELECTOR, user, amount);
vm.stopPrank();
(bytes memory receiverData, bytes memory outboundData,,) = router.lastMessage();
assertEq(abi.decode(receiverData, (address)), address(l2Bridge));
vm.prank(receiveRouterL2);
l2Bridge.ccipReceive(_message(outboundMessageId, 138, address(l1Bridge), outboundData));
assertEq(wrappedUsdc.balanceOf(user), amount);
assertEq(wrappedUsdc.totalSupply(), amount);
assertEq(l2Bridge.mintedTotal(address(wrappedUsdc)), amount);
assertEq(l2Bridge.burnedTotal(address(wrappedUsdc)), 0);
vm.prank(user);
bytes32 returnMessageId = l2Bridge.burnAndSend(address(wrappedUsdc), 138, user, amount);
assertEq(wrappedUsdc.balanceOf(user), 0);
assertEq(wrappedUsdc.totalSupply(), 0);
assertEq(l2Bridge.mintedTotal(address(wrappedUsdc)), amount);
assertEq(l2Bridge.burnedTotal(address(wrappedUsdc)), amount);
(, bytes memory returnData,,) = router.lastMessage();
vm.prank(receiveRouterL1);
l1Bridge.ccipReceive(_message(returnMessageId, AVALANCHE_SELECTOR, address(l2Bridge), returnData));
assertEq(compliantUsdcV2.balanceOf(user), 250e6);
assertEq(compliantUsdcV2.balanceOf(address(l1Bridge)), 0);
assertEq(l1Bridge.lockedBalance(address(compliantUsdcV2)), 0);
assertEq(l1Bridge.totalOutstanding(address(compliantUsdcV2)), 0);
assertEq(l1Bridge.outstandingMinted(address(compliantUsdcV2), AVALANCHE_SELECTOR), 0);
}
function _message(
bytes32 messageId,
uint64 sourceChainSelector,
address sender,
bytes memory data
) internal pure returns (IRouterClient.Any2EVMMessage memory message) {
IRouterClient.TokenAmount[] memory noTokens = new IRouterClient.TokenAmount[](0);
message = IRouterClient.Any2EVMMessage({
messageId: messageId,
sourceChainSelector: sourceChainSelector,
sender: abi.encode(sender),
data: data,
tokenAmounts: noTokens
});
}
}