-
Notifications
You must be signed in to change notification settings - Fork 7
/
CFAReclaimerFacet.sol
156 lines (133 loc) · 5.43 KB
/
CFAReclaimerFacet.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import "../libraries/LibCFABasePCO.sol";
import "../libraries/LibCFAPenaltyBid.sol";
import "../interfaces/ICFAReclaimer.sol";
import {CFABasePCOFacetModifiers} from "./CFABasePCOFacet.sol";
import {CFAv1Library} from "@superfluid-finance/ethereum-contracts/contracts/apps/CFAv1Library.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../../beneficiary/interfaces/ICFABeneficiary.sol";
/// @notice Handles reclaiming of licenses that are no longer active
contract CFAReclaimerFacet is ICFAReclaimer, CFABasePCOFacetModifiers {
using CFAv1Library for CFAv1Library.InitData;
using SafeERC20 for ISuperToken;
/**
* @notice Current price to reclaim
*/
function reclaimPrice() public view returns (uint256) {
require(
!LibCFABasePCO._isPayerBidActive(),
"CFAReclaimerFacet: Can only perform action when payer bid is not active"
);
LibCFABasePCO.DiamondStorage storage ds = LibCFABasePCO
.diamondStorage();
LibCFABasePCO.Bid storage _currentBid = LibCFABasePCO._currentBid();
uint256 originalForSalePrice = _currentBid.forSalePrice;
uint256 startTime = ds.beneficiary.getLastDeletion(address(this));
uint256 _length = ds.paramsStore.getReclaimAuctionLength();
if (block.timestamp > startTime + _length) {
return 0;
}
uint256 timeElapsed = block.timestamp - startTime;
uint256 priceDecrease = (originalForSalePrice * timeElapsed) / _length;
return originalForSalePrice - priceDecrease;
}
/**
* @notice Reclaim an inactive license as msg.sender
* - Payer bid must be inactive
* - Must have permissions to create flow for bidder
* - Must have ERC-20 approval of payment token for claimPrice amount
* @param maxClaimPrice Max price willing to pay for claim. Prevents front-running
* @param newContributionRate New contribution rate for license
* @param newForSalePrice Intented new for sale price. Must be within rounding bounds of newContributionRate
*/
function reclaim(
uint256 maxClaimPrice,
int96 newContributionRate,
uint256 newForSalePrice
) external {
require(
!LibCFABasePCO._isPayerBidActive(),
"CFAReclaimerFacet: Can only perform action when payer bid is not active"
);
LibCFABasePCO.DiamondStorage storage ds = LibCFABasePCO
.diamondStorage();
LibCFABasePCO.DiamondCFAStorage storage cs = LibCFABasePCO.cfaStorage();
require(
newForSalePrice >= ds.paramsStore.getMinForSalePrice(),
"CFAReclaimerFacet: Minimum for sale price not met"
);
{
uint256 perSecondFeeNumerator = ds
.paramsStore
.getPerSecondFeeNumerator();
uint256 perSecondFeeDenominator = ds
.paramsStore
.getPerSecondFeeDenominator();
require(
LibCFABasePCO._checkForSalePrice(
newForSalePrice,
newContributionRate,
perSecondFeeNumerator,
perSecondFeeDenominator
),
"CFAReclaimerFacet: Incorrect for sale price"
);
}
ISuperToken paymentToken = ds.paramsStore.getPaymentToken();
uint256 claimPrice = reclaimPrice();
require(
newForSalePrice >= claimPrice,
"CFAReclaimerFacet: For sale price must be greater than or equal to claim price"
);
require(
maxClaimPrice >= claimPrice,
"CFAReclaimerFacet: Claim price must be under maximum"
);
LibCFABasePCO.Bid storage _currentBid = LibCFABasePCO._currentBid();
address bidder = _currentBid.bidder;
_currentBid.timestamp = block.timestamp;
_currentBid.bidder = msg.sender;
_currentBid.contributionRate = newContributionRate;
_currentBid.perSecondFeeNumerator = ds
.paramsStore
.getPerSecondFeeNumerator();
_currentBid.perSecondFeeDenominator = ds
.paramsStore
.getPerSecondFeeDenominator();
_currentBid.forSalePrice = newForSalePrice;
emit LicenseReclaimed(msg.sender, claimPrice);
{
// Collect deposit
uint256 requiredBuffer = cs.cfaV1.cfa.getDepositRequiredForFlowRate(
paymentToken,
newContributionRate
);
paymentToken.safeTransferFrom(msg.sender, bidder, claimPrice);
paymentToken.safeTransferFrom(
msg.sender,
address(this),
requiredBuffer
);
}
// Create bidder flow
cs.cfaV1.host.callAgreement(
cs.cfaV1.cfa,
abi.encodeCall(
cs.cfaV1.cfa.createFlowByOperator,
(
paymentToken,
msg.sender,
address(this),
newContributionRate,
new bytes(0)
)
),
new bytes(0)
);
// Update beneficiary flow
LibCFABasePCO._createBeneficiaryFlow(newContributionRate);
// Transfer license (reentrancy on ERC721 transfer)
ds.license.safeTransferFrom(bidder, msg.sender, ds.licenseId);
}
}