Skip to content

Commit 2c1f544

Browse files
committed
adding upgrade with fallback
1 parent 0de46a4 commit 2c1f544

9 files changed

+27496
-37
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import "@unlock-protocol/contracts/dist/PublicLock/IPublicLockV13.sol";
2+
3+
// SPDX-License-Identifier: UNLICENSED
4+
pragma solidity ^0.8.27;
5+
6+
// Uncomment this line to use console.log
7+
// import "hardhat/console.sol";
8+
9+
interface IUniswapOracleV3 {
10+
function PERIOD() external returns (uint256);
11+
12+
function factory() external returns (address);
13+
14+
function update(address _tokenIn, address _tokenOut) external;
15+
16+
function consult(
17+
address _tokenIn,
18+
uint256 _amountIn,
19+
address _tokenOut
20+
) external view returns (uint256 _amountOut);
21+
22+
function updateAndConsult(
23+
address _tokenIn,
24+
uint256 _amountIn,
25+
address _tokenOut
26+
) external returns (uint256 _amountOut);
27+
}
28+
29+
contract UnlockPrimeHookWithRecipientAndFallback {
30+
address public unlockPrime;
31+
address public oracle;
32+
address public weth;
33+
mapping(address => uint[2]) public refunds; // [amount, time]
34+
35+
event UnlockPrimeSet(address);
36+
event OracleSet(address);
37+
event WethSet(address);
38+
event RefundSet(address, uint256, uint256);
39+
event RefundPaid(address, uint256);
40+
41+
constructor(address _unlockPrime, address _oracle, address _weth) {
42+
initialize(_unlockPrime, _oracle, _weth);
43+
}
44+
45+
function initialize(
46+
address _unlockPrime,
47+
address _oracle,
48+
address _weth
49+
) public {
50+
if (unlockPrime != address(0)) {
51+
revert("Already initialized");
52+
}
53+
unlockPrime = _unlockPrime;
54+
emit UnlockPrimeSet(_unlockPrime);
55+
oracle = _oracle;
56+
emit OracleSet(_oracle);
57+
weth = _weth;
58+
emit WethSet(_weth);
59+
}
60+
61+
// Set the unlock prime address, only callable by lock manager
62+
function setUnlockPrime(address _unlockPrime) public {
63+
if (!IPublicLockV13(unlockPrime).isLockManager(msg.sender)) {
64+
revert("Caller is not a lock manager");
65+
}
66+
unlockPrime = _unlockPrime;
67+
emit UnlockPrimeSet(_unlockPrime);
68+
}
69+
70+
// Set the oracle address, only callable by lock manager
71+
function setOracle(address _oracle) public {
72+
if (!IPublicLockV13(unlockPrime).isLockManager(msg.sender)) {
73+
revert("Caller is not a lock manager");
74+
}
75+
oracle = _oracle;
76+
emit OracleSet(_oracle);
77+
}
78+
79+
// Set the WETH address, only callable by lock manager
80+
function setWeth(address _weth) public {
81+
if (!IPublicLockV13(unlockPrime).isLockManager(msg.sender)) {
82+
revert("Caller is not a lock manager");
83+
}
84+
weth = _weth;
85+
emit WethSet(_weth);
86+
}
87+
88+
// Return the price of a key
89+
function keyPurchasePrice(
90+
address /* from */,
91+
address /* recipient */,
92+
address /* referrer */,
93+
bytes calldata /* data */
94+
) external view returns (uint256 minKeyPrice) {
95+
return IPublicLockV13(msg.sender).keyPrice();
96+
}
97+
98+
function recordRefund(address beneficiary, uint amount) private {
99+
address UPAddress = IPublicLockV13(unlockPrime).tokenAddress();
100+
// At this point, check the pricePaid and its value against Uniswap oracles
101+
uint valueInETH = IUniswapOracleV3(oracle).updateAndConsult(
102+
UPAddress,
103+
amount,
104+
weth
105+
);
106+
// Store the refund and the delay
107+
uint existingRefund = refunds[beneficiary][0];
108+
uint existingDelay = refunds[beneficiary][1];
109+
uint newRefund = (valueInETH * 11) / 10; // 10% bonus
110+
uint newDelay = block.timestamp + 60 * 60 * 24 * 14; // 2 weeks delay!
111+
112+
if (existingDelay < block.timestamp) {
113+
refunds[beneficiary] = [existingRefund + newRefund, newDelay];
114+
} else {
115+
// One needs to wait for the delay to be able to increase their refund.
116+
refunds[beneficiary] = [newRefund, newDelay];
117+
}
118+
119+
emit RefundSet(
120+
beneficiary,
121+
refunds[beneficiary][0],
122+
refunds[beneficiary][1]
123+
);
124+
}
125+
126+
// This function is called by Unlock when a key is purchased
127+
function onKeyPurchase(
128+
uint256 /* tokenId */,
129+
address /* from */,
130+
address recipient,
131+
address /* referrer */,
132+
bytes calldata /* data */,
133+
uint256 /* minKeyPrice */,
134+
uint256 /* pricePaid */
135+
) external {
136+
if (msg.sender == unlockPrime) {
137+
recordRefund(recipient, IPublicLockV13(msg.sender).keyPrice());
138+
}
139+
}
140+
141+
// This function is called by Unlock when a key is extended
142+
function onKeyExtend(
143+
uint tokenId,
144+
address /* sender */,
145+
uint /* newTimestamp */,
146+
uint /* expirationTimestamp */
147+
) external {
148+
IPublicLockV13 lock = IPublicLockV13(msg.sender);
149+
if (msg.sender == unlockPrime) {
150+
recordRefund(lock.ownerOf(tokenId), lock.keyPrice());
151+
}
152+
}
153+
154+
// Claim the refund!
155+
function claimRefund() external {
156+
uint[2] memory refund = refunds[msg.sender];
157+
require(refund[0] > 0, "No refund available");
158+
require(refund[1] < block.timestamp, "Refund not available yet");
159+
refunds[msg.sender] = [0, 0];
160+
payable(msg.sender).transfer(refund[0]);
161+
emit RefundPaid(msg.sender, refund[0]);
162+
}
163+
164+
receive() external payable {}
165+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"_format": "hh-sol-dbg-1",
3+
"buildInfo": "../build-info/1bad1a246ced45928824d35deeda720c.json"
4+
}

0 commit comments

Comments
 (0)