167 lines
5.2 KiB
Solidity

/* solium-disable security/no-block-members */
/* solium-disable security/no-inline-assembly */
pragma solidity >=0.5.0 <0.6.0;
import "../common/Ownable.sol";
import "../token/ERC20Token.sol";
import "../token/SafeTransfer.sol";
import "../token/ApproveAndCallFallBack.sol";
import "../proxy/Proxiable.sol";
/**
* @title License
* @dev Contract for buying a license
*/
contract License is Ownable, ApproveAndCallFallBack, SafeTransfer, Proxiable {
uint256 public price;
ERC20Token token;
address burnAddress;
struct LicenseDetails {
uint price;
uint creationTime;
}
address[] public licenseOwners;
mapping(address => uint) public idxLicenseOwners;
mapping(address => LicenseDetails) public licenseDetails;
event Bought(address buyer, uint256 price);
event PriceChanged(uint256 _price);
event BurnAddressChanged(address sender, address prevBurnAddress, address newBurnAddress);
/**
* @dev Changes the burn address
* @param _burnAddress New burn address
*/
function setBurnAddress(address payable _burnAddress) external onlyOwner {
emit BurnAddressChanged(msg.sender, burnAddress, _burnAddress);
burnAddress = _burnAddress;
}
/**
* @param _tokenAddress Address of token used to pay for licenses (SNT)
* @param _price Price of the licenses
* @param _burnAddress Address where the license fee is going to be sent
*/
constructor(address _tokenAddress, uint256 _price, address _burnAddress) public {
init(_tokenAddress, _price, _burnAddress);
}
/**
* @dev Initialize contract (used with proxy). Can only be called once
* @param _tokenAddress Address of token used to pay for licenses (SNT)
* @param _price Price of the licenses
* @param _burnAddress Address where the license fee is going to be sent
*/
function init(
address _tokenAddress,
uint256 _price,
address _burnAddress
) public {
assert(_initialized == false);
_initialized = true;
price = _price;
token = ERC20Token(_tokenAddress);
burnAddress = _burnAddress;
_setOwner(msg.sender);
}
function updateCode(address newCode) public onlyOwner {
updateCodeAddress(newCode);
}
/**
* @notice Check if the address already owns a license
* @param _address The address to check
* @return bool
*/
function isLicenseOwner(address _address) public view returns (bool) {
return licenseDetails[_address].price != 0 && licenseDetails[_address].creationTime != 0;
}
/**
* @notice Buy a license
* @dev Requires value to be equal to the price of the license.
* The msg.sender must not already own a license.
*/
function buy() external returns(uint) {
uint id = _buyFrom(msg.sender);
return id;
}
/**
* @notice Buy a license
* @dev Requires value to be equal to the price of the license.
* The _owner must not already own a license.
*/
function _buyFrom(address _licenseOwner) internal returns(uint) {
require(licenseDetails[_licenseOwner].creationTime == 0, "License already bought");
licenseDetails[_licenseOwner] = LicenseDetails({
price: price,
creationTime: block.timestamp
});
uint idx = licenseOwners.push(_licenseOwner);
idxLicenseOwners[_licenseOwner] = idx;
emit Bought(_licenseOwner, price);
require(_safeTransferFrom(token, _licenseOwner, burnAddress, price), "Unsuccessful token transfer");
return idx;
}
/**
* @notice Set the license price
* @param _price The new price of the license
* @dev Only the owner of the contract can perform this action
*/
function setPrice(uint256 _price) external onlyOwner {
price = _price;
emit PriceChanged(_price);
}
/**
* @dev Get number of license owners
* @return uint
*/
function getNumLicenseOwners() external view returns (uint256) {
return licenseOwners.length;
}
/**
* @notice Support for "approveAndCall". Callable only by `token()`.
* @param _from Who approved.
* @param _amount Amount being approved, need to be equal `price()`.
* @param _token Token being approved, need to be equal `token()`.
* @param _data Abi encoded data with selector of `buy(and)`.
*/
function receiveApproval(address _from, uint256 _amount, address _token, bytes memory _data) public {
require(_amount == price, "Wrong value");
require(_token == address(token), "Wrong token");
require(_token == address(msg.sender), "Wrong call");
require(_data.length == 4, "Wrong data length");
require(_abiDecodeBuy(_data) == bytes4(0xa6f2ae3a), "Wrong method selector"); //bytes4(keccak256("buy()"))
_buyFrom(_from);
}
/**
* @dev Decodes abi encoded data with selector for "buy()".
* @param _data Abi encoded data.
* @return Decoded registry call.
*/
function _abiDecodeBuy(bytes memory _data) internal pure returns(bytes4 sig) {
assembly {
sig := mload(add(_data, add(0x20, 0)))
}
}
}