/** * Source Code first verified at https://etherscan.io on Friday, April 5, 2019 (UTC) */ pragma solidity ^0.5.1; interface IERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer( address indexed from, address indexed to, uint256 value ); event Approval( address indexed owner, address indexed spender, uint256 value ); } library SafeMath { /** * @dev Multiplies two numbers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, ""); return c; } /** * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, ""); // Solidity only automatically asserts when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, ""); uint256 c = a - b; return c; } /** * @dev Adds two numbers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, ""); return c; } /** * @dev Divides two numbers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, ""); return a % b; } } contract ERC20 is IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowed; uint256 private _totalSupply; /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev Gets the balance of the specified address. * @param owner The address to query the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address owner) public view returns (uint256) { return _balances[owner]; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance( address owner, address spender ) public view returns (uint256) { return _allowed[owner][spender]; } /** * @dev Transfer token for a specified address * @param to The address to transfer to. * @param value The amount to be transferred. */ function transfer(address to, uint256 value) public returns (bool) { require(value <= _balances[msg.sender], ""); require(to != address(0), ""); _balances[msg.sender] = _balances[msg.sender].sub(value); _balances[to] = _balances[to].add(value); emit Transfer(msg.sender, to, value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * Beware that changing an allowance with this method brings the risk that someone may use both the old * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. */ function approve(address spender, uint256 value) public returns (bool) { require(spender != address(0), ""); _allowed[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } /** * @dev Transfer tokens from one address to another * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param value uint256 the amount of tokens to be transferred */ function transferFrom( address from, address to, uint256 value ) public returns (bool) { require(value <= _balances[from], ""); require(value <= _allowed[from][msg.sender], ""); require(to != address(0), ""); _balances[from] = _balances[from].sub(value); _balances[to] = _balances[to].add(value); _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); emit Transfer(from, to, value); return true; } /** * @dev Increase the amount of tokens that an owner allowed to a spender. * approve should be called when allowed_[_spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * @param spender The address which will spend the funds. * @param addedValue The amount of tokens to increase the allowance by. */ function increaseAllowance( address spender, uint256 addedValue ) public returns (bool) { require(spender != address(0), ""); _allowed[msg.sender][spender] = ( _allowed[msg.sender][spender].add(addedValue)); emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); return true; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * approve should be called when allowed_[_spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * @param spender The address which will spend the funds. * @param subtractedValue The amount of tokens to decrease the allowance by. */ function decreaseAllowance( address spender, uint256 subtractedValue ) public returns (bool) { require(spender != address(0), ""); _allowed[msg.sender][spender] = ( _allowed[msg.sender][spender].sub(subtractedValue)); emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); return true; } /** * @dev Internal function that mints an amount of the token and assigns it to * an account. This encapsulates the modification of balances such that the * proper events are emitted. * @param account The account that will receive the created tokens. * @param amount The amount that will be created. */ function _mint(address account, uint256 amount) internal { require(account != address(0), ""); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Internal function that burns an amount of the token of a given * account. * @param account The account whose tokens will be burnt. * @param amount The amount that will be burnt. */ function _burn(address account, uint256 amount) internal { require(account != address(0), ""); require(amount <= _balances[account], ""); _totalSupply = _totalSupply.sub(amount); _balances[account] = _balances[account].sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Internal function that burns an amount of the token of a given * account, deducting from the sender's allowance for said account. Uses the * internal burn function. * @param account The account whose tokens will be burnt. * @param amount The amount that will be burnt. */ function _burnFrom(address account, uint256 amount) internal { require(amount <= _allowed[account][msg.sender], ""); // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted, // this function needs to emit an event with the updated approval. _allowed[account][msg.sender] = _allowed[account][msg.sender].sub( amount); _burn(account, amount); } } /** * @title BaseSecurityToken implementation * @dev see https://eips.ethereum.org/EIPS/eip-1462 */ contract BaseSecurityToken is ERC20 { struct Document { string name; string uri; bytes32 contentHash; } mapping (string => Document) private documents; function transfer(address to, uint256 value) public returns (bool) { require(checkTransferAllowed(msg.sender, to, value) == STATUS_ALLOWED, "transfer must be allowed"); return ERC20.transfer(to, value); } function transferFrom(address from, address to, uint256 value) public returns (bool) { require(checkTransferFromAllowed(from, to, value) == STATUS_ALLOWED, "transfer must be allowed"); return ERC20.transferFrom(from, to, value); } function _mint(address account, uint256 amount) internal { require(checkMintAllowed(account, amount) == STATUS_ALLOWED, "mint must be allowed"); ERC20._mint(account, amount); } function _burn(address account, uint256 amount) internal { require(checkBurnAllowed(account, amount) == STATUS_ALLOWED, "burn must be allowed"); ERC20._burn(account, amount); } function attachDocument(string calldata _name, string calldata _uri, bytes32 _contentHash) external { require(bytes(_name).length > 0, "name of the document must not be empty"); require(bytes(_uri).length > 0, "external URI to the document must not be empty"); documents[_name] = Document(_name, _uri, _contentHash); } function lookupDocument(string calldata _name) external view returns (string memory, bytes32) { Document storage doc = documents[_name]; return (doc.uri, doc.contentHash); } // Use status codes from: // https://eips.ethereum.org/EIPS/eip-1066 byte private STATUS_ALLOWED = 0x11; function checkTransferAllowed(address, address, uint256) public view returns (byte) { return STATUS_ALLOWED; } function checkTransferFromAllowed(address, address, uint256) public view returns (byte) { return STATUS_ALLOWED; } function checkMintAllowed(address, uint256) public view returns (byte) { return STATUS_ALLOWED; } function checkBurnAllowed(address, uint256) public view returns (byte) { return STATUS_ALLOWED; } } contract LockRequestable { // MEMBERS /// @notice the count of all invocations of `generateLockId`. uint256 public lockRequestCount; constructor() public { lockRequestCount = 0; } // FUNCTIONS /** @notice Returns a fresh unique identifier. * * @dev the generation scheme uses three components. * First, the blockhash of the previous block. * Second, the deployed address. * Third, the next value of the counter. * This ensure that identifiers are unique across all contracts * following this scheme, and that future identifiers are * unpredictable. * * @return a 32-byte unique identifier. */ function generateLockId() internal returns (bytes32 lockId) { return keccak256( abi.encodePacked(blockhash(block.number - 1), address(this), ++lockRequestCount) ); } } contract CustodianUpgradeable is LockRequestable { // TYPES /// @dev The struct type for pending custodian changes. struct CustodianChangeRequest { address proposedNew; } // MEMBERS /// @dev The address of the account or contract that acts as the custodian. address public custodian; /// @dev The map of lock ids to pending custodian changes. mapping (bytes32 => CustodianChangeRequest) public custodianChangeReqs; constructor(address _custodian) public LockRequestable() { custodian = _custodian; } // MODIFIERS modifier onlyCustodian { require(msg.sender == custodian); _; } /** @notice Requests a change of the custodian associated with this contract. * * @dev Returns a unique lock id associated with the request. * Anyone can call this function, but confirming the request is authorized * by the custodian. * * @param _proposedCustodian The address of the new custodian. * @return lockId A unique identifier for this request. */ function requestCustodianChange(address _proposedCustodian) public returns (bytes32 lockId) { require(_proposedCustodian != address(0)); lockId = generateLockId(); custodianChangeReqs[lockId] = CustodianChangeRequest({ proposedNew: _proposedCustodian }); emit CustodianChangeRequested(lockId, msg.sender, _proposedCustodian); } /** @notice Confirms a pending change of the custodian associated with this contract. * * @dev When called by the current custodian with a lock id associated with a * pending custodian change, the `address custodian` member will be updated with the * requested address. * * @param _lockId The identifier of a pending change request. */ function confirmCustodianChange(bytes32 _lockId) public onlyCustodian { custodian = getCustodianChangeReq(_lockId); delete custodianChangeReqs[_lockId]; emit CustodianChangeConfirmed(_lockId, custodian); } // PRIVATE FUNCTIONS function getCustodianChangeReq(bytes32 _lockId) private view returns (address _proposedNew) { CustodianChangeRequest storage changeRequest = custodianChangeReqs[_lockId]; // reject ‘null’ results from the map lookup // this can only be the case if an unknown `_lockId` is received require(changeRequest.proposedNew != address(0)); return changeRequest.proposedNew; } /// @dev Emitted by successful `requestCustodianChange` calls. event CustodianChangeRequested( bytes32 _lockId, address _msgSender, address _proposedCustodian ); /// @dev Emitted by successful `confirmCustodianChange` calls. event CustodianChangeConfirmed(bytes32 _lockId, address _newCustodian); } interface ServiceRegistry { function getService(string calldata _name) external view returns (address); } contract ServiceDiscovery { ServiceRegistry internal services; constructor(ServiceRegistry _services) public { services = ServiceRegistry(_services); } } contract KnowYourCustomer is CustodianUpgradeable { enum Status { none, passed, suspended } struct Customer { Status status; mapping(string => string) fields; } event ProviderAuthorized(address indexed _provider, string _name); event ProviderRemoved(address indexed _provider, string _name); event CustomerApproved(address indexed _customer, address indexed _provider); event CustomerSuspended(address indexed _customer, address indexed _provider); event CustomerFieldSet(address indexed _customer, address indexed _field, string _name); mapping(address => bool) private providers; mapping(address => Customer) private customers; constructor(address _custodian) public CustodianUpgradeable(_custodian) { customers[_custodian].status = Status.passed; customers[_custodian].fields["type"] = "custodian"; emit CustomerApproved(_custodian, msg.sender); emit CustomerFieldSet(_custodian, msg.sender, "type"); } function providerAuthorize(address _provider, string calldata name) external onlyCustodian { require(providers[_provider] == false, "provider must not exist"); providers[_provider] = true; // cc:II. Manage Providers#2;Provider becomes authorized in contract;1; emit ProviderAuthorized(_provider, name); } function providerRemove(address _provider, string calldata name) external onlyCustodian { require(providers[_provider] == true, "provider must exist"); delete providers[_provider]; emit ProviderRemoved(_provider, name); } function hasWritePermissions(address _provider) external view returns (bool) { return _provider == custodian || providers[_provider] == true; } function getCustomerStatus(address _customer) external view returns (Status) { return customers[_customer].status; } function getCustomerField(address _customer, string calldata _field) external view returns (string memory) { return customers[_customer].fields[_field]; } function approveCustomer(address _customer) external onlyAuthorized { Status status = customers[_customer].status; require(status != Status.passed, "customer must not be approved before"); customers[_customer].status = Status.passed; // cc:III. Manage Customers#2;Customer becomes approved in contract;1; emit CustomerApproved(_customer, msg.sender); } function setCustomerField(address _customer, string calldata _field, string calldata _value) external onlyAuthorized { Status status = customers[_customer].status; require(status != Status.none, "customer must have a set status"); customers[_customer].fields[_field] = _value; emit CustomerFieldSet(_customer, msg.sender, _field); } function suspendCustomer(address _customer) external onlyAuthorized { Status status = customers[_customer].status; require(status != Status.suspended, "customer must be not suspended"); customers[_customer].status = Status.suspended; emit CustomerSuspended(_customer, msg.sender); } modifier onlyAuthorized() { require(msg.sender == custodian || providers[msg.sender] == true); _; } } contract TokenSettingsInterface { // METHODS function getTradeAllowed() public view returns (bool); function getMintAllowed() public view returns (bool); function getBurnAllowed() public view returns (bool); // EVENTS event TradeAllowedLocked(bytes32 _lockId, bool _newValue); event TradeAllowedConfirmed(bytes32 _lockId, bool _newValue); event MintAllowedLocked(bytes32 _lockId, bool _newValue); event MintAllowedConfirmed(bytes32 _lockId, bool _newValue); event BurnAllowedLocked(bytes32 _lockId, bool _newValue); event BurnAllowedConfirmed(bytes32 _lockId, bool _newValue); // MODIFIERS modifier onlyCustodian { _; } } contract _BurnAllowed is TokenSettingsInterface, LockRequestable { // cc:IV. BurnAllowed Setting#2;Burn Allowed Switch;1; // // SETTING: Burn Allowed Switch (bool) // Boundary: true or false // // Enables or disables token minting ability globally (even for custodian). // bool private burnAllowed = false; function getBurnAllowed() public view returns (bool) { return burnAllowed; } // SETTING MANAGEMENT struct PendingBurnAllowed { bool burnAllowed; bool set; } mapping (bytes32 => PendingBurnAllowed) public pendingBurnAllowedMap; function requestBurnAllowedChange(bool _burnAllowed) public returns (bytes32 lockId) { require(_burnAllowed != burnAllowed); lockId = generateLockId(); pendingBurnAllowedMap[lockId] = PendingBurnAllowed({ burnAllowed: _burnAllowed, set: true }); emit BurnAllowedLocked(lockId, _burnAllowed); } function confirmBurnAllowedChange(bytes32 _lockId) public onlyCustodian { PendingBurnAllowed storage value = pendingBurnAllowedMap[_lockId]; require(value.set == true); burnAllowed = value.burnAllowed; emit BurnAllowedConfirmed(_lockId, value.burnAllowed); delete pendingBurnAllowedMap[_lockId]; } } contract _MintAllowed is TokenSettingsInterface, LockRequestable { // cc:III. MintAllowed Setting#2;Mint Allowed Switch;1; // // SETTING: Mint Allowed Switch (bool) // Boundary: true or false // // Enables or disables token minting ability globally (even for custodian). // bool private mintAllowed = false; function getMintAllowed() public view returns (bool) { return mintAllowed; } // SETTING MANAGEMENT struct PendingMintAllowed { bool mintAllowed; bool set; } mapping (bytes32 => PendingMintAllowed) public pendingMintAllowedMap; function requestMintAllowedChange(bool _mintAllowed) public returns (bytes32 lockId) { require(_mintAllowed != mintAllowed); lockId = generateLockId(); pendingMintAllowedMap[lockId] = PendingMintAllowed({ mintAllowed: _mintAllowed, set: true }); emit MintAllowedLocked(lockId, _mintAllowed); } function confirmMintAllowedChange(bytes32 _lockId) public onlyCustodian { PendingMintAllowed storage value = pendingMintAllowedMap[_lockId]; require(value.set == true); mintAllowed = value.mintAllowed; emit MintAllowedConfirmed(_lockId, value.mintAllowed); delete pendingMintAllowedMap[_lockId]; } } contract _TradeAllowed is TokenSettingsInterface, LockRequestable { // cc:II. TradeAllowed Setting#2;Trade Allowed Switch;1; // // SETTING: Trade Allowed Switch (bool) // Boundary: true or false // // Enables or disables all token transfers, between any recipients, except mint and burn operations. // bool private tradeAllowed = false; function getTradeAllowed() public view returns (bool) { return tradeAllowed; } // SETTING MANAGEMENT struct PendingTradeAllowed { bool tradeAllowed; bool set; } mapping (bytes32 => PendingTradeAllowed) public pendingTradeAllowedMap; function requestTradeAllowedChange(bool _tradeAllowed) public returns (bytes32 lockId) { require(_tradeAllowed != tradeAllowed); lockId = generateLockId(); pendingTradeAllowedMap[lockId] = PendingTradeAllowed({ tradeAllowed: _tradeAllowed, set: true }); emit TradeAllowedLocked(lockId, _tradeAllowed); } function confirmTradeAllowedChange(bytes32 _lockId) public onlyCustodian { PendingTradeAllowed storage value = pendingTradeAllowedMap[_lockId]; require(value.set == true); tradeAllowed = value.tradeAllowed; emit TradeAllowedConfirmed(_lockId, value.tradeAllowed); delete pendingTradeAllowedMap[_lockId]; } } contract TokenSettings is TokenSettingsInterface, CustodianUpgradeable, _TradeAllowed, _MintAllowed, _BurnAllowed { constructor(address _custodian) public CustodianUpgradeable(_custodian) { } } /** * @title TokenController implements restriction logic for BaseSecurityToken. * @dev see https://eips.ethereum.org/EIPS/eip-1462 */ contract TokenController is CustodianUpgradeable, ServiceDiscovery { constructor(address _custodian, ServiceRegistry _services) public CustodianUpgradeable(_custodian) ServiceDiscovery(_services) { } // Use status codes from: // https://eips.ethereum.org/EIPS/eip-1066 byte private constant STATUS_ALLOWED = 0x11; function checkTransferAllowed(address _from, address _to, uint256) public view returns (byte) { require(_settings().getTradeAllowed(), "global trade must be allowed"); require(_kyc().getCustomerStatus(_from) == KnowYourCustomer.Status.passed, "sender does not have valid KYC status"); require(_kyc().getCustomerStatus(_to) == KnowYourCustomer.Status.passed, "recipient does not have valid KYC status"); // TODO: // Check user's region // Check amount for transfer limits return STATUS_ALLOWED; } function checkTransferFromAllowed(address _from, address _to, uint256 _amount) external view returns (byte) { return checkTransferAllowed(_from, _to, _amount); } function checkMintAllowed(address _from, uint256) external view returns (byte) { require(_settings().getMintAllowed(), "global mint must be allowed"); require(_kyc().getCustomerStatus(_from) == KnowYourCustomer.Status.passed, "recipient does not have valid KYC status"); return STATUS_ALLOWED; } function checkBurnAllowed(address _from, uint256) external view returns (byte) { require(_settings().getBurnAllowed(), "global burn must be allowed"); require(_kyc().getCustomerStatus(_from) == KnowYourCustomer.Status.passed, "sender does not have valid KYC status"); return STATUS_ALLOWED; } function _settings() private view returns (TokenSettings) { return TokenSettings(services.getService("token/settings")); } function _kyc() private view returns (KnowYourCustomer) { return KnowYourCustomer(services.getService("validators/kyc")); } } contract BaRA is BaseSecurityToken, CustodianUpgradeable, ServiceDiscovery { uint public limit = 400 * 1e6; string public name = "Banksia BioPharm Security Token"; string public symbol = "BaRA"; uint8 public decimals = 0; constructor(address _custodian, ServiceRegistry _services, string memory _name, string memory _symbol, uint _limit) public CustodianUpgradeable(_custodian) ServiceDiscovery(_services) { name = _name; symbol = _symbol; limit = _limit; } function mint(address _to, uint _amount) public onlyCustodian { require(_amount != 0, "check amount to mint"); require(super.totalSupply() + _amount <= limit, "check total supply after mint"); BaseSecurityToken._mint(_to, _amount); } function checkTransferAllowed (address _from, address _to, uint256 _amount) public view returns (byte) { return _controller().checkTransferAllowed(_from, _to, _amount); } function checkTransferFromAllowed (address _from, address _to, uint256 _amount) public view returns (byte) { return _controller().checkTransferFromAllowed(_from, _to, _amount); } function checkMintAllowed (address _from, uint256 _amount) public view returns (byte) { return _controller().checkMintAllowed(_from, _amount); } function checkBurnAllowed (address _from, uint256 _amount) public view returns (byte) { return _controller().checkBurnAllowed(_from, _amount); } function _controller() private view returns (TokenController) { return TokenController(services.getService("token/controller")); } }