Murabaha Agreement implementation

Murabaha Agreement

This contract implements the AIBIM Corporate Murabaha Master Agreement

Key Components of the Murabaha Agreement:

  1. Purchase Transactions: The bank acts as an agent to purchase goods on behalf of the principal.

  2. Sale Transactions: The bank sells the goods to the principal at a marked-up price with deferred payment.

  3. Special Clauses: Early repayment, late repayment, termination, and force majeure provisions.

  4. Multi-Signature Framework: Allows both parties to perform certain actions, including liquidation.

  5. Liquidation: Allows for the liquidation of commodities, with controllable permissions.

Events

    // Events to log significant actions within the contract
    event PurchaseCommodity(address indexed bank, uint256 stableAmount);
    event DeclineOffer(address indexed principle, uint256 commodityAmount);
    event AcceptOffer(address indexed principle, uint256 commodityAmount);
    event SaleOfferSet(uint256 salePrice, uint256[] installmentAmounts, uint256[] installmentDueDates);
    event InstallmentCollected(address indexed principle, uint256 amountDue);
    event EarlyRepaymentRebateSet(uint256 rebateAmount);
    event EarlyRepaid(address indexed principle, uint256 amountPaid);
    event DefaultDeclared(address indexed 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.
     */
    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

Last updated