This contract implements the AIBIM Corporate Murabaha Master Agreement
Key Components of the Murabaha Agreement:
Purchase Transactions: The bank acts as an agent to purchase goods on behalf of the principal.
Sale Transactions: The bank sells the goods to the principal at a marked-up price with deferred payment.
Special Clauses: Early repayment, late repayment, termination, and force majeure provisions.
Multi-Signature Framework: Allows both parties to perform certain actions, including liquidation.
Liquidation: Allows for the liquidation of commodities, with controllable permissions.
Events
// Events to log significant actions within the contracteventPurchaseCommodity(addressindexed bank, uint256 stableAmount);eventDeclineOffer(addressindexed principle, uint256 commodityAmount);eventAcceptOffer(addressindexed principle, uint256 commodityAmount);eventSaleOfferSet(uint256 salePrice, uint256[] installmentAmounts, uint256[] installmentDueDates);eventInstallmentCollected(addressindexed principle, uint256 amountDue);eventEarlyRepaymentRebateSet(uint256 rebateAmount);eventEarlyRepaid(addressindexed principle, uint256 amountPaid);eventDefaultDeclared(addressindexed bank, uint256 amountDue);
Purchase Commodity
Initiates a Purchase Transaction (Clause 3 and Appendix 3)
The principal requests the bank to purchase goods on their behalf
/** * @dev Allows the bank to purchase the commodity by swapping stablecoins via the Broker. * @param _stableAmount The amount of stablecoins the bank wants to use for purchasing the commodity. */functionpurchaseCommodity(uint256_stableAmount) externalonlyRole(BANK_ROLE) {require(_stableAmount >0,"Stable amount must be greater than zero");// Bank uses the Broker to swap stablecoins for the commodity broker.swapStableToCommodity(bank, bank, _stableAmount);// Emit an event to record the purchase actionemitPurchaseCommodity(bank, _stableAmount); }
Set the terms of the offer
Initiates a Sale Transaction (Clause 5 and Appendix 3)
The bank offers to sell the goods to the principal at a marked-up price
/** * @dev Sets the terms of the sale offer, including price and installment schedule. * @param _salePrice Total sale price of the commodity in stablecoin. * @param _installmentAmounts Array of installment payment amounts; must sum up to the sale price. * @param _installmentDueDates Array of due dates corresponding to each installment. */functionsetSaleOffer(uint256_salePrice,uint256[] calldata_installmentAmounts,uint256[] calldata_installmentDueDates ) externalonlyRole(BANK_ROLE) {require(!offerAccepted,"Offer already accepted");require( _installmentAmounts.length == _installmentDueDates.length,"Installments and due dates length mismatch" );// Ensure the sum of installment amounts equals the total sale priceuint256 totalInstallments =0;for (uint256 i =0; i < _installmentAmounts.length; i++) { totalInstallments += _installmentAmounts[i]; }require(totalInstallments == _salePrice,"Installment amounts do not sum to sale price");// Set the sale terms in the contract salePrice = _salePrice; installmentAmounts = _installmentAmounts; installmentDueDates = _installmentDueDates;// Emit an event to record the sale offer detailsemitSaleOfferSet(_salePrice, _installmentAmounts, _installmentDueDates); }
Accept Offer
Completes a Sale Transaction (implied by Clause 5.4)
The bank transfers the sale price to the principal on the deferred payment date
/** * @dev Allows the PRINCIPLE to accept the sale offer and receive the commodity. */functionacceptOffer() externalonlyRole(PRINCIPLE_ROLE) {require(!offerAccepted,"Offer already accepted"); offerAccepted =true;// Transfer the commodity from the bank to the PRINCIPLErequire(commodity.transferFrom(bank, principle, amount),"Commodity transfer failed");// Emit an event to record the acceptance of the offeremitAcceptOffer(principle, amount); }
Decline Offer
/** * @dev Allows the PRINCIPLE to decline the sale offer and revert the commodity back to stablecoins. */functiondeclineOffer() externalonlyRole(PRINCIPLE_ROLE) {require(!offerAccepted,"Offer already accepted");// Use the Broker to swap the commodity back to stablecoins broker.swapCommodityToStable(bank, bank, amount);// Emit an event to record the decline of the offeremitDeclineOffer(principle, amount); }
Collect Installments
/** * @dev Allows the bank or authorized entity to collect the next installment payment from the PRINCIPLE. */functioncollectInstallment() external {require(offerAccepted,"Offer not accepted");require(!defaultDeclared,"Default declared, cannot collect installments");require(nextInstallmentIndex < installmentDueDates.length,"All installments collected");require( block.timestamp >= installmentDueDates[nextInstallmentIndex],"Installment not due yet" );// Determine the amount due for the next installmentuint256 amountDue = installmentAmounts[nextInstallmentIndex];// Transfer the installment payment from the PRINCIPLE to the bankrequire(stablecoin.transferFrom(principle, bank, amountDue),"Payment transfer failed");// Emit an event to record the installment collectionemitInstallmentCollected(principle, amountDue);// Increment the installment index to move to the next installment nextInstallmentIndex++; }
Early Repayment
Early Settlement request (Appendix 8, Clause 1)
The principal can request early settlement before the deferred payment date
Approve Early Settlement (Appendix 8, Clause 1)
The bank can approve the early settlement request within 7 days
/** * @dev Allows the PRINCIPLE to repay the remaining amount early and receive the specified rebate. */functionrepayEarly() externalonlyRole(PRINCIPLE_ROLE) {require(offerAccepted,"Offer not accepted");require(!defaultDeclared,"Default declared, cannot repay early");// Calculate the total amount already paid by the PRINCIPLEuint256 amountPaidSoFar =0;for (uint256 i =0; i < nextInstallmentIndex; i++) { amountPaidSoFar += installmentAmounts[i]; }// Determine the remaining amount oweduint256 remainingAmount = salePrice - amountPaidSoFar;require(remainingAmount >= earlyRepaymentRebate,"Rebate exceeds remaining amount");// Calculate the early repayment amount after applying the rebateuint256 earlyRepaymentAmount = remainingAmount - earlyRepaymentRebate;// Transfer the early repayment amount from the PRINCIPLE to the bankrequire(stablecoin.transferFrom(principle, bank, earlyRepaymentAmount),"Payment transfer failed");// Update the installment index to prevent further installment collections nextInstallmentIndex = installmentAmounts.length;// Emit an event to record the early repaymentemitEarlyRepaid(principle, earlyRepaymentAmount); }
/** * @dev Allows the bank to set a rebate amount for early repayment by the PRINCIPLE. * @param _rebate The rebate amount to be offered to the PRINCIPLE for early repayment. */functionsetEarlyRepaymentRebate(uint256_rebate) externalonlyRole(BANK_ROLE) {require(_rebate <= salePrice,"Rebate cannot exceed sale price");// Update the rebate amount for early repayment earlyRepaymentRebate = _rebate;// Emit an event to record the rebate settingemitEarlyRepaymentRebateSet(_rebate); }
Declare Force Majeure Event (Clause 9.5)
Either party can declare a force majeure event - liquidation
/** * @dev Allows the bank to declare a default, making the full remaining amount immediately payable. */functiondeclareDefault() externalonlyRole(BANK_ROLE) {require(offerAccepted,"Offer not accepted"); defaultDeclared =true;// Calculate the total amount already paid by the PRINCIPLEuint256 amountPaidSoFar =0;for (uint256 i =0; i < nextInstallmentIndex; i++) { amountPaidSoFar += installmentAmounts[i]; }// Determine the amount still due after defaultuint256 amountDue = salePrice - amountPaidSoFar;// Emit an event to record the default declarationemitDefaultDeclared(bank, amountDue); }
TODO
Calculate Overdue Compensation
Calculate Compensation for late payment (Appendix 8, Clause 2)
Calculates the compensation amount based on the number of overdue days
Pay with Overdue Compensation
Pay with Compensation for late payment (Appendix 8, Clause 2)
The bank pays the sale price plus compensation for late payment
Terminate Agreement (Clause 7)
Either party can terminate the agreement under certain conditions