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
  • Inherited contracts, implemented Interfaces and ERCs
  • Functions
  • View Functions
  • Events
  • Errors
  • Proposal struct
  • ProposalValues struct
Edit on GitHub
  1. Smart contracts
  2. Core
  3. Smart Contract Reference
  4. Proposals
  5. Simple Loan Proposal

Dutch Proposal

PreviousElastic Chainlink ProposalNextPWN Utilized Credit

Last updated 1 month ago

1. Summary

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

The Dutch Auction Proposal creates a dutch auction with user pre-defined collateral, loan duration and interest. Credit amount drops (or goes up, if proposal is an offer) from user defined minimum and maximum for the auction duration from auction start. Interest can be either accruing or fixed.

2. Important links

3. Contract details

  • PWNSimpleLoanDutchAuctionProposal.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, _erc712EncodeProposal(proposal));

    // Calculate current credit amount
    uint256 creditAmount = getCreditAmount(proposal, block.timestamp);

    // Check acceptor values
    if (proposal.isOffer) {
        if (
            creditAmount < proposalValues.intendedCreditAmount ||
            proposalValues.intendedCreditAmount + proposalValues.slippage < creditAmount
        ) {
            revert InvalidCreditAmount({
                auctionCreditAmount: creditAmount,
                intendedCreditAmount: proposalValues.intendedCreditAmount,
                slippage: proposalValues.slippage
            });
        }
    } else {
        if (
            creditAmount > proposalValues.intendedCreditAmount ||
            proposalValues.intendedCreditAmount - proposalValues.slippage > creditAmount
        ) {
            revert InvalidCreditAmount({
                auctionCreditAmount: creditAmount,
                intendedCreditAmount: proposalValues.intendedCreditAmount,
                slippage: proposalValues.slippage
            });
        }
    }

    ProposalValuesBase memory proposalValuesBase = ProposalValuesBase({
        refinancingLoanId: refinancingLoanId,
        acceptor: acceptor,
        acceptorControllerData: proposalValues.acceptorControllerData
    });

    // Try to accept proposal
    _acceptProposal(
        proposalHash,
        proposalInclusionProof,
        signature,
        ProposalBase({
            collateralAddress: proposal.collateralAddress,
            collateralId: proposal.collateralId,
            checkCollateralStateFingerprint: proposal.checkCollateralStateFingerprint,
            collateralStateFingerprint: proposal.collateralStateFingerprint,
            creditAmount: creditAmount,
            availableCreditLimit: proposal.availableCreditLimit,
            utilizedCreditId: proposal.utilizedCreditId,
            expiration: proposal.auctionStart + proposal.auctionDuration + 1 minutes,
            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: proposal.collateralId,
            amount: proposal.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 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:

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:

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 for an auction in a specific timestamp.

This function takes two arguments supplied by the caller:

  • uint256timestamp - Timestamp to calculate auction credit amount for

Implementation

function getCreditAmount(Proposal memory proposal, uint256 timestamp) public pure returns (uint256) {
    // Check proposal
    if (proposal.auctionDuration < 1 minutes) {
        revert InvalidAuctionDuration({
            current: proposal.auctionDuration,
            limit: 1 minutes
        });
    }
    if (proposal.auctionDuration % 1 minutes > 0) {
        revert AuctionDurationNotInFullMinutes({
            current: proposal.auctionDuration
        });
    }
    if (proposal.maxCreditAmount <= proposal.minCreditAmount) {
        revert InvalidCreditAmountRange({
            minCreditAmount: proposal.minCreditAmount,
            maxCreditAmount: proposal.maxCreditAmount
        });
    }

    // Check auction is in progress
    if (timestamp < proposal.auctionStart) {
        revert AuctionNotInProgress({
            currentTimestamp: timestamp,
            auctionStart: proposal.auctionStart
        });
    }
    if (proposal.auctionStart + proposal.auctionDuration + 1 minutes <= timestamp) {
        revert Expired({
            current: timestamp,
            expiration: proposal.auctionStart + proposal.auctionDuration + 1 minutes
        });
    }

    // Note: Auction duration is increased by 1 minute to have
    // `maxCreditAmount` value in the last minutes of the auction.

    uint256 creditAmountDelta = Math.mulDiv(
        proposal.maxCreditAmount - proposal.minCreditAmount, // Max credit amount difference
        (timestamp - proposal.auctionStart) / 1 minutes, // Time passed since auction start
        proposal.auctionDuration / 1 minutes // Auction duration
    );

    // Note: Request auction is decreasing credit amount (dutch auction).
    // Offer auction is increasing credit amount (reverse dutch auction).

    // Return credit amount
    return proposal.isOffer
        ? proposal.minCreditAmount + creditAmountDelta
        : proposal.maxCreditAmount - creditAmountDelta;
}

Events

The PWN Simple Loan Dutch Auction Proposal contract defines one event and five 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 InvalidAuctionDuration(uint256 current, uint256 limit);
error AuctionDurationNotInFullMinutes(uint256 current);
error InvalidCreditAmountRange(uint256 minCreditAmount, uint256 maxCreditAmount);
error InvalidCreditAmount(uint256 auctionCreditAmount, uint256 intendedCreditAmount, uint256 slippage);
error AuctionNotInProgress(uint256 currentTimestamp, uint256 auctionStart);
InvalidAuctionDuration

InvalidAuctionDuration error is thrown when auction duration is less than min auction duration.

This error has two parameters:

  • uint256current - Provided duration

  • uint256limit - Minimal duration (1 minute)

AuctionDurationNotInFullMinutes

AuctionDurationNotInFullMinutes error is thrown when auction duration is not in full minutes.

This error has two parameters:

  • uint256current - Supplied duration

InvalidCreditAmountRange

InvalidCreditAmountRange error is thrown when min credit amount is greater than max credit amount.

This error has two parameters:

  • uint256minCreditAmount

  • uint256maxCreditAmount

InvalidCreditAmount

InvalidCreditAmount error is thrown when current auction credit amount is not in the range of intended credit amount and slippage.

This error has three parameters:

  • uint256auctionCreditAmount

  • uint256intendedCreditAmount

  • uint256slippage

AuctionNotInProgress

AuctionNotInProgress error is thrown when auction has not started yet or has already ended.

This error has two parameters:

  • uint256currentTimestamp

  • uint256auctionStart

Proposal struct

Type
Name
Comment

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

bytes32

collateralStateFingerprint

address

creditAddress

Address of credit asset

uint256

minCreditAmount

Minimum amount of tokens which are proposed as a loan to a borrower. If isOffer is true, auction will start with this amount, otherwise it will end with this amount.

uint256

maxCreditAmount

Maximum amount of tokens which is proposed as a loan to a borrower. If isOffer is true, auction will end with this amount, otherwise it will start with this amount.

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

auctionStart

Auction start timestamp in seconds

uint40

auctionDuration

Auction duration in seconds

address

acceptorController

bytes

acceptorControllerData

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

intendedCreditAmount

Amount of credit to use from the available credit limit

uint256

slippage

Slippage value that is acceptor willing to accept from the intended creditAmount. If proposal is an offer, slippage is added to the creditAmount, otherwise it is subtracted.

bytes

acceptorControllerData

This function returns supplied proposals hash according to .

Proposal memoryproposal - struct to be encoded

ProposalValues memoryproposalValues - struct to be encoded

bytes memoryproposalData - Encoded and structs

Proposal memoryproposal - struct containing all proposal data

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 passed to the acceptor controller if defined in the struct

PWNSimpleLoanProposal
EIP-712
Proposal
ProposalValues
Proposal
ProposalValues
Proposal
ERC-5
646
ERC-5
646
Acceptor Controller
Acceptor Controller
Proposal
pwn_contracts/src/loan/terms/simple/proposal/PWNSimpleLoanDutchAuctionProposal.sol at master · PWNFinance/pwn_contractsGitHub
Logo
550KB
PWNSimpleLoanDutchAuctionProposal.json