Fungible Proposal

1. Summary

PWNSimpleLoanFungibleProposal.sol defines the Fungible Proposal type for Simple Loan and implements functions to make an on-chain proposal and accept proposals.

The Fungible Proposal is not tied to a specific collateral or credit amount. The amount of collateral and credit is specified during the proposal acceptance. Interest can be either accruing or fixed.

423KB
PWNSimpleLoanFungibleProposal.json

3. Contract details

  • PWNSimpleLoanFungibleProposal.sol is written in Solidity version 0.8.16

Features

  • Provides acceptProposal function and makeProposal for on-chain proposals

  • Defines the Proposal struct

Inherited contracts, implemented Interfaces and ERCs

Functions

acceptProposal

Overview

A function to accept a proposal.

This function takes five arguments supplied by the caller:

  • addressacceptor - Address of a proposal acceptor

  • uint256refinancingLoanId - Refinancing loan ID

  • bytes32 calldataproposalData - Encoded proposal struct

  • bytes32[] calldataproposalInclusionProof - Multiproposal inclusion proof. Empty if single proposal

  • bytes calldatasignature - Signature of a proposal

Implementation

function acceptProposal(
    address acceptor,
    uint256 refinancingLoanId,
    bytes calldata proposalData,
    bytes32[] calldata proposalInclusionProof,
    bytes calldata signature
) override external returns (bytes32 proposalHash, PWNSimpleLoan.Terms memory loanTerms) {
    // Decode proposal data
    (Proposal memory proposal, ProposalValues memory proposalValues) = decodeProposalData(proposalData);

    // Make proposal hash
    proposalHash = _getProposalHash(PROPOSAL_TYPEHASH, abi.encode(proposal));

    // Check min collateral amount
    if (proposal.minCollateralAmount == 0) {
        revert MinCollateralAmountNotSet();
    }
    if (proposalValues.collateralAmount < proposal.minCollateralAmount) {
        revert InsufficientCollateralAmount({
            current: proposalValues.collateralAmount,
            limit: proposal.minCollateralAmount
        });
    }

    // Calculate credit amount
    uint256 creditAmount = getCreditAmount(proposalValues.collateralAmount, proposal.creditPerCollateralUnit);

    // Try to accept proposal
    _acceptProposal(
        acceptor,
        refinancingLoanId,
        proposalHash,
        proposalInclusionProof,
        signature,
        ProposalBase({
            collateralAddress: proposal.collateralAddress,
            collateralId: proposal.collateralId,
            checkCollateralStateFingerprint: proposal.checkCollateralStateFingerprint,
            collateralStateFingerprint: proposal.collateralStateFingerprint,
            creditAmount: creditAmount,
            availableCreditLimit: proposal.availableCreditLimit,
            expiration: proposal.expiration,
            allowedAcceptor: proposal.allowedAcceptor,
            proposer: proposal.proposer,
            isOffer: proposal.isOffer,
            refinancingLoanId: proposal.refinancingLoanId,
            nonceSpace: proposal.nonceSpace,
            nonce: proposal.nonce,
            loanContract: proposal.loanContract
        })
    );

    // Create loan terms object
    loanTerms = PWNSimpleLoan.Terms({
        lender: proposal.isOffer ? proposal.proposer : acceptor,
        borrower: proposal.isOffer ? acceptor : proposal.proposer,
        duration: proposal.duration,
        collateral: MultiToken.Asset({
            category: proposal.collateralCategory,
            assetAddress: proposal.collateralAddress,
            id: proposal.collateralId,
            amount: proposalValues.collateralAmount
        }),
        credit: MultiToken.ERC20({
            assetAddress: proposal.creditAddress,
            amount: creditAmount
        }),
        fixedInterestAmount: proposal.fixedInterestAmount,
        accruingInterestAPR: proposal.accruingInterestAPR,
        lenderSpecHash: proposal.isOffer ? proposal.proposerSpecHash : bytes32(0),
        borrowerSpecHash: proposal.isOffer ? bytes32(0) : proposal.proposerSpecHash
    });
}
makeProposal

Overview

Function to create an on-chain proposal. Marks the hash of the supplied proposal as proposed.

This function takes one argument supplied by the caller:

  • Proposal calldataproposal - Proposal struct containing all needed proposal data

Implementation

function makeProposal(Proposal calldata proposal) external returns (bytes32 proposalHash) {
    proposalHash = getProposalHash(proposal);
    _makeProposal(proposalHash, proposal.proposer);
    emit ProposalMade(proposalHash, proposal.proposer, proposal);
}

View Functions

getProposalHash

Overview

This function returns supplied proposals hash according to EIP-712.

This function takes one argument supplied by the caller:

  • Proposal calldataproposal - Proposal struct to be hashed

Implementation

function getProposalHash(Proposal calldata proposal) public view returns (bytes32) {
    return _getProposalHash(PROPOSAL_TYPEHASH, abi.encode(proposal));
}
encodeProposalData

Overview

Function to encode a proposal struct and proposal values.

This function takes two arguments supplied by the caller:

  • Proposal memoryproposal - Proposal struct to be encoded

  • ProposalValues memoryproposalValues - ProposalValues struct to be encoded

Implementation

function encodeProposalData(
    Proposal memory proposal,
    ProposalValues memory proposalValues
) external pure returns (bytes memory) {
    return abi.encode(proposal, proposalValues);
}
decodeProposalData

Overview

Function to decode an encoded proposal struct and proposal values.

This function takes one argument supplied by the caller:

  • bytes memoryproposalData - Encoded proposal and proposal values structs

Implementation

function decodeProposalData(bytes memory proposalData) public pure returns (Proposal memory, ProposalValues memory) {
    return abi.decode(proposalData, (Proposal, ProposalValues));
}
getCreditAmount

Overview

Function to compute credit amount from collateral amount and credit per collateral unit.

This function takes two arguments supplied by the caller:

  • uint256collateralAmount - Amount of collateral

  • uint256creditPerCollateralUnit - Amount of credit per collateral unit with 38 decimals

Implementation

function getCreditAmount(uint256 collateralAmount, uint256 creditPerCollateralUnit) public pure returns (uint256) {
    return Math.mulDiv(collateralAmount, creditPerCollateralUnit, CREDIT_PER_COLLATERAL_UNIT_DENOMINATOR);
}

Events

The PWN Simple Loan Fungible Proposal contract defines one event and two errors.

event ProposalMade(bytes32 indexed proposalHash, address indexed proposer, Proposal proposal);
ProposalMade

ProposalMade event is emitted when an on-chain proposal is made.

This event has three parameters:

  • bytes32 indexedproposalHash - Hash of the proposed proposal

  • address indexedproposer - Address of the proposer

  • Proposalproposal - The proposal made

Errors

error MinCollateralAmountNotSet();
error InsufficientCollateralAmount(uint256 current, uint256 limit);
MinCollateralAmountNotSet

MinCollateralAmountNotSet error is thrown when a proposal has no minimal collateral amount set.

This error doesn't define any parameters.

InsufficientCollateralAmount

InsufficientCollateralAmount error is thrown when acceptor provides insufficient collateral amount.

This error has two parameters:

  • uint256current - Provided amount

  • uint256limit - Minimal amount

Proposal struct

TypeNameComment

collateralCategory

Corresponding collateral category

address

collateralAddress

Address of a loan collateral

uint256

collateralId

ID of a collateral. Zero if ERC-20

uint256

minCollateralAmount

Minimal amount of tokens used as a collateral

bool

checkCollateralStateFingerprint

Flag to enable check of collaterals state fingerprint (see ERC-5646)

bytes32

collateralStateFingerprint

A collateral state fingerprint (see ERC-5646)

address

creditAddress

Address of credit asset

uint256

creditPerCollateralUnit

Amount of tokens that are offered per collateral unit with 38 decimals

uint256

availableCreditLimit

Maximum credit limit of credit asset

uint256

fixedInterestAmount

Fixed interest amount in credit tokens. It is the minimum amount of interest which has to be paid by a borrower

uint24

accruingInterestAPR

Accruing interest APR with 2 decimals

uint32

duration

Loan duration in seconds

uint40

expiration

Proposal expiration unix timestamp in seconds

address

allowedAcceptor

Allowed acceptor address. Zero address if propsal can be accepted by any account

address

proposer

Proposer address

bytes32

proposerSpecHash

Hash of a proposer specific data, which must be provided during a loan creation

bool

isOffer

Flag to determine if a proposal is an offer or loan request

uint256

refinancingLoanId

ID of a loan to be refinanced. Zero if creating a new loan.

uint256

nonceSpace

Nonce space of the proposal

uint256

nonce

Nonce of the proposal

address

loanContract

Loan type contract

Last updated