Created
April 4, 2022 23:55
-
-
Save Rizary/5a84ae086744fed6157d16ec66e35c4f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity >= 0.5.0 <0.7.0; | |
import "@aztec/protocol/contracts/ERC1724/ZkAssetMintable.sol"; | |
import "@aztec/protocol/contracts/libs/NoteUtils.sol"; | |
import "@aztec/protocol/contracts/interfaces/IZkAsset.sol"; | |
import "./LoanUtilities.sol"; | |
// Loan is inherit from ZkAssetMintable.sol from AZTEC protocol. | |
// each Loan contract represent a loan in the network. | |
contract Loan is ZkAssetMintable { | |
using SafeMath for uint256; | |
using NoteUtils for bytes; | |
using LoanUtilities for LoanUtilities.LoanVariables; | |
LoanUtilities.LoanVariables public loanVariables; | |
IZkAsset public settlementToken; | |
// [0] interestRate | |
// [1] interestPeriod | |
// [2] duration | |
// [3] settlementCurrencyId | |
// [4] loanSettlementDate | |
// [5] lastInterestPaymentDate address public borrower; | |
address public lender; | |
address public borrower; | |
mapping(address => bytes) lenderApprovals; | |
event LoanPayment(string paymentType, uint256 lastInterestPaymentDate); | |
event LoanDefault(); | |
event LoanRepaid(); | |
struct Note { | |
address owner; | |
bytes32 noteHash; | |
} | |
function _noteCoderToStruct(bytes memory note) internal pure returns (Note memory codedNote) { | |
(address owner, bytes32 noteHash,) = note.extractNote(); | |
return Note(owner, noteHash ); | |
} | |
// The Loan contract has its own constructor that is override version from ZkAssetMintable. | |
// It adds settlementCurrency, loanVariables, and borrower specifically used commonly in loan transaction. | |
constructor( | |
bytes32 _notional, | |
uint256[] memory _loanVariables, | |
address _borrower, | |
address _aceAddress, | |
address _settlementCurrency | |
) public ZkAssetMintable(_aceAddress, address(0), 1, true, false) { | |
// LoanVariables is a loan note to be executed. | |
loanVariables.loanFactory = msg.sender; // an instance of LoanDapp.sol | |
loanVariables.notional = _notional; | |
loanVariables.id = address(this); | |
loanVariables.interestRate = _loanVariables[0]; | |
loanVariables.interestPeriod = _loanVariables[1]; | |
loanVariables.duration = _loanVariables[2]; | |
loanVariables.borrower = _borrower; | |
borrower = _borrower; | |
loanVariables.settlementToken = IZkAsset(_settlementCurrency); | |
loanVariables.aceAddress = _aceAddress; | |
} | |
function requestAccess() public { | |
lenderApprovals[msg.sender] = '0x'; | |
} | |
function approveAccess(address _lender, bytes memory _sharedSecret) public { | |
lenderApprovals[_lender] = _sharedSecret; | |
} | |
// settleLoan function is for executing a loan in which borrowers borrow the money | |
// and lenders lends the money. It has lender address, current balance and proofData. | |
// proofData usually act as ZK proof. | |
function settleLoan( | |
bytes calldata _proofData, | |
bytes32 _currentInterestBalance, | |
address _lender | |
) external { | |
LoanUtilities.onlyLoanDapp(msg.sender, loanVariables.loanFactory); // make sure that msg.sender == loanFactory | |
// This is where the bilateral swap proofs is done with ACE. It validates and then execute loan using atomic | |
// swapping lender's and borrower's note. | |
LoanUtilities._processLoanSettlement(_proofData, loanVariables); | |
// update the loanVariables again with the new state. | |
loanVariables.loanSettlementDate = block.timestamp; | |
loanVariables.lastInterestPaymentDate = block.timestamp; | |
loanVariables.currentInterestBalance = _currentInterestBalance; | |
loanVariables.lender = _lender; | |
lender = _lender; | |
} | |
// This function is to mint a loan note from proof identifier and proofData from ZK proof provided | |
// by aztec. | |
function confidentialMint(uint24 _proof, bytes calldata _proofData) external { | |
// make sure that msg.sender == loanFactory | |
LoanUtilities.onlyLoanDapp(msg.sender, loanVariables.loanFactory); | |
// make sure that msg.sender has the authority to mint | |
require(msg.sender == owner, "only owner can call the confidentialMint() method"); | |
require(_proofData.length != 0, "proof invalid"); | |
// When ace mint using proof, proofData, and msg.sender, it validate the mints and | |
// then mints the loan note. It will minted the loan note, and give back the proofOutputs. | |
(bytes memory _proofOutputs) = ace.mint(_proof, _proofData, msg.sender); | |
(, bytes memory newTotal, ,) = _proofOutputs.get(0).extractProofOutput(); | |
(, bytes memory mintedNotes, ,) = _proofOutputs.get(1).extractProofOutput(); | |
(, | |
bytes32 noteHash, | |
bytes memory metadata) = newTotal.extractNote(); | |
// This where emit event happened | |
logOutputNotes(mintedNotes); // print log on note creation event. | |
emit UpdateTotalMinted(noteHash, metadata); | |
} | |
// This function is to withdraw the interest. It takes proof1 as a dividen proof | |
// and proof2 as joint split proof. | |
function withdrawInterest( | |
bytes memory _proof1, | |
bytes memory _proof2, | |
uint256 _interestDurationToWithdraw | |
) public { | |
// this event validate proof1 with ACE | |
(,bytes memory _proof1OutputNotes) = LoanUtilities._validateInterestProof(_proof1, _interestDurationToWithdraw, loanVariables); | |
// This line make sure that interest duration to withdraw is within the range of current block timestamp. | |
require(_interestDurationToWithdraw.add(loanVariables.lastInterestPaymentDate) < block.timestamp, ' withdraw is greater than accrued interest'); | |
// this event validate proof2 with ACE and transfer the interest in settlement | |
(bytes32 newCurrentInterestNoteHash) = LoanUtilities._processInterestWithdrawal(_proof2, _proof1OutputNotes, loanVariables); | |
// Modify the state of the loan note. | |
loanVariables.currentInterestBalance = newCurrentInterestNoteHash; | |
loanVariables.lastInterestPaymentDate = loanVariables.lastInterestPaymentDate.add(_interestDurationToWithdraw); | |
// Emith the event | |
emit LoanPayment('INTEREST', loanVariables.lastInterestPaymentDate); | |
} | |
// Borrower can withdraw borrowed tokens or repay the loan | |
function adjustInterestBalance(bytes memory _proofData) public { | |
LoanUtilities.onlyBorrower(msg.sender,borrower); // make sure that msg.sender == borrower | |
// this line validate the joint split proof using ACE. After then, it transfer the interest to the contract. | |
(bytes32 newCurrentInterestBalance) = LoanUtilities._processAdjustInterest(_proofData, loanVariables); | |
// modify the state of the loan note | |
loanVariables.currentInterestBalance = newCurrentInterestBalance; | |
} | |
// This function is used for borrower that want to repay the loan. | |
function repayLoan( | |
bytes memory _proof1, | |
bytes memory _proof2 | |
) public { | |
LoanUtilities.onlyBorrower(msg.sender, borrower); // make sure that msg.sender == borrower | |
uint256 remainingInterestDuration = loanVariables.loanSettlementDate.add(loanVariables.duration).sub(loanVariables.lastInterestPaymentDate); | |
// this line validate that _proof1 is correct using ACE | |
(,bytes memory _proof1OutputNotes) = LoanUtilities._validateInterestProof(_proof1, remainingInterestDuration, loanVariables); | |
// this line checks if the loan is mature or not | |
require(loanVariables.loanSettlementDate.add(loanVariables.duration) < block.timestamp, 'loan has not matured'); | |
// This line validate _proof2 with ACE, then transfer the interest in settlement tokens to lender. | |
LoanUtilities._processLoanRepayment( | |
_proof2, | |
_proof1OutputNotes, | |
loanVariables | |
); | |
// this is where emit happened | |
emit LoanRepaid(); | |
} | |
// This line validates whether the accrued interest is greater than interest duration to withdraw compared to current block timestamp | |
require(_interestDurationToWithdraw.add(loanVariables.lastInterestPaymentDate) < block.timestamp, 'withdraw is greater than accrued interest'); | |
// This line validates the proofs with ACE to check if loan is default or not. | |
LoanUtilities._validateDefaultProofs(_proof1, _proof2, _interestDurationToWithdraw, loanVariables); | |
// This is where emit event happened. | |
emit LoanDefault(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity >= 0.5.0 <0.7.0; | |
import "@aztec/protocol/contracts/interfaces/IAZTEC.sol"; | |
import "@aztec/protocol/contracts/libs/NoteUtils.sol"; | |
import "@aztec/protocol/contracts/ERC1724/ZkAsset.sol"; | |
import "./ZKERC20/ZKERC20.sol"; | |
import "./Loan.sol"; | |
// LoanDapp is a contract that inheret from IAZTEC. It manages and related | |
// to Loan.sol above. | |
contract LoanDapp is IAZTEC { | |
using NoteUtils for bytes; | |
event SettlementCurrencyAdded( | |
uint id, | |
address settlementAddress | |
); | |
event LoanApprovedForSettlement( | |
address loanId | |
); | |
event LoanCreated( | |
address id, | |
address borrower, | |
bytes32 notional, | |
string borrowerPublicKey, | |
uint256[] loanVariables, | |
uint createdAt | |
); | |
event ViewRequestCreated( | |
address loanId, | |
address lender, | |
string lenderPublicKey | |
); | |
event ViewRequestApproved( | |
uint accessId, | |
address loanId, | |
address user, | |
string sharedSecret | |
); | |
event NoteAccessApproved( | |
uint accessId, | |
bytes32 note, | |
address user, | |
string sharedSecret | |
); | |
address owner = msg.sender; | |
address aceAddress; | |
address[] public loans; | |
mapping(uint => address) public settlementCurrencies; | |
uint24 MINT_PRO0F = 66049; | |
uint24 BILATERAL_SWAP_PROOF = 65794; | |
modifier onlyOwner() { | |
require(msg.sender == owner); | |
_; | |
} | |
modifier onlyBorrower(address _loanAddress) { | |
Loan loanContract = Loan(_loanAddress); | |
require(msg.sender == loanContract.borrower()); | |
_; | |
} | |
constructor(address _aceAddress) public { | |
aceAddress = _aceAddress; | |
} | |
function _getCurrencyContract(uint _settlementCurrencyId) internal view returns (address) { | |
require(settlementCurrencies[_settlementCurrencyId] != address(0), 'Settlement Currency is not defined'); | |
return settlementCurrencies[_settlementCurrencyId]; | |
} | |
function _generateAccessId(bytes32 _note, address _user) internal pure returns (uint) { | |
return uint(keccak256(abi.encodePacked(_note, _user))); | |
} | |
function _approveNoteAccess( | |
bytes32 _note, | |
address _userAddress, | |
string memory _sharedSecret | |
) internal { | |
uint accessId = _generateAccessId(_note, _userAddress); | |
emit NoteAccessApproved( | |
accessId, | |
_note, | |
_userAddress, | |
_sharedSecret | |
); | |
} | |
// Creating a loan that is an instance in loan.sol, and execute the confidentialMint on it. | |
function _createLoan( | |
bytes32 _notional, | |
uint256[] memory _loanVariables, | |
bytes memory _proofData | |
) private returns (address) { | |
address loanCurrency = _getCurrencyContract(_loanVariables[3]); | |
// creating the New Loan | |
Loan newLoan = new Loan( | |
_notional, | |
_loanVariables, | |
msg.sender, | |
aceAddress, | |
loanCurrency | |
); | |
loans.push(address(newLoan)); | |
Loan loanContract = Loan(address(newLoan)); | |
loanContract.setProofs(1, uint256(-1)); | |
// This line use confidentialMint to mint the note | |
loanContract.confidentialMint(MINT_PROOF, bytes(_proofData)); | |
return address(newLoan); | |
} | |
function addSettlementCurrency(uint _id, address _address) external onlyOwner { | |
settlementCurrencies[_id] = _address; | |
emit SettlementCurrencyAdded(_id, _address); | |
} | |
// this function usually called by borrower | |
// to instantiate a loan. | |
function createLoan( | |
bytes32 _notional, | |
string calldata _viewingKey, | |
string calldata _borrowerPublicKey, | |
uint256[] calldata _loanVariables, | |
// [0] interestRate | |
// [1] interestPeriod | |
// [2] loanDuration | |
// [3] settlementCurrencyId | |
bytes calldata _proofData | |
) external { | |
// this line create and mint loan contract. | |
address loanId = _createLoan( | |
_notional, | |
_loanVariables, | |
_proofData | |
); | |
// This is where loanCreated function is emmited. | |
emit LoanCreated( | |
loanId, | |
msg.sender, | |
_notional, | |
_borrowerPublicKey, | |
_loanVariables, | |
block.timestamp | |
); | |
// If the approval is success, this line approve note acces to msg.sender | |
_approveNoteAccess( | |
_notional, | |
msg.sender, | |
_viewingKey | |
); | |
} | |
function approveLoanNotional( | |
bytes32 _noteHash, | |
bytes memory _signature, | |
address _loanId | |
) public { | |
Loan loanContract = Loan(_loanId); | |
loanContract.confidentialApprove(_noteHash, _loanId, true, _signature); | |
emit LoanApprovedForSettlement(_loanId); | |
} | |
// Lender then called this function to request the loan note. | |
function submitViewRequest(address _loanId, string calldata _lenderPublicKey) external { | |
emit ViewRequestCreated( | |
_loanId, | |
msg.sender, | |
_lenderPublicKey | |
); | |
} | |
// Borrower use this function if they want to let lender see the loan note. | |
function approveViewRequest( | |
address _loanId, | |
address _lender, | |
bytes32 _notionalNote, | |
string calldata _sharedSecret | |
) external onlyBorrower(_loanId) { | |
uint accessId = _generateAccessId(_notionalNote, _lender); | |
emit ViewRequestApproved( | |
accessId, | |
_loanId, | |
_lender, | |
_sharedSecret | |
); | |
} | |
event SettlementSuccesfull( | |
address indexed from, | |
address indexed to, | |
address loanId, | |
uint256 timestamp | |
); | |
struct LoanPayment { | |
address from; | |
address to; | |
bytes notional; | |
} | |
mapping(uint => mapping(uint => LoanPayment)) public loanPayments; | |
// Lender that approve to give loan to borrower use this function. | |
// it will execute the loan. | |
function settleInitialBalance( | |
address _loanId, | |
bytes calldata _proofData, | |
bytes32 _currentInterestBalance | |
) external { | |
Loan loanContract = Loan(_loanId); | |
loanContract.settleLoan(_proofData, _currentInterestBalance, msg.sender); | |
emit SettlementSuccesfull( | |
msg.sender, | |
loanContract.borrower(), | |
_loanId, | |
block.timestamp | |
); | |
} | |
function approveNoteAccess( | |
bytes32 _note, | |
string calldata _viewingKey, | |
string calldata _sharedSecret, | |
address _sharedWith | |
) external { | |
if (bytes(_viewingKey).length != 0) { | |
_approveNoteAccess( | |
_note, | |
msg.sender, | |
_viewingKey | |
); | |
} | |
if (bytes(_sharedSecret).length != 0) { | |
_approveNoteAccess( | |
_note, | |
_sharedWith, | |
_sharedSecret | |
); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment