From d005fab9c8cc391d1d802d81b13afcf35baffbd5 Mon Sep 17 00:00:00 2001 From: just-a-node Date: Thu, 3 Aug 2023 10:24:02 -0600 Subject: [PATCH 1/5] feat: add lockbox interface --- .../shared/IXERC20Lockbox/IXERC20Lockbox.sol | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 contracts/shared/IXERC20Lockbox/IXERC20Lockbox.sol diff --git a/contracts/shared/IXERC20Lockbox/IXERC20Lockbox.sol b/contracts/shared/IXERC20Lockbox/IXERC20Lockbox.sol new file mode 100644 index 0000000..50ecb27 --- /dev/null +++ b/contracts/shared/IXERC20Lockbox/IXERC20Lockbox.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.4 <0.9.0; + +interface IXERC20Lockbox { + /** + * @notice Emitted when tokens are deposited into the lockbox + */ + + event Deposit(address _sender, uint256 _amount); + + /** + * @notice Emitted when tokens are withdrawn from the lockbox + */ + + event Withdraw(address _sender, uint256 _amount); + + /** + * @notice Reverts when a user tries to deposit native tokens on a non-native lockbox + */ + + error IXERC20Lockbox_NotNative(); + + /** + * @notice Reverts when a user tries to deposit non-native tokens on a native lockbox + */ + + error IXERC20Lockbox_Native(); + + /** + * @notice Reverts when a user tries to withdraw and the call fails + */ + + error IXERC20Lockbox_WithdrawFailed(); + + /** + * @notice Deposit ERC20 tokens into the lockbox + * + * @param _amount The amount of tokens to deposit + */ + + function deposit(uint256 _amount) external; + + /** + * @notice Withdraw ERC20 tokens from the lockbox + * + * @param _amount The amount of tokens to withdraw + */ + + function withdraw(uint256 _amount) external; +} From 8a4c09f3627eb2a03c3b3cb934e57ad1f2c8aa66 Mon Sep 17 00:00:00 2001 From: just-a-node Date: Thu, 3 Aug 2023 23:38:38 -0600 Subject: [PATCH 2/5] feat: lockbox adapter --- .../DappRadar/DappRadarLockboxAdapter.sol | 74 +++++++++++ contracts/shared/IXERC20/IXERC20.sol | 115 ++++++++++++++++++ .../IXERC20Lockbox.sol | 0 foundry.toml | 3 +- .../DeployDappRadarLockBoxAdapter.s.sol | 15 +++ 5 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 contracts/integration/DappRadar/DappRadarLockboxAdapter.sol create mode 100644 contracts/shared/IXERC20/IXERC20.sol rename contracts/shared/{IXERC20Lockbox => IXERC20}/IXERC20Lockbox.sol (100%) create mode 100644 script/DappRadar/DeployDappRadarLockBoxAdapter.s.sol diff --git a/contracts/integration/DappRadar/DappRadarLockboxAdapter.sol b/contracts/integration/DappRadar/DappRadarLockboxAdapter.sol new file mode 100644 index 0000000..ab2bb4f --- /dev/null +++ b/contracts/integration/DappRadar/DappRadarLockboxAdapter.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IXERC20} from "../../shared/IXERC20/IXERC20.sol"; +import {IXERC20Lockbox} from "../../shared/IXERC20/IXERC20Lockbox.sol"; +import {IXReceiver} from "@connext/interfaces/core/IXReceiver.sol"; + +contract DappRadarLockboxAdapter is IXReceiver { + IXERC20Lockbox public lockbox; + IERC20 public erc20; + IXERC20 public xerc20; + + constructor(address _lockbox, address _erc20, address _xerc20) { + lockbox = IXERC20Lockbox(_lockbox); + erc20 = IERC20(_erc20); + xerc20 = IXERC20(_xerc20); + } + + /// @notice Deposit ERC20s into the Lockbox + /// @param _amount Amount of ERC20s to use + /// @param _recipient Recipient of the xERC20s + function deposit(uint256 _amount, address _recipient) internal { + require(_amount > 0, "Zero amount"); + IERC20 _xerc20 = IERC20(address(xerc20)); + + if (erc20.allowance(address(this), address(lockbox)) < _amount) { + erc20.approve(address(lockbox), type(uint256).max); + } + lockbox.deposit(_amount); + + // Transfer the xERC20s to the recipient + SafeERC20.safeTransfer(_xerc20, _recipient, _amount); + } + + /// @notice Withdraw ERC20s from the Lockbox + /// @param _amount Amount of xERC20s to use + /// @param _recipient Recipient of the ERC20s + function withdraw(uint256 _amount, address _recipient) internal { + require(_amount > 0, "Zero amount"); + IERC20 _xerc20 = IERC20(address(xerc20)); + + if (_xerc20.allowance(address(this), address(lockbox)) < _amount) { + _xerc20.approve(address(lockbox), type(uint256).max); + } + lockbox.withdraw(_amount); + + // Transfer the ERC20s to the recipient + SafeERC20.safeTransfer(erc20, _recipient, _amount); + } + + /// @dev This function receives xERC20s from Connext and calls the Lockbox + /// @param _amount The amount of funds that will be received. + /// @param _asset The address of the asset that will be received. + /// @param _transferId The id of the transfer. + /// @param _callData The data that will be sent to the targets. + function xReceive( + bytes32 _transferId, + uint256 _amount, + address _asset, + address _originSender, + uint32 _origin, + bytes memory _callData + ) external returns (bytes memory) { + // Check for the right xerc20 + require(_asset == address(xerc20), "Wrong asset received"); + + // Unpack the _callData to get the recipient's address + address _recipient = abi.decode(_callData, (address)); + + withdraw(_amount, _recipient); + } +} diff --git a/contracts/shared/IXERC20/IXERC20.sol b/contracts/shared/IXERC20/IXERC20.sol new file mode 100644 index 0000000..4adbf9c --- /dev/null +++ b/contracts/shared/IXERC20/IXERC20.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.4 <0.9.0; + +interface IXERC20 { + /** + * @notice Emits when a lockbox is set + * + * @param _lockbox The address of the lockbox + */ + + event LockboxSet(address _lockbox); + + /** + * @notice Emits when a limit is set + * + * @param _mintingLimit The updated minting limit we are setting to the bridge + * @param _burningLimit The updated burning limit we are setting to the bridge + * @param _bridge The address of the bridge we are setting the limit too + */ + event BridgeLimitsSet(uint256 _mintingLimit, uint256 _burningLimit, address indexed _bridge); + + /** + * @notice Reverts when a user with too low of a limit tries to call mint/burn + */ + + error IXERC20_NotHighEnoughLimits(); + + /** + * @notice Reverts when caller is not the factory + */ + + error IXERC20_NotFactory(); + + struct Bridge { + BridgeParameters minterParams; + BridgeParameters burnerParams; + } + + struct BridgeParameters { + uint256 timestamp; + uint256 ratePerSecond; + uint256 maxLimit; + uint256 currentLimit; + } + + /** + * @notice Sets the lockbox address + * + * @param _lockbox The address of the lockbox + */ + + function setLockbox(address _lockbox) external; + + /** + * @notice Updates the limits of any bridge + * @dev Can only be called by the owner + * @param _mintingLimit The updated minting limit we are setting to the bridge + * @param _burningLimit The updated burning limit we are setting to the bridge + * @param _bridge The address of the bridge we are setting the limits too + */ + function setLimits(address _bridge, uint256 _mintingLimit, uint256 _burningLimit) external; + + /** + * @notice Returns the max limit of a minter + * + * @param _minter The minter we are viewing the limits of + * @return _limit The limit the minter has + */ + function mintingMaxLimitOf(address _minter) external view returns (uint256 _limit); + + /** + * @notice Returns the max limit of a bridge + * + * @param _bridge the bridge we are viewing the limits of + * @return _limit The limit the bridge has + */ + + function burningMaxLimitOf(address _bridge) external view returns (uint256 _limit); + + /** + * @notice Returns the current limit of a minter + * + * @param _minter The minter we are viewing the limits of + * @return _limit The limit the minter has + */ + + function mintingCurrentLimitOf(address _minter) external view returns (uint256 _limit); + + /** + * @notice Returns the current limit of a bridge + * + * @param _bridge the bridge we are viewing the limits of + * @return _limit The limit the bridge has + */ + + function burningCurrentLimitOf(address _bridge) external view returns (uint256 _limit); + + /** + * @notice Mints tokens for a user + * @dev Can only be called by a minter + * @param _user The address of the user who needs tokens minted + * @param _amount The amount of tokens being minted + */ + + function mint(address _user, uint256 _amount) external; + + /** + * @notice Burns tokens for a user + * @dev Can only be called by a minter + * @param _user The address of the user who needs tokens burned + * @param _amount The amount of tokens being burned + */ + + function burn(address _user, uint256 _amount) external; +} diff --git a/contracts/shared/IXERC20Lockbox/IXERC20Lockbox.sol b/contracts/shared/IXERC20/IXERC20Lockbox.sol similarity index 100% rename from contracts/shared/IXERC20Lockbox/IXERC20Lockbox.sol rename to contracts/shared/IXERC20/IXERC20Lockbox.sol diff --git a/foundry.toml b/foundry.toml index 20fd330..064cbfc 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,4 +4,5 @@ out = 'out' libs = ['node_modules', 'lib'] test = 'test' cache_path = 'cache_forge' -solc = "0.8.19" \ No newline at end of file +solc = "0.8.19" +via_ir = true \ No newline at end of file diff --git a/script/DappRadar/DeployDappRadarLockBoxAdapter.s.sol b/script/DappRadar/DeployDappRadarLockBoxAdapter.s.sol new file mode 100644 index 0000000..4f6a36c --- /dev/null +++ b/script/DappRadar/DeployDappRadarLockBoxAdapter.s.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import {DappRadarLockboxAdapter} from "../../contracts/integration/DappRadar/DappRadarLockboxAdapter.sol"; + +contract DeployDappRadarLockboxAdapter is Script { + function run(address lockbox, address erc20, address xerc20) external { + vm.startBroadcast(); + + new DappRadarLockboxAdapter(lockbox, erc20, xerc20); + + vm.stopBroadcast(); + } +} From 6bafb93c861561e4d0b49c8d9ea72c50c5f60817 Mon Sep 17 00:00:00 2001 From: Prathmesh Khandelwal Date: Tue, 15 Aug 2023 23:33:56 +0530 Subject: [PATCH 3/5] fix: added gelatoLockBox adapter --- .../GelatoxTokenLoxkboxAdapter.sol | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol diff --git a/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol b/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol new file mode 100644 index 0000000..e577fd3 --- /dev/null +++ b/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IXERC20} from "../../shared/IXERC20/IXERC20.sol"; +import {IXERC20Lockbox} from "../../shared/IXERC20/IXERC20Lockbox.sol"; +import {IXReceiver} from "@connext/interfaces/core/IXReceiver.sol"; + +contract GelatoLockboxAdapter is IXReceiver { + IXERC20Lockbox public lockbox; + IERC20 public erc20; + IXERC20 public xerc20; + + constructor(address _lockbox, address _erc20, address _xerc20) { + lockbox = IXERC20Lockbox(_lockbox); + erc20 = IERC20(_erc20); + xerc20 = IXERC20(_xerc20); + } + + /// @notice Deposit ERC20s into the Lockbox + /// @param _amount Amount of ERC20s to use + /// @param _recipient Recipient of the xERC20s + function deposit(uint256 _amount, address _recipient) internal { + require(_amount > 0, "Zero amount"); + IERC20 _xerc20 = IERC20(address(xerc20)); + + if (erc20.allowance(address(this), address(lockbox)) < _amount) { + erc20.approve(address(lockbox), type(uint256).max); + } + lockbox.deposit(_amount); + + // Transfer the xERC20s to the recipient + SafeERC20.safeTransfer(_xerc20, _recipient, _amount); + } + + /// @notice Withdraw ERC20s from the Lockbox + /// @param _amount Amount of xERC20s to use + /// @param _recipient Recipient of the ERC20s + function withdraw(uint256 _amount, address _recipient) internal { + require(_amount > 0, "Zero amount"); + IERC20 _xerc20 = IERC20(address(xerc20)); + + if (_xerc20.allowance(address(this), address(lockbox)) < _amount) { + _xerc20.approve(address(lockbox), type(uint256).max); + } + lockbox.withdraw(_amount); + + // Transfer the ERC20s to the recipient + SafeERC20.safeTransfer(erc20, _recipient, _amount); + } + + /// @dev This function receives xERC20s from Connext and calls the Lockbox + /// @param _amount The amount of funds that will be received. + /// @param _asset The address of the asset that will be received. + /// @param _transferId The id of the transfer. + /// @param _callData The data that will be sent to the targets. + function xReceive( + bytes32 _transferId, + uint256 _amount, + address _asset, + address _originSender, + uint32 _origin, + bytes memory _callData + ) external returns (bytes memory) { + // Check for the right xerc20 + require(_asset == address(xerc20), "Wrong asset received"); + + // Unpack the _callData to get the recipient's address + address _recipient = abi.decode(_callData, (address)); + + withdraw(_amount, _recipient); + } +} From fe730455e6d9d027ce9dadc4e86135429fd143a1 Mon Sep 17 00:00:00 2001 From: Prathmesh Khandelwal Date: Tue, 15 Aug 2023 23:38:03 +0530 Subject: [PATCH 4/5] fix: commit --- .../integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol b/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol index e577fd3..b4a19cd 100644 --- a/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol +++ b/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol @@ -17,6 +17,7 @@ contract GelatoLockboxAdapter is IXReceiver { erc20 = IERC20(_erc20); xerc20 = IXERC20(_xerc20); } + /// @notice Deposit ERC20s into the Lockbox /// @param _amount Amount of ERC20s to use From da0b8393dc5c8829bf38f97fd21f59248de0c9bb Mon Sep 17 00:00:00 2001 From: Prathmesh <201952225@iiitvadodara.ac.in> Date: Tue, 15 Aug 2023 23:40:44 +0530 Subject: [PATCH 5/5] fix: revert commit --- .../integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol b/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol index b4a19cd..e577fd3 100644 --- a/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol +++ b/contracts/integration/GelatoxToken/GelatoxTokenLoxkboxAdapter.sol @@ -17,7 +17,6 @@ contract GelatoLockboxAdapter is IXReceiver { erc20 = IERC20(_erc20); xerc20 = IXERC20(_xerc20); } - /// @notice Deposit ERC20s into the Lockbox /// @param _amount Amount of ERC20s to use