Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x143F369d362E6F9Bb2b9282c48e55d19f5EB5305
Balance 0 ETH
Nonce 1
Code Size 15597 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

15597 bytes
0x6080604052600436106101755760003560e01c80638456cb59116100cb578063ca61164f1161007f578063e2c0a28111610059578063e2c0a281146103d4578063e45d3d28146103f4578063f2fde38b1461041457600080fd5b8063ca61164f14610374578063d7db6cba14610394578063df3e2da8146103b457600080fd5b80638da5cb5b116100b05780638da5cb5b1461031657806397a6278e14610334578063b6e879c41461035457600080fd5b80638456cb59146102e157806384e79842146102f657600080fd5b80633f4ba83a1161012d5780635c975abb116101075780635c975abb14610294578063715018a6146102ac57806381f6165a146102c157600080fd5b80633f4ba83a146102495780634f1ef2861461025e57806352d1902d1461027157600080fd5b80632c54de4f1161015e5780632c54de4f146101e75780633416794d146102095780633659cfe61461022957600080fd5b80631ffbb0641461017a5780632b0fb787146101af575b600080fd5b34801561018657600080fd5b5061019a610195366004612ad3565b610434565b60405190151581526020015b60405180910390f35b3480156101bb57600080fd5b506101cf6101ca366004612b39565b610447565b6040516001600160a01b0390911681526020016101a6565b3480156101f357600080fd5b50610207610202366004612ba5565b61047e565b005b34801561021557600080fd5b50610207610224366004612bf6565b6105d1565b34801561023557600080fd5b50610207610244366004612ad3565b610702565b34801561025557600080fd5b5061020761087d565b61020761026c366004612d54565b61088f565b34801561027d57600080fd5b506102866109fb565b6040519081526020016101a6565b3480156102a057600080fd5b5060335460ff1661019a565b3480156102b857600080fd5b50610207610ac0565b3480156102cd57600080fd5b506101cf6102dc366004612e32565b610ad2565b3480156102ed57600080fd5b50610207610e82565b34801561030257600080fd5b50610207610311366004612ad3565b610e92565b34801561032257600080fd5b506065546001600160a01b03166101cf565b34801561034057600080fd5b5061020761034f366004612ad3565b610f32565b34801561036057600080fd5b5061020761036f366004612f3d565b610fd2565b34801561038057600080fd5b506101cf61038f366004613182565b611314565b3480156103a057600080fd5b506102076103af366004612bf6565b6116c1565b3480156103c057600080fd5b506101cf6103cf3660046132d7565b61189d565b3480156103e057600080fd5b506102076103ef366004613315565b611d52565b34801561040057600080fd5b5061028661040f366004612b39565b611d64565b34801561042057600080fd5b5061020761042f366004612ad3565b611d9d565b6000610441609783611e2a565b92915050565b6000609e600061045987878787611d64565b81526020810191909152604001600020546001600160a01b031690505b949350505050565b61048733610434565b6104ef5760405162461bcd60e51b815260206004820152602e60248201527f4167656e74526f6c653a2063616c6c657220646f6573206e6f7420686176652060448201526d746865204167656e7420726f6c6560901b60648201526084015b60405180910390fd5b6040517f9fc1d0e70000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152838116602483015260448201839052851690639fc1d0e7906064016020604051808303816000875af115801561055f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105839190613357565b507fbe8d94bc98dc799fb1bc73ba798ff2a489f0c999250f4ff7a64fbe46d97fb7346105ae85611ec8565b858385866040516105c395949392919061339f565b60405180910390a150505050565b6105da33610434565b61063d5760405162461bcd60e51b815260206004820152602e60248201527f4167656e74526f6c653a2063616c6c657220646f6573206e6f7420686176652060448201526d746865204167656e7420726f6c6560901b60648201526084016104e6565b6040517f9dc29fac0000000000000000000000000000000000000000000000000000000081526001600160a01b03838116600483015260248201839052841690639dc29fac90604401600060405180830381600087803b1580156106a057600080fd5b505af11580156106b4573d6000803e3d6000fd5b505050507f318f28bc37d09d28848b584955361b10ff0af378ba45a0f023b0c66c9eaea1526106e284611ec8565b8483856040516106f594939291906133db565b60405180910390a1505050565b6001600160a01b037f000000000000000000000000143f369d362e6f9bb2b9282c48e55d19f5eb530516300361078f5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b19195b1959d85d1958d85b1b60a21b60648201526084016104e6565b7f000000000000000000000000143f369d362e6f9bb2b9282c48e55d19f5eb53056001600160a01b03166107ea7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b0316146108555760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b6163746976652070726f787960a01b60648201526084016104e6565b61085e81611f2c565b6040805160008082526020820190925261087a91839190611f34565b50565b6108856120d9565b61088d612133565b565b6001600160a01b037f000000000000000000000000143f369d362e6f9bb2b9282c48e55d19f5eb530516300361091c5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b19195b1959d85d1958d85b1b60a21b60648201526084016104e6565b7f000000000000000000000000143f369d362e6f9bb2b9282c48e55d19f5eb53056001600160a01b03166109777f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b0316146109e25760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201526b6163746976652070726f787960a01b60648201526084016104e6565b6109eb82611f2c565b6109f782826001611f34565b5050565b6000306001600160a01b037f000000000000000000000000143f369d362e6f9bb2b9282c48e55d19f5eb53051614610a9b5760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c000000000000000060648201526084016104e6565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b610ac86120d9565b61088d6000612185565b6000610adc6121d7565b8451516003141580610af9575060408501516001600160a01b0316155b15610b3257846040517f14552c240000000000000000000000000000000000000000000000000000000081526004016104e691906134b4565b600085604001516001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015610b76573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b9e91908101906134c7565b604051602001610bae919061353e565b60408051601f198184030181528282018252600583527f66734345540000000000000000000000000000000000000000000000000000006020840152609b5491517fff679e9d0000000000000000000000000000000000000000000000000000000081529093506000916001600160a01b03169063ff679e9d90610c37906002906004016135a9565b6020604051808303816000875af1158015610c56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7a91906135bc565b90506000609c60009054906101000a90046001600160a01b03166001600160a01b03166343bdb458336040518060c00160405280610cb8600261226a565b6001600160a01b039081168252609854811660208301528781166040808401919091526099549091166060830152608082018a905260a0909101889052517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b168152610d319291908e908e90600401613640565b6020604051808303816000875af1158015610d50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7491906135bc565b6040516340c10f1960e01b81526001600160a01b038881166004830152602482018a9052919250908216906340c10f1990604401600060405180830381600087803b158015610dc257600080fd5b505af1158015610dd6573d6000803e3d6000fd5b50505050806001600160a01b0316633f4ba83a6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e1557600080fd5b505af1158015610e29573d6000803e3d6000fd5b505050507f273b9f884ffe346b2bb72a39705c98bb4a8d5f9aad5dd026f9fb70724ddc95e3610e5782611ec8565b8286868b8b604051610e6e96959493929190613687565b60405180910390a198975050505050505050565b610e8a6120d9565b61088d6122b3565b610e9a6120d9565b6001600160a01b038116610ef05760405162461bcd60e51b815260206004820152601f60248201527f696e76616c696420617267756d656e74202d207a65726f20616464726573730060448201526064016104e6565b610efb6097826122f0565b6040516001600160a01b038216907ff68e73cec97f2d70aa641fb26e87a4383686e2efacb648f2165aeb02ac562ec590600090a250565b610f3a6120d9565b6001600160a01b038116610f905760405162461bcd60e51b815260206004820152601f60248201527f696e76616c696420617267756d656e74202d207a65726f20616464726573730060448201526064016104e6565b610f9b60978261236c565b6040516001600160a01b038216907fed9c8ad8d5a0a66898ea49d2956929c93ae2e8bd50281b2ed897c5d1a6737e0b90600090a250565b600054610100900460ff1615808015610ff25750600054600160ff909116105b8061100c5750303b15801561100c575060005460ff166001145b61107e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016104e6565b6000805460ff1916600117905580156110a1576000805461ff0019166101001790555b6001600160a01b0386166110f857604051636cbb462960e01b815260206004820152601060248201527f4964656e7469747952656769737472790000000000000000000000000000000060448201526064016104e6565b6001600160a01b03851661114f57604051636cbb462960e01b815260206004820152600d60248201527f41646d696e4964656e746974790000000000000000000000000000000000000060448201526064016104e6565b6001600160a01b0384166111a657604051636cbb462960e01b815260206004820152601260248201527f5065726d697373696f6e734d616e61676572000000000000000000000000000060448201526064016104e6565b6001600160a01b0383166111fd57604051636cbb462960e01b815260206004820152601160248201527f436f6d706c69616e6365466163746f727900000000000000000000000000000060448201526064016104e6565b6001600160a01b03821661125457604051636cbb462960e01b815260206004820152600d60248201527f546f6b656e73466163746f72790000000000000000000000000000000000000060448201526064016104e6565b61125c61240a565b61126461247d565b61126e88886124f0565b609880546001600160a01b03199081166001600160a01b0389811691909117909255609980548216888416179055609a80548216878416179055609b80548216868416179055609c8054909116918416919091179055801561130a576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050505050565b600061131e6121d7565b8460a00151516003141580611336575060e085015151155b80611345575061010085015151155b8061135257506020850151155b8061135f575060c0850151155b1561139857846040517f03c183950000000000000000000000000000000000000000000000000000000081526004016104e691906137af565b60a08401515115806113ad5750604084015151155b806113bb5750606084015151155b806113c557508351155b156113fe57836040517f6fbb6f1c0000000000000000000000000000000000000000000000000000000081526004016104e6919061385d565b609b546040517fff679e9d0000000000000000000000000000000000000000000000000000000081526000916001600160a01b03169063ff679e9d90611449906003906004016135a9565b6020604051808303816000875af1158015611468573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061148c91906135bc565b90506000609c60009054906101000a90046001600160a01b03166001600160a01b0316637351a9fd336040518060c001604052806114ca600361226a565b6001600160a01b03908116825260985481166020808401919091528882166040808501919091526099549092166060840152815180820183526000808252608085019190915282519182018352815260a090920191909152517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526115609291908c908c908c90600401613870565b6020604051808303816000875af115801561157f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a391906135bc565b60208601516040516340c10f1960e01b81526001600160a01b03878116600483015260248201929092529192508216906340c10f1990604401600060405180830381600087803b1580156115f657600080fd5b505af115801561160a573d6000803e3d6000fd5b50505050806001600160a01b0316633f4ba83a6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561164957600080fd5b505af115801561165d573d6000803e3d6000fd5b505050507f273b9f884ffe346b2bb72a39705c98bb4a8d5f9aad5dd026f9fb70724ddc95e361168b82611ec8565b828960e001518a61010001518960200151896040516116af96959493929190613687565b60405180910390a19695505050505050565b6116ca33610434565b61172d5760405162461bcd60e51b815260206004820152602e60248201527f4167656e74526f6c653a2063616c6c657220646f6573206e6f7420686176652060448201526d746865204167656e7420726f6c6560901b60648201526084016104e6565b6040516340c10f1960e01b81526001600160a01b038381166004830152602482018390528491908216906340c10f1990604401600060405180830381600087803b15801561177a57600080fd5b505af115801561178e573d6000803e3d6000fd5b505050507f273b9f884ffe346b2bb72a39705c98bb4a8d5f9aad5dd026f9fb70724ddc95e36117bc85611ec8565b85836001600160a01b03166306fdde036040518163ffffffff1660e01b8152600401600060405180830381865afa1580156117fb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261182391908101906134c7565b846001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015611861573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261188991908101906134c7565b86886040516105c396959493929190613687565b60006118a76121d7565b6118b18480613937565b905060031415806118ce57506118ca6020850185613937565b1590505b806118e557506118e16040850185613937565b1590505b1561191e57836040517f9fa61ed70000000000000000000000000000000000000000000000000000000081526004016104e69190613a52565b609a546040516349e40eeb60e11b81526001600160a01b03909116906393c81dd690611951908690600090600401613a65565b602060405180830381865afa15801561196e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119929190613357565b6119ba57604051630201de7d60e11b81526001600160a01b03841660048201526024016104e6565b60006119d66119c98680613937565b61040f6040890189613937565b6000818152609e60205260409020549091506001600160a01b031615611a4357611a008580613937565b611a0d6040880188613937565b6040517f8d47a5750000000000000000000000000000000000000000000000000000000081526004016104e69493929190613a8b565b6000611a4f8680613937565b611a5c6020890189613937565b604051602001611a6f9493929190613abd565b60408051601f1981840301815291905290506000611a8d8780613937565b604051602001611a9e929190613b04565b60408051601f1981840301815290829052609b547fff679e9d0000000000000000000000000000000000000000000000000000000083529092506000916001600160a01b039091169063ff679e9d90611afc906001906004016135a9565b6020604051808303816000875af1158015611b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3f91906135bc565b90506000609c60009054906101000a90046001600160a01b03166001600160a01b0316638d5dc33b336040518060c00160405280611b7d600161226a565b6001600160a01b039081168252609854811660208301528781166040808401919091526099549091166060830152608082018a905260a0909101889052517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b168152611bf49291908e90600401613b3d565b6020604051808303816000875af1158015611c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c3791906135bc565b6000868152609e60205260409081902080546001600160a01b0319166001600160a01b0384811691821790925591516340c10f1960e01b8152908b166004820152602481018a9052919250906340c10f1990604401600060405180830381600087803b158015611ca657600080fd5b505af1158015611cba573d6000803e3d6000fd5b50505050806001600160a01b0316633f4ba83a6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611cf957600080fd5b505af1158015611d0d573d6000803e3d6000fd5b505050507f273b9f884ffe346b2bb72a39705c98bb4a8d5f9aad5dd026f9fb70724ddc95e3611d3b82611ec8565b8286868b8d604051610e6e96959493929190613687565b611d5a6120d9565b6109f782826124f0565b600084848484604051602001611d7d9493929190613b71565b604051602081830303815290604052805190602001209050949350505050565b611da56120d9565b6001600160a01b038116611e215760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016104e6565b61087a81612185565b60006001600160a01b038216611ea85760405162461bcd60e51b815260206004820152602260248201527f526f6c65733a206163636f756e7420697320746865207a65726f20616464726560448201527f737300000000000000000000000000000000000000000000000000000000000060648201526084016104e6565b506001600160a01b03166000908152602091909152604090205460ff1690565b6000816001600160a01b031663f8333bfa6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f08573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104419190613b93565b61087a6120d9565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615611f6c57611f67836126b3565b505050565b826001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611fc6575060408051601f3d908101601f19168201909252611fc391810190613bb0565b60015b6120385760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f74205555505300000000000000000000000000000000000060648201526084016104e6565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81146120cd5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c6555554944000000000000000000000000000000000000000000000060648201526084016104e6565b50611f67838383612771565b6065546001600160a01b0316331461088d5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104e6565b61213b61279c565b6033805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b609a546040516349e40eeb60e11b81526001600160a01b03909116906393c81dd69061220a903390600090600401613a65565b602060405180830381865afa158015612227573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224b9190613357565b61088d57604051630201de7d60e11b81523360048201526024016104e6565b6000609d600083600381111561228257612282613379565b600381111561229357612293613379565b81526020810191909152604001600020546001600160a01b031692915050565b6122bb6127ee565b6033805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586121683390565b6122fa8282611e2a565b156123475760405162461bcd60e51b815260206004820152601f60248201527f526f6c65733a206163636f756e7420616c72656164792068617320726f6c650060448201526064016104e6565b6001600160a01b0316600090815260209190915260409020805460ff19166001179055565b6123768282611e2a565b6123e85760405162461bcd60e51b815260206004820152602160248201527f526f6c65733a206163636f756e7420646f6573206e6f74206861766520726f6c60448201527f650000000000000000000000000000000000000000000000000000000000000060648201526084016104e6565b6001600160a01b0316600090815260209190915260409020805460ff19169055565b600054610100900460ff166124755760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b60648201526084016104e6565b61088d612841565b600054610100900460ff166124e85760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b60648201526084016104e6565b61088d6128b5565b60005b81811015611f67573683838381811061250e5761250e613bc9565b604002919091019150600090506125286020830183612ad3565b6001600160a01b0316148061255d5750600061254a6040830160208401613bdf565b600381111561255b5761255b613379565b145b1561259657806040517f5afd08ad0000000000000000000000000000000000000000000000000000000081526004016104e69190613bfc565b6000609d816125ab6040850160208601613bdf565b60038111156125bc576125bc613379565b60038111156125cd576125cd613379565b81526020810191909152604001600020546001600160a01b03161461262f576125fc6040820160208301613bdf565b6040517f537f5b510000000000000000000000000000000000000000000000000000000081526004016104e691906135a9565b61263c6020820182612ad3565b609d60006126506040850160208601613bdf565b600381111561266157612661613379565b600381111561267257612672613379565b8152602081019190915260400160002080546001600160a01b0319166001600160a01b039290921691909117905550806126ab81613c3a565b9150506124f3565b6001600160a01b0381163b6127305760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e74726163740000000000000000000000000000000000000060648201526084016104e6565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b61277a8361292c565b6000825111806127875750805b15611f6757612796838361296c565b50505050565b60335460ff1661088d5760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f742070617573656400000000000000000000000060448201526064016104e6565b60335460ff161561088d5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a207061757365640000000000000000000000000000000060448201526064016104e6565b600054610100900460ff166128ac5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b60648201526084016104e6565b61088d33612185565b600054610100900460ff166129205760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b60648201526084016104e6565b6033805460ff19169055565b612935816126b3565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606129918383604051806060016040528060278152602001613c9160279139612998565b9392505050565b6060600080856001600160a01b0316856040516129b59190613c61565b600060405180830381855af49150503d80600081146129f0576040519150601f19603f3d011682016040523d82523d6000602084013e6129f5565b606091505b5091509150612a0686838387612a10565b9695505050505050565b60608315612a7f578251600003612a78576001600160a01b0385163b612a785760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104e6565b5081610476565b6104768383815115612a945781518083602001fd5b8060405162461bcd60e51b81526004016104e69190613c7d565b6001600160a01b038116811461087a57600080fd5b8035612ace81612aae565b919050565b600060208284031215612ae557600080fd5b813561299181612aae565b60008083601f840112612b0257600080fd5b50813567ffffffffffffffff811115612b1a57600080fd5b602083019150836020828501011115612b3257600080fd5b9250929050565b60008060008060408587031215612b4f57600080fd5b843567ffffffffffffffff80821115612b6757600080fd5b612b7388838901612af0565b90965094506020870135915080821115612b8c57600080fd5b50612b9987828801612af0565b95989497509550505050565b60008060008060808587031215612bbb57600080fd5b8435612bc681612aae565b93506020850135612bd681612aae565b92506040850135612be681612aae565b9396929550929360600135925050565b600080600060608486031215612c0b57600080fd5b8335612c1681612aae565b92506020840135612c2681612aae565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715612c7057612c70612c37565b60405290565b60405160e0810167ffffffffffffffff81118282101715612c7057612c70612c37565b604051610120810167ffffffffffffffff81118282101715612c7057612c70612c37565b604051601f8201601f1916810167ffffffffffffffff81118282101715612ce657612ce6612c37565b604052919050565b600067ffffffffffffffff821115612d0857612d08612c37565b50601f01601f191660200190565b6000612d29612d2484612cee565b612cbd565b9050828152838383011115612d3d57600080fd5b828260208301376000602084830101529392505050565b60008060408385031215612d6757600080fd5b8235612d7281612aae565b9150602083013567ffffffffffffffff811115612d8e57600080fd5b8301601f81018513612d9f57600080fd5b612dae85823560208401612d16565b9150509250929050565b600082601f830112612dc957600080fd5b61299183833560208501612d16565b6002811061087a57600080fd5b8035612ace81612dd8565b600060208284031215612e0257600080fd5b6040516020810181811067ffffffffffffffff82111715612e2557612e25612c37565b6040529135825250919050565b60008060008060808587031215612e4857600080fd5b843567ffffffffffffffff80821115612e6057600080fd5b9086019060608289031215612e7457600080fd5b612e7c612c4d565b823582811115612e8b57600080fd5b612e978a828601612db8565b82525060208301359150612eaa82612dd8565b81602082015260408301359250612ec083612aae565b6040810192909252509350612ed88660208701612df0565b925060408501359150612eed60608601612ac3565b905092959194509250565b60008083601f840112612f0a57600080fd5b50813567ffffffffffffffff811115612f2257600080fd5b6020830191508360208260061b8501011115612b3257600080fd5b600080600080600080600060c0888a031215612f5857600080fd5b873567ffffffffffffffff811115612f6f57600080fd5b612f7b8a828b01612ef8565b9098509650506020880135612f8f81612aae565b94506040880135612f9f81612aae565b93506060880135612faf81612aae565b92506080880135612fbf81612aae565b915060a0880135612fcf81612aae565b8091505092959891949750929550565b803560078110612ace57600080fd5b6004811061087a57600080fd5b8035612ace81612fee565b600060e0828403121561301857600080fd5b613020612c76565b90508135815261303260208301612ffb565b6020820152604082013567ffffffffffffffff8082111561305257600080fd5b61305e85838601612db8565b6040840152606084013591508082111561307757600080fd5b61308385838601612db8565b6060840152608084013591508082111561309c57600080fd5b6130a885838601612db8565b608084015260a08401359150808211156130c157600080fd5b6130cd85838601612db8565b60a084015260c08401359150808211156130e657600080fd5b506130f384828501612db8565b60c08301525092915050565b600060e0828403121561311157600080fd5b613119612c76565b9050813581526020820135602082015260408201356005811061313b57600080fd5b6040820152606082810135908201526080820135600b811061315c57600080fd5b608082015260a0828101359082015261317760c08301612ac3565b60c082015292915050565b600080600080610140858703121561319957600080fd5b843567ffffffffffffffff808211156131b157600080fd5b818701915061012080838a0312156131c857600080fd5b6131d0612c99565b83358152602084013560208201526131ea60408501612de5565b60408201526131fb60608501612fdf565b606082015261320c60808501612de5565b608082015260a08401358381111561322357600080fd5b61322f8b828701612db8565b60a08301525060c084013560c082015260e08401358381111561325157600080fd5b61325d8b828701612db8565b60e083015250610100808501358481111561327757600080fd5b6132838c828801612db8565b8284015250508097505060208801359250818311156132a157600080fd5b6132ad89848a01613006565b95506132bc8960408a016130ff565b94506132c9818901612ac3565b935050505092959194509250565b6000806000606084860312156132ec57600080fd5b833567ffffffffffffffff81111561330357600080fd5b840160608187031215612c1657600080fd5b6000806020838503121561332857600080fd5b823567ffffffffffffffff81111561333f57600080fd5b61334b85828601612ef8565b90969095509350505050565b60006020828403121561336957600080fd5b8151801515811461299157600080fd5b634e487b7160e01b600052602160045260246000fd5b6004811061087a5761087a613379565b60a081016133ac8761338f565b9581526001600160a01b0394851660208201526040810193909352908316606083015290911660809091015290565b608081016133e88661338f565b9481526001600160a01b039384166020820152604081019290925290911660609091015290565b60005b8381101561342a578181015183820152602001613412565b50506000910152565b6000815180845261344b81602086016020860161340f565b601f01601f19169290920160200192915050565b6002811061087a5761087a613379565b60008151606084526134846060850182613433565b905060208301516134948161345f565b60208501526040928301516001600160a01b031692909301919091525090565b602081526000612991602083018461346f565b6000602082840312156134d957600080fd5b815167ffffffffffffffff8111156134f057600080fd5b8201601f8101841361350157600080fd5b805161350f612d2482612cee565b81815285602083850101111561352457600080fd5b61353582602083016020860161340f565b95945050505050565b7f4672696374696f6e6c657373202d204469676974616c2053656375726974792081527f6f6620000000000000000000000000000000000000000000000000000000000060208201526000825161359c81602385016020870161340f565b9190910160230192915050565b602081016135b68361338f565b91905290565b6000602082840312156135ce57600080fd5b815161299181612aae565b60006001600160a01b0380835116845280602084015116602085015280604084015116604085015280606084015116606085015250608082015160c0608085015261362760c0850182613433565b905060a083015184820360a08601526135358282613433565b6001600160a01b038516815260806020820152600061366260808301866135d9565b8281036040840152613674818661346f565b9150508251606083015295945050505050565b6136908761338f565b86815260006001600160a01b03808816602084015260c060408401526136b960c0840188613433565b83810360608501526136cb8188613433565b6080850196909652509290921660a09091015250949350505050565b6136f08161345f565b9052565b6007811061087a5761087a613379565b6136f0816136f4565b60006101208251845260208301516020850152604083015161373260408601826136e7565b5060608301516137456060860182613704565b50608083015161375860808601826136e7565b5060a08301518160a086015261377082860182613433565b91505060c083015160c085015260e083015184820360e08601526137948282613433565b9150506101008084015185830382870152612a068382613433565b602081526000612991602083018461370d565b80518252600060208201516137d68161338f565b80602085015250604082015160e060408501526137f660e0850182613433565b90506060830151848203606086015261380f8282613433565b915050608083015184820360808601526138298282613433565b91505060a083015184820360a08601526138438282613433565b91505060c083015184820360c08601526135358282613433565b60208152600061299160208301846137c2565b60006101606001600160a01b0388168352806020840152613893818401886135d9565b905082810360408401526138a7818761370d565b905082810360608401526138bb81866137c2565b91505082516080830152602083015160a08301526040830151600581106138e4576138e4613379565b60c0830152606083015160e08301526080830151600b811061390857613908613379565b61010083015260a083015161012083015260c0909201516001600160a01b031661014090910152949350505050565b6000808335601e1984360301811261394e57600080fd5b83018035915067ffffffffffffffff82111561396957600080fd5b602001915036819003821315612b3257600080fd5b6000808335601e1984360301811261399557600080fd5b830160208101925035905067ffffffffffffffff8111156139b557600080fd5b803603821315612b3257600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60006139f9828361397e565b60608552613a0b6060860182846139c4565b915050613a1b602084018461397e565b8583036020870152613a2e8382846139c4565b92505050613a3f604084018461397e565b8583036040870152612a068382846139c4565b60208152600061299160208301846139ed565b6001600160a01b038316815260408101613a7e836136f4565b8260208301529392505050565b604081526000613a9f6040830186886139c4565b8281036020840152613ab28185876139c4565b979650505050505050565b8385823760008482017f204672696374696f6e6c6573732000000000000000000000000000000000000081528385600e83013760009301600e019283525090949350505050565b7f667300000000000000000000000000000000000000000000000000000000000081528183600283013760009101600201908152919050565b6001600160a01b0384168152606060208201526000613b5f60608301856135d9565b8281036040840152612a0681856139ed565b8385823760008482016000815283858237600093019283525090949350505050565b600060208284031215613ba557600080fd5b815161299181612fee565b600060208284031215613bc257600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b600060208284031215613bf157600080fd5b813561299181612fee565b604081018235613c0b81612aae565b6001600160a01b031682526020830135613c2481612fee565b613c2d8161338f565b8060208401525092915050565b600060018201613c5a57634e487b7160e01b600052601160045260246000fd5b5060010190565b60008251613c7381846020870161340f565b9190910192915050565b602081526000612991602083018461343356fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220c9d472b724465f6b973dbfadcf82345d6cd0571d8cfaf0d49d058e7ffd54b60a64736f6c63430008110033

Verified Source Code Full Match

Compiler: v0.8.17+commit.8df45f5f EVM: london Optimization: Yes (1000 runs)
Roles.sol 99 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;

/**
 * @title Roles
 * @dev Library for managing addresses assigned to a Role.
 */
library Roles {
    struct Role {
        mapping(address => bool) bearer;
    }

    /**
     * @dev Give an account access to this role.
     */
    function add(Role storage role, address account) internal {
        require(!has(role, account), "Roles: account already has role");
        role.bearer[account] = true;
    }

    /**
     * @dev Remove an account's access to this role.
     */
    function remove(Role storage role, address account) internal {
        require(has(role, account), "Roles: account does not have role");
        role.bearer[account] = false;
    }

    /**
     * @dev Check if an account has this role.
     * @return bool
     */
    function has(Role storage role, address account) internal view returns (bool) {
        require(account != address(0), "Roles: account is the zero address");
        return role.bearer[account];
    }
}
FrictionlessTreasuryManager.sol 417 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 { IIdentityRegistry } from "@ERC-3643/registry/interface/IIdentityRegistry.sol";
import { AgentRoleUpgradeable } from "@ERC-3643/roles/AgentRoleUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

import { IToken } from "@ERC-3643/token/IToken.sol";
import { IBasicFrictionlessToken } from "@interface/IBasicFrictionlessToken.sol";
import { IFrictionlessDigitalSecurityToken } from "@interface/IFrictionlessDigitalSecurityToken.sol";
import { IFrictionlessFundDepositToken } from "@interface/IFrictionlessFundDepositToken.sol";
import { IFrictionlessOnChainAssetToken } from "@interface/IFrictionlessOnChainAssetToken.sol";
import { IFrictionlessComplianceFactory } from "@interface/IFrictionlessComplianceFactory.sol";
import { IFrictionlessTokensFactory } from "@interface/IFrictionlessTokensFactory.sol";
import { IFrictionlessPermissionsManager } from "@interface/IFrictionlessPermissionsManager.sol";
import { IFrictionlessTreasuryManager } from "@interface/IFrictionlessTreasuryManager.sol";
import { IFrictionlessAttestationManager } from "@interface/IFrictionlessAttestationManager.sol";

/**
 * @title FrictionlessTreasuryManager - Manages the minting, transfer and burning of all tokens in the Frictionless protocol
 * @author Frictionless Group Holdings S.à.r.l
 * @notice See {IFrictionlessTreasuryManager}
 */
contract FrictionlessTreasuryManager is
    IFrictionlessTreasuryManager,
    PausableUpgradeable,
    OwnableUpgradeable,
    AgentRoleUpgradeable,
    UUPSUpgradeable
{
    /// @dev The OnChain Identity registry
    address private _identityRegistry;

    /// @dev The OnChain Identity for the PROTOCOL_ADMIN
    address private _adminIdentity;

    /// @dev The Frictionless Permissions Manager
    address private _permissionsManager;

    /// @dev The compliance factory to load the compliance contract for each specific token
    IFrictionlessComplianceFactory private _complianceFactory;

    /// @dev The tokens factory to verify the type of token
    IFrictionlessTokensFactory private _tokensFactory;

    /// @dev The map associating the Frictionless token type with the relevant Implementation Authority contract
    mapping(IBasicFrictionlessToken.FrictionlessTokenTypes => address) internal _tokensImplAuthority;

    mapping(bytes32 => address) internal _existingFundDepositTokens;

    /// @dev Modifier to ensure functions are callable by the Treasury role only.
    modifier onlyTreasury() {
        _onlyTreasury();
        _;
    }

    /**
     *  @dev initialize the FrictionlessTreasuryManager
     *  @param identityRegistry_ the address of the deployed TREX Identity Registry
     *  @param adminIdentity_ the address of the deployed PROTOCOL_ADMIN OnChainId
     *  @param permissionsManager_ the address of the deployed FrictionlessPermissionsManager
     *  requires identityRegistry_ to be valid contract address
     *  requires adminIdentity_ to be valid contract address
     *  requires permissionsManager_ to be valid contract address
     */
    function init(
        FrictionlessTokenInitData[] calldata initDataArr_,
        address identityRegistry_,
        address adminIdentity_,
        address permissionsManager_,
        address complianceFactory_,
        address tokensFactory_
    ) external initializer {
        if (identityRegistry_ == address(0)) revert FrictionlessIsZeroAddress("IdentityRegistry");
        if (adminIdentity_ == address(0)) revert FrictionlessIsZeroAddress("AdminIdentity");
        if (permissionsManager_ == address(0)) revert FrictionlessIsZeroAddress("PermissionsManager");
        if (complianceFactory_ == address(0)) revert FrictionlessIsZeroAddress("ComplianceFactory");
        if (tokensFactory_ == address(0)) revert FrictionlessIsZeroAddress("TokensFactory");

        __Ownable_init();
        __Pausable_init();

        _setTokensInitData(initDataArr_);

        _identityRegistry = identityRegistry_;
        _adminIdentity = adminIdentity_;

        _permissionsManager = permissionsManager_;
        _complianceFactory = IFrictionlessComplianceFactory(complianceFactory_);
        _tokensFactory = IFrictionlessTokensFactory(tokensFactory_);
    }

    /**
     * @dev See {PausableUpgradeable-_pause}
     */
    function pause() external override onlyOwner {
        _pause();
    }

    /**
     * @dev See {PausableUpgradeable-_unpause}
     */
    function unpause() external override onlyOwner {
        _unpause();
    }

    /// @inheritdoc IFrictionlessTreasuryManager
    function setTokensInitData(FrictionlessTokenInitData[] calldata initDataArr_) external onlyOwner {
        _setTokensInitData(initDataArr_);
    }

    /// @inheritdoc IFrictionlessTreasuryManager
    function mintFundDepositForTreasury(
        IFrictionlessFundDepositToken.FFDImmutableData calldata depositData_,
        address treasuryAddress_,
        uint256 amount_
    ) public override onlyTreasury returns (address) {
        if (
            bytes(depositData_.currency).length != 3 ||
            bytes(depositData_.description).length == 0 ||
            bytes(depositData_.fundIBAN).length == 0
        ) {
            revert FrictionlessTreasuryManagerInvalidDepositData(depositData_);
        }

        if (
            !IFrictionlessPermissionsManager(_permissionsManager).hasClaim(
                treasuryAddress_,
                IFrictionlessPermissionsManager.FrictionlessPermissionedUser.PROTOCOL_TREASURY
            )
        ) {
            revert FrictionlessTreasuryManagerNotAProtocolTreasury(treasuryAddress_);
        }

        bytes32 tokenKey_ = getFundDepositTokenKey(depositData_.currency, depositData_.fundIBAN);

        if (_existingFundDepositTokens[tokenKey_] != address(0)) {
            revert FrictionlessTreasuryManagerFundDepositTokenAlreadyExists(
                depositData_.currency,
                depositData_.fundIBAN
            );
        }

        string memory tokenName_ = string(
            abi.encodePacked(depositData_.currency, " Frictionless ", depositData_.description)
        );
        string memory tokenSymbol_ = string(abi.encodePacked("fs", depositData_.currency));

        address compliance_ = _complianceFactory.deployCompliance(
            IBasicFrictionlessToken.FrictionlessTokenTypes.FUND_DEPOSIT_TOKEN
        );

        address tokenProxyAddr_ = _tokensFactory.deployFundDepositToken(
            msg.sender,
            IFrictionlessTokensFactory.BaseTokenInitParams(
                _getTokenImplAuthority(IBasicFrictionlessToken.FrictionlessTokenTypes.FUND_DEPOSIT_TOKEN),
                _identityRegistry,
                compliance_,
                _adminIdentity,
                tokenName_,
                tokenSymbol_
            ),
            depositData_
        );

        _existingFundDepositTokens[tokenKey_] = tokenProxyAddr_;

        // Mint the deposit tokens to the Treasury address
        IToken(tokenProxyAddr_).mint(treasuryAddress_, amount_);
        IToken(tokenProxyAddr_).unpause();

        emit FrictionlessTokenMinted(
            _getTokenType(tokenProxyAddr_),
            tokenProxyAddr_,
            tokenName_,
            tokenSymbol_,
            amount_,
            treasuryAddress_
        );

        return tokenProxyAddr_;
    }

    /// @inheritdoc IFrictionlessTreasuryManager
    function mintDigitalSecurity(
        IFrictionlessDigitalSecurityToken.FDSImmutableData memory initData_,
        IFrictionlessDigitalSecurityToken.FDSMutableData memory updateData_,
        uint256 amount_,
        address userAddress_
    ) public override onlyTreasury returns (address) {
        if (bytes(initData_.baseCurrency).length != 3 || initData_.onChainAssetAddress == address(0)) {
            revert FrictionlessTreasuryManagerInvalidFDSImmutableData(initData_);
        }

        string memory tokenName_ = string(
            abi.encodePacked("Frictionless - Digital Security of ", IToken(initData_.onChainAssetAddress).symbol())
        );
        string memory tokenSymbol_ = "fsCET";

        address compliance_ = _complianceFactory.deployCompliance(
            IBasicFrictionlessToken.FrictionlessTokenTypes.DIGITAL_SECURITY_TOKEN
        );

        address tokenProxyAddr_ = _tokensFactory.deployDigitalSecurityToken(
            msg.sender,
            IFrictionlessTokensFactory.BaseTokenInitParams(
                _getTokenImplAuthority(IBasicFrictionlessToken.FrictionlessTokenTypes.DIGITAL_SECURITY_TOKEN),
                _identityRegistry,
                compliance_,
                _adminIdentity,
                tokenName_,
                tokenSymbol_
            ),
            initData_,
            updateData_
        );

        // Mint the Digital Security to the relevant registered Investor
        IToken(tokenProxyAddr_).mint(userAddress_, amount_);
        IToken(tokenProxyAddr_).unpause();

        emit FrictionlessTokenMinted(
            _getTokenType(tokenProxyAddr_),
            tokenProxyAddr_,
            tokenName_,
            tokenSymbol_,
            amount_,
            userAddress_
        );

        return tokenProxyAddr_;
    }

    /// @inheritdoc IFrictionlessTreasuryManager
    function mintOnChainAsset(
        IFrictionlessOnChainAssetToken.FOCASpecData memory specData_,
        IFrictionlessOnChainAssetToken.FOCAIssuanceData memory issuanceData_,
        IFrictionlessOnChainAssetToken.FOCAUpdateData memory updateData_,
        address custodianAddress_
    ) public override onlyTreasury returns (address) {
        if (
            bytes(specData_.baseCurrency).length != 3 ||
            bytes(specData_.name).length == 0 ||
            bytes(specData_.symbol).length == 0 ||
            specData_.maturityDays == 0 ||
            specData_.stripTotal == 0
        ) {
            revert FrictionlessTreasuryManagerInvalidFOCASpecData(specData_);
        }

        if (
            bytes(issuanceData_.issuanceDocs).length == 0 ||
            bytes(issuanceData_.onChainAssetUUID).length == 0 ||
            bytes(issuanceData_.issuerUUID).length == 0 ||
            issuanceData_.auctionedOn == 0
        ) {
            revert FrictionlessTreasuryManagerInvalidFOCAIssuanceData(issuanceData_);
        }

        address compliance_ = _complianceFactory.deployCompliance(
            IBasicFrictionlessToken.FrictionlessTokenTypes.ON_CHAIN_ASSET_TOKEN
        );

        address tokenProxyAddr_ = _tokensFactory.deployOnChainAssetToken(
            msg.sender,
            IFrictionlessTokensFactory.BaseTokenInitParams(
                _getTokenImplAuthority(IBasicFrictionlessToken.FrictionlessTokenTypes.ON_CHAIN_ASSET_TOKEN),
                _identityRegistry,
                compliance_,
                _adminIdentity,
                "",
                ""
            ),
            specData_,
            issuanceData_,
            updateData_
        );

        // Mint the OnChain Asset to the relevant registered PERMISSIONED_CUSTODIAN
        IToken(tokenProxyAddr_).mint(custodianAddress_, updateData_.total);
        IToken(tokenProxyAddr_).unpause();

        emit FrictionlessTokenMinted(
            _getTokenType(tokenProxyAddr_),
            tokenProxyAddr_,
            specData_.name,
            specData_.symbol,
            updateData_.total,
            custodianAddress_
        );

        return tokenProxyAddr_;
    }

    /// @inheritdoc IFrictionlessTreasuryManager
    function mintTokenForUser(address tokenAddr_, address userAddress_, uint256 amount_) public override onlyAgent {
        IToken token_ = IToken(tokenAddr_);

        token_.mint(userAddress_, amount_);

        emit FrictionlessTokenMinted(
            _getTokenType(tokenAddr_),
            tokenAddr_,
            token_.name(),
            token_.symbol(),
            amount_,
            userAddress_
        );
    }

    /// @inheritdoc IFrictionlessTreasuryManager
    function transferToken(
        address tokenAddr_,
        address userAddressFrom_,
        address userAddressTo_,
        uint256 amount_
    ) public override onlyAgent {
        IToken(tokenAddr_).forcedTransfer(userAddressFrom_, userAddressTo_, amount_);

        emit FrictionlessTokenTransferred(
            _getTokenType(tokenAddr_),
            tokenAddr_,
            amount_,
            userAddressTo_,
            userAddressTo_
        );
    }

    /// @inheritdoc IFrictionlessTreasuryManager
    function burnToken(address tokenAddr_, address userAddress_, uint256 amount_) public override onlyAgent {
        IToken(tokenAddr_).burn(userAddress_, amount_);

        emit FrictionlessTokenBurned(_getTokenType(tokenAddr_), tokenAddr_, amount_, userAddress_);
    }

    /// @inheritdoc IFrictionlessTreasuryManager
    function getFundDepositToken(
        string calldata currency_,
        string calldata fundIBAN_
    ) public view override returns (address) {
        return _existingFundDepositTokens[getFundDepositTokenKey(currency_, fundIBAN_)];
    }

    /// @inheritdoc IFrictionlessTreasuryManager
    function getFundDepositTokenKey(
        string calldata currency_,
        string calldata fundIBAN_
    ) public pure override returns (bytes32) {
        return keccak256(abi.encodePacked(currency_, fundIBAN_));
    }

    /// @dev Returns the address of the implementation authority for the specified Frictionless tokenType_
    function _getTokenImplAuthority(
        IBasicFrictionlessToken.FrictionlessTokenTypes tokenType_
    ) internal view returns (address) {
        return _tokensImplAuthority[tokenType_];
    }

    /// @dev Sets relevant token implementation authority with each associated token type
    function _setTokensInitData(FrictionlessTokenInitData[] calldata initDataArr_) internal {
        for (uint256 i = 0; i < initDataArr_.length; i++) {
            FrictionlessTokenInitData calldata initData_ = initDataArr_[i];

            if (
                initData_.implAuthority == address(0) ||
                initData_.tokenType == IBasicFrictionlessToken.FrictionlessTokenTypes.NONE
            ) {
                revert FrictionlessTreasuryManagerInvalidTokenInitData(initData_);
            }

            if (_tokensImplAuthority[initData_.tokenType] != address(0)) {
                revert FrictionlessTreasuryManagerUnableToUpdateTokenInitData(initData_.tokenType);
            }

            _tokensImplAuthority[initData_.tokenType] = initData_.implAuthority;
        }
    }

    /// @dev Get the token type as defined by the `IBasicFrictionlessToken.FrictionlessTokenTypes` for a specified deployed token contract address
    function _getTokenType(address tokenAddr_) internal view returns (IBasicFrictionlessToken.FrictionlessTokenTypes) {
        return IBasicFrictionlessToken(tokenAddr_).getFrictionlessTokenType();
    }

    // solhint-disable-next-line no-empty-blocks
    function _authorizeUpgrade(address) internal virtual override onlyOwner {}

    /// @dev throws `FrictionlessTreasuryManagerNotAProtocolTreasury` if the msg.sender is not a PROTOCOL_TREASURY user as per the `FrictionlessPermissionsManager`
    function _onlyTreasury() internal view {
        if (
            !IFrictionlessPermissionsManager(_permissionsManager).hasClaim(
                msg.sender,
                IFrictionlessPermissionsManager.FrictionlessPermissionedUser.PROTOCOL_TREASURY
            )
        ) {
            revert FrictionlessTreasuryManagerNotAProtocolTreasury(msg.sender);
        }
    }
}
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);
}
AgentRoleUpgradeable.sol 98 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 "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "./Roles.sol";

contract AgentRoleUpgradeable is OwnableUpgradeable {
    using Roles for Roles.Role;

    Roles.Role private _agents;

    event AgentAdded(address indexed _agent);
    event AgentRemoved(address indexed _agent);

    modifier onlyAgent() {
        require(isAgent(msg.sender), "AgentRole: caller does not have the Agent role");
        _;
    }

    function addAgent(address _agent) public onlyOwner {
        require(_agent != address(0), "invalid argument - zero address");
        _agents.add(_agent);
        emit AgentAdded(_agent);
    }

    function removeAgent(address _agent) public onlyOwner {
        require(_agent != address(0), "invalid argument - zero address");
        _agents.remove(_agent);
        emit AgentRemoved(_agent);
    }

    function isAgent(address _agent) public view returns (bool) {
        return _agents.has(_agent);
    }
}
UUPSUpgradeable.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.0;

import "../../interfaces/draft-IERC1822.sol";
import "../ERC1967/ERC1967Upgrade.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 *
 * _Available since v4.1._
 */
abstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
    address private immutable __self = address(this);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        require(address(this) != __self, "Function must be called through delegatecall");
        require(_getImplementation() == __self, "Function must be called through active proxy");
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
        _;
    }

    /**
     * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
        return _IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeTo(address newImplementation) external virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data, true);
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeTo} and {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal override onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;
}
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;
}
PausableUpgradeable.sol 117 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }

    /**
     * @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;
}
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);
}
IFrictionlessDigitalSecurityToken.sol 116 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 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 { IBasicFrictionlessToken } from "@interface/IBasicFrictionlessToken.sol";

/**
 * @title IFrictionlessDigitalSecurityToken - The permissioned & transferable digital security which represents the future cash flow from the `FrictionlessOnChainAssetToken`.
 * @author Frictionless Group Holdings S.à.r.l
 * @notice This is the permissioned & transferable digital security which represents the future cash flow from the `FrictionlessOnChainAssetToken` and is purchased by
 * the Investor using `FrictionlessFundDepositTokens`. These digital securities are permissioned and transferable between permissioned Investors in a permissioned secondary market.
 * This token is linked to the `FrictionlessOnChainAssetToken` and denominated in a FIAT currency at a future date for settlement.
 */
interface IFrictionlessDigitalSecurityToken is IBasicFrictionlessToken {
    // @dev Enumeration to represent the type of the digital security, either a coupon (yield) or strip (principal)
    enum FrictionlessDigitalSecurityTokenType {
        COUPON,
        STRIP
    }

    /**
     * @dev Struct which represents the immutable data in the Token. Once set it cannot be modified.
     * @param baseCurrency the baseCurrency is the FIAT denomination of the digital security, this is the currency the `FrictionlessOnChainAssetToken` is issued in.
     * @param tokenType the type of the token as defined in the enum
     * @param onChainAssetAddress the address of the `FrictionlessOnChainAssetToken` for which this token is a future cash distribution.
     */
    struct FDSImmutableData {
        string baseCurrency;
        FrictionlessDigitalSecurityTokenType tokenType;
        address onChainAssetAddress;
    }

    /**
     * @dev Struct which represents the updatable data in the Token. This data can be modified by the Agent only.
     * @param maturesOn the maturity date of the digital security, it can be updated if there are delays in payment or at the request of the calculating agent.
     */
    struct FDSMutableData {
        uint256 maturesOn;
    }

    /// @dev error throw if there is an attempt to modify the immutable data.
    error FrictionlessDigitalSecurityTokenInitDataHasAlreadyBeenSet();

    /// @dev error throw if there is an attempt to set zero decimals.
    error FrictionlessDigitalSecurityTokenZeroDecimals();

    /**
     * @dev Sets the immutable data for the `FrictionlessDigitalSecurityToken`
     * @param initData the immutable data for the `FrictionlessDigitalSecurityToken`
     */
    function setInitData(FDSImmutableData calldata initData) external;

    /**
     * @dev Sets the updatable data for the `FrictionlessDigitalSecurityToken`
     * @param mutableData the updatable data for the `FrictionlessDigitalSecurityToken`
     */
    function setUpdateData(FDSMutableData calldata mutableData) external;

    /**
     * @dev Sets the custodian URI for the token
     * @param custodianURI the custodian URI for the token
     */
    function setCustodianURI(string calldata custodianURI) external;

    /**
     * @dev Sets the decimals value for the token
     * @param decimals the decimals value for the token
     */
    function setDecimals(uint8 decimals) external;

    /**
     * @dev Get the baseCurrency is the FIAT denomination of the digital security, this is the currency the `FrictionlessOnChainAssetToken` is issued in.
     * @return the baseCurrency is the FIAT denomination of the digital security, this is the currency the `FrictionlessOnChainAssetToken` is issued in.
     */
    function getCurrency() external view returns (string memory);

    /**
     * @dev Get the type of the token as defined in the enum `FrictionlessDigitalSecurityTokenType`.
     * @return the type of the token as defined in the enum.
     */
    function getTokenType() external view returns (FrictionlessDigitalSecurityTokenType);

    /**
     * @dev Get the onChainAssetAddress the address of the `FrictionlessOnChainAssetToken` for which this token is a future cash distribution.
     * @return onChainAssetAddress the address of the `FrictionlessOnChainAssetToken` for which this token is a future cash distribution.
     */
    function getOnChainAssetAddress() external view returns (address);

    /**
     * @dev Get the maturity date of the digital security.
     * @return the maturity date of the digital security.
     */
    function getMaturesOn() external view returns (uint256);

    /**
     * @dev Returns the custodian URI of the token
     */
    function custodianURI() external view returns (string memory);
}
IFrictionlessFundDepositToken.sol 71 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 { IBasicFrictionlessToken } from "@interface/IBasicFrictionlessToken.sol";

/**
 * @title FrictionlessFundDepositToken - A Fund Deposit Token represents a permissioned Investors FIAT contribution to a specific fund IBAN in a denominated FIAT currency.
 * @author Frictionless Group Holdings S.à.r.l
 * @notice A Fund Deposit Token represents a permissioned Investors FIAT contribution to a specific fund IBAN in a denominated FIAT currency.
 * The Fund Deposit Token is used as a means of payment and settlement. The Fund Deposit Token can only be transferred between permissioned Investors in the fund.
 * A daily attestation of the fund IBAN serves to prove the 1:1 backing with FIAT.
 * Exclusively under Frictionless Markets S.à.r.l issuance terms Investors holding a `FrictionlessFundDepositToken` have the legal right to the FIAT value held in the fund IBAN account.
 */
interface IFrictionlessFundDepositToken is IBasicFrictionlessToken {
    /**
     * @dev Struct which represents the immutable data in the Token. Once set it cannot be modified.
     * @param currency the FIAT denomination of the deposit token.
     * @param description the description of the deposit token
     * @param fundIBAN the IBAN which Frictionless Markets S.à.r.l holds a matching FIAT currency ledger with a G-SIB for this currency, attestations are provided on this IBAN.
     */
    struct FFDImmutableData {
        string currency;
        string description;
        string fundIBAN;
    }

    /// @dev error throw if there is an attempt to modify the immutable data.
    error FrictionlessFundDepositTokenUnableToUpdateInitData();

    /**
     * @dev Sets the immutable data for the `FrictionlessFundDepositToken`
     * @param initData the immutable data for the `FrictionlessFundDepositToken`
     */
    function setInitData(FFDImmutableData calldata initData) external;

    /**
     * @dev Get the currency the FIAT denomination of the deposit token.
     * @return the currency the FIAT denomination of the deposit token.
     */
    function getCurrency() external view returns (string memory);

    /**
     * @dev Get the description the description of the deposit token.
     * @return the description the description of the deposit token
     */
    function getDescription() external view returns (string memory);

    /**
     * @dev Get the IBAN which Frictionless Markets S.à.r.l holds a matching FIAT currency ledger with a G-SIB for this currency, attestations are provided on this IBAN.
     * This is restricted to onlyAgent roles.
     * @return the IBAN which Frictionless Markets S.à.r.l holds a matching FIAT currency ledger with a G-SIB for this currency, attestations are provided on this IBAN.
     */
    function getFundIBAN() external returns (string memory);
}
IFrictionlessOnChainAssetToken.sol 214 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 { IBasicFrictionlessToken } from "@interface/IBasicFrictionlessToken.sol";

/**
 * @title FrictionlessOnChainAssetToken is the extension of the ERC-3643 Token to represent OnChain Assets
 * @author Frictionless Group Holdings S.à.r.l
 * @notice Implementation of the storage of the underlying OnChain Asset and it's data.
 */
interface IFrictionlessOnChainAssetToken is IBasicFrictionlessToken {
    /// @dev Enum for the schedule of the payments by the Manager, either pro_rat or coupon/bullet style.
    enum FrictionlessOnChainAssetSchedule {
        // The Manager will make payments for the `FrictionlessOnChainAssetToken` on an coupon_only basis, with a bullet payment for the principal investment.
        SCHEDULE_COUPON_ONLY,
        // The Manager will make pro-rata payments for the `FrictionlessOnChainAssetToken` for both the principal investment and the coupon.
        SCHEDULE_PRO_RATA
    }

    /// @dev Enum for the periodicity of payments by the Manager
    enum FrictionlessOnChainAssetPaymentFrequency {
        // Payments from the Manager for the `FrictionlessOnChainAssetToken` are made daily
        PAYMENT_FREQUENCY_DAILY,
        // Payments from the Manager for the `FrictionlessOnChainAssetToken` are made weekly
        PAYMENT_FREQUENCY_WEEKLY,
        // Payments from the Manager for the `FrictionlessOnChainAssetToken` are made monthly
        PAYMENT_FREQUENCY_MONTHLY,
        // Payments from the Manager for the `FrictionlessOnChainAssetToken` are made quarterly
        PAYMENT_FREQUENCY_QUARTERLY,
        // Payments from the Manager for the `FrictionlessOnChainAssetToken` are made semi-annually
        PAYMENT_FREQUENCY_SEMI_ANNUALLY,
        // Payments from the Manager for the `FrictionlessOnChainAssetToken` are made annually
        PAYMENT_FREQUENCY_ANNUALLY,
        // Payments from the Manager for the `FrictionlessOnChainAssetToken` are made once
        PAYMENT_FREQUENCY_SINGLE
    }

    /// @dev Enum for the yield for this `FrictionlessOnChainAssetToken` is a fixed/floating rate
    enum FrictionlessOnChainAssetYieldType {
        // The yield for this `FrictionlessOnChainAssetToken` is a fixed rate
        YIELD_FIXED,
        // The yield for this `FrictionlessOnChainAssetToken` is a floating rate
        YIELD_FLOATING
    }

    /// @dev Enum for the price quote status obtained at auction
    enum FrictionlessOnChainAssetPriceStatus {
        // The `FrictionlessOnChainAssetToken` did not receive enough offers at the offer price
        PRICE_QUOTE_STATUS_UNDER_SUBSCRIBED,
        // The aggregate bid at auction matched the offer
        PRICE_QUOTE_STATUS_PRICED_AT_PAR,
        // The aggregate bid at auction is lower than the offer
        PRICE_QUOTE_STATUS_PRICED_AT_DISCOUNT,
        // The aggregate bid at auction is higher than the offer
        PRICE_QUOTE_STATUS_PRICED_AT_PREMIUM
    }

    /// @dev Enum for the current status of the `FrictionlessOnChainAssetToken`. Updated over time by the Treasury
    enum FrictionlessOnChainAssetStatus {
        // Status reserved for `FrictionlessOnChainAssetToken` that are MINTED onChain
        STATUS_MINTED,
        // Status reserved for `FrictionlessOnChainAssetToken` that are fully purchased, which means they have minted the digital securities.
        STATUS_PURCHASED,
        // Status reserved for `FrictionlessOnChainAssetToken` that have reached their maturity event
        STATUS_MATURED,
        // Status reserved for `FrictionlessOnChainAssetToken` that are in an impaired state. The parValue may be affected.
        STATUS_IMPAIRED,
        // Status reserved for `FrictionlessOnChainAssetToken` that are fully matured and have been fully redeemed.
        STATUS_REDEEMED
    }

    /// @dev Enum for the current S&P style riskGrade of the `FrictionlessOnChainAssetToken`. Updated over time by the Manager/Treasury/Risk Oracle.
    enum FrictionlessOnChainAssetRiskGrade {
        BER_AAA,
        BER_AA,
        BER_A,
        BER_BBB,
        BER_BB,
        BER_B,
        BER_CCC,
        BER_CC,
        BER_C,
        BER_D,
        BER_UNRATED
    }

    /**
     * @dev The specification data for the `FrictionlessOnChainAssetToken`, this is an immutable data struct.
     * @param issuedOn the date this `FrictionlessOnChainAssetToken` is issued by the legal Issuer, Frictionless Markets S.à.r.l
     * @param maturityDays the number of days to maturity for this `FrictionlessOnChainAssetToken`
     * @param schedule the schedule of the payments by the Manager, either pro_rat or coupon/bullet style.
     * @param paymentFrequency the periodicity of payments by the Manager
     * @param yieldType the yield for this `FrictionlessOnChainAssetToken` is a fixed/floating rate
     * @param baseCurrency the currrency the `FrictionlessOnChainAssetToken` is issued in.
     * @param stripTotal the principal amount for the `FrictionlessOnChainAssetToken`
     * @param name the name for the `FrictionlessOnChainAssetToken`
     * @param symbol the ticker symbol for the `FrictionlessOnChainAssetToken`
     */
    struct FOCASpecData {
        uint256 issuedOn;
        uint256 maturityDays;
        FrictionlessOnChainAssetSchedule schedule;
        FrictionlessOnChainAssetPaymentFrequency paymentFrequency;
        FrictionlessOnChainAssetYieldType yieldType;
        string baseCurrency;
        uint256 stripTotal;
        string name;
        string symbol;
    }

    /**
     * @dev The issuance data for the `FrictionlessOnChainAssetToken`, this is an immutable data struct.
     * @param auctionedOn the date this `FrictionlessOnChainAssetToken` is auctioned by the legal Issuer, Frictionless Markets S.à.r.l
     * @param priceQuoteStatus the price quote status obtained at auction
     * @param onChainAssetUUID the off-chain UUID in the graphQL for the token
     * @param issuerUUID the off-chain UUID in the graphQL for the Manager issuing via the legal Issuer, Frictionless Markets S.à.r.l
     * @param isin the ISIN numbre or equivalent for the `FrictionlessOnChainAssetToken`
     * @param issuanceDocs the location of the issuance docs accessible via URI or the hash of the issuance docs.
     * @param assetClass the Managers/Issuers definition of the underlying asset class for the `FrictionlessOnChainAssetToken`
     */
    struct FOCAIssuanceData {
        uint256 auctionedOn;
        FrictionlessOnChainAssetPriceStatus priceQuoteStatus;
        string onChainAssetUUID;
        string issuerUUID;
        string isin;
        string issuanceDocs;
        string assetClass;
    }

    /**
     * @dev The uopdatable data for the `FrictionlessOnChainAssetToken`.
     * @param maturesOn the date this `FrictionlessOnChainAssetToken` fully matures. Updatable if the underlying fund is extended.
     * @param total the total value of the `FrictionlessOnChainAssetToken` (strip + yield over time). Updatable based on Manager IRRs, totalReturn, etc.
     * @param status the current status of the `FrictionlessOnChainAssetToken`. Updated over time by the Treasury
     * @param yield the current yield being paid on the `FrictionlessOnChainAssetToken`. Updated over time by the Manager/Calculating Agent.
     * @param riskGrade the current riskGrade of the `FrictionlessOnChainAssetToken`. Updated over time by the Manager/Treasury/Risk Oracle.
     * @param pullToParValue the calculation of the pullToPar value of this `FrictionlessOnChainAssetToken`. Updated over time by the Manager/Calculating Agent
     * @param custodianAddress the address of the custodian for the `FrictionlessOnChainAssetToken`
     */
    struct FOCAUpdateData {
        uint256 maturesOn;
        uint256 total;
        FrictionlessOnChainAssetStatus status;
        uint256 yield;
        FrictionlessOnChainAssetRiskGrade riskGrade;
        uint256 pullToParValue;
        address custodianAddress;
    }

    /// @dev error throw if there is an attempt to modify the immutable data.
    error FrictionlessOnChainAssetTokenUnableToUpdateData();

    /**
     * @dev Sets the specData data for the `FrictionlessOnChainAssetToken`.
     * throws `FrictionlessOnChainAssetTokenUnableToUpdateData` This data is immutable, an attempt to modify will generate the error `FrictionlessOnChainAssetTokenUnableToUpdateData`
     * @param specData the specData data for the `FrictionlessOnChainAssetToken`
     */
    function setSpecificationData(FOCASpecData calldata specData) external;

    /**
     * @dev Sets the issuanceData data for the `FrictionlessOnChainAssetToken`
     * throws `FrictionlessOnChainAssetTokenUnableToUpdateData` This data is immutable, an attempt to modify will generate the error `FrictionlessOnChainAssetTokenUnableToUpdateData`
     * @param issuanceData the updatable data for the `FrictionlessOnChainAssetToken`
     */
    function setIssuanceData(FOCAIssuanceData calldata issuanceData) external;

    /**
     * @dev Sets the updatable data for the `FrictionlessOnChainAssetToken`
     * @param updateData the updatable data for the `FrictionlessOnChainAssetToken`
     */
    function setUpdateData(FOCAUpdateData calldata updateData) external;

    /**
     * @dev Get the specData data for the `FrictionlessOnChainAssetToken`.
     * @return the specData data for the `FrictionlessOnChainAssetToken`
     */
    function getSpecificationData() external view returns (FOCASpecData memory);

    /**
     * @dev Get the issuanceData data for the `FrictionlessOnChainAssetToken`.
     * @return the issuanceData data for the `FrictionlessOnChainAssetToken`
     */
    function getIssuanceData() external view returns (FOCAIssuanceData memory);

    /**
     * @dev Get the updateData data for the `FrictionlessOnChainAssetToken`.
     * @return the updateData data for the `FrictionlessOnChainAssetToken`
     */
    function getUpdateData() external view returns (FOCAUpdateData memory);

    /**
     * @dev Get the currency the `FrictionlessOnChainAssetToken` is issued in.
     * @return the currency the `FrictionlessOnChainAssetToken` is issued in.
     */
    function getCurrency() external view returns (string memory);
}
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);
}
IFrictionlessTokensFactory.sol 136 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 { IFrictionlessDigitalSecurityToken } from "@interface/IFrictionlessDigitalSecurityToken.sol";
import { IFrictionlessFundDepositToken } from "@interface/IFrictionlessFundDepositToken.sol";
import { IFrictionlessOnChainAssetToken } from "@interface/IFrictionlessOnChainAssetToken.sol";
import { IBasicFrictionlessToken } from "@interface/IBasicFrictionlessToken.sol";

/**
 * @title IFrictionlessComplianceFactory - Interface defining the token factory for all tokens in the Frictionless protocol.
 * @author Frictionless Group Holdings S.à.r.l
 * @notice Interface defining the token factory for all tokens in the Frictionless protocol.
 */
interface IFrictionlessTokensFactory {
    /**
     * @dev Struct to represent the base contract data to deploy a Frictionless token contract.
     * @param implementationAuthority the address of the implementationAuthority contract
     * @param identityRegistry the address of the OnChainId IdentityRegistry contract
     * @param compliance the address of the relevant compliance contract for the given Frictionless token.
     * @param onChainId the address of the onChainId of the PROTOCOL_ADMIN
     * @param tokenName the name of the token
     * @param tokenSymbol the ticker for the token
     */
    struct BaseTokenInitParams {
        address implementationAuthority;
        address identityRegistry;
        address compliance;
        address onChainId;
        string tokenName;
        string tokenSymbol;
    }

    /// @dev error thrown if an attempt to set a zero address contract during function `setTreasuryManager`
    error FrictionlessTokensFactoryZeroAddress();

    /// @dev error thrown if the `msg.sender` is not the treasury manager during the functions `deployFundDepositToken`, `deployDigitalSecurityToken`, or `deployOnChainAssetToken`
    error FrictionlessTokensFactoryNotATreasuryManager(address);

    /**
     * @dev Event emitted upon successful deployment of a compliance contract.
     * @param tokenType The Frictionless token type as defined by `IBasicFrictionlessToken.FrictionlessTokenTypes`
     * @param newTokenContract The token contract to deploy
     */
    event FrictionlessTokenDeployed(
        IBasicFrictionlessToken.FrictionlessTokenTypes indexed tokenType,
        address newTokenContract
    );

    /**
     * @dev Sets the Treasury Manager to be the specified address.
     * @param newTreasuryManager_ the addresses of the treasury manager to set
     * throws `FrictionlessTokensFactoryZeroAddress` if the newTreasuryManager_ is a zero address
     */
    function setTreasuryManager(address newTreasuryManager_) external;

    /**
     * @dev Deploys the `FrictionlessFundDepositToken` contract as a proxy
     * @param tokenOwner_ The owner of the deployed contract
     * @param baseTokenInitParams_ the base contract data to deploy a Frictionless token contract
     * @param initData_ the immutable data for the `FrictionlessFundDepositToken`
     * @return tokenProxyAddr_ the address of the deployed token contract `FrictionlessFundDepositToken`
     * throws `FrictionlessTokensFactoryNotATreasuryManager` if the msg.sender is not the treasury manager
     * emits `FrictionlessTokenDeployed` event upon successful deployment of the token contract.
     */
    function deployFundDepositToken(
        address tokenOwner_,
        BaseTokenInitParams calldata baseTokenInitParams_,
        IFrictionlessFundDepositToken.FFDImmutableData calldata initData_
    ) external returns (address tokenProxyAddr_);

    /**
     * @dev Deploys the `FrictionlessDigitalSecurityToken` contract as a proxy
     * @param tokenOwner_ The owner of the deployed contract
     * @param baseTokenInitParams_ the base contract data to deploy a Frictionless token contract
     * @param initData_ the immutable data for the `FrictionlessDigitalSecurityToken`
     * @param updateData_ the mutable data for the `FrictionlessDigitalSecurityToken`
     * @return tokenProxyAddr_ the address of the deployed token contract `FrictionlessDigitalSecurityToken`
     * throws `FrictionlessTokensFactoryNotATreasuryManager` if the msg.sender is not the treasury manager
     * emits `FrictionlessTokenDeployed` event upon successful deployment of the token contract.
     */
    function deployDigitalSecurityToken(
        address tokenOwner_,
        BaseTokenInitParams calldata baseTokenInitParams_,
        IFrictionlessDigitalSecurityToken.FDSImmutableData calldata initData_,
        IFrictionlessDigitalSecurityToken.FDSMutableData calldata updateData_
    ) external returns (address tokenProxyAddr_);

    /**
     * @dev Deploys the `FrictionlessOnChainAssetToken` contract as a proxy
     * @param tokenOwner_ The owner of the deployed contract
     * @param baseTokenInitParams_ the base contract data to deploy a Frictionless token contract
     * @param specData_ the immutable specification data for the `FrictionlessOnChainAssetToken`
     * @param issuanceData_ the immutable issuance data for the `FrictionlessOnChainAssetToken`
     * @param updateData_ the mutable update data for the `FrictionlessOnChainAssetToken`
     * @return tokenProxyAddr_ the address of the deployed token contract `FrictionlessOnChainAssetToken`
     * throws `FrictionlessTokensFactoryNotATreasuryManager` if the msg.sender is not the treasury manager
     * emits `FrictionlessTokenDeployed` event upon successful deployment of the token contract.
     */
    function deployOnChainAssetToken(
        address tokenOwner_,
        BaseTokenInitParams calldata baseTokenInitParams_,
        IFrictionlessOnChainAssetToken.FOCASpecData calldata specData_,
        IFrictionlessOnChainAssetToken.FOCAIssuanceData calldata issuanceData_,
        IFrictionlessOnChainAssetToken.FOCAUpdateData calldata updateData_
    ) external returns (address tokenProxyAddr_);

    /**
     * @dev returns the address of the treasuryManager
     * @return the address of the treasuryManager
     */
    function treasuryManager() external view returns (address);

    /**
     * @dev Checks whether a given token address is an existing frictionless token
     * @param tokenAddr_ the address of the token to check
     * @return A boolean indicating whether the token is an existing frictionless token
     */
    function existingFrictionlessTokens(address tokenAddr_) external view returns (bool);
}
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);
}
IFrictionlessTreasuryManager.sol 232 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 { IBasicFrictionlessToken } from "@interface/IBasicFrictionlessToken.sol";
import { IFrictionlessDigitalSecurityToken } from "@interface/IFrictionlessDigitalSecurityToken.sol";
import { IFrictionlessFundDepositToken } from "@interface/IFrictionlessFundDepositToken.sol";
import { IFrictionlessOnChainAssetToken } from "@interface/IFrictionlessOnChainAssetToken.sol";

/**
 * @title IFrictionlessTreasuryManager - Manages the minting, transfer and burning of all tokens in the Frictionless protocol
 * @author Frictionless Group Holdings S.à.r.l
 * @notice The IFrictionlessTreasuryManager is responsible for all token operations, minting, transferring and burning in
 * the Frictionless protocol. The tokens and their lifecycles are defined in the public README for the Frictionless protocol at
 * https://gitlab.com/dfyclabs/protocol/dfyclabs-tokens/-/tree/main?ref_type=heads#tokens-overview
 */
interface IFrictionlessTreasuryManager {
    /**
     * @dev Structure that encapsulates both the implAuthority and the compliance for the specific token.
     * @param implAuthority the contract address for the implementation authority associated with the specific Frictionless token type.
     * @param tokenType the Frictionless token type as specified by the enumeration `IBasicFrictionlessToken.FrictionlessTokenTypes`
     */
    struct FrictionlessTokenInitData {
        address implAuthority;
        IBasicFrictionlessToken.FrictionlessTokenTypes tokenType;
    }

    /// @dev throws if specific address is zero.
    error FrictionlessIsZeroAddress(string);

    /**
     * @dev Event emitted when a `FrictionlessFundDeposit`, `FrictionlessDigitalSecurity` or `FrictionlessOnChainAsset` is minted.
     * @param token the address of the token minted
     * @param tokenName the name of the token
     * @param tokenSymbol the token symbol
     * @param amount the amount of the token minted
     * @param toAddress the address the token was minted to
     */
    event FrictionlessTokenMinted(
        IBasicFrictionlessToken.FrictionlessTokenTypes tokenType,
        address token,
        string tokenName,
        string tokenSymbol,
        uint256 amount,
        address toAddress
    );

    /**
     * @dev Event emitted when a `FrictionlessFundDeposit`, `FrictionlessDigitalSecurity` or `FrictionlessOnChainAsset` is transferred.
     * @param token the address of the token transferred
     * @param amount the amount of the token transferred
     * @param fromAddress the address the token was transferred from
     * @param toAddress the address the token was transferred to
     */
    event FrictionlessTokenTransferred(
        IBasicFrictionlessToken.FrictionlessTokenTypes tokenType,
        address token,
        uint256 amount,
        address fromAddress,
        address toAddress
    );

    /**
     * @dev Event emitted when a `FrictionlessFundDeposit`, `FrictionlessDigitalSecurity` or `FrictionlessOnChainAsset` is burned.
     * @param token the address of the token burned
     * @param amount the amount of the token burned
     * @param fromAddress the address the token was burned from
     */
    event FrictionlessTokenBurned(
        IBasicFrictionlessToken.FrictionlessTokenTypes tokenType,
        address token,
        uint256 amount,
        address fromAddress
    );

    /// @dev error throw if the function caller is not a PROTOCOL_TREASURY address. Thrown during the `mintFundDepositForTreasury`
    error FrictionlessTreasuryManagerNotAProtocolTreasury(address);

    /// @dev error throw if the FundDepositToken for specified currency and fundIBAN already exists
    error FrictionlessTreasuryManagerFundDepositTokenAlreadyExists(string currency, string fundIBAN);

    /// @dev error throw if the data for the token init data `FrictionlessTokenInitData` is invalid. Thrown during the `_setTokensInitData`
    error FrictionlessTreasuryManagerInvalidTokenInitData(FrictionlessTokenInitData);

    /// @dev error throw if the data for the token init data `FrictionlessTokenInitData` is already set. Thrown during the `_setTokensInitData`
    error FrictionlessTreasuryManagerUnableToUpdateTokenInitData(IBasicFrictionlessToken.FrictionlessTokenTypes);

    /// @dev error throw if the data for the IFrictionlessFundDepositToken is invalid. Thrown during the `mintFundDepositForTreasury`
    error FrictionlessTreasuryManagerInvalidDepositData(IFrictionlessFundDepositToken.FFDImmutableData);

    /// @dev error throw if the data for the IFrictionlessDigitalSecurityToken is invalid. Thrown during the `mintDigitalSecurity`
    error FrictionlessTreasuryManagerInvalidFDSImmutableData(IFrictionlessDigitalSecurityToken.FDSImmutableData);

    /// @dev error throw if the data for the IFrictionlessDigitalSecurityToken is invalid. Thrown during the `mintOnChainAsset`
    error FrictionlessTreasuryManagerInvalidFOCASpecData(IFrictionlessOnChainAssetToken.FOCASpecData);

    /// @dev error throw if the data for the IFrictionlessDigitalSecurityToken is invalid. Thrown during the `mintOnChainAsset`
    error FrictionlessTreasuryManagerInvalidFOCAIssuanceData(IFrictionlessOnChainAssetToken.FOCAIssuanceData);

    /**
     * @dev See {PausableUpgradeable-_pause}
     */
    function pause() external;

    /**
     * @dev See {PausableUpgradeable-_unpause}
     */
    function unpause() external;

    /**
     * @dev Sets and associates the implementation authority with the associated token type
     * @param initDataArr_ the `FrictionlessTokenInitData` configuration associating the implementation authority with the associated token type.
     */
    function setTokensInitData(FrictionlessTokenInitData[] calldata initDataArr_) external;

    /**
     * @dev Mints a Fund Deposit Token in the specified currency/IBAN pair. This function is invoked to create the genesis mint of the
     * deposit token in the PROTOCOL_TREASURY.
     * @param depositData the immutable deposit data for the token
     * @param treasuryAddress the address of the treasury, which receives the deposit tokens
     * @param amount the amount of tokens
     * @return address of the token minted
     * emits `FrictionlessTokenMinted` event
     * throws error `FrictionlessTreasuryManagerInvalidDepositData` if the deposit data is invalid.
     * requires the depositData.currency to be a 3 letter currency code
     * requires the depositData.description to be not empty
     * requires the depositData.IBAN to be not empty
     */
    function mintFundDepositForTreasury(
        IFrictionlessFundDepositToken.FFDImmutableData calldata depositData,
        address treasuryAddress,
        uint256 amount
    ) external returns (address);

    /**
     * @dev Mints a FrictionlessDigitalSecurityToken as the future dated cash distribution from the underlying FrictionlessOnChainAssetToken.
     * This function is invoked to create the genesis mint of the deposit token in the PROTOCOL_TREASURY.
     * @param initData the immutable data for the token
     * @param updateData the mutable data for the token
     * @param amount the amount of tokens
     * @param userAddress the address of the protocol user, which receives the digital security tokens
     * @return address of the token minted
     * emits `FrictionlessTokenMinted` event
     * throws error `FrictionlessTreasuryManagerInvalidFDSImmutableData` if the initData is invalid.
     * requires the initData.currency to be a 3 letter currency code
     * requires the initData.onChainAssetAddress to be non 0 address
     */
    function mintDigitalSecurity(
        IFrictionlessDigitalSecurityToken.FDSImmutableData memory initData,
        IFrictionlessDigitalSecurityToken.FDSMutableData memory updateData,
        uint256 amount,
        address userAddress
    ) external returns (address);

    /**
     * @dev Mints a FrictionlessOnChainAssetToken as the representation of the asset to be securitized, fractionalized & sold.
     * This function is invoked to create the genesis mint of the deposit token to the PERMISSIONED_CUSTODIAN.
     * @param specData the immutable data for the token
     * @param issuanceData the issuance data for the token
     * @param updateData the update data for the token
     * @param custodianAddress the address of the protocol custodian, which receives the `FrictionlessOnChainAssetToken`
     * @return address of the token minted
     * emits `FrictionlessTokenMinted` event
     * throws error `FrictionlessTreasuryManagerInvalidFOCASpecData` or `FrictionlessTreasuryManagerInvalidFOCAIssuanceData` if the specData or issuanceData is invalid.
     */
    function mintOnChainAsset(
        IFrictionlessOnChainAssetToken.FOCASpecData memory specData,
        IFrictionlessOnChainAssetToken.FOCAIssuanceData memory issuanceData,
        IFrictionlessOnChainAssetToken.FOCAUpdateData memory updateData,
        address custodianAddress
    ) external returns (address);

    /**
     * @dev Used to increase the mint of a Frictionless token which already exists.
     * @param token the address of the token
     * @param userAddress the address to min the token to
     * @param amount the amount of tokens to mint
     * emits `FrictionlessTokenMinted` event
     */
    function mintTokenForUser(address token, address userAddress, uint256 amount) external;

    /**
     * @dev Used to increase the mint of a Frictionless token which already exists.
     * @param token the address of the token
     * @param userAddressFrom the address to transfer the tokens from
     * @param userAddressTo the address to transfer the tokens to
     * @param amount the amount of tokens to mint
     * emits `FrictionlessTokenTransferred` event
     */
    function transferToken(address token, address userAddressFrom, address userAddressTo, uint256 amount) external;

    /**
     * @dev Used to burn an amount of Frictionless token which already exists.
     * @param token the address of the token
     * @param userAddress the address to burn the tokens from
     * @param amount the amount of tokens to burn
     * emits `FrictionlessTokenBurned` event
     */
    function burnToken(address token, address userAddress, uint256 amount) external;

    /**
     * @dev returns the address of the fund deposit token by currency and fundIBAN
     * @param currency_ the currency of the fund deposit token
     * @param fundIBAN_ the fundIBAN of the fund deposit token
     * @return the address of the fund deposit token for specified currency and fundIBAN
     */
    function getFundDepositToken(string calldata currency_, string calldata fundIBAN_) external view returns (address);

    /**
     * @dev returns fund deposit token key by currency and fundIBAN
     * @param currency_ the currency of the fund deposit token you need
     * @param fundIBAN_ the fundIBAN of the fund deposit token you need
     * @return the fund deposit token key
     */
    function getFundDepositTokenKey(string memory currency_, string memory fundIBAN_) external pure returns (bytes32);
}
IFrictionlessAttestationManager.sol 214 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";

/**
 * @title IFrictionlessAttestationManager Contract responsible for providing attestations of the balances held in the underlying funding accounts
 * for each `FrictionlessFundDepositToken`. The attestation is provided and signed by an independent 3rd party, known as the
 * PERMISSIONED_FUND_ACCOUNTANT in the Frictionless protocol.
 * @author Frictionless Group Holdings S.à.r.l
 * @notice The IFrictionlessAttestationManager See the flow and description of the attestation workflow in the diagram at
 * https://gitlab.com/dfyclabs/protocol/dfyclabs-tokens/-/raw/main/docs/img/frictionless-fund-deposit-flow-attestation.png
 */
interface IFrictionlessAttestationManager {
    /**
     * @dev struct to hold attestation data, which is designed to provide the most recent attestation of balance and
     * verified last transaction in an attestation window.
     * @param tokenAddr the address of the `FrictionlessFundDepositToken`
     * @param iban the IBAN associated with the `FrictionlessFundDepositToken`
     * @param currency the currency associated with the `FrictionlessFundDepositToken`
     * @param reportStart the start timeframe in blocktime of the attestation window (reportEnd - reportStart)
     * @param reportEnd the end timeframe in blocktime of the attestation window (reportEnd - reportStart)
     * @param balance the balance as reported in the currency account
     * @param lastTxHash the hash of the last transaction in the attestation window (reportEnd - reportStart)
     */
    struct AttestationData {
        address tokenAddr;
        string iban;
        string currency;
        uint256 reportStart;
        uint256 reportEnd;
        uint256 balance;
        string lastTxHash;
    }

    /**
     * @dev struct to hold account attestation data
     * @param attestationTokenAddr the address of the `FrictionlessFundDepositToken`
     * @param lastAttestationDataKey the key for the last attestation data associated with the account
     * @param allAttestationDataKeys set containing all attestation data keys associated with the account
     */
    struct AccountAttestationData {
        address attestationTokenAddr;
        bytes32 lastAttestationDataKey;
        EnumerableSet.Bytes32Set allAttestationDataKeys;
    }

    /**
     * @dev Emitted when the `FrictionlessFundDepositToken` is updated in the `IFrictionlessAttestationManager`.
     * @param accountAttestationKey the attestation key associated with the account
     * @param newTokenAddress the updated token contract address that the `IFrictionlessAttestationManager` is aware of
     * @param oldTokenAddress the previous token contract address before the update
     */
    event FrictionlessFundDepositAttestationTokenUpdated(
        bytes32 accountAttestationKey,
        address newTokenAddress,
        address oldTokenAddress
    );

    /// @dev thrown if msg.sender address is not a PROTOCOL_TREASURY, during `updateAttestationToken`
    error FrictionlessAttestationManagerCallerNotAProtocolTreasury(address caller);

    /**
     * @dev Event emitted upon the confirmation of a `FrictionlessFundDepositToken` in the `IFrictionlessAttestationManager`
     * @param attestationData the complete set of attestation data signed by the PERMISSIONED_FUND_ACCOUNTANT.
     */
    event FrictionlessFundDepositAttestationConfirmed(AttestationData attestationData);

    /// @dev thrown if tokenAddress_ address is a zero address, during `updateAttestationToken`
    error FrictionlessAttestationManagerTokenZeroAddress();

    /// @dev thrown if permissionManagerAddr_ address is a zero address, during `init`
    error FrictionlessAttestationManagerPermissionsZeroAddress();

    /// @dev thrown if the `FrictionlessFundDepositToken` has not been registered for the IBAN, currency pair.
    error FrictionlessAttestationManagerInvalidToken(string iban, string currency);

    /// @dev thrown if the passed `AttestationData` report is invalid.
    error FrictionlessAttestationManagerInvalidReport(AttestationData attestationData);

    /// @dev thrown if the attestation balance is too small, below the configurable amount.
    error FrictionlessAttestationManagerBalanceTooLow(uint256 balance);

    /// @dev thrown if the last transaction hash for the attestation has already been used, this avoids duplicate attestations.
    error FrictionlessAttestationManagerDuplicateLastTxHash(string lastTxHash);

    /// @dev thrown if the attestation cannot be confirmed, meaning the signed attestation cannot be verified.
    error FrictionlessAttestationManagerInvalidAttestationSigner(address signer, bytes32 attestationHash, bytes sig);

    /**
     * @dev Update the token address of the `FrictionlessFundDepositToken` for
     * the `currency_` and `iban_` pair.
     * @param tokenAddress_ the address of deposit `FrictionlessFundDepositToken`
     * @param iban_ the IBAN associated with the `FrictionlessFundDepositToken`
     * @param currency_ the currency associated with the `FrictionlessFundDepositToken`
     * throws `FrictionlessAttestationManagerTokenZeroAddress` if tokenAddress_ address is a zero address
     * emits `FrictionlessFundDepositAttestationTokenUpdated` upon successful updating
     */
    function updateAttestationToken(address tokenAddress_, string calldata iban_, string calldata currency_) external;

    /**
     * @dev Confirm the attestation by signing the signature, this is the proof of the attestation by the PERMISSIONED_FUND_ACCOUNTANT
     * @param attestationData_ attestation data to sign
     * @param sig_ the signature to sign by the PERMISSIONED_FUND_ACCOUNTANT
     * throws `FrictionlessAttestationManagerNotEnoughPermissions` if the msg.sender does not have the role PERMISSIONED_FUND_ACCOUNTANT
     * throws `FrictionlessAttestationManagerInvalidAttestation` if the attestation cannot be confirmed, meaning the signed attestation cannot be verified.
     * emits `FrictionlessFundDepositAttestationConfirmed` upon successful signing
     */
    function confirmAttestation(AttestationData calldata attestationData_, bytes calldata sig_) external;

    /**
     * @dev Get the minimum balance amount configured, for which an attestation can be run.
     * If the balance of the attestation is below the value configured, the error will be thrown.
     * The minimum allowed is 10_000 or 0.01
     * @return minimum attestation balance.
     */
    function minBalance() external view returns (uint256);

    /**
     * @dev Get minimum time (reportEnd - reportStart) in blocktime for which an attestation can be run.
     * Typically this is 1 hour, and dependent on the availability of transaction data in the underlying fund account as specified by the IBAN.
     * The minimum allowed is 300 seconds.
     * @return minimum attestation window.
     */
    function minWindow() external view returns (uint256);

    /**
     * @dev Get maximum time (reportEnd - reportStart) in blocktime for which an attestation can be run.
     * Typically this is 30 days, aligned with the reporting as per regulation for the underlying fund and issuer of the deposit token.
     * The maximum allowed is 365 days.
     * @return maximum attestation window.
     */
    function maxWindow() external view returns (uint256);

    /**
     * @dev Retrieves the keys of attestation data associated with a specific account identified by IBAN and currency.
     * @param iban_ the IBAN associated with the `FrictionlessFundDepositToken`
     * @param currency_ the currency associated with the `FrictionlessFundDepositToken`
     * @return the array of bytes32 attestation data keys associated with the account.
     */
    function getAccountAttestationDataKeys(
        string calldata iban_,
        string calldata currency_
    ) external view returns (bytes32[] memory);

    /**
     * @dev Retrieves the last attestation data associated with a specific account identified by IBAN and currency.
     * @param iban_ the IBAN associated with the `FrictionlessFundDepositToken`
     * @param currency_ the currency associated with the `FrictionlessFundDepositToken`
     * @return the `AttestationData` struct representing the last attestation data associated with the account.
     */
    function getAccountLastAttestationData(
        string calldata iban_,
        string calldata currency_
    ) external view returns (AttestationData memory);

    /**
     * @dev Retrieves the key of the last attestation data associated with a specific account identified by IBAN and currency.
     * @param iban_ the IBAN associated with the `FrictionlessFundDepositToken`
     * @param currency_ the currency associated with the `FrictionlessFundDepositToken`
     * @return the last attestation data key associated with the account.
     */
    function getAccountLastAttestationDataKey(
        string calldata iban_,
        string calldata currency_
    ) external view returns (bytes32);

    /**
     * @dev Retrieves attestation data by the attestation data key.
     * @param attestationDataKey_ the key identifying the attestation data
     * @return the AttestationData struct representing the attestation data associated with the given key.
     */
    function getAttestationData(bytes32 attestationDataKey_) external view returns (AttestationData memory);

    /**
     * @dev Get the valid `FrictionlessFundDepositToken` for the `currency_` and `iban_` pair.
     * @param iban_ the IBAN associated with the FrictionlessFundDepositToken
     * @param currency_ the currency associated with the FrictionlessFundDepositToken
     * @return the address of the valid `FrictionlessFundDepositToken` for the `currency_` and `iban_` pair
     */
    function getTokenAddress(string memory iban_, string memory currency_) external view returns (address);

    /**
     * @dev Mint a new attestation, it returns hash of attestation data to sign by the PERMISSIONED_FUND_ACCOUNTANT
     * @param attestationData_ the complete set of attestation data to for the PERMISSIONED_FUND_ACCOUNTANT to certify/sign.
     * @return a hash of the attestationData which will be signed to confirm the attestation
     */
    function getAttestationDataHash(AttestationData calldata attestationData_) external view returns (bytes32);

    /**
     * @dev Retrieves the attestation data key associated with a specific account identified by currency and IBAN.
     * @param iban_ the IBAN associated with the `FrictionlessFundDepositToken`
     * @param currency_ the currency associated with the `FrictionlessFundDepositToken`
     * @return the accound attestation key.
     */
    function getAccountAttestationKey(string calldata currency_, string calldata iban_) external pure returns (bytes32);
}
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 {}
draft-IERC1822.sol 20 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}
ERC1967Upgrade.sol 185 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeacon.sol";
import "../../interfaces/draft-IERC1822.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 *
 * @custom:oz-upgrades-unsafe-allow delegatecall
 */
abstract contract ERC1967Upgrade {
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallUUPS(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        // Upgrades from old implementations will perform a rollback test. This test requires the new
        // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
        // this special case will break upgrade paths from old UUPS implementation to new ones.
        if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
            _setImplementation(newImplementation);
        } else {
            try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
            } catch {
                revert("ERC1967Upgrade: new implementation is not UUPS");
            }
            _upgradeToAndCall(newImplementation, data, forceCall);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Emitted when the beacon is upgraded.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            Address.isContract(IBeacon(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        }
    }
}
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;
    }
}
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);
}
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);
}
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);
        }
    }
}
StorageSlot.sol 88 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }
}
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);
        }
    }
}

Read Contract

getFundDepositToken 0x2b0fb787 → address
getFundDepositTokenKey 0xe45d3d28 → bytes32
isAgent 0x1ffbb064 → bool
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
proxiableUUID 0x52d1902d → bytes32

Write Contract 16 functions

These functions modify contract state and require a wallet transaction to execute.

addAgent 0x84e79842
address _agent
burnToken 0x3416794d
address tokenAddr_
address userAddress_
uint256 amount_
init 0x49012589
tuple[] initDataArr_
address identityRegistry_
address adminIdentity_
address permissionsManager_
address complianceFactory_
address tokensFactory_
mintDigitalSecurity 0xd77f51e2
tuple initData_
tuple updateData_
uint256 amount_
address userAddress_
returns: address
mintFundDepositForTreasury 0x105ec42a
tuple depositData_
address treasuryAddress_
uint256 amount_
returns: address
mintOnChainAsset 0x9faec7ae
tuple specData_
tuple issuanceData_
tuple updateData_
address custodianAddress_
returns: address
mintTokenForUser 0xd7db6cba
address tokenAddr_
address userAddress_
uint256 amount_
pause 0x8456cb59
No parameters
removeAgent 0x97a6278e
address _agent
renounceOwnership 0x715018a6
No parameters
setTokensInitData 0x23237dab
tuple[] initDataArr_
transferOwnership 0xf2fde38b
address newOwner
transferToken 0x2c54de4f
address tokenAddr_
address userAddressFrom_
address userAddressTo_
uint256 amount_
unpause 0x3f4ba83a
No parameters
upgradeTo 0x3659cfe6
address newImplementation
upgradeToAndCall 0x4f1ef286
address newImplementation
bytes data

Recent Transactions

No transactions found for this address