DCIPs/assets/eip-3475/ERC3475.sol

434 lines
14 KiB
Solidity

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
import "./interfaces/IERC3475.sol";
contract ERC3475 is IERC3475 {
/**
* @notice this Struct is representing the Nonce properties as an object
*
*/
struct Nonce {
// stores the values corresponding to the dates (issuance and maturity date).
mapping(uint256 => IERC3475.Values) _values;
// storing the issuance of the issued bonds with their balances.
mapping(address => uint256) _balances;
// defines the mapping for amount of bonds that can be delegated by Owner => operator address.
mapping(address => mapping(address => uint256)) _allowances;
// Overall supplies of this nonce
uint256 _activeSupply;
uint256 _burnedSupply;
uint256 _redeemedSupply;
}
/**
* @notice this Struct is representing the Class properties as an object
* and can be retrieved by the classId
*/
struct Class {
mapping(uint256 => IERC3475.Values) _values;
mapping(uint256 => IERC3475.Metadata) _nonceMetadata;
mapping(uint256 => Nonce) nonces;
}
mapping(address => mapping(address => bool)) operatorApprovals;
// from classId given
mapping(uint256 => Class) internal _classes;
mapping(uint256 => IERC3475.Metadata) _classMetadata;
/**
* @notice Here the constructor is just to initialize a class and nonce,
* in practice, you will have a function to create a new class and nonce
* to be deployed during the initial deployment cycle
*/
constructor() {
// define "symbol of the given class";
_classMetadata[0].title = "symbol";
_classMetadata[0]._type = "string";
_classMetadata[0].description = "symbol of the class";
// define "period of the class";
_classMetadata[5].title = "period";
_classMetadata[5]._type = "int";
_classMetadata[5].description = "value (in months) about maturity time";
// describing the symbol of the different class
_classes[0]._values[0].stringValue = "DBIT Fix 6M";
_classes[1]._values[0].stringValue = "DBIT Fix test Instantaneous";
// define the maturity time period (for the test class).
_classes[0]._values[5].uintValue = 10;
_classes[1]._values[5].uintValue = 1;
// write the time of maturity to nonce values, in other implementation, a create nonce function can be added
_classes[0].nonces[0]._values[0].uintValue = block.timestamp + 180 days;
_classes[0].nonces[1]._values[0].uintValue = block.timestamp + 181 days;
_classes[0].nonces[2]._values[0].uintValue = block.timestamp + 182 days;
// test for review the instantaneous class
_classes[1].nonces[0]._values[0].uintValue = block.timestamp + 1;
_classes[1].nonces[1]._values[0].uintValue = block.timestamp + 2;
_classes[1].nonces[2]._values[0].uintValue = block.timestamp + 3;
// define metadata explaining "maturity of the nonce";
_classes[0]._nonceMetadata[0].title = "maturity";
_classes[0]._nonceMetadata[0]._type = "int";
_classes[0]._nonceMetadata[0].description = "maturity date in integer";
_classes[1]._nonceMetadata[0].title = "maturity";
_classes[1]._nonceMetadata[0]._type = "int";
_classes[1]._nonceMetadata[0].description = "maturity date in integer";
// initializing all of the nonces for issued bonds
_classes[0].nonces[0]._values[0].boolValue = true;
_classes[0].nonces[1]._values[0].boolValue = true;
_classes[0].nonces[2]._values[0].boolValue = true;
}
// Writable functions.
function transferFrom(
address _from,
address _to,
Transaction[] calldata _transactions
) public virtual override {
require(
_from != address(0),
"ERC3475: can't transfer from the zero address"
);
require(
_to != address(0),
"ERC3475:use burn() instead"
);
require(
msg.sender == _from ||
isApprovedFor(_from, msg.sender),
"ERC3475:caller-not-owner-or-approved"
);
uint256 len = _transactions.length;
for (uint256 i = 0; i < len; i++) {
_transferFrom(_from, _to, _transactions[i]);
}
emit Transfer(msg.sender, _from, _to, _transactions);
}
function transferAllowanceFrom(
address _from,
address _to,
Transaction[] calldata _transactions
) public virtual override {
require(
_from != address(0),
"ERC3475: can't transfer allowed amt from zero address"
);
require(
_to != address(0),
"ERC3475: use burn() instead"
);
uint256 len = _transactions.length;
for (uint256 i = 0; i < len; i++) {
require(
_transactions[i]._amount <= allowance(_from, msg.sender, _transactions[i].classId, _transactions[i].nonceId),
"ERC3475:caller-not-owner-or-approved"
);
_transferAllowanceFrom(msg.sender, _from, _to, _transactions[i]);
}
emit Transfer(msg.sender, _from, _to, _transactions);
}
function issue(address _to, Transaction[] calldata _transactions)
external
virtual
override
{
uint256 len = _transactions.length;
for (uint256 i = 0; i < len; i++) {
require(
_to != address(0),
"ERC3475: can't issue to the zero address"
);
_issue(_to, _transactions[i]);
}
emit Issue(msg.sender, _to, _transactions);
}
function redeem(address _from, Transaction[] calldata _transactions)
external
virtual
override
{
require(
_from != address(0),
"ERC3475: can't redeem from the zero address"
);
uint256 len = _transactions.length;
for (uint256 i = 0; i < len; i++) {
(, uint256 progressRemaining) = getProgress(
_transactions[i].classId,
_transactions[i].nonceId
);
require(
progressRemaining == 0,
"ERC3475 Error: Not redeemable"
);
_redeem(_from, _transactions[i]);
}
emit Redeem(msg.sender, _from, _transactions);
}
function burn(address _from, Transaction[] calldata _transactions)
external
virtual
override
{
require(
_from != address(0),
"ERC3475: can't burn from the zero address"
);
require(
msg.sender == _from ||
isApprovedFor(_from, msg.sender),
"ERC3475: caller-not-owner-or-approved"
);
uint256 len = _transactions.length;
for (uint256 i = 0; i < len; i++) {
_burn(_from, _transactions[i]);
}
emit Burn(msg.sender, _from, _transactions);
}
function approve(address _spender, Transaction[] calldata _transactions)
external
virtual
override
{
for (uint256 i = 0; i < _transactions.length; i++) {
_classes[_transactions[i].classId]
.nonces[_transactions[i].nonceId]
._allowances[msg.sender][_spender] = _transactions[i]._amount;
}
}
function setApprovalFor(
address operator,
bool approved
) public virtual override {
operatorApprovals[msg.sender][operator] = approved;
emit ApprovalFor(msg.sender, operator, approved);
}
// READABLES
function totalSupply(uint256 classId, uint256 nonceId)
public
view
override
returns (uint256)
{
return (activeSupply(classId, nonceId) +
burnedSupply(classId, nonceId) +
redeemedSupply(classId, nonceId)
);
}
function activeSupply(uint256 classId, uint256 nonceId)
public
view
override
returns (uint256)
{
return _classes[classId].nonces[nonceId]._activeSupply;
}
function burnedSupply(uint256 classId, uint256 nonceId)
public
view
override
returns (uint256)
{
return _classes[classId].nonces[nonceId]._burnedSupply;
}
function redeemedSupply(uint256 classId, uint256 nonceId)
public
view
override
returns (uint256)
{
return _classes[classId].nonces[nonceId]._redeemedSupply;
}
function balanceOf(
address account,
uint256 classId,
uint256 nonceId
) public view override returns (uint256) {
require(
account != address(0),
"ERC3475: balance query for the zero address"
);
return _classes[classId].nonces[nonceId]._balances[account];
}
function classMetadata(uint256 metadataId)
external
view
override
returns (Metadata memory) {
return (_classMetadata[metadataId]);
}
function nonceMetadata(uint256 classId, uint256 metadataId)
external
view
override
returns (Metadata memory) {
return (_classes[classId]._nonceMetadata[metadataId]);
}
function classValues(uint256 classId, uint256 metadataId)
external
view
override
returns (Values memory) {
return (_classes[classId]._values[metadataId]);
}
function nonceValues(uint256 classId, uint256 nonceId, uint256 metadataId)
external
view
override
returns (Values memory) {
return (_classes[classId].nonces[nonceId]._values[metadataId]);
}
/** determines the progress till the redemption of the bonds is valid (based on the type of bonds class).
* @notice ProgressAchieved and `progressRemaining` is abstract.
For e.g. we are giving time passed and time remaining.
*/
function getProgress(uint256 classId, uint256 nonceId)
public
view
override
returns (uint256 progressAchieved, uint256 progressRemaining){
uint256 issuanceDate = _classes[classId].nonces[nonceId]._values[0].uintValue;
uint256 maturityDate = issuanceDate + _classes[classId].nonces[nonceId]._values[5].uintValue;
// check whether the bond is being already initialized:
progressAchieved = block.timestamp - issuanceDate;
progressRemaining = block.timestamp < maturityDate
? maturityDate - block.timestamp
: 0;
}
/**
gets the allowance of the bonds identified by (classId,nonceId) held by _owner to be spend by spender.
*/
function allowance(
address _owner,
address spender,
uint256 classId,
uint256 nonceId
) public view virtual override returns (uint256) {
return _classes[classId].nonces[nonceId]._allowances[_owner][spender];
}
/**
checks the status of approval to transfer the ownership of bonds by _owner to operator.
*/
function isApprovedFor(
address _owner,
address operator
) public view virtual override returns (bool) {
return operatorApprovals[_owner][operator];
}
// INTERNALS
function _transferFrom(
address _from,
address _to,
IERC3475.Transaction calldata _transaction
) private {
Nonce storage nonce = _classes[_transaction.classId].nonces[_transaction.nonceId];
require(
nonce._balances[_from] >= _transaction._amount,
"ERC3475: not enough bond to transfer"
);
//transfer balance
nonce._balances[_from] -= _transaction._amount;
nonce._balances[_to] += _transaction._amount;
}
function _transferAllowanceFrom(
address _operator,
address _from,
address _to,
IERC3475.Transaction calldata _transaction
) private {
Nonce storage nonce = _classes[_transaction.classId].nonces[_transaction.nonceId];
require(
nonce._balances[_from] >= _transaction._amount,
"ERC3475: not allowed amount"
);
// reducing the allowance and decreasing accordingly.
nonce._allowances[_from][_operator] -= _transaction._amount;
//transfer balance
nonce._balances[_from] -= _transaction._amount;
nonce._balances[_to] += _transaction._amount;
}
function _issue(
address _to,
IERC3475.Transaction calldata _transaction
) private {
Nonce storage nonce = _classes[_transaction.classId].nonces[_transaction.nonceId];
//transfer balance
nonce._balances[_to] += _transaction._amount;
nonce._activeSupply += _transaction._amount;
}
function _redeem(
address _from,
IERC3475.Transaction calldata _transaction
) private {
Nonce storage nonce = _classes[_transaction.classId].nonces[_transaction.nonceId];
// verify whether _amount of bonds to be redeemed are sufficient available for the given nonce of the bonds
require(
nonce._balances[_from] >= _transaction._amount,
"ERC3475: not enough bond to transfer"
);
//transfer balance
nonce._balances[_from] -= _transaction._amount;
nonce._activeSupply -= _transaction._amount;
nonce._redeemedSupply += _transaction._amount;
}
function _burn(
address _from,
IERC3475.Transaction calldata _transaction
) private {
Nonce storage nonce = _classes[_transaction.classId].nonces[_transaction.nonceId];
// verify whether _amount of bonds to be burned are sfficient available for the given nonce of the bonds
require(
nonce._balances[_from] >= _transaction._amount,
"ERC3475: not enough bond to transfer"
);
//transfer balance
nonce._balances[_from] -= _transaction._amount;
nonce._activeSupply -= _transaction._amount;
nonce._burnedSupply += _transaction._amount;
}
}