Advertisement
Guest User

Untitled

a guest
Aug 23rd, 2017
514
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 22.56 KB | None | 0 0
  1. // KLEIN v. 4.03
  2. // By Mitchell F. Chan
  3.  
  4.  
  5. /*
  6.  
  7. OVERVIEW:
  8.  
  9. This contract manages the purchase and transferral of Digital Zones of Immaterial Pictorial Sensibility.
  10. It reproduces the rules originally created by Yves Klein which governed the transferral of his original Zones of Immaterial Pictorial Sensibility.
  11.  
  12. The project is described in full in the Blue Paper included in this repository.
  13.  
  14.  
  15. ABI:
  16.  
  17. [{"constant":false,"inputs":[{"name":"_edition","type":"uint256"}],"name":"ritual","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"issueNewSeries","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"totalSupply","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"saleStartTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"initialPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"records","outputs":[{"name":"addr","type":"address"},{"name":"price","type":"uint256"},{"name":"burned","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_edition","type":"uint256"}],"name":"specificTransfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"currentSeries","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"searchedRecord","type":"uint8"}],"name":"getTokenHolder","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"maxSupplyPossible","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSold","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"zonesSwarmAddress","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"buy","outputs":[{"name":"","type":"uint256[]"}],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"fund","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[],"name":"issuedToDate","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"series","outputs":[{"name":"price","type":"uint256"},{"name":"seriesSupply","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"redeemEther","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"burnedToDate","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"_saleStartTime","type":"uint256"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"IKBedition","type":"uint256"},{"indexed":false,"name":"holderAddress","type":"address"},{"indexed":false,"name":"price","type":"uint256"},{"indexed":false,"name":"burned","type":"bool"}],"name":"UpdateRecord","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"series","type":"uint256"}],"name":"SeriesCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"edition","type":"uint256"}],"name":"Burned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]
  18.  
  19. */
  20.  
  21. pragma solidity ^0.4.15;
  22.  
  23. // token boilerplate
  24. contract SafeMath {
  25.  
  26. function safeAdd(uint256 x, uint256 y) internal returns(uint256) {
  27. uint256 z = x + y;
  28. assert((z >= x) && (z >= y));
  29. return z;
  30. }
  31.  
  32. function safeSubtract(uint256 x, uint256 y) internal returns(uint256) {
  33. assert(x >= y);
  34. uint256 z = x - y;
  35. return z;
  36. }
  37.  
  38. function safeMult(uint256 x, uint256 y) internal returns(uint256) {
  39. uint256 z = x * y;
  40. assert((x == 0) || (z / x == y));
  41. return z;
  42. }
  43. }
  44.  
  45. // token boilerplate
  46. contract owned {
  47. address public owner;
  48.  
  49. function owned() {
  50. owner = msg.sender;
  51. }
  52.  
  53. modifier onlyOwner {
  54. require(msg.sender != owner);
  55. _;
  56. }
  57.  
  58. function transferOwnership(address newOwner) onlyOwner {
  59. owner = newOwner;
  60. }
  61. }
  62.  
  63. // interface for ERC20 standard token
  64. contract ERC20 {
  65. function totalSupply() constant returns (uint256 totalSupply);
  66. function balanceOf(address _owner) public constant returns (uint256 balance);
  67. function transfer(address _to, uint256 _value) returns (bool success);
  68. function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
  69. function approve(address _spender, uint256 _value) returns (bool success);
  70. function allowance(address _owner, address _spender) constant returns (uint256 remaining);
  71. event Transfer(address indexed _from, address indexed _to, uint256 _value);
  72. event Approval(address indexed _owner, address indexed _spender, uint256 _value);
  73. }
  74.  
  75. contract Klein is ERC20, SafeMath, owned {
  76. mapping (address => uint256) balances;
  77. mapping (address => mapping (address => uint256)) allowed;
  78.  
  79. //The Swarm address of the artwork is saved here for reference and posterity
  80. string public constant zonesSwarmAddress = "0a52f265d8d60a89de41a65069fa472ac3b130c269b4788811220b6546784920";
  81. string public constant name = "Digital Zone of Immaterial Pictorial Sensibility";
  82. string public constant symbol = "IKB";
  83. uint public constant decimals = 0;
  84. uint public maxSupplyPossible;
  85. bool first = true;
  86. uint public saleStartTime; // will be initialized to 8:30PM EST August 30, 2017
  87. uint public initialPrice = 10**17; // should equal 0.1 ETH
  88. uint public currentSeries;
  89. uint public issuedToDate;
  90. uint public totalSold;
  91. uint public burnedToDate;
  92. // IKB are issued in tranches, or series of editions. There will be 8 total
  93. // Each IBKSeries represents one of Klein's receipt books, or a series of issued tokens.
  94. struct IKBSeries {
  95. uint price;
  96. uint seriesSupply;
  97. }
  98.  
  99. IKBSeries[8] public series; // An array of all 8 series
  100.  
  101. struct record {
  102. address addr;
  103. uint price;
  104. bool burned;
  105. }
  106.  
  107. record[101] public records; // An array of all 101 records
  108.  
  109. event UpdateRecord(uint indexed IKBedition, address holderAddress, uint256 price, bool burned);
  110. event SeriesCreated(uint indexed series);
  111.  
  112. /* In regards to the artwork itself, do we want to record when an edition is burnt? */
  113. /* MFC: This is a really good point, actually, and one that I should think about further. */
  114. // event Burned(uint indexed edition);
  115.  
  116. function Klein(uint256 _saleStartTime) {
  117. saleStartTime = _saleStartTime;
  118. currentSeries = 0;
  119. series[0] = IKBSeries(initialPrice, 31); // the first series has unique values...
  120.  
  121. for(uint i = 1; i < series.length; i++){ // ...while the next 7 can be defined in a for loop
  122. series[i] = IKBSeries(series[i-1].price*2, 10);
  123. }
  124. // verifying that only 101 IKB may ever be issued.
  125. // it's already hardcoded that the array of records has only 101 places in it,
  126. // but it would be theoretically possible to issue IKB without making records, so this proof is important.
  127.  
  128. /* Why declare maxSupplyPossible this way instead of declaring it as 101? */
  129. /* MFC: I just wanted to be demonstrative and show my work, but yeah, let's do it your way */
  130. maxSupplyPossible = 101;
  131.  
  132. /* Uints == 0 by default so these declarations is unnecessary */
  133. /* MFC: thanks, I wasn't aware of that */
  134. }
  135.  
  136. function balanceOf(address _owner) constant returns (uint256 balance) {
  137. return balances[_owner];
  138. }
  139.  
  140. function approve(address _spender, uint256 _value) returns (bool success) {
  141. allowed[msg.sender][_spender] = _value;
  142. Approval(msg.sender, _spender, _value);
  143. return true;
  144. }
  145.  
  146. function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
  147. return allowed[_owner][_spender];
  148. }
  149.  
  150. /* Function name + returned variable are the same (won't technically mess anything up but it is confusing) */
  151. /* MFC: Thank you! I was confused by this myself, as I borrowed this code from another contract */
  152. function totalSupply() constant returns (uint _limit) {
  153. totalSupply = maxSupplyPossible;
  154. return totalSupply;
  155. }
  156.  
  157. /* To confirm the series is created, one would usually return true instead of a string (like approve above) */
  158. /* MFC: changed to return true */
  159. /* You may want to call this in the constructor */
  160. /* MFC: chose not to call in constructor, because I want to be able to issue the first series manually during an event I'm doing at the gallery */
  161.  
  162. function issueNewSeries() onlyOwner returns (bool success){
  163.  
  164. if(!first){
  165. currentSeries++; // the first time we run this function, don't run up the currentSeries counter. Keep it at 0
  166. } else if (first){
  167. first=false; // ...but only let this work once.
  168. }
  169.  
  170. /* Use requires at the top of this function instead of reverts here: require(now >= saleStartTime); */
  171. /* They serve the same purpose but are much cleaner */
  172. /* MFC: Question: will it be OK to use require() with multiple arguments separated by ||? Never actually seen this */
  173. if(issuedToDate >= maxSupplyPossible || issuedToDate + series[currentSeries].seriesSupply > maxSupplyPossible) revert(); //you can only issue a new series if there are no more tokens available to buy
  174. require(now >= saleStartTime); //you can only issue a new series if the sale has started
  175. require(balances[this] <= 0); //can only issue a new series if you've sold all the old ones
  176.  
  177. /* You have SafeMath above but don't use .add! It's not a big deal as an overflow */
  178. /* is likely not possible, but the redundancy is worth it */
  179. /* MFC: done */
  180. balances[this] = safeAdd(balances[this], series[currentSeries].seriesSupply);
  181. issuedToDate = safeAdd(issuedToDate + series[currentSeries].seriesSupply);
  182. SeriesCreated(currentSeries);
  183. return true;
  184. }
  185.  
  186. /* Again, returning true is what should happen here. */
  187. /* MFC: done */
  188. function buy() payable returns (bool success){
  189. /* If someone sends 23 Eth to series 7 with only 1 IKB left, 10.2 Eth will be lost */
  190. /* MFC: Hmmm.... suggestions? */
  191. uint amount = msg.value / series[currentSeries].price; // calculates the number of tokens the sender will buy
  192. uint[] memory editionsPurchased = new uint[](amount);
  193.  
  194. /* Again, should be requires */
  195. require(balances[this] > 0);
  196. require(msg.value > 0);
  197.  
  198. /* With the require above, all you need to check here is balances[this] < amount */
  199. if (balances[this] < amount) { // this section handles what happens if someone tries to buy more than the currently available supply
  200. uint256 receivable = safeMult(balances[this], series[currentSeries].price);
  201. uint256 returnable = safeSubtract(msg.value, receivable);
  202. amount = balances[this];
  203. /* msg.sender.transfer(returnable) will act the same (transfer will revert the function on a fail) */
  204. /* Either way, however, this isn't a security risk. Unless you explicitly define extra gas can be used, only 2300 extra gas */
  205. /* can be used from a .send or .transfer, which is not enough to even change the state of a variable */
  206. /* MFC: OK. Cool. I will actually rather use msg.sender.transfer(returnable) because I like the way that reads better */
  207. if(!msg.sender.send(returnable)) revert(); // IMPORTANT QUESTION: Is this a security risk? If the buy() function is called from a contract address rather than a wallet address, it may not have a fallback function. It could trap my send in malicious code...
  208. }
  209.  
  210. /* Use .add and .sub. I don't see a vector right now, but an underflow */
  211. /* would be disastrous for the contract */
  212. /* MFC: can you explain further? I'm curious about this. */
  213. balances[msg.sender] += amount; // adds the amount to buyer's balance
  214. balances[this] -= amount; // subtracts amount from seller's balance
  215.  
  216. /* Again, from an artistic perspective, do we want transfers recorded */
  217. /* MFC: Hmmm.... */
  218. Transfer(this, msg.sender, amount); // execute an event reflecting the change
  219.  
  220. /* From the artistic perspective, do we want a record made for every sale? The ritual may have less meaning then */
  221. /* MFC: So, the reason for the records (from a practical point of view) is that I need to know what everyone paid for their specific token in the event they choose to burn them */
  222. for(uint k = 0; k < amount; k++){ // now let's make a record of every sale
  223. records[totalSold] = record(msg.sender, series[currentSeries].price, false);
  224. totalSold++;
  225. editionsPurchased[k] = totalSold;
  226. }
  227.  
  228. /* return true */
  229. return true; // ends function and returns
  230. }
  231. // when this function is called, the caller is transferring any number of tokens. The function automatically chooses the tokens with the LOWEST index to transfer.
  232. function transfer(address _to, uint _value) returns (bool success) {
  233. /* Use requires:
  234. require(balances[msg.sender] >= _value);
  235. require(_value > 0);
  236. Instead of this whole if/else block */
  237. require(balances[msg.sender] >= _value);
  238. require(_value > 0);
  239. uint recordsChanged = 0;
  240. for(uint k = 0; k < records.length; k++){ // go through every record
  241. if(records[k].addr == msg.sender && recordsChanged < _value) {
  242. records[k].addr = _to; // change the address associated with this record
  243. recordsChanged++; // keep track of how many records you've changed in this transfer. After you've changed as many records as there are tokens being transferred, conditions of this loop will cease to be true.
  244. UpdateRecord(k, _to, records[k].price, records[k].burned);
  245. }
  246. }
  247. /* .sub, .add */
  248. balances[msg.sender] = safeSubtract(balances[msg.sender], _value);
  249. balances[_to] = safeAdd(balances[_to], _value);
  250.  
  251. /* Again, do we want this artistically? */
  252. /* It'll be much easier to see who had the IKBs with an event */
  253. /* MFC: OK, so to continue this conversation, I have come to the conclusion that the answer is "yes", we do want these records */
  254. /* For me, the artwork is about contrasting the artwork as a token vs the artwork as inherent idea */
  255. /* So while the artwork is a token, we have good recordkeeping and all that. After all, in this form the artwork can be a status symbol */
  256. /* And then when it's burned: nothing. So there's the contrast. And you help me realize the point that we shouldn't have a record for burning */
  257. Transfer(msg.sender, _to, _value);
  258. return true;
  259.  
  260. }
  261. // when this function is called, the caller is transferring only 1 IKB to another account, and specifying exactly which token they would like to transfer.
  262. function specificTransfer(address _to, uint _edition) returns (bool success) {
  263. /* Again, requires and SafeMath here */
  264. requires(balances[msg.sender] > 0);
  265. requires(records[_edition].addr == msg.sender);
  266. balances[msg.sender] = safeSubtract(balances[msg.sender], 1);
  267. balances[_to] = safeAdd(balances[_to], 1);
  268. records[_edition].addr = _to; // update the records so that the record shows this person owns the
  269.  
  270. /* Artistic question on these events as well */
  271. Transfer(msg.sender, _to, 1);
  272. UpdateRecord(_edition, _to, records[_edition].price, records[_edition].burned);
  273. }
  274. // a quick way to figure out who holds a specific token without querying the whole record. This might actually be redundant.
  275. function getTokenHolder(uint8 searchedRecord) public constant returns(address){
  276. return records[searchedRecord].addr;
  277. }
  278.  
  279. function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
  280. /* Requires instead of this if/else, SafeMath, and artistic event */
  281. /* Does not subtract the used _value from allowed[_from][msg.sender], allowing
  282. someone with any allowed _value to take all of from's IKBs */
  283. require(balances[_from] >= _value);
  284. require(allowed[_from][msg.sender] >= _value);
  285. require(_value > 0);
  286. uint recordsChanged = 0;
  287. for(uint k = 0; k < records.length; k++){ // go through every record
  288. if(records[k].addr == _from && recordsChanged < _value) {
  289. records[k].addr = _to; // change the address associated with this record
  290. recordsChanged++; // keep track of how many records you've changed in this transfer. After you've changed as many records as there are tokens being transferred, conditions of this loop will cease to be true.
  291. UpdateRecord(k, _to, records[k].price, records[k].burned);
  292. }
  293. }
  294. balances[_from] = safeSubtract(balances[_from], _value);
  295. balances[_to] = safeAdd(balances[_to], _value);
  296. Transfer(_from, _to, _value);
  297. return true;
  298. }
  299. // allows the artist to withdraw ether from the contract
  300. function redeemEther() onlyOwner{
  301. /* msg.sender.transfer(this.balance) throws an exception itself so you don't need revert */
  302. /* Also may want to just do owner.transfer to be extra safe */
  303. /* MFC: like so? */
  304. owner.transfer(this.balance);
  305. }
  306. // allows the artist to put ether back in the contract so that holders can execute the ritual function
  307. function fund() payable onlyOwner{
  308. }
  309.  
  310. /* return true */
  311. /* requires instead of if/else */
  312. /* Maybe also add a require to make sure the contract has enough gold */
  313. function ritual(uint _edition) returns (bool success){
  314. require(records[_edition].addr == msg.sender);
  315. require(!records[_edition].burned);
  316. uint256 halfTheGold = records[_edition].price / 2;
  317. require(this.balance >= halfTheGold);
  318. records[_edition].addr = 0xdead;
  319. records[_edition].burned = true;
  320. burnedToDate++;
  321. balances[msg.sender]--;
  322.  
  323. /* Artistically, you may want to create a River contract */
  324. /* that can receive funds but never disburse them instead of sending to a person */
  325. /* MFC: I thought about that a LOT actually! Ultimately, I thought that throwing the ETH to the miner felt more apt. */
  326. /* ..cause I suspect that people probably eventually found or will find gold that got thrown into the river. */
  327.  
  328. block.coinbase.transfer(halfTheGold); // call should fail if this contract isn't holding enough ETH
  329. /* Again, do we want an event */
  330. /* MFC: Agreed. */
  331. // Burned(_edition);
  332.  
  333. return true;
  334. }
  335. // fallback function
  336. function() payable {
  337. buy();
  338. }
  339. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement