From 78b63ed38d7d6443757f399e5a79de8d550f6c93 Mon Sep 17 00:00:00 2001 From: xiaoch05 Date: Tue, 30 Jan 2024 13:44:37 +0800 Subject: [PATCH 1/3] guard transfer --- helix-contract/contracts/mapping-token/v3/GuardV3.sol | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/helix-contract/contracts/mapping-token/v3/GuardV3.sol b/helix-contract/contracts/mapping-token/v3/GuardV3.sol index c6aeddc..d6d7df4 100644 --- a/helix-contract/contracts/mapping-token/v3/GuardV3.sol +++ b/helix-contract/contracts/mapping-token/v3/GuardV3.sol @@ -7,6 +7,7 @@ import "@zeppelin-solidity/contracts/token/ERC20/IERC20.sol"; import "@zeppelin-solidity/contracts/utils/math/SafeMath.sol"; import "./GuardRegistryV3.sol"; import "../interfaces/IWToken.sol"; +import "../../utils/TokenTransferHelper.sol"; contract GuardV3 is GuardRegistryV3, Pausable { using SafeMath for uint256; @@ -87,13 +88,13 @@ contract GuardV3 is GuardRegistryV3, Pausable { require(amount > 0, "Guard: Invalid amount to claim"); delete deposits[id]; if (isNative) { - require(IERC20(token).transferFrom(from, address(this), amount), "Guard: claim native token failed"); + TokenTransferHelper.safeTransferFrom(token, from, address(this), amount); uint256 balanceBefore = address(this).balance; IWToken(token).withdraw(amount); require(address(this).balance == balanceBefore.add(amount), "Guard: token is not wrapped by native token"); - payable(recipient).transfer(amount); + TokenTransferHelper.safeTransferNative(recipient, amount); } else { - require(IERC20(token).transferFrom(from, recipient, amount), "Guard: claim token failed"); + TokenTransferHelper.safeTransferFrom(token, from, recipient, amount); } emit TokenClaimed(id); } From 0d2f73a2391a2fd7e7cd4677dbdfa45b2a6c9e59 Mon Sep 17 00:00:00 2001 From: xiaoch05 Date: Thu, 1 Feb 2024 17:24:49 +0800 Subject: [PATCH 2/3] update guard transfer token --- .../contracts/mapping-token/v3/GuardV3.sol | 9 +- helix-contract/deploy/deploy_xtoken_logic.js | 10 +- helix-contract/flatten/xtoken-v3/GuardV3.sol | 245 +++++++++++------- .../flatten/xtoken-v3/MsglineMessager.sol | 8 +- .../flatten/xtoken-v3/xTokenBacking.sol | 16 +- .../flatten/xtoken-v3/xTokenErc20.sol | 2 +- .../flatten/xtoken-v3/xTokenIssuing.sol | 138 +++++----- 7 files changed, 242 insertions(+), 186 deletions(-) diff --git a/helix-contract/contracts/mapping-token/v3/GuardV3.sol b/helix-contract/contracts/mapping-token/v3/GuardV3.sol index d6d7df4..8ab092a 100644 --- a/helix-contract/contracts/mapping-token/v3/GuardV3.sol +++ b/helix-contract/contracts/mapping-token/v3/GuardV3.sol @@ -21,9 +21,14 @@ contract GuardV3 is GuardRegistryV3, Pausable { event TokenDeposit(address sender, uint256 id, uint256 timestamp, address token, address recipient, uint256 amount); event TokenClaimed(uint256 id); - constructor(address[] memory _guards, uint256 _threshold, uint256 _maxUnclaimableTime) { + constructor( + address[] memory _guards, + address _operator, + uint256 _threshold, + uint256 _maxUnclaimableTime + ) { maxUnclaimableTime = _maxUnclaimableTime; - operator = msg.sender; + operator = _operator; initialize(_guards, _threshold); } diff --git a/helix-contract/deploy/deploy_xtoken_logic.js b/helix-contract/deploy/deploy_xtoken_logic.js index 92308bd..9774853 100644 --- a/helix-contract/deploy/deploy_xtoken_logic.js +++ b/helix-contract/deploy/deploy_xtoken_logic.js @@ -8,12 +8,12 @@ const privateKey = process.env.PRIKEY const crabNetwork = { url: "https://crab-rpc.darwinia.network", - deployer: "0xbe6b2860d3c17a719be0A4911EA0EE689e8357f3", + deployer: "0x80D4c766C5142D1313D531Afe7384D0D5E108Db3", }; const sepoliaNetwork = { url: "https://rpc-sepolia.rockx.com", - deployer: "0xbe6b2860d3c17a719be0A4911EA0EE689e8357f3", + deployer: "0x80D4c766C5142D1313D531Afe7384D0D5E108Db3", } function wallet(url) { @@ -41,12 +41,12 @@ async function deployxTokenIssuing(wallet, deployerAddress, salt) { // 2. deploy mapping token factory async function main() { // deploy backing on crab - const walletCrab = wallet(crabNetwork.url); - const backingLogic = await deployxTokenBacking(walletCrab, crabNetwork.deployer, "xTokenBacking-logic-v1.0.3"); + //const walletCrab = wallet(crabNetwork.url); + //const backingLogic = await deployxTokenBacking(walletCrab, crabNetwork.deployer, "xTokenBacking-logic-v1.0.3"); // deploy issuing on sepolia const walletSpeolia = wallet(sepoliaNetwork.url); - const issuingLogic = await deployxTokenIssuing(walletSpeolia, sepoliaNetwork.deployer, "xTokenIssuing-logic-v1.0.3"); + const issuingLogic = await deployxTokenIssuing(walletSpeolia, sepoliaNetwork.deployer, "xtoken-issuing-logic-v1.0.0"); } main() diff --git a/helix-contract/flatten/xtoken-v3/GuardV3.sol b/helix-contract/flatten/xtoken-v3/GuardV3.sol index 497e258..b4da6d2 100644 --- a/helix-contract/flatten/xtoken-v3/GuardV3.sol +++ b/helix-contract/flatten/xtoken-v3/GuardV3.sol @@ -14,11 +14,144 @@ * '----------------' '----------------' '----------------' '----------------' '----------------' ' * * - * 12/26/2023 + * 1/30/2024 **/ pragma solidity ^0.8.17; +// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 +// License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} + +// File contracts/utils/TokenTransferHelper.sol +// License-Identifier: MIT + +library TokenTransferHelper { + function safeTransfer( + address token, + address receiver, + uint256 amount + ) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector( + IERC20.transfer.selector, + receiver, + amount + )); + require(success && (data.length == 0 || abi.decode(data, (bool))), "helix:transfer token failed"); + } + + function safeTransferFrom( + address token, + address sender, + address receiver, + uint256 amount + ) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector( + IERC20.transferFrom.selector, + sender, + receiver, + amount + )); + require(success && (data.length == 0 || abi.decode(data, (bool))), "helix:transferFrom token failed"); + } + + function safeTransferNative( + address receiver, + uint256 amount + ) internal { + (bool success,) = payable(receiver).call{value: amount}(""); + require(success, "helix:transfer native token failed"); + } +} + +// File contracts/mapping-token/interfaces/IWToken.sol +// License-Identifier: MIT + + +interface IWToken { + function deposit() external payable; + function withdraw(uint wad) external; +} + // File @zeppelin-solidity/contracts/utils/Strings.sol@v4.7.3 // License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) @@ -608,98 +741,6 @@ contract GuardRegistryV3 { } } -// File contracts/mapping-token/interfaces/IWToken.sol -// License-Identifier: MIT - - -interface IWToken { - function deposit() external payable; - function withdraw(uint wad) external; -} - -// File @zeppelin-solidity/contracts/token/ERC20/IERC20.sol@v4.7.3 -// License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - // File @zeppelin-solidity/contracts/utils/Context.sol@v4.7.3 // License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) @@ -1065,6 +1106,7 @@ library SafeMath { + contract GuardV3 is GuardRegistryV3, Pausable { using SafeMath for uint256; @@ -1077,9 +1119,14 @@ contract GuardV3 is GuardRegistryV3, Pausable { event TokenDeposit(address sender, uint256 id, uint256 timestamp, address token, address recipient, uint256 amount); event TokenClaimed(uint256 id); - constructor(address[] memory _guards, uint256 _threshold, uint256 _maxUnclaimableTime) { + constructor( + address[] memory _guards, + address _operator, + uint256 _threshold, + uint256 _maxUnclaimableTime + ) { maxUnclaimableTime = _maxUnclaimableTime; - operator = msg.sender; + operator = _operator; initialize(_guards, _threshold); } @@ -1142,16 +1189,16 @@ contract GuardV3 is GuardRegistryV3, Pausable { ) internal { require(hash(abi.encodePacked(from, timestamp, token, recipient, amount)) == deposits[id], "Guard: Invalid id to claim"); require(amount > 0, "Guard: Invalid amount to claim"); + delete deposits[id]; if (isNative) { - require(IERC20(token).transferFrom(from, address(this), amount), "Guard: claim native token failed"); + TokenTransferHelper.safeTransferFrom(token, from, address(this), amount); uint256 balanceBefore = address(this).balance; IWToken(token).withdraw(amount); require(address(this).balance == balanceBefore.add(amount), "Guard: token is not wrapped by native token"); - payable(recipient).transfer(amount); + TokenTransferHelper.safeTransferNative(recipient, amount); } else { - require(IERC20(token).transferFrom(from, recipient, amount), "Guard: claim token failed"); + TokenTransferHelper.safeTransferFrom(token, from, recipient, amount); } - delete deposits[id]; emit TokenClaimed(id); } diff --git a/helix-contract/flatten/xtoken-v3/MsglineMessager.sol b/helix-contract/flatten/xtoken-v3/MsglineMessager.sol index eab164b..c42b083 100644 --- a/helix-contract/flatten/xtoken-v3/MsglineMessager.sol +++ b/helix-contract/flatten/xtoken-v3/MsglineMessager.sol @@ -14,7 +14,7 @@ * '----------------' '----------------' '----------------' '----------------' '----------------' ' * * - * 12/26/2023 + * 1/30/2024 **/ pragma solidity ^0.8.17; @@ -94,7 +94,7 @@ abstract contract Application { contract MsglineMessager is Application, AccessController { - IMessageLine public immutable msgline; + IMessageLine public msgline; struct RemoteMessager { uint256 msglineRemoteChainId; @@ -128,6 +128,10 @@ contract MsglineMessager is Application, AccessController { msgline = IMessageLine(_msgline); } + function setMsgline(address _msgline) onlyDao external { + msgline = IMessageLine(_msgline); + } + function setRemoteMessager(uint256 _appRemoteChainId, uint256 _msglineRemoteChainId, address _remoteMessager) onlyDao external { remoteMessagers[_appRemoteChainId] = RemoteMessager(_msglineRemoteChainId, _remoteMessager); } diff --git a/helix-contract/flatten/xtoken-v3/xTokenBacking.sol b/helix-contract/flatten/xtoken-v3/xTokenBacking.sol index 851ce08..37f1b3e 100644 --- a/helix-contract/flatten/xtoken-v3/xTokenBacking.sol +++ b/helix-contract/flatten/xtoken-v3/xTokenBacking.sol @@ -14,7 +14,7 @@ * '----------------' '----------------' '----------------' '----------------' '----------------' ' * * - * 12/26/2023 + * 1/30/2024 **/ pragma solidity ^0.8.17; @@ -143,21 +143,21 @@ library TokenTransferHelper { } } -// File contracts/mapping-token/interfaces/IGuard.sol +// File contracts/mapping-token/interfaces/IWToken.sol // License-Identifier: MIT -interface IGuard { - function deposit(uint256 id, address token, address recipient, uint256 amount) external; +interface IWToken { + function deposit() external payable; + function withdraw(uint wad) external; } -// File contracts/mapping-token/interfaces/IWToken.sol +// File contracts/mapping-token/interfaces/IGuard.sol // License-Identifier: MIT -interface IWToken { - function deposit() external payable; - function withdraw(uint wad) external; +interface IGuard { + function deposit(uint256 id, address token, address recipient, uint256 amount) external; } // File contracts/mapping-token/v3/interfaces/IxTokenIssuing.sol diff --git a/helix-contract/flatten/xtoken-v3/xTokenErc20.sol b/helix-contract/flatten/xtoken-v3/xTokenErc20.sol index ae6d4a3..6b8569f 100644 --- a/helix-contract/flatten/xtoken-v3/xTokenErc20.sol +++ b/helix-contract/flatten/xtoken-v3/xTokenErc20.sol @@ -14,7 +14,7 @@ * '----------------' '----------------' '----------------' '----------------' '----------------' ' * * - * 12/26/2023 + * 1/30/2024 **/ pragma solidity ^0.8.17; diff --git a/helix-contract/flatten/xtoken-v3/xTokenIssuing.sol b/helix-contract/flatten/xtoken-v3/xTokenIssuing.sol index 4487af7..000215f 100644 --- a/helix-contract/flatten/xtoken-v3/xTokenIssuing.sol +++ b/helix-contract/flatten/xtoken-v3/xTokenIssuing.sol @@ -14,7 +14,7 @@ * '----------------' '----------------' '----------------' '----------------' '----------------' ' * * - * 12/26/2023 + * 1/30/2024 **/ pragma solidity ^0.8.17; @@ -151,17 +151,46 @@ interface IGuard { function deposit(uint256 id, address token, address recipient, uint256 amount) external; } -// File contracts/interfaces/IMessager.sol +// File contracts/utils/AccessController.sol // License-Identifier: MIT -interface ILowLevelMessageSender { - function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external; - function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable; -} +/// @title AccessController +/// @notice AccessController is a contract to control the access permission +/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract +contract AccessController { + address public dao; + address public operator; + address public pendingDao; -interface ILowLevelMessageReceiver { - function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external; - function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external; + modifier onlyDao() { + require(msg.sender == dao, "!dao"); + _; + } + + modifier onlyOperator() { + require(msg.sender == operator, "!operator"); + _; + } + + function _initialize(address _dao) internal { + dao = _dao; + operator = _dao; + } + + function setOperator(address _operator) onlyDao external { + operator = _operator; + } + + function transferOwnership(address _dao) onlyDao external { + pendingDao = _dao; + } + + function acceptOwnership() external { + address newDao = msg.sender; + require(pendingDao == newDao, "!pendingDao"); + delete pendingDao; + dao = newDao; + } } // File contracts/utils/DailyLimit.sol @@ -247,46 +276,17 @@ contract DailyLimit { } } -// File contracts/utils/AccessController.sol +// File contracts/interfaces/IMessager.sol // License-Identifier: MIT -/// @title AccessController -/// @notice AccessController is a contract to control the access permission -/// @dev See https://github.com/helix-bridge/contracts/tree/master/helix-contract -contract AccessController { - address public dao; - address public operator; - address public pendingDao; - - modifier onlyDao() { - require(msg.sender == dao, "!dao"); - _; - } - - modifier onlyOperator() { - require(msg.sender == operator, "!operator"); - _; - } - - function _initialize(address _dao) internal { - dao = _dao; - operator = _dao; - } - - function setOperator(address _operator) onlyDao external { - operator = _operator; - } - - function transferOwnership(address _dao) onlyDao external { - pendingDao = _dao; - } +interface ILowLevelMessageSender { + function registerRemoteReceiver(uint256 remoteChainId, address remoteBridge) external; + function sendMessage(uint256 remoteChainId, bytes memory message, bytes memory params) external payable; +} - function acceptOwnership() external { - address newDao = msg.sender; - require(pendingDao == newDao, "!pendingDao"); - delete pendingDao; - dao = newDao; - } +interface ILowLevelMessageReceiver { + function registerRemoteSender(uint256 remoteChainId, address remoteBridge) external; + function recvMessage(address remoteSender, address localReceiver, bytes memory payload) external; } // File @zeppelin-solidity/contracts/utils/Address.sol@v4.7.3 @@ -942,6 +942,29 @@ contract xTokenBridgeBase is Initializable, Pausable, AccessController, DailyLim } } +// File contracts/mapping-token/v3/interfaces/IxTokenBacking.sol +// License-Identifier: MIT + +interface IxTokenBacking { + function unlockFromRemote( + uint256 remoteChainId, + address originalToken, + address originalSender, + address recipient, + uint256 amount, + uint256 nonce + ) external; + + function handleUnlockForIssuingFailureFromRemote( + uint256 remoteChainId, + address originalToken, + address originalSender, + address recipient, + uint256 amount, + uint256 nonce + ) external; +} + // File @zeppelin-solidity/contracts/utils/math/SafeMath.sol@v4.7.3 // License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) @@ -1311,29 +1334,6 @@ contract xTokenErc20 is IERC20 { function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } -// File contracts/mapping-token/v3/interfaces/IxTokenBacking.sol -// License-Identifier: MIT - -interface IxTokenBacking { - function unlockFromRemote( - uint256 remoteChainId, - address originalToken, - address originalSender, - address recipient, - uint256 amount, - uint256 nonce - ) external; - - function handleUnlockForIssuingFailureFromRemote( - uint256 remoteChainId, - address originalToken, - address originalSender, - address recipient, - uint256 amount, - uint256 nonce - ) external; -} - // File contracts/mapping-token/v3/base/xTokenIssuing.sol // License-Identifier: MIT From 57a28c559601aaa38fc04e8b831bf1b01ef1f089 Mon Sep 17 00:00:00 2001 From: xiaoch05 Date: Tue, 20 Feb 2024 16:22:28 +0800 Subject: [PATCH 3/3] use signatures to set claim timestamp --- .../contracts/mapping-token/v3/GuardV3.sol | 3 +- helix-contract/test/6_test_xtoken_v3.js | 44 +++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/helix-contract/contracts/mapping-token/v3/GuardV3.sol b/helix-contract/contracts/mapping-token/v3/GuardV3.sol index 8ab092a..4c2623f 100644 --- a/helix-contract/contracts/mapping-token/v3/GuardV3.sol +++ b/helix-contract/contracts/mapping-token/v3/GuardV3.sol @@ -59,7 +59,8 @@ contract GuardV3 is GuardRegistryV3, Pausable { depositors[depositor] = enable; } - function setMaxUnclaimableTime(uint256 _maxUnclaimableTime) external onlyOperator { + function setMaxUnclaimableTime(uint256 _maxUnclaimableTime, bytes[] memory signatures) external { + verifyGuardSignatures(msg.sig, abi.encode(_maxUnclaimableTime), signatures); maxUnclaimableTime = _maxUnclaimableTime; } diff --git a/helix-contract/test/6_test_xtoken_v3.js b/helix-contract/test/6_test_xtoken_v3.js index 6baa648..471fbce 100644 --- a/helix-contract/test/6_test_xtoken_v3.js +++ b/helix-contract/test/6_test_xtoken_v3.js @@ -38,7 +38,7 @@ describe("xtoken tests", () => { console.log("mock msgline deployed address:", mockBackingMsgline.address, mockIssuingMsgline.address); // deploy msgline messager - const msglineMessagerContract = await ethers.getContractFactory("MsglineMessager"); + const msglineMessagerContract = await ethers.getContractFactory("MsgportMessager"); const backingMessager = await msglineMessagerContract.deploy(dao, mockBackingMsgline.address); await backingMessager.deployed(); console.log("backing messager deployed address:", backingMessager.address); @@ -89,11 +89,11 @@ describe("xtoken tests", () => { }); const guardBackingContract = await ethers.getContractFactory("GuardV3"); - const backingGuard = await guardBackingContract.deploy([guards[0].address, guards[1].address, guards[2].address], 2, 60); + const backingGuard = await guardBackingContract.deploy([guards[0].address, guards[1].address, guards[2].address], owner.address, 2, 60); await backingGuard.deployed(); await backingGuard.setDepositor(backing.address, true); const guardIssuingContract = await ethers.getContractFactory("GuardV3"); - const issuingGuard = await guardIssuingContract.deploy([guards[0].address, guards[1].address, guards[2].address], 2, 60); + const issuingGuard = await guardIssuingContract.deploy([guards[0].address, guards[1].address, guards[2].address], owner.address, 2, 60); await issuingGuard.deployed(); await issuingGuard.setDepositor(issuing.address, true); @@ -355,6 +355,42 @@ describe("xtoken tests", () => { expect(balanceRecipientAfter.sub(balanceRecipientBefore)).to.equal(amount); } + async function guardSetClaimTime( + guard, + timestamp, + wallets + ) { + // encode value + const nonce = await guard.nonce(); + console.log(nonce); + const structHash = + ethUtil.keccak256( + abi.rawEncode( + ['bytes4', 'bytes', 'uint256'], + [abi.methodID('setMaxUnclaimableTime', ['uint256', 'bytes[]' ]), + abi.rawEncode(['uint256'], [timestamp]), + Number(nonce) + ] + ) + ); + const dataHash = await guard.encodeDataHash(structHash); + const signatures = wallets.map((wallet) => { + const address = wallet.address; + const privateKey = ethers.utils.arrayify(wallet.privateKey); + const signatureECDSA = secp256k1.ecdsaSign(ethers.utils.arrayify(dataHash), privateKey); + const ethRecID = signatureECDSA.recid + 27; + const signature = Uint8Array.from( + signatureECDSA.signature.join().split(',').concat(ethRecID) + ); + return ethers.utils.hexlify(signature); + }); + const timeBefore = await guard.maxUnclaimableTime(); + expect((timeBefore.sub(timestamp)).lt(0)); + await guard.setMaxUnclaimableTime(timestamp, signatures); + const timeAfter = await guard.maxUnclaimableTime(); + expect(timeAfter).to.equal(timestamp); + } + await registerToken( nativeTokenAddress, "ethereum", @@ -545,6 +581,8 @@ describe("xtoken tests", () => { true, false ); + + await guardSetClaimTime(issuingGuard, 110011, [guards[0], guards[1]]); }); });