Murabahah sale offer prepared for customer: [Commodity Type] with [Payment Terms]
Murabaha offer accepted by Customer
Customer accepted the Murabahah offer for [Commodity Type] with [Payment Terms]
Murabaha offer declined by Customer
Customer declined the Murabahah offer for [Commodity Type]
Installment payment received
Installment payment of [Amount] received for Murabahah contract [Contract ID]
Early repayment rebate calculated
Early repayment rebate of [Rebate Amount] calculated for Murabahah contract [Contract ID]
Murabaha contract repaid early
Murabahah contract [Contract ID] settled early with a payment of [Settlement Amount]
Customer default declared
Customer default declared on Murabahah contract [Contract ID]
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.
*/
function purchaseCommodity(uint256 _stableAmount) external onlyRole(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 action
emit PurchaseCommodity(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.
*/
function setSaleOffer(
uint256 _salePrice,
uint256[] calldata _installmentAmounts,
uint256[] calldata _installmentDueDates
) external onlyRole(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 price
uint256 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 details
emit SaleOfferSet(_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.
*/
function acceptOffer() external onlyRole(PRINCIPLE_ROLE) {
require(!offerAccepted, "Offer already accepted");
offerAccepted = true;
// Transfer the commodity from the bank to the PRINCIPLE
require(commodity.transferFrom(bank, principle, amount), "Commodity transfer failed");
// Emit an event to record the acceptance of the offer
emit AcceptOffer(principle, amount);
}
Decline Offer
/**
* @dev Allows the PRINCIPLE to decline the sale offer and revert the commodity back to stablecoins.
*/
function declineOffer() external onlyRole(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 offer
emit DeclineOffer(principle, amount);
}
Collect Installments
/**
* @dev Allows the bank or authorized entity to collect the next installment payment from the PRINCIPLE.
*/
function collectInstallment() 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 installment
uint256 amountDue = installmentAmounts[nextInstallmentIndex];
// Transfer the installment payment from the PRINCIPLE to the bank
require(stablecoin.transferFrom(principle, bank, amountDue), "Payment transfer failed");
// Emit an event to record the installment collection
emit InstallmentCollected(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.
*/
function repayEarly() external onlyRole(PRINCIPLE_ROLE) {
require(offerAccepted, "Offer not accepted");
require(!defaultDeclared, "Default declared, cannot repay early");
// Calculate the total amount already paid by the PRINCIPLE
uint256 amountPaidSoFar = 0;
for (uint256 i = 0; i < nextInstallmentIndex; i++) {
amountPaidSoFar += installmentAmounts[i];
}
// Determine the remaining amount owed
uint256 remainingAmount = salePrice - amountPaidSoFar;
require(remainingAmount >= earlyRepaymentRebate, "Rebate exceeds remaining amount");
// Calculate the early repayment amount after applying the rebate
uint256 earlyRepaymentAmount = remainingAmount - earlyRepaymentRebate;
// Transfer the early repayment amount from the PRINCIPLE to the bank
require(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 repayment
emit EarlyRepaid(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.
*/
function setEarlyRepaymentRebate(uint256 _rebate) external onlyRole(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 setting
emit EarlyRepaymentRebateSet(_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.
*/
function declareDefault() external onlyRole(BANK_ROLE) {
require(offerAccepted, "Offer not accepted");
defaultDeclared = true;
// Calculate the total amount already paid by the PRINCIPLE
uint256 amountPaidSoFar = 0;
for (uint256 i = 0; i < nextInstallmentIndex; i++) {
amountPaidSoFar += installmentAmounts[i];
}
// Determine the amount still due after default
uint256 amountDue = salePrice - amountPaidSoFar;
// Emit an event to record the default declaration
emit DefaultDeclared(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