Address Contract Verified
Address
0x83FadFd65c5B0DA69B5Bb66426c9026c80f37e18
Balance
0 ETH
Nonce
1
Code Size
4447 bytes
Creator
0x55749224...2409 at tx 0xb31d64a3...0a3a29
Indexed Transactions
0
Contract Bytecode
4447 bytes
0x608060405234801561001057600080fd5b50600436106101a35760003560e01c80634cf4d295116100ee5780639cbad65511610097578063f2fde38b11610071578063f2fde38b14610363578063f3ae241514610376578063fb99b86d14610389578063fca402ed1461039c57600080fd5b80639cbad6551461033d578063cee2a9cf14610350578063f104a8c91461027357600080fd5b806386863ec6116100c857806386863ec6146103065780638da5cb5b1461031957806393c81dd61461032a57600080fd5b80634cf4d295146102bf578063516f0a1b146102eb578063715018a6146102fe57600080fd5b80632cb7e1ec1161015057806341f9b3f11161012a57806341f9b3f1146102865780634209fff1146102995780634a932544146102ac57600080fd5b80632cb7e1ec1461024d57806335c80c8c14610260578063372491a21461027357600080fd5b80630afc8743116101815780630afc8743146101f85780630c98b5e31461022357806319d1997a1461023657600080fd5b8063013b7ce4146101a8578063053af05d146101d05780630694a5fb146101e3575b600080fd5b6101bb6101b6366004610ec8565b6103af565b60405190151581526020015b60405180910390f35b6101bb6101de366004610f1b565b6103e6565b6101f66101f1366004610f1b565b6103f9565b005b60665461020b906001600160a01b031681565b6040516001600160a01b0390911681526020016101c7565b6101f6610231366004610f1b565b610454565b61023f60685481565b6040519081526020016101c7565b6101f661025b366004610f38565b610468565b6101bb61026e366004610f1b565b6104be565b6101f6610281366004610f79565b6104cb565b6101f6610294366004610f1b565b6104d8565b6101bb6102a7366004610f1b565b6104e9565b6101f66102ba366004610f1b565b610571565b6101bb6102cd366004610f1b565b6001600160a01b031660009081526067602052604090205460ff1690565b6101bb6102f9366004610f1b565b610631565b6101f661063e565b6101f6610314366004610f38565b610652565b6033546001600160a01b031661020b565b6101bb610338366004610fa5565b6107b6565b6101bb61034b366004610f1b565b6107c9565b6101bb61035e366004610f1b565b6107d6565b6101f6610371366004610f1b565b6107e3565b6101bb610384366004610f1b565b610870565b6101bb610397366004610f1b565b61087d565b60655461020b906001600160a01b031681565b60006001600160a01b0385161580156103cd57506103cd828461088a565b156103da575060006103de565b5060015b949350505050565b60006103f382600661090c565b92915050565b610402816109aa565b61040b816109f7565b6001600160a01b038116600081815260676020526040808220805460ff19169055517f408b49d9be1c914c52a0227e18a077e5a892dddf32a26cfa94a5d9708fad77189190a250565b61045c610a54565b61046581610aae565b50565b610471336109f7565b7f1f8add448731ee25896fb01890ebbb4e30dffab5719800b6df35826148ceedb7600261049d33610b1c565b8585856040516104b1959493929190610ff8565b60405180910390a1505050565b60006103f382600161090c565b6104d4336109f7565b5050565b6104e0610a54565b61046581610b80565b6065546040517f78d77ecb0000000000000000000000000000000000000000000000000000000081526001600160a01b03838116600483015260009216906378d77ecb90602401602060405180830381865afa15801561054d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f39190611035565b610579610bee565b610582816109aa565b6001600160a01b03811660009081526067602052604090205460ff16156105e5576040517f14d525030000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b6001600160a01b038116600081815260676020526040808220805460ff19166001179055517f1f7b76c58fb697eb53c6c7c1becb96911516a136e24d7ced386b2355358b75a39190a250565b60006103f382600061090c565b610646610a54565b6106506000610cab565b565b600054610100900460ff16158080156106725750600054600160ff909116105b8061068c5750303b15801561068c575060005460ff166001145b6106fe5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016105dc565b6000805460ff191660011790558015610721576000805461ff0019166101001790555b8160000361075b576040517fd5d57c0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606882905561076a8484610d0a565b80156107b0576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b60006107c2838361090c565b9392505050565b60006103f382600461090c565b60006103f382600261090c565b6107eb610a54565b6001600160a01b0381166108675760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016105dc565b61046581610cab565b60006103f382600361090c565b60006103f382600561090c565b60006068548261089985610b1c565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108fa9190611057565b6109049190611070565b119392505050565b6000610917836104e9565b80156107c257506065546040517f93c81dd60000000000000000000000000000000000000000000000000000000081526001600160a01b03909116906393c81dd6906109699086908690600401611091565b602060405180830381865afa158015610986573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c29190611035565b6001600160a01b0381163314610465576040517f5da224280000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016105dc565b6001600160a01b03811660009081526067602052604090205460ff16610465576040517fa65314ea0000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016105dc565b6033546001600160a01b031633146106505760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016105dc565b610aed816040518060400160405280601181526020017f5065726d697373696f6e4d616e61676572000000000000000000000000000000815250610d8f565b6065805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6000816001600160a01b0316636a3edf286040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b5c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f391906110be565b610bbf816040518060400160405280601181526020017f436f6d706c69616e6365466163746f7279000000000000000000000000000000815250610d8f565b6066805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6066546040517f12e396d60000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b03909116906312e396d690602401602060405180830381865afa158015610c4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c739190611035565b610650576040517f2ef76f650000000000000000000000000000000000000000000000000000000081523360048201526024016105dc565b603380546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610d755760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b60648201526084016105dc565b610d7d610dd1565b610d8682610aae565b6104d481610b80565b6001600160a01b0382166104d457806040517f6cbb46290000000000000000000000000000000000000000000000000000000081526004016105dc91906110db565b600054610100900460ff16610e3c5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b60648201526084016105dc565b610650600054610100900460ff16610eaa5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b60648201526084016105dc565b61065033610cab565b6001600160a01b038116811461046557600080fd5b60008060008060808587031215610ede57600080fd5b8435610ee981610eb3565b93506020850135610ef981610eb3565b9250604085013591506060850135610f1081610eb3565b939692955090935050565b600060208284031215610f2d57600080fd5b81356107c281610eb3565b600080600060608486031215610f4d57600080fd5b8335610f5881610eb3565b92506020840135610f6881610eb3565b929592945050506040919091013590565b60008060408385031215610f8c57600080fd5b8235610f9781610eb3565b946020939093013593505050565b60008060408385031215610fb857600080fd5b8235610fc381610eb3565b9150602083013560078110610fd757600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b60a081016004871061100c5761100c610fe2565b9581526001600160a01b0394851660208201529284166040840152921660608201526080015290565b60006020828403121561104757600080fd5b815180151581146107c257600080fd5b60006020828403121561106957600080fd5b5051919050565b808201808211156103f357634e487b7160e01b600052601160045260246000fd5b6001600160a01b038316815260408101600783106110b1576110b1610fe2565b8260208301529392505050565b6000602082840312156110d057600080fd5b81516107c281610eb3565b600060208083528351808285015260005b81811015611108578581018301518582016040015282016110ec565b506000604082860101526040601f19601f830116850101925050509291505056fea2646970667358221220030498d0598e97be185792b497302a9c50a8d1be4e4ee39e0aa6b5aa38b5f3f464736f6c63430008110033
Verified Source Code Full Match
Compiler: v0.8.17+commit.8df45f5f
EVM: london
Optimization: Yes (1000 runs)
LimitedSupplyComplianceModule.sol 107 lines
// SPDX-License-Identifier: MIT
/**
* Copyright © 2024 Frictionless Group Holdings S.à.r.l
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of the Frictionless protocol smart contracts
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FRICTIONLESS
* GROUP HOLDINGS OR ANY OF ITS SUBSIDIARIES BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
pragma solidity ^0.8.16;
import { IToken } from "@ERC-3643/token/IToken.sol";
import { IBasicFrictionlessToken } from "@interface/IBasicFrictionlessToken.sol";
import { IFrictionlessPermissionsManager } from "@interface/IFrictionlessPermissionsManager.sol";
import { AbstractFrictionlessComplianceModule } from "@rules/AbstractFrictionlessComplianceModule.sol";
/**
* @title LimitedSupplyComplianceModule Implementation of the compliance module for limited supply tokens
* @author Frictionless Group Holdings S.à.r.l
* @notice See {AbstractFrictionlessComplianceModule}
*/
contract LimitedSupplyComplianceModule is AbstractFrictionlessComplianceModule {
/// @dev error throw when attempting to set zero supply limit
error LimitedSupplyComplianceModuleZeroSupplyLimit();
/// @dev the token supply limit
uint256 public supplyLimit;
/**
* @dev Initiatizes the compliance module for limited supply tokens
* @param frictionlessPermissionsManager_ the address of the frictionless permissions manager
* @param complianceFactory_ the address of the compliance dfactory
* @param supplyLimit_ the token supply limit
*/
function init(
address frictionlessPermissionsManager_,
address complianceFactory_,
uint256 supplyLimit_
) external initializer {
if (supplyLimit_ == 0) {
revert LimitedSupplyComplianceModuleZeroSupplyLimit();
}
supplyLimit = supplyLimit_;
__AbstractFrictionlessComplianceModule_init(frictionlessPermissionsManager_, complianceFactory_);
}
/// @inheritdoc AbstractFrictionlessComplianceModule
function moduleTransferAction(
address from_,
address to_,
uint256 value_
) external virtual override onlyBoundCompliance {
emit FrictionlessTokenTransferred(
IBasicFrictionlessToken.FrictionlessTokenTypes.DIGITAL_SECURITY_TOKEN,
_getTokenBound(msg.sender),
from_,
to_,
value_
);
}
/// @inheritdoc AbstractFrictionlessComplianceModule
function moduleCheck(
address from_,
address /*to_*/,
uint256 value_,
address compliance_
) public view override returns (bool) {
if (from_ == address(0) && _checkSupplyLimit(compliance_, value_)) {
return false;
}
return true;
}
/// @inheritdoc AbstractFrictionlessComplianceModule
function _hasClaim(
address userAddress_,
IFrictionlessPermissionsManager.FrictionlessPermissionedUser userType_
) internal view override returns (bool) {
return isUser(userAddress_) && permissionsManager.hasClaim(userAddress_, userType_);
}
/**
* @dev Checks if adding the specified value to the current total supply would exceed the supply limit
* @param compliance_ the address of the compliance contract that is associated with the token
* @param value_ the amount to be added to the current total supply
* @return bool `true` if the total supply plus the value exceeds the supply limit, otherwise `false`.
*/
function _checkSupplyLimit(address compliance_, uint256 value_) private view returns (bool) {
return IToken(_getTokenBound(compliance_)).totalSupply() + value_ > supplyLimit;
}
}
IToken.sol 460 lines
// SPDX-License-Identifier: GPL-3.0
//
// :+#####%%%%%%%%%%%%%%+
// .-*@@@%+.:+%@@@@@%%#***%@@%=
// :=*%@@@#=. :#@@% *@@@%=
// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%-
// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#.
// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+
// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%-
// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%:
// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#.
// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*.
// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+
// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@-
// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#:
// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#-
// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%-
// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@#
// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+-
// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=:
// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+:
// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+.
// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+.
// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=.
// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=.
// @@@@@@+. +@@*. .+@@@@@%=.
// -@@@@@= =@@%: -#@@@@%+.
// +@@@@@. =@@@= .+@@@@@*:
// #@@@@#:%@@#. :*@@@@#-
// @@@@@%@@@= :#@@@@+.
// :@@@@@@@#.:#@@@%-
// +@@@@@@-.*@@@*:
// #@@@@#.=@@@+.
// @@@@+-%@%=
// :@@@#%@%=
// +@@@@%-
// :#%%=
//
/**
* NOTICE
*
* The T-REX software is licensed under a proprietary license or the GPL v.3.
* If you choose to receive it under the GPL v.3 license, the following applies:
* T-REX is a suite of smart contracts implementing the ERC-3643 standard and
* developed by Tokeny to manage and transfer financial assets on EVM blockchains
*
* Copyright (C) 2023, Tokeny sàrl.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity 0.8.17;
import "../registry/interface/IIdentityRegistry.sol";
import "../compliance/modular/IModularCompliance.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @dev interface
interface IToken is IERC20 {
/// events
/**
* this event is emitted when the token information is updated.
* the event is emitted by the token init function and by the setTokenInformation function
* `_newName` is the name of the token
* `_newSymbol` is the symbol of the token
* `_newDecimals` is the decimals of the token
* `_newVersion` is the version of the token, current version is 3.0
* `_newOnchainID` is the address of the onchainID of the token
*/
event UpdatedTokenInformation(string indexed _newName, string indexed _newSymbol, uint8 _newDecimals, string
_newVersion, address indexed _newOnchainID);
/**
* this event is emitted when the IdentityRegistry has been set for the token
* the event is emitted by the token constructor and by the setIdentityRegistry function
* `_identityRegistry` is the address of the Identity Registry of the token
*/
event IdentityRegistryAdded(address indexed _identityRegistry);
/**
* this event is emitted when the Compliance has been set for the token
* the event is emitted by the token constructor and by the setCompliance function
* `_compliance` is the address of the Compliance contract of the token
*/
event ComplianceAdded(address indexed _compliance);
/**
* this event is emitted when an investor successfully recovers his tokens
* the event is emitted by the recoveryAddress function
* `_lostWallet` is the address of the wallet that the investor lost access to
* `_newWallet` is the address of the wallet that the investor provided for the recovery
* `_investorOnchainID` is the address of the onchainID of the investor who asked for a recovery
*/
event RecoverySuccess(address indexed _lostWallet, address indexed _newWallet, address indexed _investorOnchainID);
/**
* this event is emitted when the wallet of an investor is frozen or unfrozen
* the event is emitted by setAddressFrozen and batchSetAddressFrozen functions
* `_userAddress` is the wallet of the investor that is concerned by the freezing status
* `_isFrozen` is the freezing status of the wallet
* if `_isFrozen` equals `true` the wallet is frozen after emission of the event
* if `_isFrozen` equals `false` the wallet is unfrozen after emission of the event
* `_owner` is the address of the agent who called the function to freeze the wallet
*/
event AddressFrozen(address indexed _userAddress, bool indexed _isFrozen, address indexed _owner);
/**
* this event is emitted when a certain amount of tokens is frozen on a wallet
* the event is emitted by freezePartialTokens and batchFreezePartialTokens functions
* `_userAddress` is the wallet of the investor that is concerned by the freezing status
* `_amount` is the amount of tokens that are frozen
*/
event TokensFrozen(address indexed _userAddress, uint256 _amount);
/**
* this event is emitted when a certain amount of tokens is unfrozen on a wallet
* the event is emitted by unfreezePartialTokens and batchUnfreezePartialTokens functions
* `_userAddress` is the wallet of the investor that is concerned by the freezing status
* `_amount` is the amount of tokens that are unfrozen
*/
event TokensUnfrozen(address indexed _userAddress, uint256 _amount);
/**
* this event is emitted when the token is paused
* the event is emitted by the pause function
* `_userAddress` is the address of the wallet that called the pause function
*/
event Paused(address _userAddress);
/**
* this event is emitted when the token is unpaused
* the event is emitted by the unpause function
* `_userAddress` is the address of the wallet that called the unpause function
*/
event Unpaused(address _userAddress);
/// functions
/**
* @dev sets the token name
* @param _name the name of token to set
* Only the owner of the token smart contract can call this function
* emits a `UpdatedTokenInformation` event
*/
function setName(string calldata _name) external;
/**
* @dev sets the token symbol
* @param _symbol the token symbol to set
* Only the owner of the token smart contract can call this function
* emits a `UpdatedTokenInformation` event
*/
function setSymbol(string calldata _symbol) external;
/**
* @dev sets the onchain ID of the token
* @param _onchainID the address of the onchain ID to set
* Only the owner of the token smart contract can call this function
* emits a `UpdatedTokenInformation` event
*/
function setOnchainID(address _onchainID) external;
/**
* @dev pauses the token contract, when contract is paused investors cannot transfer tokens anymore
* This function can only be called by a wallet set as agent of the token
* emits a `Paused` event
*/
function pause() external;
/**
* @dev unpauses the token contract, when contract is unpaused investors can transfer tokens
* if their wallet is not blocked & if the amount to transfer is <= to the amount of free tokens
* This function can only be called by a wallet set as agent of the token
* emits an `Unpaused` event
*/
function unpause() external;
/**
* @dev sets an address frozen status for this token.
* @param _userAddress The address for which to update frozen status
* @param _freeze Frozen status of the address
* This function can only be called by a wallet set as agent of the token
* emits an `AddressFrozen` event
*/
function setAddressFrozen(address _userAddress, bool _freeze) external;
/**
* @dev freezes token amount specified for given address.
* @param _userAddress The address for which to update frozen tokens
* @param _amount Amount of Tokens to be frozen
* This function can only be called by a wallet set as agent of the token
* emits a `TokensFrozen` event
*/
function freezePartialTokens(address _userAddress, uint256 _amount) external;
/**
* @dev unfreezes token amount specified for given address
* @param _userAddress The address for which to update frozen tokens
* @param _amount Amount of Tokens to be unfrozen
* This function can only be called by a wallet set as agent of the token
* emits a `TokensUnfrozen` event
*/
function unfreezePartialTokens(address _userAddress, uint256 _amount) external;
/**
* @dev sets the Identity Registry for the token
* @param _identityRegistry the address of the Identity Registry to set
* Only the owner of the token smart contract can call this function
* emits an `IdentityRegistryAdded` event
*/
function setIdentityRegistry(address _identityRegistry) external;
/**
* @dev sets the compliance contract of the token
* @param _compliance the address of the compliance contract to set
* Only the owner of the token smart contract can call this function
* calls bindToken on the compliance contract
* emits a `ComplianceAdded` event
*/
function setCompliance(address _compliance) external;
/**
* @dev force a transfer of tokens between 2 whitelisted wallets
* In case the `from` address has not enough free tokens (unfrozen tokens)
* but has a total balance higher or equal to the `amount`
* the amount of frozen tokens is reduced in order to have enough free tokens
* to proceed the transfer, in such a case, the remaining balance on the `from`
* account is 100% composed of frozen tokens post-transfer.
* Require that the `to` address is a verified address,
* @param _from The address of the sender
* @param _to The address of the receiver
* @param _amount The number of tokens to transfer
* @return `true` if successful and revert if unsuccessful
* This function can only be called by a wallet set as agent of the token
* emits a `TokensUnfrozen` event if `_amount` is higher than the free balance of `_from`
* emits a `Transfer` event
*/
function forcedTransfer(
address _from,
address _to,
uint256 _amount
) external returns (bool);
/**
* @dev mint tokens on a wallet
* Improved version of default mint method. Tokens can be minted
* to an address if only it is a verified address as per the security token.
* @param _to Address to mint the tokens to.
* @param _amount Amount of tokens to mint.
* This function can only be called by a wallet set as agent of the token
* emits a `Transfer` event
*/
function mint(address _to, uint256 _amount) external;
/**
* @dev burn tokens on a wallet
* In case the `account` address has not enough free tokens (unfrozen tokens)
* but has a total balance higher or equal to the `value` amount
* the amount of frozen tokens is reduced in order to have enough free tokens
* to proceed the burn, in such a case, the remaining balance on the `account`
* is 100% composed of frozen tokens post-transaction.
* @param _userAddress Address to burn the tokens from.
* @param _amount Amount of tokens to burn.
* This function can only be called by a wallet set as agent of the token
* emits a `TokensUnfrozen` event if `_amount` is higher than the free balance of `_userAddress`
* emits a `Transfer` event
*/
function burn(address _userAddress, uint256 _amount) external;
/**
* @dev recovery function used to force transfer tokens from a
* lost wallet to a new wallet for an investor.
* @param _lostWallet the wallet that the investor lost
* @param _newWallet the newly provided wallet on which tokens have to be transferred
* @param _investorOnchainID the onchainID of the investor asking for a recovery
* This function can only be called by a wallet set as agent of the token
* emits a `TokensUnfrozen` event if there is some frozen tokens on the lost wallet if the recovery process is successful
* emits a `Transfer` event if the recovery process is successful
* emits a `RecoverySuccess` event if the recovery process is successful
* emits a `RecoveryFails` event if the recovery process fails
*/
function recoveryAddress(
address _lostWallet,
address _newWallet,
address _investorOnchainID
) external returns (bool);
/**
* @dev function allowing to issue transfers in batch
* Require that the msg.sender and `to` addresses are not frozen.
* Require that the total value should not exceed available balance.
* Require that the `to` addresses are all verified addresses,
* IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH,
* USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION
* @param _toList The addresses of the receivers
* @param _amounts The number of tokens to transfer to the corresponding receiver
* emits _toList.length `Transfer` events
*/
function batchTransfer(address[] calldata _toList, uint256[] calldata _amounts) external;
/**
* @dev function allowing to issue forced transfers in batch
* Require that `_amounts[i]` should not exceed available balance of `_fromList[i]`.
* Require that the `_toList` addresses are all verified addresses
* IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_fromList.length` IS TOO HIGH,
* USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION
* @param _fromList The addresses of the senders
* @param _toList The addresses of the receivers
* @param _amounts The number of tokens to transfer to the corresponding receiver
* This function can only be called by a wallet set as agent of the token
* emits `TokensUnfrozen` events if `_amounts[i]` is higher than the free balance of `_fromList[i]`
* emits _fromList.length `Transfer` events
*/
function batchForcedTransfer(
address[] calldata _fromList,
address[] calldata _toList,
uint256[] calldata _amounts
) external;
/**
* @dev function allowing to mint tokens in batch
* Require that the `_toList` addresses are all verified addresses
* IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH,
* USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION
* @param _toList The addresses of the receivers
* @param _amounts The number of tokens to mint to the corresponding receiver
* This function can only be called by a wallet set as agent of the token
* emits _toList.length `Transfer` events
*/
function batchMint(address[] calldata _toList, uint256[] calldata _amounts) external;
/**
* @dev function allowing to burn tokens in batch
* Require that the `_userAddresses` addresses are all verified addresses
* IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH,
* USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION
* @param _userAddresses The addresses of the wallets concerned by the burn
* @param _amounts The number of tokens to burn from the corresponding wallets
* This function can only be called by a wallet set as agent of the token
* emits _userAddresses.length `Transfer` events
*/
function batchBurn(address[] calldata _userAddresses, uint256[] calldata _amounts) external;
/**
* @dev function allowing to set frozen addresses in batch
* IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH,
* USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION
* @param _userAddresses The addresses for which to update frozen status
* @param _freeze Frozen status of the corresponding address
* This function can only be called by a wallet set as agent of the token
* emits _userAddresses.length `AddressFrozen` events
*/
function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external;
/**
* @dev function allowing to freeze tokens partially in batch
* IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH,
* USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION
* @param _userAddresses The addresses on which tokens need to be frozen
* @param _amounts the amount of tokens to freeze on the corresponding address
* This function can only be called by a wallet set as agent of the token
* emits _userAddresses.length `TokensFrozen` events
*/
function batchFreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external;
/**
* @dev function allowing to unfreeze tokens partially in batch
* IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH,
* USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION
* @param _userAddresses The addresses on which tokens need to be unfrozen
* @param _amounts the amount of tokens to unfreeze on the corresponding address
* This function can only be called by a wallet set as agent of the token
* emits _userAddresses.length `TokensUnfrozen` events
*/
function batchUnfreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external;
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 1 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* balanceOf() and transfer().
*/
function decimals() external view returns (uint8);
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the address of the onchainID of the token.
* the onchainID of the token gives all the information available
* about the token and is managed by the token issuer or his agent.
*/
function onchainID() external view returns (address);
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the TREX version of the token.
* current version is 3.0.0
*/
function version() external view returns (string memory);
/**
* @dev Returns the Identity Registry linked to the token
*/
function identityRegistry() external view returns (IIdentityRegistry);
/**
* @dev Returns the Compliance contract linked to the token
*/
function compliance() external view returns (IModularCompliance);
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() external view returns (bool);
/**
* @dev Returns the freezing status of a wallet
* if isFrozen returns `true` the wallet is frozen
* if isFrozen returns `false` the wallet is not frozen
* isFrozen returning `true` doesn't mean that the balance is free, tokens could be blocked by
* a partial freeze or the whole token could be blocked by pause
* @param _userAddress the address of the wallet on which isFrozen is called
*/
function isFrozen(address _userAddress) external view returns (bool);
/**
* @dev Returns the amount of tokens that are partially frozen on a wallet
* the amount of frozen tokens is always <= to the total balance of the wallet
* @param _userAddress the address of the wallet on which getFrozenTokens is called
*/
function getFrozenTokens(address _userAddress) external view returns (uint256);
}
IBasicFrictionlessToken.sol 53 lines
// SPDX-License-Identifier: MIT
/**
* Copyright © 2024 Frictionless Group Holdings S.à.r.l
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of the Frictionless protocol smart contracts
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FRICTIONLESS GROUP
* HOLDINGS S.à.r.l OR AN OF ITS SUBSIDIARIES BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
pragma solidity ^0.8.16;
import { IToken } from "@ERC-3643/token/IToken.sol";
/**
* @title IBasicFrictionlessToken - Represents the base interface for Frictionless protocol tokens.
* @author Frictionless Group Holdings S.à.r.l
* @notice The IBasicFrictionlessToken Represents the base interface for Frictionless protocol tokens, this interface is used to determine a token type.
*/
interface IBasicFrictionlessToken is IToken {
/**
* @dev Enumeration to represent each of the tokens in the Frictionless protocol.
*/
enum FrictionlessTokenTypes {
NONE,
FUND_DEPOSIT_TOKEN, // IFrictionlessFundDepositToken
DIGITAL_SECURITY_TOKEN, // IFrictionlessDigitalSecurityToken
ON_CHAIN_ASSET_TOKEN // IFrictionlessOnChainAssetToken
}
/// @dev error thrown if an attempt to set an invalid token type during function `setFrictionlessTokenType`
error BasicFrictionlessTokenUnableToUpdateFrictionlessTokenType();
/**
* @dev Sets the token type according to the specified enumeration
* @param newTokenType_ the token type to set
*/
function setFrictionlessTokenType(FrictionlessTokenTypes newTokenType_) external;
/**
* @dev Returns the token type according to the specified enumeration
* @return FrictionlessTokenTypes the token type according to the specified enumeration
*/
function getFrictionlessTokenType() external view returns (FrictionlessTokenTypes);
}
IFrictionlessPermissionsManager.sol 150 lines
// SPDX-License-Identifier: MIT
/**
* Copyright © 2024 Frictionless Group Holdings S.à.r.l
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of the Frictionless protocol smart contracts
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FRICTIONLESS GROUP
* HOLDINGS S.à.r.l OR AN OF ITS SUBSIDIARIES BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
pragma solidity ^0.8.16;
/**
* @title IFrictionlessPermissionsManager - Manages the permission of participants in the Frictionless protocol
* @author Frictionless Group Holdings S.à.r.l
* @notice The IFrictionlessPermissionsManager is responsible for the management of permission of the various participants in
* the Frictionless protocol. The roles and responsibilities are defined in the public README for the Frictionless protocol at
* https://gitlab.com/dfyclabs/protocol/dfyclabs-tokens/-/blob/main/README.md?ref_type=heads#roles-responsibilities
*/
interface IFrictionlessPermissionsManager {
/// @dev Enum of the Frictionless protocol participants.
enum FrictionlessPermissionedUser {
PROTOCOL_TREASURY,
PERMISSIONED_CUSTODIAN,
PERMISSIONED_INVESTOR,
PERMISSIONED_MANAGER,
PERMISSIONED_CALCULATING_AGENT,
PERMISSIONED_TRANSFER_AGENT,
PERMISSIONED_FUND_ACCOUNTANT
}
/// @dev throws if specific address is zero.
error FrictionlessIsZeroAddress(string);
/// @dev throws if treasury tries to add or remove treasury.
error FrictionlessInvalidPermissionForTreasury();
/// @dev throws if user is not a permissioned investor
error FrictionlessUserIsNotPermssionedInvestor();
/**
* @dev Emitted when a user is added to the Frictionless protocol. This event is emitted by the `addUser` function.
* @param userIdentity the address of the user's OnChainId (Identity)
* @param userType the type of the user as per the enum
* @param claimURI the URI of the off-chain claim for the user. i.e. The Frictionless Markets graphQL endpoint
*/
event FrictionlessPermissionedUserAdded(address userIdentity, uint256 userType, string claimURI);
/**
* @dev Emitted when a user is registered in the Frictionless protocol. This event is emitted by the `registerIdentity` function.
* @param userAddress the address of the user's wallet to register
* @param userISOCountry the ISO 3166-1 numeric code of the user, can be the place of residence or the location KYC/AML onboarding was undertaken.
*/
event FrictionlessPermissionedUserRegistered(address userAddress, uint16 userISOCountry);
/**
* @dev Emitted when a user is removed in the Frictionless protocol. This event is emitted by the `removeUser` function.
* @param userAddress the address of the user's wallet to register
*/
event FrictionlessPermissionedUserRemoved(address userAddress);
/// @dev the internal struct defining a Claim for a PERMISSIONED_USER in the protocol. Used to submit claims for the OnChainId by the ClaimIssuer.
struct Claim {
address issuer;
uint256 topic;
uint8 scheme;
address identity;
bytes signature;
bytes data;
}
/**
* @dev Validates if a wallet address is permissioned in the Frictionless protocol
* @param userAddress the wallet address to verify
* @return true if the address is permissioned in the Frictionless Protocol.
*/
function isPermissioned(address userAddress) external view returns (bool);
/**
* @dev Registers a users wallet address as an OnChainId (Identity) to the Frictionless protocol.
* This Identity is used when permissioning a user to the protocol by invoking the addUser function later.
* @param userAddress the address of the user's wallet to register
* @param userISOCountry the ISO 3166-1 numeric code of the user, can be the place of residence or the location KYC/AML onboarding was undertaken.
* requires The msg.sender to have the TREX Agent permissions (PROTOCOL_TREASURY or PROTOCOL_ADMIN)
* @return address the address of the user's OnChainId (Identity) with the associated claims.
*/
function registerIdentity(address userAddress, uint16 userISOCountry) external returns (address);
/**
* @dev Gets a users OnChainId (Identity) in the Frictionless protocol.
* @param userAddress the address of the user's wallet to register
* requires The msg.sender to have the TREX Agent permissions (PROTOCOL_TREASURY or PROTOCOL_ADMIN)
* @return address the address of the user's OnChainId (Identity) with the associated claims.
*/
function getIdentity(address userAddress) external returns (address);
/**
* @dev Get the signed claimData message to be used in the addUser function.
* The message must be signed using the PK of the ClaimIssuer (PROTOCOL_ADMIN)
* @param userIdentity the address of the user's OnChainId (Identity)
* @param userType the type of the user as per the enum
* @return signed claimData message to be used in the addUser unction once signed by the ClaimIssuer PK.
*/
function getClaimMsgHash(
address userIdentity,
IFrictionlessPermissionsManager.FrictionlessPermissionedUser userType
) external view returns (bytes32);
/**
* @dev verify if the userAddress is permissioned in the Frictionless protocol and has a valid claim
* @param userAddress the address of the user's wallet to verify
* @param userType the type of the user as per the enum
* @return true if a valid permissioned user and has a valid claim, otherwise false.
*/
function hasClaim(address userAddress, FrictionlessPermissionedUser userType) external view returns (bool);
/**
* @dev Adds a user's OnChainId (Identity) to the Frictionless protocol along with its associated claim data.
* The Identity is created by invoking the registerIdentity function first.
* @param userIdentity the address of the user's OnChainId (Identity)
* @param userType the type of the user as per the enum
* @param claimSignature the signed claimData by the ClaimIssuer
* @param claimURI the URI of the off-chain claim for the user. i.e. The Frictionless Markets graphQL endpoint
* requires The msg.sender to be the Owner if the userType is the PROTOCOL_TREASURY
* requires The msg.sender to have the TREX Agent permissions (PROTOCOL_TREASURY or PROTOCOL_ADMIN) to add any user
* @return address the address of the user's OnChainId (Identity) with the associated claims.
*/
function addUser(
address userIdentity,
FrictionlessPermissionedUser userType,
bytes memory claimSignature,
string memory claimURI
) external returns (address);
/**
* @dev Removes a user from the Frictionless protocol along with its associated claim data.
* @param userAddress the address of the user's wallet
* requires The msg.sender to have the TREX Agent permissions (PROTOCOL_TREASURY or PROTOCOL_ADMIN) to remove any user
* @return true if the user is removed from the Frictionless protocol along with its associated claim data, otherwise false.
*/
function removeUser(address userAddress) external returns (bool);
}
AbstractFrictionlessComplianceModule.sol 254 lines
// SPDX-License-Identifier: MIT
/**
* Copyright © 2024 Frictionless Group Holdings S.à.r.l
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of the Frictionless protocol smart contracts
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FRICTIONLESS GROUP
* HOLDINGS S.à.r.l OR AN OF ITS SUBSIDIARIES BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
pragma solidity ^0.8.16;
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { IModule } from "@ERC-3643/compliance/modular/modules/IModule.sol";
import { IModularCompliance } from "@ERC-3643/compliance/modular/IModularCompliance.sol";
import { IFrictionlessPermissionsManager } from "@interface/IFrictionlessPermissionsManager.sol";
import { IFrictionlessComplianceFactory } from "@interface/IFrictionlessComplianceFactory.sol";
import { IFrictionlessComplianceModule } from "@interface/rules/IFrictionlessComplianceModule.sol";
/**
* @title AbstractFrictionlessComplianceModule Abstract implementation of the compliance module for the Frictionless protocol.
* @author Frictionless Group Holdings S.à.r.l
* @notice See {IFrictionlessComplianceModule}
*/
abstract contract AbstractFrictionlessComplianceModule is IFrictionlessComplianceModule, OwnableUpgradeable {
/// @dev The IFrictionlessPermissionsManager for the protocol.
IFrictionlessPermissionsManager public override permissionsManager;
/// @dev The IFrictionlessComplianceFactory for the protocol.
IFrictionlessComplianceFactory public override complianceFactory;
/// @dev the mapping of compliance modules bound.
mapping(address => bool) private _complianceBound;
/// @dev modifier used to very that only approved compliance modules can be used.
modifier onlyApprovedCompliance() {
_onlyApprovedCompliance();
_;
}
/// @dev modifier used to very that only modules that are bound, can be used.
modifier onlyBoundCompliance() {
_onlyBoundCompliance(msg.sender);
_;
}
/**
* @dev Initiatizes the compliance module
* @param frictionlessPermissionsManager_ the address of the frictionless permissions manager
* @param complianceFactory_ the address of the compliance dfactory
*/
function __AbstractFrictionlessComplianceModule_init(
address frictionlessPermissionsManager_,
address complianceFactory_
) internal onlyInitializing {
__Ownable_init();
_setPermissionsManager(frictionlessPermissionsManager_);
_setComplianceFactory(complianceFactory_);
}
/// @inheritdoc IFrictionlessComplianceModule
function setPermissionsManager(address frictionlessPermissionsManager_) external override onlyOwner {
_setPermissionsManager(frictionlessPermissionsManager_);
}
/// @inheritdoc IFrictionlessComplianceModule
function setComplianceFactory(address newComplianceFactory_) external override onlyOwner {
_setComplianceFactory(newComplianceFactory_);
}
/// @inheritdoc IModule
function bindCompliance(address compliance_) external override onlyApprovedCompliance {
_onlyValidCompliance(compliance_);
if (isComplianceBound(compliance_)) {
revert FrictionlessComplianceModuleComplianceIsAlreadyBound(compliance_);
}
_complianceBound[compliance_] = true;
emit ComplianceBound(compliance_);
}
/// @inheritdoc IModule
function unbindCompliance(address compliance_) external override {
_onlyValidCompliance(compliance_);
_onlyBoundCompliance(compliance_);
_complianceBound[compliance_] = false;
emit ComplianceUnbound(compliance_);
}
/// @inheritdoc IModule
function moduleTransferAction(
address from_,
address to_,
uint256 value_
) external virtual override onlyBoundCompliance {} // solhint-disable-line no-empty-blocks
/// @inheritdoc IModule
function moduleMintAction(address to_, uint256 value_) external virtual override onlyBoundCompliance {} // solhint-disable-line no-empty-blocks
/// @inheritdoc IModule
function moduleBurnAction(address from_, uint256 value_) external virtual override onlyBoundCompliance {} // solhint-disable-line no-empty-blocks
/// @inheritdoc IModule
function moduleCheck(
address /*from_*/,
address /*to_*/,
uint256 /*value_*/,
address /*compliance_*/
) public view virtual override returns (bool) {
return true;
}
/// @inheritdoc IModule
function isComplianceBound(address compliance_) public view override returns (bool) {
return _complianceBound[compliance_];
}
/// @inheritdoc IFrictionlessComplianceModule
function isTreasury(address userAddress_) public view override returns (bool) {
return _hasClaim(userAddress_, IFrictionlessPermissionsManager.FrictionlessPermissionedUser.PROTOCOL_TREASURY);
}
/// @inheritdoc IFrictionlessComplianceModule
function isCustodian(address userAddress_) public view override returns (bool) {
return
_hasClaim(
userAddress_,
IFrictionlessPermissionsManager.FrictionlessPermissionedUser.PERMISSIONED_CUSTODIAN
);
}
/// @inheritdoc IFrictionlessComplianceModule
function isInvestor(address userAddress_) public view override returns (bool) {
return
_hasClaim(userAddress_, IFrictionlessPermissionsManager.FrictionlessPermissionedUser.PERMISSIONED_INVESTOR);
}
/// @inheritdoc IFrictionlessComplianceModule
function isManager(address userAddress_) public view override returns (bool) {
return
_hasClaim(userAddress_, IFrictionlessPermissionsManager.FrictionlessPermissionedUser.PERMISSIONED_MANAGER);
}
/// @inheritdoc IFrictionlessComplianceModule
function isCalculatingAgent(address userAddress_) public view override returns (bool) {
return
_hasClaim(
userAddress_,
IFrictionlessPermissionsManager.FrictionlessPermissionedUser.PERMISSIONED_CALCULATING_AGENT
);
}
/// @inheritdoc IFrictionlessComplianceModule
function isTransferAgent(address userAddress_) public view override returns (bool) {
return
_hasClaim(
userAddress_,
IFrictionlessPermissionsManager.FrictionlessPermissionedUser.PERMISSIONED_TRANSFER_AGENT
);
}
/// @inheritdoc IFrictionlessComplianceModule
function isFundAccountant(address userAddress_) public view override returns (bool) {
return
_hasClaim(
userAddress_,
IFrictionlessPermissionsManager.FrictionlessPermissionedUser.PERMISSIONED_FUND_ACCOUNTANT
);
}
/// @inheritdoc IFrictionlessComplianceModule
function isUser(address userAddress_) public view override returns (bool) {
return permissionsManager.isPermissioned(userAddress_);
}
/// @inheritdoc IFrictionlessComplianceModule
function hasClaim(
address userAddress_,
IFrictionlessPermissionsManager.FrictionlessPermissionedUser userType_
) public view override returns (bool) {
return _hasClaim(userAddress_, userType_);
}
/// @dev Impl of the setting of the permissions manager
function _setPermissionsManager(address newFrictionlessPermissionsManager_) internal {
_checkZeroAddr(newFrictionlessPermissionsManager_, "PermissionManager");
permissionsManager = IFrictionlessPermissionsManager(newFrictionlessPermissionsManager_);
}
/// @dev Impl of the setting of the compliance factory
function _setComplianceFactory(address newComplianceFactory_) internal {
_checkZeroAddr(newComplianceFactory_, "ComplianceFactory");
complianceFactory = IFrictionlessComplianceFactory(newComplianceFactory_);
}
/// @dev Verifies if an address is a zero address
function _checkZeroAddr(address addrToCheck_, string memory addrName_) internal pure {
if (addrToCheck_ == address(0)) {
revert FrictionlessIsZeroAddress(addrName_);
}
}
/// @dev Verifies if the compliance_ address supplied is bound as per the mapping
function _onlyBoundCompliance(address compliance_) internal view virtual {
if (!isComplianceBound(compliance_)) {
revert FrictionlessComplianceModuleNotABoundCompliance(compliance_);
}
}
/// @dev Verifies if the compliance_ address supplied is valid
function _onlyValidCompliance(address compliance_) internal view {
if (compliance_ != msg.sender) {
revert FrictionlessComplianceModuleInvalidCompliance(compliance_);
}
}
/// @dev Verifies if the `msg.sender` is a valid modular compliance
function _onlyApprovedCompliance() internal view {
if (!complianceFactory.isModularCompliance(msg.sender)) {
revert FrictionlessComplianceModuleNotAnApprovedCompliance(msg.sender);
}
}
/// @dev Returns the address of the IToken bound by this compliance
function _getTokenBound(address compliance_) internal view returns (address) {
return IModularCompliance(compliance_).getTokenBound();
}
/**
* @dev Determine if a claim is present for a given userAddress and userType
* @param userAddress_ the address if the users wallet
* @param userType_ the type of the user as defined by the enumeration `IFrictionlessPermissionsManager.FrictionlessPermissionedUser`
*/
function _hasClaim(
address userAddress_,
IFrictionlessPermissionsManager.FrictionlessPermissionedUser userType_
) internal view virtual returns (bool);
}
IIdentityRegistry.sol 255 lines
// SPDX-License-Identifier: GPL-3.0
//
// :+#####%%%%%%%%%%%%%%+
// .-*@@@%+.:+%@@@@@%%#***%@@%=
// :=*%@@@#=. :#@@% *@@@%=
// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%-
// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#.
// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+
// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%-
// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%:
// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#.
// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*.
// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+
// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@-
// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#:
// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#-
// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%-
// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@#
// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+-
// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=:
// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+:
// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+.
// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+.
// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=.
// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=.
// @@@@@@+. +@@*. .+@@@@@%=.
// -@@@@@= =@@%: -#@@@@%+.
// +@@@@@. =@@@= .+@@@@@*:
// #@@@@#:%@@#. :*@@@@#-
// @@@@@%@@@= :#@@@@+.
// :@@@@@@@#.:#@@@%-
// +@@@@@@-.*@@@*:
// #@@@@#.=@@@+.
// @@@@+-%@%=
// :@@@#%@%=
// +@@@@%-
// :#%%=
//
/**
* NOTICE
*
* The T-REX software is licensed under a proprietary license or the GPL v.3.
* If you choose to receive it under the GPL v.3 license, the following applies:
* T-REX is a suite of smart contracts implementing the ERC-3643 standard and
* developed by Tokeny to manage and transfer financial assets on EVM blockchains
*
* Copyright (C) 2023, Tokeny sàrl.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity 0.8.17;
import "./ITrustedIssuersRegistry.sol";
import "./IClaimTopicsRegistry.sol";
import "./IIdentityRegistryStorage.sol";
import "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol";
import "@onchain-id/solidity/contracts/interface/IIdentity.sol";
interface IIdentityRegistry {
/**
* this event is emitted when the ClaimTopicsRegistry has been set for the IdentityRegistry
* the event is emitted by the IdentityRegistry constructor
* `claimTopicsRegistry` is the address of the Claim Topics Registry contract
*/
event ClaimTopicsRegistrySet(address indexed claimTopicsRegistry);
/**
* this event is emitted when the IdentityRegistryStorage has been set for the IdentityRegistry
* the event is emitted by the IdentityRegistry constructor
* `identityStorage` is the address of the Identity Registry Storage contract
*/
event IdentityStorageSet(address indexed identityStorage);
/**
* this event is emitted when the TrustedIssuersRegistry has been set for the IdentityRegistry
* the event is emitted by the IdentityRegistry constructor
* `trustedIssuersRegistry` is the address of the Trusted Issuers Registry contract
*/
event TrustedIssuersRegistrySet(address indexed trustedIssuersRegistry);
/**
* this event is emitted when an Identity is registered into the Identity Registry.
* the event is emitted by the 'registerIdentity' function
* `investorAddress` is the address of the investor's wallet
* `identity` is the address of the Identity smart contract (onchainID)
*/
event IdentityRegistered(address indexed investorAddress, IIdentity indexed identity);
/**
* this event is emitted when an Identity is removed from the Identity Registry.
* the event is emitted by the 'deleteIdentity' function
* `investorAddress` is the address of the investor's wallet
* `identity` is the address of the Identity smart contract (onchainID)
*/
event IdentityRemoved(address indexed investorAddress, IIdentity indexed identity);
/**
* this event is emitted when an Identity has been updated
* the event is emitted by the 'updateIdentity' function
* `oldIdentity` is the old Identity contract's address to update
* `newIdentity` is the new Identity contract's
*/
event IdentityUpdated(IIdentity indexed oldIdentity, IIdentity indexed newIdentity);
/**
* this event is emitted when an Identity's country has been updated
* the event is emitted by the 'updateCountry' function
* `investorAddress` is the address on which the country has been updated
* `country` is the numeric code (ISO 3166-1) of the new country
*/
event CountryUpdated(address indexed investorAddress, uint16 indexed country);
/**
* @dev Register an identity contract corresponding to a user address.
* Requires that the user doesn't have an identity contract already registered.
* This function can only be called by a wallet set as agent of the smart contract
* @param _userAddress The address of the user
* @param _identity The address of the user's identity contract
* @param _country The country of the investor
* emits `IdentityRegistered` event
*/
function registerIdentity(
address _userAddress,
IIdentity _identity,
uint16 _country
) external;
/**
* @dev Removes an user from the identity registry.
* Requires that the user have an identity contract already deployed that will be deleted.
* This function can only be called by a wallet set as agent of the smart contract
* @param _userAddress The address of the user to be removed
* emits `IdentityRemoved` event
*/
function deleteIdentity(address _userAddress) external;
/**
* @dev Replace the actual identityRegistryStorage contract with a new one.
* This function can only be called by the wallet set as owner of the smart contract
* @param _identityRegistryStorage The address of the new Identity Registry Storage
* emits `IdentityStorageSet` event
*/
function setIdentityRegistryStorage(address _identityRegistryStorage) external;
/**
* @dev Replace the actual claimTopicsRegistry contract with a new one.
* This function can only be called by the wallet set as owner of the smart contract
* @param _claimTopicsRegistry The address of the new claim Topics Registry
* emits `ClaimTopicsRegistrySet` event
*/
function setClaimTopicsRegistry(address _claimTopicsRegistry) external;
/**
* @dev Replace the actual trustedIssuersRegistry contract with a new one.
* This function can only be called by the wallet set as owner of the smart contract
* @param _trustedIssuersRegistry The address of the new Trusted Issuers Registry
* emits `TrustedIssuersRegistrySet` event
*/
function setTrustedIssuersRegistry(address _trustedIssuersRegistry) external;
/**
* @dev Updates the country corresponding to a user address.
* Requires that the user should have an identity contract already deployed that will be replaced.
* This function can only be called by a wallet set as agent of the smart contract
* @param _userAddress The address of the user
* @param _country The new country of the user
* emits `CountryUpdated` event
*/
function updateCountry(address _userAddress, uint16 _country) external;
/**
* @dev Updates an identity contract corresponding to a user address.
* Requires that the user address should be the owner of the identity contract.
* Requires that the user should have an identity contract already deployed that will be replaced.
* This function can only be called by a wallet set as agent of the smart contract
* @param _userAddress The address of the user
* @param _identity The address of the user's new identity contract
* emits `IdentityUpdated` event
*/
function updateIdentity(address _userAddress, IIdentity _identity) external;
/**
* @dev function allowing to register identities in batch
* This function can only be called by a wallet set as agent of the smart contract
* Requires that none of the users has an identity contract already registered.
* IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH,
* USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION
* @param _userAddresses The addresses of the users
* @param _identities The addresses of the corresponding identity contracts
* @param _countries The countries of the corresponding investors
* emits _userAddresses.length `IdentityRegistered` events
*/
function batchRegisterIdentity(
address[] calldata _userAddresses,
IIdentity[] calldata _identities,
uint16[] calldata _countries
) external;
/**
* @dev This functions checks whether a wallet has its Identity registered or not
* in the Identity Registry.
* @param _userAddress The address of the user to be checked.
* @return 'True' if the address is contained in the Identity Registry, 'false' if not.
*/
function contains(address _userAddress) external view returns (bool);
/**
* @dev This functions checks whether an identity contract
* corresponding to the provided user address has the required claims or not based
* on the data fetched from trusted issuers registry and from the claim topics registry
* @param _userAddress The address of the user to be verified.
* @return 'True' if the address is verified, 'false' if not.
*/
function isVerified(address _userAddress) external view returns (bool);
/**
* @dev Returns the onchainID of an investor.
* @param _userAddress The wallet of the investor
*/
function identity(address _userAddress) external view returns (IIdentity);
/**
* @dev Returns the country code of an investor.
* @param _userAddress The wallet of the investor
*/
function investorCountry(address _userAddress) external view returns (uint16);
/**
* @dev Returns the IdentityRegistryStorage linked to the current IdentityRegistry.
*/
function identityStorage() external view returns (IIdentityRegistryStorage);
/**
* @dev Returns the TrustedIssuersRegistry linked to the current IdentityRegistry.
*/
function issuersRegistry() external view returns (ITrustedIssuersRegistry);
/**
* @dev Returns the ClaimTopicsRegistry linked to the current IdentityRegistry.
*/
function topicsRegistry() external view returns (IClaimTopicsRegistry);
}
IModularCompliance.sol 230 lines
// SPDX-License-Identifier: GPL-3.0
//
// :+#####%%%%%%%%%%%%%%+
// .-*@@@%+.:+%@@@@@%%#***%@@%=
// :=*%@@@#=. :#@@% *@@@%=
// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%-
// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#.
// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+
// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%-
// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%:
// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#.
// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*.
// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+
// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@-
// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#:
// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#-
// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%-
// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@#
// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+-
// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=:
// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+:
// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+.
// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+.
// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=.
// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=.
// @@@@@@+. +@@*. .+@@@@@%=.
// -@@@@@= =@@%: -#@@@@%+.
// +@@@@@. =@@@= .+@@@@@*:
// #@@@@#:%@@#. :*@@@@#-
// @@@@@%@@@= :#@@@@+.
// :@@@@@@@#.:#@@@%-
// +@@@@@@-.*@@@*:
// #@@@@#.=@@@+.
// @@@@+-%@%=
// :@@@#%@%=
// +@@@@%-
// :#%%=
//
/**
* NOTICE
*
* The T-REX software is licensed under a proprietary license or the GPL v.3.
* If you choose to receive it under the GPL v.3 license, the following applies:
* T-REX is a suite of smart contracts implementing the ERC-3643 standard and
* developed by Tokeny to manage and transfer financial assets on EVM blockchains
*
* Copyright (C) 2023, Tokeny sàrl.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity 0.8.17;
interface IModularCompliance {
/// events
/**
* @dev Event emitted for each executed interaction with a module contract.
* For gas efficiency, only the interaction calldata selector (first 4
* bytes) is included in the event. For interactions without calldata or
* whose calldata is shorter than 4 bytes, the selector will be `0`.
*/
event ModuleInteraction(address indexed target, bytes4 selector);
/**
* this event is emitted when a token has been bound to the compliance contract
* the event is emitted by the bindToken function
* `_token` is the address of the token to bind
*/
event TokenBound(address _token);
/**
* this event is emitted when a token has been unbound from the compliance contract
* the event is emitted by the unbindToken function
* `_token` is the address of the token to unbind
*/
event TokenUnbound(address _token);
/**
* this event is emitted when a module has been added to the list of modules bound to the compliance contract
* the event is emitted by the addModule function
* `_module` is the address of the compliance module
*/
event ModuleAdded(address indexed _module);
/**
* this event is emitted when a module has been removed from the list of modules bound to the compliance contract
* the event is emitted by the removeModule function
* `_module` is the address of the compliance module
*/
event ModuleRemoved(address indexed _module);
/// functions
/**
* @dev binds a token to the compliance contract
* @param _token address of the token to bind
* This function can be called ONLY by the owner of the compliance contract
* Emits a TokenBound event
*/
function bindToken(address _token) external;
/**
* @dev unbinds a token from the compliance contract
* @param _token address of the token to unbind
* This function can be called ONLY by the owner of the compliance contract
* Emits a TokenUnbound event
*/
function unbindToken(address _token) external;
/**
* @dev adds a module to the list of compliance modules
* @param _module address of the module to add
* there cannot be more than 25 modules bound to the modular compliance for gas cost reasons
* This function can be called ONLY by the owner of the compliance contract
* Emits a ModuleAdded event
*/
function addModule(address _module) external;
/**
* @dev removes a module from the list of compliance modules
* @param _module address of the module to remove
* This function can be called ONLY by the owner of the compliance contract
* Emits a ModuleRemoved event
*/
function removeModule(address _module) external;
/**
* @dev calls any function on bound modules
* can be called only on bound modules
* @param callData the bytecode for interaction with the module, abi encoded
* @param _module The address of the module
* This function can be called only by the modular compliance owner
* emits a `ModuleInteraction` event
*/
function callModuleFunction(bytes calldata callData, address _module) external;
/**
* @dev function called whenever tokens are transferred
* from one wallet to another
* this function can update state variables in the modules bound to the compliance
* these state variables being used by the module checks to decide if a transfer
* is compliant or not depending on the values stored in these state variables and on
* the parameters of the modules
* This function can be called ONLY by the token contract bound to the compliance
* @param _from The address of the sender
* @param _to The address of the receiver
* @param _amount The amount of tokens involved in the transfer
* This function calls moduleTransferAction() on each module bound to the compliance contract
*/
function transferred(
address _from,
address _to,
uint256 _amount
) external;
/**
* @dev function called whenever tokens are created on a wallet
* this function can update state variables in the modules bound to the compliance
* these state variables being used by the module checks to decide if a transfer
* is compliant or not depending on the values stored in these state variables and on
* the parameters of the modules
* This function can be called ONLY by the token contract bound to the compliance
* @param _to The address of the receiver
* @param _amount The amount of tokens involved in the minting
* This function calls moduleMintAction() on each module bound to the compliance contract
*/
function created(address _to, uint256 _amount) external;
/**
* @dev function called whenever tokens are destroyed from a wallet
* this function can update state variables in the modules bound to the compliance
* these state variables being used by the module checks to decide if a transfer
* is compliant or not depending on the values stored in these state variables and on
* the parameters of the modules
* This function can be called ONLY by the token contract bound to the compliance
* @param _from The address on which tokens are burnt
* @param _amount The amount of tokens involved in the burn
* This function calls moduleBurnAction() on each module bound to the compliance contract
*/
function destroyed(address _from, uint256 _amount) external;
/**
* @dev checks that the transfer is compliant.
* default compliance always returns true
* READ ONLY FUNCTION, this function cannot be used to increment
* counters, emit events, ...
* @param _from The address of the sender
* @param _to The address of the receiver
* @param _amount The amount of tokens involved in the transfer
* This function will call moduleCheck() on every module bound to the compliance
* If each of the module checks return TRUE, this function will return TRUE as well
* returns FALSE otherwise
*/
function canTransfer(
address _from,
address _to,
uint256 _amount
) external view returns (bool);
/**
* @dev getter for the modules bound to the compliance contract
* returns address array of module contracts bound to the compliance
*/
function getModules() external view returns (address[] memory);
/**
* @dev getter for the address of the token bound
* returns the address of the token
*/
function getTokenBound() external view returns (address);
/**
* @dev checks if a module is bound to the compliance contract
* returns true if module is bound, false otherwise
*/
function isModuleBound(address _module) external view returns (bool);
}
IERC20.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @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 owner, 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);
}
OwnableUpgradeable.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_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 virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual 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 virtual 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 virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
IModule.sol 157 lines
// SPDX-License-Identifier: GPL-3.0
//
// :+#####%%%%%%%%%%%%%%+
// .-*@@@%+.:+%@@@@@%%#***%@@%=
// :=*%@@@#=. :#@@% *@@@%=
// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%-
// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#.
// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+
// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%-
// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%:
// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#.
// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*.
// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+
// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@-
// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#:
// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#-
// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%-
// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@#
// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+-
// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=:
// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+:
// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+.
// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+.
// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=.
// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=.
// @@@@@@+. +@@*. .+@@@@@%=.
// -@@@@@= =@@%: -#@@@@%+.
// +@@@@@. =@@@= .+@@@@@*:
// #@@@@#:%@@#. :*@@@@#-
// @@@@@%@@@= :#@@@@+.
// :@@@@@@@#.:#@@@%-
// +@@@@@@-.*@@@*:
// #@@@@#.=@@@+.
// @@@@+-%@%=
// :@@@#%@%=
// +@@@@%-
// :#%%=
//
/**
* NOTICE
*
* The T-REX software is licensed under a proprietary license or the GPL v.3.
* If you choose to receive it under the GPL v.3 license, the following applies:
* T-REX is a suite of smart contracts implementing the ERC-3643 standard and
* developed by Tokeny to manage and transfer financial assets on EVM blockchains
*
* Copyright (C) 2023, Tokeny sàrl.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity 0.8.17;
interface IModule {
/// events
/**
* this event is emitted when the compliance contract is bound to the module.
* the event is emitted by the bindCompliance function
* `_compliance` is the address of the compliance contract being bound
*/
event ComplianceBound(address indexed _compliance);
/**
* this event is emitted when the compliance contract is unbound from the module.
* the event is emitted by the unbindCompliance function
* `_compliance` is the address of the compliance contract being unbound
*/
event ComplianceUnbound(address indexed _compliance);
/// functions
/**
* @dev binds the module to a compliance contract
* once the module is bound, the compliance contract can interact with the module
* this function can be called ONLY by the compliance contract itself (_compliance), through the
* addModule function, which calls bindCompliance
* the module cannot be already bound to the compliance
* @param _compliance address of the compliance contract
* Emits a ComplianceBound event
*/
function bindCompliance(address _compliance) external;
/**
* @dev unbinds the module from a compliance contract
* once the module is unbound, the compliance contract cannot interact with the module anymore
* this function can be called ONLY by the compliance contract itself (_compliance), through the
* removeModule function, which calls unbindCompliance
* @param _compliance address of the compliance contract
* Emits a ComplianceUnbound event
*/
function unbindCompliance(address _compliance) external;
/**
* @dev action performed on the module during a transfer action
* this function is used to update variables of the module upon transfer if it is required
* if the module does not require state updates in case of transfer, this function remains empty
* This function can be called ONLY by the compliance contract itself (_compliance)
* This function can be called only on a compliance contract that is bound to the module
* @param _from address of the transfer sender
* @param _to address of the transfer receiver
* @param _value amount of tokens sent
*/
function moduleTransferAction(address _from, address _to, uint256 _value) external;
/**
* @dev action performed on the module during a mint action
* this function is used to update variables of the module upon minting if it is required
* if the module does not require state updates in case of mint, this function remains empty
* This function can be called ONLY by the compliance contract itself (_compliance)
* This function can be called only on a compliance contract that is bound to the module
* @param _to address used for minting
* @param _value amount of tokens minted
*/
function moduleMintAction(address _to, uint256 _value) external;
/**
* @dev action performed on the module during a burn action
* this function is used to update variables of the module upon burning if it is required
* if the module does not require state updates in case of burn, this function remains empty
* This function can be called ONLY by the compliance contract itself (_compliance)
* This function can be called only on a compliance contract that is bound to the module
* @param _from address on which tokens are burnt
* @param _value amount of tokens burnt
*/
function moduleBurnAction(address _from, uint256 _value) external;
/**
* @dev compliance check on the module for a specific transaction on a specific compliance contract
* this function is used to check if the transfer is allowed by the module
* This function can be called only on a compliance contract that is bound to the module
* @param _from address of the transfer sender
* @param _to address of the transfer receiver
* @param _value amount of tokens sent
* @param _compliance address of the compliance contract concerned by the transfer action
* the function returns TRUE if the module allows the transfer, FALSE otherwise
*/
function moduleCheck(address _from, address _to, uint256 _value, address _compliance) external view returns (bool);
/**
* @dev getter for compliance binding status on module
* @param _compliance address of the compliance contract
*/
function isComplianceBound(address _compliance) external view returns (bool);
}
IFrictionlessComplianceFactory.sol 197 lines
// SPDX-License-Identifier: MIT
/**
* Copyright © 2024 Frictionless Group Holdings S.à.r.l
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of the Frictionless protocol smart contracts
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FRICTIONLESS GROUP
* HOLDINGS S.à.r.l OR AN OF ITS SUBSIDIARIES BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
pragma solidity ^0.8.16;
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { ProxyBeacon } from "@solidity-lib/contracts-registry/pools/proxy/ProxyBeacon.sol";
import { IBasicFrictionlessToken } from "@interface/IBasicFrictionlessToken.sol";
/**
* @title IFrictionlessComplianceFactory - Interface defining the upgradeable compliance factory for all tokens in the Frictionless protocol.
* @author Frictionless Group Holdings S.à.r.l
* @notice Interface defining the upgradeable compliance factory for all tokens in the Frictionless protocol.
*/
interface IFrictionlessComplianceFactory {
struct SupportedComplianceData {
uint256 maxModulesCount;
EnumerableSet.AddressSet modules;
bytes32[48] _gap;
}
struct ModularComplianceInfo {
uint256 maxModulesCount;
address[] modules;
}
/**
* @dev Structure to represent the update of modular compliance data.
* @param modularCompliance the modularCompliance contract address
* @param modulesToUpdate the array of comliance modules to associate with the modularCompliance contract
* @param isAdding true if the modules are being added, false if they are being updated
*/
struct UpdateModularComplianceData {
address modularCompliance;
ModularComplianceInfo complianceInfo;
bool isAdding;
}
/**
* @dev Structure to represent the update of modular compliance data for a given token type
* @param tokenType the type of token as defined by the `IBasicFrictionlessToken.FrictionlessTokenTypes` enumerated type
* @param modulesToUpdate the array of comliance modules to associate with the token type
* @param isAdding true if the modules are being added, false if they are being updated
*/
struct UpdateSupportedComplianceData {
IBasicFrictionlessToken.FrictionlessTokenTypes tokenType;
ModularComplianceInfo complianceInfo;
bool isAdding;
}
/// @dev error thrown if the specified contract address is a zero address, during `init`, `setTreasuryManager`, and `updateModularComplianceImpl`
error FrictionlessComplianceFactoryZeroAddr(string);
/// @dev error thrown if the `msg.sender` is not the treasury manager during the function `deployCompliance`
error FrictionlessComplianceFactoryNotATreasuryManager(address);
/// @dev error thrown if the modular compliance is invlaid for hte token type during the function `updateModularCompliancesModules`
error FrictionlessComplianceFactoryNotAModularCompliance(address);
/// @dev error thrown if an invalid tokenType is specified during `deployCompliance`
error FrictionlessComplianceFactoryInvalidTokenType();
/// @dev error thrown if an invalid module is specified during `updateModularCompliancesModules`
error FrictionlessComplianceFactoryInvalidModularComplianceData(UpdateModularComplianceData modularComplianceData);
/**
* @dev Event emitted upon successful deployment of a compliance contract.
* @param tokenType The Frictionless token type as defined by `IBasicFrictionlessToken.FrictionlessTokenTypes`
* @param newComplianceContract The compliance contract to deploy
*/
event FrictionlessComplianceDeployed(
IBasicFrictionlessToken.FrictionlessTokenTypes indexed tokenType,
address newComplianceContract
);
/**
* @dev Sets the Treasury Manager to be the specified address.
* @param newTreasuryManager_ the address of the treasury manager to set
* throws `FrictionlessComplianceFactoryZeroAddress` if the newTreasuryManager_ is a zero address
*/
function setTreasuryManager(address newTreasuryManager_) external;
/**
* @dev Updates and upgrades the modular compliance implementation
* @param newModularComplianceImpl_ the address of the modular compliance implementation
*/
function updateModularComplianceImpl(address newModularComplianceImpl_) external;
/**
* @dev Updates the set of supported modular compliance modules.
* @param updateSupportedComplianceDataArr_ the set of supported modular compliance modules.
*/
function updateSupportedComplianceData(
UpdateSupportedComplianceData[] calldata updateSupportedComplianceDataArr_
) external;
/**
* @dev Updates the modular compliance data.
* @param updateModularComplianceDataArr_ the modular compliance data.
*/
function updateModularComplianceData(
UpdateModularComplianceData[] calldata updateModularComplianceDataArr_
) external;
/**
* @dev Deploys the compliance contract using the ProxyBeacon with the associated FrictionlessPermissionsManager contract
* @param tokenType_ The Frictionless token type as defined by `IBasicFrictionlessToken.FrictionlessTokenTypes`
* @return the address of the deployed compliance contract for the specified Frictionless token type
* throws `FrictionlessComplianceFactoryNotATreasuryManager` if the msg.sender is not the treasury manager
* emits FrictionlessComplianceDeployed event upon successful deployment of the compliance contract.
*/
function deployCompliance(IBasicFrictionlessToken.FrictionlessTokenTypes tokenType_) external returns (address);
/**
* @dev returns the address of the treasuryManager
* @return the address of the treasuryManager
*/
function treasuryManager() external view returns (address);
/**
* @dev returns the ProxyBeacon of the ModularCompliance
* @return the ProxyBeacon of the ModularCompliance
*/
function modularComplianceBeacon() external view returns (ProxyBeacon);
/**
* @dev returns the address of the ModularCompliance
* @return the address of the ModularCompliance
*/
function getModularComplianceImpl() external view returns (address);
/**
* @dev returns the `FrictionlessTokenTypes` which is bound by the ModularCompliance
* @param modularComplianceAddr_ the address of the ModularComplianceImpl
* @return the address of the ModularCompliance
*/
function getModularComplianceTokenType(
address modularComplianceAddr_
) external view returns (IBasicFrictionlessToken.FrictionlessTokenTypes);
/**
* @dev returns the amount of supported compliances ModularCompliance for the specified tokenType_
* @param tokenType_ the type of token as defined by the `IBasicFrictionlessToken.FrictionlessTokenTypes` enumerated type
* @return the amount of supported compliances
*/
function getSupportedComplianceModulesCount(
IBasicFrictionlessToken.FrictionlessTokenTypes tokenType_
) external view returns (uint256);
/**
* @dev returns the array of supported compliances ModularCompliance for the specified tokenType_
* @param tokenType_ the type of token as defined by the `IBasicFrictionlessToken.FrictionlessTokenTypes` enumerated type
* @return the array of supported compliances
*/
function getSupportedComplianceModules(
IBasicFrictionlessToken.FrictionlessTokenTypes tokenType_
) external view returns (address[] memory);
function getSupportedComplianceInfo(
IBasicFrictionlessToken.FrictionlessTokenTypes tokenType_
) external view returns (ModularComplianceInfo memory);
/**
* @dev returns true if the compliance module supports the Frictionless token type, otherwise false
* @param tokenType_ the type of token as defined by the `IBasicFrictionlessToken.FrictionlessTokenTypes` enumerated type
* @param moduleToCheck_ the address of the modular compliance contract to verify
* @return true if the compliance module supports the Frictionless token type, otherwise false
*/
function isSupportedComplianceModule(
IBasicFrictionlessToken.FrictionlessTokenTypes tokenType_,
address moduleToCheck_
) external view returns (bool);
/**
* @dev Returns true if the address provided is a ModularCompliance contract
* @param modularComplianceAddr_ the address of the ModularCompliance contract
* @return true if the address provided is a ModularCompliance contract, otherwise false
*/
function isModularCompliance(address modularComplianceAddr_) external view returns (bool);
}
IFrictionlessComplianceModule.sol 153 lines
// SPDX-License-Identifier: MIT
/**
* Copyright © 2024 Frictionless Group Holdings S.à.r.l
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of the Frictionless protocol smart contracts
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FRICTIONLESS GROUP
* HOLDINGS S.à.r.l OR AN OF ITS SUBSIDIARIES BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
pragma solidity ^0.8.16;
import { IModule } from "@ERC-3643/compliance/modular/modules/IModule.sol";
import { IBasicFrictionlessToken } from "@interface/IBasicFrictionlessToken.sol";
import { IFrictionlessPermissionsManager } from "@interface/IFrictionlessPermissionsManager.sol";
import { IFrictionlessComplianceFactory } from "@interface/IFrictionlessComplianceFactory.sol";
/**
* @title IFrictionlessCompliance - Manages the compliance of participants in the Frictionless protocol w.r.t Tokens
* @author Frictionless Group Holdings S.à.r.l
* @notice The IFrictionlessCompliance is responsible for the compliant transfer of the various Tokens in the Frictionless protocol.
*/
interface IFrictionlessComplianceModule is IModule {
/// @dev throws if specific address is zero.
error FrictionlessIsZeroAddress(string);
/// @dev thrown if the compliance module address is invalid during `bindCompliance` and `unBindCompliance`
error FrictionlessComplianceModuleInvalidCompliance(address compliance);
/// @dev thrown if the compliance module address is not approved during `bindCompliance`
error FrictionlessComplianceModuleNotAnApprovedCompliance(address compliance);
/// @dev thrown if the compliance module address is already bound, thrown by `bindCompliance`
error FrictionlessComplianceModuleComplianceIsAlreadyBound(address compliance);
/// @dev thrown if the compliance module address is not already bound during `unBindCompliance`
error FrictionlessComplianceModuleNotABoundCompliance(address compliance);
/**
* @dev Event emitted during compliance module transfers
* @param tokenAddr the address of the token transferred
* @param from the address the token was transferred from
* @param to the address the token was transferred to
* @param amount the amount of the token transferred
*/
event FrictionlessTokenTransferred(
IBasicFrictionlessToken.FrictionlessTokenTypes tokenType,
address tokenAddr,
address from,
address to,
uint256 amount
);
/**
* @dev Sets the instance of the `IFrictionlessPermissionsManager` to determine permissions for users.
* @param frictionlessPermissionsManager_ the address of the `IFrictionlessPermissionsManager` contract
*/
function setPermissionsManager(address frictionlessPermissionsManager_) external;
/**
* @dev Sets the instance of the `IFrictionlessComplianceFactory` to determine the compliance module for the protocol.
* @param newComplianceFactory_ the address of the `IFrictionlessComplianceFactory` contract
*/
function setComplianceFactory(address newComplianceFactory_) external;
/**
* @dev Gets the instance of the `IFrictionlessPermissionsManager` to determine permissions for users.
* @return the address of the `IFrictionlessPermissionsManager` contract
*/
function permissionsManager() external view returns (IFrictionlessPermissionsManager);
/**
* @dev Gets the instance of the `IFrictionlessComplianceFactory` to determine the compliance module for the protocol.
* @return the address of the `IFrictionlessComplianceFactory` contract
*/
function complianceFactory() external view returns (IFrictionlessComplianceFactory);
/**
* @dev Verifies if the specified wallet address is a PROTOCOL_TREASURY user.
* @param walletAddress_ the address of the users wallet.
* @return true if the specified wallet address is a PROTOCOL_TREASURY user, otherwise false
*/
function isTreasury(address walletAddress_) external view returns (bool);
/**
* @dev Verifies if the specified wallet address is a PERMISSIONED_CUSTODIAN user.
* @param walletAddress_ the address of the users wallet.
* @return true if the specified wallet address is a PERMISSIONED_CUSTODIAN user, otherwise false
*/
function isCustodian(address walletAddress_) external view returns (bool);
/**
* @dev Verifies if the specified wallet address is a PERMISSIONED_INVESTOR user.
* @param walletAddress_ the address of the users wallet.
* @return true if the specified wallet address is a PERMISSIONED_INVESTOR user, otherwise false
*/
function isInvestor(address walletAddress_) external view returns (bool);
/**
* @dev Verifies if the specified wallet address is a PERMISSIONED_MANAGER user.
* @param walletAddress_ the address of the users wallet.
* @return true if the specified wallet address is a PERMISSIONED_MANAGER user, otherwise false
*/
function isManager(address walletAddress_) external view returns (bool);
/**
* @dev Verifies if the specified wallet address is a PERMISSIONED_CALCULATING_AGENT user.
* @param walletAddress_ the address of the users wallet.
* @return true if the specified wallet address is a PERMISSIONED_CALCULATING_AGENT user, otherwise false
*/
function isCalculatingAgent(address walletAddress_) external view returns (bool);
/**
* @dev Verifies if the specified wallet address is a PERMISSIONED_TRANSFER_AGENT user.
* @param walletAddress_ the address of the users wallet.
* @return true if the specified wallet address is a PERMISSIONED_TRANSFER_AGENT user, otherwise false
*/
function isTransferAgent(address walletAddress_) external view returns (bool);
/**
* @dev Verifies if the specified wallet address is a PERMISSIONED_FUND_ACCOUNTANT user.
* @param walletAddress_ the address of the users wallet.
* @return true if the specified wallet address is a PERMISSIONED_FUND_ACCOUNTANT user, otherwise false
*/
function isFundAccountant(address walletAddress_) external view returns (bool);
/**
* @dev Verifies if the specified wallet address is a permissioned user in the Frictionless protocol.
* @param walletAddress_ the address of the users wallet.
* @return true if the specified wallet address is a permissioned user in the Frictionless protocol, otherwise false
*/
function isUser(address walletAddress_) external view returns (bool);
/**
* @dev verify if the userAddress is permissioned in the Frictionless protocol and has a valid claim
* @param userAddress_ the address of the user's wallet to verify
* @param userType_ the type of the user as per the enum
* @return true if a valid permissioned user and has a valid claim, otherwise false.
*/
function hasClaim(
address userAddress_,
IFrictionlessPermissionsManager.FrictionlessPermissionedUser userType_
) external view returns (bool);
}
ITrustedIssuersRegistry.sol 162 lines
// SPDX-License-Identifier: GPL-3.0
//
// :+#####%%%%%%%%%%%%%%+
// .-*@@@%+.:+%@@@@@%%#***%@@%=
// :=*%@@@#=. :#@@% *@@@%=
// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%-
// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#.
// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+
// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%-
// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%:
// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#.
// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*.
// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+
// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@-
// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#:
// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#-
// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%-
// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@#
// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+-
// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=:
// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+:
// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+.
// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+.
// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=.
// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=.
// @@@@@@+. +@@*. .+@@@@@%=.
// -@@@@@= =@@%: -#@@@@%+.
// +@@@@@. =@@@= .+@@@@@*:
// #@@@@#:%@@#. :*@@@@#-
// @@@@@%@@@= :#@@@@+.
// :@@@@@@@#.:#@@@%-
// +@@@@@@-.*@@@*:
// #@@@@#.=@@@+.
// @@@@+-%@%=
// :@@@#%@%=
// +@@@@%-
// :#%%=
//
/**
* NOTICE
*
* The T-REX software is licensed under a proprietary license or the GPL v.3.
* If you choose to receive it under the GPL v.3 license, the following applies:
* T-REX is a suite of smart contracts implementing the ERC-3643 standard and
* developed by Tokeny to manage and transfer financial assets on EVM blockchains
*
* Copyright (C) 2023, Tokeny sàrl.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity 0.8.17;
import "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol";
interface ITrustedIssuersRegistry {
/**
* this event is emitted when a trusted issuer is added in the registry.
* the event is emitted by the addTrustedIssuer function
* `trustedIssuer` is the address of the trusted issuer's ClaimIssuer contract
* `claimTopics` is the set of claims that the trusted issuer is allowed to emit
*/
event TrustedIssuerAdded(IClaimIssuer indexed trustedIssuer, uint256[] claimTopics);
/**
* this event is emitted when a trusted issuer is removed from the registry.
* the event is emitted by the removeTrustedIssuer function
* `trustedIssuer` is the address of the trusted issuer's ClaimIssuer contract
*/
event TrustedIssuerRemoved(IClaimIssuer indexed trustedIssuer);
/**
* this event is emitted when the set of claim topics is changed for a given trusted issuer.
* the event is emitted by the updateIssuerClaimTopics function
* `trustedIssuer` is the address of the trusted issuer's ClaimIssuer contract
* `claimTopics` is the set of claims that the trusted issuer is allowed to emit
*/
event ClaimTopicsUpdated(IClaimIssuer indexed trustedIssuer, uint256[] claimTopics);
/**
* @dev registers a ClaimIssuer contract as trusted claim issuer.
* Requires that a ClaimIssuer contract doesn't already exist
* Requires that the claimTopics set is not empty
* Requires that there is no more than 15 claimTopics
* Requires that there is no more than 50 Trusted issuers
* @param _trustedIssuer The ClaimIssuer contract address of the trusted claim issuer.
* @param _claimTopics the set of claim topics that the trusted issuer is allowed to emit
* This function can only be called by the owner of the Trusted Issuers Registry contract
* emits a `TrustedIssuerAdded` event
*/
function addTrustedIssuer(IClaimIssuer _trustedIssuer, uint256[] calldata _claimTopics) external;
/**
* @dev Removes the ClaimIssuer contract of a trusted claim issuer.
* Requires that the claim issuer contract to be registered first
* @param _trustedIssuer the claim issuer to remove.
* This function can only be called by the owner of the Trusted Issuers Registry contract
* emits a `TrustedIssuerRemoved` event
*/
function removeTrustedIssuer(IClaimIssuer _trustedIssuer) external;
/**
* @dev Updates the set of claim topics that a trusted issuer is allowed to emit.
* Requires that this ClaimIssuer contract already exists in the registry
* Requires that the provided claimTopics set is not empty
* Requires that there is no more than 15 claimTopics
* @param _trustedIssuer the claim issuer to update.
* @param _claimTopics the set of claim topics that the trusted issuer is allowed to emit
* This function can only be called by the owner of the Trusted Issuers Registry contract
* emits a `ClaimTopicsUpdated` event
*/
function updateIssuerClaimTopics(IClaimIssuer _trustedIssuer, uint256[] calldata _claimTopics) external;
/**
* @dev Function for getting all the trusted claim issuers stored.
* @return array of all claim issuers registered.
*/
function getTrustedIssuers() external view returns (IClaimIssuer[] memory);
/**
* @dev Function for getting all the trusted issuer allowed for a given claim topic.
* @param claimTopic the claim topic to get the trusted issuers for.
* @return array of all claim issuer addresses that are allowed for the given claim topic.
*/
function getTrustedIssuersForClaimTopic(uint256 claimTopic) external view returns (IClaimIssuer[] memory);
/**
* @dev Checks if the ClaimIssuer contract is trusted
* @param _issuer the address of the ClaimIssuer contract
* @return true if the issuer is trusted, false otherwise.
*/
function isTrustedIssuer(address _issuer) external view returns (bool);
/**
* @dev Function for getting all the claim topic of trusted claim issuer
* Requires the provided ClaimIssuer contract to be registered in the trusted issuers registry.
* @param _trustedIssuer the trusted issuer concerned.
* @return The set of claim topics that the trusted issuer is allowed to emit
*/
function getTrustedIssuerClaimTopics(IClaimIssuer _trustedIssuer) external view returns (uint256[] memory);
/**
* @dev Function for checking if the trusted claim issuer is allowed
* to emit a certain claim topic
* @param _issuer the address of the trusted issuer's ClaimIssuer contract
* @param _claimTopic the Claim Topic that has to be checked to know if the `issuer` is allowed to emit it
* @return true if the issuer is trusted for this claim topic.
*/
function hasClaimTopic(address _issuer, uint256 _claimTopic) external view returns (bool);
}
IClaimTopicsRegistry.sol 102 lines
// SPDX-License-Identifier: GPL-3.0
//
// :+#####%%%%%%%%%%%%%%+
// .-*@@@%+.:+%@@@@@%%#***%@@%=
// :=*%@@@#=. :#@@% *@@@%=
// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%-
// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#.
// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+
// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%-
// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%:
// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#.
// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*.
// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+
// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@-
// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#:
// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#-
// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%-
// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@#
// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+-
// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=:
// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+:
// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+.
// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+.
// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=.
// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=.
// @@@@@@+. +@@*. .+@@@@@%=.
// -@@@@@= =@@%: -#@@@@%+.
// +@@@@@. =@@@= .+@@@@@*:
// #@@@@#:%@@#. :*@@@@#-
// @@@@@%@@@= :#@@@@+.
// :@@@@@@@#.:#@@@%-
// +@@@@@@-.*@@@*:
// #@@@@#.=@@@+.
// @@@@+-%@%=
// :@@@#%@%=
// +@@@@%-
// :#%%=
//
/**
* NOTICE
*
* The T-REX software is licensed under a proprietary license or the GPL v.3.
* If you choose to receive it under the GPL v.3 license, the following applies:
* T-REX is a suite of smart contracts implementing the ERC-3643 standard and
* developed by Tokeny to manage and transfer financial assets on EVM blockchains
*
* Copyright (C) 2023, Tokeny sàrl.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity 0.8.17;
interface IClaimTopicsRegistry {
/**
* this event is emitted when a claim topic has been added to the ClaimTopicsRegistry
* the event is emitted by the 'addClaimTopic' function
* `claimTopic` is the required claim added to the Claim Topics Registry
*/
event ClaimTopicAdded(uint256 indexed claimTopic);
/**
* this event is emitted when a claim topic has been removed from the ClaimTopicsRegistry
* the event is emitted by the 'removeClaimTopic' function
* `claimTopic` is the required claim removed from the Claim Topics Registry
*/
event ClaimTopicRemoved(uint256 indexed claimTopic);
/**
* @dev Add a trusted claim topic (For example: KYC=1, AML=2).
* Only owner can call.
* emits `ClaimTopicAdded` event
* cannot add more than 15 topics for 1 token as adding more could create gas issues
* @param _claimTopic The claim topic index
*/
function addClaimTopic(uint256 _claimTopic) external;
/**
* @dev Remove a trusted claim topic (For example: KYC=1, AML=2).
* Only owner can call.
* emits `ClaimTopicRemoved` event
* @param _claimTopic The claim topic index
*/
function removeClaimTopic(uint256 _claimTopic) external;
/**
* @dev Get the trusted claim topics for the security token
* @return Array of trusted claim topics
*/
function getClaimTopics() external view returns (uint256[] memory);
}
IIdentityRegistryStorage.sol 197 lines
// SPDX-License-Identifier: GPL-3.0
//
// :+#####%%%%%%%%%%%%%%+
// .-*@@@%+.:+%@@@@@%%#***%@@%=
// :=*%@@@#=. :#@@% *@@@%=
// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%-
// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#.
// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+
// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%-
// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%:
// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#.
// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*.
// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+
// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@-
// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#:
// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#-
// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%-
// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@#
// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+-
// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=:
// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+:
// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+.
// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+.
// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=.
// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=.
// @@@@@@+. +@@*. .+@@@@@%=.
// -@@@@@= =@@%: -#@@@@%+.
// +@@@@@. =@@@= .+@@@@@*:
// #@@@@#:%@@#. :*@@@@#-
// @@@@@%@@@= :#@@@@+.
// :@@@@@@@#.:#@@@%-
// +@@@@@@-.*@@@*:
// #@@@@#.=@@@+.
// @@@@+-%@%=
// :@@@#%@%=
// +@@@@%-
// :#%%=
//
/**
* NOTICE
*
* The T-REX software is licensed under a proprietary license or the GPL v.3.
* If you choose to receive it under the GPL v.3 license, the following applies:
* T-REX is a suite of smart contracts implementing the ERC-3643 standard and
* developed by Tokeny to manage and transfer financial assets on EVM blockchains
*
* Copyright (C) 2023, Tokeny sàrl.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity 0.8.17;
import "@onchain-id/solidity/contracts/interface/IIdentity.sol";
interface IIdentityRegistryStorage {
/// events
/**
* this event is emitted when an Identity is registered into the storage contract.
* the event is emitted by the 'registerIdentity' function
* `investorAddress` is the address of the investor's wallet
* `identity` is the address of the Identity smart contract (onchainID)
*/
event IdentityStored(address indexed investorAddress, IIdentity indexed identity);
/**
* this event is emitted when an Identity is removed from the storage contract.
* the event is emitted by the 'deleteIdentity' function
* `investorAddress` is the address of the investor's wallet
* `identity` is the address of the Identity smart contract (onchainID)
*/
event IdentityUnstored(address indexed investorAddress, IIdentity indexed identity);
/**
* this event is emitted when an Identity has been updated
* the event is emitted by the 'updateIdentity' function
* `oldIdentity` is the old Identity contract's address to update
* `newIdentity` is the new Identity contract's
*/
event IdentityModified(IIdentity indexed oldIdentity, IIdentity indexed newIdentity);
/**
* this event is emitted when an Identity's country has been updated
* the event is emitted by the 'updateCountry' function
* `investorAddress` is the address on which the country has been updated
* `country` is the numeric code (ISO 3166-1) of the new country
*/
event CountryModified(address indexed investorAddress, uint16 indexed country);
/**
* this event is emitted when an Identity Registry is bound to the storage contract
* the event is emitted by the 'addIdentityRegistry' function
* `identityRegistry` is the address of the identity registry added
*/
event IdentityRegistryBound(address indexed identityRegistry);
/**
* this event is emitted when an Identity Registry is unbound from the storage contract
* the event is emitted by the 'removeIdentityRegistry' function
* `identityRegistry` is the address of the identity registry removed
*/
event IdentityRegistryUnbound(address indexed identityRegistry);
/// functions
/**
* @dev adds an identity contract corresponding to a user address in the storage.
* Requires that the user doesn't have an identity contract already registered.
* This function can only be called by an address set as agent of the smart contract
* @param _userAddress The address of the user
* @param _identity The address of the user's identity contract
* @param _country The country of the investor
* emits `IdentityStored` event
*/
function addIdentityToStorage(
address _userAddress,
IIdentity _identity,
uint16 _country
) external;
/**
* @dev Removes an user from the storage.
* Requires that the user have an identity contract already deployed that will be deleted.
* This function can only be called by an address set as agent of the smart contract
* @param _userAddress The address of the user to be removed
* emits `IdentityUnstored` event
*/
function removeIdentityFromStorage(address _userAddress) external;
/**
* @dev Updates the country corresponding to a user address.
* Requires that the user should have an identity contract already deployed that will be replaced.
* This function can only be called by an address set as agent of the smart contract
* @param _userAddress The address of the user
* @param _country The new country of the user
* emits `CountryModified` event
*/
function modifyStoredInvestorCountry(address _userAddress, uint16 _country) external;
/**
* @dev Updates an identity contract corresponding to a user address.
* Requires that the user address should be the owner of the identity contract.
* Requires that the user should have an identity contract already deployed that will be replaced.
* This function can only be called by an address set as agent of the smart contract
* @param _userAddress The address of the user
* @param _identity The address of the user's new identity contract
* emits `IdentityModified` event
*/
function modifyStoredIdentity(address _userAddress, IIdentity _identity) external;
/**
* @notice Adds an identity registry as agent of the Identity Registry Storage Contract.
* This function can only be called by the wallet set as owner of the smart contract
* This function adds the identity registry to the list of identityRegistries linked to the storage contract
* cannot bind more than 300 IR to 1 IRS
* @param _identityRegistry The identity registry address to add.
*/
function bindIdentityRegistry(address _identityRegistry) external;
/**
* @notice Removes an identity registry from being agent of the Identity Registry Storage Contract.
* This function can only be called by the wallet set as owner of the smart contract
* This function removes the identity registry from the list of identityRegistries linked to the storage contract
* @param _identityRegistry The identity registry address to remove.
*/
function unbindIdentityRegistry(address _identityRegistry) external;
/**
* @dev Returns the identity registries linked to the storage contract
*/
function linkedIdentityRegistries() external view returns (address[] memory);
/**
* @dev Returns the onchainID of an investor.
* @param _userAddress The wallet of the investor
*/
function storedIdentity(address _userAddress) external view returns (IIdentity);
/**
* @dev Returns the country code of an investor.
* @param _userAddress The wallet of the investor
*/
function storedInvestorCountry(address _userAddress) external view returns (uint16);
}
IClaimIssuer.sol 59 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;
import "./IIdentity.sol";
interface IClaimIssuer is IIdentity {
/**
* @dev Emitted when a claim is revoked.
*
* Specification: MUST be triggered when revoking a claim.
*/
event ClaimRevoked(bytes indexed signature);
/**
* @dev Revoke a claim previously issued, the claim is no longer considered as valid after revocation.
* @notice will fetch the claim from the identity contract (unsafe).
* @param _claimId the id of the claim
* @param _identity the address of the identity contract
* @return isRevoked true when the claim is revoked
*/
function revokeClaim(bytes32 _claimId, address _identity) external returns(bool);
/**
* @dev Revoke a claim previously issued, the claim is no longer considered as valid after revocation.
* @param signature the signature of the claim
*/
function revokeClaimBySignature(bytes calldata signature) external;
/**
* @dev Returns revocation status of a claim.
* @param _sig the signature of the claim
* @return isRevoked true if the claim is revoked and false otherwise
*/
function isClaimRevoked(bytes calldata _sig) external view returns (bool);
/**
* @dev Checks if a claim is valid.
* @param _identity the identity contract related to the claim
* @param claimTopic the claim topic of the claim
* @param sig the signature of the claim
* @param data the data field of the claim
* @return claimValid true if the claim is valid, false otherwise
*/
function isClaimValid(
IIdentity _identity,
uint256 claimTopic,
bytes calldata sig,
bytes calldata data)
external view returns (bool);
/**
* @dev returns the address that signed the given data
* @param sig the signature of the data
* @param dataHash the data that was signed
* returns the address that signed dataHash and created the signature sig
*/
function getRecoveredAddress(bytes calldata sig, bytes32 dataHash) external pure returns (address);
}
IIdentity.sol 8 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;
import "./IERC734.sol";
import "./IERC735.sol";
// solhint-disable-next-line no-empty-blocks
interface IIdentity is IERC734, IERC735 {}
ContextUpgradeable.sol 37 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
Initializable.sol 166 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
EnumerableSet.sol 378 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
ProxyBeacon.sol 41 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
/**
* @notice The PoolContractsRegistry module
*
* This is a utility lightweighted ProxyBeacon contract this is used as a beacon that BeaconProxies point to.
*/
contract ProxyBeacon is IBeacon {
using Address for address;
address private immutable _OWNER;
address private _implementation;
event Upgraded(address implementation);
modifier onlyOwner() {
require(_OWNER == msg.sender, "ProxyBeacon: not an owner");
_;
}
constructor() {
_OWNER = msg.sender;
}
function upgrade(address newImplementation_) external onlyOwner {
require(newImplementation_.isContract(), "ProxyBeacon: not a contract");
_implementation = newImplementation_;
emit Upgraded(newImplementation_);
}
function implementation() external view override returns (address) {
return _implementation;
}
}
IERC734.sol 111 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;
/**
* @dev interface of the ERC734 (Key Holder) standard as defined in the EIP.
*/
interface IERC734 {
/**
* @dev Emitted when an execution request was approved.
*
* Specification: MUST be triggered when approve was successfully called.
*/
event Approved(uint256 indexed executionId, bool approved);
/**
* @dev Emitted when an execute operation was approved and successfully performed.
*
* Specification: MUST be triggered when approve was called and the execution was successfully approved.
*/
event Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data);
/**
* @dev Emitted when an execution request was performed via `execute`.
*
* Specification: MUST be triggered when execute was successfully called.
*/
event ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data);
/**
* @dev Emitted when an execute operation was called and failed
*
* Specification: MUST be triggered when execute call failed
*/
event ExecutionFailed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data);
/**
* @dev Emitted when a key was added to the Identity.
*
* Specification: MUST be triggered when addKey was successfully called.
*/
event KeyAdded(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType);
/**
* @dev Emitted when a key was removed from the Identity.
*
* Specification: MUST be triggered when removeKey was successfully called.
*/
event KeyRemoved(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType);
/**
* @dev Adds a _key to the identity. The _purpose specifies the purpose of the key.
*
* Triggers Event: `KeyAdded`
*
* Specification: MUST only be done by keys of purpose 1, or the identity
* itself. If it's the identity itself, the approval process will determine its approval.
*/
function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) external returns (bool success);
/**
* @dev Approves an execution.
*
* Triggers Event: `Approved`
* Triggers on execution successful Event: `Executed`
* Triggers on execution failure Event: `ExecutionFailed`
*/
function approve(uint256 _id, bool _approve) external returns (bool success);
/**
* @dev Removes _purpose for _key from the identity.
*
* Triggers Event: `KeyRemoved`
*
* Specification: MUST only be done by keys of purpose 1, or the identity itself.
* If it's the identity itself, the approval process will determine its approval.
*/
function removeKey(bytes32 _key, uint256 _purpose) external returns (bool success);
/**
* @dev Passes an execution instruction to an ERC734 identity.
* How the execution is handled is up to the identity implementation:
* An execution COULD be requested and require `approve` to be called with one or more keys of purpose 1 or 2 to
* approve this execution.
* Execute COULD be used as the only accessor for `addKey` and `removeKey`.
*
* Triggers Event: ExecutionRequested
* Triggers on direct execution Event: Executed
*/
function execute(address _to, uint256 _value, bytes calldata _data) external payable returns (uint256 executionId);
/**
* @dev Returns the full key data, if present in the identity.
*/
function getKey(bytes32 _key) external view returns (uint256[] memory purposes, uint256 keyType, bytes32 key);
/**
* @dev Returns the list of purposes associated with a key.
*/
function getKeyPurposes(bytes32 _key) external view returns(uint256[] memory _purposes);
/**
* @dev Returns an array of public key bytes32 held by this identity.
*/
function getKeysByPurpose(uint256 _purpose) external view returns (bytes32[] memory keys);
/**
* @dev Returns TRUE if a key is present and has the given purpose. If the key is not present it returns FALSE.
*/
function keyHasPurpose(bytes32 _key, uint256 _purpose) external view returns (bool exists);
}
IERC735.sol 98 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;
/**
* @dev interface of the ERC735 (Claim Holder) standard as defined in the EIP.
*/
interface IERC735 {
/**
* @dev Emitted when a claim was added.
*
* Specification: MUST be triggered when a claim was successfully added.
*/
event ClaimAdded(
bytes32 indexed claimId,
uint256 indexed topic,
uint256 scheme,
address indexed issuer,
bytes signature,
bytes data,
string uri);
/**
* @dev Emitted when a claim was removed.
*
* Specification: MUST be triggered when removeClaim was successfully called.
*/
event ClaimRemoved(
bytes32 indexed claimId,
uint256 indexed topic,
uint256 scheme,
address indexed issuer,
bytes signature,
bytes data,
string uri);
/**
* @dev Emitted when a claim was changed.
*
* Specification: MUST be triggered when addClaim was successfully called on an existing claimId.
*/
event ClaimChanged(
bytes32 indexed claimId,
uint256 indexed topic,
uint256 scheme,
address indexed issuer,
bytes signature,
bytes data,
string uri);
/**
* @dev Add or update a claim.
*
* Triggers Event: `ClaimAdded`, `ClaimChanged`
*
* Specification: Add or update a claim from an issuer.
*
* _signature is a signed message of the following structure:
* `keccak256(abi.encode(address identityHolder_address, uint256 topic, bytes data))`.
* Claim IDs are generated using `keccak256(abi.encode(address issuer_address + uint256 topic))`.
*/
function addClaim(
uint256 _topic,
uint256 _scheme,
address issuer,
bytes calldata _signature,
bytes calldata _data,
string calldata _uri)
external returns (bytes32 claimRequestId);
/**
* @dev Removes a claim.
*
* Triggers Event: `ClaimRemoved`
*
* Claim IDs are generated using `keccak256(abi.encode(address issuer_address, uint256 topic))`.
*/
function removeClaim(bytes32 _claimId) external returns (bool success);
/**
* @dev Get a claim by its ID.
*
* Claim IDs are generated using `keccak256(abi.encode(address issuer_address, uint256 topic))`.
*/
function getClaim(bytes32 _claimId)
external view returns(
uint256 topic,
uint256 scheme,
address issuer,
bytes memory signature,
bytes memory data,
string memory uri);
/**
* @dev Returns an array of claim IDs by topic.
*/
function getClaimIdsByTopic(uint256 _topic) external view returns(bytes32[] memory claimIds);
}
AddressUpgradeable.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
IBeacon.sol 16 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.0;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Read Contract
complianceFactory 0x0afc8743 → address
hasClaim 0x93c81dd6 → bool
isCalculatingAgent 0x9cbad655 → bool
isComplianceBound 0x4cf4d295 → bool
isCustodian 0x35c80c8c → bool
isFundAccountant 0x053af05d → bool
isInvestor 0xcee2a9cf → bool
isManager 0xf3ae2415 → bool
isTransferAgent 0xfb99b86d → bool
isTreasury 0x516f0a1b → bool
isUser 0x4209fff1 → bool
moduleCheck 0x013b7ce4 → bool
owner 0x8da5cb5b → address
permissionsManager 0xfca402ed → address
supplyLimit 0x19d1997a → uint256
Write Contract 10 functions
These functions modify contract state and require a wallet transaction to execute.
bindCompliance 0x4a932544
address compliance_
init 0x86863ec6
address frictionlessPermissionsManager_
address complianceFactory_
uint256 supplyLimit_
moduleBurnAction 0x372491a2
address from_
uint256 value_
moduleMintAction 0xf104a8c9
address to_
uint256 value_
moduleTransferAction 0x2cb7e1ec
address from_
address to_
uint256 value_
renounceOwnership 0x715018a6
No parameters
setComplianceFactory 0x41f9b3f1
address newComplianceFactory_
setPermissionsManager 0x0c98b5e3
address frictionlessPermissionsManager_
transferOwnership 0xf2fde38b
address newOwner
unbindCompliance 0x0694a5fb
address compliance_
Recent Transactions
No transactions found for this address