Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update redeem flow #159

Merged
merged 6 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ solc_version = "0.8.21"
evm_version = "paris" # to prevent usage of PUSH0, which is not supported on all chains

optimizer = true
optimizer_runs = 10_000
optimizer_runs = 1_000
verbosity = 3

[profile.default.fuzz]
Expand Down Expand Up @@ -43,7 +43,7 @@ fail_on_revert = false
"src/UserEscrow.sol" = ["UserEscrow"]
"src/Root.sol" = ["Root"]
"src/token/ERC20.sol" = ["ERC20"]
"src/token/Tranche.sol" = ["Tranche"]
"src/token/Tranche.sol" = ["TrancheToken"]
"src/token/RestrictionManager.sol" = ["RestrictionManager"]
"src/admins/DelayedAdmin.sol" = ["DelayedAdmin"]
"src/admins/PauseAdmin.sol" = ["PauseAdmin"]
2 changes: 1 addition & 1 deletion src/InvestmentManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ contract InvestmentManager is Auth {
require(poolManager.isAllowedAsInvestmentCurrency(poolId, currency), "InvestmentManager/currency-not-allowed");

// Transfer the tranche token amount from user to escrow (lock tranche tokens in escrow)
lPool.transferFrom(user, address(escrow), _trancheTokenAmount);
AuthTransferLike(address(lPool.share())).authTransferFrom(user, address(escrow), _trancheTokenAmount);

LPValues storage lpValues = orderbook[liquidityPool][user];
lpValues.remainingRedeemOrder = lpValues.remainingRedeemOrder + _trancheTokenAmount;
Expand Down
9 changes: 0 additions & 9 deletions src/LiquidityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -267,15 +267,6 @@ contract LiquidityPool is Auth, IERC4626 {
emit RedeemRequest(owner, shares);
}

/// @notice Similar to requestRedeem, but with a permit option.
function requestRedeemWithPermit(uint256 shares, address owner, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
public
{
_withPermit(address(share), owner, address(investmentManager), shares, deadline, v, r, s);
investmentManager.requestRedeem(address(this), shares, owner);
emit RedeemRequest(owner, shares);
}

/// @notice Request decreasing the outstanding redemption orders. Will return the shares once the order
/// on Centrifuge is successfully decreased.
function decreaseRedeemRequest(uint256 shares, address owner) public withApproval(owner) {
Expand Down
4 changes: 0 additions & 4 deletions test/Deploy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,6 @@ contract DeployTest is Test {
function redeemWithdraw(uint64 poolId, bytes16 trancheId, uint128 price, uint256 amount, LiquidityPool lPool)
public
{
vm.expectRevert(bytes("ERC20/insufficient-allowance"));
lPool.requestRedeem(amount, self);
lPool.approve(address(investmentManager), amount);
console.log(lPool.allowance(self, address(lPool)));
lPool.requestRedeem(amount, self);

// redeem
Expand Down
91 changes: 8 additions & 83 deletions test/LiquidityPool.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ contract LiquidityPoolTest is TestSetup {
vm.expectRevert(bytes("LiquidityPool/no-approval"));
lPool.requestRedeem(amount, investor);

// fail: ward can not requestRedeem if investment manager has no auth on the tranche token
root.denyContract(address(lPool.share()), address(investmentManager));
vm.prank(investor);
vm.expectRevert(bytes("Auth/not-authorized"));
lPool.requestRedeem(amount, investor);
root.relyContract(address(lPool.share()), address(investmentManager));

uint128 tokenAmount = uint128(lPool.balanceOf(address(escrow)));
centrifugeChain.isExecutedCollectRedeem(
lPool.poolId(),
Expand Down Expand Up @@ -441,7 +448,6 @@ contract LiquidityPoolTest is TestSetup {
assertEq(lPool.balanceOf(self), firstTrancheTokenPayout + secondTrancheTokenPayout);

// redeem
lPool.approve(address(investmentManager), firstTrancheTokenPayout + secondTrancheTokenPayout);
lPool.requestRedeem(firstTrancheTokenPayout + secondTrancheTokenPayout, self);

// trigger executed collectRedeem at a price of 1.5
Expand Down Expand Up @@ -528,7 +534,6 @@ contract LiquidityPoolTest is TestSetup {
assertEq(lPool.balanceOf(self), firstTrancheTokenPayout + secondTrancheTokenPayout);

// redeem
lPool.approve(address(investmentManager), firstTrancheTokenPayout + secondTrancheTokenPayout);
lPool.requestRedeem(firstTrancheTokenPayout + secondTrancheTokenPayout, self);

// trigger executed collectRedeem at a price of 1.5
Expand Down Expand Up @@ -942,51 +947,7 @@ contract LiquidityPoolTest is TestSetup {
assertEq(erc20.balanceOf(investor), 0);
}

function testRedeemWithPermitFR(uint256 amount, address random) public {
amount = uint128(bound(amount, 2, MAX_UINT128));
vm.assume(addressAssumption(random));

// Use a wallet with a known private key so we can sign the permit message
address investor = vm.addr(0xABCD);

address lPool_ = deploySimplePool();
LiquidityPool lPool = LiquidityPool(lPool_);
deposit(lPool_, investor, amount); // deposit funds first

TrancheToken trancheToken = TrancheToken(address(lPool.share()));

// Sign permit for redeeming tranche tokens
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
0xABCD,
keccak256(
abi.encodePacked(
"\x19\x01",
trancheToken.DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
trancheToken.PERMIT_TYPEHASH(),
investor,
address(investmentManager),
amount,
0,
block.timestamp
)
)
)
)
);

vm.prank(random); // random fr permit
trancheToken.permit(investor, address(investmentManager), amount, block.timestamp, v, r, s);

// investor still able to requestDepositWithPermit
lPool.requestRedeemWithPermit(amount, investor, block.timestamp, v, r, s);
// ensure tokens are locked in escrow
assertEq(trancheToken.balanceOf(address(escrow)), amount);
assertEq(trancheToken.balanceOf(investor), 0);
}

function testDepositAndRedeemWithPermit(uint256 amount) public {
function testDepositWithPermit(uint256 amount) public {
amount = uint128(bound(amount, 2, MAX_UINT128));

// Use a wallet with a known private key so we can sign the permit message
Expand Down Expand Up @@ -1041,32 +1002,6 @@ contract LiquidityPoolTest is TestSetup {

TrancheToken trancheToken = TrancheToken(address(lPool.share()));
assertEq(trancheToken.balanceOf(address(investor)), maxMint);

// Sign permit for redeeming tranche tokens
(v, r, s) = vm.sign(
0xABCD,
keccak256(
abi.encodePacked(
"\x19\x01",
trancheToken.DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
trancheToken.PERMIT_TYPEHASH(),
investor,
address(investmentManager),
maxMint,
0,
block.timestamp
)
)
)
)
);

lPool.requestRedeemWithPermit(maxMint, investor, block.timestamp, v, r, s);
// ensure tokens are locked in escrow
assertEq(trancheToken.balanceOf(address(escrow)), maxMint);
assertEq(trancheToken.balanceOf(investor), 0);
}

function testRedeem(uint256 amount) public {
Expand All @@ -1077,18 +1012,12 @@ contract LiquidityPoolTest is TestSetup {
deposit(lPool_, self, amount); // deposit funds first
centrifugeChain.updateTrancheTokenPrice(lPool.poolId(), lPool.trancheId(), defaultCurrencyId, defaultPrice);

// will fail - user did not give tranche token allowance to investmentManager
vm.expectRevert(bytes("ERC20/insufficient-allowance"));
lPool.requestRedeem(amount, self);
lPool.approve(address(investmentManager), amount); // add allowance

// success
lPool.requestRedeem(amount, self);
assertEq(lPool.balanceOf(address(escrow)), amount);
assertEq(lPool.userRedeemRequest(self), amount);

// fail: no tokens left
lPool.approve(address(investmentManager), amount); // add allowance
vm.expectRevert(bytes("ERC20/insufficient-balance"));
lPool.requestRedeem(amount, self);

Expand Down Expand Up @@ -1142,9 +1071,7 @@ contract LiquidityPoolTest is TestSetup {
LiquidityPool lPool = LiquidityPool(lPool_);
deposit(lPool_, self, amount); // deposit funds first

lPool.approve(address(investmentManager), amount); // add allowance
lPool.requestRedeem(amount, self);

assertEq(lPool.balanceOf(address(escrow)), amount);
assertEq(lPool.balanceOf(self), 0);

Expand Down Expand Up @@ -1177,7 +1104,6 @@ contract LiquidityPoolTest is TestSetup {
// will fail - user did not give tranche token allowance to investmentManager
vm.expectRevert(bytes("SafeTransferLib/safe-transfer-from-failed"));
lPool.requestDeposit(amount, self);
lPool.approve(address(investmentManager), amount); // add allowance

lPool.requestRedeem(amount, self);
assertEq(lPool.balanceOf(address(escrow)), amount);
Expand Down Expand Up @@ -1257,7 +1183,6 @@ contract LiquidityPoolTest is TestSetup {
LiquidityPool lPool = LiquidityPool(lPool_);
centrifugeChain.updateTrancheTokenPrice(lPool.poolId(), lPool.trancheId(), defaultCurrencyId, defaultPrice);
deposit(lPool_, self, amount);
lPool.approve(address(investmentManager), amount);
lPool.requestRedeem(amount, self);

assertEq(lPool.balanceOf(address(escrow)), amount);
Expand Down
Loading