Makes necessary checks for accepting a proposal and reverts if any loan parameters are not valid.
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
});
}
}
}