-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
254 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
// SPDX-License-Identifier: Apache 2 | ||
pragma solidity ^0.8.22; | ||
|
||
import "forge-std/Test.sol"; | ||
import "forge-std/console.sol"; | ||
|
||
import {IWormhole} from "src/interfaces/IWormhole.sol"; | ||
import {ICircleIntegration} from "src/interfaces/ICircleIntegration.sol"; | ||
|
||
import {Utils} from "src/libraries/Utils.sol"; | ||
|
||
import {Implementation} from "src/contracts/CircleIntegration/Implementation.sol"; | ||
|
||
import { | ||
CircleIntegrationOverride, | ||
CraftedCctpMessageParams, | ||
CraftedVaaParams | ||
} from "test-helpers/libraries/CircleIntegrationOverride.sol"; | ||
import {UsdcDeal} from "test-helpers/libraries/UsdcDeal.sol"; | ||
import {WormholeOverride} from "test-helpers/libraries/WormholeOverride.sol"; | ||
|
||
contract ForkSlots is Test { | ||
using CircleIntegrationOverride for *; | ||
using WormholeOverride for *; | ||
using Utils for address; | ||
|
||
bytes32 constant GOVERNANCE_MODULE = | ||
0x000000000000000000000000000000436972636c65496e746567726174696f6e; | ||
|
||
address constant FORKED_CIRCLE_INTEGRATION_ADDRESS = 0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c; | ||
|
||
ICircleIntegration forked; | ||
IWormhole wormhole; | ||
|
||
function setUp() public { | ||
forked = ICircleIntegration(FORKED_CIRCLE_INTEGRATION_ADDRESS); | ||
forked.setUpOverride(uint256(vm.envBytes32("TESTING_DEVNET_GUARDIAN"))); | ||
|
||
wormhole = forked.wormhole(); | ||
} | ||
|
||
function test_UpgradeForkAndCheckSlots() public { | ||
// Deploy new implementation. | ||
Implementation implementation = new Implementation( | ||
address(wormhole), | ||
vm.envAddress("TESTING_CIRCLE_BRIDGE_ADDRESS") // tokenMessenger | ||
); | ||
|
||
// Should not be initialized yet. | ||
assertFalse(forked.isInitialized(address(implementation)), "already initialized"); | ||
|
||
// Also verify slot reflects same information. | ||
{ | ||
bytes32 data = vm.load( | ||
address(forked), keccak256(abi.encode(address(implementation), uint256(0x5))) | ||
); | ||
assertEq( | ||
uint256(data), | ||
0, // false | ||
"isInitialized does not match slot value (false)" | ||
); | ||
} | ||
|
||
// Check slots 0x0, 0x1, 0x2, 0x3, 0x4 and 0xa before upgrade. | ||
|
||
// 0x0: | ||
// | ||
// uint16 chainId; | ||
// uint8 wormholeFinality; | ||
// uint32 localDomain; | ||
// address wormhole; | ||
// uint16 governanceChainId; | ||
{ | ||
bytes32 data = vm.load(address(forked), bytes32(0)); | ||
|
||
uint256 bitOffset; | ||
|
||
// First 2 bytes is chain ID. | ||
assertEq(uint16(uint256(data >> bitOffset)), forked.chainId()); | ||
bitOffset += 16; | ||
|
||
// Next byte is wormhole finality. | ||
assertEq(uint8(uint256(data >> bitOffset)), forked.wormholeFinality()); | ||
bitOffset += 8; | ||
|
||
// Next 4 bytes is local domain. | ||
assertEq(uint32(uint256(data >> bitOffset)), forked.localDomain()); | ||
bitOffset += 32; | ||
|
||
// Next 20 bytes is wormhole address. | ||
assertEq(address(uint160(uint256(data >> bitOffset))), address(wormhole)); | ||
bitOffset += 160; | ||
|
||
// Next 2 bytes is governance chain ID. | ||
assertEq(uint16(uint256(data >> bitOffset)), forked.governanceChainId()); | ||
bitOffset += 16; | ||
|
||
// Remaining bytes are zero. | ||
assertEq(uint256(data >> bitOffset), 0); | ||
} | ||
|
||
// 0x1: | ||
// | ||
// governanceContract | ||
{ | ||
bytes32 data = vm.load(address(forked), bytes32(uint256(0x1))); | ||
assertEq(data, forked.governanceContract()); | ||
} | ||
|
||
// 0x2: | ||
// | ||
// circleBridgeAddress | ||
{ | ||
bytes32 data = vm.load(address(forked), bytes32(uint256(0x2))); | ||
assertEq(address(uint160(uint256(data))), address(forked.circleBridge())); | ||
} | ||
|
||
// 0x3: | ||
// | ||
// circleTransmitterAddress | ||
{ | ||
bytes32 data = vm.load(address(forked), bytes32(uint256(0x3))); | ||
assertEq(address(uint160(uint256(data))), address(forked.circleTransmitter())); | ||
} | ||
|
||
// 0x4: | ||
// | ||
// circleTokenMinterAddress | ||
{ | ||
bytes32 data = vm.load(address(forked), bytes32(uint256(0x4))); | ||
assertEq(address(uint160(uint256(data))), address(forked.circleTokenMinter())); | ||
} | ||
|
||
// 0xa: | ||
// | ||
// evmChain | ||
{ | ||
bytes32 data = vm.load(address(forked), bytes32(uint256(0xa))); | ||
assertEq(uint256(data), forked.evmChain()); | ||
} | ||
|
||
// Before upgrading, fetch some expected values. | ||
uint16 registeredChainId = 2; | ||
bytes32 expectedEmitter = forked.getRegisteredEmitter(registeredChainId); | ||
|
||
// Also verify slot reflects same information. | ||
{ | ||
bytes32 data = | ||
vm.load(address(forked), keccak256(abi.encode(registeredChainId, uint256(0x6)))); | ||
assertEq(data, expectedEmitter); | ||
} | ||
|
||
uint32 expectedCctpDomain = forked.getDomainFromChainId(registeredChainId); | ||
|
||
// Also verify slot reflects same information. | ||
{ | ||
bytes32 data = | ||
vm.load(address(forked), keccak256(abi.encode(registeredChainId, uint256(0x7)))); | ||
assertEq(uint32(uint256(data)), expectedCctpDomain); | ||
} | ||
|
||
assertEq(forked.getChainIdFromDomain(expectedCctpDomain), registeredChainId); | ||
|
||
// Also verify slot reflects same information. | ||
{ | ||
bytes32 data = | ||
vm.load(address(forked), keccak256(abi.encode(expectedCctpDomain, uint256(0x8)))); | ||
assertEq(uint16(uint256(data)), registeredChainId); | ||
} | ||
|
||
(IWormhole.VM memory vaa, bytes memory encodedVaa) = wormhole.craftGovernanceVaa( | ||
GOVERNANCE_MODULE, | ||
3, // action | ||
forked.chainId(), | ||
69, // sequence | ||
abi.encodePacked(address(implementation).toUniversalAddress()) | ||
); | ||
|
||
// Show that VAA has not been consumed yet. | ||
assertFalse(forked.isMessageConsumed(vaa.hash), "VAA already consumed"); | ||
|
||
// Also verify slot reflects same information. | ||
{ | ||
bytes32 data = vm.load(address(forked), keccak256(abi.encode(vaa.hash, uint256(0x9)))); | ||
assertEq( | ||
uint256(data), | ||
0, // false | ||
"isMessageConsumed does not match slot value (true)" | ||
); | ||
} | ||
|
||
// Upgrade contract. | ||
forked.upgradeContract(encodedVaa); | ||
|
||
// Now initialized. | ||
assertTrue(forked.isInitialized(address(implementation)), "implementation not initialized"); | ||
|
||
// Also verify slot reflects same information. | ||
{ | ||
bytes32 data = vm.load( | ||
address(forked), keccak256(abi.encode(address(implementation), uint256(0x5))) | ||
); | ||
assertEq( | ||
uint256(data), | ||
1, // true | ||
"isInitialized does not match slot value (true)" | ||
); | ||
} | ||
|
||
// Show that VAA is now consumed. | ||
assertTrue(forked.isMessageConsumed(vaa.hash), "VAA not consumed"); | ||
|
||
// Also verify slot reflects same information. | ||
{ | ||
bytes32 data = vm.load(address(forked), keccak256(abi.encode(vaa.hash, uint256(0x9)))); | ||
assertEq( | ||
uint256(data), | ||
1, // true | ||
"isMessageConsumed does not match slot value (true)" | ||
); | ||
} | ||
|
||
// Slots checked above should now be zero. | ||
{ | ||
uint256[6] memory slots = | ||
[0, uint256(0x1), uint256(0x2), uint256(0x3), uint256(0x4), uint256(0xa)]; | ||
for (uint256 i; i < slots.length;) { | ||
bytes32 data = vm.load(address(forked), bytes32(slots[i])); | ||
assertEq(uint256(data), 0); | ||
unchecked { | ||
++i; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters