Skip to content

Commit

Permalink
Fix auction edge case and add WLWrappedERC20 (#343)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xdomrom authored May 28, 2024
1 parent 0d5c04a commit c46fec2
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 1 deletion.
86 changes: 86 additions & 0 deletions src/assets/WLWrappedERC20Asset.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;

import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {SafeCast} from "openzeppelin/utils/math/SafeCast.sol";
import {ConvertDecimals} from "lyra-utils/decimals/ConvertDecimals.sol";

import {IERC20Metadata} from "openzeppelin/token/ERC20/extensions/IERC20Metadata.sol";
import {ISubAccounts} from "../interfaces/ISubAccounts.sol";
import {IAsset} from "../interfaces/IAsset.sol";
import {WrappedERC20Asset} from "./WrappedERC20Asset.sol";

/**
* @title Whitelisted Wrapped ERC20 Asset
* @dev Select users (subaccounts) can deposit the given ERC20, and can only have positive balances.
* @dev Fee-on-transfer and rebasing tokens are not supported
* @author Lyra
*/
contract WLWrappedERC20Asset is WrappedERC20Asset {
using SafeERC20 for IERC20Metadata;
using ConvertDecimals for uint;
using SafeCast for uint;
using SafeCast for int;

/// @dev Subaccounts which have been whitelisted to be able to deposit
mapping(uint accountId => bool) public wlAccounts;
bool public wlEnabled = true;

constructor(ISubAccounts _subAccounts, IERC20Metadata _wrappedAsset) WrappedERC20Asset(_subAccounts, _wrappedAsset) {}

///////////
// Admin //
///////////

/**
* @dev Whitelist a subaccount to be able to deposit the ERC20
* @param accountId The subaccount to whitelist
*/
function setSubAccountWL(uint accountId, bool isWhitelisted) external onlyOwner {
wlAccounts[accountId] = isWhitelisted;
emit SubAccountWhitelisted(accountId, isWhitelisted);
}

function setWhitelistEnabled(bool enabled) external onlyOwner {
wlEnabled = enabled;
}

////////////////////////////
// External Functions //
////////////////////////////

/**
* @dev Deposit ERC20 asset and increase account balance
* @param recipientAccount account id to receive the cash asset
* @param assetAmount amount of the wrapped asset to deposit
*/
function deposit(uint recipientAccount, uint assetAmount) external override {
if (wlEnabled && !wlAccounts[recipientAccount]) {
revert WLWERC_NotWhitelisted();
}

wrappedAsset.safeTransferFrom(msg.sender, address(this), assetAmount);
uint adjustmentAmount = assetAmount.to18Decimals(assetDecimals);

subAccounts.assetAdjustment(
ISubAccounts.AssetAdjustment({
acc: recipientAccount,
asset: IAsset(address(this)),
subId: 0,
amount: int(adjustmentAmount),
assetData: bytes32(0)
}),
true,
""
);

emit Deposit(recipientAccount, msg.sender, adjustmentAmount, assetAmount);
}

///////////////////
// Events/Errors //
///////////////////
event SubAccountWhitelisted(uint indexed subaccount, bool isWhitelisted);

error WLWERC_NotWhitelisted();
}
2 changes: 1 addition & 1 deletion src/assets/WrappedERC20Asset.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ contract WrappedERC20Asset is ManagerWhitelist, PositionTracking, IWrappedERC20A
* @param recipientAccount account id to receive the cash asset
* @param assetAmount amount of the wrapped asset to deposit
*/
function deposit(uint recipientAccount, uint assetAmount) external {
function deposit(uint recipientAccount, uint assetAmount) external virtual {
wrappedAsset.safeTransferFrom(msg.sender, address(this), assetAmount);
uint adjustmentAmount = assetAmount.to18Decimals(assetDecimals);

Expand Down
76 changes: 76 additions & 0 deletions test/assets/wrapped-erc20/unit-tests/WLWrappedERC20Hook.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.18;

import "forge-std/Test.sol";
import "forge-std/console2.sol";

import "test/shared/mocks/MockERC20.sol";
import "test/shared/mocks/MockManager.sol";
import {IPositionTracking} from "../../../../src/interfaces/IPositionTracking.sol";
import {IAllowances} from "../../../../src/interfaces/IAllowances.sol";
import "../../../../src/assets/WLWrappedERC20Asset.sol";
import "../../../../src/SubAccounts.sol";

contract UNIT_WLWrappedBaseAssetHook is Test {
WLWrappedERC20Asset asset;
MockERC20 wbtc;
MockManager manager;

SubAccounts subAccounts;

uint accId1;
uint accId2;

function setUp() public {
subAccounts = new SubAccounts("Lyra Margin Accounts", "LyraMarginNFTs");

manager = new MockManager(address(subAccounts));
wbtc = new MockERC20("WBTC", "WBTC");
wbtc.setDecimals(8);

asset = new WLWrappedERC20Asset(subAccounts, wbtc);
accId1 = subAccounts.createAccount(address(this), manager);
accId2 = subAccounts.createAccount(address(this), manager);
asset.setWhitelistManager(address(manager), true);

wbtc.mint(address(this), 1000e8);
wbtc.approve(address(asset), 1000e8);
}

function testDeposit() public {
vm.expectRevert(WLWrappedERC20Asset.WLWERC_NotWhitelisted.selector);
asset.deposit(accId1, 100e8);

asset.setSubAccountWL(accId1, true);

// only acc1 can deposit now
asset.deposit(accId1, 100e8);

assertEq(wbtc.balanceOf(address(asset)), 100e8);
assertEq(subAccounts.getBalance(accId1, asset, 0), 100e18); // 18 decimals

vm.expectRevert(WLWrappedERC20Asset.WLWERC_NotWhitelisted.selector);
asset.deposit(accId2, 100e8);

// even though not WL can still be transferred to
ISubAccounts.AssetTransfer memory transfer = ISubAccounts.AssetTransfer({
fromAcc: accId1, toAcc: accId2, asset: asset, subId: 0, amount: 20e18, assetData: ""
});
subAccounts.submitTransfer(transfer, "");

assertEq(subAccounts.getBalance(accId1, asset, 0), 80e18); // 18 decimals
assertEq(subAccounts.getBalance(accId2, asset, 0), 20e18); // 18 decimals

// acc 2 can also still withdraw
asset.withdraw(accId2, 10e8, address(this));
assertEq(subAccounts.getBalance(accId2, asset, 0), 10e18); // 18 decimals
assertEq(wbtc.balanceOf(address(asset)), 90e8); // 100 deposited and 10 withdrawn

// now enable it for anyone
asset.setWhitelistEnabled(false);
asset.deposit(accId2, 100e8);

assertEq(wbtc.balanceOf(address(asset)), 190e8); // 200 deposited and 10 withdrawn
assertEq(subAccounts.getBalance(accId2, asset, 0), 110e18); // 18 decimals
}
}

0 comments on commit c46fec2

Please sign in to comment.