编译器
0.8.18+commit.87f61d96
文件 1 的 36:AdminFacet.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IOwnershipFacet} from "./interface/IOwnershipFacet.sol";
import {IAdminFacet} from "./interface/IAdminFacet.sol";
import {Ray} from "./DataStructure/Objects.sol";
import {SupplyPosition, Protocol} from "./DataStructure/Storage.sol";
import {protocolStorage, ONE} from "./DataStructure/Global.sol";
import {CallerIsNotOwner} from "./DataStructure/Errors.sol";
import {RayMath} from "./utils/RayMath.sol";
contract AdminFacet is IAdminFacet {
using RayMath for Ray;
modifier onlyOwner() {
address admin = IOwnershipFacet(address(this)).owner();
if (msg.sender != admin) {
revert CallerIsNotOwner(admin);
}
_;
}
function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {
protocolStorage().auction.duration = newAuctionDuration;
emit NewAuctionDuration(newAuctionDuration);
}
function setAuctionPriceFactor(Ray newAuctionPriceFactor) external onlyOwner {
require(newAuctionPriceFactor.gte(ONE.mul(5).div(2)), "");
protocolStorage().auction.priceFactor = newAuctionPriceFactor;
emit NewAuctionPriceFactor(newAuctionPriceFactor);
}
function createTranche(Ray newTranche) external onlyOwner returns (uint256 newTrancheId) {
Protocol storage proto = protocolStorage();
newTrancheId = proto.nbOfTranches++;
proto.tranche[newTrancheId] = newTranche;
emit NewTranche(newTranche, newTrancheId);
}
function setMinOfferCost(IERC20 currency, uint256 newMinOfferCost) external onlyOwner {
protocolStorage().minOfferCost[currency] = newMinOfferCost;
emit NewMininimumOfferCost(currency, newMinOfferCost);
}
function setBorrowAmountPerOfferLowerBound(IERC20 currency, uint256 newLowerBound) external onlyOwner {
protocolStorage().offerBorrowAmountLowerBound[currency] = newLowerBound;
emit NewBorrowAmountPerOfferLowerBound(currency, newLowerBound);
}
}
文件 2 的 36:AuctionFacet.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IAuctionFacet} from "./interface/IAuctionFacet.sol";
import {BuyArg, NFToken, Ray} from "./DataStructure/Objects.sol";
import {Loan, Protocol, Provision, SupplyPosition} from "./DataStructure/Storage.sol";
import {RayMath} from "./utils/RayMath.sol";
import {Erc20CheckedTransfer} from "./utils/Erc20CheckedTransfer.sol";
import {SafeMint} from "./SupplyPositionLogic/SafeMint.sol";
import {protocolStorage, supplyPositionStorage, ONE, ZERO} from "./DataStructure/Global.sol";
import {LoanAlreadyRepaid, CollateralIsNotLiquidableYet, PriceOverMaximum} from "./DataStructure/Errors.sol";
contract AuctionFacet is IAuctionFacet, SafeMint {
using RayMath for Ray;
using RayMath for uint256;
using Erc20CheckedTransfer for IERC20;
function buy(BuyArg[] memory args) external {
for (uint256 i = 0; i < args.length; i++) {
useLoan(args[i]);
}
}
function price(uint256 loanId) public view returns (uint256) {
Loan storage loan = protocolStorage().loan[loanId];
uint256 loanEndDate = loan.endDate;
checkLoanStatus(loanId);
uint256 timeSinceLiquidable = block.timestamp - loanEndDate;
Ray decreasingFactor = timeSinceLiquidable >= loan.auction.duration
? ZERO
: ONE.sub(timeSinceLiquidable.div(loan.auction.duration));
uint256 estimatedValue = loan.lent.div(loan.shareLent);
return estimatedValue.mul(loan.auction.priceFactor).mul(decreasingFactor);
}
function useLoan(BuyArg memory arg) internal {
Loan storage loan = protocolStorage().loan[arg.loanId];
uint256 toPay = price(arg.loanId);
if (toPay > arg.maxPrice) {
revert PriceOverMaximum(arg.maxPrice, toPay);
}
loan.payment.liquidated = true;
loan.payment.paid = toPay;
loan.assetLent.checkedTransferFrom(msg.sender, address(this), toPay);
loan.collateral.implem.safeTransferFrom(address(this), arg.to, loan.collateral.id);
emit Buy(arg.loanId, abi.encode(arg));
}
function checkLoanStatus(uint256 loanId) internal view {
Loan storage loan = protocolStorage().loan[loanId];
if (block.timestamp <= loan.endDate) {
revert CollateralIsNotLiquidableYet(loan.endDate, loanId);
}
if (loan.payment.paid != 0 || loan.payment.liquidated) {
revert LoanAlreadyRepaid(loanId);
}
}
}
文件 3 的 36:BorrowCheckers.sol
pragma solidity 0.8.18;
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {IBorrowCheckers} from "../interface/IBorrowCheckers.sol";
import {Signature} from "../Signature.sol";
import {NFTokenUtils} from "../utils/NFTokenUtils.sol";
import {Offer, OfferArg, NFToken} from "../../src/DataStructure/Objects.sol";
import {Protocol} from "../../src/DataStructure/Storage.sol";
import {protocolStorage} from "../../src/DataStructure/Global.sol";
import {BadCollateral, OfferHasExpired, RequestedAmountTooHigh, RequestedAmountIsUnderMinimum, CurrencyNotSupported, InvalidTranche} from "../../src/DataStructure/Errors.sol";
abstract contract BorrowCheckers is IBorrowCheckers, Signature {
using NFTokenUtils for NFToken;
function checkOfferArg(OfferArg memory arg) internal view returns (address signer) {
Protocol storage proto = protocolStorage();
signer = ECDSA.recover(offerDigest(arg.offer), arg.signature);
uint256 amountLowerBound = proto.offerBorrowAmountLowerBound[arg.offer.assetToLend];
if (amountLowerBound == 0 || proto.minOfferCost[arg.offer.assetToLend] == 0) {
revert CurrencyNotSupported(arg.offer.assetToLend);
}
if (!(arg.amount > amountLowerBound)) {
revert RequestedAmountIsUnderMinimum(arg.offer, arg.amount, amountLowerBound);
}
if (block.timestamp > arg.offer.expirationDate) {
revert OfferHasExpired(arg.offer, arg.offer.expirationDate);
}
if (arg.offer.tranche >= proto.nbOfTranches) {
revert InvalidTranche(proto.nbOfTranches);
}
}
function checkCollateral(Offer memory offer, NFToken memory providedNft) internal pure {
if (!offer.collateral.eq(providedNft)) {
revert BadCollateral(offer, providedNft);
}
}
}
文件 4 的 36:BorrowFacet.sol
pragma solidity 0.8.18;
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IBorrowFacet} from "./interface/IBorrowFacet.sol";
import {BorrowHandlers} from "./BorrowLogic/BorrowHandlers.sol";
import {BorrowArg, NFToken, Offer, OfferArg} from "./DataStructure/Objects.sol";
import {Signature} from "./Signature.sol";
contract BorrowFacet is IBorrowFacet, BorrowHandlers {
function onERC721Received(
address,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4) {
OfferArg[] memory args = abi.decode(data, (OfferArg[]));
useCollateral(args, from, NFToken({implem: IERC721(msg.sender), id: tokenId}));
return this.onERC721Received.selector;
}
function borrow(BorrowArg[] calldata args) external {
for (uint256 i = 0; i < args.length; i++) {
args[i].nft.implem.transferFrom(msg.sender, address(this), args[i].nft.id);
useCollateral(args[i].args, msg.sender, args[i].nft);
}
}
}
文件 5 的 36:BorrowHandlers.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IBorrowHandlers} from "../interface/IBorrowHandlers.sol";
import {BorrowCheckers} from "./BorrowCheckers.sol";
import {CollateralState, NFToken, OfferArg, Ray} from "../DataStructure/Objects.sol";
import {Loan, Payment, Protocol, Provision, Auction} from "../DataStructure/Storage.sol";
import {ONE, protocolStorage, supplyPositionStorage} from "../DataStructure/Global.sol";
import {RayMath} from "../utils/RayMath.sol";
import {Erc20CheckedTransfer} from "../utils/Erc20CheckedTransfer.sol";
import {SafeMint} from "../SupplyPositionLogic/SafeMint.sol";
import {RequestedAmountTooHigh, UnsafeAmountLent, MultipleOffersUsed, ShareMatchedIsTooLow} from "../DataStructure/Errors.sol";
abstract contract BorrowHandlers is IBorrowHandlers, BorrowCheckers, SafeMint {
using RayMath for uint256;
using RayMath for Ray;
using Erc20CheckedTransfer for IERC20;
Ray private immutable minShareLent;
constructor() {
minShareLent = ONE.div(100_000_000);
}
function useOffer(
OfferArg memory arg,
CollateralState memory collatState
) internal view returns (CollateralState memory, address ) {
address signer = checkOfferArg(arg);
Ray shareMatched;
checkCollateral(arg.offer, collatState.nft);
shareMatched = arg.amount.div(arg.offer.loanToValue);
if (shareMatched.lt(minShareLent)) {
revert ShareMatchedIsTooLow(arg.offer, arg.amount);
}
collatState.matched = collatState.matched.add(shareMatched);
if (collatState.matched.gt(ONE)) {
revert RequestedAmountTooHigh(
arg.amount,
arg.offer.loanToValue.mul(ONE.sub(collatState.matched.sub(shareMatched))),
arg.offer
);
}
return (collatState, signer);
}
function useCollateral(
OfferArg[] memory args,
address from,
NFToken memory nft
) internal returns (Loan memory loan) {
address signer;
CollateralState memory collatState = initializedCollateralState(args[0], from, nft);
if (args.length > 1) {
revert MultipleOffersUsed();
}
(collatState, signer) = useOffer(args[0], collatState);
uint256 lent = args[0].amount;
if (lent > 1e40) {
revert UnsafeAmountLent(lent);
}
loan = initializedLoan(collatState, from, nft, lent);
protocolStorage().loan[collatState.loanId] = loan;
collatState.assetLent.checkedTransferFrom(signer, collatState.from, lent);
safeMint(signer, Provision({amount: lent, share: collatState.matched, loanId: collatState.loanId}));
emit Borrow(collatState.loanId, abi.encode(loan));
}
function initializedCollateralState(
OfferArg memory firstOfferArg,
address from,
NFToken memory nft
) internal returns (CollateralState memory) {
return
CollateralState({
matched: Ray.wrap(0),
assetLent: firstOfferArg.offer.assetToLend,
tranche: firstOfferArg.offer.tranche,
minOfferDuration: firstOfferArg.offer.duration,
minOfferLoanToValue: firstOfferArg.offer.loanToValue,
maxOfferLoanToValue: firstOfferArg.offer.loanToValue,
from: from,
nft: nft,
loanId: ++protocolStorage().nbOfLoans
});
}
function initializedLoan(
CollateralState memory collatState,
address from,
NFToken memory nft,
uint256 lent
) internal view returns (Loan memory) {
Protocol storage proto = protocolStorage();
uint256 endDate = block.timestamp + collatState.minOfferDuration;
Payment memory notPaid;
notPaid.minInterestsToRepay = proto.minOfferCost[collatState.assetLent];
return
Loan({
assetLent: collatState.assetLent,
lent: lent,
shareLent: collatState.matched,
startDate: block.timestamp,
endDate: endDate,
auction: Auction({duration: proto.auction.duration, priceFactor: proto.auction.priceFactor}),
interestPerSecond: proto.tranche[collatState.tranche],
borrower: from,
collateral: nft,
payment: notPaid
});
}
}
文件 6 的 36:ClaimFacet.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IClaimFacet} from "./interface/IClaimFacet.sol";
import {BorrowerAlreadyClaimed, LoanNotRepaidOrLiquidatedYet, NotBorrowerOfTheLoan} from "./DataStructure/Errors.sol";
import {ERC721CallerIsNotOwner} from "./DataStructure/ERC721Errors.sol";
import {Loan, Protocol, Provision, SupplyPosition} from "./DataStructure/Storage.sol";
import {ONE, protocolStorage, supplyPositionStorage} from "./DataStructure/Global.sol";
import {Ray} from "./DataStructure/Objects.sol";
import {RayMath} from "./utils/RayMath.sol";
import {Erc20CheckedTransfer} from "./utils/Erc20CheckedTransfer.sol";
import {SafeMint} from "./SupplyPositionLogic/SafeMint.sol";
contract ClaimFacet is IClaimFacet, SafeMint {
using RayMath for Ray;
using RayMath for uint256;
using Erc20CheckedTransfer for IERC20;
function claim(uint256[] calldata positionIds) external returns (uint256 sent) {
Protocol storage proto = protocolStorage();
SupplyPosition storage sp = supplyPositionStorage();
Loan storage loan;
Provision storage provision;
uint256 loanId;
uint256 sentTemp;
for (uint256 i = 0; i < positionIds.length; i++) {
if (sp.owner[positionIds[i]] != msg.sender) {
revert ERC721CallerIsNotOwner();
}
_burn(positionIds[i]);
provision = sp.provision[positionIds[i]];
loanId = provision.loanId;
loan = proto.loan[loanId];
if (loan.payment.liquidated) {
sentTemp = sendShareOfSaleAsSupplier(loan, provision);
} else {
if (loan.payment.paid == 0) {
revert LoanNotRepaidOrLiquidatedYet(loanId);
}
sentTemp = sendInterests(loan, provision);
}
emit Claim(msg.sender, sentTemp, loanId);
sent += sentTemp;
}
}
function claimAsBorrower(uint256[] calldata loanIds) external returns (uint256 sent) {
Protocol storage proto = protocolStorage();
Loan storage loan;
uint256 sentTemp;
uint256 loanId;
for (uint256 i = 0; i < loanIds.length; i++) {
loanId = loanIds[i];
loan = proto.loan[loanId];
if (loan.borrower != msg.sender) {
revert NotBorrowerOfTheLoan(loanId);
}
if (loan.payment.borrowerClaimed) {
revert BorrowerAlreadyClaimed(loanId);
}
if (loan.payment.liquidated) {
loan.payment.borrowerClaimed = true;
sentTemp = loan.payment.paid.mul(ONE.sub(loan.shareLent));
} else {
revert LoanNotRepaidOrLiquidatedYet(loanId);
}
if (sentTemp > 0) {
loan.assetLent.checkedTransfer(msg.sender, sentTemp);
sent += sentTemp;
emit Claim(msg.sender, sentTemp, loanId);
}
}
}
function sendInterests(Loan storage loan, Provision storage provision) internal returns (uint256 sent) {
uint256 interests = loan.payment.paid - loan.lent;
if (interests == loan.payment.minInterestsToRepay) {
sent = provision.amount + interests;
} else {
sent = provision.amount + (interests * (provision.amount)) / loan.lent;
}
loan.assetLent.checkedTransfer(msg.sender, sent);
}
function sendShareOfSaleAsSupplier(Loan storage loan, Provision storage provision) internal returns (uint256 sent) {
sent = loan.payment.paid.mul(provision.share);
loan.assetLent.checkedTransfer(msg.sender, sent);
}
}
文件 7 的 36:ContractsCreator.sol
pragma solidity 0.8.18;
import {OwnershipFacet} from "diamond/contracts/facets/OwnershipFacet.sol";
import {DiamondCutFacet} from "diamond/contracts/facets/DiamondCutFacet.sol";
import {IDiamond} from "diamond/contracts/interfaces/IDiamond.sol";
import {IDiamondCut} from "diamond/contracts/interfaces/IDiamondCut.sol";
import {DiamondLoupeFacet} from "diamond/contracts/facets/DiamondLoupeFacet.sol";
import {AdminFacet} from "./AdminFacet.sol";
import {AuctionFacet} from "./AuctionFacet.sol";
import {BorrowFacet} from "./BorrowFacet.sol";
import {ClaimFacet} from "./ClaimFacet.sol";
import {Initializer} from "./Initializer.sol";
import {ProtocolFacet} from "./ProtocolFacet.sol";
import {RepayFacet} from "./RepayFacet.sol";
import {SupplyPositionFacet} from "./SupplyPositionFacet.sol";
import {adminFS, auctionFS, claimFS, borrowFS, cutFS, loupeFS, protoFS, ownershipFS, repayFS, supplyPositionFS} from "./utils/FuncSelectors.h.sol";
contract ContractsCreator {
Initializer internal initializer;
DiamondCutFacet internal cut;
OwnershipFacet internal ownership;
DiamondLoupeFacet internal loupe;
AdminFacet internal admin;
BorrowFacet internal borrow;
SupplyPositionFacet internal supplyPosition;
ProtocolFacet internal protocol;
RepayFacet internal repay;
AuctionFacet internal auction;
ClaimFacet internal claim;
function createContracts() internal {
admin = new AdminFacet();
cut = new DiamondCutFacet();
loupe = new DiamondLoupeFacet();
ownership = new OwnershipFacet();
repay = new RepayFacet();
borrow = new BorrowFacet();
supplyPosition = new SupplyPositionFacet();
protocol = new ProtocolFacet();
initializer = new Initializer();
auction = new AuctionFacet();
claim = new ClaimFacet();
}
function getFacetCuts() internal view returns (IDiamondCut.FacetCut[] memory) {
IDiamondCut.FacetCut[] memory facetCuts = new IDiamondCut.FacetCut[](10);
facetCuts[0] = getAddFacetCut(address(loupe), loupeFS());
facetCuts[1] = getAddFacetCut(address(ownership), ownershipFS());
facetCuts[2] = getAddFacetCut(address(cut), cutFS());
facetCuts[3] = getAddFacetCut(address(borrow), borrowFS());
facetCuts[4] = getAddFacetCut(address(supplyPosition), supplyPositionFS());
facetCuts[5] = getAddFacetCut(address(protocol), protoFS());
facetCuts[6] = getAddFacetCut(address(repay), repayFS());
facetCuts[7] = getAddFacetCut(address(auction), auctionFS());
facetCuts[8] = getAddFacetCut(address(claim), claimFS());
facetCuts[9] = getAddFacetCut(address(admin), adminFS());
return facetCuts;
}
function getAddFacetCut(
address facet,
bytes4[] memory selectors
) internal pure returns (IDiamondCut.FacetCut memory) {
return
IDiamond.FacetCut({facetAddress: facet, action: IDiamond.FacetCutAction.Add, functionSelectors: selectors});
}
function getUpgradeFacetCut(
address facet,
bytes4[] memory selectors
) internal pure returns (IDiamondCut.FacetCut memory) {
return
IDiamond.FacetCut({
facetAddress: facet,
action: IDiamond.FacetCutAction.Replace,
functionSelectors: selectors
});
}
function getRemoveFacetCut(bytes4[] memory selectors) internal pure returns (IDiamondCut.FacetCut memory) {
return
IDiamond.FacetCut({
facetAddress: address(0),
action: IDiamond.FacetCutAction.Remove,
functionSelectors: selectors
});
}
}
文件 8 的 36:DeployEagle.s.sol
pragma solidity 0.8.18;
import {console} from "forge-std/console.sol";
import {Script} from "forge-std/Script.sol";
import {Diamond, DiamondArgs} from "diamond/contracts/Diamond.sol";
import {DiamondCutFacet} from "diamond/contracts/facets/DiamondCutFacet.sol";
import {OwnershipFacet} from "diamond/contracts/facets/OwnershipFacet.sol";
import {DiamondLoupeFacet} from "diamond/contracts/facets/DiamondLoupeFacet.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IDiamondCut} from "diamond/contracts/interfaces/IDiamondCut.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {LibDiamond} from "diamond/contracts/libraries/LibDiamond.sol";
import {IERC173} from "diamond/contracts/interfaces/IERC173.sol";
import {IERC165} from "diamond/contracts/interfaces/IERC165.sol";
import {IDiamondLoupe} from "diamond/contracts/interfaces/IDiamondLoupe.sol";
import {DiamondERC721} from "../SupplyPositionLogic/DiamondERC721.sol";
import {supplyPositionStorage} from "../DataStructure/Global.sol";
import {SupplyPosition} from "../DataStructure/Storage.sol";
import {ContractsCreator} from "../ContractsCreator.sol";
import {KairosEagleFacet} from "./KairosEagle.sol";
import {loupeFS, ownershipFS, cutFS, getSelector} from "../utils/FuncSelectors.h.sol";
contract KairosEagle is Diamond {
constructor(IDiamondCut.FacetCut[] memory cuts, DiamondArgs memory _args) Diamond(cuts, _args) {}
}
contract EagleInitializer {
function init() external {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
ds.supportedInterfaces[type(IERC165).interfaceId] = true;
ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true;
ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true;
ds.supportedInterfaces[type(IERC173).interfaceId] = true;
ds.supportedInterfaces[type(IERC721).interfaceId] = true;
SupplyPosition storage sp = supplyPositionStorage();
sp.name = "Kairos Eagle";
sp.symbol = "KEG";
}
}
contract DeployEagle is Script, ContractsCreator {
KairosEagleFacet internal eagle;
EagleInitializer internal eagleInitializer;
function run() public {
uint256 privateKey = vm.envUint("DEPLOYER_KEY");
vm.startBroadcast(privateKey);
cut = DiamondCutFacet(0xc5126Eb24430d459Cd810B882C3AD286D380B6bD);
loupe = DiamondLoupeFacet(0x47Ae465f27c69659e7D5012c4e2a6732A8aA8370);
ownership = OwnershipFacet(0xC433107B136b6ea1Abb1b99E0A0a39459687599c);
eagle = new KairosEagleFacet(IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2));
eagleInitializer = new EagleInitializer();
DiamondArgs memory args = DiamondArgs({
owner: vm.addr(privateKey),
init: address(eagleInitializer),
initCalldata: abi.encodeWithSelector(eagleInitializer.init.selector)
});
new KairosEagle(eagleFacetCuts(), args);
console.logBytes(abi.encode(eagleFacetCuts(), args));
}
function eagleFacetCuts() internal view returns (IDiamondCut.FacetCut[] memory) {
IDiamondCut.FacetCut[] memory facetCuts = new IDiamondCut.FacetCut[](4);
facetCuts[0] = getAddFacetCut(address(loupe), loupeFS());
facetCuts[1] = getAddFacetCut(address(ownership), ownershipFS());
facetCuts[2] = getAddFacetCut(address(cut), cutFS());
facetCuts[3] = getAddFacetCut(address(eagle), eagleFS());
return facetCuts;
}
function eagleFS() internal pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](18);
functionSelectors[0] = IERC721.balanceOf.selector;
functionSelectors[1] = IERC721.ownerOf.selector;
functionSelectors[2] = DiamondERC721.name.selector;
functionSelectors[3] = DiamondERC721.symbol.selector;
functionSelectors[4] = IERC721.approve.selector;
functionSelectors[5] = IERC721.getApproved.selector;
functionSelectors[6] = IERC721.setApprovalForAll.selector;
functionSelectors[7] = IERC721.isApprovedForAll.selector;
functionSelectors[8] = IERC721.transferFrom.selector;
functionSelectors[9] = getSelector("safeTransferFrom(address,address,uint256)");
functionSelectors[10] = getSelector("safeTransferFrom(address,address,uint256,bytes)");
functionSelectors[11] = KairosEagleFacet.buy.selector;
functionSelectors[12] = KairosEagleFacet.setRaffle.selector;
functionSelectors[13] = KairosEagleFacet.withdrawFunds.selector;
functionSelectors[14] = KairosEagleFacet.setBaseMetadataUri.selector;
functionSelectors[15] = KairosEagleFacet.tokenURI.selector;
functionSelectors[16] = KairosEagleFacet.totalSupply.selector;
functionSelectors[17] = KairosEagleFacet.getHardCap.selector;
return functionSelectors;
}
}
文件 9 的 36:DiamondERC721.sol
pragma solidity 0.8.18;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {NFTUtils} from "./NFTUtils.sol";
import {SupplyPosition} from "../DataStructure/Storage.sol";
import {supplyPositionStorage} from "../DataStructure/Global.sol";
import {ERC721AddressZeroIsNotAValidOwner, ERC721ApprovalToCurrentOwner, ERC721CallerIsNotOwnerNorApproved, ERC721CallerIsNotOwnerNorApprovedForAll} from "../DataStructure/ERC721Errors.sol";
abstract contract DiamondERC721 is IERC721, NFTUtils {
using Address for address;
error Unauthorized();
function supportsInterface(bytes4 interfaceId) public view returns (bool) {}
function emitTransfer(address from, address to, uint256 tokenId) internal virtual override {
emit Transfer(from, to, tokenId);
}
function emitApproval(address owner, address approved, uint256 tokenId) internal virtual override {
emit Approval(owner, approved, tokenId);
}
function emitApprovalForAll(address owner, address operator, bool approved) internal virtual override {
emit ApprovalForAll(owner, operator, approved);
}
function balanceOf(address owner) public view virtual returns (uint256) {
SupplyPosition storage sp = supplyPositionStorage();
if (owner == address(0)) {
revert ERC721AddressZeroIsNotAValidOwner();
}
return sp.balance[owner];
}
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _ownerOf(tokenId);
}
function name() public view virtual returns (string memory) {
SupplyPosition storage sp = supplyPositionStorage();
return sp.name;
}
function symbol() public view virtual returns (string memory) {
SupplyPosition storage sp = supplyPositionStorage();
return sp.symbol;
}
function approve(address to, uint256 tokenId) public virtual {
address owner = ownerOf(tokenId);
if (to == owner) {
revert ERC721ApprovalToCurrentOwner();
}
if (msg.sender != owner && !isApprovedForAll(owner, msg.sender)) {
revert ERC721CallerIsNotOwnerNorApprovedForAll();
}
_approve(to, tokenId);
}
function getApproved(uint256 tokenId) public view virtual returns (address) {
return _getApproved(tokenId);
}
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(msg.sender, operator, approved);
}
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
return _isApprovedForAll(owner, operator);
}
function transferFrom(address from, address to, uint256 tokenId) public virtual {
if (!_isApprovedOrOwner(msg.sender, tokenId)) {
revert ERC721CallerIsNotOwnerNorApproved();
}
_transfer(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
if (!_isApprovedOrOwner(msg.sender, tokenId)) {
revert ERC721CallerIsNotOwnerNorApproved();
}
_safeTransfer(from, to, tokenId, data);
}
}
文件 10 的 36:ERC721Errors.sol
pragma solidity 0.8.18;
error ERC721AddressZeroIsNotAValidOwner();
error ERC721InvalidTokenId();
error ERC721ApprovalToCurrentOwner();
error ERC721CallerIsNotOwnerNorApprovedForAll();
error ERC721CallerIsNotOwnerNorApproved();
error ERC721TransferToNonERC721ReceiverImplementer();
error ERC721MintToTheZeroAddress();
error ERC721TokenAlreadyMinted();
error ERC721TransferFromIncorrectOwner();
error ERC721TransferToTheZeroAddress();
error ERC721ApproveToCaller();
error ERC721CallerIsNotOwner();
文件 11 的 36:Erc20CheckedTransfer.sol
pragma solidity 0.8.18;
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC20TransferFailed} from "../DataStructure/Errors.sol";
library Erc20CheckedTransfer {
using SafeERC20 for IERC20;
modifier skipZeroAmount(uint256 amount) {
if (amount > 0) {
_;
}
}
function checkedTransferFrom(
IERC20 currency,
address from,
address to,
uint256 amount
) internal skipZeroAmount(amount) {
currency.safeTransferFrom(from, to, amount);
}
function checkedTransfer(IERC20 currency, address to, uint256 amount) internal skipZeroAmount(amount) {
currency.safeTransfer(to, amount);
}
}
文件 12 的 36:Errors.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {NFToken, Offer} from "./Objects.sol";
error BadCollateral(Offer offer, NFToken providedNft);
error ERC20TransferFailed(IERC20 token, address from, address to);
error OfferHasExpired(Offer offer, uint256 expirationDate);
error RequestedAmountIsUnderMinimum(Offer offer, uint256 requested, uint256 lowerBound);
error RequestedAmountTooHigh(uint256 requested, uint256 offered, Offer offer);
error LoanAlreadyRepaid(uint256 loanId);
error LoanNotRepaidOrLiquidatedYet(uint256 loanId);
error NotBorrowerOfTheLoan(uint256 loanId);
error BorrowerAlreadyClaimed(uint256 loanId);
error CallerIsNotOwner(address admin);
error InvalidTranche(uint256 nbOfTranches);
error CollateralIsNotLiquidableYet(uint256 endDate, uint256 loanId);
error UnsafeAmountLent(uint256 lent);
error MultipleOffersUsed();
error PriceOverMaximum(uint256 maxPrice, uint256 price);
error CurrencyNotSupported(IERC20 currency);
error ShareMatchedIsTooLow(Offer offer, uint256 requested);
文件 13 的 36:FuncSelectors.h.sol
pragma solidity 0.8.18;
import {IDiamondCut} from "diamond/contracts/interfaces/IDiamondCut.sol";
import {IDiamondLoupe} from "diamond/contracts/interfaces/IDiamondLoupe.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC165} from "diamond/contracts/interfaces/IERC165.sol";
import {OwnershipFacet} from "diamond/contracts/facets/OwnershipFacet.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IAuctionFacet} from "../interface/IAuctionFacet.sol";
import {IBorrowFacet} from "../interface/IBorrowFacet.sol";
import {IClaimFacet} from "../interface/IClaimFacet.sol";
import {IProtocolFacet} from "../interface/IProtocolFacet.sol";
import {IRepayFacet} from "../interface/IRepayFacet.sol";
import {IAdminFacet} from "../interface/IAdminFacet.sol";
import {ISignature} from "../interface/ISignature.sol";
import {SupplyPositionFacet} from "../SupplyPositionFacet.sol";
import {DiamondERC721} from "../SupplyPositionLogic/DiamondERC721.sol";
function loupeFS() pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](5);
functionSelectors[0] = IDiamondLoupe.facets.selector;
functionSelectors[1] = IDiamondLoupe.facetFunctionSelectors.selector;
functionSelectors[2] = IDiamondLoupe.facetAddresses.selector;
functionSelectors[3] = IDiamondLoupe.facetAddress.selector;
functionSelectors[4] = IERC165.supportsInterface.selector;
return functionSelectors;
}
function ownershipFS() pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](2);
functionSelectors[0] = OwnershipFacet.transferOwnership.selector;
functionSelectors[1] = OwnershipFacet.owner.selector;
return functionSelectors;
}
function cutFS() pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](1);
functionSelectors[0] = IDiamondCut.diamondCut.selector;
return functionSelectors;
}
function borrowFS() pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](3);
functionSelectors[0] = IERC721Receiver.onERC721Received.selector;
functionSelectors[1] = ISignature.offerDigest.selector;
functionSelectors[2] = IBorrowFacet.borrow.selector;
return functionSelectors;
}
function supplyPositionFS() pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](13);
functionSelectors[0] = IERC721.balanceOf.selector;
functionSelectors[1] = IERC721.ownerOf.selector;
functionSelectors[2] = DiamondERC721.name.selector;
functionSelectors[3] = DiamondERC721.symbol.selector;
functionSelectors[4] = IERC721.approve.selector;
functionSelectors[5] = IERC721.getApproved.selector;
functionSelectors[6] = IERC721.setApprovalForAll.selector;
functionSelectors[7] = IERC721.isApprovedForAll.selector;
functionSelectors[8] = IERC721.transferFrom.selector;
functionSelectors[9] = getSelector("safeTransferFrom(address,address,uint256)");
functionSelectors[10] = getSelector("safeTransferFrom(address,address,uint256,bytes)");
functionSelectors[11] = SupplyPositionFacet.position.selector;
functionSelectors[12] = SupplyPositionFacet.totalSupply.selector;
return functionSelectors;
}
function protoFS() pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](4);
functionSelectors[0] = IProtocolFacet.getRateOfTranche.selector;
functionSelectors[1] = IProtocolFacet.getParameters.selector;
functionSelectors[2] = IProtocolFacet.getLoan.selector;
functionSelectors[3] = IProtocolFacet.getMinOfferCostAndBorrowableAmount.selector;
return functionSelectors;
}
function repayFS() pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](1);
functionSelectors[0] = IRepayFacet.repay.selector;
return functionSelectors;
}
function auctionFS() pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](2);
functionSelectors[0] = IAuctionFacet.buy.selector;
functionSelectors[1] = IAuctionFacet.price.selector;
return functionSelectors;
}
function claimFS() pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](2);
functionSelectors[0] = IClaimFacet.claim.selector;
functionSelectors[1] = IClaimFacet.claimAsBorrower.selector;
return functionSelectors;
}
function adminFS() pure returns (bytes4[] memory) {
bytes4[] memory functionSelectors = new bytes4[](5);
functionSelectors[0] = IAdminFacet.setAuctionDuration.selector;
functionSelectors[1] = IAdminFacet.setAuctionPriceFactor.selector;
functionSelectors[2] = IAdminFacet.createTranche.selector;
functionSelectors[3] = IAdminFacet.setMinOfferCost.selector;
functionSelectors[4] = IAdminFacet.setBorrowAmountPerOfferLowerBound.selector;
return functionSelectors;
}
function getSelector(string memory _func) pure returns (bytes4) {
return bytes4(keccak256(bytes(_func)));
}
文件 14 的 36:Global.sol
pragma solidity 0.8.18;
import {Protocol, SupplyPosition} from "./Storage.sol";
import {Ray} from "./Objects.sol";
bytes32 constant PROTOCOL_SP = keccak256("eth.kairosloan.protocol.v1.0");
bytes32 constant SUPPLY_SP = keccak256("eth.kairosloan.supply-position.v1.0");
uint256 constant RAY = 1e27;
Ray constant ONE = Ray.wrap(RAY);
Ray constant ZERO = Ray.wrap(0);
function protocolStorage() pure returns (Protocol storage protocol) {
bytes32 position = PROTOCOL_SP;
assembly {
protocol.slot := position
}
}
function supplyPositionStorage() pure returns (SupplyPosition storage sp) {
bytes32 position = SUPPLY_SP;
assembly {
sp.slot := position
}
}
文件 15 的 36:IAdminFacet.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ray} from "../DataStructure/Objects.sol";
interface IAdminFacet {
event NewAuctionDuration(uint256 indexed newAuctionDuration);
event NewAuctionPriceFactor(Ray indexed newAuctionPriceFactor);
event NewTranche(Ray indexed tranche, uint256 indexed newTrancheId);
event NewMininimumOfferCost(IERC20 indexed currency, uint256 indexed newMinOfferCost);
event NewBorrowAmountPerOfferLowerBound(IERC20 indexed currency, uint256 indexed newLowerBound);
function setAuctionDuration(uint256 newAuctionDuration) external;
function setAuctionPriceFactor(Ray newAuctionPriceFactor) external;
function createTranche(Ray newTranche) external returns (uint256 newTrancheId);
function setMinOfferCost(IERC20 currency, uint256 newMinOfferCost) external;
function setBorrowAmountPerOfferLowerBound(IERC20 currency, uint256 newLowerBound) external;
}
文件 16 的 36:IAuctionFacet.sol
pragma solidity 0.8.18;
import {BuyArg} from "../DataStructure/Objects.sol";
interface IAuctionFacet {
event Buy(uint256 indexed loanId, bytes args);
function buy(BuyArg[] memory args) external;
function price(uint256 loanId) external view returns (uint256);
}
文件 17 的 36:IBorrowCheckers.sol
pragma solidity 0.8.18;
import {ISignature} from "./ISignature.sol";
interface IBorrowCheckers is ISignature {
}
文件 18 的 36:IBorrowFacet.sol
pragma solidity 0.8.18;
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IBorrowHandlers} from "./IBorrowHandlers.sol";
import {BorrowArg} from "../DataStructure/Objects.sol";
interface IBorrowFacet is IBorrowHandlers, IERC721Receiver {
function borrow(BorrowArg[] calldata args) external;
}
文件 19 的 36:IBorrowHandlers.sol
pragma solidity 0.8.18;
import {IBorrowCheckers} from "./IBorrowCheckers.sol";
interface IBorrowHandlers is IBorrowCheckers {
event Borrow(uint256 indexed loanId, bytes loan);
}
文件 20 的 36:IClaimFacet.sol
pragma solidity 0.8.18;
interface IClaimEmitter {
event Claim(address indexed claimant, uint256 indexed claimed, uint256 indexed loanId);
}
interface IClaimFacet is IClaimEmitter {
function claim(uint256[] calldata positionIds) external returns (uint256 sent);
function claimAsBorrower(uint256[] calldata loanIds) external returns (uint256 sent);
}
文件 21 的 36:IOwnershipFacet.sol
pragma solidity 0.8.18;
interface IOwnershipFacet {
function transferOwnership(address _newOwner) external;
function owner() external view returns (address owner_);
}
文件 22 的 36:IProtocolFacet.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ray} from "../DataStructure/Objects.sol";
import {Loan} from "../DataStructure/Storage.sol";
interface IProtocolFacet {
function getRateOfTranche(uint256 id) external view returns (Ray rate);
function getParameters()
external
view
returns (Ray auctionPriceFactor, uint256 auctionDuration, uint256 nbOfLoans, uint256 nbOfTranches);
function getLoan(uint256 id) external view returns (Loan memory);
function getMinOfferCostAndBorrowableAmount(
IERC20 currency
) external view returns (uint256 minOfferCost, uint256 offerBorrowAmountLowerBound);
}
文件 23 的 36:IRepayFacet.sol
pragma solidity 0.8.18;
interface IRepayFacet {
event Repay(uint256 indexed loanId);
function repay(uint256[] memory loanIds) external;
}
文件 24 的 36:ISignature.sol
pragma solidity 0.8.18;
import {Offer} from "../DataStructure/Objects.sol";
interface ISignature {
function offerDigest(Offer memory offer) external view returns (bytes32);
}
文件 25 的 36:Initializer.sol
pragma solidity 0.8.18;
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {LibDiamond} from "diamond/contracts/libraries/LibDiamond.sol";
import {IDiamondLoupe} from "diamond/contracts/interfaces/IDiamondLoupe.sol";
import {IDiamondCut} from "diamond/contracts/interfaces/IDiamondCut.sol";
import {IERC173} from "diamond/contracts/interfaces/IERC173.sol";
import {IERC165} from "diamond/contracts/interfaces/IERC165.sol";
import {ONE, protocolStorage, supplyPositionStorage} from "./DataStructure/Global.sol";
import {Ray} from "./DataStructure/Objects.sol";
import {Protocol, SupplyPosition} from "./DataStructure/Storage.sol";
import {RayMath} from "./utils/RayMath.sol";
contract Initializer {
using RayMath for Ray;
function init() external {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
ds.supportedInterfaces[type(IERC165).interfaceId] = true;
ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true;
ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true;
ds.supportedInterfaces[type(IERC173).interfaceId] = true;
Protocol storage proto = protocolStorage();
proto.tranche[0] = ONE.div(10).mul(4).div(365 days);
proto.nbOfTranches = 1;
proto.auction.priceFactor = ONE.mul(3);
proto.auction.duration = 3 days;
SupplyPosition storage sp = supplyPositionStorage();
sp.name = "Kairos Supply Position";
sp.symbol = "KSP";
ds.supportedInterfaces[type(IERC721).interfaceId] = true;
}
}
文件 26 的 36:KairosEagle.sol
pragma solidity 0.8.18;
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {IOwnershipFacet} from "../interface/IOwnershipFacet.sol";
import {DiamondERC721} from "../SupplyPositionLogic/DiamondERC721.sol";
import {SupplyPosition} from "../DataStructure/Storage.sol";
import {supplyPositionStorage} from "../DataStructure/Global.sol";
import {CallerIsNotOwner} from "../DataStructure/Errors.sol";
struct Raffle {
bytes32 whitelistMerkleRoot;
uint256 mintCap;
uint256 mintCapPerAddress;
uint256 wethUnitPrice;
uint256 amountMinted;
mapping(address => uint256) amountMintedBy;
}
struct EagleStorage {
string baseMetadataUri;
mapping(uint256 => Raffle) raffle;
}
bytes32 constant EAGLE_STORAGE_POSITION = keccak256("eth.kairosloan.eagle.v1.0");
function eagleStorage() pure returns (EagleStorage storage eagle) {
bytes32 position = EAGLE_STORAGE_POSITION;
assembly {
eagle.slot := position
}
}
error InvalidMerkleProof();
error HardCapExceeded();
error RaffleMintCapExceeded();
error AccountMintCapExceeded();
contract KairosEagleFacet is DiamondERC721 {
using MerkleProof for bytes32[];
using Strings for uint256;
IERC20 internal immutable wEth;
uint256 internal constant HARD_CAP = 555;
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
modifier onlyOwner() {
address admin = IOwnershipFacet(address(this)).owner();
if (msg.sender != admin) {
revert CallerIsNotOwner(admin);
}
_;
}
constructor(IERC20 _weth) {
wEth = _weth;
}
function buy(bytes32[] calldata merkleProof, uint256 raffleId) external returns (uint256) {
Raffle storage raffle = eagleStorage().raffle[raffleId];
bytes32 merkleRoot = raffle.whitelistMerkleRoot;
uint256 amountMintedByCaller = ++raffle.amountMintedBy[msg.sender];
uint256 amountMintedInRaffle = ++raffle.amountMinted;
if (amountMintedByCaller > raffle.mintCapPerAddress) {
revert AccountMintCapExceeded();
}
if (amountMintedInRaffle > raffle.mintCap) {
revert RaffleMintCapExceeded();
}
if (!merkleProof.verifyCalldata(merkleRoot, keccak256(abi.encode(msg.sender)))) {
revert InvalidMerkleProof();
}
wEth.transferFrom(msg.sender, address(this), raffle.wethUnitPrice);
return mint();
}
function setRaffle(
uint256 raffleId,
bytes32 whitelistMerkleRoot,
uint256 mintCapPerAddress,
uint256 mintCap,
uint256 wethUnitPrice
) external onlyOwner {
Raffle storage raffle = eagleStorage().raffle[raffleId];
raffle.whitelistMerkleRoot = whitelistMerkleRoot;
raffle.mintCapPerAddress = mintCapPerAddress;
raffle.mintCap = mintCap;
raffle.wethUnitPrice = wethUnitPrice;
}
function withdrawFunds() external onlyOwner {
wEth.transfer(msg.sender, wEth.balanceOf(address(this)));
}
function setBaseMetadataUri(string calldata baseMetadataUri) external onlyOwner {
eagleStorage().baseMetadataUri = baseMetadataUri;
emit BatchMetadataUpdate(1, type(uint256).max);
}
function tokenURI(uint256 tokenId) external view returns (string memory) {
return string(abi.encodePacked(eagleStorage().baseMetadataUri, tokenId.toString()));
}
function totalSupply() external view returns (uint256) {
return supplyPositionStorage().totalSupply;
}
function getHardCap() external pure returns (uint256) {
return HARD_CAP;
}
function mint() internal returns (uint256 tokenId) {
tokenId = ++supplyPositionStorage().totalSupply;
if (tokenId >= HARD_CAP) {
revert HardCapExceeded();
}
_safeMint(msg.sender, tokenId);
}
}
文件 27 的 36:NFTUtils.sol
pragma solidity 0.8.18;
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {supplyPositionStorage} from "../DataStructure/Global.sol";
import {SupplyPosition} from "../DataStructure/Storage.sol";
import {ERC721ApproveToCaller, ERC721InvalidTokenId, ERC721TokenAlreadyMinted, ERC721MintToTheZeroAddress, ERC721TransferFromIncorrectOwner, ERC721TransferToNonERC721ReceiverImplementer, ERC721TransferToTheZeroAddress} from "../DataStructure/ERC721Errors.sol";
abstract contract NFTUtils {
using Address for address;
function emitTransfer(address from, address to, uint256 tokenId) internal virtual;
function emitApproval(address owner, address approved, uint256 tokenId) internal virtual;
function emitApprovalForAll(address owner, address operator, bool approved) internal virtual;
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory data
) internal returns (bool) {
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721TransferToNonERC721ReceiverImplementer();
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal {
_transfer(from, to, tokenId);
if (!_checkOnERC721Received(from, to, tokenId, data)) {
revert ERC721TransferToNonERC721ReceiverImplementer();
}
}
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
function _safeMint(address to, uint256 tokenId, bytes memory data) internal {
_mint(to, tokenId);
if (!_checkOnERC721Received(address(0), to, tokenId, data)) {
revert ERC721TransferToNonERC721ReceiverImplementer();
}
}
function _mint(address to, uint256 tokenId) internal {
SupplyPosition storage sp = supplyPositionStorage();
if (to == address(0)) {
revert ERC721MintToTheZeroAddress();
}
if (_exists(tokenId)) {
revert ERC721TokenAlreadyMinted();
}
sp.balance[to] += 1;
sp.owner[tokenId] = to;
emitTransfer(address(0), to, tokenId);
}
function _burn(uint256 tokenId) internal {
SupplyPosition storage sp = supplyPositionStorage();
address owner = _ownerOf(tokenId);
_approve(address(0), tokenId);
sp.balance[owner] -= 1;
delete sp.owner[tokenId];
emitTransfer(owner, address(0), tokenId);
}
function _transfer(address from, address to, uint256 tokenId) internal {
SupplyPosition storage sp = supplyPositionStorage();
if (_ownerOf(tokenId) != from) {
revert ERC721TransferFromIncorrectOwner();
}
if (to == address(0)) {
revert ERC721TransferToTheZeroAddress();
}
_approve(address(0), tokenId);
sp.balance[from] -= 1;
sp.balance[to] += 1;
sp.owner[tokenId] = to;
emitTransfer(from, to, tokenId);
}
function _approve(address to, uint256 tokenId) internal {
SupplyPosition storage sp = supplyPositionStorage();
sp.tokenApproval[tokenId] = to;
emitApproval(_ownerOf(tokenId), to, tokenId);
}
function _setApprovalForAll(address owner, address operator, bool approved) internal {
SupplyPosition storage sp = supplyPositionStorage();
if (owner == operator) {
revert ERC721ApproveToCaller();
}
sp.operatorApproval[owner][operator] = approved;
emitApprovalForAll(owner, operator, approved);
}
function _exists(uint256 tokenId) internal view returns (bool) {
SupplyPosition storage sp = supplyPositionStorage();
return sp.owner[tokenId] != address(0);
}
function _ownerOf(uint256 tokenId) internal view returns (address) {
SupplyPosition storage sp = supplyPositionStorage();
address owner = sp.owner[tokenId];
if (owner == address(0)) {
revert ERC721InvalidTokenId();
}
return owner;
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
address owner = _ownerOf(tokenId);
return (spender == owner || _isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
}
function _getApproved(uint256 tokenId) internal view returns (address) {
if (!_exists(tokenId)) {
revert ERC721InvalidTokenId();
}
return supplyPositionStorage().tokenApproval[tokenId];
}
function _isApprovedForAll(address owner, address operator) internal view returns (bool) {
return supplyPositionStorage().operatorApproval[owner][operator];
}
}
文件 28 的 36:NFTokenUtils.sol
pragma solidity 0.8.18;
import {NFToken} from "../DataStructure/Objects.sol";
library NFTokenUtils {
function eq(NFToken memory a, NFToken memory b) internal pure returns (bool) {
return (a.id == b.id && a.implem == b.implem);
}
}
文件 29 的 36:Objects.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
type Ray is uint256;
struct BuyArg {
uint256 loanId;
address to;
uint256 maxPrice;
}
struct BorrowArg {
NFToken nft;
OfferArg[] args;
}
struct OfferArg {
bytes signature;
uint256 amount;
Offer offer;
}
struct CollateralState {
Ray matched;
IERC20 assetLent;
uint256 tranche;
uint256 minOfferDuration;
uint256 minOfferLoanToValue;
uint256 maxOfferLoanToValue;
address from;
NFToken nft;
uint256 loanId;
}
struct Offer {
IERC20 assetToLend;
uint256 loanToValue;
uint256 duration;
uint256 expirationDate;
uint256 tranche;
NFToken collateral;
}
struct NFToken {
IERC721 implem;
uint256 id;
}
文件 30 的 36:ProtocolFacet.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IProtocolFacet} from "./interface/IProtocolFacet.sol";
import {Loan, Protocol} from "./DataStructure/Storage.sol";
import {protocolStorage} from "./DataStructure/Global.sol";
import {Ray} from "./DataStructure/Objects.sol";
contract ProtocolFacet is IProtocolFacet {
function getRateOfTranche(uint256 id) external view returns (Ray rate) {
return protocolStorage().tranche[id];
}
function getParameters()
external
view
returns (Ray auctionPriceFactor, uint256 auctionDuration, uint256 nbOfLoans, uint256 nbOfTranches)
{
Protocol storage proto = protocolStorage();
auctionPriceFactor = proto.auction.priceFactor;
auctionDuration = proto.auction.duration;
nbOfLoans = proto.nbOfLoans;
nbOfTranches = proto.nbOfTranches;
}
function getLoan(uint256 id) external view returns (Loan memory) {
return protocolStorage().loan[id];
}
function getMinOfferCostAndBorrowableAmount(
IERC20 currency
) external view returns (uint256 minOfferCost, uint256 offerBorrowAmountLowerBound) {
Protocol storage proto = protocolStorage();
minOfferCost = proto.minOfferCost[currency];
offerBorrowAmountLowerBound = proto.offerBorrowAmountLowerBound[currency];
}
}
文件 31 的 36:RayMath.sol
pragma solidity 0.8.18;
import {RAY} from "../DataStructure/Global.sol";
import {Ray} from "../DataStructure/Objects.sol";
library RayMath {
function add(Ray a, Ray b) internal pure returns (Ray) {
return Ray.wrap(Ray.unwrap(a) + Ray.unwrap(b));
}
function sub(Ray a, Ray b) internal pure returns (Ray) {
return Ray.wrap(Ray.unwrap(a) - Ray.unwrap(b));
}
function mul(Ray a, Ray b) internal pure returns (Ray) {
return Ray.wrap((Ray.unwrap(a) * Ray.unwrap(b)) / RAY);
}
function mul(Ray a, uint256 b) internal pure returns (Ray) {
return Ray.wrap(Ray.unwrap(a) * b);
}
function mul(uint256 a, Ray b) internal pure returns (uint256) {
return (a * Ray.unwrap(b)) / RAY;
}
function div(Ray a, Ray b) internal pure returns (Ray) {
return Ray.wrap((Ray.unwrap(a) * RAY) / Ray.unwrap(b));
}
function div(Ray a, uint256 b) internal pure returns (Ray) {
return Ray.wrap(Ray.unwrap(a) / b);
}
function div(uint256 a, Ray b) internal pure returns (uint256) {
return (a * RAY) / Ray.unwrap(b);
}
function div(uint256 a, uint256 b) internal pure returns (Ray) {
return Ray.wrap((a * RAY) / b);
}
function lt(Ray a, Ray b) internal pure returns (bool) {
return Ray.unwrap(a) < Ray.unwrap(b);
}
function gt(Ray a, Ray b) internal pure returns (bool) {
return Ray.unwrap(a) > Ray.unwrap(b);
}
function gte(Ray a, Ray b) internal pure returns (bool) {
return Ray.unwrap(a) >= Ray.unwrap(b);
}
function eq(Ray a, Ray b) internal pure returns (bool) {
return Ray.unwrap(a) == Ray.unwrap(b);
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
}
文件 32 的 36:RepayFacet.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IRepayFacet} from "./interface/IRepayFacet.sol";
import {Loan, Protocol} from "./DataStructure/Storage.sol";
import {LoanAlreadyRepaid} from "./DataStructure/Errors.sol";
import {protocolStorage} from "./DataStructure/Global.sol";
import {Ray} from "./DataStructure/Objects.sol";
import {RayMath} from "./utils/RayMath.sol";
import {Erc20CheckedTransfer} from "./utils/Erc20CheckedTransfer.sol";
contract RepayFacet is IRepayFacet {
using RayMath for Ray;
using RayMath for uint256;
using Erc20CheckedTransfer for IERC20;
function repay(uint256[] memory loanIds) external {
Protocol storage proto = protocolStorage();
Loan storage loan;
uint256 lent;
uint256 interests;
uint256 toRepay;
for (uint256 i = 0; i < loanIds.length; i++) {
loan = proto.loan[loanIds[i]];
if (loan.payment.paid > 0 || loan.payment.borrowerClaimed || loan.payment.liquidated) {
revert LoanAlreadyRepaid(loanIds[i]);
}
lent = loan.lent;
interests = RayMath.max(
lent.mul(loan.interestPerSecond.mul(block.timestamp - loan.startDate)),
loan.payment.minInterestsToRepay
);
toRepay = lent + interests;
loan.payment.paid = toRepay;
loan.payment.borrowerClaimed = true;
loan.assetLent.checkedTransferFrom(msg.sender, address(this), toRepay);
loan.collateral.implem.safeTransferFrom(address(this), loan.borrower, loan.collateral.id);
emit Repay(loanIds[i]);
}
}
}
文件 33 的 36:SafeMint.sol
pragma solidity 0.8.18;
import {NFTUtils} from "./NFTUtils.sol";
import {Provision, SupplyPosition} from "../DataStructure/Storage.sol";
import {supplyPositionStorage} from "../DataStructure/Global.sol";
contract SafeMint is NFTUtils {
function safeMint(address to, Provision memory provision) internal returns (uint256 tokenId) {
SupplyPosition storage sp = supplyPositionStorage();
tokenId = ++sp.totalSupply;
sp.provision[tokenId] = provision;
_safeMint(to, tokenId);
}
function emitTransfer(address from, address to, uint256 tokenId) internal virtual override {}
function emitApproval(address owner, address approved, uint256 tokenId) internal virtual override {}
function emitApprovalForAll(address owner, address operator, bool approved) internal virtual override {}
}
文件 34 的 36:Signature.sol
pragma solidity 0.8.18;
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {ISignature} from "./interface/ISignature.sol";
import {NFToken} from "./DataStructure/Objects.sol";
import {Offer} from "./DataStructure/Objects.sol";
abstract contract Signature is ISignature, EIP712 {
bytes32 internal constant OFFER_TYPEHASH =
keccak256(
"Offer(address assetToLend,uint256 loanToValue,uint256 duration,"
"uint256 expirationDate,uint256 tranche,NFToken collateral)"
"NFToken(address implem,uint256 id)"
);
bytes32 internal constant NFTOKEN_TYPEHASH = keccak256("NFToken(address implem,uint256 id)");
constructor() EIP712("Kairos Loan protocol", "0.1") {}
function offerDigest(Offer memory offer) public view returns (bytes32) {
return _hashTypedDataV4(typeHashOffer(offer));
}
function typeHashNFToken(NFToken memory nft) internal pure returns (bytes32) {
return keccak256(abi.encode(NFTOKEN_TYPEHASH, nft));
}
function typeHashOffer(Offer memory offer) internal pure returns (bytes32) {
return
keccak256(
abi.encode(
OFFER_TYPEHASH,
offer.assetToLend,
offer.loanToValue,
offer.duration,
offer.expirationDate,
offer.tranche,
typeHashNFToken(offer.collateral)
)
);
}
}
文件 35 的 36:Storage.sol
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {NFToken, Ray} from "./Objects.sol";
struct Auction {
uint256 duration;
Ray priceFactor;
}
struct Protocol {
uint256 nbOfLoans;
uint256 nbOfTranches;
Auction auction;
mapping(uint256 => Ray) tranche;
mapping(uint256 => Loan) loan;
mapping(IERC20 => uint256) minOfferCost;
mapping(IERC20 => uint256) offerBorrowAmountLowerBound;
}
struct Loan {
IERC20 assetLent;
uint256 lent;
Ray shareLent;
uint256 startDate;
uint256 endDate;
Auction auction;
Ray interestPerSecond;
address borrower;
NFToken collateral;
Payment payment;
}
struct Payment {
uint256 paid;
uint256 minInterestsToRepay;
bool liquidated;
bool borrowerClaimed;
}
struct SupplyPosition {
string name;
string symbol;
uint256 totalSupply;
mapping(uint256 => address) owner;
mapping(address => uint256) balance;
mapping(uint256 => address) tokenApproval;
mapping(address => mapping(address => bool)) operatorApproval;
mapping(uint256 => Provision) provision;
}
struct Provision {
uint256 amount;
Ray share;
uint256 loanId;
}
文件 36 的 36:SupplyPositionFacet.sol
pragma solidity 0.8.18;
import {DiamondERC721} from "./SupplyPositionLogic/DiamondERC721.sol";
import {ERC721InvalidTokenId} from "./DataStructure/ERC721Errors.sol";
import {SupplyPosition, Provision} from "./DataStructure/Storage.sol";
import {supplyPositionStorage} from "./DataStructure/Global.sol";
contract SupplyPositionFacet is DiamondERC721 {
function position(uint256 tokenId) external view returns (Provision memory) {
SupplyPosition storage sp = supplyPositionStorage();
if (tokenId > sp.totalSupply) {
revert ERC721InvalidTokenId();
}
return sp.provision[tokenId];
}
function totalSupply() external view returns (uint256) {
return supplyPositionStorage().totalSupply;
}
}
{
"compilationTarget": {
"src/annex/DeployEagle.s.sol": "KairosEagle"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/=../../node_modules/@openzeppelin/",
":diamond/=../../node_modules/diamond/",
":ds-test/=../../node_modules/ds-test/src/",
":forge-std/=../../node_modules/forge-std/src/"
]
}
[{"inputs":[{"components":[{"internalType":"address","name":"facetAddress","type":"address"},{"internalType":"enum IDiamond.FacetCutAction","name":"action","type":"uint8"},{"internalType":"bytes4[]","name":"functionSelectors","type":"bytes4[]"}],"internalType":"struct IDiamond.FacetCut[]","name":"cuts","type":"tuple[]"},{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"init","type":"address"},{"internalType":"bytes","name":"initCalldata","type":"bytes"}],"internalType":"struct DiamondArgs","name":"_args","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotAddFunctionToDiamondThatAlreadyExists","type":"error"},{"inputs":[{"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"CannotAddSelectorsToZeroAddress","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotRemoveFunctionThatDoesNotExist","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotRemoveImmutableFunction","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotReplaceFunctionThatDoesNotExists","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet","type":"error"},{"inputs":[{"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"CannotReplaceFunctionsFromFacetWithZeroAddress","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotReplaceImmutableFunction","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_functionSelector","type":"bytes4"}],"name":"FunctionNotFound","type":"error"},{"inputs":[{"internalType":"uint8","name":"_action","type":"uint8"}],"name":"IncorrectFacetCutAction","type":"error"},{"inputs":[{"internalType":"address","name":"_initializationContractAddress","type":"address"},{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"InitializationFunctionReverted","type":"error"},{"inputs":[{"internalType":"address","name":"_contractAddress","type":"address"},{"internalType":"string","name":"_message","type":"string"}],"name":"NoBytecodeAtAddress","type":"error"},{"inputs":[{"internalType":"address","name":"_facetAddress","type":"address"}],"name":"NoSelectorsProvidedForFacetForCut","type":"error"},{"inputs":[{"internalType":"address","name":"_facetAddress","type":"address"}],"name":"RemoveFacetAddressMustBeZeroAddress","type":"error"},{"stateMutability":"payable","type":"fallback"},{"stateMutability":"payable","type":"receive"}]