List Proposal
1. Summary
PWNSimpleLoanListProposal.sol defines the List Proposal type for Simple Loan and implements functions to make an on-chain proposal and accept proposals.
The List Proposal can define a list of acceptable collateral ids or a whole collection. Interest can be either accruing or fixed.
2. Important links
3. Contract details
- PWNSimpleLoanListProposal.sol is written in Solidity version 0.8.16
 
Features
- Provides 
acceptProposalfunction andmakeProposalfor on-chain proposals - Defines the 
Proposalstruct 
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 acceptoruint256refinancingLoanId- Refinancing loan IDbytes32 calldataproposalData- Encoded Proposal and ProposalValues structsbytes32[] calldataproposalInclusionProof- Multiproposal inclusion proof. Empty if single proposalbytes 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, _erc712EncodeProposal(proposal));
    // Check provided collateral id
    if (proposal.collateralIdsWhitelistMerkleRoot != bytes32(0)) {
        // Verify whitelisted collateral id
        if (
            !MerkleProof.verify({
                proof: proposalValues.merkleInclusionProof,
                root: proposal.collateralIdsWhitelistMerkleRoot,
                leaf: keccak256(abi.encodePacked(proposalValues.collateralId))
            })
        ) revert CollateralIdNotWhitelisted({ id: proposalValues.collateralId });
    }
    // Note: If the `collateralIdsWhitelistMerkleRoot` is empty, any collateral id can be used.
    ProposalValuesBase memory proposalValuesBase = ProposalValuesBase({
        refinancingLoanId: refinancingLoanId,
        acceptor: acceptor,
        acceptorControllerData: proposalValues.acceptorControllerData
    });
    // Try to accept proposal
    _acceptProposal(
        proposalHash,
        proposalInclusionProof,
        signature,
        ProposalBase({
            collateralAddress: proposal.collateralAddress,
            collateralId: proposalValues.collateralId,
            checkCollateralStateFingerprint: proposal.checkCollateralStateFingerprint,
            collateralStateFingerprint: proposal.collateralStateFingerprint,
            creditAmount: proposal.creditAmount,
            availableCreditLimit: proposal.availableCreditLimit,
            utilizedCreditId: proposal.utilizedCreditId,
            expiration: proposal.expiration,
            acceptorController: proposal.acceptorController,
            acceptorControllerData: proposal.acceptorControllerData,
            proposer: proposal.proposer,
            isOffer: proposal.isOffer,
            refinancingLoanId: proposal.refinancingLoanId,
            nonceSpace: proposal.nonceSpace,
            nonce: proposal.nonce,
            loanContract: proposal.loanContract
        }),
        proposalValuesBase
    );
    // Create loan terms object
    loanTerms = PWNSimpleLoan.Terms({
        lender: proposal.isOffer ? proposal.proposer : acceptor,
        borrower: proposal.isOffer ? acceptor : proposal.proposer,
        duration: _getLoanDuration(proposal.durationOrDate),
        collateral: MultiToken.Asset({
            category: proposal.collateralCategory,
            assetAddress: proposal.collateralAddress,
            id: proposalValues.collateralId,
            amount: proposal.collateralAmount
        }),
        credit: MultiToken.ERC20({
            assetAddress: proposal.creditAddress,
            amount: proposal.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, _erc712EncodeProposal(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 encodedProposalValues 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 ProposalValues structs
Implementation
function decodeProposalData(bytes memory proposalData) public pure returns (Proposal memory, ProposalValues memory) {
    return abi.decode(proposalData, (Proposal, ProposalValues));
}
Events
The PWN Simple Loan List Proposal contract defines one event and one error.
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 proposaladdress indexedproposer- Address of the proposerProposalproposal- The proposal made
Errors
error CollateralIdNotWhitelisted(uint256 id);
CollateralIdNotWhitelisted
CollateralIdNotWhitelisted error is thrown when a collateral id is not whitelisted.
This error has one parameter:
uint256id- The collateral id that's not whitelisted
Proposal struct
| Type | Name | Comment | 
|---|---|---|
MultiToken.Category | collateralCategory | Corresponding collateral category | 
address | collateralAddress | Address of a loan collateral | 
bytes32 | collateralIdsWhitelistMerkleRoot | Merkle tree root of a set of whitelisted collateral ids | 
uint256 | collateralAmount | Amount of a collateral. Zero if ERC-721 | 
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 | creditAmount | Amount of credit asset | 
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 | durationOrDate | Duration of a loan in seconds. If the value is greater than 10^9, it's considered a timestamp of the loan end | 
uint40 | expiration | Proposal expiration unix timestamp in seconds | 
address | acceptorController | Address of Acceptor Controller contract that will verify submitted acceptor data | 
bytes | acceptorControllerData | Data provided by proposer to be verified by Acceptor Controller | 
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 | 
ProposalValues struct
| Type | Name | Comment | 
|---|---|---|
uint256 | collateralId | ID of the collateral to use. ID must be included in the merkle tree which root was submitted in the Proposal struct. | 
bytes32[] | merkleInclusionProof | ID merkle inclusion proof | 
bytes | acceptorControllerData | Data provided by proposal acceptor to be passed to the acceptor controller if defined in the Proposal struct |