Developer Docs
AppGitHub
  • Welcome!
  • Smart contracts
    • Core
      • Introduction
      • Deep Dive
      • Smart Contract Reference
        • PWN Hub
          • Tags
        • PWN Config
        • PWN Vault
        • Loan Types
          • Simple Loan
        • Proposals
          • Simple Loan Proposal
            • Simple Proposal
            • List Proposal
            • Elastic Proposal
            • Elastic Chainlink Proposal
            • Dutch Proposal
        • PWN Utilized Credit
        • PWN LOAN
        • PWN Revoked Nonce
        • Peripheral Contracts
          • Acceptor Controller
            • World ID
          • State Fingerprint Computer
            • UniV3
            • Chicken Bonds
          • Pool Adapter
            • Aave
            • Compound
            • ERC4626
        • Miscellaneous
          • PWN Fee Calculator
          • PWN Signature Checker
          • PWN Errors
          • PWN Periphery
          • Timelock
    • PWN DAO
      • Governance
        • Optimistic
        • Token
      • Tokens
        • PWN
        • stPWN
        • vePWN
          • Stake
          • Power
          • Metadata
      • Epoch Clock
      • Miscellaneous
        • Errors
        • EpochPowerLib
    • Tools
      • PWN Safe
        • Architecture
        • Security considerations
        • Smart Contract Reference
          • PWN Safe Factory
          • ATR Module
            • Tokenized Asset Manager
            • Recipient Permission Manager
          • Whitelist
          • ATR Guard
            • Operators context
      • Token Bundler
      • PWN Deployer
    • Libraries
      • MultiToken
    • Contract Addresses
  • More documentation
    • PWN Docs
    • FAQ
    • Audits
    • Using PWN without front-end
  • Deprecated
    • PWN Beta
      • Architecture
      • PWN
        • Off-chain signed offer
        • Offer types
      • PWN Vault
      • PWN LOAN
Powered by GitBook
On this page
  • 1. Summary
  • 2. Important links
  • 3. Contract details
  • Features
  • Functions
  • Internal Functions
  • View Functions
  • Errors
  • ProposalBase Struct
  • ProposalValues Struct
  • Multiproposal Struct
Edit on GitHub
  1. Smart contracts
  2. Core
  3. Smart Contract Reference
  4. Proposals

Simple Loan Proposal

PreviousProposalsNextSimple Proposal

Last updated 1 month ago

1. Summary

PWNSimpleLoanProposal.sol is an abstract contract inherited by Simple Loan Proposal types.

2. Important links

3. Contract details

  • PWNSimpleLoanProposal.sol is written in Solidity version 0.8.16

Features

  • Defines base interface for Simple Loan Proposals

  • Implements _makeProposal and _acceptProposal functions

Functions

revokeNonce

Overview

A helper function for revoking a proposal nonce on behalf of a caller.

This function takes two arguments supplied by the caller:

  • uint256nonceSpace - Nonce space of a proposal nonce to be revoked

  • uint256nonce - Proposal nonce to be revoked

Implementation

function revokeNonce(uint256 nonceSpace, uint256 nonce) external {
    revokedNonce.revokeNonce(msg.sender, nonceSpace, nonce);
}

Internal Functions

_makeProposal

Overview

Function to make an on-chain proposal.

This function takes two arguments:

  • bytes32proposalHash - hash of the respective proposal type struct

  • addressproposer - Address of a proposal proposer

Implementation

function _makeProposal(bytes32 proposalHash, address proposer) internal {
    if (msg.sender != proposer) {
        revert CallerIsNotStatedProposer({ addr: proposer });
    }

    proposalsMade[proposalHash] = true;
}
_acceptProposal

Overview

Makes necessary checks for accepting a proposal and reverts if any loan parameters are not valid.

This function takes six arguments:

  • bytes32 proposalHash - Hash of the proposal

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

  • bytes calldata signature - Signature of the proposal

Implementation

function _acceptProposal(
    bytes32 proposalHash,
    bytes32[] calldata proposalInclusionProof,
    bytes calldata signature,
    ProposalBase memory proposal,
    ProposalValuesBase memory proposalValues
) internal {
    // Check loan contract
    if (msg.sender != proposal.loanContract) {
        revert CallerNotLoanContract({ caller: msg.sender, loanContract: proposal.loanContract });
    }
    if (!hub.hasTag(proposal.loanContract, PWNHubTags.ACTIVE_LOAN)) {
        revert AddressMissingHubTag({ addr: proposal.loanContract, tag: PWNHubTags.ACTIVE_LOAN });
    }

    // Check proposal signature or that it was made on-chain
    if (proposalInclusionProof.length == 0) {
        // Single proposal signature
        if (!proposalsMade[proposalHash]) {
            if (!PWNSignatureChecker.isValidSignatureNow(proposal.proposer, proposalHash, signature)) {
                revert PWNSignatureChecker.InvalidSignature({ signer: proposal.proposer, digest: proposalHash });
            }
        }
    } else {
        // Multiproposal signature
        bytes32 multiproposalHash = getMultiproposalHash(
            Multiproposal({
                multiproposalMerkleRoot: MerkleProof.processProofCalldata({
                    proof: proposalInclusionProof,
                    leaf: proposalHash
                })
            })
        );
        if (!PWNSignatureChecker.isValidSignatureNow(proposal.proposer, multiproposalHash, signature)) {
            revert PWNSignatureChecker.InvalidSignature({ signer: proposal.proposer, digest: multiproposalHash });
        }
    }

    // Check proposer is not acceptor
    if (proposal.proposer == proposalValues.acceptor) {
        revert AcceptorIsProposer({ addr: proposalValues.acceptor});
    }

    // Check refinancing proposal
    if (proposalValues.refinancingLoanId == 0) {
        if (proposal.refinancingLoanId != 0) {
            revert InvalidRefinancingLoanId({ refinancingLoanId: proposal.refinancingLoanId });
        }
    } else {
        if (proposalValues.refinancingLoanId != proposal.refinancingLoanId) {
            if (proposal.refinancingLoanId != 0 || !proposal.isOffer) {
                revert InvalidRefinancingLoanId({ refinancingLoanId: proposal.refinancingLoanId });
            }
        }
    }

    // Check proposal is not expired
    if (block.timestamp >= proposal.expiration) {
        revert Expired({ current: block.timestamp, expiration: proposal.expiration });
    }

    // Check proposal is not revoked
    if (!revokedNonce.isNonceUsable(proposal.proposer, proposal.nonceSpace, proposal.nonce)) {
        revert PWNRevokedNonce.NonceNotUsable({
            addr: proposal.proposer,
            nonceSpace: proposal.nonceSpace,
            nonce: proposal.nonce
        });
    }

    // Check proposal acceptor controller
    if (proposal.acceptorController != address(0)) {
        if (IPWNAcceptorController(proposal.acceptorController).checkAcceptor({
            acceptor: proposalValues.acceptor,
            proposerData: proposal.acceptorControllerData,
            acceptorData: proposalValues.acceptorControllerData
        }) != type(IPWNAcceptorController).interfaceId) {
            revert InvalidAcceptorController({ acceptorController: proposal.acceptorController });
        }
    }

    if (proposal.availableCreditLimit == 0) {
        // Revoke nonce if credit limit is 0, proposal can be accepted only once
        revokedNonce.revokeNonce(proposal.proposer, proposal.nonceSpace, proposal.nonce);
    } else {
        // Update utilized credit
        // Note: This will revert if utilized credit would exceed the available credit limit
        utilizedCredit.utilizeCredit(
            proposal.proposer, proposal.utilizedCreditId, proposal.creditAmount, proposal.availableCreditLimit
        );
    }

    // Check collateral state fingerprint if needed
    if (proposal.checkCollateralStateFingerprint) {
        bytes32 currentFingerprint;
        IStateFingerpringComputer computer = config.getStateFingerprintComputer(proposal.collateralAddress);
        if (address(computer) != address(0)) {
            // Asset has registered computer
            currentFingerprint = computer.computeStateFingerprint({
                token: proposal.collateralAddress, tokenId: proposal.collateralId
            });
        } else if (ERC165Checker.supportsInterface(proposal.collateralAddress, type(IERC5646).interfaceId)) {
            // Asset implements ERC5646
            currentFingerprint = IERC5646(proposal.collateralAddress).getStateFingerprint(proposal.collateralId);
        } else {
            // Asset is not implementing ERC5646 and no computer is registered
            revert MissingStateFingerprintComputer();
        }

        if (proposal.collateralStateFingerprint != currentFingerprint) {
            // Fingerprint mismatch
            revert InvalidCollateralStateFingerprint({
                current: currentFingerprint,
                proposed: proposal.collateralStateFingerprint
            });
        }
    }
}

View Functions

getMultiproposalHash

Overview

This function takes one argument supplied by the caller:

Implementation

function getMultiproposalHash(Multiproposal memory multiproposal) public view returns (bytes32) {
    return keccak256(abi.encodePacked(
        hex"1901", MULTIPROPOSAL_DOMAIN_SEPARATOR, keccak256(abi.encodePacked(
            MULTIPROPOSAL_TYPEHASH, abi.encode(multiproposal)
        ))
    ));
}
_getProposalHash

Overview

This function takes two arguments supplied by the caller:

  • bytes32proposalTypehash - Hash of the respective proposal type

  • bytes memoryencodedProposal - Encoded respective proposal type struct

Implementation

function _getProposalHash(
    bytes32 proposalTypehash,
    bytes memory encodedProposal
) internal view returns (bytes32) {
    return keccak256(abi.encodePacked(
        hex"1901", DOMAIN_SEPARATOR, keccak256(abi.encodePacked(
            proposalTypehash, encodedProposal
        ))
    ));
}
_getLoanDuration

Overview

This function returns loan duration given supplied timestamp or duration.

This function takes one argument supplied by the caller:

  • uint32durationOrDate - Duration of a loan in seconds. If the value is greater than 10^9, it's considered a timestamp of the loan end

Implementation

function _getLoanDuration(uint32 durationOrDate) internal view returns (uint32) {
    if (durationOrDate <= 1e9) {
        // Value is duration
        return durationOrDate;
    } else if (durationOrDate >= block.timestamp) {
        // Value is date
        return uint32(uint256(durationOrDate) - block.timestamp);
    } else {
        revert DefaultDateInPast({ defaultDate: durationOrDate, current: uint32(block.timestamp) });
    }
}

Errors

The PWN Simple Loan Offer contract defines eight errors and no events.

error CallerNotLoanContract(address caller, address loanContract);
error MissingStateFingerprintComputer();
error InvalidCollateralStateFingerprint(bytes32 current, bytes32 proposed);
error CallerIsNotStatedProposer(address addr);
error AcceptorIsProposer(address addr);
error InvalidRefinancingLoanId(uint256 refinancingLoanId);
error AvailableCreditLimitExceeded(uint256 used, uint256 limit);
error InvalidAcceptorController(address acceptorController);
error DefaultDateInPast(uint32 defaultDate, uint32 current);
CallerNotLoanContract

A CallerNotLoanContract error is thrown when a caller is missing a required hub tag.

This error has two parameters:

  • addresscaller

  • addressloanContract

MissingStateFingerprintComputer

A MissingStateFingerprintComputer error is thrown when a state fingerprint computer is not registered.

This error doesn't define any parameters.

InvalidCollateralStateFingerprint

A InvalidCollateralStateFingerprint error is thrown when a proposed collateral state fingerprint doesn't match the current state.

This error has two parameters:

  • bytes32current

  • bytes32proposed

CallerIsNotStatedProposer

A CallerIsNotStatedProposer error is thrown when a caller is not a stated proposer.

This error has one parameter:

  • addressaddr

AcceptorIsProposer

An AcceptorIsProposer error is thrown when proposal acceptor and proposer are the same.

This error has one parameter:

  • addressaddr

InvalidRefinancingLoanId

An InvalidRefinancingLoanId error is thrown when provided refinance loan id cannot be used.

This error has one parameter:

  • uint256refinancingLoanId

AvailableCreditLimitExceeded

An AvailableCreditLimitExceeded error is thrown when a proposal would exceed the available credit limit.

This error has two parameters:

  • uint256used

  • uint256limit

InvalidAcceptorController

A InvalidAcceptorController error is thrown when supplied acceptor controller isn't a valid acceptor controller contract.

This error has one parameter:

  • addressacceptorController

DefaultDateInPast

A DefaultDateInPast error is thrown when caller supplies a loan default date that's in the past.

This error has two parameters:

  • uint32defaultDate

  • uint32current

ProposalBase Struct

Type
Name
Comment

address

collateralAddress

Address of a loan collateral

uint256

collateralId

ID of a collateral. Zero if ERC-20

bool

checkCollateralStateFingerprint

bytes32

collateralStateFingerprint

uint256

creditAmount

Amount of credit asset

uint256

availableCreditLimit

Maximum credit limit of credit asset

uint40

expiration

Proposal expiration unix timestamp in seconds

address

acceptorController

bytes

acceptorControllerData

address

proposer

Proposer address

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

refinancingLoanId

Loan ID to refinance if refinancing a loan

address

acceptor

Address of the acceptor

bytes

acceptorControllerData

Multiproposal Struct

Type
Name
Comment

bytes32

multiproposalMerkleRoot

Root of the multiproposal merkle tree

Defines struct

ProposalBase memory proposal - struct

ProposalValuesBase memory proposalValues - struct

This function returns a multiproposal hash according to .

Multiproposal memorymultiproposal - struct

This function returns a proposal hash according to .

Flag to enable check of collaterals state fingerprint (see )

A collateral state fingerprint (see )

Address of contract that will verify submitted acceptor data

Data provided by proposer to be verified by

Data provided by proposal acceptor to be verified by

EIP-712
EIP-712
ProposalBase
ProposalBase
ProposalValues
Multiproposal
ERC-5
646
ERC-5
646
Acceptor Controller
Acceptor Controller
Acceptor Controller
pwn_contracts/src/loan/terms/simple/proposal/PWNSimpleLoanProposal.sol at master · PWNFinance/pwn_contractsGitHub
Logo