ethereum: migrate truffle tests to forge
This commit is contained in:
parent
088c18b39a
commit
2f4811c190
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,976 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../contracts/nft/NFTBridge.sol";
|
||||
import "../contracts/nft/NFTBridgeSetup.sol";
|
||||
import "../contracts/nft/NFTBridgeImplementation.sol";
|
||||
import "../contracts/nft/NFTBridgeEntrypoint.sol";
|
||||
import "../contracts/interfaces/IWormhole.sol";
|
||||
import "../contracts/nft/interfaces/INFTBridge.sol";
|
||||
|
||||
import "../contracts/nft/interfaces/INFTBridge.sol";
|
||||
import "../contracts/nft/token/NFTImplementation.sol";
|
||||
import "../contracts/nft/mock/MockNFTImplementation.sol";
|
||||
import "../contracts/nft/mock/MockNFTBridgeImplementation.sol";
|
||||
import "forge-std/Test.sol";
|
||||
import "./Implementation.t.sol";
|
||||
import "../contracts/bridge/mock/MockWETH9.sol";
|
||||
|
||||
contract TestNFTBridge is Test {
|
||||
NFTBridgeSetup bridgeSetup;
|
||||
NFTBridgeImplementation bridgeImpl;
|
||||
NFTImplementation tokenImpl;
|
||||
INFTBridge bridge;
|
||||
IWormhole wormhole;
|
||||
|
||||
TestImplementation implementationTest;
|
||||
|
||||
uint16 testChainId;
|
||||
uint256 testEvmChainId;
|
||||
uint16 governanceChainId;
|
||||
bytes32 governanceContract;
|
||||
uint8 constant finality = 15;
|
||||
|
||||
// "NFTBridge" (left padded)
|
||||
bytes32 constant NFTBridgeModule =
|
||||
0x00000000000000000000000000000000000000000000004e4654427269646765;
|
||||
uint8 actionRegisterChain = 1;
|
||||
uint8 actionContractUpgrade = 2;
|
||||
uint8 actionRecoverChainId = 3;
|
||||
|
||||
uint16 fakeChainId = 1337;
|
||||
uint256 fakeEvmChainId = 10001;
|
||||
|
||||
uint16 testForeignChainId = 1;
|
||||
bytes32 testForeignBridgeContract =
|
||||
0x000000000000000000000000000000000000000000000000000000000000ffff;
|
||||
uint16 testBridgedAssetChain = 3;
|
||||
bytes32 testBridgedAssetAddress =
|
||||
0x000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e;
|
||||
|
||||
uint256 public constant testGuardian =
|
||||
93941733246223705020089879371323733820373732307041878556247502674739205313440;
|
||||
|
||||
function setUp() public {
|
||||
// Setup wormhole
|
||||
implementationTest = new TestImplementation();
|
||||
implementationTest.setUp();
|
||||
|
||||
// Get wormhole from implementation tests
|
||||
wormhole = IWormhole(address(implementationTest.proxied()));
|
||||
|
||||
// Deploy setup
|
||||
bridgeSetup = new NFTBridgeSetup();
|
||||
// Deploy implementation contract
|
||||
bridgeImpl = new NFTBridgeImplementation();
|
||||
// Deploy token implementation
|
||||
tokenImpl = new NFTImplementation();
|
||||
|
||||
testChainId = implementationTest.testChainId();
|
||||
testEvmChainId = implementationTest.testEvmChainId();
|
||||
vm.chainId(testEvmChainId);
|
||||
governanceChainId = implementationTest.governanceChainId();
|
||||
governanceContract = implementationTest.governanceContract();
|
||||
|
||||
bytes memory setupAbi = abi.encodeWithSelector(
|
||||
NFTBridgeSetup.setup.selector,
|
||||
address(bridgeImpl),
|
||||
testChainId,
|
||||
address(wormhole),
|
||||
governanceChainId,
|
||||
governanceContract,
|
||||
address(tokenImpl),
|
||||
finality,
|
||||
testEvmChainId
|
||||
);
|
||||
|
||||
// Deploy proxy
|
||||
bridge = INFTBridge(
|
||||
address(new NFTBridgeEntrypoint(address(bridgeSetup), setupAbi))
|
||||
);
|
||||
}
|
||||
|
||||
function testShouldBeInitializedWithTheCorrectSignersAndValues() public {
|
||||
assertEq(bridge.tokenImplementation(), address(tokenImpl));
|
||||
// test beacon functionality
|
||||
assertEq(bridge.implementation(), address(tokenImpl));
|
||||
assertEq(bridge.chainId(), testChainId);
|
||||
assertEq(bridge.evmChainId(), testEvmChainId);
|
||||
assertEq(bridge.finality(), finality);
|
||||
|
||||
// governance
|
||||
uint16 readGovernanceChainId = bridge.governanceChainId();
|
||||
bytes32 readGovernanceContract = bridge.governanceContract();
|
||||
assertEq(
|
||||
readGovernanceChainId,
|
||||
governanceChainId,
|
||||
"Wrong governance chain ID"
|
||||
);
|
||||
assertEq(
|
||||
readGovernanceContract,
|
||||
governanceContract,
|
||||
"Wrong governance contract"
|
||||
);
|
||||
}
|
||||
|
||||
function testShouldRegisterAForeignBridgeImplementationCorrectly() public {
|
||||
bytes memory data = abi.encodePacked(
|
||||
NFTBridgeModule,
|
||||
actionRegisterChain,
|
||||
uint16(0),
|
||||
testForeignChainId,
|
||||
testForeignBridgeContract
|
||||
);
|
||||
bytes memory vaa = signAndEncodeVM(
|
||||
1,
|
||||
1,
|
||||
governanceChainId,
|
||||
governanceContract,
|
||||
0,
|
||||
data,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
assertEq(
|
||||
bridge.bridgeContracts(testForeignChainId),
|
||||
bytes32(
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
);
|
||||
bridge.registerChain(vaa);
|
||||
assertEq(
|
||||
bridge.bridgeContracts(testForeignChainId),
|
||||
testForeignBridgeContract
|
||||
);
|
||||
}
|
||||
|
||||
function testShouldAcceptAValidUpgrade() public {
|
||||
MockNFTBridgeImplementation mock = new MockNFTBridgeImplementation();
|
||||
bytes memory data = abi.encodePacked(
|
||||
NFTBridgeModule,
|
||||
actionContractUpgrade,
|
||||
testChainId,
|
||||
addressToBytes32(address(mock))
|
||||
);
|
||||
bytes memory vaa = signAndEncodeVM(
|
||||
1,
|
||||
1,
|
||||
governanceChainId,
|
||||
governanceContract,
|
||||
0,
|
||||
data,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
bytes32 IMPLEMENTATION_STORAGE_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
||||
assertEq(
|
||||
vm.load(address(bridge), IMPLEMENTATION_STORAGE_SLOT),
|
||||
addressToBytes32(address(bridgeImpl))
|
||||
);
|
||||
bridge.upgrade(vaa);
|
||||
assertEq(
|
||||
vm.load(address(bridge), IMPLEMENTATION_STORAGE_SLOT),
|
||||
addressToBytes32(address(mock))
|
||||
);
|
||||
|
||||
assertTrue(
|
||||
MockNFTBridgeImplementation(address(bridge))
|
||||
.testNewImplementationActive(),
|
||||
"implementation not active"
|
||||
);
|
||||
}
|
||||
|
||||
function testBridgedTokensShouldOnlyBeMintAndBurnableByOwner() public {
|
||||
address owner = address(this);
|
||||
address notOwner = address(0x1);
|
||||
tokenImpl.initialize("TestToken", "TT", owner, 0, 0x0);
|
||||
tokenImpl.mint(owner, 10, "");
|
||||
|
||||
vm.expectRevert("caller is not the owner");
|
||||
vm.prank(address(notOwner));
|
||||
tokenImpl.mint(owner, 11, "");
|
||||
|
||||
vm.expectRevert("caller is not the owner");
|
||||
vm.prank(address(notOwner));
|
||||
tokenImpl.burn(10);
|
||||
|
||||
tokenImpl.burn(10);
|
||||
}
|
||||
|
||||
event LogMessagePublished(
|
||||
address indexed sender,
|
||||
uint64 sequence,
|
||||
uint32 nonce,
|
||||
bytes payload,
|
||||
uint8 consistencyLevel
|
||||
);
|
||||
|
||||
function testShouldDepositAndLogTransfersCorrectly() public {
|
||||
testShouldRegisterAForeignBridgeImplementationCorrectly();
|
||||
testBridgedTokensShouldOnlyBeMintAndBurnableByOwner();
|
||||
|
||||
uint256 tokenId = 1000000000000000000;
|
||||
|
||||
tokenImpl.mint(address(this), tokenId, "abcd");
|
||||
tokenImpl.approve(address(bridge), tokenId);
|
||||
|
||||
address ownerBefore = tokenImpl.ownerOf(tokenId);
|
||||
assertEq(ownerBefore, address(this));
|
||||
|
||||
uint16 toChain = testForeignChainId;
|
||||
bytes32 toAddress = testForeignBridgeContract;
|
||||
|
||||
bytes memory transferPayload = abi.encodePacked(
|
||||
uint8(1),
|
||||
addressToBytes32(address(tokenImpl)),
|
||||
testChainId,
|
||||
bytes32("TT"),
|
||||
bytes32("TestToken"),
|
||||
tokenId,
|
||||
uint8(4),
|
||||
hex"61626364",
|
||||
toAddress,
|
||||
toChain
|
||||
);
|
||||
vm.expectEmit();
|
||||
emit LogMessagePublished(
|
||||
address(bridge),
|
||||
uint64(0),
|
||||
uint32(234),
|
||||
transferPayload,
|
||||
uint8(finality)
|
||||
);
|
||||
bridge.transferNFT(
|
||||
address(tokenImpl),
|
||||
tokenId,
|
||||
toChain,
|
||||
toAddress,
|
||||
234
|
||||
);
|
||||
|
||||
address ownerAfter = tokenImpl.ownerOf(tokenId);
|
||||
assertEq(ownerAfter, address(bridge));
|
||||
}
|
||||
|
||||
function testShouldTransferOutLockedAssetsForAValidTransferVM() public {
|
||||
testShouldDepositAndLogTransfersCorrectly();
|
||||
|
||||
uint256 tokenId = 1000000000000000000;
|
||||
|
||||
uint16 toChain = testChainId;
|
||||
bytes32 toAddress = addressToBytes32(address(this));
|
||||
|
||||
address ownerBefore = tokenImpl.ownerOf(tokenId);
|
||||
assertEq(ownerBefore, address(bridge));
|
||||
|
||||
bytes memory transferPayload = abi.encodePacked(
|
||||
uint8(1),
|
||||
addressToBytes32(address(tokenImpl)),
|
||||
testChainId,
|
||||
bytes32(0x0),
|
||||
bytes32(0x0),
|
||||
tokenId,
|
||||
uint8(0),
|
||||
hex"",
|
||||
toAddress,
|
||||
toChain
|
||||
);
|
||||
|
||||
bytes memory vaa = signAndEncodeVM(
|
||||
0,
|
||||
0,
|
||||
testForeignChainId,
|
||||
testForeignBridgeContract,
|
||||
294, // sequence
|
||||
transferPayload,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
bridge.completeTransfer(vaa);
|
||||
|
||||
address ownerAfter = tokenImpl.ownerOf(tokenId);
|
||||
assertEq(ownerAfter, address(this));
|
||||
}
|
||||
|
||||
function testShouldMintBridgedAssetWrappersOnTransferFromAnotherChainAndHandleFeesCorrectly()
|
||||
public
|
||||
{
|
||||
testShouldTransferOutLockedAssetsForAValidTransferVM();
|
||||
|
||||
uint256 tokenId = 1000000000000000001;
|
||||
|
||||
bytes memory transferPayload = abi.encodePacked(
|
||||
uint8(1),
|
||||
testBridgedAssetAddress,
|
||||
testBridgedAssetChain,
|
||||
// symbol
|
||||
bytes32(
|
||||
0x464f520000000000000000000000000000000000000000000000000000000000
|
||||
),
|
||||
// name
|
||||
bytes32(
|
||||
0x466f726569676e20436861696e204e4654000000000000000000000000000000
|
||||
),
|
||||
tokenId,
|
||||
// no URL
|
||||
uint8(0),
|
||||
hex"",
|
||||
addressToBytes32(address(this)),
|
||||
testChainId
|
||||
);
|
||||
|
||||
bytes memory vaa = signAndEncodeVM(
|
||||
0,
|
||||
0,
|
||||
testForeignChainId,
|
||||
testForeignBridgeContract,
|
||||
0,
|
||||
transferPayload,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
address sender = address(0x1);
|
||||
vm.prank(sender);
|
||||
bridge.completeTransfer(vaa);
|
||||
|
||||
address wrappedAddress = bridge.wrappedAsset(
|
||||
testBridgedAssetChain,
|
||||
testBridgedAssetAddress
|
||||
);
|
||||
NFTImplementation wrapped = NFTImplementation(wrappedAddress);
|
||||
|
||||
assertTrue(
|
||||
bridge.isWrappedAsset(address(wrapped)),
|
||||
"not wrapped asset"
|
||||
);
|
||||
|
||||
address ownerAfter = wrapped.ownerOf(tokenId);
|
||||
assertEq(ownerAfter, address(this));
|
||||
|
||||
assertEq(wrapped.symbol(), "FOR");
|
||||
assertEq(wrapped.name(), "Foreign Chain NFT");
|
||||
assertEq(wrapped.chainId(), testBridgedAssetChain);
|
||||
assertEq(wrapped.nativeContract(), testBridgedAssetAddress);
|
||||
|
||||
tokenId = 1000000000000000002;
|
||||
transferPayload = abi.encodePacked(
|
||||
uint8(1),
|
||||
testBridgedAssetAddress,
|
||||
testBridgedAssetChain,
|
||||
// symbol
|
||||
bytes32(
|
||||
0x464f520000000000000000000000000000000000000000000000000000000000
|
||||
),
|
||||
// name
|
||||
bytes32(
|
||||
0x466f726569676e20436861696e204e4654000000000000000000000000000000
|
||||
),
|
||||
tokenId,
|
||||
// no URL
|
||||
uint8(0),
|
||||
hex"",
|
||||
addressToBytes32(address(this)),
|
||||
testChainId
|
||||
);
|
||||
|
||||
vaa = signAndEncodeVM(
|
||||
0,
|
||||
0,
|
||||
testForeignChainId,
|
||||
testForeignBridgeContract,
|
||||
0,
|
||||
transferPayload,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
sender = address(0x1);
|
||||
vm.prank(sender);
|
||||
bridge.completeTransfer(vaa);
|
||||
|
||||
ownerAfter = wrapped.ownerOf(tokenId);
|
||||
assertEq(ownerAfter, address(this));
|
||||
}
|
||||
|
||||
function testShouldMintBridgedAssetsFromSolanaUnderUnifiedNameCachingTheOriginal()
|
||||
public
|
||||
{
|
||||
testShouldTransferOutLockedAssetsForAValidTransferVM();
|
||||
|
||||
uint256 tokenId = 1000000000000000001;
|
||||
|
||||
bytes memory transferPayload = abi.encodePacked(
|
||||
uint8(1),
|
||||
testBridgedAssetAddress,
|
||||
uint16(1), // solana
|
||||
// symbol
|
||||
bytes32(
|
||||
0x464f520000000000000000000000000000000000000000000000000000000000
|
||||
),
|
||||
// name
|
||||
bytes32(
|
||||
0x466f726569676e20436861696e204e4654000000000000000000000000000000
|
||||
),
|
||||
tokenId,
|
||||
// no URL
|
||||
uint8(0),
|
||||
hex"",
|
||||
addressToBytes32(address(this)),
|
||||
testChainId
|
||||
);
|
||||
|
||||
bytes memory vaa = signAndEncodeVM(
|
||||
0,
|
||||
0,
|
||||
testForeignChainId,
|
||||
testForeignBridgeContract,
|
||||
0,
|
||||
transferPayload,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
address sender = address(0x1);
|
||||
vm.prank(sender);
|
||||
bridge.completeTransfer(vaa);
|
||||
|
||||
address wrappedAddress = bridge.wrappedAsset(
|
||||
1,
|
||||
testBridgedAssetAddress
|
||||
);
|
||||
NFTImplementation wrapped = NFTImplementation(wrappedAddress);
|
||||
|
||||
INFTBridge.SPLCache memory cache = bridge.splCache(tokenId);
|
||||
assertEq(
|
||||
cache.symbol,
|
||||
bytes32(
|
||||
0x464f520000000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
);
|
||||
assertEq(
|
||||
cache.name,
|
||||
bytes32(
|
||||
0x466f726569676e20436861696e204e4654000000000000000000000000000000
|
||||
)
|
||||
);
|
||||
|
||||
address ownerAfter = wrapped.ownerOf(tokenId);
|
||||
assertEq(ownerAfter, address(this));
|
||||
|
||||
assertEq(wrapped.symbol(), "WORMSPLNFT");
|
||||
assertEq(wrapped.name(), "Wormhole Bridged Solana-NFT");
|
||||
assertEq(wrapped.chainId(), 1);
|
||||
assertEq(wrapped.nativeContract(), testBridgedAssetAddress);
|
||||
}
|
||||
|
||||
function testCachedSPLNamesAreLoadedWhenTransferringOutCacheIsCleared()
|
||||
public
|
||||
{
|
||||
testShouldMintBridgedAssetsFromSolanaUnderUnifiedNameCachingTheOriginal();
|
||||
address wrappedAddress = bridge.wrappedAsset(
|
||||
1,
|
||||
testBridgedAssetAddress
|
||||
);
|
||||
NFTImplementation wrapped = NFTImplementation(wrappedAddress);
|
||||
|
||||
uint256 tokenId = 1000000000000000001;
|
||||
|
||||
bytes memory transferPayload = abi.encodePacked(
|
||||
uint8(1),
|
||||
testBridgedAssetAddress,
|
||||
uint16(1),
|
||||
bytes32(
|
||||
0x464f520000000000000000000000000000000000000000000000000000000000
|
||||
),
|
||||
bytes32(
|
||||
0x466f726569676e20436861696e204e4654000000000000000000000000000000
|
||||
),
|
||||
tokenId,
|
||||
uint8(0),
|
||||
hex"",
|
||||
testBridgedAssetAddress, // to address
|
||||
testBridgedAssetChain // to chain
|
||||
);
|
||||
|
||||
wrapped.approve(address(bridge), tokenId);
|
||||
|
||||
vm.expectEmit();
|
||||
emit LogMessagePublished(
|
||||
address(bridge),
|
||||
uint64(1),
|
||||
uint32(2345),
|
||||
transferPayload,
|
||||
uint8(finality)
|
||||
);
|
||||
bridge.transferNFT(
|
||||
wrappedAddress,
|
||||
tokenId,
|
||||
testBridgedAssetChain, // to chain
|
||||
testBridgedAssetAddress, // to address
|
||||
2345
|
||||
);
|
||||
|
||||
INFTBridge.SPLCache memory cache = bridge.splCache(tokenId);
|
||||
assertEq(
|
||||
cache.symbol,
|
||||
bytes32(
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
);
|
||||
assertEq(
|
||||
cache.name,
|
||||
bytes32(
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function testShouldFailDepositUnapprovedNFTs() public {
|
||||
testShouldRegisterAForeignBridgeImplementationCorrectly();
|
||||
testBridgedTokensShouldOnlyBeMintAndBurnableByOwner();
|
||||
|
||||
uint256 tokenId = 1000000000000000000;
|
||||
|
||||
tokenImpl.mint(address(this), tokenId, "abcd");
|
||||
// tokenImpl.approve(address(bridge), tokenId);
|
||||
|
||||
address ownerBefore = tokenImpl.ownerOf(tokenId);
|
||||
assertEq(ownerBefore, address(this));
|
||||
|
||||
uint16 toChain = testForeignChainId;
|
||||
bytes32 toAddress = testForeignBridgeContract;
|
||||
|
||||
vm.expectRevert("ERC721: transfer caller is not owner nor approved");
|
||||
bridge.transferNFT(
|
||||
address(tokenImpl),
|
||||
tokenId,
|
||||
toChain,
|
||||
toAddress,
|
||||
234
|
||||
);
|
||||
}
|
||||
|
||||
function testShouldRefuseToBurnWrappersNotHeldByMsgSender() public {
|
||||
testShouldMintBridgedAssetWrappersOnTransferFromAnotherChainAndHandleFeesCorrectly();
|
||||
address wrappedAddress = bridge.wrappedAsset(
|
||||
testBridgedAssetChain,
|
||||
testBridgedAssetAddress
|
||||
);
|
||||
NFTImplementation wrapped = NFTImplementation(wrappedAddress);
|
||||
|
||||
uint256 tokenId = 1000000000000000001;
|
||||
|
||||
wrapped.approve(address(bridge), tokenId);
|
||||
|
||||
vm.expectRevert("ERC721: transfer of token that is not own");
|
||||
vm.prank(address(0x1));
|
||||
bridge.transferNFT(
|
||||
wrappedAddress,
|
||||
tokenId,
|
||||
testBridgedAssetChain, // to chain
|
||||
testBridgedAssetAddress, // to address
|
||||
2345
|
||||
);
|
||||
}
|
||||
|
||||
function testShouldDepositAndBurnApprovedBridgedAssetWrapperOnTransferToAnotherChain()
|
||||
public
|
||||
{
|
||||
testShouldMintBridgedAssetWrappersOnTransferFromAnotherChainAndHandleFeesCorrectly();
|
||||
address wrappedAddress = bridge.wrappedAsset(
|
||||
testBridgedAssetChain,
|
||||
testBridgedAssetAddress
|
||||
);
|
||||
NFTImplementation wrapped = NFTImplementation(wrappedAddress);
|
||||
|
||||
uint256 tokenId = 1000000000000000001;
|
||||
|
||||
wrapped.approve(address(bridge), tokenId);
|
||||
|
||||
bridge.transferNFT(
|
||||
wrappedAddress,
|
||||
tokenId,
|
||||
testBridgedAssetChain, // to chain
|
||||
testBridgedAssetAddress, // to address
|
||||
2345
|
||||
);
|
||||
|
||||
vm.expectRevert("ERC721: owner query for nonexistent token");
|
||||
wrapped.ownerOf(tokenId);
|
||||
}
|
||||
|
||||
function addressToBytes32(address input) internal returns (bytes32 output) {
|
||||
return bytes32(uint256(uint160(input)));
|
||||
}
|
||||
|
||||
function uint256Array(
|
||||
uint256 member
|
||||
) internal returns (uint256[] memory arr) {
|
||||
arr = new uint256[](1);
|
||||
arr[0] = member;
|
||||
}
|
||||
|
||||
function signAndEncodeVM(
|
||||
uint32 timestamp,
|
||||
uint32 nonce,
|
||||
uint16 emitterChainId,
|
||||
bytes32 emitterAddress,
|
||||
uint64 sequence,
|
||||
bytes memory data,
|
||||
uint256[] memory signers,
|
||||
uint32 guardianSetIndex,
|
||||
uint8 consistencyLevel
|
||||
) public returns (bytes memory signedMessage) {
|
||||
bytes memory body = abi.encodePacked(
|
||||
timestamp,
|
||||
nonce,
|
||||
emitterChainId,
|
||||
emitterAddress,
|
||||
sequence,
|
||||
consistencyLevel,
|
||||
data
|
||||
);
|
||||
bytes32 bodyHash = keccak256(abi.encodePacked(keccak256(body)));
|
||||
|
||||
// Sign the hash with the devnet guardian private key
|
||||
IWormhole.Signature[] memory sigs = new IWormhole.Signature[](
|
||||
signers.length
|
||||
);
|
||||
for (uint256 i = 0; i < signers.length; i++) {
|
||||
(sigs[i].v, sigs[i].r, sigs[i].s) = vm.sign(signers[i], bodyHash);
|
||||
sigs[i].guardianIndex = 0;
|
||||
}
|
||||
|
||||
signedMessage = abi.encodePacked(
|
||||
uint8(1),
|
||||
guardianSetIndex,
|
||||
uint8(sigs.length)
|
||||
);
|
||||
|
||||
for (uint256 i = 0; i < signers.length; i++) {
|
||||
signedMessage = abi.encodePacked(
|
||||
signedMessage,
|
||||
sigs[i].guardianIndex,
|
||||
sigs[i].r,
|
||||
sigs[i].s,
|
||||
sigs[i].v - 27
|
||||
);
|
||||
}
|
||||
|
||||
signedMessage = abi.encodePacked(signedMessage, body);
|
||||
}
|
||||
|
||||
function testShouldRejectSmartContractUpgradesOnForks() public {
|
||||
uint32 timestamp = 1000;
|
||||
uint32 nonce = 1001;
|
||||
|
||||
// Perform a successful upgrade
|
||||
MockNFTBridgeImplementation mock = new MockNFTBridgeImplementation();
|
||||
|
||||
bytes memory data = abi.encodePacked(
|
||||
NFTBridgeModule,
|
||||
actionContractUpgrade,
|
||||
testChainId,
|
||||
addressToBytes32(address(mock))
|
||||
);
|
||||
bytes memory vaa = signAndEncodeVM(
|
||||
timestamp,
|
||||
nonce,
|
||||
governanceChainId,
|
||||
governanceContract,
|
||||
0,
|
||||
data,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
2
|
||||
);
|
||||
|
||||
bytes32 IMPLEMENTATION_STORAGE_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
||||
bytes32 before = vm.load(address(bridge), IMPLEMENTATION_STORAGE_SLOT);
|
||||
|
||||
bridge.upgrade(vaa);
|
||||
|
||||
bytes32 afterUpgrade = vm.load(
|
||||
address(bridge),
|
||||
IMPLEMENTATION_STORAGE_SLOT
|
||||
);
|
||||
assertEq(afterUpgrade, addressToBytes32(address(mock)));
|
||||
assertEq(
|
||||
MockNFTBridgeImplementation(payable(address(bridge)))
|
||||
.testNewImplementationActive(),
|
||||
true,
|
||||
"New implementation not active"
|
||||
);
|
||||
|
||||
// Overwrite EVM Chain ID
|
||||
MockNFTBridgeImplementation(payable(address(bridge)))
|
||||
.testOverwriteEVMChainId(fakeChainId, fakeEvmChainId);
|
||||
assertEq(
|
||||
bridge.chainId(),
|
||||
fakeChainId,
|
||||
"Overwrite didn't work for chain ID"
|
||||
);
|
||||
assertEq(
|
||||
bridge.evmChainId(),
|
||||
fakeEvmChainId,
|
||||
"Overwrite didn't work for evm chain ID"
|
||||
);
|
||||
|
||||
data = abi.encodePacked(
|
||||
NFTBridgeModule,
|
||||
actionContractUpgrade,
|
||||
testChainId,
|
||||
addressToBytes32(address(mock))
|
||||
);
|
||||
vaa = signAndEncodeVM(
|
||||
timestamp,
|
||||
nonce,
|
||||
governanceChainId,
|
||||
governanceContract,
|
||||
0,
|
||||
data,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
2
|
||||
);
|
||||
|
||||
vm.expectRevert("invalid fork");
|
||||
bridge.upgrade(vaa);
|
||||
}
|
||||
|
||||
function testShouldAllowRecoverChainIDGovernancePacketsForks() public {
|
||||
uint32 timestamp = 1000;
|
||||
uint32 nonce = 1001;
|
||||
|
||||
// Perform a successful upgrade
|
||||
MockNFTBridgeImplementation mock = new MockNFTBridgeImplementation();
|
||||
|
||||
bytes memory data = abi.encodePacked(
|
||||
NFTBridgeModule,
|
||||
actionContractUpgrade,
|
||||
testChainId,
|
||||
addressToBytes32(address(mock))
|
||||
);
|
||||
bytes memory vaa = signAndEncodeVM(
|
||||
timestamp,
|
||||
nonce,
|
||||
governanceChainId,
|
||||
governanceContract,
|
||||
0,
|
||||
data,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
2
|
||||
);
|
||||
|
||||
bytes32 IMPLEMENTATION_STORAGE_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
||||
bytes32 before = vm.load(address(bridge), IMPLEMENTATION_STORAGE_SLOT);
|
||||
|
||||
bridge.upgrade(vaa);
|
||||
|
||||
bytes32 afterUpgrade = vm.load(
|
||||
address(bridge),
|
||||
IMPLEMENTATION_STORAGE_SLOT
|
||||
);
|
||||
assertEq(afterUpgrade, bytes32(uint256(uint160(address(mock)))));
|
||||
assertEq(
|
||||
MockNFTBridgeImplementation(payable(address(bridge)))
|
||||
.testNewImplementationActive(),
|
||||
true,
|
||||
"New implementation not active"
|
||||
);
|
||||
|
||||
// Overwrite EVM Chain ID
|
||||
MockNFTBridgeImplementation(payable(address(bridge)))
|
||||
.testOverwriteEVMChainId(fakeChainId, fakeEvmChainId);
|
||||
assertEq(
|
||||
bridge.chainId(),
|
||||
fakeChainId,
|
||||
"Overwrite didn't work for chain ID"
|
||||
);
|
||||
assertEq(
|
||||
bridge.evmChainId(),
|
||||
fakeEvmChainId,
|
||||
"Overwrite didn't work for evm chain ID"
|
||||
);
|
||||
|
||||
// recover chain ID
|
||||
data = abi.encodePacked(
|
||||
NFTBridgeModule,
|
||||
actionRecoverChainId,
|
||||
testEvmChainId,
|
||||
testChainId
|
||||
);
|
||||
vaa = signAndEncodeVM(
|
||||
timestamp,
|
||||
nonce,
|
||||
governanceChainId,
|
||||
governanceContract,
|
||||
0,
|
||||
data,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
2
|
||||
);
|
||||
|
||||
bridge.submitRecoverChainId(vaa);
|
||||
|
||||
assertEq(
|
||||
bridge.chainId(),
|
||||
testChainId,
|
||||
"Recover didn't work for chain ID"
|
||||
);
|
||||
assertEq(
|
||||
bridge.evmChainId(),
|
||||
testEvmChainId,
|
||||
"Recover didn't work for evm chain ID"
|
||||
);
|
||||
}
|
||||
|
||||
function testShouldAcceptSmartContractUpgradesAfterChainIdHasBeenRecovered()
|
||||
public
|
||||
{
|
||||
uint32 timestamp = 1000;
|
||||
uint32 nonce = 1001;
|
||||
|
||||
// Perform a successful upgrade
|
||||
MockNFTBridgeImplementation mock = new MockNFTBridgeImplementation();
|
||||
|
||||
bytes memory data = abi.encodePacked(
|
||||
NFTBridgeModule,
|
||||
actionContractUpgrade,
|
||||
testChainId,
|
||||
addressToBytes32(address(mock))
|
||||
);
|
||||
bytes memory vaa = signAndEncodeVM(
|
||||
timestamp,
|
||||
nonce,
|
||||
governanceChainId,
|
||||
governanceContract,
|
||||
0,
|
||||
data,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
2
|
||||
);
|
||||
|
||||
bytes32 IMPLEMENTATION_STORAGE_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
||||
bytes32 before = vm.load(address(bridge), IMPLEMENTATION_STORAGE_SLOT);
|
||||
|
||||
bridge.upgrade(vaa);
|
||||
|
||||
bytes32 afterUpgrade = vm.load(
|
||||
address(bridge),
|
||||
IMPLEMENTATION_STORAGE_SLOT
|
||||
);
|
||||
assertEq(afterUpgrade, bytes32(uint256(uint160(address(mock)))));
|
||||
assertEq(
|
||||
MockNFTBridgeImplementation(payable(address(bridge)))
|
||||
.testNewImplementationActive(),
|
||||
true,
|
||||
"New implementation not active"
|
||||
);
|
||||
|
||||
// Overwrite EVM Chain ID
|
||||
MockNFTBridgeImplementation(payable(address(bridge)))
|
||||
.testOverwriteEVMChainId(fakeChainId, fakeEvmChainId);
|
||||
assertEq(
|
||||
bridge.chainId(),
|
||||
fakeChainId,
|
||||
"Overwrite didn't work for chain ID"
|
||||
);
|
||||
assertEq(
|
||||
bridge.evmChainId(),
|
||||
fakeEvmChainId,
|
||||
"Overwrite didn't work for evm chain ID"
|
||||
);
|
||||
|
||||
// recover chain ID
|
||||
data = abi.encodePacked(
|
||||
NFTBridgeModule,
|
||||
actionRecoverChainId,
|
||||
testEvmChainId,
|
||||
testChainId
|
||||
);
|
||||
vaa = signAndEncodeVM(
|
||||
timestamp,
|
||||
nonce,
|
||||
governanceChainId,
|
||||
governanceContract,
|
||||
0,
|
||||
data,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
2
|
||||
);
|
||||
|
||||
bridge.submitRecoverChainId(vaa);
|
||||
|
||||
assertEq(
|
||||
bridge.chainId(),
|
||||
testChainId,
|
||||
"Recover didn't work for chain ID"
|
||||
);
|
||||
assertEq(
|
||||
bridge.evmChainId(),
|
||||
testEvmChainId,
|
||||
"Recover didn't work for evm chain ID"
|
||||
);
|
||||
|
||||
// Perform a successful upgrade
|
||||
mock = new MockNFTBridgeImplementation();
|
||||
|
||||
data = abi.encodePacked(
|
||||
NFTBridgeModule,
|
||||
actionContractUpgrade,
|
||||
testChainId,
|
||||
addressToBytes32(address(mock))
|
||||
);
|
||||
vaa = signAndEncodeVM(
|
||||
timestamp,
|
||||
nonce,
|
||||
governanceChainId,
|
||||
governanceContract,
|
||||
0,
|
||||
data,
|
||||
uint256Array(testGuardian),
|
||||
0,
|
||||
2
|
||||
);
|
||||
|
||||
before = vm.load(address(bridge), IMPLEMENTATION_STORAGE_SLOT);
|
||||
|
||||
bridge.upgrade(vaa);
|
||||
|
||||
afterUpgrade = vm.load(address(bridge), IMPLEMENTATION_STORAGE_SLOT);
|
||||
assertEq(afterUpgrade, bytes32(uint256(uint160(address(mock)))));
|
||||
assertEq(
|
||||
MockNFTBridgeImplementation(payable(address(bridge)))
|
||||
.testNewImplementationActive(),
|
||||
true,
|
||||
"New implementation not active"
|
||||
);
|
||||
}
|
||||
|
||||
function onERC721Received(
|
||||
address,
|
||||
address,
|
||||
uint256,
|
||||
bytes calldata
|
||||
) external returns (bytes4) {
|
||||
return 0x150b7a02;
|
||||
}
|
||||
|
||||
fallback() external payable {}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// test/Messages.sol
|
||||
// SPDX-License-Identifier: Apache 2
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../contracts/bridge/token/TokenImplementation.sol";
|
||||
import "../contracts/Setters.sol";
|
||||
import "../contracts/bridge/utils/Migrator.sol";
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
contract TestTokenMigrator is Test {
|
||||
TokenImplementation fromToken;
|
||||
TokenImplementation toToken;
|
||||
uint8 fromDecimals = 8;
|
||||
uint8 toDecimals = 18;
|
||||
Migrator migrator;
|
||||
|
||||
function setUp() public {
|
||||
fromToken = new TokenImplementation();
|
||||
fromToken.initialize(
|
||||
"TestFrom",
|
||||
"FROM",
|
||||
fromDecimals,
|
||||
0,
|
||||
address(this),
|
||||
0,
|
||||
bytes32(0x0)
|
||||
);
|
||||
|
||||
toToken = new TokenImplementation();
|
||||
toToken.initialize(
|
||||
"TestTo",
|
||||
"TO",
|
||||
toDecimals,
|
||||
0,
|
||||
address(this),
|
||||
0,
|
||||
bytes32(0x0)
|
||||
);
|
||||
migrator = new Migrator(address(fromToken), address(toToken));
|
||||
|
||||
assertEq(address(migrator.fromAsset()), address(fromToken));
|
||||
assertEq(address(migrator.toAsset()), address(toToken));
|
||||
assertEq(migrator.fromDecimals(), fromDecimals);
|
||||
assertEq(migrator.toDecimals(), toDecimals);
|
||||
}
|
||||
|
||||
function testShouldGiveOutLPTokens1to1ForAToTokenDeposit() public {
|
||||
uint256 amount = 1000000000000000000;
|
||||
toToken.mint(address(this), amount);
|
||||
toToken.approve(address(migrator), amount);
|
||||
migrator.add(amount);
|
||||
assertEq(migrator.balanceOf(address(this)), amount);
|
||||
assertEq(toToken.balanceOf(address(migrator)), amount);
|
||||
}
|
||||
|
||||
function testShouldRefundToTokenForLPTokens() public {
|
||||
testShouldGiveOutLPTokens1to1ForAToTokenDeposit();
|
||||
uint256 amount = 500000000000000000;
|
||||
migrator.remove(amount);
|
||||
assertEq(migrator.balanceOf(address(this)), amount);
|
||||
assertEq(toToken.balanceOf(address(migrator)), amount);
|
||||
assertEq(toToken.balanceOf(address(this)), amount);
|
||||
}
|
||||
|
||||
function testShouldRedeemFromTokenToToTokenAdjustingForDecimals() public {
|
||||
testShouldRefundToTokenForLPTokens();
|
||||
address newAddr = address(0x1);
|
||||
fromToken.mint(newAddr, 50000000);
|
||||
vm.prank(newAddr);
|
||||
fromToken.approve(address(migrator), 50000000);
|
||||
vm.prank(newAddr);
|
||||
migrator.migrate(50000000);
|
||||
|
||||
assertEq(toToken.balanceOf(newAddr), 500000000000000000);
|
||||
assertEq(toToken.balanceOf(address(migrator)), 0);
|
||||
assertEq(fromToken.balanceOf(newAddr), 0);
|
||||
assertEq(fromToken.balanceOf(address(migrator)), 50000000);
|
||||
}
|
||||
|
||||
function testFromTokenShouldBeClaimableForLPTokensAdjustingForDecimals()
|
||||
public
|
||||
{
|
||||
testShouldRedeemFromTokenToToTokenAdjustingForDecimals();
|
||||
migrator.claim(500000000000000000);
|
||||
|
||||
assertEq(fromToken.balanceOf(address(this)), 50000000);
|
||||
assertEq(fromToken.balanceOf(address(migrator)), 0);
|
||||
assertEq(migrator.balanceOf(address(this)), 0);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue