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 link
  • 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

Elastic Chainlink Proposal

PreviousElastic ProposalNextDutch Proposal

Last updated 2 months ago

1. Summary

PWNSimpleLoanElasticChainlinkProposal.sol implements elastic loan proposals using Chainlink oracles for price feeds. This proposal type calculates collateral requirements dynamically based on real-time market data, supporting multiple intermediary denominations and LTV ratios.

The elastic proposal determines collateral amount during acceptance using Chainlink price feeds, LTV, and credit amount. Interest can be accruing or fixed, with support for L2 sequencer uptime checks.

2. Important link

3. Contract details

  • PWNSimpleLoanElasticChainlinkProposal.sol is written in Solidity version 0.8.16

Features

  • Dynamic proposal terms based on Chainlink price feeds

  • L2 sequencer uptime checks for oracle reliability

  • Feeds with last update older that 1 day are considered invalid

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 - Proposal acceptor address

  • uint256refinancingLoanId - ID of loan being refinanced

  • bytes calldataproposalData - Encoded proposal data

  • bytes32[] calldataproposalInclusionProof - Merkle proof for multiproposal

  • bytes calldatasignature - Proposal signature

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 min credit amount
        if (proposal.minCreditAmount == 0) {
            revert MinCreditAmountNotSet();
        }

        // Check sufficient credit amount
        if (proposalValues.creditAmount < proposal.minCreditAmount) {
            revert InsufficientCreditAmount({ current: proposalValues.creditAmount, limit: proposal.minCreditAmount });
        }

        // Calculate collateral amount
        uint256 collateralAmount = getCollateralAmount(
            proposal.creditAddress,
            proposalValues.creditAmount,
            proposal.collateralAddress,
            proposal.feedIntermediaryDenominations,
            proposal.feedInvertFlags,
            proposal.loanToValue
        );

        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: proposalValues.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: proposal.collateralId,
                amount: collateralAmount
            }),
            credit: MultiToken.ERC20({
                assetAddress: proposal.creditAddress,
                amount: proposalValues.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:

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));
}
getCollateralAmount

Overview

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

This function takes two arguments supplied by the caller:

  • uint256creditAmount - Amount of credit

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

Implementation

function getCollateralAmount(
    address creditAddress,
    uint256 creditAmount,
    address collateralAddress,
    address[] memory feedIntermediaryDenominations,
    bool[] memory feedInvertFlags,
    uint256 loanToValue
) public view returns (uint256) {
    // check L2 sequencer uptime if necessary
    l2SequencerUptimeFeed.checkSequencerUptime();

    // don't allow more than 2 intermediary denominations
    if (feedIntermediaryDenominations.length > MAX_INTERMEDIARY_DENOMINATIONS) {
        revert IntermediaryDenominationsOutOfBounds({
            current: feedIntermediaryDenominations.length,
            limit: MAX_INTERMEDIARY_DENOMINATIONS
        });
    }

    // fetch credit asset price with collateral asset as denomination
    // Note: use ETH price feed for WETH asset due to absence of WETH price feed
    (uint256 price, uint8 priceDecimals) = chainlinkFeedRegistry.fetchCreditPriceWithCollateralDenomination({
        creditAsset: creditAddress == WETH ? Chainlink.ETH : creditAddress,
        collateralAsset: collateralAddress == WETH ? Chainlink.ETH : collateralAddress,
        feedIntermediaryDenominations: feedIntermediaryDenominations,
        feedInvertFlags: feedInvertFlags
    });

    // fetch asset decimals
    uint256 creditDecimals = safeFetchDecimals(creditAddress);
    uint256 collateralDecimals = safeFetchDecimals(collateralAddress);

    if (collateralDecimals > creditDecimals) {
        creditAmount *= 10 ** (collateralDecimals - creditDecimals);
    }

    uint256 collateralAmount = Math.mulDiv(creditAmount, price, 10 ** priceDecimals);
    collateralAmount = Math.mulDiv(collateralAmount, LOAN_TO_VALUE_DENOMINATOR, loanToValue);

    if (collateralDecimals < creditDecimals) {
        collateralAmount /= 10 ** (creditDecimals - collateralDecimals);
    }

    return collateralAmount;
}

Events

The PWN Simple Loan Elastic Chainlink Proposal contract defines one event and three 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 InsufficientCreditAmount(uint256 current, uint256 limit);
error IntermediaryDenominationsOutOfBounds(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.

InsufficientCreditAmount

InsufficientCreditAmount error is thrown when acceptor provides insufficient credit amount.

This error has two parameters:

  • uint256current - Provided amount

  • uint256limit - Minimal amount

IntermediaryDenominationsOutOfBounds

IntermediaryDenominationsOutOfBounds error is thrown when intermediary denominations are out of bounds.

This error has two parameters:

  • uint256current

  • uint256limit

Proposal struct

Parameter
Type
Description

collateralCategory

MultiToken.Category

Collateral type (0=ERC20, 1=ERC721, 2=ERC1155)

collateralAddress

address

Collateral token address

collateralId

uint256

Collateral token ID (0 for ERC20)

checkCollateralStateFingerprint

bool

Enable ERC-5646 state verification

collateralStateFingerprint

bytes32

ERC-5646 state fingerprint

creditAddress

address

Loan credit token address

feedIntermediaryDenominations

address[]

Chainlink price feed conversion path

feedInvertFlags

bool[]

Flags for inverted price feeds

loanToValue

uint256

LTV ratio (6231 = 62.31%)

minCreditAmount

uint256

Minimum borrowable credit

availableCreditLimit

uint256

Maximum credit pool for multiple accepts

utilizedCreditId

bytes32

Shared credit utilization identifier

fixedInterestAmount

uint256

Minimum interest payment

accruingInterestAPR

uint24

APR with 2 decimals

durationOrDate

uint32

Loan duration (seconds) or end timestamp

expiration

uint40

Proposal expiration timestamp

address

acceptorController

bytes

acceptorControllerData

proposer

address

Proposal creator address

proposerSpecHash

bytes32

Proposer-specific data hash

isOffer

bool

True=loan offer, False=loan request

refinancingLoanId

uint256

ID of loan being refinanced

nonceSpace

uint256

Nonce grouping identifier

nonce

uint256

Proposal uniqueness nonce

loanContract

address

Associated loan contract address

ProposalValues struct

Type
Name
Comment

uint256

creditAmount

Amount of credit to use from the available credit limit

bytes

acceptorControllerData

This function returns supplied proposals hash according to .

Proposal calldataproposal - struct to be hashed

Proposal memoryproposal - struct to be encoded

ProposalValues memoryproposalValues - struct to be encoded

bytes memoryproposalData - Encoded and structs

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
Proposal
ProposalValues
Proposal
ProposalValues
Acceptor Controller
Acceptor Controller
Proposal
pwn_contracts/src/loan/terms/simple/proposal/PWNSimpleLoanElasticChainlinkProposal.sol at master · PWNDAO/pwn_contractsGitHub
Logo