// SPDX-License-Identifier: MIT// Copied and adjusted from OpenZeppelin// Adjustments:// - modifications to support ERC-677// - removed unnecessary require statements// - removed GSN Context// - upgraded to 0.8 to drop SafeMath// - let name() and symbol() be implemented by subclass// - infinite allowance support, with 2^255 and above considered infinite// - use upper 32 bits of balance for flags// - add a global settings variablepragmasolidity ^0.8.0;import"./IERC20.sol";
import"./IERC677Receiver.sol";
/**
* @dev Implementation of the `IERC20` interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using `_mint`.
* For a generic mechanism see `ERC20Mintable`.
*
* *For a detailed writeup see our guide [How to implement supply
* mechanisms](https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226).*
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an `Approval` event is emitted on calls to `transferFrom`.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard `decreaseAllowance` and `increaseAllowance`
* functions have been added to mitigate the well-known issues around setting
* allowances. See `IERC20.approve`.
*/abstractcontractERC20FlaggableisIERC20{
// as Documented in /doc/infiniteallowance.md// 0x8000000000000000000000000000000000000000000000000000000000000000uint256constantprivate INFINITE_ALLOWANCE =2**255;
uint256privateconstant FLAGGING_MASK =0xFFFFFFFF00000000000000000000000000000000000000000000000000000000;
// Documentation of flags used by subclasses:// NOTE: flags denote the bit number that is being used and must be smaller than 32// ERC20Draggable: uint8 private constant FLAG_INDEX_VOTED = 1;// ERC20Recoverable: uint8 private constant FLAG_INDEX_CLAIM_PRESENT = 10;// ERCAllowlistable: uint8 private constant FLAG_INDEX_ALLOWLIST = 20;// ERCAllowlistable: uint8 private constant FLAG_INDEX_FORBIDDEN = 21;// ERCAllowlistable: uint8 private constant FLAG_INDEX_POWERLIST = 22;mapping (address=>uint256) private _balances; // upper 32 bits reserved for flagsmapping (address=>mapping (address=>uint256)) private _allowances;
uint256private _totalSupply;
uint8publicoverride decimals;
eventNameChanged(string name, string symbol);
constructor(uint8 _decimals) {
decimals = _decimals;
}
/**
* @dev See `IERC20.totalSupply`.
*/functiontotalSupply() publicviewoverridereturns (uint256) {
return _totalSupply;
}
/**
* @dev See `IERC20.balanceOf`.
*/functionbalanceOf(address account) publicviewoverridereturns (uint256) {
returnuint224 (_balances [account]);
}
functionhasFlag(address account, uint8 number) externalviewreturns (bool) {
return hasFlagInternal(account, number);
}
functionsetFlag(address account, uint8 index, bool value) internal{
uint256 flagMask =1<< (index +224);
uint256 balance = _balances [account];
if ((balance & flagMask == flagMask) != value) {
_balances [account] = balance ^ flagMask;
}
}
functionhasFlagInternal(address account, uint8 number) internalviewreturns (bool) {
uint256 flag =0x1<< (number +224);
return _balances[account] & flag == flag;
}
/**
* @dev See `IERC20.transfer`.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/functiontransfer(address recipient, uint256 amount) publicvirtualoverridereturns (bool) {
_transfer(msg.sender, recipient, amount);
returntrue;
}
/**
* @dev See `IERC20.allowance`.
*/functionallowance(address owner, address spender) externalviewoverridereturns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See `IERC20.approve`.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/functionapprove(address spender, uint256 value) externaloverridereturns (bool) {
_approve(msg.sender, spender, value);
returntrue;
}
/**
* @dev See `IERC20.transferFrom`.
*
* Emits an `Approval` event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of `ERC20`;
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `value`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/functiontransferFrom(address sender, address recipient, uint256 amount) externaloverridereturns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][msg.sender];
if (currentAllowance < INFINITE_ALLOWANCE){
// Only decrease the allowance if it was not set to 'infinite'// Documented in /doc/infiniteallowance.md
_allowances[sender][msg.sender] = currentAllowance - amount;
}
returntrue;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to `transfer`, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a `Transfer` event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/function_transfer(address sender, address recipient, uint256 amount) internalvirtual{
_beforeTokenTransfer(sender, recipient, amount);
decreaseBalance(sender, amount);
increaseBalance(recipient, amount);
emit Transfer(sender, recipient, amount);
}
// ERC-677 functionality, can be useful for swapping and wrapping tokensfunctiontransferAndCall(address recipient, uint amount, bytescalldata data) externalvirtualreturns (bool) {
return transfer (recipient, amount)
&& IERC677Receiver (recipient).onTokenTransfer (msg.sender, amount, data);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a `Transfer` event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/function_mint(address recipient, uint256 amount) internalvirtual{
_beforeTokenTransfer(address(0), recipient, amount);
_totalSupply += amount;
increaseBalance(recipient, amount);
emit Transfer(address(0), recipient, amount);
}
functionincreaseBalance(address recipient, uint256 amount) private{
require(recipient !=address(0x0), "0x0"); // use burn insteaduint256 oldBalance = _balances[recipient];
uint256 newBalance = oldBalance + amount;
require(oldBalance & FLAGGING_MASK == newBalance & FLAGGING_MASK, "overflow");
_balances[recipient] = newBalance;
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a `Transfer` event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/function_burn(address account, uint256 amount) internalvirtual{
_beforeTokenTransfer(account, address(0), amount);
_totalSupply -= amount;
decreaseBalance(account, amount);
emit Transfer(account, address(0), amount);
}
functiondecreaseBalance(address sender, uint256 amount) private{
uint256 oldBalance = _balances[sender];
uint256 newBalance = oldBalance - amount;
require(oldBalance & FLAGGING_MASK == newBalance & FLAGGING_MASK, "underflow");
_balances[sender] = newBalance;
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an `Approval` event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/function_approve(address owner, address spender, uint256 value) internal{
_allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/// solhint-disable-next-line no-empty-blocksfunction_beforeTokenTransfer(addressfrom, address to, uint256 amount) virtualinternal{
// intentionally left blank
}
}
/**
* SPDX-License-Identifier: LicenseRef-Aktionariat
*
* MIT License with Automated License Fee Payments
*
* Copyright (c) 2020 Aktionariat AG (aktionariat.com)
*
* Permission is hereby granted to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - All automated license fee payments integrated into this and related Software
* are preserved.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/pragmasolidity ^0.8.0;import"../ERC20/ERC20Flaggable.sol";
import"./IRecoveryHub.sol";
import"./IRecoverable.sol";
/**
* @title Recoverable
* In case of tokens that represent real-world assets such as shares of a company, one needs a way
* to handle lost private keys. With physical certificates, courts can declare share certificates as
* invalid so the company can issue replacements. Here, we want a solution that does not depend on
* third parties to resolve such cases. Instead, when someone has lost a private key, he can use the
* declareLost function on the recovery hub to post a deposit and claim that the shares assigned to a
* specific address are lost.
* If an attacker trying to claim shares belonging to someone else, they risk losing the deposit
* as it can be claimed at anytime by the rightful owner.
* Furthermore, if "getClaimDeleter" is defined in the subclass, the returned address is allowed to
* delete claims, returning the collateral. This can help to prevent obvious cases of abuse of the claim
* function, e.g. cases of front-running.
* Most functionality is implemented in a shared RecoveryHub.
*/abstractcontractERC20RecoverableisERC20Flaggable, IRecoverable{
uint8privateconstant FLAG_CLAIM_PRESENT =10;
// ERC-20 token that can be used as collateral or 0x0 if disabled
IERC20 public customCollateralAddress;
// Rate the custom collateral currency is multiplied to be valued like one share.uint256public customCollateralRate;
uint256constant CLAIM_PERIOD =180days;
IRecoveryHub publicimmutable recovery;
constructor(IRecoveryHub recoveryHub){
recovery = recoveryHub;
}
/**
* Returns the collateral rate for the given collateral type and 0 if that type
* of collateral is not accepted. By default, only the token itself is accepted at
* a rate of 1:1.
*
* Subclasses should override this method if they want to add additional types of
* collateral.
*/functiongetCollateralRate(IERC20 collateralType) publicoverridevirtualviewreturns (uint256) {
if (address(collateralType) ==address(this)) {
return1;
} elseif (collateralType == customCollateralAddress) {
return customCollateralRate;
} else {
return0;
}
}
functionclaimPeriod() externalpureoverridereturns (uint256){
return CLAIM_PERIOD;
}
/**
* Allows subclasses to set a custom collateral besides the token itself.
* The collateral must be an ERC-20 token that returns true on successful transfers and
* throws an exception or returns false on failure.
* Also, do not forget to multiply the rate in accordance with the number of decimals of the collateral.
* For example, rate should be 7*10**18 for 7 units of a collateral with 18 decimals.
*/function_setCustomClaimCollateral(IERC20 collateral, uint256 rate) internal{
customCollateralAddress = collateral;
if (address(customCollateralAddress) ==address(0)) {
customCollateralRate =0; // disabled
} else {
require(rate >0, "zero");
customCollateralRate = rate;
}
}
functiongetClaimDeleter() virtualpublicviewreturns (address);
functiontransfer(address recipient, uint256 amount) override(ERC20Flaggable, IERC20) virtualpublicreturns (bool) {
require(super.transfer(recipient, amount), "transfer");
if (hasFlagInternal(msg.sender, FLAG_CLAIM_PRESENT)){
recovery.clearClaimFromToken(msg.sender);
}
returntrue;
}
functionnotifyClaimMade(address target) externaloverride{
require(msg.sender==address(recovery), "not recovery");
setFlag(target, FLAG_CLAIM_PRESENT, true);
}
functionnotifyClaimDeleted(address target) externaloverride{
require(msg.sender==address(recovery), "not recovery");
setFlag(target, FLAG_CLAIM_PRESENT, false);
}
functiondeleteClaim(address lostAddress) external{
require(msg.sender== getClaimDeleter(), "not claim deleter");
recovery.deleteClaim(lostAddress);
}
functionrecover(address oldAddress, address newAddress) externaloverride{
require(msg.sender==address(recovery), "not recovery");
_transfer(oldAddress, newAddress, balanceOf(oldAddress));
}
}
Contract Source Code
File 4 of 10: IERC20.sol
/**
* SPDX-License-Identifier: MIT
*
* Copyright (c) 2016-2019 zOS Global Limited
*
*/pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/interfaceIERC20{
// Optional functionsfunctionname() externalviewreturns (stringmemory);
functionsymbol() externalviewreturns (stringmemory);
functiondecimals() externalviewreturns (uint8);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/functiontransfer(address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through `transferFrom`. This is
* zero by default.
*
* This value changes when `approve` or `transferFrom` are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* > 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
*
* Emits an `Approval` event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/functiontransferFrom(address sender, address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to `approve`. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
}
// SPDX-License-Identifier: MIT//// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol//// Modifications:// - Replaced Context._msgSender() with msg.sender// - Made leaner// - Extracted interfacepragmasolidity ^0.8.0;/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/contractOwnable{
addresspublic owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor (address initialOwner) {
owner = initialOwner;
emit OwnershipTransferred(address(0), owner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) externalonlyOwner{
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
modifieronlyOwner() {
require(owner ==msg.sender, "not owner");
_;
}
}
Contract Source Code
File 10 of 10: Shares.sol
/**
* SPDX-License-Identifier: LicenseRef-Aktionariat
*
* MIT License with Automated License Fee Payments
*
* Copyright (c) 2020 Aktionariat AG (aktionariat.com)
*
* Permission is hereby granted to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - All automated license fee payments integrated into this and related Software
* are preserved.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/pragmasolidity ^0.8.0;import"../ERC20/ERC20Named.sol";
import"../ERC20/IERC677Receiver.sol";
import"../recovery/ERC20Recoverable.sol";
import"../shares/IShares.sol";
/**
* @title CompanyName AG Shares
* @author Luzius Meisser, luzius@aktionariat.com
*
* These tokens represent ledger-based securities according to article 973d of the Swiss Code of Obligations.
* This smart contract serves as an ownership registry, enabling the token holders to register them as
* shareholders in the issuer's shareholder registry. This is equivalent to the traditional system
* of having physical share certificates kept at home by the shareholders and a shareholder registry run by
* the company. Just like with physical certificates, the owners of the tokens are the owners of the shares.
* However, in order to exercise their rights (for example receive a dividend), shareholders must register
* themselves. For example, in case the company pays out a dividend to a previous shareholder because
* the current shareholder did not register, the company cannot be held liable for paying the dividend to
* the "wrong" shareholder. In relation to the company, only the registered shareholders count as such.
*/contractSharesisERC20Recoverable, ERC20Named, IShares{
stringpublic terms;
uint256publicoverride totalShares; // total number of shares, maybe not all tokenizeduint256public invalidTokens;
eventAnnouncement(string message);
eventTokensDeclaredInvalid(addressindexed holder, uint256 amount, string message);
eventChangeTerms(string terms);
eventChangeTotalShares(uint256 total);
constructor(stringmemory _symbol,
stringmemory _name,
stringmemory _terms,
uint256 _totalShares,
address _owner,
IRecoveryHub _recoveryHub
)
ERC20Named(_symbol, _name, 0, _owner)
ERC20Recoverable(_recoveryHub)
{
totalShares = _totalShares;
terms = _terms;
invalidTokens =0;
_recoveryHub.setRecoverable(false);
}
functionsetTerms(stringmemory _terms) externalonlyOwner{
terms = _terms;
emit ChangeTerms(_terms);
}
/**
* Declares the number of total shares, including those that have not been tokenized and those
* that are held by the company itself. This number can be substiantially higher than totalSupply()
* in case not all shares have been tokenized. Also, it can be lower than totalSupply() in case some
* tokens have become invalid.
*/functionsetTotalShares(uint256 _newTotalShares) externalonlyOwner() {
require(_newTotalShares >= totalValidSupply(), "below supply");
totalShares = _newTotalShares;
emit ChangeTotalShares(_newTotalShares);
}
/**
* Allows the issuer to make public announcements that are visible on the blockchain.
*/functionannouncement(stringcalldata message) externalonlyOwner() {
emit Announcement(message);
}
/**
* See parent method for collateral requirements.
*/functionsetCustomClaimCollateral(IERC20 collateral, uint256 rate) externalonlyOwner() {
super._setCustomClaimCollateral(collateral, rate);
}
functiongetClaimDeleter() publicoverrideviewreturns (address) {
return owner;
}
/**
* Signals that the indicated tokens have been declared invalid (e.g. by a court ruling in accordance
* with article 973g of the Swiss Code of Obligations) and got detached from
* the underlying shares. Invalid tokens do not carry any shareholder rights any more.
*
* This function is purely declarative. It does not technically immobilize the affected tokens as
* that would give the issuer too much power.
*/functiondeclareInvalid(address holder, uint256 amount, stringcalldata message) externalonlyOwner() {
uint256 holderBalance = balanceOf(holder);
require(amount <= holderBalance, "amount too high");
invalidTokens += amount;
emit TokensDeclaredInvalid(holder, amount, message);
}
/**
* The total number of valid tokens in circulation. In case some tokens have been declared invalid, this
* number might be lower than totalSupply(). Also, it will always be lower than or equal to totalShares().
*/functiontotalValidSupply() publicviewreturns (uint256) {
return totalSupply() - invalidTokens;
}
/**
* Allows the company to tokenize shares and transfer them e.g to the draggable contract and wrap them.
* If these shares are newly created, setTotalShares must be called first in order to adjust the total number of shares.
*/functionmintAndCall(address shareholder, address callee, uint256 amount, bytescalldata data) external{
mint(callee, amount);
require(IERC677Receiver(callee).onTokenTransfer(shareholder, amount, data));
}
functionmint(address target, uint256 amount) publiconlyOwner{
_mint(target, amount);
}
function_mint(address account, uint256 amount) internalvirtualoverride{
require(totalValidSupply() + amount <= totalShares, "total");
super._mint(account, amount);
}
functiontransfer(address to, uint256 value) virtualoverride(ERC20Recoverable, ERC20Flaggable) publicreturns (bool) {
returnsuper.transfer(to, value);
}
/**
* Transfers _amount tokens to the company and burns them.
* The meaning of this operation depends on the circumstances and the fate of the shares does
* not necessarily follow the fate of the tokens. For example, the company itself might call
* this function to implement a formal decision to destroy some of the outstanding shares.
* Also, this function might be called by an owner to return the shares to the company and
* get them back in another form under an according agreement (e.g. printed certificates or
* tokens on a different blockchain). It is not recommended to call this function without
* having agreed with the company on the further fate of the shares in question.
*/functionburn(uint256 _amount) overrideexternal{
_transfer(msg.sender, address(this), _amount);
_burn(address(this), _amount);
}
}