Elastic Proposal
1. Summary
PWNSimpleLoanElasticProposal.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.
2. Important link
3. Contract details
- PWNSimpleLoanElasticProposal.sol is written in Solidity version 0.8.16
Features
- Provides
acceptProposal
function andmakeProposal
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:
address
acceptor
- Address of a proposal acceptoruint256
refinancingLoanId
- Refinancing loan IDbytes32 calldata
proposalData
- Encoded proposal structbytes32[] calldata
proposalInclusionProof
- Multiproposal inclusion proof. Empty if single proposalbytes calldata
signature
- 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 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 credit amount
uint256 collateralAmount = getCollateralAmount(proposalValues.creditAmount, proposal.creditPerCollateralUnit);
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 calldata
proposal
- 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 calldata
proposal
- 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 memory
proposal
- Proposal struct to be encodedProposalValues memory
proposalValues
- 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 memory
proposalData
- Encoded Proposal and ProposalValues structs
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:
uint256
creditAmount
- Amount of credituint256
creditPerCollateralUnit
- Amount of credit per collateral unit with 38 decimals
Implementation
function getCollateralAmount(uint256 creditAmount, uint256 creditPerCollateralUnit) public pure returns (uint256) {
if (creditPerCollateralUnit == 0) {
revert ZeroCreditPerCollateralUnit();
}
return Math.mulDiv(creditAmount, CREDIT_PER_COLLATERAL_UNIT_DENOMINATOR, creditPerCollateralUnit);
}
Events
The PWN Simple Loan Elastic 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 indexed
proposalHash
- Hash of the proposed proposaladdress indexed
proposer
- Address of the proposerProposal
proposal
- The proposal made
Errors
error MinCollateralAmountNotSet();
error InsufficientCreditAmount(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:
uint256
current
- Provided amountuint256
limit
- Minimal amount
Proposal
struct
Type | Name | Comment |
---|---|---|
MultiToken.Category | 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 | 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 | creditAmount | Amount of credit to use from the available credit limit |
bytes | acceptorControllerData | Data provided by proposal acceptor to be passed to the acceptor controller if defined in the Proposal struct |