文件 1 的 10:ERC20Flaggable.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./IERC677Receiver.sol";
abstract contract ERC20Flaggable is IERC20 {
uint256 constant private INFINITE_ALLOWANCE = 2**255;
uint256 private constant FLAGGING_MASK = 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
uint8 public override decimals;
event NameChanged(string name, string symbol);
constructor(uint8 _decimals) {
decimals = _decimals;
}
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view override returns (uint256) {
return uint224 (_balances [account]);
}
function hasFlag(address account, uint8 number) external view returns (bool) {
return hasFlagInternal(account, number);
}
function setFlag(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;
}
}
function hasFlagInternal(address account, uint8 number) internal view returns (bool) {
uint256 flag = 0x1 << (number + 224);
return _balances[account] & flag == flag;
}
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
function allowance(address owner, address spender) external view override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 value) external override returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][msg.sender];
if (currentAllowance < INFINITE_ALLOWANCE){
_allowances[sender][msg.sender] = currentAllowance - amount;
}
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
_beforeTokenTransfer(sender, recipient, amount);
decreaseBalance(sender, amount);
increaseBalance(recipient, amount);
emit Transfer(sender, recipient, amount);
}
function transferAndCall(address recipient, uint amount, bytes calldata data) external virtual returns (bool) {
return transfer (recipient, amount)
&& IERC677Receiver (recipient).onTokenTransfer (msg.sender, amount, data);
}
function _mint(address recipient, uint256 amount) internal virtual {
_beforeTokenTransfer(address(0), recipient, amount);
_totalSupply += amount;
increaseBalance(recipient, amount);
emit Transfer(address(0), recipient, amount);
}
function increaseBalance(address recipient, uint256 amount) private {
require(recipient != address(0x0), "0x0");
uint256 oldBalance = _balances[recipient];
uint256 newBalance = oldBalance + amount;
require(oldBalance & FLAGGING_MASK == newBalance & FLAGGING_MASK, "overflow");
_balances[recipient] = newBalance;
}
function _burn(address account, uint256 amount) internal virtual {
_beforeTokenTransfer(account, address(0), amount);
_totalSupply -= amount;
decreaseBalance(account, amount);
emit Transfer(account, address(0), amount);
}
function decreaseBalance(address sender, uint256 amount) private {
uint256 oldBalance = _balances[sender];
uint256 newBalance = oldBalance - amount;
require(oldBalance & FLAGGING_MASK == newBalance & FLAGGING_MASK, "underflow");
_balances[sender] = newBalance;
}
function _approve(address owner, address spender, uint256 value) internal {
_allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _beforeTokenTransfer(address from, address to, uint256 amount) virtual internal {
}
}
文件 2 的 10:ERC20Named.sol
pragma solidity ^0.8.0;
import "./ERC20Flaggable.sol";
import "../utils/Ownable.sol";
contract ERC20Named is ERC20Flaggable, Ownable {
string public override name;
string public override symbol;
constructor(string memory _symbol, string memory _name, uint8 _decimals, address _admin) ERC20Flaggable(_decimals) Ownable(_admin) {
setNameInternal(_symbol, _name);
}
function setName(string memory _symbol, string memory _name) external onlyOwner {
setNameInternal(_symbol, _name);
}
function setNameInternal(string memory _symbol, string memory _name) internal {
symbol = _symbol;
name = _name;
emit NameChanged(_name, _symbol);
}
}
文件 3 的 10:ERC20Recoverable.sol
pragma solidity ^0.8.0;
import "../ERC20/ERC20Flaggable.sol";
import "./IRecoveryHub.sol";
import "./IRecoverable.sol";
abstract contract ERC20Recoverable is ERC20Flaggable, IRecoverable {
uint8 private constant FLAG_CLAIM_PRESENT = 10;
IERC20 public customCollateralAddress;
uint256 public customCollateralRate;
uint256 constant CLAIM_PERIOD = 180 days;
IRecoveryHub public immutable recovery;
constructor(IRecoveryHub recoveryHub){
recovery = recoveryHub;
}
function getCollateralRate(IERC20 collateralType) public override virtual view returns (uint256) {
if (address(collateralType) == address(this)) {
return 1;
} else if (collateralType == customCollateralAddress) {
return customCollateralRate;
} else {
return 0;
}
}
function claimPeriod() external pure override returns (uint256){
return CLAIM_PERIOD;
}
function _setCustomClaimCollateral(IERC20 collateral, uint256 rate) internal {
customCollateralAddress = collateral;
if (address(customCollateralAddress) == address(0)) {
customCollateralRate = 0;
} else {
require(rate > 0, "zero");
customCollateralRate = rate;
}
}
function getClaimDeleter() virtual public view returns (address);
function transfer(address recipient, uint256 amount) override(ERC20Flaggable, IERC20) virtual public returns (bool) {
require(super.transfer(recipient, amount), "transfer");
if (hasFlagInternal(msg.sender, FLAG_CLAIM_PRESENT)){
recovery.clearClaimFromToken(msg.sender);
}
return true;
}
function notifyClaimMade(address target) external override {
require(msg.sender == address(recovery), "not recovery");
setFlag(target, FLAG_CLAIM_PRESENT, true);
}
function notifyClaimDeleted(address target) external override {
require(msg.sender == address(recovery), "not recovery");
setFlag(target, FLAG_CLAIM_PRESENT, false);
}
function deleteClaim(address lostAddress) external {
require(msg.sender == getClaimDeleter(), "not claim deleter");
recovery.deleteClaim(lostAddress);
}
function recover(address oldAddress, address newAddress) external override {
require(msg.sender == address(recovery), "not recovery");
_transfer(oldAddress, newAddress, balanceOf(oldAddress));
}
}
文件 4 的 10:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 5 的 10:IERC677Receiver.sol
pragma solidity ^0.8.0;
interface IERC677Receiver {
function onTokenTransfer(address from, uint256 amount, bytes calldata data) external returns (bool);
}
文件 6 的 10:IRecoverable.sol
pragma solidity ^0.8.0;
import "../ERC20/IERC20.sol";
interface IRecoverable is IERC20{
function claimPeriod() external view returns (uint256);
function notifyClaimMade(address target) external;
function notifyClaimDeleted(address target) external;
function getCollateralRate(IERC20 collateral) external view returns(uint256);
function recover(address oldAddress, address newAddress) external;
}
文件 7 的 10:IRecoveryHub.sol
pragma solidity ^0.8.0;
interface IRecoveryHub {
function setRecoverable(bool flag) external;
function deleteClaim(address target) external;
function clearClaimFromToken(address holder) external;
}
文件 8 的 10:IShares.sol
pragma solidity ^0.8.0;
interface IShares {
function burn(uint256) external;
function totalShares() external view returns (uint256);
}
文件 9 的 10:Ownable.sol
pragma solidity ^0.8.0;
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor (address initialOwner) {
owner = initialOwner;
emit OwnershipTransferred(address(0), owner);
}
function transferOwnership(address newOwner) external onlyOwner {
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
modifier onlyOwner() {
require(owner == msg.sender, "not owner");
_;
}
}
文件 10 的 10:Shares.sol
pragma solidity ^0.8.0;
import "../ERC20/ERC20Named.sol";
import "../ERC20/IERC677Receiver.sol";
import "../recovery/ERC20Recoverable.sol";
import "../shares/IShares.sol";
contract Shares is ERC20Recoverable, ERC20Named, IShares{
string public terms;
uint256 public override totalShares;
uint256 public invalidTokens;
event Announcement(string message);
event TokensDeclaredInvalid(address indexed holder, uint256 amount, string message);
event ChangeTerms(string terms);
event ChangeTotalShares(uint256 total);
constructor(
string memory _symbol,
string memory _name,
string memory _terms,
uint256 _totalShares,
address _owner,
IRecoveryHub _recoveryHub
)
ERC20Named(_symbol, _name, 0, _owner)
ERC20Recoverable(_recoveryHub)
{
totalShares = _totalShares;
terms = _terms;
invalidTokens = 0;
_recoveryHub.setRecoverable(false);
}
function setTerms(string memory _terms) external onlyOwner {
terms = _terms;
emit ChangeTerms(_terms);
}
function setTotalShares(uint256 _newTotalShares) external onlyOwner() {
require(_newTotalShares >= totalValidSupply(), "below supply");
totalShares = _newTotalShares;
emit ChangeTotalShares(_newTotalShares);
}
function announcement(string calldata message) external onlyOwner() {
emit Announcement(message);
}
function setCustomClaimCollateral(IERC20 collateral, uint256 rate) external onlyOwner() {
super._setCustomClaimCollateral(collateral, rate);
}
function getClaimDeleter() public override view returns (address) {
return owner;
}
function declareInvalid(address holder, uint256 amount, string calldata message) external onlyOwner() {
uint256 holderBalance = balanceOf(holder);
require(amount <= holderBalance, "amount too high");
invalidTokens += amount;
emit TokensDeclaredInvalid(holder, amount, message);
}
function totalValidSupply() public view returns (uint256) {
return totalSupply() - invalidTokens;
}
function mintAndCall(address shareholder, address callee, uint256 amount, bytes calldata data) external {
mint(callee, amount);
require(IERC677Receiver(callee).onTokenTransfer(shareholder, amount, data));
}
function mint(address target, uint256 amount) public onlyOwner {
_mint(target, amount);
}
function _mint(address account, uint256 amount) internal virtual override {
require(totalValidSupply() + amount <= totalShares, "total");
super._mint(account, amount);
}
function transfer(address to, uint256 value) virtual override(ERC20Recoverable, ERC20Flaggable) public returns (bool) {
return super.transfer(to, value);
}
function burn(uint256 _amount) override external {
_transfer(msg.sender, address(this), _amount);
_burn(address(this), _amount);
}
}
{
"compilationTarget": {
"src/shares/Shares.sol": "Shares"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_terms","type":"string"},{"internalType":"uint256","name":"_totalShares","type":"uint256"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"contract IRecoveryHub","name":"_recoveryHub","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"Announcement","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"terms","type":"string"}],"name":"ChangeTerms","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"total","type":"uint256"}],"name":"ChangeTotalShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"}],"name":"NameChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holder","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"TokensDeclaredInvalid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"message","type":"string"}],"name":"announcement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"customCollateralAddress","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"customCollateralRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"message","type":"string"}],"name":"declareInvalid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lostAddress","type":"address"}],"name":"deleteClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getClaimDeleter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"collateralType","type":"address"}],"name":"getCollateralRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint8","name":"number","type":"uint8"}],"name":"hasFlag","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"invalidTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"shareholder","type":"address"},{"internalType":"address","name":"callee","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mintAndCall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"notifyClaimDeleted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"notifyClaimMade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oldAddress","type":"address"},{"internalType":"address","name":"newAddress","type":"address"}],"name":"recover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recovery","outputs":[{"internalType":"contract IRecoveryHub","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"collateral","type":"address"},{"internalType":"uint256","name":"rate","type":"uint256"}],"name":"setCustomClaimCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"string","name":"_name","type":"string"}],"name":"setName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_terms","type":"string"}],"name":"setTerms","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newTotalShares","type":"uint256"}],"name":"setTotalShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"terms","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalValidSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transferAndCall","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]