-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAdSpotContract.sol
More file actions
276 lines (244 loc) · 9.71 KB
/
AdSpotContract.sol
File metadata and controls
276 lines (244 loc) · 9.71 KB
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
/**
* @title AdSpotContract
* @dev This contract is used to create and manage ad spots on Advertisement platform.
* The Ad Spot is taken by the highest bidder by defining an NFT to showcase.
* The previous advertisers are members of a distribution pool and are paid out half of of the new advertiser's flow.
* @author @youssefea
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {
ISuperfluid,
ISuperToken,
ISuperfluidPool,
PoolConfig
} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
import {CFASuperAppBase} from "@superfluid-finance/ethereum-contracts/contracts/apps/CFASuperAppBase.sol";
import {SuperTokenV1Library} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol";
contract AdSpotContract is CFASuperAppBase {
using SuperTokenV1Library for ISuperToken;
uint256 private number;
address private poolAddress;
ISuperfluidPool pool;
ISuperToken acceptedToken;
address private owner;
address public highestBidder;
int96 private highestFlowRate;
uint256 private lastUpdate;
PoolConfig private poolConfig;
address public nftAddress;
uint256 public nftTokenId;
event newHighestBidder(address highestBidder, int96 flowRate);
event NftToShowcaseSet(address nftAddress, uint256 tokenId);
/*
* @dev Constructor to initialize the contract with necessary Superfluid interfaces and parameters.
* @param _acceptedToken The SuperToken accepted for streaming payments.
*/
constructor(ISuperToken _acceptedToken) CFASuperAppBase(ISuperfluid(ISuperToken(_acceptedToken).getHost())) {
acceptedToken = _acceptedToken;
owner = msg.sender;
poolConfig.transferabilityForUnitsOwner = true;
poolConfig.distributionFromAnyAddress = true;
pool = SuperTokenV1Library.createPool(acceptedToken, address(this), poolConfig);
poolAddress = address(pool);
pool.updateMemberUnits(owner, 1);
highestFlowRate = acceptedToken.getFlowRate(owner, address(this));
lastUpdate = block.timestamp;
highestBidder = address(0);
selfRegister(true, true, true);
}
function isAcceptedSuperToken(ISuperToken superToken) public view override returns (bool) {
return superToken == acceptedToken;
}
/*
* @dev Allows the highest bidder to set an NFT to showcase.
* @param _nftAddress The address of the NFT contract.
* @param _tokenId The token ID of the NFT.
*/
function setNftToShowcase(address _nftAddress, uint256 _tokenId) external {
require(msg.sender == highestBidder, "Only the highest bidder can set the NFT");
nftAddress = _nftAddress;
nftTokenId = _tokenId;
emit NftToShowcaseSet(_nftAddress, _tokenId);
}
// ---------------------------------------------------------------------------------------------
// Getters
// ---------------------------------------------------------------------------------------------
/**
* @dev Returns the address of the pool.
*/
function getPoolAddress() public view returns (address) {
return poolAddress;
}
/**
* @dev Returns the accepted token for streaming payments.
*/
function getAcceptedToken() public view returns (ISuperToken) {
return acceptedToken;
}
/**
* @dev Returns the address of the contract owner.
*/
function getOwner() public view returns (address) {
return owner;
}
/**
* @dev Returns the address of the highest bidder.
*/
function getHighestBidder() public view returns (address) {
return highestBidder;
}
/**
* @dev Returns the highest flow rate.
*/
function getHighestFlowRate() public view returns (int96) {
return highestFlowRate;
}
/**
* @dev Returns the last update timestamp.
*/
function getLastUpdate() public view returns (uint256) {
return lastUpdate;
}
/**
* @dev Returns owner's shares.
*/
function getOwnerShares() public view returns (uint128) {
return pool.getUnits(owner);
}
/**
* @dev Returns shares of the highest bidder.
*/
function getBidderShares() public view returns (uint128) {
return pool.getUnits(highestBidder);
}
/**
* @dev Returns the shares of an address.
* @param memberAddress The address to check.
*/
function getMemberShares(address memberAddress) public view returns (uint128) {
return pool.getUnits(memberAddress);
}
/**
* @dev Returns the total shares.
*/
function getTotalShares() public view returns (uint128) {
return pool.getTotalUnits();
}
/**
* @dev Returns the NFT address.
*/
function getNftAddress() public view returns (address) {
return nftAddress;
}
/**
* @dev Returns the NFT token ID.
*/
function getNftTokenId() public view returns (uint256) {
return nftTokenId;
}
// ---------------------------------------------------------------------------------------------
// SUPER APP CALLBACKS
// ---------------------------------------------------------------------------------------------
/*
* @dev Callback function that gets executed when a new flow is created to this contract.
* It handles logic for updating the highest bidder and distributing shares.
* @param sender The address of the sender creating the flow.
* @param ctx The context of the current flow transaction.
* @return bytes Returns the new transaction context.
*/
function onFlowCreated(
ISuperToken,
/*superToken*/
address sender,
int96 flowRate,
bytes calldata ctx
)
internal
override
returns (bytes memory newCtx)
{
require(flowRate > highestFlowRate, "Sender flowrate lower than current flowRate");
newCtx = ctx;
if (highestBidder != address(0)) {
newCtx = acceptedToken.deleteFlowWithCtx(highestBidder, address(this), ctx);
uint128 halfShares = SafeCast.toUint128(block.timestamp - lastUpdate) / 2;
if (pool.getUnits(owner) == 1) {
pool.updateMemberUnits(owner, halfShares + pool.getUnits(owner) - 1);
} else {
pool.updateMemberUnits(owner, halfShares + pool.getUnits(owner));
}
pool.updateMemberUnits(highestBidder, halfShares + pool.getUnits(highestBidder));
}
highestBidder = sender;
highestFlowRate = flowRate;
lastUpdate = block.timestamp;
emit newHighestBidder(highestBidder, highestFlowRate);
newCtx = acceptedToken.distributeFlowWithCtx(address(this), pool, flowRate, newCtx);
return newCtx;
}
/*
* @dev Callback function that gets executed when an existing flow to this contract is updated.
* It updates the highest bidder and adjusts share distribution accordingly.
* @param sender The address of the sender updating the flow.
* @param previousflowRate The previous flow rate before the update.
* @param lastUpdated The timestamp of the last update.
* @param ctx The context of the current flow transaction.
* @return bytes Returns the new transaction context.
*/
function onFlowUpdated(
ISuperToken,
address sender,
int96 flowRate,
int96 previousFlowRate,
uint256 lastUpdated,
bytes calldata ctx
) internal override returns (bytes memory newCtx) {
require(flowRate > previousFlowRate, "Sender flowRate is lower than the previous one, delete flowrate and start a new one lower");
require(flowRate > highestFlowRate, "You already have a flowrate that is higher than this one");
newCtx = ctx;
uint128 halfShares = SafeCast.toUint128(block.timestamp - lastUpdate) / 2;
ISuperfluidPool(poolAddress).updateMemberUnits(owner, halfShares + pool.getUnits(owner));
ISuperfluidPool(poolAddress).updateMemberUnits(highestBidder, halfShares + pool.getUnits(highestBidder));
newCtx = acceptedToken.distributeFlowWithCtx(address(this), pool, flowRate, newCtx);
highestBidder = sender;
highestFlowRate = flowRate;
lastUpdate = block.timestamp;
emit newHighestBidder(highestBidder, highestFlowRate);
return newCtx;
}
/*
* @dev Callback function that gets executed when a flow to this contract is deleted.
* Handles the removal of a bidder and adjustment of shares.
* @param sender The address of the sender deleting the flow.
* @param ctx The context of the current flow transaction.
* @return bytes Returns the new transaction context.
*/
function onInFlowDeleted(
ISuperToken,
/*superToken*/
address sender,
int96,
/*previousFlowRate*/
uint256,
/*lastUpdated*/
bytes calldata ctx
)
internal
override
returns (bytes memory newCtx)
{
require(sender == highestBidder, "You don't have an active stream");
uint128 halfShares = SafeCast.toUint128(block.timestamp - lastUpdate) / 2;
pool.updateMemberUnits(owner, halfShares + pool.getUnits(owner));
pool.updateMemberUnits(highestBidder, halfShares + pool.getUnits(highestBidder));
newCtx = acceptedToken.distributeFlowWithCtx(address(this), pool, 0, ctx);
highestBidder = address(0);
highestFlowRate = 0;
lastUpdate = block.timestamp;
emit newHighestBidder(highestBidder, highestFlowRate);
return newCtx;
}
}