Skip to content

Commit

Permalink
allow more precision with exchange rate
Browse files Browse the repository at this point in the history
  • Loading branch information
0xble committed Nov 16, 2023
1 parent 60787f2 commit 6ff5042
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 34 deletions.
53 changes: 28 additions & 25 deletions contracts/authorities/SellPartyCardsAuthority.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { Party } from "contracts/party/Party.sol";
import { IGateKeeper } from "contracts/gatekeepers/IGateKeeper.sol";
import { LibSafeCast } from "contracts/utils/LibSafeCast.sol";
import { LibAddress } from "contracts/utils/LibAddress.sol";
import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol";

contract SellPartyCardsAuthority {
using FixedPointMathLib for uint96;
using LibSafeCast for uint96;
using LibSafeCast for uint256;
using LibAddress for address payable;
Expand Down Expand Up @@ -38,9 +40,9 @@ contract SellPartyCardsAuthority {
uint96 maxContribution;
// The maximum total amount that can be contributed for the sale.
uint96 maxTotalContributions;
// The exchange rate from contribution amount to voting power, in basis
// points. May be greater than 1e4 (100%).
uint16 exchangeRateBps;
// The exchange rate from contribution amount to voting power. May be
// greater than 1e18 (100%).
uint96 exchangeRate;
// The split from each contribution to be received by the
// fundingSplitRecipient, in basis points.
uint16 fundingSplitBps;
Expand All @@ -63,9 +65,9 @@ contract SellPartyCardsAuthority {
uint96 totalContributions;
// The maximum total amount that can be contributed for the sale.
uint96 maxTotalContributions;
// The exchange rate from contribution amount to voting power, in basis
// points. May be greater than 1e4 (100%).
uint16 exchangeRateBps;
// The exchange rate from contribution amount to voting power. May be
// greater than 1e18 (100%).
uint96 exchangeRate;
// The split from each contribution to be received by the
// fundingSplitRecipient, in basis points.
uint16 fundingSplitBps;
Expand Down Expand Up @@ -99,7 +101,7 @@ contract SellPartyCardsAuthority {
error NotAuthorizedError();
error MinGreaterThanMaxError(uint96 minContribution, uint96 maxContribution);
error ZeroMaxTotalContributionsError();
error ZeroExchangeRateBpsError();
error ZeroExchangeRateError();
error InvalidBpsError(uint16 fundingSplitBps);
error ZeroVotingPowerError();
error InvalidMessageValue();
Expand Down Expand Up @@ -127,8 +129,9 @@ contract SellPartyCardsAuthority {
maxContribution: opts.pricePerMembership,
totalContributions: 0,
maxTotalContributions: opts.pricePerMembership * opts.totalMembershipsForSale,
exchangeRateBps: ((opts.votingPowerPerMembership * 1e4) /
opts.pricePerMembership).safeCastUint96ToUint16(),
exchangeRate: uint96(
opts.votingPowerPerMembership.mulDivDown(1e18, opts.pricePerMembership)
),
fundingSplitBps: opts.fundingSplitBps,
fundingSplitRecipient: opts.fundingSplitRecipient,
expiry: uint40(block.timestamp + opts.duration),
Expand All @@ -151,7 +154,7 @@ contract SellPartyCardsAuthority {
maxContribution: opts.maxContribution,
totalContributions: 0,
maxTotalContributions: opts.maxTotalContributions,
exchangeRateBps: opts.exchangeRateBps,
exchangeRate: opts.exchangeRate,
fundingSplitBps: opts.fundingSplitBps,
fundingSplitRecipient: opts.fundingSplitRecipient,
expiry: uint40(block.timestamp + opts.duration),
Expand All @@ -165,7 +168,7 @@ contract SellPartyCardsAuthority {
if (state.minContribution > state.maxContribution)
revert MinGreaterThanMaxError(state.minContribution, state.maxContribution);
if (state.maxTotalContributions == 0) revert ZeroMaxTotalContributionsError();
if (state.exchangeRateBps == 0) revert ZeroExchangeRateBpsError();
if (state.exchangeRate == 0) revert ZeroExchangeRateError();
if (state.fundingSplitBps > 1e4) revert InvalidBpsError(state.fundingSplitBps);

Party party = Party(payable(msg.sender));
Expand Down Expand Up @@ -332,7 +335,7 @@ contract SellPartyCardsAuthority {
pricePerMembership = opts.minContribution;
votingPowerPerMembership = _convertContributionToVotingPower(
pricePerMembership,
opts.exchangeRateBps
opts.exchangeRate
);
totalContributions = opts.totalContributions;
totalMembershipsForSale = opts.maxTotalContributions / opts.minContribution;
Expand All @@ -351,8 +354,8 @@ contract SellPartyCardsAuthority {
/// @return totalContributions The total amount that has been contributed.
/// @return maxTotalContributions The maximum total amount that can be
/// contributed for the sale.
/// @return exchangeRateBps The exchange rate from contribution amount to
/// voting power, in basis points.
/// @return exchangeRate The exchange rate from contribution amount to
/// voting power.
/// @return fundingSplitBps The split from each contribution to be received
/// by the fundingSplitRecipient, in basis points.
/// @return fundingSplitRecipient The recipient of the funding split.
Expand All @@ -370,7 +373,7 @@ contract SellPartyCardsAuthority {
uint96 maxContribution,
uint96 totalContributions,
uint96 maxTotalContributions,
uint16 exchangeRateBps,
uint96 exchangeRate,
uint16 fundingSplitBps,
address payable fundingSplitRecipient,
uint40 expiry,
Expand All @@ -383,7 +386,7 @@ contract SellPartyCardsAuthority {
maxContribution = opts.maxContribution;
totalContributions = opts.totalContributions;
maxTotalContributions = opts.maxTotalContributions;
exchangeRateBps = opts.exchangeRateBps;
exchangeRate = opts.exchangeRate;
fundingSplitBps = opts.fundingSplitBps;
fundingSplitRecipient = opts.fundingSplitRecipient;
expiry = opts.expiry;
Expand Down Expand Up @@ -417,8 +420,8 @@ contract SellPartyCardsAuthority {
uint256 saleId,
uint96 contribution
) external view returns (uint96) {
uint16 exchangeRateBps = _saleStates[party][saleId].exchangeRateBps;
return _convertContributionToVotingPower(contribution, exchangeRateBps);
uint96 exchangeRate = _saleStates[party][saleId].exchangeRate;
return _convertContributionToVotingPower(contribution, exchangeRate);
}

/// @notice Convert a voting power amount to a contribution amount.
Expand All @@ -432,8 +435,8 @@ contract SellPartyCardsAuthority {
uint256 saleId,
uint96 votingPower
) external view returns (uint96) {
uint16 exchangeRateBps = _saleStates[party][saleId].exchangeRateBps;
return _convertVotingPowerToContribution(votingPower, exchangeRateBps);
uint96 exchangeRate = _saleStates[party][saleId].exchangeRate;
return _convertVotingPowerToContribution(votingPower, exchangeRate);
}

function _contribute(
Expand Down Expand Up @@ -573,7 +576,7 @@ contract SellPartyCardsAuthority {
}

// Calculate voting power.
votingPower = _convertContributionToVotingPower(amount, state.exchangeRateBps);
votingPower = _convertContributionToVotingPower(amount, state.exchangeRate);

if (votingPower == 0) revert ZeroVotingPowerError();
}
Expand Down Expand Up @@ -609,16 +612,16 @@ contract SellPartyCardsAuthority {

function _convertContributionToVotingPower(
uint96 contribution,
uint16 exchangeRateBps
uint96 exchangeRate
) private pure returns (uint96) {
return (contribution * exchangeRateBps) / 1e4;
return uint96(contribution.mulDivDown(exchangeRate, 1e18));
}

function _convertVotingPowerToContribution(
uint96 votingPower,
uint16 exchangeRateBps
uint96 exchangeRate
) private pure returns (uint96) {
return (votingPower * 1e4) / exchangeRateBps;
return uint96(votingPower.mulDivDown(1e18, exchangeRate));
}

function _isSaleActive(
Expand Down
18 changes: 9 additions & 9 deletions test/authorities/SellPartyCardsAuthority.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ contract SellPartyCardsAuthorityTest is SetupPartyHelper {
minContribution: 3 ether,
maxContribution: 2 ether,
maxTotalContributions: 3 ether,
exchangeRateBps: 1e4,
exchangeRate: 1e18,
fundingSplitBps: 0,
fundingSplitRecipient: payable(address(0)),
duration: 100,
Expand All @@ -231,7 +231,7 @@ contract SellPartyCardsAuthorityTest is SetupPartyHelper {
minContribution: 1 ether,
maxContribution: 2 ether,
maxTotalContributions: 0 ether,
exchangeRateBps: 1e4,
exchangeRate: 1e18,
fundingSplitBps: 0,
fundingSplitRecipient: payable(address(0)),
duration: 100,
Expand All @@ -250,7 +250,7 @@ contract SellPartyCardsAuthorityTest is SetupPartyHelper {
minContribution: 1 ether,
maxContribution: 2 ether,
maxTotalContributions: 5 ether,
exchangeRateBps: 0,
exchangeRate: 0,
fundingSplitBps: 0,
fundingSplitRecipient: payable(address(0)),
duration: 100,
Expand All @@ -259,7 +259,7 @@ contract SellPartyCardsAuthorityTest is SetupPartyHelper {
});

vm.prank(address(party));
vm.expectRevert(SellPartyCardsAuthority.ZeroExchangeRateBpsError.selector);
vm.expectRevert(SellPartyCardsAuthority.ZeroExchangeRateError.selector);
sellPartyCardsAuthority.createFlexibleMembershipSale(opts);
}

Expand All @@ -269,7 +269,7 @@ contract SellPartyCardsAuthorityTest is SetupPartyHelper {
minContribution: 1 ether,
maxContribution: 2 ether,
maxTotalContributions: 5 ether,
exchangeRateBps: 1e4,
exchangeRate: 1e18,
fundingSplitBps: 10001,
fundingSplitRecipient: payable(address(this)),
duration: 100,
Expand Down Expand Up @@ -393,7 +393,7 @@ contract SellPartyCardsAuthorityTest is SetupPartyHelper {
uint96 maxContribution,
uint96 totalContributions,
uint96 maxTotalContributions,
uint16 exchangeRateBps,
uint96 exchangeRate,
uint16 fundingSplitBps,
address payable fundingSplitRecipient,
uint40 expiry,
Expand All @@ -405,7 +405,7 @@ contract SellPartyCardsAuthorityTest is SetupPartyHelper {
assertEq(maxContribution, 2 ether);
assertEq(totalContributions, 0 ether);
assertEq(maxTotalContributions, 3 ether);
assertEq(exchangeRateBps, 1e4);
assertEq(exchangeRate, 1e18);
assertEq(fundingSplitBps, 0);
assertEq(fundingSplitRecipient, payable(address(0)));
assertEq(expiry, uint40(block.timestamp + 100 - 10));
Expand Down Expand Up @@ -488,7 +488,7 @@ contract SellPartyCardsAuthorityTest is SetupPartyHelper {
minContribution: 0,
maxContribution: 2 ether,
maxTotalContributions: 3 ether,
exchangeRateBps: 1e4,
exchangeRate: 1e18,
fundingSplitBps: 0,
fundingSplitRecipient: payable(address(0)),
duration: 100,
Expand Down Expand Up @@ -565,7 +565,7 @@ contract SellPartyCardsAuthorityTest is SetupPartyHelper {
minContribution: 0.001 ether,
maxContribution: 2 ether,
maxTotalContributions: 3 ether,
exchangeRateBps: 1e4,
exchangeRate: 1e18,
fundingSplitBps: 0,
fundingSplitRecipient: payable(address(0)),
duration: 100,
Expand Down

0 comments on commit 6ff5042

Please sign in to comment.