Skip to content

Commit

Permalink
feat: extract Originator into a base abstract (#32)
Browse files Browse the repository at this point in the history
* feat: extract Originator into a base abstract

* format & snapshot

---------

Co-authored-by: 0xgregthedev <[email protected]>
  • Loading branch information
dangerousfood and 0xgregthedev authored Oct 18, 2023
1 parent 8a78323 commit 1b59637
Show file tree
Hide file tree
Showing 13 changed files with 325 additions and 469 deletions.
48 changes: 24 additions & 24 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
EnforcerTest:testCollateralEnforcer() (gas: 950836)
EnforcerTest:testFailCollateralEnforcerDifferentCollateral() (gas: 832965)
EnforcerTest:testFailRateEnforcerMaxCarryRate() (gas: 788105)
EnforcerTest:testFailRateEnforcerMaxRate() (gas: 788077)
EnforcerTest:testFailRateEnforcerMaxRateAndMaxCarryRate() (gas: 787942)
EnforcerTest:testRateEnforcerBasic() (gas: 908012)
EnforcerTest:testTermEnforcerBasic() (gas: 980340)
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecall() (gas: 969012)
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLender() (gas: 859410)
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLiquidation() (gas: 906274)
EnforcerTest:testCollateralEnforcer() (gas: 950647)
EnforcerTest:testFailCollateralEnforcerDifferentCollateral() (gas: 832776)
EnforcerTest:testFailRateEnforcerMaxCarryRate() (gas: 787916)
EnforcerTest:testFailRateEnforcerMaxRate() (gas: 787888)
EnforcerTest:testFailRateEnforcerMaxRateAndMaxCarryRate() (gas: 787753)
EnforcerTest:testRateEnforcerBasic() (gas: 907823)
EnforcerTest:testTermEnforcerBasic() (gas: 980151)
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecall() (gas: 968823)
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLender() (gas: 859221)
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLiquidation() (gas: 906085)
TestCustodian:testCannotLazyMintTwice() (gas: 76591)
TestCustodian:testCannotMintInvalidLoanInvalidCustodian() (gas: 66811)
TestCustodian:testCannotMintInvalidLoanValidCustodian() (gas: 72394)
Expand All @@ -16,9 +16,9 @@ TestCustodian:testDefaultCustodySelectorRevert() (gas: 11673)
TestCustodian:testGenerateOrderInvalidHandlerExecution() (gas: 147388)
TestCustodian:testGenerateOrderRepay() (gas: 189283)
TestCustodian:testGenerateOrderRepayAsRepayApprovedBorrower() (gas: 214857)
TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNative() (gas: 1130044)
TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNativeHandlerAuthorized() (gas: 1029768)
TestCustodian:testGenerateOrderRepayERC1155WithRevert() (gas: 530761)
TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNative() (gas: 1129477)
TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNativeHandlerAuthorized() (gas: 1029201)
TestCustodian:testGenerateOrderRepayERC1155WithRevert() (gas: 530572)
TestCustodian:testGenerateOrderRepayInvalidHookAddress() (gas: 104841)
TestCustodian:testGenerateOrderRepayInvalidHookReturnType() (gas: 99244)
TestCustodian:testGenerateOrderRepayNotBorrower() (gas: 110879)
Expand All @@ -44,23 +44,23 @@ TestCustodian:testSupportsInterface() (gas: 9428)
TestCustodian:testSymbol() (gas: 7105)
TestCustodian:testTokenURI() (gas: 64811)
TestCustodian:testTokenURIInvalidLoan() (gas: 13196)
TestExoticLoans:testSwap() (gas: 1353956)
TestExoticLoans:testSwap() (gas: 1353767)
TestLoanCombinations:testLoan20For721SimpleInterestDutchFixedRepay() (gas: 164)
TestLoanCombinations:testLoan20for20SimpleInterestDutchFixedRepay() (gas: 539940)
TestLoanCombinations:testLoan721for20SimpleInterestDutchFixedRepay() (gas: 756253)
TestLoanCombinations:testLoanAstariaSettlementRepay() (gas: 575128)
TestLoanCombinations:testLoanSimpleInterestEnglishFixed() (gas: 747899)
TestLoanCombinations:testLoan20for20SimpleInterestDutchFixedRepay() (gas: 539789)
TestLoanCombinations:testLoan721for20SimpleInterestDutchFixedRepay() (gas: 756102)
TestLoanCombinations:testLoanAstariaSettlementRepay() (gas: 574976)
TestLoanCombinations:testLoanSimpleInterestEnglishFixed() (gas: 747748)
TestLoanManager:testGenerateOrder() (gas: 1005642)
TestLoanManager:testGenerateOrderNotSeaport() (gas: 13074)
TestLoanManager:testName() (gas: 7206)
TestLoanManager:testSupportsInterface() (gas: 9579)
TestLoanManager:testSymbol() (gas: 7148)
TestNewLoan:testBuyNowPayLater() (gas: 1148671)
TestNewLoan:testNewLoanERC721CollateralDefaultTerms2():((uint256,address,address,address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256)[],(address,bytes,address,bytes,address,bytes))) (gas: 979121)
TestNewLoan:testNewLoanERC721CollateralDefaultTermsRefinance() (gas: 639892)
TestNewLoan:testNewLoanERC721CollateralLessDebtThanOffered():((uint256,address,address,address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256)[],(address,bytes,address,bytes,address,bytes))) (gas: 979455)
TestNewLoan:testSettleLoan() (gas: 1233583)
TestRepayLoan:testRepayLoan() (gas: 694754)
TestNewLoan:testBuyNowPayLater() (gas: 1141742)
TestNewLoan:testNewLoanERC721CollateralDefaultTerms2():((uint256,address,address,address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256)[],(address,bytes,address,bytes,address,bytes))) (gas: 978940)
TestNewLoan:testNewLoanERC721CollateralDefaultTermsRefinance() (gas: 639711)
TestNewLoan:testNewLoanERC721CollateralLessDebtThanOffered():((uint256,address,address,address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256)[],(address,bytes,address,bytes,address,bytes))) (gas: 979274)
TestNewLoan:testSettleLoan() (gas: 1233410)
TestRepayLoan:testRepayLoan() (gas: 694565)
TestStarLiteUtils:testEncodeReceivedWithRecipient() (gas: 17955)
TestStarLiteUtils:testSpentToReceived() (gas: 17708)
TestStarLiteUtils:testValidateSaltOpt(address,bytes32) (runs: 256, μ: 26479, ~: 26479)
Expand Down
1 change: 0 additions & 1 deletion src/Custodian.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import {ContractOffererInterface} from "seaport-types/src/interfaces/ContractOff
import {ConduitHelper} from "starport-core/ConduitHelper.sol";
import {TokenReceiverInterface} from "starport-core/interfaces/TokenReceiverInterface.sol";
import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";
import {Originator} from "starport-core/originators/Originator.sol";
import {SettlementHook} from "starport-core/hooks/SettlementHook.sol";
import {SettlementHandler} from "starport-core/handlers/SettlementHandler.sol";
import {Pricing} from "starport-core/pricing/Pricing.sol";
Expand Down
1 change: 0 additions & 1 deletion src/handlers/DutchAuctionHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
OrderParameters
} from "seaport-types/src/lib/ConsiderationStructs.sol";

import {Originator} from "starport-core/originators/Originator.sol";
import {Pricing} from "starport-core/pricing/Pricing.sol";
import {AmountDeriver} from "seaport-core/src/lib/AmountDeriver.sol";
import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";
Expand Down
199 changes: 2 additions & 197 deletions src/originators/Originator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,16 @@ pragma solidity =0.8.17;

import {LoanManager} from "starport-core/LoanManager.sol";

import {ItemType, ReceivedItem, SpentItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {SpentItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {ConduitTransfer, ConduitItemType} from "seaport-types/src/conduit/lib/ConduitStructs.sol";
import {ConduitControllerInterface} from "seaport-types/src/interfaces/ConduitControllerInterface.sol";
import {ConduitInterface} from "seaport-types/src/interfaces/ConduitInterface.sol";

import {ECDSA} from "solady/src/utils/ECDSA.sol";
import {SignatureCheckerLib} from "solady/src/utils/SignatureCheckerLib.sol";
import {Ownable} from "solady/src/auth/Ownable.sol";

// Validator abstract contract that lays out the necessary structure and functions for the validator
abstract contract Originator is Ownable {
enum State {
INITIALIZED,
CLOSED
}

struct Response {
LoanManager.Terms terms;
address issuer;
}

event StrategistTransferred(address newStrategist);

mapping(bytes32 => bool) public usedHashes;

struct Request {
address custodian;
address receiver;
Expand All @@ -56,79 +41,9 @@ abstract contract Originator is Ownable {
bytes approval;
}

struct Details {
address custodian;
address conduit;
address issuer;
uint256 deadline;
Offer offer;
}

struct Offer {
bytes32 salt; //can be bytes32(0) if so do not invalidate the hash
LoanManager.Terms terms;
SpentItem[] collateral;
SpentItem[] debt;
}

event Origination(uint256 indexed loanId, address indexed issuer, bytes nlrDetails);

event CounterUpdated();

event HashInvalidated(bytes32 hash);

modifier onlyLoanManager() {
if (msg.sender != address(LM)) {
revert NotLoanManager();
}
_;
}

error NotLoanManager();
error NotAuthorized();
error InvalidDebt();
error InvalidDebtLength();
error InvalidDebtAmount();
error InvalidCustodian();
error InvalidCollateral();
error InvalidDeadline();
error InvalidOffer();
error InvalidSigner();
error ConduitTransferError();

LoanManager public immutable LM;

// Define the EIP712 domain and typehash constants for generating signatures
bytes32 constant EIP_DOMAIN = keccak256("EIP712Domain(string version,uint256 chainId,address verifyingContract)");
bytes32 public constant ORIGINATOR_DETAILS_TYPEHASH = keccak256("Origination(uint256 nonce,bytes32 hash)");
bytes32 constant VERSION = keccak256("0");

bytes32 internal immutable _DOMAIN_SEPARATOR;

// Strategist address and fee
address public strategist;
uint256 public strategistFee;
uint256 private _counter;

constructor(LoanManager LM_, address strategist_, uint256 fee_, address owner) {
_initializeOwner(owner);
strategist = strategist_;
strategistFee = fee_;
LM = LM_;
_DOMAIN_SEPARATOR = keccak256(
abi.encode(
EIP_DOMAIN,
VERSION, //version
block.chainid,
address(this)
)
);
}

function setStrategist(address newStrategist) external onlyOwner {
strategist = newStrategist;
emit StrategistTransferred(newStrategist);
}
function execute(Request calldata params) external virtual returns (Response memory response);

function _packageTransfers(SpentItem[] memory loan, address borrower, address issuer)
internal
Expand Down Expand Up @@ -162,114 +77,4 @@ abstract contract Originator is Ownable {
}
}
}

function terms(bytes calldata details) public view virtual returns (LoanManager.Terms memory) {
return abi.decode(details, (Details)).offer.terms;
}

function execute(Request calldata params) external virtual returns (Response memory response);

function _buildResponse(Request calldata params, Details memory details)
internal
virtual
returns (Response memory response)
{
response = Response({terms: details.offer.terms, issuer: details.issuer});
}

// Encode the data with the account's nonce for generating a signature
function encodeWithAccountCounter(bytes32 contextHash) public view virtual returns (bytes memory) {
bytes32 hash = keccak256(abi.encode(ORIGINATOR_DETAILS_TYPEHASH, _counter, contextHash));

return abi.encodePacked(bytes1(0x19), bytes1(0x01), _DOMAIN_SEPARATOR, hash);
}

function getStrategistData() public view virtual returns (address, uint256) {
return (strategist, strategistFee);
}

// Get the nonce of an account
function getCounter() public view virtual returns (uint256) {
return _counter;
}

function incrementCounter() external {
if (msg.sender != strategist || msg.sender != owner()) {
revert NotAuthorized();
}
_counter += uint256(blockhash(block.number - 1) << 0x80);
emit CounterUpdated();
}

// Function to generate the domain separator for signatures
function domainSeparator() public view virtual returns (bytes32) {
return _DOMAIN_SEPARATOR;
}

function _validateAsk(Request calldata request, Details memory details) internal virtual {
if (keccak256(abi.encode(request.collateral)) != keccak256(abi.encode(details.offer.collateral))) {
revert InvalidCollateral();
}

//loop through collateral and check if the collateral is the same

for (uint256 i = 0; i < request.collateral.length;) {
if (
request.debt[i].itemType != details.offer.debt[i].itemType
|| request.debt[i].token != details.offer.debt[i].token
|| request.debt[i].identifier != details.offer.debt[i].identifier
) {
revert InvalidDebt();
}

if (
request.debt[i].amount > details.offer.debt[i].amount || request.debt[i].amount == 0
|| details.offer.debt[i].amount == 0
) {
revert InvalidDebtAmount();
}
unchecked {
i++;
}
}
}

function _validateOffer(Request calldata request, Details memory details) internal virtual {
bytes32 contextHash = keccak256(request.details);
_validateSignature(keccak256(encodeWithAccountCounter(keccak256(request.details))), request.approval);
if (request.custodian != details.custodian) {
revert InvalidCustodian();
}
if (request.debt.length != details.offer.debt.length) {
revert InvalidDebtLength();
}
_validateAsk(request, details);

if (details.offer.salt != bytes32(0)) {
if (!usedHashes[contextHash]) {
usedHashes[contextHash] = true;
emit HashInvalidated(contextHash);
} else {
revert InvalidOffer();
}
}
if (block.timestamp > details.deadline) {
revert InvalidDeadline();
}
}

function _execute(Request calldata request, Details memory details) internal virtual {
if (
ConduitInterface(details.conduit).execute(_packageTransfers(request.debt, request.receiver, details.issuer))
!= ConduitInterface.execute.selector
) {
revert ConduitTransferError();
}
}

function _validateSignature(bytes32 hash, bytes memory signature) internal view virtual {
if (!SignatureCheckerLib.isValidSignatureNow(strategist, hash, signature)) {
revert InvalidSigner();
}
}
}
Loading

0 comments on commit 1b59637

Please sign in to comment.