// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
abstract contract DomainAware {
// Mapping of ChainID to domain separators. This is a very gas efficient way
// to not recalculate the domain separator on every call, while still
// automatically detecting ChainID changes.
mapping(uint256 => bytes32) private domainSeparators;
constructor() {
_updateDomainSeparator();
}
function domainName() public virtual view returns (string memory);
function domainVersion() public virtual view returns (string memory);
function generateDomainSeparator() public view returns (bytes32) {
uint256 chainID = _chainID();
// no need for assembly, running very rarely
bytes32 domainSeparatorHash = keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256(bytes(domainName())), // ERC-20 Name
keccak256(bytes(domainVersion())), // Version
chainID,
address(this)
)
);
return domainSeparatorHash;
}
function domainSeparator() external returns (bytes32) {
return _domainSeparator();
}
function _updateDomainSeparator() private returns (bytes32) {
uint256 chainID = _chainID();
bytes32 newDomainSeparator = generateDomainSeparator();
domainSeparators[chainID] = newDomainSeparator;
return newDomainSeparator;
}
// Returns the domain separator, updating it if chainID changes
function _domainSeparator() private returns (bytes32) {
bytes32 currentDomainSeparator = domainSeparators[_chainID()];
if (currentDomainSeparator != 0x00) {
return currentDomainSeparator;
}
return _updateDomainSeparator();
}
function _chainID() internal view returns (uint256) {
uint256 chainID;
assembly {
chainID := chainid()
}
return chainID;
}
}
/**
* @title IERC1400TokensRecipient
* @dev ERC1400TokensRecipient interface
*/
interface IERC1400TokensRecipient {
function canReceive(
bytes calldata payload,
bytes32 partition,
address operator,
address from,
address to,
uint value,
bytes calldata data,
bytes calldata operatorData
) external view returns(bool);
function tokensReceived(
bytes calldata payload,
bytes32 partition,
address operator,
address from,
address to,
uint value,
bytes calldata data,
bytes calldata operatorData
) external;
}
/**
* @title IERC1400TokensSender
* @dev ERC1400TokensSender interface
*/
interface IERC1400TokensSender {
function canTransfer(
bytes calldata payload,
bytes32 partition,
address operator,
address from,
address to,
uint value,
bytes calldata data,
bytes calldata operatorData
) external view returns(bool);
function tokensToTransfer(
bytes calldata payload,
bytes32 partition,
address operator,
address from,
address to,
uint value,
bytes calldata data,
bytes calldata operatorData
) external;
}
/**
* @title IERC1400TokensChecker
* @dev IERC1400TokensChecker interface
*/
interface IERC1400TokensChecker {
// function canTransfer(
// bytes calldata payload,
// address operator,
// address from,
// address to,
// uint256 value,
// bytes calldata data,
// bytes calldata operatorData
// ) external view returns (byte, bytes32);
function canTransferByPartition(
bytes calldata payload,
bytes32 partition,
address operator,
address from,
address to,
uint256 value,
bytes calldata data,
bytes calldata operatorData
) external view returns (bytes1, bytes32, bytes32);
}
/**
* @title IERC1400TokensValidator
* @dev ERC1400TokensValidator interface
*/
interface IERC1400TokensValidator {
/**
* @dev Verify if a token transfer can be executed or not, on the validator's perspective.
* @param token Token address.
* @param payload Payload of the initial transaction.
* @param partition Name of the partition (left empty for ERC20 transfer).
* @param operator Address which triggered the balance decrease (through transfer or redemption).
* @param from Token holder.
* @param to Token recipient for a transfer and 0x for a redemption.
* @param value Number of tokens the token holder balance is decreased by.
* @param data Extra information.
* @param operatorData Extra information, attached by the operator (if any).
* @return 'true' if the token transfer can be validated, 'false' if not.
*/
struct ValidateData {
address token;
bytes payload;
bytes32 partition;
address operator;
address from;
address to;
uint value;
bytes data;
bytes operatorData;
}
function canValidate(ValidateData calldata data) external view returns(bool);
function tokensToValidate(
bytes calldata payload,
bytes32 partition,
address operator,
address from,
address to,
uint value,
bytes calldata data,
bytes calldata operatorData
) external;
}
/**
* @title Roles
* @dev Library for managing addresses assigned to a Role.
*/
library Roles {
struct Role {
mapping (address => bool) bearer;
}
/**
* @dev Give an account access to this role.
*/
function add(Role storage role, address account) internal {
require(!has(role, account), "Roles: account already has role");
role.bearer[account] = true;
}
/**
* @dev Remove an account's access to this role.
*/
function remove(Role storage role, address account) internal {
require(has(role, account), "Roles: account does not have role");
role.bearer[account] = false;
}
/**
* @dev Check if an account has this role.
* @return bool
*/
function has(Role storage role, address account) internal view returns (bool) {
require(account != address(0), "Roles: account is the zero address");
return role.bearer[account];
}
}
/**
* @title MinterRole
* @dev Minters are responsible for minting new tokens.
*/
contract MinterRole {
using Roles for Roles.Role;
event MinterAdded(address indexed account);
event MinterRemoved(address indexed account);
Roles.Role private _minters;
constructor() {
_addMinter(msg.sender);
}
modifier onlyMinter() virtual {
require(isMinter(msg.sender));
_;
}
function isMinter(address account) public view returns (bool) {
return _minters.has(account);
}
function addMinter(address account) external onlyMinter {
_addMinter(account);
}
function removeMinter(address account) external onlyMinter {
_removeMinter(account);
}
function renounceMinter() external {
_removeMinter(msg.sender);
}
function _addMinter(address account) internal {
_minters.add(account);
emit MinterAdded(account);
}
function _removeMinter(address account) internal {
_minters.remove(account);
emit MinterRemoved(account);
}
}
contract ERC1820Implementer {
bytes32 constant ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
mapping(bytes32 => bool) internal _interfaceHashes;
function canImplementInterfaceForAddress(bytes32 interfaceHash, address /*addr*/) // Comments to avoid compilation warnings for unused variables.
external
view
returns(bytes32)
{
if(_interfaceHashes[interfaceHash]) {
return ERC1820_ACCEPT_MAGIC;
} else {
return "";
}
}
function _setInterface(string memory interfaceLabel) internal {
_interfaceHashes[keccak256(abi.encodePacked(interfaceLabel))] = true;
}
}
interface IERC1820Registry {
event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
event ManagerChanged(address indexed account, address indexed newManager);
/**
* @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`.
*/
function setManager(address account, address newManager) external;
/**
* @dev Returns the manager for `account`.
*
* See {setManager}.
*/
function getManager(address account) external view returns (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}.
*/
function setInterfaceImplementer(
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.
*/
function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (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].
*/
function interfaceHash(string calldata interfaceName) external pure returns (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.
*/
function updateERC165Cache(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.
*/
function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (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.
*/
function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
}
contract ERC1820Client {
IERC1820Registry constant ERC1820REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
function setInterfaceImplementation(string memory _interfaceLabel, address _implementation) internal {
bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel));
ERC1820REGISTRY.setInterfaceImplementer(address(this), interfaceHash, _implementation);
}
function interfaceAddr(address addr, string memory _interfaceLabel) internal view returns(address) {
bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel));
return ERC1820REGISTRY.getInterfaceImplementer(addr, interfaceHash);
}
}
abstract contract Context {
function _msgSender() internal view returns (address) {
return msg.sender;
}
}
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() external onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed 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.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (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.
*/
function allowance(address from, address spender) external view returns (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.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` 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.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
/// @title IERC1643 Document Management (part of the ERC1400 Security Token Standards)
/// @dev See https://github.com/SecurityTokenStandard/EIP-Spec
interface IERC1643 {
// Document Management
function getDocument(bytes32 _name) external view returns (string memory, bytes32, uint256);
function setDocument(bytes32 _name, string memory _uri, bytes32 _documentHash) external;
function removeDocument(bytes32 _name) external;
function getAllDocuments() external view returns (bytes32[] memory);
// Document Events
event DocumentRemoved(bytes32 indexed name, string uri, bytes32 documentHash);
event DocumentUpdated(bytes32 indexed name, string uri, bytes32 documentHash);
}
/**
* @title IERC1400 security token standard
* @dev See https://github.com/SecurityTokenStandard/EIP-Spec/blob/master/eip/eip-1400.md
*/
interface IERC1400 is IERC20, IERC1643 {
// ******************* Token Information ********************
function balanceOfByPartition(bytes32 partition, address tokenHolder) external view returns (uint256);
function partitionsOf(address tokenHolder) external view returns (bytes32[] memory);
// *********************** Transfers ************************
function transferWithData(address to, uint256 value, bytes calldata data) external;
function transferFromWithData(address from, address to, uint256 value, bytes calldata data) external;
// *************** Partition Token Transfers ****************
function transferByPartition(bytes32 partition, address to, uint256 value, bytes calldata data) external returns (bytes32);
function operatorTransferByPartition(bytes32 partition, address from, address to, uint256 value, bytes calldata data, bytes calldata operatorData) external returns (bytes32);
function allowanceByPartition(bytes32 partition, address owner, address spender) external view returns (uint256);
// ****************** Controller Operation ******************
function isControllable() external view returns (bool);
// function controllerTransfer(address from, address to, uint256 value, bytes calldata data, bytes calldata operatorData) external; // removed because same action can be achieved with "operatorTransferByPartition"
// function controllerRedeem(address tokenHolder, uint256 value, bytes calldata data, bytes calldata operatorData) external; // removed because same action can be achieved with "operatorRedeemByPartition"
// ****************** Operator Management *******************
function authorizeOperator(address operator) external;
function revokeOperator(address operator) external;
function authorizeOperatorByPartition(bytes32 partition, address operator) external;
function revokeOperatorByPartition(bytes32 partition, address operator) external;
// ****************** Operator Information ******************
function isOperator(address operator, address tokenHolder) external view returns (bool);
function isOperatorForPartition(bytes32 partition, address operator, address tokenHolder) external view returns (bool);
// ********************* Token Issuance *********************
function isIssuable() external view returns (bool);
function issue(address tokenHolder, uint256 value, bytes calldata data) external;
function issueByPartition(bytes32 partition, address tokenHolder, uint256 value, bytes calldata data) external;
// ******************** Token Redemption ********************
function redeem(uint256 value, bytes calldata data) external;
function redeemFrom(address tokenHolder, uint256 value, bytes calldata data) external;
function redeemByPartition(bytes32 partition, uint256 value, bytes calldata data) external;
function operatorRedeemByPartition(bytes32 partition, address tokenHolder, uint256 value, bytes calldata operatorData) external;
event TransferByPartition(
bytes32 indexed fromPartition,
address operator,
address indexed from,
address indexed to,
uint256 value,
bytes data,
bytes operatorData
);
event ChangedPartition(
bytes32 indexed fromPartition,
bytes32 indexed toPartition,
uint256 value
);
// ******************** Operator Events *********************
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
event RevokedOperator(address indexed operator, address indexed tokenHolder);
event AuthorizedOperatorByPartition(bytes32 indexed partition, address indexed operator, address indexed tokenHolder);
event RevokedOperatorByPartition(bytes32 indexed partition, address indexed operator, address indexed tokenHolder);
// ************** Issuance / Redemption Events **************
event Issued(address indexed operator, address indexed to, uint256 value, bytes data);
event Redeemed(address indexed operator, address indexed from, uint256 value, bytes data);
event IssuedByPartition(bytes32 indexed partition, address indexed operator, address indexed to, uint256 value, bytes data, bytes operatorData);
event RedeemedByPartition(bytes32 indexed partition, address indexed operator, address indexed from, uint256 value, bytes operatorData);
}
/**
* Reason codes - ERC-1066
*
* To improve the token holder experience, canTransfer MUST return a reason byte code
* on success or failure based on the ERC-1066 application-specific status codes specified below.
* An implementation can also return arbitrary data as a bytes32 to provide additional
* information not captured by the reason code.
*
* Code Reason
* 0x50 transfer failure
* 0x51 transfer success
* 0x52 insufficient balance
* 0x53 insufficient allowance
* 0x54 transfers halted (contract paused)
* 0x55 funds locked (lockup period)
* 0x56 invalid sender
* 0x57 invalid receiver
* 0x58 invalid operator (transfer agent)
* 0x59
* 0x5a
* 0x5b
* 0x5a
* 0x5b
* 0x5c
* 0x5d
* 0x5e
* 0x5f token meta or info
*
* These codes are being discussed at: https://ethereum-magicians.org/t/erc-1066-ethereum-status-codes-esc/283/24
*/
/**
* @title ERC1400
* @dev ERC1400 logic
*/
contract ERC1400 is IERC20, IERC1400, Ownable, ERC1820Client, ERC1820Implementer, MinterRole, DomainAware, ReentrancyGuard {
// Token
string constant internal ERC1400_INTERFACE_NAME = "ERC1400Token";
string constant internal ERC20_INTERFACE_NAME = "ERC20Token";
// Token extensions
string constant internal ERC1400_TOKENS_CHECKER = "ERC1400TokensChecker";
string constant internal ERC1400_TOKENS_VALIDATOR = "ERC1400TokensValidator";
// User extensions
string constant internal ERC1400_TOKENS_SENDER = "ERC1400TokensSender";
string constant internal ERC1400_TOKENS_RECIPIENT = "ERC1400TokensRecipient";
/************************************* Token description ****************************************/
string internal _name;
string internal _symbol;
uint256 internal _granularity;
uint256 internal _totalSupply;
bool internal _migrated;
/************************************************************************************************/
/**************************************** Token behaviours **************************************/
// Indicate whether the token can still be controlled by operators or not anymore.
bool internal _isControllable;
// Indicate whether the token can still be issued by the issuer or not anymore.
bool internal _isIssuable;
/************************************************************************************************/
/********************************** ERC20 Token mappings ****************************************/
// Mapping from tokenHolder to balance.
mapping(address => uint256) internal _balances;
// Mapping from (tokenHolder, spender) to allowed value.
mapping (address => mapping (address => uint256)) internal _allowed;
/************************************************************************************************/
/**************************************** Documents *********************************************/
struct Doc {
string docURI;
bytes32 docHash;
uint256 timestamp;
}
// Mapping for documents.
mapping(bytes32 => Doc) internal _documents;
mapping(bytes32 => uint256) internal _indexOfDocHashes;
bytes32[] internal _docHashes;
/************************************************************************************************/
/*********************************** Partitions mappings ***************************************/
// List of partitions.
bytes32[] internal _totalPartitions;
// Mapping from partition to their index.
mapping (bytes32 => uint256) internal _indexOfTotalPartitions;
// Mapping from partition to global balance of corresponding partition.
mapping (bytes32 => uint256) internal _totalSupplyByPartition;
// Mapping from tokenHolder to their partitions.
mapping (address => bytes32[]) internal _partitionsOf;
// Mapping from (tokenHolder, partition) to their index.
mapping (address => mapping (bytes32 => uint256)) internal _indexOfPartitionsOf;
// Mapping from (tokenHolder, partition) to balance of corresponding partition.
mapping (address => mapping (bytes32 => uint256)) internal _balanceOfByPartition;
// List of token default partitions (for ERC20 compatibility).
bytes32[] internal _defaultPartitions;
/************************************************************************************************/
/********************************* Global operators mappings ************************************/
// Mapping from (operator, tokenHolder) to authorized status. [TOKEN-HOLDER-SPECIFIC]
mapping(address => mapping(address => bool)) internal _authorizedOperator;
// Array of controllers. [GLOBAL - NOT TOKEN-HOLDER-SPECIFIC]
address[] internal _controllers;
// Mapping from operator to controller status. [GLOBAL - NOT TOKEN-HOLDER-SPECIFIC]
mapping(address => bool) internal _isController;
/************************************************************************************************/
/******************************** Partition operators mappings **********************************/
// Mapping from (partition, tokenHolder, spender) to allowed value. [TOKEN-HOLDER-SPECIFIC]
mapping(bytes32 => mapping (address => mapping (address => uint256))) internal _allowedByPartition;
// Mapping from (tokenHolder, partition, operator) to 'approved for partition' status. [TOKEN-HOLDER-SPECIFIC]
mapping (address => mapping (bytes32 => mapping (address => bool))) internal _authorizedOperatorByPartition;
// Mapping from partition to controllers for the partition. [NOT TOKEN-HOLDER-SPECIFIC]
mapping (bytes32 => address[]) internal _controllersByPartition;
// Mapping from (partition, operator) to PartitionController status. [NOT TOKEN-HOLDER-SPECIFIC]
mapping (bytes32 => mapping (address => bool)) internal _isControllerByPartition;
/************************************************************************************************/
/***************************************** Modifiers ********************************************/
/**
* @dev Modifier to verify if token is issuable.
*/
modifier isIssuableToken() {
require(_isIssuable, "55"); // 0x55 funds locked (lockup period)
_;
}
/**
* @dev Modifier to make a function callable only when the contract is not migrated.
*/
modifier isNotMigratedToken() {
require(!_migrated, "54"); // 0x54 transfers halted (contract paused)
_;
}
/**
* @dev Modifier to verifiy if sender is a minter.
*/
modifier onlyMinter() override {
require(isMinter(msg.sender) || owner() == _msgSender());
_;
}
/************************************************************************************************/
/**************************** Events (additional - not mandatory) *******************************/
event ApprovalByPartition(bytes32 indexed partition, address indexed owner, address indexed spender, uint256 value);
/************************************************************************************************/
/**
* @dev Initialize ERC1400 + register the contract implementation in ERC1820Registry.
* @param name_ Name of the token.
* @param symbol_ Symbol of the token.
* @param granularity_ Granularity of the token.
* @param defaultPartitions_ Partitions chosen by default, when partition is
* not specified, like the case ERC20 tranfers.
*/
constructor(
string memory name_,
string memory symbol_,
uint256 granularity_,
bytes32[] memory defaultPartitions_
)
{
_name = name_;
_symbol = symbol_;
_totalSupply = 0;
require(granularity_ >= 1); // Constructor Blocked - Token granularity can not be lower than 1
_granularity = granularity_;
_defaultPartitions = defaultPartitions_;
_isControllable = true;
_isIssuable = true;
// Register contract in ERC1820 registry
ERC1820Client.setInterfaceImplementation(ERC1400_INTERFACE_NAME, address(this));
ERC1820Client.setInterfaceImplementation(ERC20_INTERFACE_NAME, address(this));
// Indicate token verifies ERC1400 and ERC20 interfaces
ERC1820Implementer._setInterface(ERC1400_INTERFACE_NAME); // For migration
ERC1820Implementer._setInterface(ERC20_INTERFACE_NAME); // For migration
}
/************************************************************************************************/
/****************************** EXTERNAL FUNCTIONS (ERC20 INTERFACE) ****************************/
/************************************************************************************************/
/**
* @dev Get the total number of issued tokens.
* @return Total supply of tokens currently in circulation.
*/
function totalSupply() external override view returns (uint256) {
return _totalSupply;
}
/**
* @dev Get the balance of the account with address 'tokenHolder'.
* @param tokenHolder Address for which the balance is returned.
* @return Amount of token held by 'tokenHolder' in the token contract.
*/
function balanceOf(address tokenHolder) external override view returns (uint256) {
return _balances[tokenHolder];
}
/**
* @dev Transfer token for a specified address.
* @param to The address to transfer to.
* @param value The value to be transferred.
* @return A boolean that indicates if the operation was successful.
*/
function transfer(address to, uint256 value) external override returns (bool) {
_transferByDefaultPartitions(msg.sender, msg.sender, to, value, "");
return true;
}
/**
* @dev Check the value of tokens that an owner allowed to a spender.
* @param from address The address which owns the funds.
* @param spender address The address which will spend the funds.
* @return A uint256 specifying the value of tokens still available for the spender.
*/
function allowance(address from, address spender) external override view returns (uint256) {
return _allowed[from][spender];
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of 'msg.sender'.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean that indicates if the operation was successful.
*/
function approve(address spender, uint256 value) external override returns (bool) {
require(spender != address(0), "56"); // 0x56 invalid sender
_allowed[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
/**
* @dev Transfer tokens from one address to another.
* @param from The address which you want to transfer tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean that indicates if the operation was successful.
*/
function transferFrom(address from, address to, uint256 value) external override returns (bool) {
require( _isOperator(msg.sender, from)
|| (value <= _allowed[from][msg.sender]), "53"); // 0x53 insufficient allowance
if(_allowed[from][msg.sender] >= value) {
_allowed[from][msg.sender] = _allowed[from][msg.sender] - value;
} else {
_allowed[from][msg.sender] = 0;
}
_transferByDefaultPartitions(msg.sender, from, to, value, "");
return true;
}
/************************************************************************************************/
/****************************** EXTERNAL FUNCTIONS (ERC1400 INTERFACE) **************************/
/************************************************************************************************/
/************************************* Document Management **************************************/
/**
* @dev Access a document associated with the token.
* @param shortName Short name (represented as a bytes32) associated to the document.
* @return Requested document + document hash + document timestamp.
*/
function getDocument(bytes32 shortName) external override view returns (string memory, bytes32, uint256) {
require(bytes(_documents[shortName].docURI).length != 0); // Action Blocked - Empty document
return (
_documents[shortName].docURI,
_documents[shortName].docHash,
_documents[shortName].timestamp
);
}
/**
* @dev Associate a document with the token.
* @param shortName Short name (represented as a bytes32) associated to the document.
* @param uri Document content.
* @param documentHash Hash of the document [optional parameter].
*/
function setDocument(bytes32 shortName, string calldata uri, bytes32 documentHash) external override {
require(_isController[msg.sender]);
_documents[shortName] = Doc({
docURI: uri,
docHash: documentHash,
timestamp: block.timestamp
});
if (_indexOfDocHashes[documentHash] == 0) {
_docHashes.push(documentHash);
_indexOfDocHashes[documentHash] = _docHashes.length;
}
emit DocumentUpdated(shortName, uri, documentHash);
}
function removeDocument(bytes32 shortName) external override {
require(_isController[msg.sender], "Unauthorized");
require(bytes(_documents[shortName].docURI).length != 0, "Document doesnt exist"); // Action Blocked - Empty document
Doc memory data = _documents[shortName];
uint256 index1 = _indexOfDocHashes[data.docHash];
require(index1 > 0, "Invalid index"); //Indexing starts at 1, 0 is not allowed
// move the last item into the index being vacated
bytes32 lastValue = _docHashes[_docHashes.length - 1];
_docHashes[index1 - 1] = lastValue; // adjust for 1-based indexing
_indexOfDocHashes[lastValue] = index1;
//_totalPartitions.length -= 1;
_docHashes.pop();
_indexOfDocHashes[data.docHash] = 0;
delete _documents[shortName];
emit DocumentRemoved(shortName, data.docURI, data.docHash);
}
function getAllDocuments() external override view returns (bytes32[] memory) {
return _docHashes;
}
/************************************************************************************************/
/************************************** Token Information ***************************************/
/**
* @dev Get balance of a tokenholder for a specific partition.
* @param partition Name of the partition.
* @param tokenHolder Address for which the balance is returned.
* @return Amount of token of partition 'partition' held by 'tokenHolder' in the token contract.
*/
function balanceOfByPartition(bytes32 partition, address tokenHolder) external override view returns (uint256) {
return _balanceOfByPartition[tokenHolder][partition];
}
/**
* @dev Get partitions index of a tokenholder.
* @param tokenHolder Address for which the partitions index are returned.
* @return Array of partitions index of 'tokenHolder'.
*/
function partitionsOf(address tokenHolder) external override view returns (bytes32[] memory) {
return _partitionsOf[tokenHolder];
}
/************************************************************************************************/
/****************************************** Transfers *******************************************/
/**
* @dev Transfer the amount of tokens from the address 'msg.sender' to the address 'to'.
* @param to Token recipient.
* @param value Number of tokens to transfer.
* @param data Information attached to the transfer, by the token holder.
*/
function transferWithData(address to, uint256 value, bytes calldata data) external override {
_transferByDefaultPartitions(msg.sender, msg.sender, to, value, data);
}
/**
* @dev Transfer the amount of tokens on behalf of the address 'from' to the address 'to'.
* @param from Token holder (or 'address(0)' to set from to 'msg.sender').
* @param to Token recipient.
* @param value Number of tokens to transfer.
* @param data Information attached to the transfer, and intended for the token holder ('from').
*/
function transferFromWithData(address from, address to, uint256 value, bytes calldata data) external override virtual {
require( _isOperator(msg.sender, from)
|| (value <= _allowed[from][msg.sender]), "53"); // 0x53 insufficient allowance
if(_allowed[from][msg.sender] >= value) {
_allowed[from][msg.sender] = _allowed[from][msg.sender] - value;
} else {
_allowed[from][msg.sender] = 0;
}
_transferByDefaultPartitions(msg.sender, from, to, value, data);
}
/************************************************************************************************/
/********************************** Partition Token Transfers ***********************************/
/**
* @dev Transfer tokens from a specific partition.
* @param partition Name of the partition.
* @param to Token recipient.
* @param value Number of tokens to transfer.
* @param data Information attached to the transfer, by the token holder.
* @return Destination partition.
*/
function transferByPartition(
bytes32 partition,
address to,
uint256 value,
bytes calldata data
)
external
override
nonReentrant
returns (bytes32)
{
return _transferByPartition(partition, msg.sender, msg.sender, to, value, data, "");
}
/**
* @dev Transfer tokens from a specific partition through an operator.
* @param partition Name of the partition.
* @param from Token holder.
* @param to Token recipient.
* @param value Number of tokens to transfer.
* @param data Information attached to the transfer. [CAN CONTAIN THE DESTINATION PARTITION]
* @param operatorData Information attached to the transfer, by the operator.
* @return Destination partition.
*/
function operatorTransferByPartition(
bytes32 partition,
address from,
address to,
uint256 value,
bytes calldata data,
bytes calldata operatorData
)
external
override
returns (bytes32)
{
//We want to check if the msg.sender is an authorized operator for `from`
//(msg.sender == from OR msg.sender is authorized by from OR msg.sender is a controller if this token is controlable)
//OR
//We want to check if msg.sender is an `allowed` operator/spender for `from`
require(_isOperatorForPartition(partition, msg.sender, from)
|| (value <= _allowedByPartition[partition][from][msg.sender]), "53"); // 0x53 insufficient allowance
if(_allowedByPartition[partition][from][msg.sender] >= value) {
_allowedByPartition[partition][from][msg.sender] = _allowedByPartition[partition][from][msg.sender] - value;
} else {
_allowedByPartition[partition][from][msg.sender] = 0;
}
return _transferByPartition(partition, msg.sender, from, to, value, data, operatorData);
}
/************************************************************************************************/
/************************************* Controller Operation *************************************/
/**
* @dev Know if the token can be controlled by operators.
* If a token returns 'false' for 'isControllable()'' then it MUST always return 'false' in the future.
* @return bool 'true' if the token can still be controlled by operators, 'false' if it can't anymore.
*/
function isControllable() external override view returns (bool) {
return _isControllable;
}
/************************************************************************************************/
/************************************* Operator Management **************************************/
/**
* @dev Set a third party operator address as an operator of 'msg.sender' to transfer
* and redeem tokens on its behalf.
* @param operator Address to set as an operator for 'msg.sender'.
*/
function authorizeOperator(address operator) external override {
require(operator != msg.sender);
_authorizedOperator[operator][msg.sender] = true;
emit AuthorizedOperator(operator, msg.sender);
}
/**
* @dev Remove the right of the operator address to be an operator for 'msg.sender'
* and to transfer and redeem tokens on its behalf.
* @param operator Address to rescind as an operator for 'msg.sender'.
*/
function revokeOperator(address operator) external override {
require(operator != msg.sender);
_authorizedOperator[operator][msg.sender] = false;
emit RevokedOperator(operator, msg.sender);
}
/**
* @dev Set 'operator' as an operator for 'msg.sender' for a given partition.
* @param partition Name of the partition.
* @param operator Address to set as an operator for 'msg.sender'.
*/
function authorizeOperatorByPartition(bytes32 partition, address operator) external override {
_authorizedOperatorByPartition[msg.sender][partition][operator] = true;
emit AuthorizedOperatorByPartition(partition, operator, msg.sender);
}
/**
* @dev Remove the right of the operator address to be an operator on a given
* partition for 'msg.sender' and to transfer and redeem tokens on its behalf.
* @param partition Name of the partition.
* @param operator Address to rescind as an operator on given partition for 'msg.sender'.
*/
function revokeOperatorByPartition(bytes32 partition, address operator) external override {
_authorizedOperatorByPartition[msg.sender][partition][operator] = false;
emit RevokedOperatorByPartition(partition, operator, msg.sender);
}
/************************************************************************************************/
/************************************* Operator Information *************************************/
/**
* @dev Indicate whether the operator address is an operator of the tokenHolder address.
* @param operator Address which may be an operator of tokenHolder.
* @param tokenHolder Address of a token holder which may have the operator address as an operator.
* @return 'true' if operator is an operator of 'tokenHolder' and 'false' otherwise.
*/
function isOperator(address operator, address tokenHolder) external override view returns (bool) {
return _isOperator(operator, tokenHolder);
}
/**
* @dev Indicate whether the operator address is an operator of the tokenHolder
* address for the given partition.
* @param partition Name of the partition.
* @param operator Address which may be an operator of tokenHolder for the given partition.
* @param tokenHolder Address of a token holder which may have the operator address as an operator for the given partition.
* @return 'true' if 'operator' is an operator of 'tokenHolder' for partition 'partition' and 'false' otherwise.
*/
function isOperatorForPartition(bytes32 partition, address operator, address tokenHolder) external override view returns (bool) {
return _isOperatorForPartition(partition, operator, tokenHolder);
}
/************************************************************************************************/
/**************************************** Token Issuance ****************************************/
/**
* @dev Know if new tokens can be issued in the future.
* @return bool 'true' if tokens can still be issued by the issuer, 'false' if they can't anymore.
*/
function isIssuable() external override view returns (bool) {
return _isIssuable;
}
/**
* @dev Issue tokens from default partition.
* @param tokenHolder Address for which we want to issue tokens.
* @param value Number of tokens issued.
* @param data Information attached to the issuance, by the issuer.
*/
function issue(address tokenHolder, uint256 value, bytes calldata data)
external
override
onlyMinter
nonReentrant
isIssuableToken
{
require(_defaultPartitions.length != 0, "55"); // 0x55 funds locked (lockup period)
_issueByPartition(_defaultPartitions[0], msg.sender, tokenHolder, value, data);
}
/**
* @dev Issue tokens from a specific partition.
* @param partition Name of the partition.
* @param tokenHolder Address for which we want to issue tokens.
* @param value Number of tokens issued.
* @param data Information attached to the issuance, by the issuer.
*/
function issueByPartition(bytes32 partition, address tokenHolder, uint256 value, bytes calldata data)
external
override
onlyMinter
nonReentrant
isIssuableToken
{
_issueByPartition(partition, msg.sender, tokenHolder, value, data);
}
/************************************************************************************************/
/*************************************** Token Redemption ***************************************/
/**
* @dev Redeem the amount of tokens from the address 'msg.sender'.
* @param value Number of tokens to redeem.
* @param data Information attached to the redemption, by the token holder.
*/
function redeem(uint256 value, bytes calldata data)
external
override
nonReentrant
{
_redeemByDefaultPartitions(msg.sender, msg.sender, value, data);
}
/**
* @dev Redeem the amount of tokens on behalf of the address from.
* @param from Token holder whose tokens will be redeemed (or address(0) to set from to msg.sender).
* @param value Number of tokens to redeem.
* @param data Information attached to the redemption.
*/
function redeemFrom(address from, uint256 value, bytes calldata data)
external
override
virtual
nonReentrant
{
require(_isOperator(msg.sender, from)
|| (value <= _allowed[from][msg.sender]), "53"); // 0x53 insufficient allowance
if(_allowed[from][msg.sender] >= value) {
_allowed[from][msg.sender] = _allowed[from][msg.sender] - value;
} else {
_allowed[from][msg.sender] = 0;
}
_redeemByDefaultPartitions(msg.sender, from, value, data);
}
/**
* @dev Redeem tokens of a specific partition.
* @param partition Name of the partition.
* @param value Number of tokens redeemed.
* @param data Information attached to the redemption, by the redeemer.
*/
function redeemByPartition(bytes32 partition, uint256 value, bytes calldata data)
external
override
nonReentrant
{
_redeemByPartition(partition, msg.sender, msg.sender, value, data, "");
}
/**
* @dev Redeem tokens of a specific partition.
* @param partition Name of the partition.
* @param tokenHolder Address for which we want to redeem tokens.
* @param value Number of tokens redeemed
* @param operatorData Information attached to the redemption, by the operator.
*/
function operatorRedeemByPartition(bytes32 partition, address tokenHolder, uint256 value, bytes calldata operatorData)
external
override
nonReentrant
nonReentrant
{
require(_isOperatorForPartition(partition, msg.sender, tokenHolder) || value <= _allowedByPartition[partition][tokenHolder][msg.sender], "58"); // 0x58 invalid operator (transfer agent)
if(_allowedByPartition[partition][tokenHolder][msg.sender] >= value) {
_allowedByPartition[partition][tokenHolder][msg.sender] = _allowedByPartition[partition][tokenHolder][msg.sender] - value;
} else {
_allowedByPartition[partition][tokenHolder][msg.sender] = 0;
}
_redeemByPartition(partition, msg.sender, tokenHolder, value, "", operatorData);
}
/************************************************************************************************/
/************************************************************************************************/
/************************ EXTERNAL FUNCTIONS (ADDITIONAL - NOT MANDATORY) ***********************/
/************************************************************************************************/
/************************************ Token description *****************************************/
/**
* @dev Get the name of the token, e.g., "MyToken".
* @return Name of the token.
*/
function name() external view returns(string memory) {
return _name;
}
/**
* @dev Get the symbol of the token, e.g., "MYT".
* @return Symbol of the token.
*/
function symbol() external view returns(string memory) {
return _symbol;
}
/**
* @dev Get the number of decimals of the token.
* @return The number of decimals of the token. For retrocompatibility, decimals are forced to 18 in ERC1400.
*/
function decimals() external pure returns(uint8) {
return uint8(18);
}
/**
* @dev Get the smallest part of the token that’s not divisible.
* @return The smallest non-divisible part of the token.
*/
function granularity() external view returns(uint256) {
return _granularity;
}
/**
* @dev Get list of existing partitions.
* @return Array of all exisiting partitions.
*/
function totalPartitions() external view returns (bytes32[] memory) {
return _totalPartitions;
}
/**
* @dev Get the total number of issued tokens for a given partition.
* @param partition Name of the partition.
* @return Total supply of tokens currently in circulation, for a given partition.
*/
function totalSupplyByPartition(bytes32 partition) external view returns (uint256) {
return _totalSupplyByPartition[partition];
}
/************************************************************************************************/
/**************************************** Token behaviours **************************************/
/**
* @dev Definitely renounce the possibility to control tokens on behalf of tokenHolders.
* Once set to false, '_isControllable' can never be set to 'true' again.
*/
function renounceControl() external onlyOwner {
_isControllable = false;
}
/**
* @dev Definitely renounce the possibility to issue new tokens.
* Once set to false, '_isIssuable' can never be set to 'true' again.
*/
function renounceIssuance() external onlyOwner {
_isIssuable = false;
}
/************************************************************************************************/
/************************************ Token controllers *****************************************/
/**
* @dev Get the list of controllers as defined by the token contract.
* @return List of addresses of all the controllers.
*/
function controllers() external view returns (address[] memory) {
return _controllers;
}
/**
* @dev Get controllers for a given partition.
* @param partition Name of the partition.
* @return Array of controllers for partition.
*/
function controllersByPartition(bytes32 partition) external view returns (address[] memory) {
return _controllersByPartition[partition];
}
/**
* @dev Set list of token controllers.
* @param operators Controller addresses.
*/
function setControllers(address[] calldata operators) external onlyOwner {
_setControllers(operators);
}
/**
* @dev Set list of token partition controllers.
* @param partition Name of the partition.
* @param operators Controller addresses.
*/
function setPartitionControllers(bytes32 partition, address[] calldata operators) external onlyOwner {
_setPartitionControllers(partition, operators);
}
/************************************************************************************************/
/********************************* Token default partitions *************************************/
/**
* @dev Get default partitions to transfer from.
* Function used for ERC20 retrocompatibility.
* For example, a security token may return the bytes32("unrestricted").
* @return Array of default partitions.
*/
function getDefaultPartitions() external view returns (bytes32[] memory) {
return _defaultPartitions;
}
/**
* @dev Set default partitions to transfer from.
* Function used for ERC20 retrocompatibility.
* @param partitions partitions to use by default when not specified.
*/
function setDefaultPartitions(bytes32[] calldata partitions) external onlyOwner {
_defaultPartitions = partitions;
}
/************************************************************************************************/
/******************************** Partition Token Allowances ************************************/
/**
* @dev Check the value of tokens that an owner allowed to a spender.
* @param partition Name of the partition.
* @param owner address The address which owns the funds.
* @param spender address The address which will spend the funds.
* @return A uint256 specifying the value of tokens still available for the spender.
*/
function allowanceByPartition(bytes32 partition, address owner, address spender) external override view returns (uint256) {
return _allowedByPartition[partition][owner][spender];
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of 'msg.sender'.
* @param partition Name of the partition.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean that indicates if the operation was successful.
*/
function approveByPartition(bytes32 partition, address spender, uint256 value) external returns (bool) {
require(spender != address(0), "56"); // 0x56 invalid sender
_allowedByPartition[partition][msg.sender][spender] = value;
emit ApprovalByPartition(partition, msg.sender, spender, value);
return true;
}
/************************************************************************************************/
/************************************** Token extension *****************************************/
/**
* @dev Set token extension contract address.
* The extension contract can for example verify "ERC1400TokensValidator" or "ERC1400TokensChecker" interfaces.
* If the extension is an "ERC1400TokensValidator", it will be called everytime a transfer is executed.
* @param extension Address of the extension contract.
* @param interfaceLabel Interface label of extension contract.
* @param removeOldExtensionRoles If set to 'true', the roles of the old extension(minter, controller) will be removed extension.
* @param addMinterRoleForExtension If set to 'true', the extension contract will be added as minter.
* @param addControllerRoleForExtension If set to 'true', the extension contract will be added as controller.
*/
function setTokenExtension(address extension, string calldata interfaceLabel, bool removeOldExtensionRoles, bool addMinterRoleForExtension, bool addControllerRoleForExtension) external onlyOwner {
_setTokenExtension(extension, interfaceLabel, removeOldExtensionRoles, addMinterRoleForExtension, addControllerRoleForExtension);
}
/************************************************************************************************/
/************************************* Token migration ******************************************/
/**
* @dev Migrate contract.
*
* ===> CAUTION: DEFINITIVE ACTION
*
* This function shall be called once a new version of the smart contract has been created.
* Once this function is called:
* - The address of the new smart contract is set in ERC1820 registry
* - If the choice is definitive, the current smart contract is turned off and can never be used again
*
* @param newContractAddress Address of the new version of the smart contract.
* @param definitive If set to 'true' the contract is turned off definitely.
*/
function migrate(address newContractAddress, bool definitive) external onlyOwner {
_migrate(newContractAddress, definitive);
}
/************************************************************************************************/
/************************************************************************************************/
/************************************* INTERNAL FUNCTIONS ***************************************/
/************************************************************************************************/
/**************************************** Token Transfers ***************************************/
/**
* @dev Perform the transfer of tokens.
* @param from Token holder.
* @param to Token recipient.
* @param value Number of tokens to transfer.
*/
function _transferWithData(
address from,
address to,
uint256 value
)
internal
isNotMigratedToken
{
require(_isMultiple(value), "50"); // 0x50 transfer failure
require(to != address(0), "57"); // 0x57 invalid receiver
require(_balances[from] >= value, "52"); // 0x52 insufficient balance
_balances[from] = _balances[from] - value;
_balances[to] = _balances[to] + value;
emit Transfer(from, to, value); // ERC20 retrocompatibility
}
/**
* @dev Transfer tokens from a specific partition.
* @param fromPartition Partition of the tokens to transfer.
* @param operator The address performing the transfer.
* @param from Token holder.
* @param to Token recipient.
* @param value Number of tokens to transfer.
* @param data Information attached to the transfer. [CAN CONTAIN THE DESTINATION PARTITION]
* @param operatorData Information attached to the transfer, by the operator (if any).
* @return Destination partition.
*/
function _transferByPartition(
bytes32 fromPartition,
address operator,
address from,
address to,
uint256 value,
bytes memory data,
bytes memory operatorData
)
internal
returns (bytes32)
{
require(_balanceOfByPartition[from][fromPartition] >= value, "52"); // 0x52 insufficient balance
bytes32 toPartition = fromPartition;
if(operatorData.length != 0 && data.length >= 64) {
toPartition = _getDestinationPartition(fromPartition, data);
}
_callSenderExtension(fromPartition, operator, from, to, value, data, operatorData);
_callTokenExtension(fromPartition, operator, from, to, value, data, operatorData);
_removeTokenFromPartition(from, fromPartition, value);
_transferWithData(from, to, value);
_addTokenToPartition(to, toPartition, value);
_callRecipientExtension(toPartition, operator, from, to, value, data, operatorData);
emit TransferByPartition(fromPartition, operator, from, to, value, data, operatorData);
if(toPartition != fromPartition) {
emit ChangedPartition(fromPartition, toPartition, value);
}
return toPartition;
}
/**
* @dev Transfer tokens from default partitions.
* Function used for ERC20 retrocompatibility.
* @param operator The address performing the transfer.
* @param from Token holder.
* @param to Token recipient.
* @param value Number of tokens to transfer.
* @param data Information attached to the transfer, and intended for the token holder ('from') [CAN CONTAIN THE DESTINATION PARTITION].
*/
function _transferByDefaultPartitions(
address operator,
address from,
address to,
uint256 value,
bytes memory data
)
internal
{
require(_defaultPartitions.length != 0, "55"); // // 0x55 funds locked (lockup period)
uint256 _remainingValue = value;
uint256 _localBalance;
for (uint i = 0; i < _defaultPartitions.length; i++) {
_localBalance = _balanceOfByPartition[from][_defaultPartitions[i]];
if(_remainingValue <= _localBalance) {
_transferByPartition(_defaultPartitions[i], operator, from, to, _remainingValue, data, "");
_remainingValue = 0;
break;
} else if (_localBalance != 0) {
_transferByPartition(_defaultPartitions[i], operator, from, to, _localBalance, data, "");
_remainingValue = _remainingValue - _localBalance;
}
}
require(_remainingValue == 0, "52"); // 0x52 insufficient balance
}
/**
* @dev Retrieve the destination partition from the 'data' field.
* By convention, a partition change is requested ONLY when 'data' starts
* with the flag: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
* When the flag is detected, the destination tranche is extracted from the
* 32 bytes following the flag.
* @param fromPartition Partition of the tokens to transfer.
* @param data Information attached to the transfer. [CAN CONTAIN THE DESTINATION PARTITION]
* @return toPartition Destination partition.
*/
function _getDestinationPartition(bytes32 fromPartition, bytes memory data) internal pure returns(bytes32 toPartition) {
bytes32 changePartitionFlag = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
bytes32 flag;
assembly {
flag := mload(add(data, 32))
}
if(flag == changePartitionFlag) {
assembly {
toPartition := mload(add(data, 64))
}
} else {
toPartition = fromPartition;
}
}
/**
* @dev Remove a token from a specific partition.
* @param from Token holder.
* @param partition Name of the partition.
* @param value Number of tokens to transfer.
*/
function _removeTokenFromPartition(address from, bytes32 partition, uint256 value) internal {
_balanceOfByPartition[from][partition] = _balanceOfByPartition[from][partition]- value;
_totalSupplyByPartition[partition] = _totalSupplyByPartition[partition] - value;
// If the total supply is zero, finds and deletes the partition.
if(_totalSupplyByPartition[partition] == 0) {
uint256 index1 = _indexOfTotalPartitions[partition];
require(index1 > 0, "50"); // 0x50 transfer failure
// move the last item into the index being vacated
bytes32 lastValue = _totalPartitions[_totalPartitions.length - 1];
_totalPartitions[index1 - 1] = lastValue; // adjust for 1-based indexing
_indexOfTotalPartitions[lastValue] = index1;
//_totalPartitions.length -= 1;
_totalPartitions.pop();
_indexOfTotalPartitions[partition] = 0;
}
// If the balance of the TokenHolder's partition is zero, finds and deletes the partition.
if(_balanceOfByPartition[from][partition] == 0) {
uint256 index2 = _indexOfPartitionsOf[from][partition];
require(index2 > 0, "50"); // 0x50 transfer failure
// move the last item into the index being vacated
bytes32 lastValue = _partitionsOf[from][_partitionsOf[from].length - 1];
_partitionsOf[from][index2 - 1] = lastValue; // adjust for 1-based indexing
_indexOfPartitionsOf[from][lastValue] = index2;
//_partitionsOf[from].length -= 1;
_partitionsOf[from].pop();
_indexOfPartitionsOf[from][partition] = 0;
}
}
/**
* @dev Add a token to a specific partition.
* @param to Token recipient.
* @param partition Name of the partition.
* @param value Number of tokens to transfer.
*/
function _addTokenToPartition(address to, bytes32 partition, uint256 value) internal {
if(value != 0) {
if (_indexOfPartitionsOf[to][partition] == 0) {
_partitionsOf[to].push(partition);
_indexOfPartitionsOf[to][partition] = _partitionsOf[to].length;
}
_balanceOfByPartition[to][partition] = _balanceOfByPartition[to][partition] + value;
if (_indexOfTotalPartitions[partition] == 0) {
_totalPartitions.push(partition);
_indexOfTotalPartitions[partition] = _totalPartitions.length;
}
_totalSupplyByPartition[partition] = _totalSupplyByPartition[partition] + value;
}
}
/**
* @dev Check if 'value' is multiple of the granularity.
* @param value The quantity that want's to be checked.
* @return 'true' if 'value' is a multiple of the granularity.
*/
function _isMultiple(uint256 value) internal view returns(bool) {
return(uint256(value / _granularity) * _granularity == value);
}
/************************************************************************************************/
/****************************************** Hooks ***********************************************/
/**
* @dev Check for 'ERC1400TokensSender' user extension in ERC1820 registry and call it.
* @param partition Name of the partition (bytes32 to be left empty for transfers where partition is not specified).
* @param operator Address which triggered the balance decrease (through transfer or redemption).
* @param from Token holder.
* @param to Token recipient for a transfer and 0x for a redemption.
* @param value Number of tokens the token holder balance is decreased by.
* @param data Extra information.
* @param operatorData Extra information, attached by the operator (if any).
*/
function _callSenderExtension(
bytes32 partition,
address operator,
address from,
address to,
uint256 value,
bytes memory data,
bytes memory operatorData
)
internal
{
address senderImplementation;
senderImplementation = interfaceAddr(from, ERC1400_TOKENS_SENDER);
if (senderImplementation != address(0)) {
IERC1400TokensSender(senderImplementation).tokensToTransfer(msg.data, partition, operator, from, to, value, data, operatorData);
}
}
/**
* @dev Check for 'ERC1400TokensValidator' token extension in ERC1820 registry and call it.
* @param partition Name of the partition (bytes32 to be left empty for transfers where partition is not specified).
* @param operator Address which triggered the balance decrease (through transfer or redemption).
* @param from Token holder.
* @param to Token recipient for a transfer and 0x for a redemption.
* @param value Number of tokens the token holder balance is decreased by.
* @param data Extra information.
* @param operatorData Extra information, attached by the operator (if any).
*/
function _callTokenExtension(
bytes32 partition,
address operator,
address from,
address to,
uint256 value,
bytes memory data,
bytes memory operatorData
)
internal
{
address validatorImplementation;
validatorImplementation = interfaceAddr(address(this), ERC1400_TOKENS_VALIDATOR);
if (validatorImplementation != address(0)) {
IERC1400TokensValidator(validatorImplementation).tokensToValidate(msg.data, partition, operator, from, to, value, data, operatorData);
}
}
/**
* @dev Check for 'ERC1400TokensRecipient' user extension in ERC1820 registry and call it.
* @param partition Name of the partition (bytes32 to be left empty for transfers where partition is not specified).
* @param operator Address which triggered the balance increase (through transfer or issuance).
* @param from Token holder for a transfer and 0x for an issuance.
* @param to Token recipient.
* @param value Number of tokens the recipient balance is increased by.
* @param data Extra information, intended for the token holder ('from').
* @param operatorData Extra information attached by the operator (if any).
*/
function _callRecipientExtension(
bytes32 partition,
address operator,
address from,
address to,
uint256 value,
bytes memory data,
bytes memory operatorData
)
internal
virtual
{
address recipientImplementation;
recipientImplementation = interfaceAddr(to, ERC1400_TOKENS_RECIPIENT);
if (recipientImplementation != address(0)) {
IERC1400TokensRecipient(recipientImplementation).tokensReceived(msg.data, partition, operator, from, to, value, data, operatorData);
}
}
/************************************************************************************************/
/************************************* Operator Information *************************************/
/**
* @dev Indicate whether the operator address is an operator of the tokenHolder address.
* @param operator Address which may be an operator of 'tokenHolder'.
* @param tokenHolder Address of a token holder which may have the 'operator' address as an operator.
* @return 'true' if 'operator' is an operator of 'tokenHolder' and 'false' otherwise.
*/
function _isOperator(address operator, address tokenHolder) internal view returns (bool) {
return (operator == tokenHolder
|| _authorizedOperator[operator][tokenHolder]
|| (_isControllable && _isController[operator])
);
}
/**
* @dev Indicate whether the operator address is an operator of the tokenHolder
* address for the given partition.
* @param partition Name of the partition.
* @param operator Address which may be an operator of tokenHolder for the given partition.
* @param tokenHolder Address of a token holder which may have the operator address as an operator for the given partition.
* @return 'true' if 'operator' is an operator of 'tokenHolder' for partition 'partition' and 'false' otherwise.
*/
function _isOperatorForPartition(bytes32 partition, address operator, address tokenHolder) internal view returns (bool) {
return (_isOperator(operator, tokenHolder)
|| _authorizedOperatorByPartition[tokenHolder][partition][operator]
|| (_isControllable && _isControllerByPartition[partition][operator])
);
}
/************************************************************************************************/
/**************************************** Token Issuance ****************************************/
/**
* @dev Perform the issuance of tokens.
* @param operator Address which triggered the issuance.
* @param to Token recipient.
* @param value Number of tokens issued.
* @param data Information attached to the issuance, and intended for the recipient (to).
*/
function _issue(address operator, address to, uint256 value, bytes memory data)
internal
isNotMigratedToken
{
require(_isMultiple(value), "50"); // 0x50 transfer failure
require(to != address(0), "57"); // 0x57 invalid receiver
_totalSupply = _totalSupply + value;
_balances[to] = _balances[to] + value;
emit Issued(operator, to, value, data);
emit Transfer(address(0), to, value); // ERC20 retrocompatibility
}
/**
* @dev Issue tokens from a specific partition.
* @param toPartition Name of the partition.
* @param operator The address performing the issuance.
* @param to Token recipient.
* @param value Number of tokens to issue.
* @param data Information attached to the issuance.
*/
function _issueByPartition(
bytes32 toPartition,
address operator,
address to,
uint256 value,
bytes memory data
)
internal
{
_callTokenExtension(toPartition, operator, address(0), to, value, data, "");
_issue(operator, to, value, data);
_addTokenToPartition(to, toPartition, value);
_callRecipientExtension(toPartition, operator, address(0), to, value, data, "");
emit IssuedByPartition(toPartition, operator, to, value, data, "");
}
/************************************************************************************************/
/*************************************** Token Redemption ***************************************/
/**
* @dev Perform the token redemption.
* @param operator The address performing the redemption.
* @param from Token holder whose tokens will be redeemed.
* @param value Number of tokens to redeem.
* @param data Information attached to the redemption.
*/
function _redeem(address operator, address from, uint256 value, bytes memory data)
internal
isNotMigratedToken
{
require(_isMultiple(value), "50"); // 0x50 transfer failure
require(from != address(0), "56"); // 0x56 invalid sender
require(_balances[from] >= value, "52"); // 0x52 insufficient balance
_balances[from] = _balances[from] - value;
_totalSupply = _totalSupply - value;
emit Redeemed(operator, from, value, data);
emit Transfer(from, address(0), value); // ERC20 retrocompatibility
}
/**
* @dev Redeem tokens of a specific partition.
* @param fromPartition Name of the partition.
* @param operator The address performing the redemption.
* @param from Token holder whose tokens will be redeemed.
* @param value Number of tokens to redeem.
* @param data Information attached to the redemption.
* @param operatorData Information attached to the redemption, by the operator (if any).
*/
function _redeemByPartition(
bytes32 fromPartition,
address operator,
address from,
uint256 value,
bytes memory data,
bytes memory operatorData
)
internal
{
require(_balanceOfByPartition[from][fromPartition] >= value, "52"); // 0x52 insufficient balance
_callSenderExtension(fromPartition, operator, from, address(0), value, data, operatorData);
_callTokenExtension(fromPartition, operator, from, address(0), value, data, operatorData);
_removeTokenFromPartition(from, fromPartition, value);
_redeem(operator, from, value, data);
emit RedeemedByPartition(fromPartition, operator, from, value, operatorData);
}
/**
* @dev Redeem tokens from a default partitions.
* @param operator The address performing the redeem.
* @param from Token holder.
* @param value Number of tokens to redeem.
* @param data Information attached to the redemption.
*/
function _redeemByDefaultPartitions(
address operator,
address from,
uint256 value,
bytes memory data
)
internal
{
require(_defaultPartitions.length != 0, "55"); // 0x55 funds locked (lockup period)
uint256 _remainingValue = value;
uint256 _localBalance;
for (uint i = 0; i < _defaultPartitions.length; i++) {
_localBalance = _balanceOfByPartition[from][_defaultPartitions[i]];
if(_remainingValue <= _localBalance) {
_redeemByPartition(_defaultPartitions[i], operator, from, _remainingValue, data, "");
_remainingValue = 0;
break;
} else {
_redeemByPartition(_defaultPartitions[i], operator, from, _localBalance, data, "");
_remainingValue = _remainingValue - _localBalance;
}
}
require(_remainingValue == 0, "52"); // 0x52 insufficient balance
}
/************************************************************************************************/
/************************************** Transfer Validity ***************************************/
/**
* @dev Know the reason on success or failure based on the EIP-1066 application-specific status codes.
* @param payload Payload of the initial transaction.
* @param partition Name of the partition.
* @param operator The address performing the transfer.
* @param from Token holder.
* @param to Token recipient.
* @param value Number of tokens to transfer.
* @param data Information attached to the transfer. [CAN CONTAIN THE DESTINATION PARTITION]
* @param operatorData Information attached to the transfer, by the operator (if any).
* @return ESC (Ethereum Status Code) following the EIP-1066 standard.
* @return Additional bytes32 parameter that can be used to define
* application specific reason codes with additional details (for example the
* transfer restriction rule responsible for making the transfer operation invalid).
* @return Destination partition.
*/
function _canTransfer(bytes memory payload, bytes32 partition, address operator, address from, address to, uint256 value, bytes memory data, bytes memory operatorData)
internal
view
returns (bytes1, bytes32, bytes32)
{
address checksImplementation = interfaceAddr(address(this), ERC1400_TOKENS_CHECKER);
if((checksImplementation != address(0))) {
return IERC1400TokensChecker(checksImplementation).canTransferByPartition(payload, partition, operator, from, to, value, data, operatorData);
}
else {
return(hex"00", "", partition);
}
}
/************************************************************************************************/
/************************************************************************************************/
/************************ INTERNAL FUNCTIONS (ADDITIONAL - NOT MANDATORY) ***********************/
/************************************************************************************************/
/************************************ Token controllers *****************************************/
/**
* @dev Set list of token controllers.
* @param operators Controller addresses.
*/
function _setControllers(address[] memory operators) internal {
for (uint i = 0; i<_controllers.length; i++){
_isController[_controllers[i]] = false;
}
for (uint j = 0; j<operators.length; j++){
_isController[operators[j]] = true;
}
_controllers = operators;
}
/**
* @dev Set list of token partition controllers.
* @param partition Name of the partition.
* @param operators Controller addresses.
*/
function _setPartitionControllers(bytes32 partition, address[] memory operators) internal {
for (uint i = 0; i<_controllersByPartition[partition].length; i++){
_isControllerByPartition[partition][_controllersByPartition[partition][i]] = false;
}
for (uint j = 0; j<operators.length; j++){
_isControllerByPartition[partition][operators[j]] = true;
}
_controllersByPartition[partition] = operators;
}
/************************************************************************************************/
/************************************** Token extension *****************************************/
/**
* @dev Set token extension contract address.
* The extension contract can for example verify "ERC1400TokensValidator" or "ERC1400TokensChecker" interfaces.
* If the extension is an "ERC1400TokensValidator", it will be called everytime a transfer is executed.
* @param extension Address of the extension contract.
* @param interfaceLabel Interface label of extension contract.
* @param removeOldExtensionRoles If set to 'true', the roles of the old extension(minter, controller) will be removed extension.
* @param addMinterRoleForExtension If set to 'true', the extension contract will be added as minter.
* @param addControllerRoleForExtension If set to 'true', the extension contract will be added as controller.
*/
function _setTokenExtension(address extension, string memory interfaceLabel, bool removeOldExtensionRoles, bool addMinterRoleForExtension, bool addControllerRoleForExtension) internal {
address oldExtension = interfaceAddr(address(this), interfaceLabel);
if (oldExtension != address(0) && removeOldExtensionRoles) {
if(isMinter(oldExtension)) {
_removeMinter(oldExtension);
}
_isController[oldExtension] = false;
}
ERC1820Client.setInterfaceImplementation(interfaceLabel, extension);
if(addMinterRoleForExtension && !isMinter(extension)) {
_addMinter(extension);
}
if (addControllerRoleForExtension) {
_isController[extension] = true;
}
}
/************************************************************************************************/
/************************************* Token migration ******************************************/
/**
* @dev Migrate contract.
*
* ===> CAUTION: DEFINITIVE ACTION
*
* This function shall be called once a new version of the smart contract has been created.
* Once this function is called:
* - The address of the new smart contract is set in ERC1820 registry
* - If the choice is definitive, the current smart contract is turned off and can never be used again
*
* @param newContractAddress Address of the new version of the smart contract.
* @param definitive If set to 'true' the contract is turned off definitely.
*/
function _migrate(address newContractAddress, bool definitive) internal {
ERC1820Client.setInterfaceImplementation(ERC20_INTERFACE_NAME, newContractAddress);
ERC1820Client.setInterfaceImplementation(ERC1400_INTERFACE_NAME, newContractAddress);
if(definitive) {
_migrated = true;
}
}
/************************************************************************************************/
/************************************* Domain Aware ******************************************/
function domainName() public override view returns (string memory) {
return _name;
}
function domainVersion() public override pure returns (string memory) {
return "1";
}
/************************************************************************************************/
}
/**
* @notice Interface to the extension types
*/
interface IExtensionTypes {
enum CertificateValidation {
None,
NonceBased,
SaltBased
}
}
/**
* @notice Interface to the extension contract
*/
abstract contract Extension is IExtensionTypes {
function registerTokenSetup(
address token,
CertificateValidation certificateActivated,
bool allowlistActivated,
bool blocklistActivated,
bool granularityByPartitionActivated,
bool holdsActivated,
address[] calldata operators
) external virtual;
function addCertificateSigner(
address token,
address account
) external virtual;
}
contract VFIN is ERC1400, IExtensionTypes {
uint256 public constant ISSUED_TOKENS = 27_708_333 * 10 ** 18;
uint256 public constant FOR_SALE_TOKENS = 22_166_666 * 10 ** 18;
bytes32 public constant VFIN_DEFAULT_PARTITION = keccak256(abi.encodePacked("VFIN_DEFAULT_PARTITION"));
bytes32[] private VFIN_DEFAULT_PARTITIONS = [bytes32(VFIN_DEFAULT_PARTITION)];
uint256 constant private GRANULARITY = 1;
/**
* @dev Initialize ERC1400.
* @param newOwner Address whom contract ownership shall be transferred to.
* @param seller Address who will sell tokens.
*/
constructor(
address newOwner,
address seller
)
ERC1400("VFIN", "VFIN", GRANULARITY, VFIN_DEFAULT_PARTITIONS)
{
require(newOwner != address(0), "VFIN: wrong address");
require(seller != address(0), "VFIN: wrong address");
transferOwnership(newOwner);
_issueByPartition(VFIN_DEFAULT_PARTITION, msg.sender, newOwner, ISSUED_TOKENS, "");
_isIssuable = false;
_transferByDefaultPartitions(msg.sender, newOwner, seller, FOR_SALE_TOKENS, "");
}
/**
* @dev Initialize extension.
* @param extension Address of token extension.
* @param certificateSigner Address of the off-chain service which signs the
* conditional ownership certificates required for token transfers, issuance,
* redemption (Cf. CertificateController.sol).
* @param certificateActivated If set to 'true', the certificate controller
* is activated at contract creation.
* @param controllers_ Array of initial controllers.
*/
function initExtention(
address extension,
address certificateSigner,
CertificateValidation certificateActivated,
address[] memory controllers_
) external onlyOwner
{
if(extension != address(0)) {
Extension(extension).registerTokenSetup(
address(this), // token
certificateActivated, // certificateActivated
true, // allowlistActivated
true, // blocklistActivated
true, // granularityByPartitionActivated
true, // holdsActivated
controllers_ // token controllers
);
if(certificateSigner != address(0)) {
Extension(extension).addCertificateSigner(address(this), certificateSigner);
}
_setTokenExtension(extension, ERC1400_TOKENS_VALIDATOR, true, true, true);
}
_setControllers(controllers_);
}
/************************************** Transfer Validity ***************************************/
/**
* @dev Know the reason on success or failure based on the EIP-1066 application-specific status codes.
* @param partition Name of the partition.
* @param to Token recipient.
* @param value Number of tokens to transfer.
* @param data Information attached to the transfer, by the token holder. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE]
* @return ESC (Ethereum Status Code) following the EIP-1066 standard.
* @return Additional bytes32 parameter that can be used to define
* application specific reason codes with additional details (for example the
* transfer restriction rule responsible for making the transfer operation invalid).
* @return Destination partition.
*/
function canTransferByPartition(bytes32 partition, address to, uint256 value, bytes calldata data)
external
view
returns (bytes1, bytes32, bytes32)
{
return ERC1400._canTransfer(
_replaceFunctionSelector(this.transferByPartition.selector, msg.data), // 0xf3d490db: 4 first bytes of keccak256(transferByPartition(bytes32,address,uint256,bytes))
partition,
msg.sender,
msg.sender,
to,
value,
data,
""
);
}
/**
* @dev Know the reason on success or failure based on the EIP-1066 application-specific status codes.
* @param partition Name of the partition.
* @param from Token holder.
* @param to Token recipient.
* @param value Number of tokens to transfer.
* @param data Information attached to the transfer. [CAN CONTAIN THE DESTINATION PARTITION]
* @param operatorData Information attached to the transfer, by the operator. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE]
* @return ESC (Ethereum Status Code) following the EIP-1066 standard.
* @return Additional bytes32 parameter that can be used to define
* application specific reason codes with additional details (for example the
* transfer restriction rule responsible for making the transfer operation invalid).
* @return Destination partition.
*/
function canOperatorTransferByPartition(bytes32 partition, address from, address to, uint256 value, bytes calldata data, bytes calldata operatorData)
external
view
returns (bytes1, bytes32, bytes32)
{
return ERC1400._canTransfer(
_replaceFunctionSelector(this.operatorTransferByPartition.selector, msg.data), // 0x8c0dee9c: 4 first bytes of keccak256(operatorTransferByPartition(bytes32,address,address,uint256,bytes,bytes))
partition,
msg.sender,
from,
to,
value,
data,
operatorData
);
}
/**
* @dev Replace function selector
* @param functionSig Replacement function selector.
* @param payload Payload, where function selector needs to be replaced.
*/
function _replaceFunctionSelector(bytes4 functionSig, bytes memory payload) internal pure returns(bytes memory) {
bytes memory updatedPayload = new bytes(payload.length);
for (uint i = 0; i<4; i++){
updatedPayload[i] = functionSig[i];
}
for (uint j = 4; j<payload.length; j++){
updatedPayload[j] = payload[j];
}
return updatedPayload;
}
/************************************************************************************************/
}
{
"compilationTarget": {
"VFIN.sol": "VFIN"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"newOwner","type":"address"},{"internalType":"address","name":"seller","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":true,"internalType":"bytes32","name":"partition","type":"bytes32"},{"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":"ApprovalByPartition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"tokenHolder","type":"address"}],"name":"AuthorizedOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"partition","type":"bytes32"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"tokenHolder","type":"address"}],"name":"AuthorizedOperatorByPartition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"fromPartition","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"toPartition","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"ChangedPartition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"name","type":"bytes32"},{"indexed":false,"internalType":"string","name":"uri","type":"string"},{"indexed":false,"internalType":"bytes32","name":"documentHash","type":"bytes32"}],"name":"DocumentRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"name","type":"bytes32"},{"indexed":false,"internalType":"string","name":"uri","type":"string"},{"indexed":false,"internalType":"bytes32","name":"documentHash","type":"bytes32"}],"name":"DocumentUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"Issued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"partition","type":"bytes32"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"operatorData","type":"bytes"}],"name":"IssuedByPartition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"MinterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"MinterRemoved","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":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"Redeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"partition","type":"bytes32"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"operatorData","type":"bytes"}],"name":"RedeemedByPartition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"tokenHolder","type":"address"}],"name":"RevokedOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"partition","type":"bytes32"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"tokenHolder","type":"address"}],"name":"RevokedOperatorByPartition","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"fromPartition","type":"bytes32"},{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"operatorData","type":"bytes"}],"name":"TransferByPartition","type":"event"},{"inputs":[],"name":"FOR_SALE_TOKENS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ISSUED_TOKENS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VFIN_DEFAULT_PARTITION","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowanceByPartition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approveByPartition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"authorizeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"operator","type":"address"}],"name":"authorizeOperatorByPartition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenHolder","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"tokenHolder","type":"address"}],"name":"balanceOfByPartition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"interfaceHash","type":"bytes32"},{"internalType":"address","name":"","type":"address"}],"name":"canImplementInterfaceForAddress","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"operatorData","type":"bytes"}],"name":"canOperatorTransferByPartition","outputs":[{"internalType":"bytes1","name":"","type":"bytes1"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"canTransferByPartition","outputs":[{"internalType":"bytes1","name":"","type":"bytes1"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"controllers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"}],"name":"controllersByPartition","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"domainName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"domainVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"generateDomainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllDocuments","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDefaultPartitions","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"shortName","type":"bytes32"}],"name":"getDocument","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"granularity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"extension","type":"address"},{"internalType":"address","name":"certificateSigner","type":"address"},{"internalType":"enum IExtensionTypes.CertificateValidation","name":"certificateActivated","type":"uint8"},{"internalType":"address[]","name":"controllers_","type":"address[]"}],"name":"initExtention","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isControllable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isIssuable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"tokenHolder","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"tokenHolder","type":"address"}],"name":"isOperatorForPartition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenHolder","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"issue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"tokenHolder","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"issueByPartition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newContractAddress","type":"address"},{"internalType":"bool","name":"definitive","type":"bool"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"tokenHolder","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"operatorData","type":"bytes"}],"name":"operatorRedeemByPartition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"operatorData","type":"bytes"}],"name":"operatorTransferByPartition","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenHolder","type":"address"}],"name":"partitionsOf","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"redeemByPartition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"redeemFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"shortName","type":"bytes32"}],"name":"removeDocument","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"removeMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceIssuance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"revokeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"operator","type":"address"}],"name":"revokeOperatorByPartition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"}],"name":"setControllers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"partitions","type":"bytes32[]"}],"name":"setDefaultPartitions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"shortName","type":"bytes32"},{"internalType":"string","name":"uri","type":"string"},{"internalType":"bytes32","name":"documentHash","type":"bytes32"}],"name":"setDocument","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address[]","name":"operators","type":"address[]"}],"name":"setPartitionControllers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"extension","type":"address"},{"internalType":"string","name":"interfaceLabel","type":"string"},{"internalType":"bool","name":"removeOldExtensionRoles","type":"bool"},{"internalType":"bool","name":"addMinterRoleForExtension","type":"bool"},{"internalType":"bool","name":"addControllerRoleForExtension","type":"bool"}],"name":"setTokenExtension","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPartitions","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"partition","type":"bytes32"}],"name":"totalSupplyByPartition","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":"bytes32","name":"partition","type":"bytes32"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transferByPartition","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transferFromWithData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transferWithData","outputs":[],"stateMutability":"nonpayable","type":"function"}]