From ed06f0aaa80a15885e3a63e9bb0430ce341ce39c Mon Sep 17 00:00:00 2001 From: Richard Watts Date: Tue, 9 Jul 2024 18:08:32 +0100 Subject: [PATCH] (feat) Reimport lock proxy and ccm with byte -> bytes1; this makes unpickling work again. (feat) Now have enough mechanism that we can register an extension (forcibly) (feat) more docs (feat) Start of the bridge contract for tokens. --- docs/zilbridge.md | 36 +++++---- .../zilbridge/1/ccmCrossChainManager.sol | 36 ++++----- .../contracts/zilbridge/1/lockProxy.sol | 25 ++---- .../contracts/zilbridge/2/CallingProxy.sol | 77 ------------------- .../2/LockProxyTokenManagerUpgradeableV3.sol | 50 ++++++++++++ .../2/ccmExtendCrossChainManager.sol | 76 ++++++++++++------ ...aZilBridge.t.sol => DeployZilBridge.t.sol} | 47 +++++++---- .../test/zilbridge/ZilBridgeTransfer.t.sol | 19 +++++ .../ccmExtendCrossChainManager.t.sol | 76 ++++++++++++++++++ 9 files changed, 275 insertions(+), 167 deletions(-) delete mode 100644 smart-contracts/contracts/zilbridge/2/CallingProxy.sol create mode 100644 smart-contracts/contracts/zilbridge/2/LockProxyTokenManagerUpgradeableV3.sol rename smart-contracts/test/zilbridge/{DeployVanillaZilBridge.t.sol => DeployZilBridge.t.sol} (69%) create mode 100644 smart-contracts/test/zilbridge/ZilBridgeTransfer.t.sol create mode 100644 smart-contracts/test/zilbridge/ccmExtendCrossChainManager.t.sol diff --git a/docs/zilbridge.md b/docs/zilbridge.md index 0b5d1fe..36919bd 100644 --- a/docs/zilbridge.md +++ b/docs/zilbridge.md @@ -3,21 +3,25 @@ ## CrossChainManager extensions -The CCM contains state; the current approach to retaining this state -is to replace the ccm in the ccmProxy with a shim contract that -forwards requests to the underlying contract but additionally allows -an owner to register extensions. - -This works, and has the advantage that you don't need to change the -address of the ccm baked into the relayers, but it breaks the -invariant that cross-chain events come from the contract referred to -by the ccmProxy. If there is software that reads the address of the -CCM from the ccmProxy and then expects cross-chain events to come from -it, we will need to replace the ccm entirely. - -This is not as straightforward as it looks because the deployed CCM on -ethereum is quite old and we would need to test that it still works -with zilBridge 1 when upgraded or updated for a reasonably modern -solidity version. +The currently deployed CCM on Ethereum does not contain functions to register lockProxy extensions. + +These can be run remotely from the `counterpartChainId` (in `lockProxy`), currently set to 5 (which is presumably Carbon). + +This means we need to either proxy or replace it. + +Replacing it is undesirable, because the address of the CCM is baked into the configuration files for the relayers. + +My first attempt was to proxy it with a `CCMExtendProxy`, but this doesn't work, because: + + * To upgrade you have to call `ccmProxy::upgradeEthCrossChainManager()` + * (side-note: this calls the _current_ `eccm.upgradeToNew()` which hands ownership of the CCM data (proxied by the CCM contract) to the CCMExtendProxty, which now needs to hand it back to the old `ccm`) + * Subsequent calls through the `CCMExtendProxy` need to use the original `ccm` state, and therefore have the `CCMExtendProxy` as `msg.sender`. + * But there is no way to make the `CCMExtendProxy` an owner of the `ccm`. + +The second attempt is to write a new CCM contract which duplicates the +original CCM and contains the new functions. Sadly, this means that +someone needs to remember what the whitelist parameters were, because +it is a map that does not emit events and we thus can't work out what +is in it. diff --git a/smart-contracts/contracts/zilbridge/1/ccmCrossChainManager.sol b/smart-contracts/contracts/zilbridge/1/ccmCrossChainManager.sol index f9e51fd..c396c90 100644 --- a/smart-contracts/contracts/zilbridge/1/ccmCrossChainManager.sol +++ b/smart-contracts/contracts/zilbridge/1/ccmCrossChainManager.sol @@ -2,11 +2,12 @@ *Submitted for verification at Etherscan.io on 2021-10-19 */ -pragma solidity 0.8.20; -//pragma solidity ^0.5.0; -//pragma experimental ABIEncoderV2; +pragma solidity ^0.8.2; abstract contract Context { + // Empty internal constructor, to prevent people from mistakenly deploying + // an instance of this contract, which should be used via inheritance. + constructor () { } // solhint-disable-previous-line no-empty-blocks function _msgSender() internal view returns (address payable) { @@ -181,7 +182,7 @@ library ZeroCopySink { * @param b The byte value * @return Converted bytes array */ - function WriteByte(uint8 b) internal pure returns (bytes memory) { + function WriteByte(bytes1 b) internal pure returns (bytes memory) { return WriteUint8(uint8(b)); } @@ -332,7 +333,7 @@ library ZeroCopySource { function NextBool(bytes memory buff, uint256 offset) internal pure returns(bool, uint256) { require(offset + 1 <= buff.length && offset < offset + 1, "Offset exceeds limit"); // byte === bytes1 - uint8 v; + bytes1 v; assembly{ v := mload(add(add(buff, 0x20), offset)) } @@ -352,9 +353,9 @@ library ZeroCopySource { * @param offset The position from where we read the byte value * @return The read byte value and new offset */ - function NextByte(bytes memory buff, uint256 offset) internal pure returns (uint8, uint256) { + function NextByte(bytes memory buff, uint256 offset) internal pure returns (bytes1, uint256) { require(offset + 1 <= buff.length && offset < offset + 1, "NextByte, Offset exceeds maximum"); - uint8 v; + bytes1 v; assembly{ v := mload(add(add(buff, 0x20), offset)) } @@ -573,7 +574,7 @@ library ZeroCopySource { } function NextVarUint(bytes memory buff, uint256 offset) internal pure returns(uint, uint256) { - uint8 v; + bytes1 v; (v, offset) = NextByte(buff, offset); uint value; @@ -828,7 +829,7 @@ library Utils { * @return Hashed value in bytes32 format */ function hashLeaf(bytes memory _data) internal pure returns (bytes32 result) { - result = sha256(abi.encodePacked(uint8(0x0), _data)); + result = sha256(abi.encodePacked(bytes1(0x0), _data)); } /* @notice Do hash children as the multi-chain does @@ -1101,7 +1102,7 @@ library ECCUtils { bytes32 hash = Utils.hashLeaf(value); uint size = _auditPath.length.sub(off).div(33); bytes32 nodeHash; - uint8 pos; + bytes1 pos; for (uint i = 0; i < size; i++) { (pos, off) = ZeroCopySource.NextByte(_auditPath, off); (nodeHash, off) = ZeroCopySource.NextHash(_auditPath, off); @@ -1302,10 +1303,10 @@ interface IEthCrossChainData { } interface IUpgradableECCM is IOwnable, IPausable { - function upgradeToNew(address) external returns (bool); - function setChainId(uint64 _newChainId) external returns (bool); function pause() external returns (bool); function unpause() external returns (bool); + function upgradeToNew(address) external returns (bool); + function setChainId(uint64 _newChainId) external returns (bool); } @@ -1313,15 +1314,14 @@ interface IEthCrossChainManager { function crossChain(uint64 _toChainId, bytes calldata _toContract, bytes calldata _method, bytes calldata _txData) external returns (bool); } -contract UpgradableECCM is Ownable, Pausable, IUpgradableECCM { +contract UpgradableECCM is IUpgradableECCM, Ownable, Pausable { address public EthCrossChainDataAddress; - uint64 public chainId; + uint64 public chainId; constructor (address ethCrossChainDataAddr, uint64 _chainId) Pausable() Ownable() { EthCrossChainDataAddress = ethCrossChainDataAddr; chainId = _chainId; } - function pause() onlyOwner public returns (bool) { if (!paused()) { _pause(); @@ -1332,7 +1332,7 @@ contract UpgradableECCM is Ownable, Pausable, IUpgradableECCM { } return true; } - + function unpause() onlyOwner public returns (bool) { if (paused()) { _unpause(); @@ -1359,7 +1359,7 @@ contract UpgradableECCM is Ownable, Pausable, IUpgradableECCM { contract EthCrossChainManager is IEthCrossChainManager, UpgradableECCM { using SafeMath for uint256; - + address public whiteLister; mapping(address => bool) public whiteListFromContract; mapping(address => mapping(bytes => bool)) public whiteListContractMethodMap; @@ -1373,7 +1373,7 @@ contract EthCrossChainManager is IEthCrossChainManager, UpgradableECCM { uint64 _chainId, address[] memory fromContractWhiteList, bytes[] memory contractMethodWhiteList - ) UpgradableECCM(_eccd,_chainId) { + ) UpgradableECCM(_eccd,_chainId) { whiteLister = msg.sender; for (uint i=0;i 0, "counterpartChainId cannot be zero"); require(_ccmProxyAddress != address(0), "ccmProxyAddress cannot be empty"); counterpartChainId = _counterpartChainId; diff --git a/smart-contracts/contracts/zilbridge/2/CallingProxy.sol b/smart-contracts/contracts/zilbridge/2/CallingProxy.sol deleted file mode 100644 index a3e64ad..0000000 --- a/smart-contracts/contracts/zilbridge/2/CallingProxy.sol +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: MIT - - -// Because of the slightly odd way in which our proxy works, we want an OpenZeppelin Proxy.sol, -// but one that uses call(), not delegatecall(). -// I can't find such a contract in // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol) - -pragma solidity ^0.8.20; - -/** - * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM - * instruction `call`. We refer to the second contract as the _implementation_ behind the proxy, and it has to - * be specified by overriding the virtual {_implementation} function. - * - * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a - * different contract through the {_delegate} function. - * - * The success and return data of the delegated call will be returned back to the caller of the proxy. - */ -abstract contract CallingProxy { - /** - * @dev Delegates the current call to `implementation`. - * - * This function does not return to its internal call site, it will return directly to the external caller. - */ - function _delegate(address implementation) internal virtual { - assembly { - // Copy msg.data. We take full control of memory in this inline assembly - // block because it will not return to Solidity code. We overwrite the - // Solidity scratch pad at memory position 0. - calldatacopy(0, 0, calldatasize()) - - // Call the implementation. - // out and outsize are 0 because we don't know the size yet. - let result := call(gas(), implementation, 0, 0, calldatasize(), 0, 0) - - // Copy the returned data. - returndatacopy(0, 0, returndatasize()) - - switch result - // delegatecall returns 0 on error. - case 0 { - revert(0, returndatasize()) - } - default { - return(0, returndatasize()) - } - } - } - - /** - * @dev This is a virtual function that should be overridden so it returns the address to which the fallback - * function and {_fallback} should delegate. - */ - function _implementation() internal view virtual returns (address); - - /** - * @dev Delegates the current call to the address returned by `_implementation()`. - * - * This function does not return to its internal call site, it will return directly to the external caller. - */ - function _fallback() internal virtual { - _delegate(_implementation()); - } - - /** - * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other - * function in the contract matches the call data. - */ - fallback() external payable virtual { - _fallback(); - } - - receive() external payable virtual { - _fallback(); - } -} diff --git a/smart-contracts/contracts/zilbridge/2/LockProxyTokenManagerUpgradeableV3.sol b/smart-contracts/contracts/zilbridge/2/LockProxyTokenManagerUpgradeableV3.sol new file mode 100644 index 0000000..b07e106 --- /dev/null +++ b/smart-contracts/contracts/zilbridge/2/LockProxyTokenManagerUpgradeableV3.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.20; + +import {TokenManagerUpgradeableV3, ITokenManager} from "contracts/periphery/TokenManagerV3/TokenManagerUpgradeableV3.sol"; +import {BridgedToken} from "contracts/periphery/BridgedToken.sol"; +import { LockProxy } from "contracts/zilbridge/1/lockProxy.sol"; +import {IERC20} from "contracts/periphery/LockAndReleaseTokenManagerUpgradeable.sol"; + +interface ILockProxyTokenManager { + // Args in this order to match other token managers. + event SentToLockProxy(address indexed token, address indexed sender, uint amount); + event WithdrawnFromLockProxy(address indexed token, address indexed receipient, uint amount); +} + +contract LockProxyTokenManagerUpgradeableV3 is TokenManagerUpgradeableV3, ILockProxyTokenManager { + address _lockProxyAddress; + address public constant NATIVE_ASSET_HASH = address(0); + + constructor(address lockProxyAddress) { + _disableInitializers(); + _lockProxyAddress = lockProxyAddress; + } + + function changeLockProxy(address newLockProxy) public onlyOwner { + _lockProxyAddress = newLockProxy; + } + + // Incoming currency - transfer into the lock proxy + function _handleTransfer(address token, address from, uint amount) internal override { + // Just transfer value to the lock proxy. + if (token == NATIVE_ASSET_HASH) { + (bool success, ) = _lockProxyAddress.call{value: amount}(""); + emit SentToLockProxy(token, from, amount); + require(success, "Transfer failed"); + return; + } + + IERC20 erc20token = IERC20(token); + erc20token.transferFrom(from, address(_lockProxyAddress), amount); + emit SentToLockProxy(token, from, amount); + } + + function _handleAccept(address token, address recipient, uint amount) internal override { + LockProxy lp = LockProxy(payable(_lockProxyAddress)); + // Sadly, extensionTransfer() takes the same arguments as the withdrawn event but in a + // different order. This will automagically transfer native token if token==0. + lp.extensionTransfer(recipient, token, amount); + emit WithdrawnFromLockProxy(token, recipient, amount); + } +} diff --git a/smart-contracts/contracts/zilbridge/2/ccmExtendCrossChainManager.sol b/smart-contracts/contracts/zilbridge/2/ccmExtendCrossChainManager.sol index 7c39f29..94920f7 100644 --- a/smart-contracts/contracts/zilbridge/2/ccmExtendCrossChainManager.sol +++ b/smart-contracts/contracts/zilbridge/2/ccmExtendCrossChainManager.sol @@ -1,42 +1,72 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity 0.8.20; -import { IEthCrossChainManager, IUpgradableECCM, UpgradableECCM, IEthCrossChainData } from "contracts/zilbridge/1/ccmCrossChainManager.sol"; -import { CallingProxy } from "./CallingProxy.sol"; +import { Utils, ZeroCopySink, IEthCrossChainManager, IUpgradableECCM, + UpgradableECCM, IEthCrossChainData, EthCrossChainManager } from "contracts/zilbridge/1/ccmCrossChainManager.sol"; import { Ownable } from "lib/openzeppelin-contracts/contracts/access/Ownable.sol"; + +interface ILockProxy { + function addExtension( + bytes calldata _argsBz, + bytes calldata /* _fromContractAddr */, + uint64 _fromChainId + ) external returns (bool); + + function removeExtension( + bytes calldata _argsBz, + bytes calldata /* _fromContractAddr */, + uint64 _fromChainId + ) external returns (bool); +} + // This is a contract which can replace the CCM. It allows the owner to register lock proxy extensions, // and forwards all other requests to the original cross chain manager. // see docs/zilbridge.md for details. // We can't implement IEthCrossChainManager, because we use the fallback for that. -contract EthExtendCrossChainManager is CallingProxy { - address public originalCCM; - address public _owner; - - // Why payable? Because that's what the parent CCM does. - constructor(address _originalCCM) { - _owner = address(msg.sender); - originalCCM = _originalCCM; +contract EthExtendCrossChainManager is EthCrossChainManager { + address public _extensionManager; + event ExtensionManagerTransferred(address indexed previousExtender, address indexed newExtender); + + constructor(address _eccd, + uint64 _chainId, + address[] memory fromContractWhiteList, + bytes[] memory contractMethodWhiteList) EthCrossChainManager(_eccd, _chainId, fromContractWhiteList, contractMethodWhiteList) + { + _extensionManager = payable(msg.sender); + emit ExtensionManagerTransferred(address(0), _extensionManager); } - function _implementation() internal view override returns (address) { - return originalCCM; + function extensionManager() public view returns (address) { return _extensionManager; } + + function isExtensionManager() public view returns (bool) { + return payable(msg.sender) == _extensionManager; } - function proxyOwner() external view returns (address) { - return _owner; + modifier onlyExtensionManager() { + require(isExtensionManager(), "CCM: Caller is not the extension manager"); + _; } - // The upgrade process for the EthCrossChainManager (in ccmproxy::upgradeEthCrossChainManager()) is such that - // the new CCM gets handed ownership of the cross chain data (ccmCrossChainManager::upgradeToNew()). - // We don't want it and must therefore arrange to hand it back .. - function handCrossChainDataBackToImplementation() public { - require(address(msg.sender) == _owner); - UpgradableECCM eccm = UpgradableECCM(originalCCM); - address dataAddress = eccm.EthCrossChainDataAddress(); - IEthCrossChainData eccd = IEthCrossChainData(dataAddress); - eccd.transferOwnership(originalCCM); + function transferExtensionManagement(address newManager) public onlyExtensionManager { + _transferExtensionManagement(newManager); } + function renounceExtensionManagement() public onlyExtensionManager { + emit ExtensionManagerTransferred(_extensionManager, address(0)); + _extensionManager = address(0); + } + + function _transferExtensionManagement(address newManager) internal { + require(newManager != address(0), "ExtensionManager: new owner is 0 address"); + emit ExtensionManagerTransferred(_extensionManager, newManager); + _extensionManager = newManager; + } + + function forciblyAddExtension(address targetAddress, address addressToRegister, uint64 fromChainId) external onlyExtensionManager { + ILockProxy lockProxy = ILockProxy(targetAddress); + bytes memory payload = ZeroCopySink.WriteVarBytes(Utils.addressToBytes(addressToRegister)); + lockProxy.addExtension(payload, payload, fromChainId); + } } diff --git a/smart-contracts/test/zilbridge/DeployVanillaZilBridge.t.sol b/smart-contracts/test/zilbridge/DeployZilBridge.t.sol similarity index 69% rename from smart-contracts/test/zilbridge/DeployVanillaZilBridge.t.sol rename to smart-contracts/test/zilbridge/DeployZilBridge.t.sol index 6bdc4d8..588d545 100644 --- a/smart-contracts/test/zilbridge/DeployVanillaZilBridge.t.sol +++ b/smart-contracts/test/zilbridge/DeployZilBridge.t.sol @@ -9,12 +9,16 @@ import { EthCrossChainManagerProxy } from "contracts/zilbridge/1/ccmProxy.sol"; import { EthCrossChainManager } from "contracts/zilbridge/1/ccmCrossChainManager.sol"; import { EthCrossChainData } from "contracts/zilbridge/1/ethCrossChainData.sol"; import { EthExtendCrossChainManager } from "contracts/zilbridge/2/ccmExtendCrossChainManager.sol"; +import { LockProxyTokenManagerUpgradeableV3 } from "contracts/zilbridge/2/LockProxyTokenManagerUpgradeableV3.sol"; abstract contract ZilBridgeFixture is Tester { address owner = vm.createWallet("owner").addr; address tokenDeployer = vm.createWallet("tokenDeployer").addr; - + address other = vm.createWallet("other").addr; + address third = vm.createWallet("third").addr; + uint64 constant COUNTERPART_CHAIN_ID = 5; + uint64 constant CHAIN_ID = 2; TestToken testToken; EthCrossChainManager ccm; @@ -22,7 +26,8 @@ abstract contract ZilBridgeFixture is Tester { EthCrossChainData eccd; LockProxy lockProxy; EthExtendCrossChainManager extendCCM; - + LockProxyTokenManagerUpgradeableV3 lpTokenManager; + function setUp() internal { vm.prank(tokenDeployer); testToken = new TestToken(10_000); @@ -34,34 +39,50 @@ abstract contract ZilBridgeFixture is Tester { bytes[] memory b = new bytes[](0); console.log("deploy_as = %s", owner); eccd = new EthCrossChainData(); - ccm = new EthCrossChainManager(address(eccd), 2, a,b); + ccm = new EthCrossChainManager(address(eccd), CHAIN_ID, a,b); ccmProxy = new EthCrossChainManagerProxy(address(ccm)); // Now give the ccm to the ccmProxy ccm.transferOwnership(address(ccmProxy)); // and give the data to the ccm. eccd.transferOwnership(address(ccm)); - lockProxy = new LockProxy(address(ccmProxy), 18); + lockProxy = new LockProxy(address(ccmProxy), COUNTERPART_CHAIN_ID); vm.stopPrank(); } function installExtendCrossChainManager(address act_as) internal { vm.startPrank(act_as); console.log("act_as = %s", act_as); - extendCCM = new EthExtendCrossChainManager(address(ccm)); + address[] memory a = new address[](0); + bytes[] memory b = new bytes[](0); + extendCCM = new EthExtendCrossChainManager(address(eccd), 2, a, b); console.log("ccmProxy owner = %s", ccmProxy.owner()); console.log("ccmProxy ccm = %s", ccmProxy.getEthCrossChainManager()); console.log("eccm owner = %s", ccm.owner()); console.log("eccd owner = %s", eccd.owner()); console.log("ccm = %s", address(ccm)); ccmProxy.pauseEthCrossChainManager(); + extendCCM.transferOwnership(address(ccmProxy)); require(ccmProxy.upgradeEthCrossChainManager(address(extendCCM))); - extendCCM.handCrossChainDataBackToImplementation(); ccmProxy.unpauseEthCrossChainManager(); vm.stopPrank(); } + + function setUpZilBridgeForTesting() internal { + deployOriginalContracts(); + installExtendCrossChainManager(owner); + } + + function installTokenManager() internal { + vm.startPrank(owner); + // Create a lock proxy token manager. + lpTokenManager = new LockProxyTokenManagerUpgradeableV3(address(lockProxy)); + // Make it an extension + extendCCM.forciblyAddExtension(address(lockProxy), address(lpTokenManager), COUNTERPART_CHAIN_ID); + vm.stopPrank(); + } } -contract ZilBridgeVanillaTests is ZilBridgeFixture { +contract DeployZilBridgeTest is ZilBridgeFixture { function test_ZilBridgeDeploy() external { setUp(); deployOriginalContracts(); @@ -86,19 +107,15 @@ contract ZilBridgeVanillaTests is ZilBridgeFixture { require(ccmProxy.owner() == owner); // The ccmProxy's ccm is the new CCM require(ccmProxy.getEthCrossChainManager() == address(extendCCM)); - // The owner of the extended CCM is us (NOT the ccmProxy!) - require(extendCCM.proxyOwner() == address(owner)); - // The extendCCM 's chained CCM is the previous ccm - require(extendCCM.originalCCM() == address(ccm)); // The ccm's owner is the ccm Proxy - require(ccm.owner() == address(ccmProxy)); + require(extendCCM.owner() == address(ccmProxy)); // The ccm's eccd is intact - require(ccm.EthCrossChainDataAddress() == address(eccd)); + require(extendCCM.EthCrossChainDataAddress() == address(eccd)); // The eccd's owner is the ccm. - require(eccd.owner() == address(ccm)); + require(eccd.owner() == address(extendCCM)); // We're unpaused - require(!ccm.paused()); + require(!extendCCM.paused()); require(!ccmProxy.paused()); } } diff --git a/smart-contracts/test/zilbridge/ZilBridgeTransfer.t.sol b/smart-contracts/test/zilbridge/ZilBridgeTransfer.t.sol new file mode 100644 index 0000000..061b658 --- /dev/null +++ b/smart-contracts/test/zilbridge/ZilBridgeTransfer.t.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.20; + +import "forge-std/console.sol"; +import {Tester} from "test/Tester.sol"; +import {TestToken} from "test/Helpers.sol"; +import { LockProxy } from "contracts/zilbridge/1/lockProxy.sol"; +import { EthCrossChainManagerProxy } from "contracts/zilbridge/1/ccmProxy.sol"; +import { EthCrossChainManager } from "contracts/zilbridge/1/ccmCrossChainManager.sol"; +import { EthCrossChainData } from "contracts/zilbridge/1/ethCrossChainData.sol"; +import { EthExtendCrossChainManager } from "contracts/zilbridge/2/ccmExtendCrossChainManager.sol"; +import { ZilBridgeFixture } from "./DeployZilBridge.t.sol"; + +contract ZilBridgeTransfer is ZilBridgeFixture { + function test_installNativeTokenBridge() internal { + setUpZilBridgeForTesting(); + installTokenManager(); + } +} diff --git a/smart-contracts/test/zilbridge/ccmExtendCrossChainManager.t.sol b/smart-contracts/test/zilbridge/ccmExtendCrossChainManager.t.sol new file mode 100644 index 0000000..0e9c750 --- /dev/null +++ b/smart-contracts/test/zilbridge/ccmExtendCrossChainManager.t.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.20; + +import "forge-std/console.sol"; +import {Tester} from "test/Tester.sol"; +import {TestToken} from "test/Helpers.sol"; +import { EthExtendCrossChainManager } from "contracts/zilbridge/2/ccmExtendCrossChainManager.sol"; +import { Utils, ZeroCopySink, ZeroCopySource } from "contracts/zilbridge/1/ccmCrossChainManager.sol"; +import { EthCrossChainData } from "contracts/zilbridge/1/ethCrossChainData.sol"; +import { ZilBridgeFixture } from "./DeployZilBridge.t.sol"; + +contract ccmExtendCrossChainManagerFixture is ZilBridgeFixture { + + function test_extensionManagerValue() external { + setUpZilBridgeForTesting(); + require(extendCCM._extensionManager() == owner); + require(extendCCM.extensionManager() == owner); + } + + function testFail_invalidRenounce() external { + setUpZilBridgeForTesting(); + vm.startPrank(other); + extendCCM.renounceExtensionManagement(); + vm.stopPrank(); + } + + function testFail_invalidTransfer() external { + setUpZilBridgeForTesting(); + vm.startPrank(other); + extendCCM.transferExtensionManagement(third); + vm.stopPrank(); + } + + function test_transferExtensionManager() external { + setUpZilBridgeForTesting(); + vm.startPrank(owner); + extendCCM.transferExtensionManagement(other); + vm.stopPrank(); + vm.startPrank(other); + extendCCM.transferExtensionManagement(third); + vm.stopPrank(); + } + + // TODO: remainder of the authorisation tests for transferExtensionManager. + + function test_addExtension() external { + setUpZilBridgeForTesting(); + installTokenManager(); + // Check that the token manager is present. + require(lockProxy.extensions(address(lpTokenManager))==true); + } + + function test_Pickle() external { + console.log("owner = %s", owner); + bytes memory payload = ZeroCopySink.WriteVarBytes(Utils.addressToBytes(owner)); + console.logBytes(payload); + uint256 off = 0; + bytes memory result; + { + bytes1 v; + uint256 offset = 0; + (v,offset) = ZeroCopySource.NextByte(payload, offset); + console.log("F"); + console.logBytes1(v); + } + { + uint len; + uint256 offset = 0; + (len,offset) = ZeroCopySource.NextVarUint(payload, offset); + console.log("len = %d offset = %d payload = %d", len, offset, payload.length); + } + (result, off) = ZeroCopySource.NextVarBytes(payload, off); + console.logBytes(result); + } +} +