pragma solidity ^0.4.24;
interface ERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
/// Note: the ERC-165 identifier for this interface is 0x5b5e139f
interface ERC721Metadata /* is ERC721 */ {
/// @notice A descriptive name for a collection of NFTs in this contract
function name() external pure returns (string _name);
/// @notice An abbreviated name for NFTs in this contract
function symbol() external pure returns (string _symbol);
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
/// 3986. The URI may point to a JSON file that conforms to the "ERC721
/// Metadata JSON Schema".
function tokenURI(uint256 _tokenId) external view returns (string);
}
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
/// Note: the ERC-165 identifier for this interface is 0x80ac58cd
interface ERC721 /* is ERC165 */ {
/// @dev This emits when ownership of any NFT changes by any mechanism.
/// This event emits when NFTs are created (`from` == 0) and destroyed
/// (`to` == 0). Exception: during contract creation, any number of NFTs
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
/// @dev This emits when the approved address for an NFT is changed or
/// reaffirmed. The zero address indicates there is no approved address.
/// When a Transfer event emits, this also indicates that the approved
/// address for that NFT (if any) is reset to none.
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
/// @dev This emits when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param _owner An address for whom to query the balance
/// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(address _owner) external view returns (uint256);
/// @notice Find the owner of an NFT
/// @dev NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param _tokenId The identifier for an NFT
/// @return The address of the owner of the NFT
function ownerOf(uint256 _tokenId) external view returns (address);
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to ""
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Set or reaffirm the approved address for an NFT
/// @dev The zero address indicates there is no approved address.
/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
/// operator of the current owner.
/// @param _approved The new approved NFT controller
/// @param _tokenId The NFT to approve
function approve(address _approved, uint256 _tokenId) external payable;
/// @notice Enable or disable approval for a third party ("operator") to manage
/// all of `msg.sender`'s assets.
/// @dev Emits the ApprovalForAll event. The contract MUST allow
/// multiple operators per owner.
/// @param _operator Address to add to the set of authorized operators.
/// @param _approved True if the operator is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved) external;
/// @notice Get the approved address for a single NFT
/// @dev Throws if `_tokenId` is not a valid NFT
/// @param _tokenId The NFT to find the approved address for
/// @return The approved address for this NFT, or the zero address if there is none
function getApproved(uint256 _tokenId) external view returns (address);
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
contract ERC721Receiver {
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a `safetransfer`. This function MAY throw to revert and reject the
* transfer. This function MUST use 50,000 gas or less. Return of other
* than the magic value MUST result in the transaction being reverted.
* Note: the contract address is always the message sender.
* @param _from The sending address
* @param _tokenId The NFT identifier which is being transfered
* @param _data Additional data with no specified format
* @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
*/
function onERC721Received(address _from, uint256 _tokenId, bytes _data) external returns(bytes4);
}
/**
* Owned contract
*/
contract Owned {
address public owner;
address public newOwner;
event OwnershipTransferred(address indexed from, address indexed to);
/**
* Constructor
*/
constructor() public {
owner = msg.sender;
}
/**
* @dev Only the owner of contract
*/
modifier onlyOwner {
require(msg.sender == owner);
_;
}
/**
* @dev transfer the ownership to other
* - Only the owner can operate
*/
function transferOwnership(address _newOwner) public onlyOwner {
newOwner = _newOwner;
}
/**
* @dev Accept the ownership from last owner
*/
function acceptOwnership() public {
require(msg.sender == newOwner);
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
newOwner = address(0);
}
}
contract TRNData is Owned {
TripioRoomNightData dataSource;
/**
* Only the valid vendor and the vendor is valid
*/
modifier onlyVendor {
uint256 vendorId = dataSource.vendorIds(msg.sender);
require(vendorId > 0);
(,,,bool valid) = dataSource.getVendor(vendorId);
require(valid);
_;
}
/**
* The vendor is valid
*/
modifier vendorValid(address _vendor) {
uint256 vendorId = dataSource.vendorIds(_vendor);
require(vendorId > 0);
(,,,bool valid) = dataSource.getVendor(vendorId);
require(valid);
_;
}
/**
* The vendorId is valid
*/
modifier vendorIdValid(uint256 _vendorId) {
(,,,bool valid) = dataSource.getVendor(_vendorId);
require(valid);
_;
}
/**
* Rate plan exist.
*/
modifier ratePlanExist(uint256 _vendorId, uint256 _rpid) {
(,,,bool valid) = dataSource.getVendor(_vendorId);
require(valid);
require(dataSource.ratePlanIsExist(_vendorId, _rpid));
_;
}
/**
* Token is valid
*/
modifier validToken(uint256 _tokenId) {
require(_tokenId > 0);
require(dataSource.roomNightIndexToOwner(_tokenId) != address(0));
_;
}
/**
* Tokens are valid
*/
modifier validTokenInBatch(uint256[] _tokenIds) {
for(uint256 i = 0; i < _tokenIds.length; i++) {
require(_tokenIds[i] > 0);
require(dataSource.roomNightIndexToOwner(_tokenIds[i]) != address(0));
}
_;
}
/**
* Whether the `_tokenId` can be transfered
*/
modifier canTransfer(uint256 _tokenId) {
address owner = dataSource.roomNightIndexToOwner(_tokenId);
bool isOwner = (msg.sender == owner);
bool isApproval = (msg.sender == dataSource.roomNightApprovals(_tokenId));
bool isOperator = (dataSource.operatorApprovals(owner, msg.sender));
require(isOwner || isApproval || isOperator);
_;
}
/**
* Whether the `_tokenIds` can be transfered
*/
modifier canTransferInBatch(uint256[] _tokenIds) {
for(uint256 i = 0; i < _tokenIds.length; i++) {
address owner = dataSource.roomNightIndexToOwner(_tokenIds[i]);
bool isOwner = (msg.sender == owner);
bool isApproval = (msg.sender == dataSource.roomNightApprovals(_tokenIds[i]));
bool isOperator = (dataSource.operatorApprovals(owner, msg.sender));
require(isOwner || isApproval || isOperator);
}
_;
}
/**
* Whether the `_tokenId` can be operated by `msg.sender`
*/
modifier canOperate(uint256 _tokenId) {
address owner = dataSource.roomNightIndexToOwner(_tokenId);
bool isOwner = (msg.sender == owner);
bool isOperator = (dataSource.operatorApprovals(owner, msg.sender));
require(isOwner || isOperator);
_;
}
/**
* Whether the `_date` is valid(no hours, no seconds)
*/
modifier validDate(uint256 _date) {
require(_date > 0);
require(dateIsLegal(_date));
_;
}
/**
* Whether the `_dates` are valid(no hours, no seconds)
*/
modifier validDates(uint256[] _dates) {
for(uint256 i = 0;i < _dates.length; i++) {
require(_dates[i] > 0);
require(dateIsLegal(_dates[i]));
}
_;
}
function dateIsLegal(uint256 _date) pure private returns(bool) {
uint256 year = _date / 10000;
uint256 mon = _date / 100 - year * 100;
uint256 day = _date - mon * 100 - year * 10000;
if(year < 1970 || mon <= 0 || mon > 12 || day <= 0 || day > 31)
return false;
if(4 == mon || 6 == mon || 9 == mon || 11 == mon){
if (day == 31) {
return false;
}
}
if(((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
if(2 == mon && day > 29) {
return false;
}
}else {
if(2 == mon && day > 28){
return false;
}
}
return true;
}
/**
* Constructor
*/
constructor() public {
}
}
contract TRNOwners is TRNData {
/**
* Constructor
*/
constructor() public {
}
/**
* Add room night token to `_owner`'s account(from the header)
*/
function _pushRoomNight(address _owner, uint256 _rnid, bool _isVendor) internal {
require(_owner != address(0));
require(_rnid != 0);
if (_isVendor) {
dataSource.pushOrderOfVendor(_owner, _rnid, false);
} else {
dataSource.pushOrderOfOwner(_owner, _rnid, false);
}
}
/**
* Remove room night token from `_owner`'s account
*/
function _removeRoomNight(address _owner, uint256 _rnid) internal {
dataSource.removeOrderOfOwner(_owner, _rnid);
}
/**
* @dev Returns all the room nights of the `msg.sender`(Customer)
* @param _from The begin of room nights Id
* @param _limit The total room nights
* @param _isVendor Is Vendor
* @return Room nights of the `msg.sender` and the next vernier
*/
function roomNightsOfOwner(uint256 _from, uint256 _limit, bool _isVendor)
external
view
returns(uint256[], uint256) {
if(_isVendor) {
return dataSource.getOrdersOfVendor(msg.sender, _from, _limit, true);
}else {
return dataSource.getOrdersOfOwner(msg.sender, _from, _limit, true);
}
}
/**
* @dev Returns the room night infomation in detail
* @param _rnid Room night id
* @return Room night infomation in detail
*/
function roomNight(uint256 _rnid)
external
view
returns(uint256 _vendorId,uint256 _rpid,uint256 _token,uint256 _price,uint256 _timestamp,uint256 _date,bytes32 _ipfs, string _name) {
(_vendorId, _rpid, _token, _price, _timestamp, _date, _ipfs) = dataSource.roomnights(_rnid);
(_name,,) = dataSource.getRatePlan(_vendorId, _rpid);
}
}
library IPFSLib {
bytes constant ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
bytes constant HEX = "0123456789abcdef";
/**
* @dev Base58 encoding
* @param _source Bytes data
* @return Encoded bytes data
*/
function base58Address(bytes _source) internal pure returns (bytes) {
uint8[] memory digits = new uint8[](_source.length * 136/100 + 1);
digits[0] = 0;
uint8 digitlength = 1;
for (uint i = 0; i < _source.length; ++i) {
uint carry = uint8(_source[i]);
for (uint j = 0; j<digitlength; ++j) {
carry += uint(digits[j]) * 256;
digits[j] = uint8(carry % 58);
carry = carry / 58;
}
while (carry > 0) {
digits[digitlength] = uint8(carry % 58);
digitlength++;
carry = carry / 58;
}
}
return toAlphabet(reverse(truncate(digits, digitlength)));
}
/**
* @dev Hex encoding, convert bytes32 data to hex string
* @param _source Bytes32 data
* @return hex string bytes
*/
function hexAddress(bytes32 _source) internal pure returns(bytes) {
uint256 value = uint256(_source);
bytes memory result = "0000000000000000000000000000000000000000000000000000000000000000";
uint8 index = 0;
while(value > 0) {
result[index] = HEX[value & 0xf];
index++;
value = value>>4;
}
bytes memory ipfsBytes = reverseBytes(result);
return ipfsBytes;
}
/**
* @dev Truncate `_array` by `_length`
* @param _array The source array
* @param _length The target length of the `_array`
* @return The truncated array
*/
function truncate(uint8[] _array, uint8 _length) internal pure returns (uint8[]) {
uint8[] memory output = new uint8[](_length);
for (uint i = 0; i < _length; i++) {
output[i] = _array[i];
}
return output;
}
/**
* @dev Reverse `_input` array
* @param _input The source array
* @return The reversed array
*/
function reverse(uint8[] _input) internal pure returns (uint8[]) {
uint8[] memory output = new uint8[](_input.length);
for (uint i = 0; i < _input.length; i++) {
output[i] = _input[_input.length - 1 - i];
}
return output;
}
/**
* @dev Reverse `_input` bytes
* @param _input The source bytes
* @return The reversed bytes
*/
function reverseBytes(bytes _input) private pure returns (bytes) {
bytes memory output = new bytes(_input.length);
for (uint8 i = 0; i < _input.length; i++) {
output[i] = _input[_input.length-1-i];
}
return output;
}
/**
* @dev Convert the indices to alphabet
* @param _indices The indices of alphabet
* @return The alphabets
*/
function toAlphabet(uint8[] _indices) internal pure returns (bytes) {
bytes memory output = new bytes(_indices.length);
for (uint i = 0; i < _indices.length; i++) {
output[i] = ALPHABET[_indices[i]];
}
return output;
}
/**
* @dev Convert bytes32 to bytes
* @param _input The source bytes32
* @return The bytes
*/
function toBytes(bytes32 _input) internal pure returns (bytes) {
bytes memory output = new bytes(32);
for (uint8 i = 0; i < 32; i++) {
output[i] = _input[i];
}
return output;
}
/**
* @dev Concat two bytes to one
* @param _byteArray The first bytes
* @param _byteArray2 The second bytes
* @return The concated bytes
*/
function concat(bytes _byteArray, bytes _byteArray2) internal pure returns (bytes) {
bytes memory returnArray = new bytes(_byteArray.length + _byteArray2.length);
for (uint16 i = 0; i < _byteArray.length; i++) {
returnArray[i] = _byteArray[i];
}
for (i; i < (_byteArray.length + _byteArray2.length); i++) {
returnArray[i] = _byteArray2[i - _byteArray.length];
}
return returnArray;
}
}
contract TRNAsset is TRNData, ERC721Metadata {
using IPFSLib for bytes;
using IPFSLib for bytes32;
/**
* Constructor
*/
constructor() public {
}
/**
* @dev Descriptive name for Tripio's Room Night Token in this contract
* @return The name of the contract
*/
function name() external pure returns (string _name) {
return "Tripio Room Night";
}
/**
* @dev Abbreviated name for Tripio's Room Night Token in this contract
* @return The simple name of the contract
*/
function symbol() external pure returns (string _symbol) {
return "TRN";
}
/**
* @dev If `_tokenId` is not valid trows an exception otherwise return a URI which point to a JSON file like:
* {
* "name": "Identifies the asset to which this NFT represents",
* "description": "Describes the asset to which this NFT represents",
* "image": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
* }
* @param _tokenId The RoomNight digital token
* @return The digital token asset uri
*/
function tokenURI(uint256 _tokenId)
external
view
validToken(_tokenId)
returns (string) {
bytes memory prefix = new bytes(2);
prefix[0] = 0x12;
prefix[1] = 0x20;
(,,,,,,bytes32 ipfs) = dataSource.roomnights(_tokenId);
bytes memory value = prefix.concat(ipfs.toBytes());
bytes memory ipfsBytes = value.base58Address();
bytes memory tokenBaseURIBytes = bytes(dataSource.tokenBaseURI());
return string(tokenBaseURIBytes.concat(ipfsBytes));
}
}
contract TRNOwnership is TRNOwners, ERC721 {
/**
* Constructor
*/
constructor() public {
}
/**
* This emits when ownership of any TRN changes by any mechanism.
*/
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
/**
* This emits when the approved address for an RTN is changed or reaffirmed.
*/
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
/**
* This emits when an operator is enabled or disabled for an owner.
* The operator can manage all RTNs of the owner.
*/
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/**
* @dev Transfer the `_tokenId` to `_to` directly
* @param _tokenId The room night token
* @param _to The target owner
*/
function _transfer(uint256 _tokenId, address _to) private {
// Find the FROM address
address from = dataSource.roomNightIndexToOwner(_tokenId);
// Remove room night from the `from`
_removeRoomNight(from, _tokenId);
// Add room night to the `_to`
_pushRoomNight(_to, _tokenId, false);
// Change the owner of `_tokenId`
// Remove approval of `_tokenId`
dataSource.transferTokenTo(_tokenId, _to);
// Emit Transfer event
emit Transfer(from, _to, _tokenId);
}
function _safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data)
private
validToken(_tokenId)
canTransfer(_tokenId) {
// The token's owner is equal to `_from`
address owner = dataSource.roomNightIndexToOwner(_tokenId);
require(owner == _from);
// Avoid `_to` is equal to address(0)
require(_to != address(0));
_transfer(_tokenId, _to);
uint256 codeSize;
assembly { codeSize := extcodesize(_to) }
if (codeSize == 0) {
return;
}
bytes4 retval = ERC721Receiver(_to).onERC721Received(_from, _tokenId, _data);
require (retval == dataSource.ERC721_RECEIVED());
}
/**
* @dev Count all TRNs assigned to an owner.
* Throw when `_owner` is equal to address(0)
* @param _owner An address for whom to query the balance.
* @return The number of TRNs owned by `_owner`.
*/
function balanceOf(address _owner) external view returns (uint256) {
require(_owner != address(0));
return dataSource.balanceOf(_owner);
}
/**
* @dev Find the owner of an TRN
* Throw unless `_tokenId` more than zero
* @param _tokenId The identifier for an TRN
* @return The address of the owner of the TRN
*/
function ownerOf(uint256 _tokenId) external view returns (address) {
require(_tokenId > 0);
return dataSource.roomNightIndexToOwner(_tokenId);
}
/**
* @dev Transfers the ownership of an TRN from one address to another address.
* Throws unless `msg.sender` is the current owner or an approved address for this TRN.
* Throws if `_tokenId` is not a valid TRN. When transfer is complete, this function checks if
* `_to` is a smart contract (code size > 0). If so, it calls `onERC721Received` on `_to` and
* throws if the return value is not `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`.
* @param _from The current owner of the TRN
* @param _to The new owner
* @param _tokenId The TRN to transfer
* @param _data Additional data with no specified format, sent in call to `_to`
*/
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) external payable {
_safeTransferFrom(_from, _to, _tokenId, _data);
}
/**
* @dev Same like safeTransferFrom with an extra data parameter, except this function just sets data to ""(empty)
* @param _from The current owner of the TRN
* @param _to The new owner
* @param _tokenId The TRN to transfer
*/
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable {
_safeTransferFrom(_from, _to, _tokenId, "");
}
/**
* @dev Transfers the ownership of an TRN from one address to another address.
* Throws unless `msg.sender` is the current owner or an approved address for this TRN.
* Throws if `_tokenId` is not a valid TRN.
* @param _from The current owner of the TRN
* @param _to The new owner
* @param _tokenId The TRN to transfer
*/
function transferFrom(address _from, address _to, uint256 _tokenId)
external
payable
validToken(_tokenId)
canTransfer(_tokenId) {
// The token's owner is equal to `_from`
address owner = dataSource.roomNightIndexToOwner(_tokenId);
require(owner == _from);
// Avoid `_to` is equal to address(0)
require(_to != address(0));
_transfer(_tokenId, _to);
}
/**
* @dev Transfers the ownership of TRNs from one address to another address.
* Throws unless `msg.sender` is the current owner or an approved address for this TRN.
* Throws if `_tokenIds` are not valid TRNs.
* @param _from The current owner of the TRN
* @param _to The new owner
* @param _tokenIds The TRNs to transfer
*/
function transferFromInBatch(address _from, address _to, uint256[] _tokenIds)
external
payable
validTokenInBatch(_tokenIds)
canTransferInBatch(_tokenIds) {
for(uint256 i = 0; i < _tokenIds.length; i++) {
// The token's owner is equal to `_from`
address owner = dataSource.roomNightIndexToOwner(_tokenIds[i]);
require(owner == _from);
// Avoid `_to` is equal to address(0)
require(_to != address(0));
_transfer(_tokenIds[i], _to);
}
}
/**
* @dev Set or reaffirm the approved address for an TRN.
* Throws unless `msg.sender` is the current TRN owner, or an authorized
* @param _approved The new approved TRN controller
* @param _tokenId The TRN to approve
*/
function approve(address _approved, uint256 _tokenId)
external
payable
validToken(_tokenId)
canOperate(_tokenId) {
address owner = dataSource.roomNightIndexToOwner(_tokenId);
dataSource.approveTokenTo(_tokenId, _approved);
emit Approval(owner, _approved, _tokenId);
}
/**
* @dev Enable or disable approval for a third party ("operator") to manage
* all of `msg.sender`'s assets.
* Emits the ApprovalForAll event.
* @param _operator Address to add to the set of authorized operators.
* @param _approved True if the operator is approved, false to revoke approval.
*/
function setApprovalForAll(address _operator, bool _approved) external {
require(_operator != address(0));
dataSource.approveOperatorTo(_operator, msg.sender, _approved);
emit ApprovalForAll(msg.sender, _operator, _approved);
}
/**
* @dev Get the approved address for a single TRN.
* Throws if `_tokenId` is not a valid TRN.
* @param _tokenId The TRN to find the approved address for
* @return The approved address for this TRN, or the zero address if there is none
*/
function getApproved(uint256 _tokenId)
external
view
validToken(_tokenId)
returns (address) {
return dataSource.roomNightApprovals(_tokenId);
}
/**
* @dev Query if an address is an authorized operator for another address.
* @param _owner The address that owns The TRNs
* @param _operator The address that acts on behalf of the owner
* @return True if `_operator` is an approved operator for `_owner`, false otherwise
*/
function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
return dataSource.operatorApprovals(_owner, _operator);
}
}
contract TRNSupportsInterface is TRNData, ERC165 {
/**
* Constructor
*/
constructor() public {
}
/**
* @dev Query if a contract implements an interface
* @param interfaceID The interface identifier, as specified in ERC-165
* @return true if the contract implements `interfaceID`
* and `interfaceID` is not 0xffffffff, false otherwise
*/
function supportsInterface(bytes4 interfaceID)
external
view
returns (bool) {
return ((interfaceID == dataSource.interfaceSignature_ERC165()) ||
(interfaceID == dataSource.interfaceSignature_ERC721Metadata()) ||
(interfaceID == dataSource.interfaceSignature_ERC721())) &&
(interfaceID != 0xffffffff);
}
}
/**
* This utility library was forked from https://github.com/o0ragman0o/LibCLL
*/
library LinkedListLib {
uint256 constant NULL = 0;
uint256 constant HEAD = 0;
bool constant PREV = false;
bool constant NEXT = true;
struct LinkedList {
mapping (uint256 => mapping (bool => uint256)) list;
uint256 length;
uint256 index;
}
/**
* @dev returns true if the list exists
* @param self stored linked list from contract
*/
function listExists(LinkedList storage self)
internal
view returns (bool) {
return self.length > 0;
}
/**
* @dev returns true if the node exists
* @param self stored linked list from contract
* @param _node a node to search for
*/
function nodeExists(LinkedList storage self, uint256 _node)
internal
view returns (bool) {
if (self.list[_node][PREV] == HEAD && self.list[_node][NEXT] == HEAD) {
if (self.list[HEAD][NEXT] == _node) {
return true;
} else {
return false;
}
} else {
return true;
}
}
/**
* @dev Returns the number of elements in the list
* @param self stored linked list from contract
*/
function sizeOf(LinkedList storage self)
internal
view
returns (uint256 numElements) {
return self.length;
}
/**
* @dev Returns the links of a node as a tuple
* @param self stored linked list from contract
* @param _node id of the node to get
*/
function getNode(LinkedList storage self, uint256 _node)
public
view
returns (bool, uint256, uint256) {
if (!nodeExists(self,_node)) {
return (false, 0, 0);
} else {
return (true, self.list[_node][PREV], self.list[_node][NEXT]);
}
}
/**
* @dev Returns the link of a node `_node` in direction `_direction`.
* @param self stored linked list from contract
* @param _node id of the node to step from
* @param _direction direction to step in
*/
function getAdjacent(LinkedList storage self, uint256 _node, bool _direction)
public
view
returns (bool, uint256) {
if (!nodeExists(self,_node)) {
return (false,0);
} else {
return (true,self.list[_node][_direction]);
}
}
/**
* @dev Can be used before `insert` to build an ordered list
* @param self stored linked list from contract
* @param _node an existing node to search from, e.g. HEAD.
* @param _value value to seek
* @param _direction direction to seek in
* @return next first node beyond '_node' in direction `_direction`
*/
function getSortedSpot(LinkedList storage self, uint256 _node, uint256 _value, bool _direction)
public
view
returns (uint256) {
if (sizeOf(self) == 0) {
return 0;
}
require((_node == 0) || nodeExists(self,_node));
bool exists;
uint256 next;
(exists,next) = getAdjacent(self, _node, _direction);
while ((next != 0) && (_value != next) && ((_value < next) != _direction)) next = self.list[next][_direction];
return next;
}
/**
* @dev Creates a bidirectional link between two nodes on direction `_direction`
* @param self stored linked list from contract
* @param _node first node for linking
* @param _link node to link to in the _direction
*/
function createLink(LinkedList storage self, uint256 _node, uint256 _link, bool _direction)
private {
self.list[_link][!_direction] = _node;
self.list[_node][_direction] = _link;
}
/**
* @dev Insert node `_new` beside existing node `_node` in direction `_direction`.
* @param self stored linked list from contract
* @param _node existing node
* @param _new new node to insert
* @param _direction direction to insert node in
*/
function insert(LinkedList storage self, uint256 _node, uint256 _new, bool _direction)
internal
returns (bool) {
if(!nodeExists(self,_new) && nodeExists(self,_node)) {
uint256 c = self.list[_node][_direction];
createLink(self, _node, _new, _direction);
createLink(self, _new, c, _direction);
self.length++;
return true;
} else {
return false;
}
}
/**
* @dev removes an entry from the linked list
* @param self stored linked list from contract
* @param _node node to remove from the list
*/
function remove(LinkedList storage self, uint256 _node)
internal
returns (uint256) {
if ((_node == NULL) || (!nodeExists(self,_node))) {
return 0;
}
createLink(self, self.list[_node][PREV], self.list[_node][NEXT], NEXT);
delete self.list[_node][PREV];
delete self.list[_node][NEXT];
self.length--;
return _node;
}
/**
* @dev pushes an enrty to the head of the linked list
* @param self stored linked list from contract
* @param _index The node Id
* @param _direction push to the head (NEXT) or tail (PREV)
*/
function add(LinkedList storage self, uint256 _index, bool _direction)
internal
returns (uint256) {
insert(self, HEAD, _index, _direction);
return self.index;
}
/**
* @dev pushes an enrty to the head of the linked list
* @param self stored linked list from contract
* @param _direction push to the head (NEXT) or tail (PREV)
*/
function push(LinkedList storage self, bool _direction)
internal
returns (uint256) {
self.index++;
insert(self, HEAD, self.index, _direction);
return self.index;
}
/**
* @dev pops the first entry from the linked list
* @param self stored linked list from contract
* @param _direction pop from the head (NEXT) or the tail (PREV)
*/
function pop(LinkedList storage self, bool _direction)
internal
returns (uint256) {
bool exists;
uint256 adj;
(exists,adj) = getAdjacent(self, HEAD, _direction);
return remove(self, adj);
}
}
contract TripioToken {
string public name;
string public symbol;
uint8 public decimals;
function transfer(address _to, uint256 _value) public returns (bool);
function balanceOf(address who) public view returns (uint256);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool);
}
contract TripioRoomNightData is Owned {
using LinkedListLib for LinkedListLib.LinkedList;
// Interface signature of erc165.
// bytes4(keccak256("supportsInterface(bytes4)"))
bytes4 constant public interfaceSignature_ERC165 = 0x01ffc9a7;
// Interface signature of erc721 metadata.
// bytes4(keccak256("name()")) ^ bytes4(keccak256("symbol()")) ^ bytes4(keccak256("tokenURI(uint256)"));
bytes4 constant public interfaceSignature_ERC721Metadata = 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd;
// Interface signature of erc721.
// bytes4(keccak256("balanceOf(address)")) ^
// bytes4(keccak256("ownerOf(uint256)")) ^
// bytes4(keccak256("safeTransferFrom(address,address,uint256,bytes)")) ^
// bytes4(keccak256("safeTransferFrom(address,address,uint256)")) ^
// bytes4(keccak256("transferFrom(address,address,uint256)")) ^
// bytes4(keccak256("approve(address,uint256)")) ^
// bytes4(keccak256("setApprovalForAll(address,bool)")) ^
// bytes4(keccak256("getApproved(uint256)")) ^
// bytes4(keccak256("isApprovedForAll(address,address)"));
bytes4 constant public interfaceSignature_ERC721 = 0x70a08231 ^ 0x6352211e ^ 0xb88d4fde ^ 0x42842e0e ^ 0x23b872dd ^ 0x095ea7b3 ^ 0xa22cb465 ^ 0x081812fc ^ 0xe985e9c5;
// Base URI of token asset
string public tokenBaseURI;
// Authorized contracts
struct AuthorizedContract {
string name;
address acontract;
}
mapping (address=>uint256) public authorizedContractIds;
mapping (uint256 => AuthorizedContract) public authorizedContracts;
LinkedListLib.LinkedList public authorizedContractList = LinkedListLib.LinkedList(0, 0);
// Rate plan prices
struct Price {
uint16 inventory; // Rate plan inventory
bool init; // Whether the price is initied
mapping (uint256 => uint256) tokens;
}
// Vendor hotel RPs
struct RatePlan {
string name; // Name of rate plan.
uint256 timestamp; // Create timestamp.
bytes32 ipfs; // The address of rate plan detail on IPFS.
Price basePrice; // The base price of rate plan
mapping (uint256 => Price) prices; // date -> Price
}
// Vendors
struct Vendor {
string name; // Name of vendor.
address vendor; // Address of vendor.
uint256 timestamp; // Create timestamp.
bool valid; // Whether the vendor is valid(default is true)
LinkedListLib.LinkedList ratePlanList;
mapping (uint256=>RatePlan) ratePlans;
}
mapping (address => uint256) public vendorIds;
mapping (uint256 => Vendor) vendors;
LinkedListLib.LinkedList public vendorList = LinkedListLib.LinkedList(0, 0);
// Supported digital currencies
mapping (uint256 => address) public tokenIndexToAddress;
LinkedListLib.LinkedList public tokenList = LinkedListLib.LinkedList(0, 0);
// RoomNight tokens
struct RoomNight {
uint256 vendorId;
uint256 rpid;
uint256 token; // The digital currency token
uint256 price; // The digital currency price
uint256 timestamp; // Create timestamp.
uint256 date; // The checkin date
bytes32 ipfs; // The address of rate plan detail on IPFS.
}
RoomNight[] public roomnights;
// rnid -> owner
mapping (uint256 => address) public roomNightIndexToOwner;
// Owner Account
mapping (address => LinkedListLib.LinkedList) public roomNightOwners;
// Vendor Account
mapping (address => LinkedListLib.LinkedList) public roomNightVendors;
// The authorized address for each TRN
mapping (uint256 => address) public roomNightApprovals;
// The authorized operators for each address
mapping (address => mapping (address => bool)) public operatorApprovals;
// The applications of room night redund
mapping (address => mapping (uint256 => bool)) public refundApplications;
// The signature of `onERC721Received(address,uint256,bytes)`
// bytes4(keccak256("onERC721Received(address,uint256,bytes)"));
bytes4 constant public ERC721_RECEIVED = 0xf0b9e5ba;
/**
* This emits when contract authorized
*/
event ContractAuthorized(address _contract);
/**
* This emits when contract deauthorized
*/
event ContractDeauthorized(address _contract);
/**
* The contract is valid
*/
modifier authorizedContractValid(address _contract) {
require(authorizedContractIds[_contract] > 0);
_;
}
/**
* The contract is valid
*/
modifier authorizedContractIdValid(uint256 _cid) {
require(authorizedContractList.nodeExists(_cid));
_;
}
/**
* Only the owner or authorized contract is valid
*/
modifier onlyOwnerOrAuthorizedContract {
require(msg.sender == owner || authorizedContractIds[msg.sender] > 0);
_;
}
/**
* Constructor
*/
constructor() public {
// Add one invalid RoomNight, avoid subscript 0
roomnights.push(RoomNight(0, 0, 0, 0, 0, 0, 0));
}
/**
* @dev Returns the node list and next node as a tuple
* @param self stored linked list from contract
* @param _node the begin id of the node to get
* @param _limit the total nodes of one page
* @param _direction direction to step in
*/
function getNodes(LinkedListLib.LinkedList storage self, uint256 _node, uint256 _limit, bool _direction)
private
view
returns (uint256[], uint256) {
bool exists;
uint256 i = 0;
uint256 ei = 0;
uint256 index = 0;
uint256 count = _limit;
if(count > self.length) {
count = self.length;
}
(exists, i) = self.getAdjacent(_node, _direction);
if(!exists || count == 0) {
return (new uint256[](0), 0);
}else {
uint256[] memory temp = new uint256[](count);
if(_node != 0) {
index++;
temp[0] = _node;
}
while (i != 0 && index < count) {
temp[index] = i;
(exists,i) = self.getAdjacent(i, _direction);
index++;
}
ei = i;
if(index < count) {
uint256[] memory result = new uint256[](index);
for(i = 0; i < index; i++) {
result[i] = temp[i];
}
return (result, ei);
}else {
return (temp, ei);
}
}
}
/**
* @dev Authorize `_contract` to execute this contract's funs
* @param _contract The contract address
* @param _name The contract name
*/
function authorizeContract(address _contract, string _name)
public
onlyOwner
returns(bool) {
uint256 codeSize;
assembly { codeSize := extcodesize(_contract) }
require(codeSize != 0);
// Not exists
require(authorizedContractIds[_contract] == 0);
// Add
uint256 id = authorizedContractList.push(false);
authorizedContractIds[_contract] = id;
authorizedContracts[id] = AuthorizedContract(_name, _contract);
// Event
emit ContractAuthorized(_contract);
return true;
}
/**
* @dev Deauthorized `_contract` by address
* @param _contract The contract address
*/
function deauthorizeContract(address _contract)
public
onlyOwner
authorizedContractValid(_contract)
returns(bool) {
uint256 id = authorizedContractIds[_contract];
authorizedContractList.remove(id);
authorizedContractIds[_contract] = 0;
delete authorizedContracts[id];
// Event
emit ContractDeauthorized(_contract);
return true;
}
/**
* @dev Deauthorized `_contract` by contract id
* @param _cid The contract id
*/
function deauthorizeContractById(uint256 _cid)
public
onlyOwner
authorizedContractIdValid(_cid)
returns(bool) {
address acontract = authorizedContracts[_cid].acontract;
authorizedContractList.remove(_cid);
authorizedContractIds[acontract] = 0;
delete authorizedContracts[_cid];
// Event
emit ContractDeauthorized(acontract);
return true;
}
/**
* @dev Get authorize contract ids by page
* @param _from The begin authorize contract id
* @param _limit How many authorize contract ids one page
* @return The authorize contract ids and the next authorize contract id as tuple, the next page not exists when next eq 0
*/
function getAuthorizeContractIds(uint256 _from, uint256 _limit)
external
view
returns(uint256[], uint256){
return getNodes(authorizedContractList, _from, _limit, true);
}
/**
* @dev Get authorize contract by id
* @param _cid Then authorize contract id
* @return The authorize contract info(_name, _acontract)
*/
function getAuthorizeContract(uint256 _cid)
external
view
returns(string _name, address _acontract) {
AuthorizedContract memory acontract = authorizedContracts[_cid];
_name = acontract.name;
_acontract = acontract.acontract;
}
/*************************************** GET ***************************************/
/**
* @dev Get the rate plan by `_vendorId` and `_rpid`
* @param _vendorId The vendor id
* @param _rpid The rate plan id
*/
function getRatePlan(uint256 _vendorId, uint256 _rpid)
public
view
returns (string _name, uint256 _timestamp, bytes32 _ipfs) {
_name = vendors[_vendorId].ratePlans[_rpid].name;
_timestamp = vendors[_vendorId].ratePlans[_rpid].timestamp;
_ipfs = vendors[_vendorId].ratePlans[_rpid].ipfs;
}
/**
* @dev Get the rate plan price by `_vendorId`, `_rpid`, `_date` and `_tokenId`
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _date The date desc (20180723)
* @param _tokenId The digital token id
* @return The price info(inventory, init, price)
*/
function getPrice(uint256 _vendorId, uint256 _rpid, uint256 _date, uint256 _tokenId)
public
view
returns(uint16 _inventory, bool _init, uint256 _price) {
_inventory = vendors[_vendorId].ratePlans[_rpid].prices[_date].inventory;
_init = vendors[_vendorId].ratePlans[_rpid].prices[_date].init;
_price = vendors[_vendorId].ratePlans[_rpid].prices[_date].tokens[_tokenId];
if(!_init) {
// Get the base price
_inventory = vendors[_vendorId].ratePlans[_rpid].basePrice.inventory;
_price = vendors[_vendorId].ratePlans[_rpid].basePrice.tokens[_tokenId];
_init = vendors[_vendorId].ratePlans[_rpid].basePrice.init;
}
}
/**
* @dev Get the rate plan prices by `_vendorId`, `_rpid`, `_dates` and `_tokenId`
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _dates The dates desc ([20180723,20180724,20180725])
* @param _tokenId The digital token id
* @return The price info(inventory, init, price)
*/
function getPrices(uint256 _vendorId, uint256 _rpid, uint256[] _dates, uint256 _tokenId)
public
view
returns(uint16[] _inventories, uint256[] _prices) {
uint16[] memory inventories = new uint16[](_dates.length);
uint256[] memory prices = new uint256[](_dates.length);
uint256 date;
for(uint256 i = 0; i < _dates.length; i++) {
date = _dates[i];
uint16 inventory = vendors[_vendorId].ratePlans[_rpid].prices[date].inventory;
bool init = vendors[_vendorId].ratePlans[_rpid].prices[date].init;
uint256 price = vendors[_vendorId].ratePlans[_rpid].prices[date].tokens[_tokenId];
if(!init) {
// Get the base price
inventory = vendors[_vendorId].ratePlans[_rpid].basePrice.inventory;
price = vendors[_vendorId].ratePlans[_rpid].basePrice.tokens[_tokenId];
init = vendors[_vendorId].ratePlans[_rpid].basePrice.init;
}
inventories[i] = inventory;
prices[i] = price;
}
return (inventories, prices);
}
/**
* @dev Get the inventory by by `_vendorId`, `_rpid` and `_date`
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _date The date desc (20180723)
* @return The inventory info(inventory, init)
*/
function getInventory(uint256 _vendorId, uint256 _rpid, uint256 _date)
public
view
returns(uint16 _inventory, bool _init) {
_inventory = vendors[_vendorId].ratePlans[_rpid].prices[_date].inventory;
_init = vendors[_vendorId].ratePlans[_rpid].prices[_date].init;
if(!_init) {
// Get the base price
_inventory = vendors[_vendorId].ratePlans[_rpid].basePrice.inventory;
}
}
/**
* @dev Whether the rate plan is exist
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @return If the rate plan of the vendor is exist returns true otherwise return false
*/
function ratePlanIsExist(uint256 _vendorId, uint256 _rpid)
public
view
returns (bool) {
return vendors[_vendorId].ratePlanList.nodeExists(_rpid);
}
/**
* @dev Get orders of owner by page
* @param _owner The owner address
* @param _from The begin id of the node to get
* @param _limit The total nodes of one page
* @param _direction Direction to step in
* @return The order ids and the next id
*/
function getOrdersOfOwner(address _owner, uint256 _from, uint256 _limit, bool _direction)
public
view
returns (uint256[], uint256) {
return getNodes(roomNightOwners[_owner], _from, _limit, _direction);
}
/**
* @dev Get orders of vendor by page
* @param _owner The vendor address
* @param _from The begin id of the node to get
* @param _limit The total nodes of on page
* @param _direction Direction to step in
* @return The order ids and the next id
*/
function getOrdersOfVendor(address _owner, uint256 _from, uint256 _limit, bool _direction)
public
view
returns (uint256[], uint256) {
return getNodes(roomNightVendors[_owner], _from, _limit, _direction);
}
/**
* @dev Get the token count of somebody
* @param _owner The owner of token
* @return The token count of `_owner`
*/
function balanceOf(address _owner)
public
view
returns(uint256) {
return roomNightOwners[_owner].length;
}
/**
* @dev Get rate plan ids of `_vendorId`
* @param _from The begin id of the node to get
* @param _limit The total nodes of on page
* @param _direction Direction to step in
* @return The rate plan ids and the next id
*/
function getRatePlansOfVendor(uint256 _vendorId, uint256 _from, uint256 _limit, bool _direction)
public
view
returns(uint256[], uint256) {
return getNodes(vendors[_vendorId].ratePlanList, _from, _limit, _direction);
}
/**
* @dev Get token ids
* @param _from The begin id of the node to get
* @param _limit The total nodes of on page
* @param _direction Direction to step in
* @return The token ids and the next id
*/
function getTokens(uint256 _from, uint256 _limit, bool _direction)
public
view
returns(uint256[], uint256) {
return getNodes(tokenList, _from, _limit, _direction);
}
/**
* @dev Get token Info
* @param _tokenId The token id
* @return The token info(symbol, name, decimals)
*/
function getToken(uint256 _tokenId)
public
view
returns(string _symbol, string _name, uint8 _decimals, address _token) {
_token = tokenIndexToAddress[_tokenId];
TripioToken tripio = TripioToken(_token);
_symbol = tripio.symbol();
_name = tripio.name();
_decimals = tripio.decimals();
}
/**
* @dev Get vendor ids
* @param _from The begin id of the node to get
* @param _limit The total nodes of on page
* @param _direction Direction to step in
* @return The vendor ids and the next id
*/
function getVendors(uint256 _from, uint256 _limit, bool _direction)
public
view
returns(uint256[], uint256) {
return getNodes(vendorList, _from, _limit, _direction);
}
/**
* @dev Get the vendor infomation by vendorId
* @param _vendorId The vendor id
* @return The vendor infomation(name, vendor, timestamp, valid)
*/
function getVendor(uint256 _vendorId)
public
view
returns(string _name, address _vendor,uint256 _timestamp, bool _valid) {
_name = vendors[_vendorId].name;
_vendor = vendors[_vendorId].vendor;
_timestamp = vendors[_vendorId].timestamp;
_valid = vendors[_vendorId].valid;
}
/*************************************** SET ***************************************/
/**
* @dev Update base uri of token metadata
* @param _tokenBaseURI The base uri
*/
function updateTokenBaseURI(string _tokenBaseURI)
public
onlyOwnerOrAuthorizedContract {
tokenBaseURI = _tokenBaseURI;
}
/**
* @dev Push order to user's order list
* @param _owner The buyer address
* @param _rnid The room night order id
* @param _direction direction to step in
*/
function pushOrderOfOwner(address _owner, uint256 _rnid, bool _direction)
public
onlyOwnerOrAuthorizedContract {
if(!roomNightOwners[_owner].listExists()) {
roomNightOwners[_owner] = LinkedListLib.LinkedList(0, 0);
}
roomNightOwners[_owner].add(_rnid, _direction);
}
/**
* @dev Remove order from owner's order list
* @param _owner The owner address
* @param _rnid The room night order id
*/
function removeOrderOfOwner(address _owner, uint _rnid)
public
onlyOwnerOrAuthorizedContract {
require(roomNightOwners[_owner].nodeExists(_rnid));
roomNightOwners[_owner].remove(_rnid);
}
/**
* @dev Push order to the vendor's order list
* @param _vendor The vendor address
* @param _rnid The room night order id
* @param _direction direction to step in
*/
function pushOrderOfVendor(address _vendor, uint256 _rnid, bool _direction)
public
onlyOwnerOrAuthorizedContract {
if(!roomNightVendors[_vendor].listExists()) {
roomNightVendors[_vendor] = LinkedListLib.LinkedList(0, 0);
}
roomNightVendors[_vendor].add(_rnid, _direction);
}
/**
* @dev Remove order from vendor's order list
* @param _vendor The vendor address
* @param _rnid The room night order id
*/
function removeOrderOfVendor(address _vendor, uint256 _rnid)
public
onlyOwnerOrAuthorizedContract {
require(roomNightVendors[_vendor].nodeExists(_rnid));
roomNightVendors[_vendor].remove(_rnid);
}
/**
* @dev Transfer token to somebody
* @param _tokenId The token id
* @param _to The target owner of the token
*/
function transferTokenTo(uint256 _tokenId, address _to)
public
onlyOwnerOrAuthorizedContract {
roomNightIndexToOwner[_tokenId] = _to;
roomNightApprovals[_tokenId] = address(0);
}
/**
* @dev Approve `_to` to operate the `_tokenId`
* @param _tokenId The token id
* @param _to Somebody to be approved
*/
function approveTokenTo(uint256 _tokenId, address _to)
public
onlyOwnerOrAuthorizedContract {
roomNightApprovals[_tokenId] = _to;
}
/**
* @dev Approve `_operator` to operate all the Token of `_to`
* @param _operator The operator to be approved
* @param _to The owner of tokens to be operate
* @param _approved Approved or not
*/
function approveOperatorTo(address _operator, address _to, bool _approved)
public
onlyOwnerOrAuthorizedContract {
operatorApprovals[_to][_operator] = _approved;
}
/**
* @dev Update base price of rate plan
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _tokenId The digital token id
* @param _price The price to be updated
*/
function updateBasePrice(uint256 _vendorId, uint256 _rpid, uint256 _tokenId, uint256 _price)
public
onlyOwnerOrAuthorizedContract {
vendors[_vendorId].ratePlans[_rpid].basePrice.init = true;
vendors[_vendorId].ratePlans[_rpid].basePrice.tokens[_tokenId] = _price;
}
/**
* @dev Update base inventory of rate plan
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _inventory The inventory to be updated
*/
function updateBaseInventory(uint256 _vendorId, uint256 _rpid, uint16 _inventory)
public
onlyOwnerOrAuthorizedContract {
vendors[_vendorId].ratePlans[_rpid].basePrice.inventory = _inventory;
}
/**
* @dev Update price by `_vendorId`, `_rpid`, `_date`, `_tokenId` and `_price`
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _date The date desc (20180723)
* @param _tokenId The digital token id
* @param _price The price to be updated
*/
function updatePrice(uint256 _vendorId, uint256 _rpid, uint256 _date, uint256 _tokenId, uint256 _price)
public
onlyOwnerOrAuthorizedContract {
if (vendors[_vendorId].ratePlans[_rpid].prices[_date].init) {
vendors[_vendorId].ratePlans[_rpid].prices[_date].tokens[_tokenId] = _price;
} else {
vendors[_vendorId].ratePlans[_rpid].prices[_date] = Price(0, true);
vendors[_vendorId].ratePlans[_rpid].prices[_date].tokens[_tokenId] = _price;
}
}
/**
* @dev Update inventory by `_vendorId`, `_rpid`, `_date`, `_inventory`
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _date The date desc (20180723)
* @param _inventory The inventory to be updated
*/
function updateInventories(uint256 _vendorId, uint256 _rpid, uint256 _date, uint16 _inventory)
public
onlyOwnerOrAuthorizedContract {
if (vendors[_vendorId].ratePlans[_rpid].prices[_date].init) {
vendors[_vendorId].ratePlans[_rpid].prices[_date].inventory = _inventory;
} else {
vendors[_vendorId].ratePlans[_rpid].prices[_date] = Price(_inventory, true);
}
}
/**
* @dev Reduce inventories
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _date The date desc (20180723)
* @param _inventory The amount to be reduced
*/
function reduceInventories(uint256 _vendorId, uint256 _rpid, uint256 _date, uint16 _inventory)
public
onlyOwnerOrAuthorizedContract {
uint16 a = 0;
if(vendors[_vendorId].ratePlans[_rpid].prices[_date].init) {
a = vendors[_vendorId].ratePlans[_rpid].prices[_date].inventory;
require(_inventory <= a);
vendors[_vendorId].ratePlans[_rpid].prices[_date].inventory = a - _inventory;
}else if(vendors[_vendorId].ratePlans[_rpid].basePrice.init){
a = vendors[_vendorId].ratePlans[_rpid].basePrice.inventory;
require(_inventory <= a);
vendors[_vendorId].ratePlans[_rpid].basePrice.inventory = a - _inventory;
}
}
/**
* @dev Add inventories
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _date The date desc (20180723)
* @param _inventory The amount to be add
*/
function addInventories(uint256 _vendorId, uint256 _rpid, uint256 _date, uint16 _inventory)
public
onlyOwnerOrAuthorizedContract {
uint16 c = 0;
if(vendors[_vendorId].ratePlans[_rpid].prices[_date].init) {
c = _inventory + vendors[_vendorId].ratePlans[_rpid].prices[_date].inventory;
require(c >= _inventory);
vendors[_vendorId].ratePlans[_rpid].prices[_date].inventory = c;
}else if(vendors[_vendorId].ratePlans[_rpid].basePrice.init) {
c = _inventory + vendors[_vendorId].ratePlans[_rpid].basePrice.inventory;
require(c >= _inventory);
vendors[_vendorId].ratePlans[_rpid].basePrice.inventory = c;
}
}
/**
* @dev Update inventory and price by `_vendorId`, `_rpid`, `_date`, `_tokenId`, `_price` and `_inventory`
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _date The date desc (20180723)
* @param _tokenId The digital token id
* @param _price The price to be updated
* @param _inventory The inventory to be updated
*/
function updatePriceAndInventories(uint256 _vendorId, uint256 _rpid, uint256 _date, uint256 _tokenId, uint256 _price, uint16 _inventory)
public
onlyOwnerOrAuthorizedContract {
if (vendors[_vendorId].ratePlans[_rpid].prices[_date].init) {
vendors[_vendorId].ratePlans[_rpid].prices[_date].inventory = _inventory;
vendors[_vendorId].ratePlans[_rpid].prices[_date].tokens[_tokenId] = _price;
} else {
vendors[_vendorId].ratePlans[_rpid].prices[_date] = Price(_inventory, true);
vendors[_vendorId].ratePlans[_rpid].prices[_date].tokens[_tokenId] = _price;
}
}
/**
* @dev Push rate plan to `_vendorId`'s rate plan list
* @param _vendorId The vendor id
* @param _name The name of rate plan
* @param _ipfs The rate plan IPFS address
* @param _direction direction to step in
*/
function pushRatePlan(uint256 _vendorId, string _name, bytes32 _ipfs, bool _direction)
public
onlyOwnerOrAuthorizedContract
returns(uint256) {
RatePlan memory rp = RatePlan(_name, uint256(now), _ipfs, Price(0, false));
uint256 id = vendors[_vendorId].ratePlanList.push(_direction);
vendors[_vendorId].ratePlans[id] = rp;
return id;
}
/**
* @dev Remove rate plan of `_vendorId` by `_rpid`
* @param _vendorId The vendor id
* @param _rpid The rate plan id
*/
function removeRatePlan(uint256 _vendorId, uint256 _rpid)
public
onlyOwnerOrAuthorizedContract {
delete vendors[_vendorId].ratePlans[_rpid];
vendors[_vendorId].ratePlanList.remove(_rpid);
}
/**
* @dev Update `_rpid` of `_vendorId` by `_name` and `_ipfs`
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _name The rate plan name
* @param _ipfs The rate plan IPFS address
*/
function updateRatePlan(uint256 _vendorId, uint256 _rpid, string _name, bytes32 _ipfs)
public
onlyOwnerOrAuthorizedContract {
vendors[_vendorId].ratePlans[_rpid].ipfs = _ipfs;
vendors[_vendorId].ratePlans[_rpid].name = _name;
}
/**
* @dev Push token contract to the token list
* @param _direction direction to step in
*/
function pushToken(address _contract, bool _direction)
public
onlyOwnerOrAuthorizedContract
returns(uint256) {
uint256 id = tokenList.push(_direction);
tokenIndexToAddress[id] = _contract;
return id;
}
/**
* @dev Remove token by `_tokenId`
* @param _tokenId The digital token id
*/
function removeToken(uint256 _tokenId)
public
onlyOwnerOrAuthorizedContract {
delete tokenIndexToAddress[_tokenId];
tokenList.remove(_tokenId);
}
/**
* @dev Generate room night token
* @param _vendorId The vendor id
* @param _rpid The rate plan id
* @param _date The date desc (20180723)
* @param _token The token id
* @param _price The token price
* @param _ipfs The rate plan IPFS address
*/
function generateRoomNightToken(uint256 _vendorId, uint256 _rpid, uint256 _date, uint256 _token, uint256 _price, bytes32 _ipfs)
public
onlyOwnerOrAuthorizedContract
returns(uint256) {
roomnights.push(RoomNight(_vendorId, _rpid, _token, _price, now, _date, _ipfs));
// Give the token to `_customer`
uint256 rnid = uint256(roomnights.length - 1);
return rnid;
}
/**
* @dev Update refund applications
* @param _buyer The room night token holder
* @param _rnid The room night token id
* @param _isRefund Is redund or not
*/
function updateRefundApplications(address _buyer, uint256 _rnid, bool _isRefund)
public
onlyOwnerOrAuthorizedContract {
refundApplications[_buyer][_rnid] = _isRefund;
}
/**
* @dev Push vendor info to the vendor list
* @param _name The name of vendor
* @param _vendor The vendor address
* @param _direction direction to step in
*/
function pushVendor(string _name, address _vendor, bool _direction)
public
onlyOwnerOrAuthorizedContract
returns(uint256) {
uint256 id = vendorList.push(_direction);
vendorIds[_vendor] = id;
vendors[id] = Vendor(_name, _vendor, uint256(now), true, LinkedListLib.LinkedList(0, 0));
return id;
}
/**
* @dev Remove vendor from vendor list
* @param _vendorId The vendor id
*/
function removeVendor(uint256 _vendorId)
public
onlyOwnerOrAuthorizedContract {
vendorList.remove(_vendorId);
address vendor = vendors[_vendorId].vendor;
vendorIds[vendor] = 0;
delete vendors[_vendorId];
}
/**
* @dev Make vendor valid or invalid
* @param _vendorId The vendor id
* @param _valid The vendor is valid or not
*/
function updateVendorValid(uint256 _vendorId, bool _valid)
public
onlyOwnerOrAuthorizedContract {
vendors[_vendorId].valid = _valid;
}
/**
* @dev Modify vendor's name
* @param _vendorId The vendor id
* @param _name Then vendor name
*/
function updateVendorName(uint256 _vendorId, string _name)
public
onlyOwnerOrAuthorizedContract {
vendors[_vendorId].name = _name;
}
}
contract TRNTransactions is TRNOwners {
/**
* Constructor
*/
constructor() public {
}
/**
* This emits when rate plan is bought in batch
*/
event BuyInBatch(address indexed _customer, address indexed _vendor, uint256 indexed _rpid, uint256[] _dates, uint256 _token);
/**
* This emits when token refund is applied
*/
event ApplyRefund(address _customer, uint256 indexed _rnid, bool _isRefund);
/**
* This emits when refunded
*/
event Refund(address _vendor, uint256 _rnid);
/**
* @dev Complete the buy transaction,
* The inventory minus one and the room night token transfer to customer
* @param _vendorId The vendor account
* @param _rpid The vendor's rate plan id
* @param _date The booking date
* @param _customer The customer account
* @param _token The token Id
*/
function _buy(uint256 _vendorId, uint256 _rpid, uint256 _date, address _customer, uint256 _token) private {
// Product room night token
(,,uint256 _price) = dataSource.getPrice(_vendorId, _rpid, _date, _token);
(,,bytes32 _ipfs) = dataSource.getRatePlan(_vendorId, _rpid);
uint256 rnid = dataSource.generateRoomNightToken(_vendorId, _rpid, _date, _token, _price, _ipfs);
// Give the token to `_customer`
dataSource.transferTokenTo(rnid, _customer);
// Record the token to `_customer` account
_pushRoomNight(_customer, rnid, false);
// Record the token to `_vendor` account
(,address vendor,,) = dataSource.getVendor(_vendorId);
_pushRoomNight(vendor, rnid, true);
// The inventory minus one
dataSource.reduceInventories(_vendorId, _rpid, _date, 1);
}
/**
* @dev Complete the buy transaction in batch,
* The inventory minus one and the room night token transfer to customer
* @param _vendorId The vendor account
* @param _vendor Then vendor address
* @param _rpid The vendor's rate plan id
* @param _dates The booking date
* @param _token The token Id
*/
function _buyInBatch(uint256 _vendorId, address _vendor, uint256 _rpid, uint256[] _dates, uint256 _token) private returns(bool) {
(uint16[] memory inventories, uint256[] memory values) = dataSource.getPrices(_vendorId, _rpid, _dates, _token);
uint256 totalValues = 0;
for(uint256 i = 0; i < _dates.length; i++) {
if(inventories[i] == 0 || values[i] == 0) {
return false;
}
totalValues += values[i];
// Transfer the room night to `msg.sender`
_buy(_vendorId, _rpid, _dates[i], msg.sender, _token);
}
if (_token == 0) {
// By through ETH
require(msg.value == totalValues);
// Transfer the ETH to `_vendor`
_vendor.transfer(totalValues);
} else {
// By through other digital token
address tokenAddress = dataSource.tokenIndexToAddress(_token);
require(tokenAddress != address(0));
// This contract transfer `price.trio` from `msg.sender` account
TripioToken tripio = TripioToken(tokenAddress);
tripio.transferFrom(msg.sender, _vendor, totalValues);
}
return true;
}
/**
* Complete the refund transaction
* Remove the `_rnid` from the owner account and the inventory plus one
*/
function _refund(uint256 _rnid, uint256 _vendorId, uint256 _rpid, uint256 _date) private {
// Remove the `_rnid` from the owner
_removeRoomNight(dataSource.roomNightIndexToOwner(_rnid), _rnid);
// The inventory plus one
dataSource.addInventories(_vendorId, _rpid, _date, 1);
// Change the owner of `_rnid`
dataSource.transferTokenTo(_rnid, address(0));
}
/**
* @dev By room nigth in batch through ETH(`_token` == 0) or other digital token(`_token != 0`)
* Throw when `_rpid` not exist
* Throw unless each inventory more than zero
* Throw unless `msg.value` equal to `price.eth`
* This method is payable, can accept ETH transfer
* @param _vendorId The vendor Id
* @param _rpid The _vendor's rate plan id
* @param _dates The booking dates
* @param _token The digital currency token
*/
function buyInBatch(uint256 _vendorId, uint256 _rpid, uint256[] _dates, uint256 _token)
external
payable
ratePlanExist(_vendorId, _rpid)
validDates(_dates)
returns(bool) {
(,address vendor,,) = dataSource.getVendor(_vendorId);
bool result = _buyInBatch(_vendorId, vendor, _rpid, _dates, _token);
require(result);
// Event
emit BuyInBatch(msg.sender, vendor, _rpid, _dates, _token);
return true;
}
/**
* @dev Apply room night refund
* Throw unless `_rnid` is valid
* Throw unless `_rnid` can transfer
* @param _rnid room night identifier
* @param _isRefund if `true` the `_rnid` can transfer else not
*/
function applyRefund(uint256 _rnid, bool _isRefund)
external
validToken(_rnid)
canTransfer(_rnid)
returns(bool) {
dataSource.updateRefundApplications(msg.sender, _rnid, _isRefund);
// Event
emit ApplyRefund(msg.sender, _rnid, _isRefund);
return true;
}
/**
* @dev Whether the `_rnid` is in refund applications
* @param _rnid room night identifier
*/
function isRefundApplied(uint256 _rnid)
external
view
validToken(_rnid) returns(bool) {
return dataSource.refundApplications(dataSource.roomNightIndexToOwner(_rnid), _rnid);
}
/**
* @dev Refund through ETH or other digital token, give the room night ETH/TOKEN to customer and take back inventory
* Throw unless `_rnid` is valid
* Throw unless `msg.sender` is vendor
* Throw unless the refund application is true
* Throw unless the `msg.value` is equal to `roomnight.eth`
* @param _rnid room night identifier
*/
function refund(uint256 _rnid)
external
payable
validToken(_rnid)
returns(bool) {
// Refund application is true
require(dataSource.refundApplications(dataSource.roomNightIndexToOwner(_rnid), _rnid));
// The `msg.sender` is the vendor of the room night.
(uint256 vendorId,uint256 rpid,uint256 token,uint256 price,,uint256 date,) = dataSource.roomnights(_rnid);
(,address vendor,,) = dataSource.getVendor(vendorId);
require(msg.sender == vendor);
address ownerAddress = dataSource.roomNightIndexToOwner(_rnid);
if (token == 0) {
// Refund by ETH
// The `msg.sender` is equal to `roomnight.eth`
uint256 value = price;
require(msg.value >= value);
// Transfer the ETH to roomnight's owner
ownerAddress.transfer(value);
} else {
// Refund by TRIO
// The `roomnight.trio` is more than zero
require(price > 0);
// This contract transfer `price.trio` from `msg.sender` account
TripioToken tripio = TripioToken(dataSource.tokenIndexToAddress(token));
tripio.transferFrom(msg.sender, ownerAddress, price);
}
// Refund
_refund(_rnid, vendorId, rpid, date);
// Event
emit Refund(msg.sender, _rnid);
return true;
}
}
contract TripioRoomNightCustomer is TRNAsset, TRNSupportsInterface, TRNOwnership, TRNTransactions {
/**
* Constructor
*/
constructor(address _dataSource) public {
// Init the data source
dataSource = TripioRoomNightData(_dataSource);
}
/**
* @dev Withdraw ETH balance from contract account, the balance will transfer to the contract owner
*/
function withdrawBalance() external onlyOwner {
owner.transfer(address(this).balance);
}
/**
* @dev Withdraw other TOKEN balance from contract account, the balance will transfer to the contract owner
* @param _token The TOKEN id
*/
function withdrawTokenId(uint _token) external onlyOwner {
TripioToken tripio = TripioToken(dataSource.tokenIndexToAddress(_token));
uint256 tokens = tripio.balanceOf(address(this));
tripio.transfer(owner, tokens);
}
/**
* @dev Withdraw other TOKEN balance from contract account, the balance will transfer to the contract owner
* @param _tokenAddress The TOKEN address
*/
function withdrawToken(address _tokenAddress) external onlyOwner {
TripioToken tripio = TripioToken(_tokenAddress);
uint256 tokens = tripio.balanceOf(address(this));
tripio.transfer(owner, tokens);
}
/**
* @dev Destory the contract
*/
function destroy() external onlyOwner {
selfdestruct(owner);
}
function() external payable {
}
}