diff --git a/README.md b/README.md index e65e6bd..b4166c6 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,9 @@ For example, if you are using a solc version newer than `0.8.19` and are plannin It is strongly recommended that you run the forge test suite of this SDK with your own compiler version to catch potential errors that stem from differences in compiler versions early. Yes, strictly speaking the Solidity version pragma should prevent these issues, but better to be safe than sorry, especially given that some components make extensive use of inline assembly. -**IERC20 Remapping** +**IERC20 and SafeERC20 Remapping** -This SDK comes with its own IERC20 interface. Given that projects tend to combine different SDKs, there's often this annoying issue of clashes of IERC20 interfaces, even though the are effectively the same. We handle this issue by importing `IERC20/IERC20.sol` which allows remapping the `IERC20/` prefix to whatever directory contains `IERC20.sol` in your project, thus providing an override mechanism that should allow dealing with this problem seamlessly until forge allows remapping of individual files. +This SDK comes with its own IERC20 interface and SafeERC20 implementation. Given that projects tend to combine different SDKs, there's often this annoying issue of clashes of IERC20 interfaces, even though the are effectively the same. We handle this issue by importing `IERC20/IERC20.sol` which allows remapping the `IERC20/` prefix to whatever directory contains `IERC20.sol` in your project, thus providing an override mechanism that should allow dealing with this problem seamlessly until forge allows remapping of individual files. The same approach is used for SafeERC20. ## Components diff --git a/foundry.toml b/foundry.toml index 7016577..f553a94 100644 --- a/foundry.toml +++ b/foundry.toml @@ -16,7 +16,7 @@ remappings = [ "forge-std/=lib/forge-std/src/", "wormhole-sdk/=src/", "IERC20/=src/interfaces/token/", - "@openzeppelin/=lib/openzeppelin-contracts/contracts/" + "SafeERC20/=src/libraries/", ] verbosity = 3 diff --git a/src/TransferUtils.sol b/src/TransferUtils.sol deleted file mode 100644 index a53efbf..0000000 --- a/src/TransferUtils.sol +++ /dev/null @@ -1,26 +0,0 @@ - -// SPDX-License-Identifier: Apache 2 -pragma solidity ^0.8.19; - -import {SafeERC20} from "@openzeppelin/token/ERC20/utils/SafeERC20.sol"; -import {IERC20} from "@openzeppelin/token/ERC20/IERC20.sol"; - -/** - * Payment to the target failed. - */ -error PaymentFailure(address target); - -using SafeERC20 for IERC20; -function transferTokens(address token, address to, uint256 amount) { - if (token == address(0)) - _transferEth(to, amount); - else - IERC20(token).safeTransfer(to, amount); -} - -function _transferEth(address to, uint256 amount) { - if (amount == 0) return; - - (bool success, ) = to.call{value: amount}(new bytes(0)); - if (!success) revert PaymentFailure(to); -} \ No newline at end of file diff --git a/src/Utils.sol b/src/Utils.sol index 63949f8..4b39b85 100644 --- a/src/Utils.sol +++ b/src/Utils.sol @@ -2,27 +2,6 @@ // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; -error NotAnEvmAddress(bytes32); - -function toUniversalAddress(address addr) pure returns (bytes32 universalAddr) { - universalAddr = bytes32(uint256(uint160(addr))); -} - -function fromUniversalAddress(bytes32 universalAddr) pure returns (address addr) { - if (bytes12(universalAddr) != 0) - revert NotAnEvmAddress(universalAddr); - - assembly ("memory-safe") { - addr := universalAddr - } -} - -/** - * Reverts with a given buffer data. - * Meant to be used to easily bubble up errors from low level calls when they fail. - */ -function reRevert(bytes memory err) pure { - assembly ("memory-safe") { - revert(add(err, 32), mload(err)) - } -} +import {tokenOrNativeTransfer} from "wormhole-sdk/utils/Transfer.sol"; +import {reRevert} from "wormhole-sdk/utils/Revert.sol"; +import {toUniversalAddress, fromUniversalAddress} from "wormhole-sdk/utils/UniversalAddress.sol"; diff --git a/src/dispatcherComponents/AccessControl.sol b/src/components/dispatcher/AccessControl.sol similarity index 85% rename from src/dispatcherComponents/AccessControl.sol rename to src/components/dispatcher/AccessControl.sol index fd9cf48..2d617c5 100644 --- a/src/dispatcherComponents/AccessControl.sol +++ b/src/components/dispatcher/AccessControl.sol @@ -3,7 +3,19 @@ pragma solidity ^0.8.4; import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; -import "./ids.sol"; +import { + ACCESS_CONTROL_ID, + ACCESS_CONTROL_QUERIES_ID, + OWNER_ID, + PENDING_OWNER_ID, + IS_ADMIN_ID, + ADMINS_ID, + REVOKE_ADMIN_ID, + ADD_ADMIN_ID, + PROPOSE_OWNERSHIP_TRANSFER_ID, + ACQUIRE_OWNERSHIP_ID, + RELINQUISH_OWNERSHIP_ID +} from "wormhole-sdk/components/dispatcher/Ids.sol"; //rationale for different roles (owner, admin): // * owner should be a mulit-sig / ultra cold wallet that is only activated in exceptional @@ -42,10 +54,15 @@ enum Role { Admin } -function senderHasAuth() view returns (Role) { - Role role = senderRole(); - if (Role.None == role) +function failAuthIf(bool condition) pure { + if (condition) revert NotAuthorized(); +} + +function senderAtLeastAdmin() view returns (Role) { + Role role = senderRole(); + failAuthIf(role == Role.None); + return role; } @@ -53,10 +70,8 @@ function senderRole() view returns (Role) { AccessControlState storage state = accessControlState(); if (msg.sender == state.owner) //check highest privilege level first return Role.Owner; - else if (state.isAdmin[msg.sender] != 0) - return Role.Admin; - else - return Role.None; + + return state.isAdmin[msg.sender] != 0 ? Role.Admin : Role.None; } abstract contract AccessControl { @@ -77,16 +92,14 @@ abstract contract AccessControl { function transferOwnership(address newOwner) external { AccessControlState storage state = accessControlState(); - if (msg.sender != state.owner) - revert NotAuthorized(); + failAuthIf(msg.sender != state.owner); state.pendingOwner = newOwner; } function cancelOwnershipTransfer() external { AccessControlState storage state = accessControlState(); - if (state.owner != msg.sender) - revert NotAuthorized(); + failAuthIf(msg.sender != state.owner); state.pendingOwner = address(0); } @@ -97,27 +110,31 @@ abstract contract AccessControl { // ---- internals ---- - /** - * Dispatch an execute function. Execute functions almost always modify contract state. - */ - function dispatchExecAccessControl(bytes calldata data, uint256 offset, uint8 command) internal returns (bool, uint256) { + function dispatchExecAccessControl( + bytes calldata data, + uint offset, + uint8 command + ) internal returns (bool, uint) { if (command == ACCESS_CONTROL_ID) offset = _batchAccessControlCommands(data, offset); else if (command == ACQUIRE_OWNERSHIP_ID) _acquireOwnership(); - else return (false, offset); + else + return (false, offset); return (true, offset); } - /** - * Dispatch a query function. Query functions never modify contract state. - */ - function dispatchQueryAccessControl(bytes calldata data, uint256 offset, uint8 query) view internal returns (bool, bytes memory, uint256) { + function dispatchQueryAccessControl( + bytes calldata data, + uint offset, + uint8 query + ) view internal returns (bool, bytes memory, uint) { bytes memory result; if (query == ACCESS_CONTROL_QUERIES_ID) (result, offset) = _batchAccessControlQueries(data, offset); - else return (false, new bytes(0), offset); + else + return (false, new bytes(0), offset); return (true, result, offset); } @@ -127,7 +144,7 @@ abstract contract AccessControl { uint offset ) internal returns (uint) { AccessControlState storage state = accessControlState(); - bool isOwner = senderHasAuth() == Role.Owner; + bool isOwner = senderAtLeastAdmin() == Role.Owner; uint remainingCommands; (remainingCommands, offset) = commands.asUint8CdUnchecked(offset); @@ -180,12 +197,12 @@ abstract contract AccessControl { for (uint i = 0; i < remainingQueries; ++i) { uint8 query; (query, offset) = queries.asUint8CdUnchecked(offset); - + if (query == IS_ADMIN_ID) { address admin; (admin, offset) = queries.asAddressCdUnchecked(offset); ret = abi.encodePacked(ret, state.isAdmin[admin] != 0); - } + } else if (query == ADMINS_ID) { ret = abi.encodePacked(ret, uint8(state.admins.length)); for (uint j = 0; j < state.admins.length; ++j) @@ -209,7 +226,7 @@ abstract contract AccessControl { function _acquireOwnership() internal { AccessControlState storage state = accessControlState(); - if (state.pendingOwner != msg.sender) + if (msg.sender !=state.pendingOwner) revert NotAuthorized(); state.pendingOwner = address(0); diff --git a/src/dispatcherComponents/ids.sol b/src/components/dispatcher/Ids.sol similarity index 100% rename from src/dispatcherComponents/ids.sol rename to src/components/dispatcher/Ids.sol diff --git a/src/components/dispatcher/SweepTokens.sol b/src/components/dispatcher/SweepTokens.sol new file mode 100644 index 0000000..780cae5 --- /dev/null +++ b/src/components/dispatcher/SweepTokens.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.4; + +import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; +import {tokenOrNativeTransfer} from "wormhole-sdk/utils/Transfer.sol"; +import {senderAtLeastAdmin} from "wormhole-sdk/components/dispatcher/AccessControl.sol"; +import {SWEEP_TOKENS_ID} from "wormhole-sdk/components/dispatcher/Ids.sol"; + +abstract contract SweepTokens { + using BytesParsing for bytes; + + function dispatchExecSweepTokens( + bytes calldata data, + uint offset, + uint8 command + ) internal returns (bool, uint) { + return command == SWEEP_TOKENS_ID + ? (true, _sweepTokens(data, offset)) + : (false, offset); + } + + function _sweepTokens( + bytes calldata commands, + uint offset + ) internal returns (uint) { + senderAtLeastAdmin(); + + address token; + uint256 amount; + (token, offset) = commands.asAddressCdUnchecked(offset); + (amount, offset) = commands.asUint256CdUnchecked(offset); + + tokenOrNativeTransfer(token, msg.sender, amount); + return offset; + } +} \ No newline at end of file diff --git a/src/components/dispatcher/Upgrade.sol b/src/components/dispatcher/Upgrade.sol new file mode 100644 index 0000000..bba180c --- /dev/null +++ b/src/components/dispatcher/Upgrade.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.4; + +import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; +import {ProxyBase} from "wormhole-sdk/proxy/ProxyBase.sol"; +import {Role, senderRole, failAuthIf} from "wormhole-sdk/components/dispatcher/AccessControl.sol"; +import {UPGRADE_CONTRACT_ID, IMPLEMENTATION_ID} from "wormhole-sdk/components/dispatcher/Ids.sol"; + +error InvalidGovernanceCommand(uint8 command); +error InvalidGovernanceQuery(uint8 query); + +abstract contract Upgrade is ProxyBase { + using BytesParsing for bytes; + + function dispatchExecUpgrade( + bytes calldata data, + uint offset, + uint8 command + ) internal returns (bool, uint) { + return (command == UPGRADE_CONTRACT_ID) + ? (true, _upgradeContract(data, offset)) + : (false, offset); + } + + function dispatchQueryUpgrade( + bytes calldata, + uint offset, + uint8 query + ) view internal returns (bool, bytes memory, uint) { + return query == IMPLEMENTATION_ID + ? (true, abi.encodePacked(_getImplementation()), offset) + : (false, new bytes(0), offset); + } + + function upgrade(address implementation, bytes calldata data) external { + failAuthIf(senderRole() != Role.Owner); + + _upgradeTo(implementation, data); + } + + function _upgradeContract( + bytes calldata commands, + uint offset + ) internal returns (uint) { + failAuthIf(senderRole() != Role.Owner); + + address newImplementation; + (newImplementation, offset) = commands.asAddressCdUnchecked(offset); + //contract upgrades must be the last command in the batch + commands.checkLengthCd(offset); + + _upgradeTo(newImplementation, new bytes(0)); + + return offset; + } +} diff --git a/src/constants/Common.sol b/src/constants/Common.sol index 27f19d9..f3ac5f5 100644 --- a/src/constants/Common.sol +++ b/src/constants/Common.sol @@ -1,11 +1,17 @@ // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.4; -uint256 constant FREE_MEMORY_PTR = 0x40; +// ┌──────────────────────────────────────────────────────────────────────────────┐ +// │ NOTE: We can't define e.g. WORD_SIZE_MINUS_ONE via WORD_SIZE - 1 because │ +// │ of solc restrictions on what constants can be used in inline assembly. │ +// └──────────────────────────────────────────────────────────────────────────────┘ + uint256 constant WORD_SIZE = 32; -//we can't define _WORD_SIZE_MINUS_ONE via _WORD_SIZE - 1 because of solc restrictions -// what constants can be used in inline assembly uint256 constant WORD_SIZE_MINUS_ONE = 31; //=0x1f=0b00011111 - //see section "prefer `< MAX + 1` over `<= MAX` for const comparison" in docs/Optimization.md uint256 constant WORD_SIZE_PLUS_ONE = 33; + +uint256 constant SCRATCH_SPACE_PTR = 0x00; +uint256 constant SCRATCH_SPACE_SIZE = 64; + +uint256 constant FREE_MEMORY_PTR = 0x40; \ No newline at end of file diff --git a/src/dispatcherComponents/SweepTokens.sol b/src/dispatcherComponents/SweepTokens.sol deleted file mode 100644 index 9f16da4..0000000 --- a/src/dispatcherComponents/SweepTokens.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: Apache 2 - -pragma solidity ^0.8.4; - -import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; -import {transferTokens} from "../TransferUtils.sol"; -import {senderHasAuth} from "./AccessControl.sol"; -import "./ids.sol"; - -abstract contract SweepTokens { - using BytesParsing for bytes; - - /** - * Dispatch an execute function. Execute functions almost always modify contract state. - */ - function dispatchExecSweepTokens(bytes calldata data, uint256 offset, uint8 command) internal returns (bool, uint256) { - if (command == SWEEP_TOKENS_ID) - offset = _sweepTokens(data, offset); - else return (false, offset); - - return (true, offset); - } - - function _sweepTokens( - bytes calldata commands, - uint offset - ) internal returns (uint) { - senderHasAuth(); - - address token; - uint256 amount; - (token, offset) = commands.asAddressCdUnchecked(offset); - (amount, offset) = commands.asUint256CdUnchecked(offset); - - transferTokens(token, msg.sender, amount); - return offset; - } -} \ No newline at end of file diff --git a/src/dispatcherComponents/Upgrade.sol b/src/dispatcherComponents/Upgrade.sol deleted file mode 100644 index 6e99a28..0000000 --- a/src/dispatcherComponents/Upgrade.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: Apache 2 - -pragma solidity ^0.8.4; - -import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; -import {ProxyBase} from "wormhole-sdk/proxy/ProxyBase.sol"; -import { - accessControlState, - AccessControlState, - NotAuthorized, - senderHasAuth, - Role -} from "./AccessControl.sol"; -import "./ids.sol"; - -error InvalidGovernanceCommand(uint8 command); -error InvalidGovernanceQuery(uint8 query); - -abstract contract Upgrade is ProxyBase { - using BytesParsing for bytes; - - /** - * Dispatch an execute function. Execute functions almost always modify contract state. - */ - function dispatchExecUpgrade(bytes calldata data, uint256 offset, uint8 command) internal returns (bool, uint256) { - if (command == UPGRADE_CONTRACT_ID) - offset = _upgradeContract(data, offset); - else return (false, offset); - - return (true, offset); - } - - /** - * Dispatch a query function. Query functions never modify contract state. - */ - function dispatchQueryUpgrade(bytes calldata, uint256 offset, uint8 query) view internal returns (bool, bytes memory, uint256) { - bytes memory result; - if (query == IMPLEMENTATION_ID) - result = abi.encodePacked(_getImplementation()); - else return (false, new bytes(0), offset); - - return (true, result, offset); - } - - function upgrade(address implementation, bytes calldata data) external { - if (senderHasAuth() != Role.Owner) - revert NotAuthorized(); - - _upgradeTo(implementation, data); - } - - function _upgradeContract( - bytes calldata commands, - uint offset - ) internal returns (uint) { - if (senderHasAuth() != Role.Owner) - revert NotAuthorized(); - - address newImplementation; - (newImplementation, offset) = commands.asAddressCdUnchecked(offset); - //contract upgrades must be the last command in the batch - commands.checkLengthCd(offset); - - _upgradeTo(newImplementation, new bytes(0)); - - return offset; - } -} diff --git a/src/libraries/BytesParsing.sol b/src/libraries/BytesParsing.sol index 86a0aac..9f886af 100644 --- a/src/libraries/BytesParsing.sol +++ b/src/libraries/BytesParsing.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.4; -import "../constants/Common.sol"; +import "wormhole-sdk/constants/Common.sol"; //This file appears comically large, but all unused functions are removed by the compiler. library BytesParsing { diff --git a/src/libraries/SafeERC20.sol b/src/libraries/SafeERC20.sol new file mode 100644 index 0000000..dfa7e90 --- /dev/null +++ b/src/libraries/SafeERC20.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.4; + +import {IERC20} from "IERC20/IERC20.sol"; +import {WORD_SIZE, SCRATCH_SPACE_PTR} from "wormhole-sdk/constants/Common.sol"; + +//Like OpenZeppelin's SafeERC20.sol, but slimmed down and more gas efficient. +// +//The main difference to OZ's implementation (besides the missing functions) is that we skip the +// EXTCODESIZE check that OZ does upon successful calls to ensure that an actual contract was +// called. The rationale for omitting this check is that ultimately the contract using the token +// has to verify that it "makes sense" for its use case regardless. Otherwise, a random token, or +// even just a contract that always returns true, could be passed, which makes this check +// superfluous in the final analysis. +// +//We also save on code size by not duplicating the assembly code in two separate functions. +// Otoh, we simply swallow revert reasons of failing token operations instead of bubbling them up. +// This is less clean and makes debugging harder, but is likely still a worthwhile trade-off +// given the cost in gas and code size. +library SafeERC20 { + error SafeERC20FailedOperation(address token); + + function safeTransfer(IERC20 token, address to, uint256 value) internal { + _revertOnFailure(token, abi.encodeCall(token.transfer, (to, value))); + } + + function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { + _revertOnFailure(token, abi.encodeCall(token.transferFrom, (from, to, value))); + } + + function forceApprove(IERC20 token, address spender, uint256 value) internal { + bytes memory approveCall = abi.encodeCall(token.approve, (spender, value)); + + if (!_callWithOptionalReturnCheck(token, approveCall)) { + _revertOnFailure(token, abi.encodeCall(token.approve, (spender, 0))); + _revertOnFailure(token, approveCall); + } + } + + function _callWithOptionalReturnCheck( + IERC20 token, + bytes memory encodedCall + ) private returns (bool success) { + /// @solidity memory-safe-assembly + assembly { + mstore(SCRATCH_SPACE_PTR, 0) + success := call( //see https://www.evm.codes/?fork=cancun#f1 + gas(), //gas + token, //callee + 0, //value + add(encodedCall, WORD_SIZE), //input ptr + mload(encodedCall), //input size + SCRATCH_SPACE_PTR, //output ptr + WORD_SIZE //output size + ) + //calls to addresses without code are always successful + if success { + success := or(iszero(returndatasize()), mload(SCRATCH_SPACE_PTR)) + } + } + } + + function _revertOnFailure(IERC20 token, bytes memory encodedCall) private { + if (!_callWithOptionalReturnCheck(token, encodedCall)) + revert SafeERC20FailedOperation(address(token)); + } +} diff --git a/src/utils/Revert.sol b/src/utils/Revert.sol new file mode 100644 index 0000000..c5c2928 --- /dev/null +++ b/src/utils/Revert.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.19; + +import {WORD_SIZE} from "wormhole-sdk/constants/Common.sol"; + +//bubble up errors from low level calls +function reRevert(bytes memory err) pure { + assembly ("memory-safe") { + revert(add(err, WORD_SIZE), mload(err)) + } +} diff --git a/src/utils/Transfer.sol b/src/utils/Transfer.sol new file mode 100644 index 0000000..c1774b2 --- /dev/null +++ b/src/utils/Transfer.sol @@ -0,0 +1,23 @@ + +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.19; + +import {IERC20} from "IERC20/IERC20.sol"; +import {SafeERC20} from "SafeERC20/SafeERC20.sol"; + +error PaymentFailure(address target); + +//Note: Always forwards all gas, so consider gas griefing attack opportunities by the recipient. +//Note: Don't use this method if you need events for 0 amount transfers. +function tokenOrNativeTransfer(address tokenOrZeroForNative, address to, uint256 amount) { + if (amount == 0) + return; + + if (tokenOrZeroForNative == address(0)) { + (bool success, ) = to.call{value: amount}(new bytes(0)); + if (!success) + revert PaymentFailure(to); + } + else + SafeERC20.safeTransfer(IERC20(tokenOrZeroForNative), to, amount); +} diff --git a/src/utils/UniversalAddress.sol b/src/utils/UniversalAddress.sol new file mode 100644 index 0000000..eb607f1 --- /dev/null +++ b/src/utils/UniversalAddress.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.19; + +error NotAnEvmAddress(bytes32); + +function toUniversalAddress(address addr) pure returns (bytes32 universalAddr) { + universalAddr = bytes32(uint256(uint160(addr))); +} + +function fromUniversalAddress(bytes32 universalAddr) pure returns (address addr) { + if (bytes12(universalAddr) != 0) + revert NotAnEvmAddress(universalAddr); + + assembly ("memory-safe") { + addr := universalAddr + } +} diff --git a/test/dispatcherComponents/AccessControl.t.sol b/test/components/dispatcher/AccessControl.t.sol similarity index 95% rename from test/dispatcherComponents/AccessControl.t.sol rename to test/components/dispatcher/AccessControl.t.sol index 435872f..3a2d3bd 100644 --- a/test/dispatcherComponents/AccessControl.t.sol +++ b/test/components/dispatcher/AccessControl.t.sol @@ -2,10 +2,22 @@ pragma solidity ^0.8.4; -import { NotAuthorized } from "wormhole-sdk/dispatcherComponents/AccessControl.sol"; -import { BytesParsing } from "wormhole-sdk/libraries/BytesParsing.sol"; -import { DispatcherTestBase } from "./utils/DispatcherTestBase.sol"; -import "wormhole-sdk/dispatcherComponents/ids.sol"; +import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; +import {NotAuthorized} from "wormhole-sdk/components/dispatcher/AccessControl.sol"; +import { + ACCESS_CONTROL_ID, + ACCESS_CONTROL_QUERIES_ID, + OWNER_ID, + PENDING_OWNER_ID, + ACQUIRE_OWNERSHIP_ID, + IS_ADMIN_ID, + ADMINS_ID, + REVOKE_ADMIN_ID, + ADD_ADMIN_ID, + PROPOSE_OWNERSHIP_TRANSFER_ID, + RELINQUISH_OWNERSHIP_ID +} from "wormhole-sdk/components/dispatcher/Ids.sol"; +import {DispatcherTestBase} from "./utils/DispatcherTestBase.sol"; contract AcessControlTest is DispatcherTestBase { using BytesParsing for bytes; diff --git a/test/dispatcherComponents/SweepTokens.t.sol b/test/components/dispatcher/SweepTokens.t.sol similarity index 85% rename from test/dispatcherComponents/SweepTokens.t.sol rename to test/components/dispatcher/SweepTokens.t.sol index 0fb1d96..f57efd9 100644 --- a/test/dispatcherComponents/SweepTokens.t.sol +++ b/test/components/dispatcher/SweepTokens.t.sol @@ -2,9 +2,10 @@ pragma solidity ^0.8.4; -import {SWEEP_TOKENS_ID} from "wormhole-sdk/dispatcherComponents/ids.sol"; +import {SWEEP_TOKENS_ID} from "wormhole-sdk/components/dispatcher/Ids.sol"; +import {UpgradeTester} from "wormhole-sdk/testing/UpgradeTester.sol"; +import {ERC20Mock} from "wormhole-sdk/testing/ERC20Mock.sol"; import {DispatcherTestBase} from "./utils/DispatcherTestBase.sol"; -import {ERC20Mock} from "wormhole-sdk/testing/ERC20Mock.sol"; contract SweepTokensTest is DispatcherTestBase { ERC20Mock token; diff --git a/test/dispatcherComponents/Upgrade.t.sol b/test/components/dispatcher/Upgrade.t.sol similarity index 87% rename from test/dispatcherComponents/Upgrade.t.sol rename to test/components/dispatcher/Upgrade.t.sol index b3c50de..264292f 100644 --- a/test/dispatcherComponents/Upgrade.t.sol +++ b/test/components/dispatcher/Upgrade.t.sol @@ -2,12 +2,15 @@ pragma solidity ^0.8.4; -import {NotAuthorized} from "wormhole-sdk/dispatcherComponents/AccessControl.sol"; -import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; -import {UpgradeTester} from "wormhole-sdk/testing/UpgradeTester.sol"; -import {IdempotentUpgrade} from "wormhole-sdk/proxy/ProxyBase.sol"; -import {DispatcherTestBase} from "./utils/DispatcherTestBase.sol"; -import "wormhole-sdk/dispatcherComponents/ids.sol"; +import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; +import {IdempotentUpgrade} from "wormhole-sdk/proxy/ProxyBase.sol"; +import {NotAuthorized} from "wormhole-sdk/components/dispatcher/AccessControl.sol"; +import { + UPGRADE_CONTRACT_ID, + IMPLEMENTATION_ID +} from "wormhole-sdk/components/dispatcher/Ids.sol"; +import {UpgradeTester} from "wormhole-sdk/testing/UpgradeTester.sol"; +import {DispatcherTestBase} from "./utils/DispatcherTestBase.sol"; contract UpgradeTest is DispatcherTestBase { using BytesParsing for bytes; diff --git a/test/dispatcherComponents/utils/Dispatcher.sol b/test/components/dispatcher/utils/Dispatcher.sol similarity index 86% rename from test/dispatcherComponents/utils/Dispatcher.sol rename to test/components/dispatcher/utils/Dispatcher.sol index 8224877..973f80a 100644 --- a/test/dispatcherComponents/utils/Dispatcher.sol +++ b/test/components/dispatcher/utils/Dispatcher.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.4; -import {AccessControl} from "wormhole-sdk/dispatcherComponents/AccessControl.sol"; -import {SweepTokens} from "wormhole-sdk/dispatcherComponents/SweepTokens.sol"; -import {Upgrade} from "wormhole-sdk/dispatcherComponents/Upgrade.sol"; -import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; +import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; import {RawDispatcher} from "wormhole-sdk/RawDispatcher.sol"; +import {AccessControl} from "wormhole-sdk/components/dispatcher/AccessControl.sol"; +import {SweepTokens} from "wormhole-sdk/components/dispatcher/SweepTokens.sol"; +import {Upgrade} from "wormhole-sdk/components/dispatcher/Upgrade.sol"; contract Dispatcher is RawDispatcher, AccessControl, SweepTokens, Upgrade { using BytesParsing for bytes; diff --git a/test/dispatcherComponents/utils/DispatcherTestBase.sol b/test/components/dispatcher/utils/DispatcherTestBase.sol similarity index 91% rename from test/dispatcherComponents/utils/DispatcherTestBase.sol rename to test/components/dispatcher/utils/DispatcherTestBase.sol index 59f067a..6ad46bc 100644 --- a/test/dispatcherComponents/utils/DispatcherTestBase.sol +++ b/test/components/dispatcher/utils/DispatcherTestBase.sol @@ -2,12 +2,13 @@ pragma solidity ^0.8.4; -import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; -import {Proxy} from "wormhole-sdk/proxy/Proxy.sol"; -import {reRevert} from "wormhole-sdk/Utils.sol"; -import {Dispatcher} from "./Dispatcher.sol"; import "forge-std/Test.sol"; +import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; +import {Proxy} from "wormhole-sdk/proxy/Proxy.sol"; +import {reRevert} from "wormhole-sdk/Utils.sol"; +import {Dispatcher} from "./Dispatcher.sol"; + contract DispatcherTestBase is Test { using BytesParsing for bytes;