-
Notifications
You must be signed in to change notification settings - Fork 0
/
PRMContract.sol
278 lines (257 loc) · 10.8 KB
/
PRMContract.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
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
277
278
pragma solidity ^0.4.19;
/**
* @title PRM Token Implementation Smart Contract
* @author Alex "AL_X" Papageorgiou
* @dev
* The stripped-down token interface of the PRM Token for the PRM contract to communicate with in its future deployment.
* This is purely to generate the function signatures in a human-readable & secure format instead of using address.call(bytes4(sha3("function_name(types)")), parameters_values).
*/
contract PRMToken {
//Implementation of the standard ERC-20 balanceOf method
function balanceOf(address) public returns (uint256);
//Implementation of the standard ERC-20 transfer method
function transfer(address, uint256) public;
//Implementation of the bidding process PRM Token function
function refundAndBid(address, uint256, address, uint256) external;
//Implementation for the sale process PRM Token function
function saleTransfer(address, uint256, address) external;
//Implementaiton of the bidding resolution process PRM Token function
function releaseBid(address, uint256) external;
}
/**
* @title PRM (Photo Rights Management) Smart Contract
* @author Alex "AL_X" Papageorgiou
* @dev The smart contract facilitating the operation of the X platform & handling the sale/auction of photos as well as the ownership.
*/
contract PRM {
address public PRMTokenAddress;
address public admin;
uint256 public incrementalID = 0;
mapping(uint256 => PhotoAsset) photoAssets;
/**
* @dev
* All photo details gathered in one place for
* easy access and a single mapping reference
*/
struct PhotoAsset {
uint256 photoPrice;
uint256 photoTokenPrice;
uint256 expiryDate;
bool saleType;
address owner;
address lastBidder;
}
event PhotoOwnershipTransfer(address indexed _newOwner, address indexed _previousOwner, uint256 indexed _photoID);
event PhotoRelease(address indexed _viewer, uint256 indexed _photoID);
event PhotoBid(uint256 indexed _photoID, uint256 _bidPrice);
event PhotoUpload(address indexed _uploader, uint256 _photoID);
/**
* @notice Ensures admin is the caller of a function
*/
modifier isAdmin() {
require(msg.sender == admin);
//Continue executing rest of method body
_;
}
/**
* @notice PRM Constructor
*/
function PRM() public {
admin = msg.sender;
}
/**
* @notice Set the PRM Token Address. Interoperable with PRMToken schema-compliant tokens as well
* @param _PRMTokenAddress The address of the PRM Token contract
* @dev
* This function is unnecessary in case this contract is deployed
* after the token contract has been as the address can be hard-coded.
*/
function setPRMTokenAddress(address _PRMTokenAddress) external isAdmin {
PRMTokenAddress = _PRMTokenAddress;
}
/**
* @notice Enables retrieval of ERC-20 compliant tokens
* @param _tokenAddress The address of the ERC-20 compliant token
* @dev
* Feel free to reach out to us for any accidental loss
* of tokens to retrieve them.
*/
function retrieveAccidentalTransfers(address _tokenAddress) external isAdmin {
require(_tokenAddress != PRMTokenAddress);
PRMToken tokenObject = PRMToken(_tokenAddress);
uint256 fullBalance = tokenObject.balanceOf(this);
tokenObject.transfer(admin, fullBalance);
}
/**
* @notice Retrieve uploaded photo info based on the photo's ID
* @param _photoID The ID of the photo whose info we wish to retrieve
* @returns The photo's contained information as stored within the object's structure
*/
function getPhotoInfo(uint _photoID) external view returns (uint256, uint256, uint256, bool, address, address) {
PhotoAsset memory photo = photoAssets[_photoID];
return (photo.photoPrice, photo.photoTokenPrice, photo.expiryDate, photo.saleType, photo.owner, photo.lastBidder);
}
/**
* @notice Upload photo to the PRM network & set for sale as an opt-in
* @param _saleType The type of sale should there be one (1 = Single Sale, 2 = Reselling, 2< = Auction Sale & Hour Duration)
* @param _salePrice The price of the photo should a sale take place
* @param _isToken Whether tokens should be used for the sale or not
*/
function photoUpload(uint256 _saleType, uint256 _salePrice, bool _isToken) external {
photoAssets[incrementalID].owner = msg.sender;
if (_saleType == 1) {
photoSale(_salePrice, incrementalID, _isToken);
} else if (_saleType == 2) {
photoResale(_salePrice, incrementalID, _isToken);
} else if (_saleType > 2) {
photoAuction(_salePrice, _saleType, incrementalID, _isToken);
}
PhotoUpload(msg.sender, incrementalID);
incrementalID++;
}
/**
* @notice Set photo up for sale
* @param _salePrice The price to sell the photo at
* @param _photoID The photo's ID
* @param _isToken Whether tokens should be used for the sale or not
*/
function photoSale(uint256 _salePrice, uint256 _photoID, bool _isToken) public {
if (_isToken) {
photoAssets[_photoID].photoTokenPrice = _salePrice;
} else {
photoAssets[_photoID].photoPrice = _salePrice;
}
}
/**
* @notice Set photo up for resale
* @param _resalePrice The price to re-sell the photo at
* @param _photoID The photo's ID
* @param _isToken Whether tokens should be used for the sale or not
*/
function photoResale(uint256 _resalePrice, uint256 _photoID, bool _isToken) public {
PhotoAsset storage asset = photoAssets[_photoID];
if (_isToken) {
asset.photoTokenPrice = _resalePrice;
} else {
asset.photoPrice = _resalePrice;
}
asset.saleType = true;
}
/**
* @notice Set photo up for auction
* @param _startingBid The minimum bid requirement
* @param _auctionDuration The duration of the auction in days
* @param _photoID The photo's ID
* @param _isToken Whether tokens should be used for the sale or not
*/
function photoAuction(uint256 _startingBid, uint256 _auctionDuration, uint256 _photoID, bool _isToken) public {
PhotoAsset storage asset = photoAssets[_photoID];
if (_isToken) {
asset.photoTokenPrice = _startingBid;
} else {
asset.photoPrice = _startingBid;
}
asset.expiryDate = now + (1 hours * _auctionDuration);
}
/**
* @notice Bid on auction of photo
* @param _photoID The ID of the photo
* @param _tokenAmount The amount of tokens to use, should the token system be in effect
*/
function bidOnAuction(uint256 _photoID, uint256 _tokenAmount) external payable {
PhotoAsset storage asset = photoAssets[_photoID];
assert(asset.expiryDate > now);
if (_tokenAmount > asset.photoTokenPrice) {
PRMToken tokenObject = PRMToken(PRMTokenAddress);
tokenObject.refundAndBid(asset.lastBidder, asset.photoTokenPrice, msg.sender, _tokenAmount);
asset.photoTokenPrice = _tokenAmount;
asset.lastBidder = msg.sender;
PhotoBid(_photoID, _tokenAmount);
} else if (msg.value > asset.photoPrice) {
if (asset.lastBidder != 0x0) {
asset.lastBidder.transfer(asset.photoPrice);
}
asset.photoPrice = msg.value;
asset.lastBidder = msg.sender;
PhotoBid(_photoID, msg.value);
} else {
revert();
}
}
/**
* @notice Purchase right to view & use a photo
* @param _photoID The ID of the photo
* @param _tokenAmount The amount of tokens to use, should the token system be in effect
*/
function purchaseUsageRight(uint256 _photoID, uint256 _tokenAmount) external payable {
PhotoAsset storage asset = photoAssets[_photoID];
assert(asset.saleType);
if (_tokenAmount == asset.photoTokenPrice && asset.photoTokenPrice > 0) {
PRMToken tokenObject = PRMToken(PRMTokenAddress);
tokenObject.saleTransfer(msg.sender, _tokenAmount, asset.owner);
PhotoRelease(msg.sender, _photoID);
} else if (msg.value == asset.photoPrice && asset.photoPrice > 0) {
asset.owner.transfer(msg.value);
PhotoRelease(msg.sender, _photoID);
} else {
revert();
}
}
/**
* @notice Purchase complete ownership of photo
* @param _photoID The ID of the photo
* @param _tokenAmount The amount of tokens to use, should the token system be in effect
*/
function purchaseOwnershipRight(uint256 _photoID, uint256 _tokenAmount) external payable {
PhotoAsset storage asset = photoAssets[_photoID];
assert(!asset.saleType && asset.expiryDate == 0);
if (_tokenAmount == asset.photoTokenPrice && asset.photoTokenPrice > 0) {
PRMToken tokenObject = PRMToken(PRMTokenAddress);
tokenObject.saleTransfer(msg.sender, _tokenAmount, asset.owner);
asset.photoTokenPrice = 0;
PhotoOwnershipTransfer(msg.sender, asset.owner, _photoID);
asset.owner = msg.sender;
} else if (msg.value == asset.photoPrice && asset.photoPrice > 0) {
asset.owner.transfer(msg.value);
asset.photoPrice = 0;
PhotoOwnershipTransfer(msg.sender, asset.owner, _photoID);
asset.owner = msg.sender;
} else {
revert();
}
}
/**
* @notice Resolve auction outcome
* @param _photoID The ID of the photo
*/
function finalizeAuction(uint256 _photoID) external {
PhotoAsset storage asset = photoAssets[_photoID];
assert(asset.expiryDate < now && (asset.lastBidder == msg.sender || (msg.sender == asset.owner && asset.lastBidder == 0x0)));
if (asset.photoPrice > 0) {
if (asset.lastBidder != 0x0) {
asset.owner.transfer(asset.photoPrice);
PhotoOwnershipTransfer(asset.lastBidder, asset.owner, _photoID);
asset.owner = asset.lastBidder;
asset.lastBidder = 0x0;
}
asset.photoPrice = 0;
asset.expiryDate = 0;
} else if (asset.photoTokenPrice > 0) {
if (asset.lastBidder != 0x0) {
PRMToken tokenObject = PRMToken(PRMTokenAddress);
tokenObject.releaseBid(asset.owner, asset.photoTokenPrice);
PhotoOwnershipTransfer(asset.lastBidder, asset.owner, _photoID);
asset.owner = asset.lastBidder;
asset.lastBidder = 0x0;
}
asset.photoTokenPrice = 0;
asset.expiryDate = 0;
}
}
/**
* @notice Reverting fallback to prevent accidental Ethereum transfers
*/
function() public payable {
revert();
}
}