// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)pragmasolidity ^0.8.0;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in// construction, since the code is only stored at the end of the// constructor execution.uint256 size;
assembly {
size :=extcodesize(account)
}
return size >0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
require(address(this).balance>= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytesmemory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytesmemory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/functionverifyCallResult(bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalpurereturns (bytesmemory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
Contract Source Code
File 2 of 9: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 3 of 9: ERC777.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC777/ERC777.sol)pragmasolidity ^0.8.0;import"./IERC777.sol";
import"./IERC777Recipient.sol";
import"./IERC777Sender.sol";
import"./IERC20.sol";
import"./Address.sol";
import"./Context.sol";
import"./IERC1820Registry.sol";
/**
* @dev Implementation of the {IERC777} 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}.
*
* Support for ERC20 is included in this contract, as specified by the EIP: both
* the ERC777 and ERC20 interfaces can be safely used when interacting with it.
* Both {IERC777-Sent} and {IERC20-Transfer} events are emitted on token
* movements.
*
* Additionally, the {IERC777-granularity} value is hard-coded to `1`, meaning that there
* are no special restrictions in the amount of tokens that created, moved, or
* destroyed. This makes integration with ERC20 applications seamless.
*/contractERC777isContext, IERC777, IERC20{
usingAddressforaddress;
IERC1820Registry internalconstant _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
mapping(address=>uint256) private _balances;
uint256private _totalSupply;
stringprivate _name;
stringprivate _symbol;
bytes32privateconstant _TOKENS_SENDER_INTERFACE_HASH =keccak256("ERC777TokensSender");
bytes32privateconstant _TOKENS_RECIPIENT_INTERFACE_HASH =keccak256("ERC777TokensRecipient");
// This isn't ever read from - it's only used to respond to the defaultOperators query.address[] private _defaultOperatorsArray;
// Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators).mapping(address=>bool) private _defaultOperators;
// For each account, a mapping of its operators and revoked default operators.mapping(address=>mapping(address=>bool)) private _operators;
mapping(address=>mapping(address=>bool)) private _revokedDefaultOperators;
// ERC20-allowancesmapping(address=>mapping(address=>uint256)) private _allowances;
/**
* @dev `defaultOperators` may be an empty array.
*/constructor(stringmemory name_,
stringmemory symbol_,
address[] memory defaultOperators_
) {
_name = name_;
_symbol = symbol_;
_defaultOperatorsArray = defaultOperators_;
for (uint256 i =0; i < defaultOperators_.length; i++) {
_defaultOperators[defaultOperators_[i]] =true;
}
// register interfaces
_ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this));
_ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this));
}
/**
* @dev See {IERC777-name}.
*/functionname() publicviewvirtualoverridereturns (stringmemory) {
return _name;
}
/**
* @dev See {IERC777-symbol}.
*/functionsymbol() publicviewvirtualoverridereturns (stringmemory) {
return _symbol;
}
/**
* @dev See {ERC20-decimals}.
*
* Always returns 18, as per the
* [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility).
*/functiondecimals() publicpurevirtualreturns (uint8) {
return18;
}
/**
* @dev See {IERC777-granularity}.
*
* This implementation always returns `1`.
*/functiongranularity() publicviewvirtualoverridereturns (uint256) {
return1;
}
/**
* @dev See {IERC777-totalSupply}.
*/functiontotalSupply() publicviewvirtualoverride(IERC20, IERC777) returns (uint256) {
return _totalSupply;
}
/**
* @dev Returns the amount of tokens owned by an account (`tokenHolder`).
*/functionbalanceOf(address tokenHolder) publicviewvirtualoverride(IERC20, IERC777) returns (uint256) {
return _balances[tokenHolder];
}
/**
* @dev See {IERC777-send}.
*
* Also emits a {IERC20-Transfer} event for ERC20 compatibility.
*/functionsend(address recipient,
uint256 amount,
bytesmemory data
) publicvirtualoverride{
_send(_msgSender(), recipient, amount, data, "", true);
}
/**
* @dev See {IERC20-transfer}.
*
* Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient}
* interface if it is a contract.
*
* Also emits a {Sent} event.
*/functiontransfer(address recipient, uint256 amount) publicvirtualoverridereturns (bool) {
require(recipient !=address(0), "ERC777: transfer to the zero address");
addressfrom= _msgSender();
_callTokensToSend(from, from, recipient, amount, "", "");
_move(from, from, recipient, amount, "", "");
_callTokensReceived(from, from, recipient, amount, "", "", false);
returntrue;
}
/**
* @dev See {IERC777-burn}.
*
* Also emits a {IERC20-Transfer} event for ERC20 compatibility.
*/functionburn(uint256 amount, bytesmemory data) publicvirtualoverride{
_burn(_msgSender(), amount, data, "");
}
/**
* @dev See {IERC777-isOperatorFor}.
*/functionisOperatorFor(address operator, address tokenHolder) publicviewvirtualoverridereturns (bool) {
return
operator == tokenHolder ||
(_defaultOperators[operator] &&!_revokedDefaultOperators[tokenHolder][operator]) ||
_operators[tokenHolder][operator];
}
/**
* @dev See {IERC777-authorizeOperator}.
*/functionauthorizeOperator(address operator) publicvirtualoverride{
require(_msgSender() != operator, "ERC777: authorizing self as operator");
if (_defaultOperators[operator]) {
delete _revokedDefaultOperators[_msgSender()][operator];
} else {
_operators[_msgSender()][operator] =true;
}
emit AuthorizedOperator(operator, _msgSender());
}
/**
* @dev See {IERC777-revokeOperator}.
*/functionrevokeOperator(address operator) publicvirtualoverride{
require(operator != _msgSender(), "ERC777: revoking self as operator");
if (_defaultOperators[operator]) {
_revokedDefaultOperators[_msgSender()][operator] =true;
} else {
delete _operators[_msgSender()][operator];
}
emit RevokedOperator(operator, _msgSender());
}
/**
* @dev See {IERC777-defaultOperators}.
*/functiondefaultOperators() publicviewvirtualoverridereturns (address[] memory) {
return _defaultOperatorsArray;
}
/**
* @dev See {IERC777-operatorSend}.
*
* Emits {Sent} and {IERC20-Transfer} events.
*/functionoperatorSend(address sender,
address recipient,
uint256 amount,
bytesmemory data,
bytesmemory operatorData
) publicvirtualoverride{
require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder");
_send(sender, recipient, amount, data, operatorData, true);
}
/**
* @dev See {IERC777-operatorBurn}.
*
* Emits {Burned} and {IERC20-Transfer} events.
*/functionoperatorBurn(address account,
uint256 amount,
bytesmemory data,
bytesmemory operatorData
) publicvirtualoverride{
require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder");
_burn(account, amount, data, operatorData);
}
/**
* @dev See {IERC20-allowance}.
*
* Note that operator and allowance concepts are orthogonal: operators may
* not have allowance, and accounts with allowance may not be operators
* themselves.
*/functionallowance(address holder, address spender) publicviewvirtualoverridereturns (uint256) {
return _allowances[holder][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Note that accounts cannot have allowance issued by their operators.
*/functionapprove(address spender, uint256 value) publicvirtualoverridereturns (bool) {
address holder = _msgSender();
_approve(holder, spender, value);
returntrue;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Note that operator and allowance concepts are orthogonal: operators cannot
* call `transferFrom` (unless they have allowance), and accounts with
* allowance cannot call `operatorSend` (unless they are operators).
*
* Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events.
*/functiontransferFrom(address holder,
address recipient,
uint256 amount
) publicvirtualoverridereturns (bool) {
require(recipient !=address(0), "ERC777: transfer to the zero address");
require(holder !=address(0), "ERC777: transfer from the zero address");
address spender = _msgSender();
_callTokensToSend(spender, holder, recipient, amount, "", "");
_move(spender, holder, recipient, amount, "", "");
uint256 currentAllowance = _allowances[holder][spender];
require(currentAllowance >= amount, "ERC777: transfer amount exceeds allowance");
_approve(holder, spender, currentAllowance - amount);
_callTokensReceived(spender, holder, recipient, amount, "", "", false);
returntrue;
}
/**
* @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* If a send hook is registered for `account`, the corresponding function
* will be called with `operator`, `data` and `operatorData`.
*
* See {IERC777Sender} and {IERC777Recipient}.
*
* Emits {Minted} and {IERC20-Transfer} events.
*
* Requirements
*
* - `account` cannot be the zero address.
* - if `account` is a contract, it must implement the {IERC777Recipient}
* interface.
*/function_mint(address account,
uint256 amount,
bytesmemory userData,
bytesmemory operatorData
) internalvirtual{
_mint(account, amount, userData, operatorData, true);
}
/**
* @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* If `requireReceptionAck` is set to true, and if a send hook is
* registered for `account`, the corresponding function will be called with
* `operator`, `data` and `operatorData`.
*
* See {IERC777Sender} and {IERC777Recipient}.
*
* Emits {Minted} and {IERC20-Transfer} events.
*
* Requirements
*
* - `account` cannot be the zero address.
* - if `account` is a contract, it must implement the {IERC777Recipient}
* interface.
*/function_mint(address account,
uint256 amount,
bytesmemory userData,
bytesmemory operatorData,
bool requireReceptionAck
) internalvirtual{
require(account !=address(0), "ERC777: mint to the zero address");
address operator = _msgSender();
_beforeTokenTransfer(operator, address(0), account, amount);
// Update state variables
_totalSupply += amount;
_balances[account] += amount;
_callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck);
emit Minted(operator, account, amount, userData, operatorData);
emit Transfer(address(0), account, amount);
}
/**
* @dev Send tokens
* @param from address token holder address
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param userData bytes extra information provided by the token holder (if any)
* @param operatorData bytes extra information provided by the operator (if any)
* @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient
*/function_send(addressfrom,
address to,
uint256 amount,
bytesmemory userData,
bytesmemory operatorData,
bool requireReceptionAck
) internalvirtual{
require(from!=address(0), "ERC777: send from the zero address");
require(to !=address(0), "ERC777: send to the zero address");
address operator = _msgSender();
_callTokensToSend(operator, from, to, amount, userData, operatorData);
_move(operator, from, to, amount, userData, operatorData);
_callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);
}
/**
* @dev Burn tokens
* @param from address token holder address
* @param amount uint256 amount of tokens to burn
* @param data bytes extra information provided by the token holder
* @param operatorData bytes extra information provided by the operator (if any)
*/function_burn(addressfrom,
uint256 amount,
bytesmemory data,
bytesmemory operatorData
) internalvirtual{
require(from!=address(0), "ERC777: burn from the zero address");
address operator = _msgSender();
_callTokensToSend(operator, from, address(0), amount, data, operatorData);
_beforeTokenTransfer(operator, from, address(0), amount);
// Update state variablesuint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC777: burn amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_totalSupply -= amount;
emit Burned(operator, from, amount, data, operatorData);
emit Transfer(from, address(0), amount);
}
function_move(address operator,
addressfrom,
address to,
uint256 amount,
bytesmemory userData,
bytesmemory operatorData
) private{
_beforeTokenTransfer(operator, from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC777: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Sent(operator, from, to, amount, userData, operatorData);
emit Transfer(from, to, amount);
}
/**
* @dev See {ERC20-_approve}.
*
* Note that accounts cannot have allowance issued by their operators.
*/function_approve(address holder,
address spender,
uint256 value
) internal{
require(holder !=address(0), "ERC777: approve from the zero address");
require(spender !=address(0), "ERC777: approve to the zero address");
_allowances[holder][spender] = value;
emit Approval(holder, spender, value);
}
/**
* @dev Call from.tokensToSend() if the interface is registered
* @param operator address operator requesting the transfer
* @param from address token holder address
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param userData bytes extra information provided by the token holder (if any)
* @param operatorData bytes extra information provided by the operator (if any)
*/function_callTokensToSend(address operator,
addressfrom,
address to,
uint256 amount,
bytesmemory userData,
bytesmemory operatorData
) private{
address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH);
if (implementer !=address(0)) {
IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
}
}
/**
* @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but
* tokensReceived() was not registered for the recipient
* @param operator address operator requesting the transfer
* @param from address token holder address
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param userData bytes extra information provided by the token holder (if any)
* @param operatorData bytes extra information provided by the operator (if any)
* @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient
*/function_callTokensReceived(address operator,
addressfrom,
address to,
uint256 amount,
bytesmemory userData,
bytesmemory operatorData,
bool requireReceptionAck
) private{
address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH);
if (implementer !=address(0)) {
IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);
} elseif (requireReceptionAck) {
require((!to.isContract() || to==address(this)), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient");
}
}
/**
* @dev Hook that is called before any token transfer. This includes
* calls to {send}, {transfer}, {operatorSend}, 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].
*/function_beforeTokenTransfer(address operator,
addressfrom,
address to,
uint256 amount
) internalvirtual{}
}
Contract Source Code
File 4 of 9: IERC1820Registry.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC1820Registry.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the global ERC1820 Registry, as defined in the
* https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
* implementers for interfaces in this registry, as well as query support.
*
* Implementers may be shared by multiple accounts, and can also implement more
* than a single interface for each account. Contracts can implement interfaces
* for themselves, but externally-owned accounts (EOA) must delegate this to a
* contract.
*
* {IERC165} interfaces can also be queried via the registry.
*
* For an in-depth explanation and source code analysis, see the EIP text.
*/interfaceIERC1820Registry{
/**
* @dev Sets `newManager` as the manager for `account`. A manager of an
* account is able to set interface implementers for it.
*
* By default, each account is its own manager. Passing a value of `0x0` in
* `newManager` will reset the manager to this initial state.
*
* Emits a {ManagerChanged} event.
*
* Requirements:
*
* - the caller must be the current manager for `account`.
*/functionsetManager(address account, address newManager) external;
/**
* @dev Returns the manager for `account`.
*
* See {setManager}.
*/functiongetManager(address account) externalviewreturns (address);
/**
* @dev Sets the `implementer` contract as ``account``'s implementer for
* `interfaceHash`.
*
* `account` being the zero address is an alias for the caller's address.
* The zero address can also be used in `implementer` to remove an old one.
*
* See {interfaceHash} to learn how these are created.
*
* Emits an {InterfaceImplementerSet} event.
*
* Requirements:
*
* - the caller must be the current manager for `account`.
* - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
* end in 28 zeroes).
* - `implementer` must implement {IERC1820Implementer} and return true when
* queried for support, unless `implementer` is the caller. See
* {IERC1820Implementer-canImplementInterfaceForAddress}.
*/functionsetInterfaceImplementer(address account,
bytes32 _interfaceHash,
address implementer
) external;
/**
* @dev Returns the implementer of `interfaceHash` for `account`. If no such
* implementer is registered, returns the zero address.
*
* If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
* zeroes), `account` will be queried for support of it.
*
* `account` being the zero address is an alias for the caller's address.
*/functiongetInterfaceImplementer(address account, bytes32 _interfaceHash) externalviewreturns (address);
/**
* @dev Returns the interface hash for an `interfaceName`, as defined in the
* corresponding
* https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
*/functioninterfaceHash(stringcalldata interfaceName) externalpurereturns (bytes32);
/**
* @notice Updates the cache with whether the contract implements an ERC165 interface or not.
* @param account Address of the contract for which to update the cache.
* @param interfaceId ERC165 interface for which to update the cache.
*/functionupdateERC165Cache(address account, bytes4 interfaceId) external;
/**
* @notice Checks whether a contract implements an ERC165 interface or not.
* If the result is not cached a direct lookup on the contract address is performed.
* If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
* {updateERC165Cache} with the contract address.
* @param account Address of the contract to check.
* @param interfaceId ERC165 interface to check.
* @return True if `account` implements `interfaceId`, false otherwise.
*/functionimplementsERC165Interface(address account, bytes4 interfaceId) externalviewreturns (bool);
/**
* @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
* @param account Address of the contract to check.
* @param interfaceId ERC165 interface to check.
* @return True if `account` implements `interfaceId`, false otherwise.
*/functionimplementsERC165InterfaceNoCache(address account, bytes4 interfaceId) externalviewreturns (bool);
eventInterfaceImplementerSet(addressindexed account, bytes32indexed interfaceHash, addressindexed implementer);
eventManagerChanged(addressindexed account, addressindexed newManager);
}
Contract Source Code
File 5 of 9: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @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.
*
* IMPORTANT: 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);
}
Contract Source Code
File 6 of 9: IERC777.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC777Token standard as defined in the EIP.
*
* This contract uses the
* https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let
* token holders and recipients react to token movements by using setting implementers
* for the associated interfaces in said registry. See {IERC1820Registry} and
* {ERC1820Implementer}.
*/interfaceIERC777{
/**
* @dev Returns the name of the token.
*/functionname() externalviewreturns (stringmemory);
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/functionsymbol() externalviewreturns (stringmemory);
/**
* @dev Returns the smallest part of the token that is not divisible. This
* means all token operations (creation, movement and destruction) must have
* amounts that are a multiple of this number.
*
* For most token contracts, this value will equal 1.
*/functiongranularity() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by an account (`owner`).
*/functionbalanceOf(address owner) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* If send or receive hooks are registered for the caller and `recipient`,
* the corresponding functions will be called with `data` and empty
* `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
*
* Emits a {Sent} event.
*
* Requirements
*
* - the caller must have at least `amount` tokens.
* - `recipient` cannot be the zero address.
* - if `recipient` is a contract, it must implement the {IERC777Recipient}
* interface.
*/functionsend(address recipient,
uint256 amount,
bytescalldata data
) external;
/**
* @dev Destroys `amount` tokens from the caller's account, reducing the
* total supply.
*
* If a send hook is registered for the caller, the corresponding function
* will be called with `data` and empty `operatorData`. See {IERC777Sender}.
*
* Emits a {Burned} event.
*
* Requirements
*
* - the caller must have at least `amount` tokens.
*/functionburn(uint256 amount, bytescalldata data) external;
/**
* @dev Returns true if an account is an operator of `tokenHolder`.
* Operators can send and burn tokens on behalf of their owners. All
* accounts are their own operator.
*
* See {operatorSend} and {operatorBurn}.
*/functionisOperatorFor(address operator, address tokenHolder) externalviewreturns (bool);
/**
* @dev Make an account an operator of the caller.
*
* See {isOperatorFor}.
*
* Emits an {AuthorizedOperator} event.
*
* Requirements
*
* - `operator` cannot be calling address.
*/functionauthorizeOperator(address operator) external;
/**
* @dev Revoke an account's operator status for the caller.
*
* See {isOperatorFor} and {defaultOperators}.
*
* Emits a {RevokedOperator} event.
*
* Requirements
*
* - `operator` cannot be calling address.
*/functionrevokeOperator(address operator) external;
/**
* @dev Returns the list of default operators. These accounts are operators
* for all token holders, even if {authorizeOperator} was never called on
* them.
*
* This list is immutable, but individual holders may revoke these via
* {revokeOperator}, in which case {isOperatorFor} will return false.
*/functiondefaultOperators() externalviewreturns (address[] memory);
/**
* @dev Moves `amount` tokens from `sender` to `recipient`. The caller must
* be an operator of `sender`.
*
* If send or receive hooks are registered for `sender` and `recipient`,
* the corresponding functions will be called with `data` and
* `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
*
* Emits a {Sent} event.
*
* Requirements
*
* - `sender` cannot be the zero address.
* - `sender` must have at least `amount` tokens.
* - the caller must be an operator for `sender`.
* - `recipient` cannot be the zero address.
* - if `recipient` is a contract, it must implement the {IERC777Recipient}
* interface.
*/functionoperatorSend(address sender,
address recipient,
uint256 amount,
bytescalldata data,
bytescalldata operatorData
) external;
/**
* @dev Destroys `amount` tokens from `account`, reducing the total supply.
* The caller must be an operator of `account`.
*
* If a send hook is registered for `account`, the corresponding function
* will be called with `data` and `operatorData`. See {IERC777Sender}.
*
* Emits a {Burned} event.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
* - the caller must be an operator for `account`.
*/functionoperatorBurn(address account,
uint256 amount,
bytescalldata data,
bytescalldata operatorData
) external;
eventSent(addressindexed operator,
addressindexedfrom,
addressindexed to,
uint256 amount,
bytes data,
bytes operatorData
);
eventMinted(addressindexed operator, addressindexed to, uint256 amount, bytes data, bytes operatorData);
eventBurned(addressindexed operator, addressindexedfrom, uint256 amount, bytes data, bytes operatorData);
eventAuthorizedOperator(addressindexed operator, addressindexed tokenHolder);
eventRevokedOperator(addressindexed operator, addressindexed tokenHolder);
}
Contract Source Code
File 7 of 9: IERC777Recipient.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Recipient.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.
*
* Accounts can be notified of {IERC777} tokens being sent to them by having a
* contract implement this interface (contract holders can be their own
* implementer) and registering it on the
* https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
*
* See {IERC1820Registry} and {ERC1820Implementer}.
*/interfaceIERC777Recipient{
/**
* @dev Called by an {IERC777} token contract whenever tokens are being
* moved or created into a registered account (`to`). The type of operation
* is conveyed by `from` being the zero address or not.
*
* This call occurs _after_ the token contract's state is updated, so
* {IERC777-balanceOf}, etc., can be used to query the post-operation state.
*
* This function may revert to prevent the operation from being executed.
*/functiontokensReceived(address operator,
addressfrom,
address to,
uint256 amount,
bytescalldata userData,
bytescalldata operatorData
) external;
}
Contract Source Code
File 8 of 9: IERC777Sender.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Sender.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC777TokensSender standard as defined in the EIP.
*
* {IERC777} Token holders can be notified of operations performed on their
* tokens by having a contract implement this interface (contract holders can be
* their own implementer) and registering it on the
* https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
*
* See {IERC1820Registry} and {ERC1820Implementer}.
*/interfaceIERC777Sender{
/**
* @dev Called by an {IERC777} token contract whenever a registered holder's
* (`from`) tokens are about to be moved or destroyed. The type of operation
* is conveyed by `to` being the zero address or not.
*
* This call occurs _before_ the token contract's state is updated, so
* {IERC777-balanceOf}, etc., can be used to query the pre-operation state.
*
* This function may revert to prevent the operation from being executed.
*/functiontokensToSend(address operator,
addressfrom,
address to,
uint256 amount,
bytescalldata userData,
bytescalldata operatorData
) external;
}
Contract Source Code
File 9 of 9: Lockable.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.12;import"./ERC777.sol";
//----------------------------------------------------------------------------------contractLockedCoinisERC777{
Lockable public coin; //MASTER COINstringpublic uri="https://www.epigeon.org/lockable";
mapping(address=>mapping(bytes32=>uint256)) private _lockedAmmountByHash;
mapping(address=>mapping(bytes32=>uint256)) private _lockedAmmountByHashOfSender;
mapping(address=>mapping(bytes32=>address)) private _holderToLockerByHash;
constructor () ERC777("LockedCoin", "LdC", newaddress[](0)) {
coin = Lockable(msg.sender);
}
functionbalanceByHash(address to, bytes32 hash) publicviewreturns (uint256 amount){
return _lockedAmmountByHash[to][hash];
}
functionburnByHash(address to, bytes32 hash) publicreturns (uint256 amount){
require(msg.sender==address(coin), "Only available for master coin");
require(_lockedAmmountByHash[to][hash] >0, "No amount locked with given hash");
uint256 lockedValue = _lockedAmmountByHash[to][hash];
_burn(to, lockedValue, "", "");
_lockedAmmountByHash[to][hash] =0;
_lockedAmmountByHashOfSender[_holderToLockerByHash[to][hash]][hash] =0;
_holderToLockerByHash[to][hash] =address(0);
return lockedValue;
}
functionburnByHash(addressfrom, address to, bytes32 hash) publicreturns (uint256 amount){
require(msg.sender==address(coin), "Only available for master coin");
require(_lockedAmmountByHash[to][hash] >0, "No amount locked with given hash");
require(_holderToLockerByHash[to][hash] ==from, "Not locked by this address");
uint256 lockedValue = _lockedAmmountByHash[to][hash];
_burn(to, lockedValue, "", "");
_lockedAmmountByHash[to][hash] =0;
_lockedAmmountByHashOfSender[from][hash] =0;
_holderToLockerByHash[to][hash] =address(0);
return lockedValue;
}
functiongetOwner() publicviewreturns (address owner) {
return coin.getOwner();
}
functionlockerOf(address to, bytes32 hash) publicviewreturns (addressfrom){
return _holderToLockerByHash[to][hash];
}
functionlockedAmount(addressfrom, bytes32 hash) publicviewreturns (uint256 amount){
return _lockedAmmountByHashOfSender[from][hash];
}
functionmintByHash(address to, uint256 amount, bytes32 hash, addressfrom) public{
require(msg.sender==address(coin), "Only available for master coin");
require(_lockedAmmountByHash[to][hash] ==0, "Amount locked with the same hash");
_mint(to, amount, "", "");
_lockedAmmountByHash[to][hash] = amount;
_lockedAmmountByHashOfSender[from][hash] = amount;
_holderToLockerByHash[to][hash] =from;
}
functionreclaim(address to, stringmemory unlockerPhrase) public{
bytes32 hash =keccak256(bytes(unlockerPhrase));
require(_holderToLockerByHash[to][hash] ==msg.sender, "Not locked by this address");
burnByHash(msg.sender, to, hash);
coin.reclaimByLockedCoinContract(msg.sender, to, hash);
}
functionsetUri(stringmemory url) public{
require(msg.sender== coin.getOwner());
uri = url;
}
functionunlock(stringmemory unlockerPhrase) public{
bytes32 hash =keccak256(bytes(unlockerPhrase));
coin.unlockByLockedCoinContract(msg.sender, hash);
}
//TRANSFERS ONLY ALLOWED FROM MASTER COINfunction_beforeTokenTransfer(address operator, addressfrom, address to, uint256 amount) internaloverride{
require(msg.sender==address(coin), "Unlock to transferable coin");
super._beforeTokenTransfer(operator, from, to, amount);
}
}
//----------------------------------------------------------------------------------contractLockableisERC777{
LockedCoin public lockedCoin;
addressprivate _owner;
uint256public lockedSupply;
mapping(address=>uint256) private _lockedBalances;
constructor () ERC777("Lockable", "LC", newaddress[](0)) {
_owner =msg.sender;
_mint(msg.sender, 10000000*10**18, "", "");
lockedCoin =new LockedCoin();
}
functiongetOwner() publicviewreturns (address owner) {
return _owner;
}
functionlock(address to, uint256 amount, bytes32 hash) public{
uint256 fromBalance = balanceOf(msg.sender);
require(fromBalance >= amount, "ERC777: amount exceeds balance");
lockedCoin.mintByHash(to, amount, hash, msg.sender);
_send(msg.sender, address(this), amount, "", "", false);
lockedSupply += amount;
_lockedBalances[to] += amount;
require(lockedSupply >= amount, "Math is not ok");
require(_lockedBalances[to] == lockedCoin.balanceOf(to), "Balances did not sync");
}
functionlockedAmount(addressfrom, bytes32 hash) publicviewreturns (uint256 amount){
return lockedCoin.lockedAmount(from, hash);
}
functionlockedBalanceOf(address account) publicviewreturns (uint256){
return _lockedBalances[account];
}
functionoperatorLock(addressfrom, address to, uint256 amount, bytes32 hash, bytesmemory data, bytesmemory operatorData) external{
require(isOperatorFor(_msgSender(), from), "ERC777: caller is not an operator for holder");
uint256 fromBalance = balanceOf(from);
require(fromBalance >= amount, "ERC777: amount exceeds balance");
lockedCoin.mintByHash(to, amount, hash, from);
_send(from, address(this), amount, data, operatorData, true);
lockedSupply += amount;
_lockedBalances[to] += amount;
require(lockedSupply >= amount, "Math is not ok");
require(_lockedBalances[to] == lockedCoin.balanceOf(to), "Balances did not sync");
}
functionoperatorReclaim(addressfrom, address to, stringmemory unlockerPhrase, bytesmemory data, bytesmemory operatorData) external{
bytes32 hash =keccak256(bytes(unlockerPhrase));
require(isOperatorFor(_msgSender(), from), "ERC777: caller is not an operator for holder");
require(msg.sender!=from);
uint256 amount = lockedCoin.burnByHash(from, to, hash);
require(lockedSupply >= amount, "Balances are not in sync");
require(_lockedBalances[to] >= amount, "Balances are not in sync");
_send(address(this), from, amount, data, operatorData, true);
lockedSupply -= amount;
_lockedBalances[to] -= amount;
require(_lockedBalances[to] == lockedCoin.balanceOf(to), "Balances did not sync");
}
functionoperatorUnlock(address to, stringmemory unlockerPhrase, bytesmemory data, bytesmemory operatorData) external{
bytes32 hash =keccak256(bytes(unlockerPhrase));
require(isOperatorFor(_msgSender(), lockedCoin.lockerOf(to, hash)), "ERC777: caller is not an operator for holder");
uint256 amount = lockedCoin.burnByHash(to, hash);
require(lockedSupply >= amount, "Balances are not in sync");
require(_lockedBalances[to] >= amount, "Balances are not in sync");
_send(address(this), to, amount, data, operatorData, true);
lockedSupply -= amount;
_lockedBalances[to] -= amount;
require(_lockedBalances[to] == lockedCoin.balanceOf(to), "Balances did not sync");
}
functionreclaim(address to, stringmemory unlockerPhrase) public{
bytes32 hash =keccak256(bytes(unlockerPhrase));
uint256 amount = lockedCoin.burnByHash(msg.sender, to, hash);
require(lockedSupply >= amount, "Balances are not in sync");
require(_lockedBalances[to] >= amount, "Balances are not in sync");
_send(address(this), msg.sender, amount, "", "", false);
lockedSupply -= amount;
_lockedBalances[to] -= amount;
require(_lockedBalances[to] == lockedCoin.balanceOf(to), "Balances did not sync");
}
functionreclaimByLockedCoinContract(addressfrom, address to, bytes32 hash) public{
require(msg.sender==address(lockedCoin), "Only avaliable for the locked coin contract");
uint256 amount = lockedCoin.burnByHash(from, to, hash);
require(lockedSupply >= amount, "Balances are not in sync");
require(_lockedBalances[to] >= amount, "Balances are not in sync");
_send(address(this), from, amount, "", "", false);
lockedSupply -= amount;
_lockedBalances[to] -= amount;
require(_lockedBalances[to] == lockedCoin.balanceOf(to), "Balances did not sync");
}
functionunlock(stringmemory unlockerPhrase) public{
bytes32 hash =keccak256(bytes(unlockerPhrase));
uint256 amount = lockedCoin.burnByHash(msg.sender, hash);
require(lockedSupply >= amount, "Balances are not in sync");
require(_lockedBalances[msg.sender] >= amount, "Balances are not in sync");
_send(address(this), msg.sender, amount, "", "", false);
lockedSupply -= amount;
_lockedBalances[msg.sender] -= amount;
require(_lockedBalances[msg.sender] == lockedCoin.balanceOf(msg.sender), "Balances did not sync");
}
functionunlockByLockedCoinContract(address to, bytes32 hash) public{
require(msg.sender==address(lockedCoin), "Only avaliable for the locked coin contract");
uint256 amount = lockedCoin.burnByHash(to, hash);
require(lockedSupply >= amount, "Balances are not in sync");
require(_lockedBalances[to] >= amount, "Balances are not in sync");
_send(address(this), to, amount, "", "", false);
lockedSupply -= amount;
_lockedBalances[to] -= amount;
require(_lockedBalances[to] == lockedCoin.balanceOf(to), "Balances did not sync");
}
}