pragma solidity 0.8.0; pragma experimental ABIEncoderV2; import "../libraries/BasicTokenLibrary.sol"; import "../libraries/OperatorManagerLibrary.sol"; import "../libraries/SecurityTokenBalancesLibrary.sol"; import "../libraries/IterableBalances.sol"; import "../libraries/SettlementRepositoryLibrary.sol"; import "../libraries/SettlementWorkflowLibrary.sol"; import "../interfaces/IBasicToken.sol"; import "../interfaces/IInstrument.sol"; import "../interfaces/IOperatorManager.sol"; import "../interfaces/ISettlement.sol"; contract ForgeBond is IBasicToken, IOperatorManager, ISettlement, IInstrument { using BasicTokenLibrary for BasicTokenLibrary.BasicToken; using BasicTokenLibrary for BasicTokenLibrary.Bond; using SecurityTokenBalancesLibrary for SecurityTokenBalancesLibrary.SecurityTokenBalances; using OperatorManagerLibrary for OperatorManagerLibrary.OperatorManager; using SettlementRepositoryLibrary for SettlementRepositoryLibrary.SettlementTransactionRepository; BasicTokenLibrary.BasicToken private token; BasicTokenLibrary.Bond private bond; OperatorManagerLibrary.OperatorManager private operatorManager; SettlementRepositoryLibrary.SettlementTransactionRepository private settlementTransactionRepository; uint256 public constant REGISTRAR_ROLE = 0x01; uint256 public constant SETTLER_ROLE = 0x02; // bond state Type uint256 private constant STATE_CREATED = 0x01; uint256 private constant STATE_RUNNING = 0x02; uint256 private constant STATE_REDEEMED = 0x03; // operation Type uint256 private constant SUBSCRIPTION = 0x01; uint256 private constant REDEMPTION = 0x02; uint256 private constant TRADE = 0x03; // Settlement Status uint256 private constant CREATED = 0x01; uint256 private constant TOKEN_LOCKED = 0x02; uint256 private constant CASH_RECEIVED = 0x03; uint256 public constant CASH_TRANSFERRED = 0x04; uint256 public constant CANCELLED = 0x05; uint256 private constant ERROR = 0xFF; event Transfer(address indexed _from, address indexed _to, uint256 _value); // Only for erc20 explorer constructor(BasicTokenLibrary.BasicTokenInput memory basicTokenInput) public { token.owner = basicTokenInput.owner; token.initialSupply = basicTokenInput.initialSupply; token.currentSupply = basicTokenInput.initialSupply; token.isinCode = basicTokenInput.isinCode; token.name = basicTokenInput.name; token.symbol = basicTokenInput.symbol; token.settler = basicTokenInput.settler; token.registrar = basicTokenInput.registrar; token.securityTokenBalances.setIssuer(token.owner); token.securityTokenBalances.mint(token.owner, token.initialSupply); bond.denomination = basicTokenInput.denomination; bond.divisor = basicTokenInput.divisor; bond.startDate = basicTokenInput.startDate; bond.maturityDate = basicTokenInput.initialMaturityDate; bond.currentMaturityDate = bond.maturityDate; bond.firstCouponDate = basicTokenInput.firstCouponDate; bond.couponFrequencyInMonths = basicTokenInput.couponFrequencyInMonths; bond.interestRateInBips = basicTokenInput.interestRateInBips; bond.callable = basicTokenInput.callable; bond.isSoftBullet = basicTokenInput.isSoftBullet; bond.state = STATE_CREATED; bond.currency = basicTokenInput.currency; bond.softBulletPeriodInMonths = basicTokenInput .softBulletPeriodInMonths; operatorManager.authorizeOperator(REGISTRAR_ROLE, token.registrar); operatorManager.authorizeOperator(SETTLER_ROLE, token.settler); } // Basic Token function owner() public view returns (address) { return token.owner; } function settler() public view returns (address) { return token.settler; } function registrar() public view returns (address) { return token.registrar; } function initialSupply() public view returns (uint256) { return token.initialSupply; } function currentSupply() public view returns (uint256) { return token.currentSupply; } function name() public view returns (string memory) { return token.name; } function symbol() public view returns (string memory) { return token.symbol; } function isinCode() public view returns (string memory) { return token.isinCode; } // Bond function denomination() public view returns (uint256) { return bond.denomination; } function divisor() public view returns (uint256) { return bond.divisor; } function startDate() public view returns (uint256) { return bond.startDate; } function maturityDate() public view returns (uint256) { return bond.maturityDate; } function currentMaturityDate() public view returns (uint256) { return bond.currentMaturityDate; } function firstCouponDate() public view returns (uint256) { return bond.firstCouponDate; } function couponFrequencyInMonths() public view returns (uint256) { return bond.couponFrequencyInMonths; } function interestRateInBips() public view returns (uint256) { return bond.interestRateInBips; } function callable() public view returns (bool) { return bond.callable; } function isSoftBullet() public view returns (bool) { return bond.isSoftBullet; } function softBulletPeriodInMonths() public view returns (uint256) { return bond.softBulletPeriodInMonths; } function currency() public view returns (string memory) { return bond.currency; } function state() public view returns (uint256) { return bond.state; } function getType() public view override returns (string memory) { return "Bond"; } // Modifiers modifier issuerOnly() { require( msg.sender == token.owner, "Only issuer can perform this action" ); _; } modifier registrarOnly() { require( operatorManager.isOperatorWithRoleAuthorized( msg.sender, REGISTRAR_ROLE ), "Only a registrar can perform this action" ); _; } modifier settlerOnly() { require( operatorManager.isOperatorWithRoleAuthorized( msg.sender, SETTLER_ROLE ), "Only a settler can perform this action" ); _; } // [ERC-20] Only function balanceOf(address _owner) public view returns (uint256 balance) { return token.securityTokenBalances.getBalance(_owner); } function transfer(address _to, uint256 _value) public pure returns (bool success) { return false; } function decimals() public pure returns (uint8) { return 0; } function totalSupply() public view returns (uint256) { return token.securityTokenBalances.totalSupply(); } function burn(uint256 quantity) public registrarOnly { token.securityTokenBalances.burn(token.owner, quantity); } // IBasicToken function getFullBalances() public view returns (SecurityTokenBalancesLibrary.Balance[] memory value) { return token.securityTokenBalances.getFullBalances(); } function getBalance(address _address) public view returns (uint256 value) { return token.securityTokenBalances.getBalance(_address); } // IOperatorManager function authorizeOperator(uint256 _roleName, address _operatorAddress) public override issuerOnly { operatorManager.authorizeOperator(_roleName, _operatorAddress); } function isOperatorWithRoleAuthorized( address _operatorAddress, uint256 _roleName ) public view override returns (bool) { return operatorManager.isOperatorWithRoleAuthorized( _operatorAddress, _roleName ); } function revokeOperatorAuthorization( address _operatorAddress, uint256 _roleName ) public override issuerOnly { operatorManager.revokeOperatorAuthorization( _operatorAddress, _roleName ); } // ISettlement function initiateSubscription( SettlementRepositoryLibrary.PartialSettlementTransaction memory partialSettlementTransaction ) public override registrarOnly { require( bond.state < STATE_REDEEMED, "Instrument already fully redeemed" ); SettlementWorkflowLibrary.initiateSubscription( settlementTransactionRepository, token, partialSettlementTransaction ); bond.state = STATE_RUNNING; emit SubscriptionInitiated(partialSettlementTransaction.txId); } function initiateTrade( SettlementRepositoryLibrary.PartialSettlementTransaction memory partialSettlementTransaction ) public override registrarOnly { require( bond.state < STATE_REDEEMED, "Instrument already fully redeemed" ); SettlementWorkflowLibrary.initiateTrade( settlementTransactionRepository, token, partialSettlementTransaction ); bond.state = STATE_RUNNING; emit TradeInitiated(partialSettlementTransaction.txId); } function confirmPaymentReceived(uint256 _settlementTransactionId) external override settlerOnly { uint256 settlementTransactionOperationType = settlementTransactionRepository .getOperationTypeForSettlementTransaction( _settlementTransactionId ); if (settlementTransactionOperationType == SUBSCRIPTION) { handleConfirmPaymentReceived(_settlementTransactionId); } else if (settlementTransactionOperationType == REDEMPTION) { handleConfirmPaymentReceived(_settlementTransactionId); } else if (settlementTransactionOperationType == TRADE) { handleConfirmPaymentReceived(_settlementTransactionId); } else { revert("If you see this, this is really bad"); } emit PaymentReceived( _settlementTransactionId, settlementTransactionOperationType ); } function handleConfirmPaymentReceived(uint256 settlementTransactionId) internal { SettlementRepositoryLibrary.SettlementTransaction memory st = settlementTransactionRepository .getSettlementTransactionById(settlementTransactionId); require( st.status == TOKEN_LOCKED, "The settlement transaction is not in TOKEN_LOCKED state" ); token.securityTokenBalances.transferLocked( st.deliverySenderAccountNumber, st.deliveryReceiverAccountNumber, st.deliveryQuantity ); if ( settlementTransactionRepository.getOperationType(st.operationId) == REDEMPTION ) { token.securityTokenBalances.burn( st.deliveryReceiverAccountNumber, st.deliveryQuantity ); } settlementTransactionRepository.setSettlementTransactionStatus( settlementTransactionId, CASH_RECEIVED ); } function confirmPaymentTransferred(uint256 _settlementTransactionId) external override settlerOnly { SettlementRepositoryLibrary.SettlementTransaction memory st = settlementTransactionRepository .getSettlementTransactionById(_settlementTransactionId); require( st.status == CASH_RECEIVED, "The settlement transaction is not in CASH_RECEIVED state" ); uint256 settlementTransactionOperationType = settlementTransactionRepository .getOperationTypeForSettlementTransaction( _settlementTransactionId ); emit PaymentTransferred( _settlementTransactionId, settlementTransactionOperationType ); settlementTransactionRepository.setSettlementTransactionStatus( _settlementTransactionId, CASH_TRANSFERRED ); } function getCurrentState(uint256 _settlementTransactionId) external view returns (uint256) { return settlementTransactionRepository .getSettlementTransactionById(_settlementTransactionId) .status; } function getOperationType(uint256 _operationId) external view returns (uint256) { return settlementTransactionRepository.getOperationType(_operationId); } function initiateRedemption( SettlementRepositoryLibrary.PartialSettlementTransaction[] memory partialSettlementTransactions ) public override registrarOnly { require( bond.state < STATE_REDEEMED, "Instrument already fully redeemed" ); uint256[] memory ids = new uint256[]( partialSettlementTransactions.length ); SettlementWorkflowLibrary.initiateRedemption( settlementTransactionRepository, token, partialSettlementTransactions ); for (uint256 i = 0; i < partialSettlementTransactions.length; i++) { ids[i] = partialSettlementTransactions[i].txId; } bond.state = STATE_REDEEMED; emit RedemptionInitiated(ids); } function cancelSettlementTransaction( SettlementRepositoryLibrary.PartialSettlementTransaction memory partialSettlementTransaction ) public override registrarOnly { SettlementWorkflowLibrary.cancelSettlementTransaction( settlementTransactionRepository, token, partialSettlementTransaction ); emit SettlementTransactionCanceled(partialSettlementTransaction.txId); } }