-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adds WithdrawablePeriphery base contract for token withdrawals (#831)
* adds WithdrawablePeriphery base contract for token withdrawals * fix test * adds version tag to contract * audit report added
- Loading branch information
Showing
4 changed files
with
165 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// SPDX-License-Identifier: MIT | ||
/// @custom:version 1.0.0 | ||
pragma solidity 0.8.17; | ||
|
||
import { TransferrableOwnership } from "./TransferrableOwnership.sol"; | ||
import { LibAsset } from "../Libraries/LibAsset.sol"; | ||
import { ExternalCallFailed } from "../Errors/GenericErrors.sol"; | ||
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; | ||
|
||
abstract contract WithdrawablePeriphery is TransferrableOwnership { | ||
using SafeTransferLib for address; | ||
|
||
event TokensWithdrawn( | ||
address assetId, | ||
address payable receiver, | ||
uint256 amount | ||
); | ||
|
||
constructor(address _owner) TransferrableOwnership(_owner) {} | ||
|
||
function withdrawToken( | ||
address assetId, | ||
address payable receiver, | ||
uint256 amount | ||
) external onlyOwner { | ||
if (LibAsset.isNativeAsset(assetId)) { | ||
// solhint-disable-next-line avoid-low-level-calls | ||
(bool success, ) = receiver.call{ value: amount }(""); | ||
if (!success) revert ExternalCallFailed(); | ||
} else { | ||
assetId.safeTransfer(receiver, amount); | ||
} | ||
|
||
emit TokensWithdrawn(assetId, receiver, amount); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// SPDX-License-Identifier: MIT | ||
/// @custom:version 1.0.0 | ||
pragma solidity 0.8.17; | ||
|
||
import { WithdrawablePeriphery } from "lifi/Helpers/WithdrawablePeriphery.sol"; | ||
|
||
import { TestBase } from "../utils/TestBase.sol"; | ||
import { NonETHReceiver } from "../utils/TestHelpers.sol"; | ||
|
||
contract TestContract is WithdrawablePeriphery { | ||
constructor(address _owner) WithdrawablePeriphery(_owner) {} | ||
} | ||
|
||
contract WithdrawablePeripheryTest is TestBase { | ||
WithdrawablePeriphery internal withdrawable; | ||
|
||
event TokensWithdrawn( | ||
address assetId, | ||
address payable receiver, | ||
uint256 amount | ||
); | ||
|
||
error UnAuthorized(); | ||
|
||
function setUp() public { | ||
initTestBase(); | ||
|
||
// deploy contract | ||
withdrawable = new TestContract(USER_DIAMOND_OWNER); | ||
|
||
// fund contract with native and ERC20 | ||
deal( | ||
ADDRESS_USDC, | ||
address(withdrawable), | ||
100_000 * 10 ** usdc.decimals() | ||
); | ||
deal(address(withdrawable), 1 ether); | ||
} | ||
|
||
function test_AllowsOwnerToWithdrawNative() public { | ||
uint256 withdrawAmount = 0.1 ether; | ||
|
||
vm.startPrank(USER_DIAMOND_OWNER); | ||
|
||
vm.expectEmit(true, true, true, true, address(withdrawable)); | ||
emit TokensWithdrawn( | ||
address(0), | ||
payable(USER_RECEIVER), | ||
withdrawAmount | ||
); | ||
|
||
withdrawable.withdrawToken( | ||
address(0), | ||
payable(USER_RECEIVER), | ||
withdrawAmount | ||
); | ||
} | ||
|
||
function test_AllowsOwnerToWithdrawERC20() public { | ||
uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); | ||
vm.startPrank(USER_DIAMOND_OWNER); | ||
|
||
vm.expectEmit(true, true, true, true, address(withdrawable)); | ||
emit TokensWithdrawn( | ||
ADDRESS_USDC, | ||
payable(USER_RECEIVER), | ||
withdrawAmount | ||
); | ||
|
||
withdrawable.withdrawToken( | ||
ADDRESS_USDC, | ||
payable(USER_RECEIVER), | ||
withdrawAmount | ||
); | ||
} | ||
|
||
function testRevert_FailsIfNonOwnerTriesToWithdrawNative() public { | ||
uint256 withdrawAmount = 0.1 ether; | ||
|
||
vm.startPrank(USER_SENDER); | ||
|
||
vm.expectRevert(UnAuthorized.selector); | ||
|
||
withdrawable.withdrawToken( | ||
address(0), | ||
payable(USER_RECEIVER), | ||
withdrawAmount | ||
); | ||
} | ||
|
||
function testRevert_FailsIfNonOwnerTriesToWithdrawERC20() public { | ||
uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); | ||
vm.startPrank(USER_SENDER); | ||
|
||
vm.expectRevert(UnAuthorized.selector); | ||
|
||
withdrawable.withdrawToken( | ||
ADDRESS_USDC, | ||
payable(USER_RECEIVER), | ||
withdrawAmount | ||
); | ||
} | ||
|
||
function testRevert_FailsIfNativeTokenTransferFails() public { | ||
uint256 withdrawAmount = 0.1 ether; | ||
|
||
address nonETHReceiver = address(new NonETHReceiver()); | ||
|
||
vm.startPrank(USER_DIAMOND_OWNER); | ||
|
||
vm.expectRevert(); | ||
|
||
withdrawable.withdrawToken( | ||
address(0), | ||
payable(nonETHReceiver), | ||
withdrawAmount | ||
); | ||
} | ||
} |