邓心一 před 6 roky
revize
6a230e3973

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+* text eol=lf

+ 12 - 0
contracts/ERC721.sol

@@ -0,0 +1,12 @@
+pragma solidity ^0.4.23;
+
+contract ERC721 {
+    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
+    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
+
+    function balanceOf(address _owner) public view returns (uint256 _balance);
+    function ownerOf(uint256 _tokenId) public view returns (address _owner);
+    function transfer(address _to, uint256 _tokenId) public;
+    function approve(address _to, uint256 _tokenId) public;
+    function takeOwnership(uint256 _tokenId) public;
+}

+ 23 - 0
contracts/Migrations.sol

@@ -0,0 +1,23 @@
+pragma solidity ^0.4.23;
+
+contract Migrations {
+  address public owner;
+  uint public last_completed_migration;
+
+  constructor() public {
+    owner = msg.sender;
+  }
+
+  modifier restricted() {
+    if (msg.sender == owner) _;
+  }
+
+  function setCompleted(uint completed) public restricted {
+    last_completed_migration = completed;
+  }
+
+  function upgrade(address new_address) public restricted {
+    Migrations upgraded = Migrations(new_address);
+    upgraded.setCompleted(last_completed_migration);
+  }
+}

+ 22 - 0
contracts/Ownable.sol

@@ -0,0 +1,22 @@
+pragma solidity ^0.4.23;
+
+contract Ownable {
+    address public owner;
+
+    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
+
+    constructor () public {
+        owner = msg.sender;
+    }
+
+    modifier onlyOwner() {
+        require(msg.sender == owner);
+        _;
+    }
+
+    function transferOwnership(address newOwner) public onlyOwner {
+        require(newOwner != address(0));
+        emit OwnershipTransferred(owner, newOwner);
+        owner = newOwner;
+    }
+}

+ 48 - 0
contracts/SafeMath.sol

@@ -0,0 +1,48 @@
+pragma solidity ^0.4.23;
+
+
+/**
+ * @title SafeMath
+ * @dev Math operations with safety checks that throw on error
+ */
+library SafeMath {
+
+    /**
+    * @dev Multiplies two numbers, throws on overflow.
+    */
+    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
+        if (a == 0) {
+            return 0;
+        }
+        uint256 c = a * b;
+        assert(c / a == b);
+        return c;
+    }
+
+    /**
+    * @dev Integer division of two numbers, truncating the quotient.
+    */
+    function div(uint256 a, uint256 b) internal pure returns (uint256) {
+        // assert(b > 0); // Solidity automatically throws when dividing by 0
+        uint256 c = a / b;
+        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
+        return c;
+    }
+
+    /**
+    * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
+    */
+    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
+        assert(b <= a);
+        return a - b;
+    }
+
+    /**
+    * @dev Adds two numbers, throws on overflow.
+    */
+    function add(uint256 a, uint256 b) internal pure returns (uint256) {
+        uint256 c = a + b;
+        assert(c >= a);
+        return c;
+    }
+}

+ 35 - 0
contracts/ShowFactory.sol

@@ -0,0 +1,35 @@
+pragma solidity ^0.4.23;
+
+import "./Ownable.sol";
+
+contract ShowFactory is Ownable {
+    event NewShow(uint id, string name, bytes music);
+
+    struct Show {
+        string name;
+        bytes dance;
+        bytes music;
+    }
+
+    Show[] public shows;
+
+    mapping (uint=>address) public showToOwner;
+    mapping (address=>uint) public ownerShowCount;
+
+    modifier onlyOwnerOf(uint id) {
+        require(msg.sender == showToOwner[id]);
+        _;
+    }
+
+    function _createShow(string name, bytes dance, bytes music) internal {
+        uint id = shows.push(Show(name, dance, music)) - 1;
+        showToOwner[id] = msg.sender;
+        ownerShowCount[msg.sender]++;
+        emit NewShow(id, name, music);
+    }
+
+    function createFirstShow(string name, bytes dance, bytes music) public {
+        require(ownerShowCount[msg.sender] == 0); // The 1st show is free
+        _createShow(name, dance, music);
+    }
+}

+ 17 - 0
contracts/ShowHelper.sol

@@ -0,0 +1,17 @@
+pragma solidity ^0.4.23;
+
+import "./ShowFactory.sol";
+
+contract ShowHelper is ShowFactory {
+    function getShowsByOwner(address _owner) external view returns (uint[]) {
+        uint[] memory result = new uint[](ownerShowCount[_owner]);
+        uint counter = 0;
+        for (uint i = 0; i < shows.length; i++) {
+            if (showToOwner[i] == _owner) {
+                result[counter] = i;
+                counter++;
+            }
+        }
+        return result;
+    }
+}

+ 65 - 0
contracts/ShowOwnership.sol

@@ -0,0 +1,65 @@
+pragma solidity ^0.4.23;
+
+import "./ERC721.sol";
+import "./SafeMath.sol";
+import "./ShowHelper.sol";
+
+contract ShowOwnership is ERC721, ShowHelper {
+    event WantToExchange(uint mine, uint yours);
+    event RefuseToExchange(uint indexed yours);
+
+    using SafeMath for uint256;
+
+    mapping (uint=>address) showApprovals;
+    mapping (uint=>uint) public showExchange; // 0: not for exchange, n: exchange for the n_th show
+
+    function balanceOf(address _owner) public view returns (uint256 _balance) {
+        return ownerShowCount[_owner];
+    }
+
+    function ownerOf(uint256 _tokenId) public view returns (address _owner) {
+        return showToOwner[_tokenId];
+    }
+
+    function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
+        _transfer(msg.sender, _to, _tokenId);
+    }
+
+    function approve(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
+        showApprovals[_tokenId] = _to;
+        emit Approval(msg.sender, _to, _tokenId);
+    }
+
+    function takeOwnership(uint256 _tokenId) public {
+        require(msg.sender == showApprovals[_tokenId] && showExchange[_tokenId] == 0);
+        address owner = ownerOf(_tokenId);
+        _transfer(owner, msg.sender, _tokenId);
+    }
+
+    function wantToExchange(uint mine, uint yours) public onlyOwnerOf(mine) {
+        if (msg.sender == showApprovals[yours] && showExchange[yours] == mine.add(1)) {
+            showExchange[yours] = 0;
+            transfer(ownerOf(yours), mine);
+            takeOwnership(yours);
+        } else {
+            showExchange[mine] = yours.add(1);
+            showApprovals[mine] = ownerOf(yours);
+            emit WantToExchange(mine, yours);
+        }
+    }
+
+    function refuseToExchange(uint yours) public {
+        require(msg.sender == showApprovals[yours]);
+        require(showExchange[yours] != 0);
+        require(msg.sender == showToOwner[showExchange[yours].sub(1)]);
+        showApprovals[yours] = address(0);
+        showExchange[yours] = 0;
+    }
+
+    function _transfer(address _from, address _to, uint _tokenId) private {
+        ownerShowCount[_from] = ownerShowCount[_from].sub(1);
+        ownerShowCount[_to] = ownerShowCount[_to].add(1);
+        showToOwner[_tokenId] = _to;
+        emit Transfer(_from, _to, _tokenId);
+    }
+}

+ 46 - 0
contracts/ShowTrader.sol

@@ -0,0 +1,46 @@
+pragma solidity ^0.4.23;
+
+import "./ShowOwnership.sol";
+
+contract ShowTrader is ShowOwnership {
+    event Sell(uint id, uint price);
+    event Buy(uint id);
+
+    mapping (uint=>uint) public showToPrice;
+    uint productsCount;
+
+    constructor () public {
+        productsCount = 0;
+    }
+
+    function sell(uint id, uint price) external onlyOwnerOf(id) {
+        require(price != 0); // 0 means not for sale
+        productsCount = productsCount.add(1);
+        _sell(id, price);
+    }
+
+    function buy(uint id) external payable {
+        require(msg.value >= showToPrice[id]);
+        msg.sender.transfer(msg.value.sub(showToPrice[id])); // Take charge
+        ownerOf(id).transfer(showToPrice[id]);
+        transfer(msg.sender, id);
+        productsCount = productsCount.sub(1);
+    }
+
+    function getShowsForSale() external view returns (uint[]) {
+        uint[] memory result = new uint[](productsCount);
+        uint counter = 0;
+        for (uint id = 0; id < shows.length; id++) {
+            if (showToPrice[id] != 0) {
+                result[counter] = id;
+                counter++;
+            }
+        }
+        return result;
+    }
+
+    function _sell(uint id, uint price) private {
+        showToPrice[id] = price;
+        emit Sell(id, price);
+    }
+}

+ 6 - 0
migrations/1_initial_migration.js

@@ -0,0 +1,6 @@
+/* eslint no-undef: "off" */
+const Migrations = artifacts.require('./Migrations.sol')
+
+module.exports = (deployer) => {
+    deployer.deploy(Migrations, { gas: 4500000 })
+}

+ 12 - 0
migrations/2_deploy_contracts.js

@@ -0,0 +1,12 @@
+/* eslint no-undef: "off" */
+const ShowFactory = artifacts.require('ShowFactory')
+const ShowHelper = artifacts.require('ShowHelper')
+const ShowOwnership = artifacts.require('ShowOwnership')
+const ShowTrader = artifacts.require('ShowTrader')
+
+module.exports = (deployer) => {
+    deployer.deploy(ShowFactory)
+    deployer.deploy(ShowHelper)
+    deployer.deploy(ShowOwnership)
+    deployer.deploy(ShowTrader)
+}

+ 19 - 0
src/index.html

@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>CryptoZombies front-end</title>
+  </head>
+  <body>
+    <div id="txStatus"></div>
+    <div id="zombies"></div>
+
+    <input type="button" value="Create random zombie" id="createRandomZombie">
+
+    <script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
+    <script language="javascript" type="text/javascript" src="js/web3.min.js"></script>
+    <script language="javascript" type="text/javascript" src="js/truffle-contract.js"></script>
+
+    <script src="js/app.js"></script>
+  </body>
+</html>

+ 0 - 0
src/js/app.js


+ 18 - 0
test/TestShowTrader.sol

@@ -0,0 +1,18 @@
+pragma solidity ^0.4.23;
+
+import "truffle/Assert.sol";
+import "truffle/DeployedAddresses.sol";
+import "../contracts/ShowTrader.sol";
+
+contract TestShowTrader {
+    ShowTrader trader = ShowTrader(DeployedAddresses.ShowTrader());
+
+    function testCreateFirstShow() public {
+        string memory d = "dance";
+        string memory m = "music";
+        trader.createFirstShow("my first show", bytes(d), bytes(m));
+        string memory name;
+        (name,,) = trader.shows(0);
+        Assert.equal(name, "my first show", "The name of show is not as expected");
+    }
+} // quite useless

+ 29 - 0
test/test-contract.js

@@ -0,0 +1,29 @@
+/* eslint no-undef: "off" */
+/* eslint arrow-body-style: ["error", "always"] */
+const ShowTrader = artifacts.require('ShowTrader')
+
+contract('ShowTrader Test', (accounts) => {
+    let trader
+    const showName = '1st show'
+    const danceData = '0x111000'
+    const musicData = '0x111000'
+    it('should create 1 show', () => {
+        return ShowTrader.deployed()
+        .then((instance) => {
+            trader = instance
+            return trader.createFirstShow(showName, danceData, musicData, { from: accounts[0] })
+        })
+        .then(() => {
+            return trader.getShowsByOwner.call(accounts[0])
+        })
+        .then((shows) => {
+            const id = shows[0].toNumber()
+            return trader.shows(id)
+        })
+        .then((show) => {
+            assert.equal(show[0], showName)
+            assert.equal(show[1], danceData)
+            assert.equal(show[2], musicData)
+        })
+    })
+})

+ 18 - 0
truffle-config.js

@@ -0,0 +1,18 @@
+/*
+ * NB: since truffle-hdwallet-provider 0.0.5 you must wrap HDWallet providers in a
+ * function when declaring them. Failure to do so will cause commands to hang. ex:
+ * ```
+ * mainnet: {
+ *     provider: function() {
+ *       return new HDWalletProvider(mnemonic, 'https://mainnet.infura.io/<infura-key>')
+ *     },
+ *     network_id: '1',
+ *     gas: 4500000,
+ *     gasPrice: 10000000000,
+ *   },
+ */
+
+module.exports = {
+  // See <http://truffleframework.com/docs/advanced/configuration>
+  // to customize your Truffle configuration!
+}

+ 28 - 0
truffle.js

@@ -0,0 +1,28 @@
+/*
+ * NB: since truffle-hdwallet-provider 0.0.5 you must wrap HDWallet providers in a
+ * function when declaring them. Failure to do so will cause commands to hang. ex:
+ * ```
+ * mainnet: {
+ *     provider: function() {
+ *       return new HDWalletProvider(mnemonic, 'https://mainnet.infura.io/<infura-key>')
+ *     },
+ *     network_id: '1',
+ *     gas: 4500000,
+ *     gasPrice: 10000000000,
+ *   },
+ */
+
+module.exports = {
+  // See <http://truffleframework.com/docs/advanced/configuration>
+  // to customize your Truffle configuration!
+  migrations_directory: './migrations',
+  networks: {
+    development: {
+      host: 'localhost',
+      port: 7545,
+      network_id: '*', // Match any network id
+      gasPrice: 1,
+      gas: 8000000,
+    },
+  },
+}