xiongzhu пре 4 година
родитељ
комит
988d877ac5

+ 1060 - 0
src/main/contract/9th.sol

@@ -0,0 +1,1060 @@
+pragma solidity ^0.6.3;
+
+library Strings {
+    function toString(uint256 value) internal pure returns (string memory) {
+        // Inspired by OraclizeAPI's implementation - MIT licence
+        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
+
+        if (value == 0) {
+            return "0";
+        }
+        uint256 temp = value;
+        uint256 digits;
+        while (temp != 0) {
+            digits++;
+            temp /= 10;
+        }
+        bytes memory buffer = new bytes(digits);
+        uint256 index = digits - 1;
+        temp = value;
+        while (temp != 0) {
+            buffer[index--] = bytes1(uint8(48 + (temp % 10)));
+            temp /= 10;
+        }
+        return string(buffer);
+    }
+}
+
+library EnumerableMap {
+    struct MapEntry {
+        bytes32 _key;
+        bytes32 _value;
+    }
+
+    struct Map {
+        // Storage of map keys and values
+        MapEntry[] _entries;
+        // Position of the entry defined by a key in the `entries` array, plus 1
+        // because index 0 means a key is not in the map.
+        mapping(bytes32 => uint256) _indexes;
+    }
+
+    function _set(
+        Map storage map,
+        bytes32 key,
+        bytes32 value
+    ) private returns (bool) {
+        // We read and store the key's index to prevent multiple reads from the same storage slot
+        uint256 keyIndex = map._indexes[key];
+
+        if (keyIndex == 0) {
+            // Equivalent to !contains(map, key)
+            map._entries.push(MapEntry({_key: key, _value: value}));
+            // The entry is stored at length-1, but we add 1 to all indexes
+            // and use 0 as a sentinel value
+            map._indexes[key] = map._entries.length;
+            return true;
+        } else {
+            map._entries[keyIndex - 1]._value = value;
+            return false;
+        }
+    }
+
+    function _remove(Map storage map, bytes32 key) private returns (bool) {
+        // We read and store the key's index to prevent multiple reads from the same storage slot
+        uint256 keyIndex = map._indexes[key];
+
+        if (keyIndex != 0) {
+            // Equivalent to contains(map, key)
+            // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
+            // in the array, and then remove the last entry (sometimes called as 'swap and pop').
+            // This modifies the order of the array, as noted in {at}.
+
+            uint256 toDeleteIndex = keyIndex - 1;
+            uint256 lastIndex = map._entries.length - 1;
+
+            // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs
+            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
+
+            MapEntry storage lastEntry = map._entries[lastIndex];
+
+            // Move the last entry to the index where the entry to delete is
+            map._entries[toDeleteIndex] = lastEntry;
+            // Update the index for the moved entry
+            map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based
+
+            // Delete the slot where the moved entry was stored
+            map._entries.pop();
+
+            // Delete the index for the deleted slot
+            delete map._indexes[key];
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    function _contains(Map storage map, bytes32 key)
+        private
+        view
+        returns (bool)
+    {
+        return map._indexes[key] != 0;
+    }
+
+    function _length(Map storage map) private view returns (uint256) {
+        return map._entries.length;
+    }
+
+    function _at(Map storage map, uint256 index)
+        private
+        view
+        returns (bytes32, bytes32)
+    {
+        require(
+            map._entries.length > index,
+            "EnumerableMap: index out of bounds"
+        );
+
+        MapEntry storage entry = map._entries[index];
+        return (entry._key, entry._value);
+    }
+
+    function _tryGet(Map storage map, bytes32 key)
+        private
+        view
+        returns (bool, bytes32)
+    {
+        uint256 keyIndex = map._indexes[key];
+        if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key)
+        return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based
+    }
+
+    function _get(Map storage map, bytes32 key) private view returns (bytes32) {
+        uint256 keyIndex = map._indexes[key];
+        require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key)
+        return map._entries[keyIndex - 1]._value; // All indexes are 1-based
+    }
+
+    function _get(
+        Map storage map,
+        bytes32 key,
+        string memory errorMessage
+    ) private view returns (bytes32) {
+        uint256 keyIndex = map._indexes[key];
+        require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
+        return map._entries[keyIndex - 1]._value; // All indexes are 1-based
+    }
+
+    // UintToAddressMap
+
+    struct UintToAddressMap {
+        Map _inner;
+    }
+
+    function set(
+        UintToAddressMap storage map,
+        uint256 key,
+        identity value
+    ) internal returns (bool) {
+        return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
+    }
+
+    function remove(UintToAddressMap storage map, uint256 key)
+        internal
+        returns (bool)
+    {
+        return _remove(map._inner, bytes32(key));
+    }
+
+    function contains(UintToAddressMap storage map, uint256 key)
+        internal
+        view
+        returns (bool)
+    {
+        return _contains(map._inner, bytes32(key));
+    }
+
+    function length(UintToAddressMap storage map)
+        internal
+        view
+        returns (uint256)
+    {
+        return _length(map._inner);
+    }
+
+    function at(UintToAddressMap storage map, uint256 index)
+        internal
+        view
+        returns (uint256, identity)
+    {
+        (bytes32 key, bytes32 value) = _at(map._inner, index);
+        return (uint256(key), identity(uint160(uint256(value))));
+    }
+
+    function tryGet(UintToAddressMap storage map, uint256 key)
+        internal
+        view
+        returns (bool, identity)
+    {
+        (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
+        return (success, identity(uint160(uint256(value))));
+    }
+
+    function get(UintToAddressMap storage map, uint256 key)
+        internal
+        view
+        returns (identity)
+    {
+        return identity(uint160(uint256(_get(map._inner, bytes32(key)))));
+    }
+
+    function get(
+        UintToAddressMap storage map,
+        uint256 key,
+        string memory errorMessage
+    ) internal view returns (identity) {
+        return
+            identity(
+                uint160(uint256(_get(map._inner, bytes32(key), errorMessage)))
+            );
+    }
+}
+
+library EnumerableSet {
+
+    struct Set {
+        bytes32[] _values;
+
+        mapping (bytes32 => uint256) _indexes;
+    }
+
+    function _add(Set storage set, bytes32 value) private returns (bool) {
+        if (!_contains(set, value)) {
+            set._values.push(value);
+            set._indexes[value] = set._values.length;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+
+    function _remove(Set storage set, bytes32 value) private returns (bool) {
+        uint256 valueIndex = set._indexes[value];
+
+        if (valueIndex != 0) {
+
+            uint256 toDeleteIndex = valueIndex - 1;
+            uint256 lastIndex = set._values.length - 1;
+
+            bytes32 lastvalue = set._values[lastIndex];
+
+            set._values[toDeleteIndex] = lastvalue;
+            set._indexes[lastvalue] = toDeleteIndex + 1;
+
+            set._values.pop();
+
+            delete set._indexes[value];
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    function _contains(Set storage set, bytes32 value) private view returns (bool) {
+        return set._indexes[value] != 0;
+    }
+
+    function _length(Set storage set) private view returns (uint256) {
+        return set._values.length;
+    }
+
+    function _at(Set storage set, uint256 index) private view returns (bytes32) {
+        require(set._values.length > index, "EnumerableSet: index out of bounds");
+        return set._values[index];
+    }
+
+
+    struct Bytes32Set {
+        Set _inner;
+    }
+
+    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
+        return _add(set._inner, value);
+    }
+
+    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
+        return _remove(set._inner, value);
+    }
+
+    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
+        return _contains(set._inner, value);
+    }
+
+    function length(Bytes32Set storage set) internal view returns (uint256) {
+        return _length(set._inner);
+    }
+
+    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
+        return _at(set._inner, index);
+    }
+
+    struct AddressSet {
+        Set _inner;
+    }
+
+    function add(AddressSet storage set, identity value) internal returns (bool) {
+        return _add(set._inner, bytes32(uint256(uint160(value))));
+    }
+
+    function remove(AddressSet storage set, identity value) internal returns (bool) {
+        return _remove(set._inner, bytes32(uint256(uint160(value))));
+    }
+
+    function contains(AddressSet storage set, identity value) internal view returns (bool) {
+        return _contains(set._inner, bytes32(uint256(uint160(value))));
+    }
+
+    function length(AddressSet storage set) internal view returns (uint256) {
+        return _length(set._inner);
+    }
+
+    function at(AddressSet storage set, uint256 index) internal view returns (identity) {
+        return identity(uint160(uint256(_at(set._inner, index))));
+    }
+
+    struct UintSet {
+        Set _inner;
+    }
+
+    function add(UintSet storage set, uint256 value) internal returns (bool) {
+        return _add(set._inner, bytes32(value));
+    }
+
+    function remove(UintSet storage set, uint256 value) internal returns (bool) {
+        return _remove(set._inner, bytes32(value));
+    }
+
+    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
+        return _contains(set._inner, bytes32(value));
+    }
+
+    function length(UintSet storage set) internal view returns (uint256) {
+        return _length(set._inner);
+    }
+
+    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
+        return uint256(_at(set._inner, index));
+    }
+}
+
+library Address {
+
+    function isContract(identity account) internal view returns (bool) {
+        // This method relies on extcodesize, which returns 0 for contracts in
+        // construction, since the code is only stored at the end of the
+        // constructor execution.
+
+        uint256 size;
+        // solhint-disable-next-line no-inline-assembly
+        assembly { size := extcodesize(account) }
+        return size > 0;
+    }
+
+    function sendValue(identity recipient, uint256 amount) internal {
+        require(identity(this).balance >= amount, "Address: insufficient balance");
+
+        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
+        (bool success, ) = recipient.call{ value: amount }("");
+        require(success, "Address: unable to send value, recipient may have reverted");
+    }
+
+    function functionCall(identity target, bytes memory data) internal returns (bytes memory) {
+      return functionCall(target, data, "Address: low-level call failed");
+    }
+
+    function functionCall(identity target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
+        return functionCallWithValue(target, data, 0, errorMessage);
+    }
+
+    function functionCallWithValue(identity target, bytes memory data, uint256 value) internal returns (bytes memory) {
+        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
+    }
+
+    function functionCallWithValue(identity target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
+        require(identity(this).balance >= value, "Address: insufficient balance for call");
+        require(isContract(target), "Address: call to non-contract");
+
+        // solhint-disable-next-line avoid-low-level-calls
+        (bool success, bytes memory returndata) = target.call{ value: value }(data);
+        return _verifyCallResult(success, returndata, errorMessage);
+    }
+
+    function functionStaticCall(identity target, bytes memory data) internal view returns (bytes memory) {
+        return functionStaticCall(target, data, "Address: low-level static call failed");
+    }
+
+    function functionStaticCall(identity target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
+        require(isContract(target), "Address: static call to non-contract");
+
+        // solhint-disable-next-line avoid-low-level-calls
+        (bool success, bytes memory returndata) = target.staticcall(data);
+        return _verifyCallResult(success, returndata, errorMessage);
+    }
+
+    function functionDelegateCall(identity target, bytes memory data) internal returns (bytes memory) {
+        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
+    }
+
+    function functionDelegateCall(identity target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
+        require(isContract(target), "Address: delegate call to non-contract");
+
+        // solhint-disable-next-line avoid-low-level-calls
+        (bool success, bytes memory returndata) = target.delegatecall(data);
+        return _verifyCallResult(success, returndata, errorMessage);
+    }
+
+    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
+        if (success) {
+            return returndata;
+        } else {
+            // Look for revert reason and bubble it up if present
+            if (returndata.length > 0) {
+                // The easiest way to bubble the revert reason is using memory via assembly
+
+                // solhint-disable-next-line no-inline-assembly
+                assembly {
+                    let returndata_size := mload(returndata)
+                    revert(add(32, returndata), returndata_size)
+                }
+            } else {
+                revert(errorMessage);
+            }
+        }
+    }
+}
+
+library SafeMath {
+
+    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
+        uint256 c = a + b;
+        if (c < a) return (false, 0);
+        return (true, c);
+    }
+
+    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
+        if (b > a) return (false, 0);
+        return (true, a - b);
+    }
+
+    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
+        if (a == 0) return (true, 0);
+        uint256 c = a * b;
+        if (c / a != b) return (false, 0);
+        return (true, c);
+    }
+
+    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
+        if (b == 0) return (false, 0);
+        return (true, a / b);
+    }
+
+    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
+        if (b == 0) return (false, 0);
+        return (true, a % b);
+    }
+
+    function add(uint256 a, uint256 b) internal pure returns (uint256) {
+        uint256 c = a + b;
+        require(c >= a, "SafeMath: addition overflow");
+        return c;
+    }
+
+    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
+        require(b <= a, "SafeMath: subtraction overflow");
+        return a - b;
+    }
+
+    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
+        if (a == 0) return 0;
+        uint256 c = a * b;
+        require(c / a == b, "SafeMath: multiplication overflow");
+        return c;
+    }
+
+    function div(uint256 a, uint256 b) internal pure returns (uint256) {
+        require(b > 0, "SafeMath: division by zero");
+        return a / b;
+    }
+
+    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
+        require(b > 0, "SafeMath: modulo by zero");
+        return a % b;
+    }
+
+    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
+        require(b <= a, errorMessage);
+        return a - b;
+    }
+
+    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
+        require(b > 0, errorMessage);
+        return a / b;
+    }
+
+    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
+        require(b > 0, errorMessage);
+        return a % b;
+    }
+}
+
+library Counters {
+    using SafeMath for uint256;
+
+    struct Counter {
+        // This variable should never be directly accessed by users of the library: interactions must be restricted to
+        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
+        // this feature: see https://github.com/ethereum/solidity/issues/4637
+        uint256 _value; // default: 0
+    }
+
+    function current(Counter storage counter) internal view returns (uint256) {
+        return counter._value;
+    }
+
+    function increment(Counter storage counter) internal {
+        // The {SafeMath} overflow check can be skipped here, see the comment at the top
+        counter._value += 1;
+    }
+
+    function decrement(Counter storage counter) internal {
+        counter._value = counter._value.sub(1);
+    }
+}
+
+abstract contract Context {
+    function _msgSender() internal view virtual returns (identity) {
+        return msg.sender;
+    }
+
+    function _msgData() internal view virtual returns (bytes memory) {
+        this;
+        return msg.data;
+    }
+}
+
+abstract contract AccessControl is Context {
+    using EnumerableSet for EnumerableSet.AddressSet;
+    using Address for identity;
+
+    struct RoleData {
+        EnumerableSet.AddressSet members;
+        bytes32 adminRole;
+    }
+
+    mapping (bytes32 => RoleData) private _roles;
+
+    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
+
+    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
+
+    event RoleGranted(bytes32 indexed role, identity indexed account, identity indexed sender);
+
+    event RoleRevoked(bytes32 indexed role, identity indexed account, identity indexed sender);
+
+    function hasRole(bytes32 role, identity account) public view returns (bool) {
+        return _roles[role].members.contains(account);
+    }
+
+    function getRoleMemberCount(bytes32 role) public view returns (uint256) {
+        return _roles[role].members.length();
+    }
+
+    function getRoleMember(bytes32 role, uint256 index) public view returns (identity) {
+        return _roles[role].members.at(index);
+    }
+
+    function getRoleAdmin(bytes32 role) public view returns (bytes32) {
+        return _roles[role].adminRole;
+    }
+
+    function grantRole(bytes32 role, identity account) public virtual {
+        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
+
+        _grantRole(role, account);
+    }
+
+    function revokeRole(bytes32 role, identity account) public virtual {
+        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
+
+        _revokeRole(role, account);
+    }
+
+    function renounceRole(bytes32 role, identity account) public virtual {
+        require(account == _msgSender(), "AccessControl: can only renounce roles for self");
+
+        _revokeRole(role, account);
+    }
+
+    function _setupRole(bytes32 role, identity account) internal virtual {
+        _grantRole(role, account);
+    }
+
+    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
+        emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
+        _roles[role].adminRole = adminRole;
+    }
+
+    function _grantRole(bytes32 role, identity account) private {
+        if (_roles[role].members.add(account)) {
+            emit RoleGranted(role, account, _msgSender());
+        }
+    }
+
+    function _revokeRole(bytes32 role, identity account) private {
+        if (_roles[role].members.remove(account)) {
+            emit RoleRevoked(role, account, _msgSender());
+        }
+    }
+}
+
+interface IERC165 {
+    function supportsInterface(bytes4 interfaceId) external view returns (bool);
+}
+
+abstract contract ERC165 is IERC165 {
+
+    bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
+
+    mapping(bytes4 => bool) private _supportedInterfaces;
+
+    constructor () internal {
+        _registerInterface(_INTERFACE_ID_ERC165);
+    }
+
+    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
+        return _supportedInterfaces[interfaceId];
+    }
+
+    function _registerInterface(bytes4 interfaceId) internal virtual {
+        require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
+        _supportedInterfaces[interfaceId] = true;
+    }
+}
+
+interface IERC721 is IERC165 {
+
+    event Transfer(identity indexed from, identity indexed to, uint256 indexed tokenId);
+
+    event Approval(identity indexed owner, identity indexed approved, uint256 indexed tokenId);
+
+    event ApprovalForAll(identity indexed owner, identity indexed operator, bool approved);
+
+    function balanceOf(identity owner) external view returns (uint256 balance);
+
+    function ownerOf(uint256 tokenId) external view returns (identity owner);
+
+    function safeTransferFrom(identity from, identity to, uint256 tokenId) external;
+
+    function transferFrom(identity from, identity to, uint256 tokenId) external;
+
+    function approve(identity to, uint256 tokenId) external;
+
+    function getApproved(uint256 tokenId) external view returns (identity operator);
+
+    function setApprovalForAll(identity operator, bool _approved) external;
+
+    function isApprovedForAll(identity owner, identity operator) external view returns (bool);
+
+    function safeTransferFrom(identity from, identity to, uint256 tokenId, bytes calldata data) external;
+}
+
+interface IERC721Metadata is IERC721 {
+
+    function name() external view returns (string memory);
+
+    function symbol() external view returns (string memory);
+
+    function tokenURI(uint256 tokenId) external view returns (string memory);
+}
+
+interface IERC721Enumerable is IERC721 {
+
+    function totalSupply() external view returns (uint256);
+
+    function tokenOfOwnerByIndex(identity owner, uint256 index) external view returns (uint256 tokenId);
+
+    function tokenByIndex(uint256 index) external view returns (uint256);
+}
+
+interface IERC721Receiver {
+    function onERC721Received(identity operator, identity from, uint256 tokenId, bytes calldata data) external returns (bytes4);
+}
+
+contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {
+    using SafeMath for uint256;
+    using Address for identity;
+    using EnumerableSet for EnumerableSet.UintSet;
+    using EnumerableMap for EnumerableMap.UintToAddressMap;
+    using Strings for uint256;
+
+    bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
+    mapping (identity => EnumerableSet.UintSet) private _holderTokens;
+    EnumerableMap.UintToAddressMap private _tokenOwners;
+    mapping (uint256 => identity) private _tokenApprovals;
+    mapping (identity => mapping (identity => bool)) private _operatorApprovals;
+    string private _name;
+    string private _symbol;
+    mapping (uint256 => string) private _tokenURIs;
+    string private _baseURI;
+    bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
+    bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
+    bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
+
+    constructor (string memory name_, string memory symbol_) public {
+        _name = name_;
+        _symbol = symbol_;
+
+        // register the supported interfaces to conform to ERC721 via ERC165
+        _registerInterface(_INTERFACE_ID_ERC721);
+        _registerInterface(_INTERFACE_ID_ERC721_METADATA);
+        _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
+    }
+
+    function balanceOf(identity owner) public view virtual override returns (uint256) {
+        require(owner != identity(0), "ERC721: balance query for the zero address");
+        return _holderTokens[owner].length();
+    }
+
+    function ownerOf(uint256 tokenId) public view virtual override returns (identity) {
+        return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
+    }
+
+    function name() public view virtual override returns (string memory) {
+        return _name;
+    }
+
+    function symbol() public view virtual override returns (string memory) {
+        return _symbol;
+    }
+
+    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
+        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
+
+        string memory _tokenURI = _tokenURIs[tokenId];
+        string memory base = baseURI();
+
+        // If there is no base URI, return the token URI.
+        if (bytes(base).length == 0) {
+            return _tokenURI;
+        }
+        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
+        if (bytes(_tokenURI).length > 0) {
+            return string(abi.encodePacked(base, _tokenURI));
+        }
+        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
+        return string(abi.encodePacked(base, tokenId.toString()));
+    }
+
+    function baseURI() public view virtual returns (string memory) {
+        return _baseURI;
+    }
+
+    function tokenOfOwnerByIndex(identity owner, uint256 index) public view virtual override returns (uint256) {
+        return _holderTokens[owner].at(index);
+    }
+
+    function totalSupply() public view virtual override returns (uint256) {
+        // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
+        return _tokenOwners.length();
+    }
+
+    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
+        (uint256 tokenId, ) = _tokenOwners.at(index);
+        return tokenId;
+    }
+
+    function approve(identity to, uint256 tokenId) public virtual override {
+        identity owner = ERC721.ownerOf(tokenId);
+        require(to != owner, "ERC721: approval to current owner");
+
+        require(_msgSender() == owner || ERC721.isApprovedForAll(owner, _msgSender()),
+            "ERC721: approve caller is not owner nor approved for all"
+        );
+
+        _approve(to, tokenId);
+    }
+
+    function getApproved(uint256 tokenId) public view virtual override returns (identity) {
+        require(_exists(tokenId), "ERC721: approved query for nonexistent token");
+
+        return _tokenApprovals[tokenId];
+    }
+
+    function setApprovalForAll(identity operator, bool approved) public virtual override {
+        require(operator != _msgSender(), "ERC721: approve to caller");
+
+        _operatorApprovals[_msgSender()][operator] = approved;
+        emit ApprovalForAll(_msgSender(), operator, approved);
+    }
+
+    function isApprovedForAll(identity owner, identity operator) public view virtual override returns (bool) {
+        return _operatorApprovals[owner][operator];
+    }
+
+    function transferFrom(identity from, identity to, uint256 tokenId) public virtual override {
+        //solhint-disable-next-line max-line-length
+        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
+
+        _transfer(from, to, tokenId);
+    }
+
+    function safeTransferFrom(identity from, identity to, uint256 tokenId) public virtual override {
+        safeTransferFrom(from, to, tokenId, "");
+    }
+
+    function safeTransferFrom(identity from, identity to, uint256 tokenId, bytes memory _data) public virtual override {
+        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
+        _safeTransfer(from, to, tokenId, _data);
+    }
+
+    function _safeTransfer(identity from, identity to, uint256 tokenId, bytes memory _data) internal virtual {
+        _transfer(from, to, tokenId);
+        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
+    }
+
+    function _exists(uint256 tokenId) internal view virtual returns (bool) {
+        return _tokenOwners.contains(tokenId);
+    }
+
+    function _isApprovedOrOwner(identity spender, uint256 tokenId) internal view virtual returns (bool) {
+        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
+        identity owner = ERC721.ownerOf(tokenId);
+        return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, spender));
+    }
+
+    function _safeMint(identity to, uint256 tokenId) internal virtual {
+        _safeMint(to, tokenId, "");
+    }
+
+    function _safeMint(identity to, uint256 tokenId, bytes memory _data) internal virtual {
+        _mint(to, tokenId);
+        require(_checkOnERC721Received(identity(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
+    }
+
+    function _mint(identity to, uint256 tokenId) internal virtual {
+        require(to != identity(0), "ERC721: mint to the zero address");
+        require(!_exists(tokenId), "ERC721: token already minted");
+
+        _beforeTokenTransfer(identity(0), to, tokenId);
+
+        _holderTokens[to].add(tokenId);
+
+        _tokenOwners.set(tokenId, to);
+
+        emit Transfer(identity(0), to, tokenId);
+    }
+
+    function _burn(uint256 tokenId) internal virtual {
+        identity owner = ERC721.ownerOf(tokenId); // internal owner
+
+        _beforeTokenTransfer(owner, identity(0), tokenId);
+
+        // Clear approvals
+        _approve(identity(0), tokenId);
+
+        // Clear metadata (if any)
+        if (bytes(_tokenURIs[tokenId]).length != 0) {
+            delete _tokenURIs[tokenId];
+        }
+
+        _holderTokens[owner].remove(tokenId);
+
+        _tokenOwners.remove(tokenId);
+
+        emit Transfer(owner, identity(0), tokenId);
+    }
+
+    function _transfer(identity from, identity to, uint256 tokenId) internal virtual {
+        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner
+        require(to != identity(0), "ERC721: transfer to the zero address");
+
+        _beforeTokenTransfer(from, to, tokenId);
+
+        // Clear approvals from the previous owner
+        _approve(identity(0), tokenId);
+
+        _holderTokens[from].remove(tokenId);
+        _holderTokens[to].add(tokenId);
+
+        _tokenOwners.set(tokenId, to);
+
+        emit Transfer(from, to, tokenId);
+    }
+
+    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
+        require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
+        _tokenURIs[tokenId] = _tokenURI;
+    }
+
+    function _setBaseURI(string memory baseURI_) internal virtual {
+        _baseURI = baseURI_;
+    }
+
+    function _checkOnERC721Received(identity from, identity to, uint256 tokenId, bytes memory _data)
+        private returns (bool)
+    {
+        if (!to.isContract()) {
+            return true;
+        }
+        bytes memory returndata = to.functionCall(abi.encodeWithSelector(
+            IERC721Receiver(to).onERC721Received.selector,
+            _msgSender(),
+            from,
+            tokenId,
+            _data
+        ), "ERC721: transfer to non ERC721Receiver implementer");
+        bytes4 retval = abi.decode(returndata, (bytes4));
+        return (retval == _ERC721_RECEIVED);
+    }
+
+    function _approve(identity to, uint256 tokenId) private {
+        _tokenApprovals[tokenId] = to;
+        emit Approval(ERC721.ownerOf(tokenId), to, tokenId); // internal owner
+    }
+
+    function _beforeTokenTransfer(identity from, identity to, uint256 tokenId) internal virtual { }
+}
+
+abstract contract ERC721Burnable is Context, ERC721 {
+    function burn(uint256 tokenId) public virtual {
+        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Burnable: caller is not owner nor approved");
+        _burn(tokenId);
+    }
+}
+
+abstract contract Pausable is Context {
+
+    event Paused(identity account);
+
+    event Unpaused(identity account);
+
+    bool private _paused;
+
+    constructor () internal {
+        _paused = false;
+    }
+
+    function paused() public view virtual returns (bool) {
+        return _paused;
+    }
+
+    modifier whenNotPaused() {
+        require(!paused(), "Pausable: paused");
+        _;
+    }
+
+    modifier whenPaused() {
+        require(paused(), "Pausable: not paused");
+        _;
+    }
+
+    function _pause() internal virtual whenNotPaused {
+        _paused = true;
+        emit Paused(_msgSender());
+    }
+
+    function _unpause() internal virtual whenPaused {
+        _paused = false;
+        emit Unpaused(_msgSender());
+    }
+}
+
+abstract contract ERC721Pausable is ERC721, Pausable {
+
+    function _beforeTokenTransfer(identity from, identity to, uint256 tokenId) internal virtual override {
+        super._beforeTokenTransfer(from, to, tokenId);
+
+        require(!paused(), "ERC721Pausable: token transfer while paused");
+    }
+}
+
+contract ERC721PresetMinterPauserAutoId is Context, AccessControl, ERC721Burnable, ERC721Pausable {
+    using Counters for Counters.Counter;
+
+    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
+    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
+
+    Counters.Counter private _tokenIdTracker;
+
+    /**
+     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
+     * account that deploys the contract.
+     *
+     * Token URIs will be autogenerated based on `baseURI` and their token IDs.
+     * See {ERC721-tokenURI}.
+     */
+    constructor(string memory name, string memory symbol, string memory baseURI) public ERC721(name, symbol) {
+        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
+
+        _setupRole(MINTER_ROLE, _msgSender());
+        _setupRole(PAUSER_ROLE, _msgSender());
+
+        _setBaseURI(baseURI);
+    }
+
+    /**
+     * @dev Creates a new token for `to`. Its token ID will be automatically
+     * assigned (and available on the emitted {IERC721-Transfer} event), and the token
+     * URI autogenerated based on the base URI passed at construction.
+     *
+     * See {ERC721-_mint}.
+     *
+     * Requirements:
+     *
+     * - the caller must have the `MINTER_ROLE`.
+     */
+    function mint(identity to) public virtual {
+        require(hasRole(MINTER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have minter role to mint");
+
+        // We cannot just use balanceOf to create the new tokenId because tokens
+        // can be burned (destroyed), so we need a separate counter.
+        _mint(to, _tokenIdTracker.current());
+        _tokenIdTracker.increment();
+    }
+
+    /**
+     * @dev Pauses all token transfers.
+     *
+     * See {ERC721Pausable} and {Pausable-_pause}.
+     *
+     * Requirements:
+     *
+     * - the caller must have the `PAUSER_ROLE`.
+     */
+    function pause() public virtual {
+        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to pause");
+        _pause();
+    }
+
+    /**
+     * @dev Unpauses all token transfers.
+     *
+     * See {ERC721Pausable} and {Pausable-_unpause}.
+     *
+     * Requirements:
+     *
+     * - the caller must have the `PAUSER_ROLE`.
+     */
+    function unpause() public virtual {
+        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to unpause");
+        _unpause();
+    }
+
+    function _beforeTokenTransfer(identity from, identity to, uint256 tokenId) internal virtual override(ERC721, ERC721Pausable) {
+        super._beforeTokenTransfer(from, to, tokenId);
+    }
+}

BIN
src/main/contract/alipay-solc-0.6.4.tgz


+ 1 - 8
src/main/java/com/izouma/nineth/event/CreateAssetEvent.java

@@ -1,25 +1,18 @@
 package com.izouma.nineth.event;
 
 import com.izouma.nineth.domain.Asset;
-import com.izouma.nineth.domain.Order;
 import org.springframework.context.ApplicationEvent;
 
 public class CreateAssetEvent extends ApplicationEvent {
     private final boolean success;
-    private final Order   order;
     private final Asset   asset;
 
-    public CreateAssetEvent(Object source, boolean success, Order order, Asset asset) {
+    public CreateAssetEvent(Object source, boolean success, Asset asset) {
         super(source);
         this.success = success;
-        this.order = order;
         this.asset = asset;
     }
 
-    public Order getOrder() {
-        return order;
-    }
-
     public Asset getAsset() {
         return asset;
     }

+ 77 - 88
src/main/java/com/izouma/nineth/service/AssetService.java

@@ -38,7 +38,6 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.context.ApplicationContext;
 import org.springframework.core.env.Environment;
 import org.springframework.data.domain.Page;
-import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.ui.Model;
 
@@ -73,71 +72,80 @@ public class AssetService {
         return assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery));
     }
 
-    @Async
-    public void createAsset(Order order) {
+    public Asset createAsset(Order order) {
         User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("用户不存在"));
-        if (StringUtils.isEmpty(user.getPublicKey())) {
-            NFTAccount account = nftService.createAccount(user.getUsername());
-            user.setNftAccount(account.getAccountId());
-            user.setKmsId(account.getAccountKmsId());
-            user.setPublicKey(account.getPublicKey());
-            userRepo.save(user);
-        }
-        try {
-            NFT nft = nftService.createToken(user.getNftAccount());
-            String ipfsUrl = ipfsUpload(order.getPic().get(0).getUrl());
-            if (nft != null) {
-                Asset asset = Asset.builder()
-                        .userId(user.getId())
-                        .orderId(order.getId())
-                        .collectionId(order.getCollectionId())
-                        .minter(order.getMinter())
-                        .minterId(order.getMinterId())
-                        .minterAvatar(order.getMinterAvatar())
-                        .name(order.getName())
-                        .detail(order.getDetail())
-                        .pic(order.getPic())
-                        .properties(order.getProperties())
-                        .category(order.getCategory())
-                        .canResale(order.isCanResale())
-                        .royalties(order.getRoyalties())
-                        .serviceCharge(order.getServiceCharge())
-                        .tokenId(nft.getTokenId())
-                        .blockNumber(nft.getBlockNumber())
-                        .txHash(nft.getTxHash())
-                        .gasUsed(nft.getGasUsed())
-                        .price(order.getPrice())
-                        .status(AssetStatus.NORMAL)
-                        .ipfsUrl(ipfsUrl)
-                        .number((int) (assetRepo.countByIpfsUrlAndStatusNot(ipfsUrl, AssetStatus.TRANSFERRED) + 1))
-                        .owner(user.getNickname())
-                        .ownerId(user.getId())
-                        .ownerAvatar(user.getAvatar())
-                        .build();
-                assetRepo.save(asset);
-                applicationContext.publishEvent(new CreateAssetEvent(this, true, order, asset));
-
-                tokenHistoryRepo.save(TokenHistory.builder()
-                        .tokenId(asset.getTokenId())
-                        .fromUser(order.getMinter())
-                        .fromUserId(order.getMinterId())
-                        .toUser(user.getNickname())
-                        .toUserId(user.getId())
-                        .operation("出售")
-                        .price(order.getPrice())
-                        .build());
-                return;
-            }
-        } catch (Exception e) {
-            log.error("创建token失败", e);
-        }
-        applicationContext.publishEvent(new CreateAssetEvent(this, false, order, null));
+        Asset asset = Asset.builder()
+                .userId(user.getId())
+                .orderId(order.getId())
+                .collectionId(order.getCollectionId())
+                .minter(order.getMinter())
+                .minterId(order.getMinterId())
+                .minterAvatar(order.getMinterAvatar())
+                .name(order.getName())
+                .detail(order.getDetail())
+                .pic(order.getPic())
+                .properties(order.getProperties())
+                .category(order.getCategory())
+                .canResale(order.isCanResale())
+                .royalties(order.getRoyalties())
+                .serviceCharge(order.getServiceCharge())
+                .price(order.getPrice())
+                .status(AssetStatus.NORMAL)
+                .owner(user.getNickname())
+                .ownerId(user.getId())
+                .ownerAvatar(user.getAvatar())
+                .build();
+        assetRepo.save(asset);
 
+        tokenHistoryRepo.save(TokenHistory.builder()
+                .tokenId(asset.getTokenId())
+                .fromUser(order.getMinter())
+                .fromUserId(order.getMinterId())
+                .toUser(user.getNickname())
+                .toUserId(user.getId())
+                .operation("出售")
+                .price(order.getPrice())
+                .build());
+
+        return asset;
     }
 
-    @Async
-    public void createAsset(Order order, BlindBoxItem winItem) {
+    public Asset createAsset(Order order, BlindBoxItem winItem) {
         User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("用户不存在"));
+        Asset asset = Asset.builder()
+                .userId(user.getId())
+                .orderId(order.getId())
+                .collectionId(order.getCollectionId())
+                .minter(winItem.getMinter())
+                .minterId(winItem.getMinterId())
+                .minterAvatar(winItem.getMinterAvatar())
+                .name(winItem.getName())
+                .detail(winItem.getDetail())
+                .pic(winItem.getPic())
+                .properties(winItem.getProperties())
+                .canResale(winItem.isCanResale())
+                .royalties(winItem.getRoyalties())
+                .serviceCharge(winItem.getServiceCharge())
+                .price(order.getPrice())
+                .status(AssetStatus.NORMAL)
+                .ipfsUrl(ipfsUpload(winItem.getPic().get(0).getUrl()))
+                .build();
+        assetRepo.save(asset);
+        tokenHistoryRepo.save(TokenHistory.builder()
+                .tokenId(asset.getTokenId())
+                .fromUser(order.getMinter())
+                .fromUserId(order.getMinterId())
+                .toUser(user.getNickname())
+                .toUserId(user.getId())
+                .operation("出售")
+                .price(order.getPrice())
+                .build());
+
+        return asset;
+    }
+
+    public void mint(Asset asset) {
+        User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
         if (StringUtils.isEmpty(user.getPublicKey())) {
             NFTAccount account = nftService.createAccount(user.getUsername());
             user.setNftAccount(account.getAccountId());
@@ -148,41 +156,22 @@ public class AssetService {
         try {
             NFT nft = nftService.createToken(user.getNftAccount());
             if (nft != null) {
-                Asset asset = Asset.builder()
-                        .userId(user.getId())
-                        .orderId(order.getId())
-                        .collectionId(order.getCollectionId())
-                        .minter(winItem.getMinter())
-                        .minterId(winItem.getMinterId())
-                        .minterAvatar(winItem.getMinterAvatar())
-                        .name(winItem.getName())
-                        .detail(winItem.getDetail())
-                        .pic(winItem.getPic())
-                        .properties(winItem.getProperties())
-                        .canResale(winItem.isCanResale())
-                        .royalties(winItem.getRoyalties())
-                        .serviceCharge(winItem.getServiceCharge())
-                        .tokenId(nft.getTokenId())
-                        .blockNumber(nft.getBlockNumber())
-                        .txHash(nft.getTxHash())
-                        .gasUsed(nft.getGasUsed())
-                        .price(order.getPrice())
-                        .status(AssetStatus.NORMAL)
-                        .ipfsUrl(ipfsUpload(winItem.getPic().get(0).getUrl()))
-                        .build();
+                asset.setTokenId(nft.getTokenId());
+                asset.setBlockNumber(nft.getBlockNumber());
+                asset.setTxHash(nft.getTxHash());
+                asset.setGasUsed(nft.getGasUsed());
+                asset.setIpfsUrl(ipfsUpload(asset.getPic().get(0).getUrl()));
                 assetRepo.save(asset);
-                applicationContext.publishEvent(new CreateAssetEvent(this, true, order, asset));
-                return;
             }
         } catch (Exception e) {
-            log.error("创建token失败", e);
+            e.printStackTrace();
         }
-        applicationContext.publishEvent(new CreateAssetEvent(this, false, order, null));
+        applicationContext.publishEvent(new CreateAssetEvent(this, false, asset));
     }
 
     public String ipfsUpload(String url) {
         try {
-            IPFS ipfs = new IPFS("121.40.132.44", 5001);
+            IPFS ipfs = new IPFS("112.74.34.84", 5001);
             HttpRequest request = HttpRequest.get(url);
             File file = File.createTempFile("ipfs", ".tmp");
             request.receive(file);

+ 53 - 1
src/main/java/com/izouma/nineth/service/NFTService.java

@@ -3,10 +3,13 @@ package com.izouma.nineth.service;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alipay.mychain.sdk.api.utils.Utils;
+import com.alipay.mychain.sdk.common.VMTypeEnum;
 import com.alipay.mychain.sdk.domain.transaction.LogEntry;
+import com.alipay.mychain.sdk.utils.ByteUtils;
 import com.antfinancial.mychain.baas.tool.restclient.RestClient;
 import com.antfinancial.mychain.baas.tool.restclient.RestClientProperties;
 import com.antfinancial.mychain.baas.tool.restclient.model.CallRestBizParam;
+import com.antfinancial.mychain.baas.tool.restclient.model.ClientParam;
 import com.antfinancial.mychain.baas.tool.restclient.model.Method;
 import com.antfinancial.mychain.baas.tool.restclient.model.ReceiptDecoration;
 import com.antfinancial.mychain.baas.tool.restclient.response.BaseResp;
@@ -113,7 +116,7 @@ public class NFTService {
                 .bizid(restClientProperties.getBizid())
                 .account(restClientProperties.getAccount())
                 .contractName(Constants.CONTRACT_NAME)
-                .methodSignature("safeTransferFrom(identity,identity,uint256)")
+                .methodSignature("transferFrom(identity,identity,uint256)")
                 .inputParamListStr(jsonArray.toJSONString())
                 .outTypes("[]")//合约返回值类型
                 .mykmsKeyId(restClientProperties.getKmsId())
@@ -147,4 +150,53 @@ public class NFTService {
         }
         throw new BusinessException("创建nft失败");
     }
+
+    public NFT setApprovalForAll(String account) throws Exception {
+        JSONArray jsonArray = new JSONArray();
+        jsonArray.add(Utils.getIdentityByName(account));
+        jsonArray.add(true);
+
+        CallRestBizParam callRestBizParam = CallRestBizParam.builder()
+                .orderId(String.valueOf(new SnowflakeIdWorker(0, 0).nextId()))
+                .bizid(restClientProperties.getBizid())
+                .account(restClientProperties.getAccount())
+                .contractName(Constants.CONTRACT_NAME)
+                .methodSignature("setApprovalForAll(identity,bool)")
+                .inputParamListStr(jsonArray.toJSONString())
+                .outTypes("[]")//合约返回值类型
+                .mykmsKeyId(restClientProperties.getKmsId())
+                .method(Method.CALLCONTRACTBIZASYNC)
+                .tenantid(restClientProperties.getTenantid())
+                .gas(500000L)
+                .build();
+        BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
+        if (!resp.isSuccess()) {
+            log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
+        }
+        if ("200".equals(resp.getCode())) {
+            log.info("EVM合约执行成功");
+            // 合约调用交易回执内容
+            ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
+            BigInteger gasUsed = txReceipt.getGasUsed();
+            long result = txReceipt.getResult();
+            log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
+        } else {
+            // 异步交易未成功需要根据状态码判断交易状态
+            log.error("EVM合约执行未成功: " + resp.getCode());
+        }
+        throw new BusinessException("创建nft失败");
+    }
+
+    public void deployContract() throws Exception {
+        ClientParam clientParam = restClient.createDeployContractTransaction(
+                restClientProperties.getDefaultAccount(),
+                "rest_sol_test_2ghd2g2fr",
+                ByteUtils.hexStringToBytes("6080604052606460005534801561001557600080fd5b50610128806100256000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631ab06ee514605857806360fe47b114608c5780636d4ce63c1460b6575b600080fd5b348015606357600080fd5b50608a600480360381019080803590602001909291908035906020019092919050505060de565b005b348015609757600080fd5b5060b46004803603810190808035906020019092919050505060e9565b005b34801560c157600080fd5b5060c860f3565b6040518082815260200191505060405180910390f35b816000819055505050565b8060008190555050565b600080549050905600a165627a7a723058205bcd66c88d325808b2a6429cba1e79b49c8a6cfa4190c9158d4b63a9030ea1d70029"),
+                VMTypeEnum.EVM,
+                50000L);
+        BaseResp resp = restClient.chainCall(
+                clientParam.getHash(),
+                clientParam.getSignData(),
+                Method.DEPLOYCONTRACT);
+    }
 }

+ 16 - 21
src/main/java/com/izouma/nineth/service/OrderService.java

@@ -207,10 +207,11 @@ public class OrderService {
 
     }
 
-    public void notifyAlipay(Long orderId, PayMethod payMethod, String transactionId) {
+    public void notifyOrder(Long orderId, PayMethod payMethod, String transactionId) {
         Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
         Collection collection = collectionRepo.findById(order.getCollectionId())
                 .orElseThrow(new BusinessException("藏品不存在"));
+        User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("用户不存在"));
         if (order.getStatus() == OrderStatus.NOT_PAID) {
             if (order.getType() == CollectionType.BLIND_BOX) {
                 List<BlindBoxItem> items = blindBoxItemRepo.findByBlindBoxId(order.getCollectionId());
@@ -268,26 +269,21 @@ public class OrderService {
                 order.setTransactionId(transactionId);
                 order.setPayMethod(payMethod);
                 orderRepo.save(order);
-                assetService.createAsset(order, winItem);
-
-
+                Asset asset = assetService.createAsset(order, winItem);
+                assetService.mint(asset);
             } else {
-                order.setStatus(OrderStatus.PROCESSING);
-                order.setPayTime(LocalDateTime.now());
-                order.setTransactionId(transactionId);
-                order.setPayMethod(payMethod);
-                orderRepo.save(order);
-                assetService.createAsset(order);
-            }
-
-            if (collection.getSource() == CollectionSource.TRANSFER) {
-                Asset asset = assetRepo.findById(collection.getAssetId()).orElse(null);
-                if (asset != null) {
-                    asset.setStatus(AssetStatus.TRANSFERRED);
-                    asset.setPublicShow(false);
-                    asset.setPublicCollectionId(null);
-                    assetRepo.save(asset);
+                if (collection.getSource() == CollectionSource.TRANSFER) {
+                    Asset asset = assetRepo.findById(collection.getAssetId()).orElse(null);
+                    assetService.transfer(asset, user);
                     collectionRepo.delete(collection);
+                } else {
+                    order.setStatus(OrderStatus.PROCESSING);
+                    order.setPayTime(LocalDateTime.now());
+                    order.setTransactionId(transactionId);
+                    order.setPayMethod(payMethod);
+                    orderRepo.save(order);
+                    Asset asset = assetService.createAsset(order);
+                    assetService.mint(asset);
                 }
             }
         } else if (order.getStatus() == OrderStatus.CANCELLED) {
@@ -305,9 +301,8 @@ public class OrderService {
 
     @EventListener
     public void onCreateAsset(CreateAssetEvent event) {
-        Order order = event.getOrder();
         Asset asset = event.getAsset();
-
+        Order order = orderRepo.findById(asset.getOrderId()).orElseThrow(new BusinessException("订单不存在"));
         if (event.isSuccess()) {
             order.setTxHash(asset.getTxHash());
             order.setGasUsed(asset.getGasUsed());

+ 2 - 2
src/main/java/com/izouma/nineth/web/OrderNotifyController.java

@@ -69,7 +69,7 @@ public class OrderNotifyController {
             switch (action) {
                 case "payOrder": {
                     Long orderId = body.getLong("orderId");
-                    orderService.notifyAlipay(orderId, PayMethod.ALIPAY, MapUtils.getString(params, "trade_no"));
+                    orderService.notifyOrder(orderId, PayMethod.ALIPAY, MapUtils.getString(params, "trade_no"));
                     break;
                 }
                 case "payGiftOrder": {
@@ -94,7 +94,7 @@ public class OrderNotifyController {
         switch (action) {
             case "payOrder": {
                 Long orderId = attach.getLong("orderId");
-                orderService.notifyAlipay(orderId, PayMethod.WEIXIN, notifyResult.getTransactionId());
+                orderService.notifyOrder(orderId, PayMethod.WEIXIN, notifyResult.getTransactionId());
                 break;
             }
             case "payGiftOrder": {

+ 6 - 3
src/test/java/com/izouma/nineth/service/NFTServiceTest.java

@@ -5,8 +5,6 @@ import org.apache.commons.lang3.RandomStringUtils;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
-import static org.junit.Assert.*;
-
 public class NFTServiceTest extends ApplicationTests {
     @Autowired
     private NFTService nftService;
@@ -27,7 +25,12 @@ public class NFTServiceTest extends ApplicationTests {
 
     @Test
     public void transferToken() throws Exception {
-        nftService.transferToken("0000000000000000000000000000000000000000000000000000000000000097",
+        nftService.transferToken("000000000000000000000000000000000000000000000000000000000000009d",
                 "9th_BHlKkGWw", "9th_test");
     }
+
+    @Test
+    public void setApprovalForAll() throws Exception {
+        nftService.setApprovalForAll("9th_test");
+    }
 }