Skip to content

Commit

Permalink
update v1Status and BaseRecall to validate pricing address (#27)
Browse files Browse the repository at this point in the history
* update v1Status and BaseRecall to validate pricing address

* add tests, update dependencies

* improve tests
  • Loading branch information
0xgregthedev authored Jan 9, 2024
1 parent d922e72 commit e494dae
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 4 deletions.
28 changes: 25 additions & 3 deletions src/status/AstariaV1Status.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,22 @@ import {Validation} from "starport-core/lib/Validation.sol";
import {BasePricing} from "starport-core/pricing/BasePricing.sol";
import {BaseRecall} from "v1-core/status/BaseRecall.sol";
import {BaseStatus} from "v1-core/status/BaseStatus.sol";
import {Ownable} from "solady/src/auth/Ownable.sol";

contract AstariaV1Status is BaseStatus, BaseRecall {
contract AstariaV1Status is BaseStatus, BaseRecall, Ownable {
using {StarportLib.getId} for Starport.Loan;

mapping(address => bool) public isValidPricing;

error InvalidPricingContract();

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

constructor(Starport SP_) BaseRecall(SP_) {}
constructor(Starport SP_) BaseRecall(SP_) {
_initializeOwner(msg.sender);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EXTERNAL FUNCTIONS */
Expand All @@ -54,9 +61,24 @@ contract AstariaV1Status is BaseStatus, BaseRecall {
Details memory details = abi.decode(loan.terms.statusData, (Details));
BasePricing.Details memory pDetails = abi.decode(loan.terms.pricingData, (BasePricing.Details));
bool valid = true;
if (details.recallerRewardRatio > 10 ** pDetails.decimals || details.recallMax > 10 * 10 ** pDetails.decimals) {
if (
details.recallerRewardRatio > 10 ** pDetails.decimals || details.recallMax > 10 * 10 ** pDetails.decimals
|| !isValidPricing[loan.terms.pricing]
) {
valid = false;
}

return valid ? Validation.validate.selector : bytes4(0xFFFFFFFF);
}

// @inheritdoc BaseRecall
function validatePricingContract(address pricingContract) internal virtual override {
if (!isValidPricing[pricingContract]) {
revert InvalidPricingContract();
}
}

function setValidPricing(address pricing, bool valid) external onlyOwner {
isValidPricing[pricing] = valid;
}
}
13 changes: 13 additions & 0 deletions src/status/BaseRecall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol";
import {ConsiderationInterface} from "seaport-types/src/interfaces/ConsiderationInterface.sol";
import {ERC20} from "solady/src/tokens/ERC20.sol";
import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";
import {Ownable} from "solady/src/auth/Ownable.sol";

abstract contract BaseRecall {
using FixedPointMathLib for uint256;
Expand Down Expand Up @@ -109,6 +110,14 @@ abstract contract BaseRecall {
return (details.recallMax * ratio) / baseAdjustment;
}

/**
* @dev Implement to validate the pricing contract.
* @dev Malicious pricing contracts can lie about the withdrawable recall amount.
* @dev This function should revert if the pricing contract is invalid.
* @param pricingContract The pricing contract to validate
*/
function validatePricingContract(address pricingContract) internal virtual;

/**
* @dev Recalls a loan
* @param loan The loan to recall
Expand All @@ -129,6 +138,8 @@ abstract contract BaseRecall {
revert RecallAlreadyExists();
}

validatePricingContract(loan.terms.pricing);

AdditionalTransfer[] memory recallConsideration = _generateRecallConsideration(
msg.sender, loan, 0, details.recallStakeDuration, 0, msg.sender, payable(address(this))
);
Expand All @@ -154,6 +165,8 @@ abstract contract BaseRecall {
revert LoanHasNotBeenRefinanced();
}

validatePricingContract(loan.terms.pricing);

Recall storage recall = recalls[loanId];
address recaller = recall.recaller;
// Ensure that a recall exists for the provided tokenId, ensure that the recall
Expand Down
2 changes: 2 additions & 0 deletions test/AstariaV1Test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ contract AstariaV1Test is StarportTest {
vm.label(address(pricing), "V1Pricing");
settlement = new AstariaV1Settlement(SP);
vm.label(address(settlement), "V1Settlement");

status = new AstariaV1Status(SP);
vm.label(address(status), "V1Status");
AstariaV1Status(address(status)).setValidPricing(address(pricing), true);

lenderEnforcer = new AstariaV1LenderEnforcer();
vm.label(address(lenderEnforcer), "V1LenderEnforcer");
Expand Down
49 changes: 49 additions & 0 deletions test/TestV1Status.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {DeepEq} from "starport-test/utils/DeepEq.sol";
import {SpentItemLib} from "seaport-sol/src/lib/SpentItemLib.sol";
import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";
import {Validation} from "starport-core/lib/Validation.sol";
import {Ownable} from "solady/src/auth/Ownable.sol";

contract TestAstariaV1Status is AstariaV1Test, DeepEq {
using Cast for *;
Expand Down Expand Up @@ -75,6 +76,54 @@ contract TestAstariaV1Status is AstariaV1Test, DeepEq {
assert(AstariaV1Status(loan.terms.status).isRecalled(loan));
}

function testV1StatusRevertInvalidPricing() public {
AstariaV1Status v1Status = AstariaV1Status(address(status));
vm.prank(v1Status.owner());
v1Status.setValidPricing(address(pricing), false);

Starport.Terms memory terms = Starport.Terms({
status: address(status),
settlement: address(settlement),
pricing: address(pricing),
pricingData: defaultPricingData,
settlementData: defaultSettlementData,
statusData: defaultStatusData
});
Starport.Loan memory loan =
_createLoan721Collateral20Debt({lender: lender.addr, borrowAmount: 1e18, terms: terms});
uint256 loanId = loan.getId();

BaseRecall.Details memory details = abi.decode(loan.terms.statusData, (BaseRecall.Details));

erc20s[0].mint(address(this), 10e18);
erc20s[0].approve(loan.terms.status, 10e18);

skip(details.honeymoon);
vm.expectRevert(AstariaV1Status.InvalidPricingContract.selector);
v1Status.recall(loan);
}

function testSetValidPricing() public {
AstariaV1Status v1Status = AstariaV1Status(address(status));
vm.startPrank(v1Status.owner());
v1Status.setValidPricing(address(0xdead), true);
assertTrue(v1Status.isValidPricing(address(0xdead)));
v1Status.setValidPricing(address(0xdead), false);
assertFalse(v1Status.isValidPricing(address(0xdead)));
vm.stopPrank();

vm.prank(v1Status.owner());
v1Status.transferOwnership(address(0xdead));
assertEq(v1Status.owner(), address(0xdead));

vm.prank(address(0xdead));
v1Status.setValidPricing(address(0xdead), true);
assertTrue(v1Status.isValidPricing(address(0xdead)));

vm.expectRevert(Ownable.Unauthorized.selector);
v1Status.setValidPricing(address(0xdead), true);
}

function testRecallAndRefinanceInsideWindow() public {
Starport.Terms memory terms = Starport.Terms({
status: address(status),
Expand Down

0 comments on commit e494dae

Please sign in to comment.