Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New tokenIds #583

Merged
merged 15 commits into from
Apr 20, 2023
3 changes: 2 additions & 1 deletion contracts/mock/MockExchangeHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,8 @@ contract MockExchangeHandlerFacet is BuyerBase, DisputeBase {
// Burn the voucher
(, Offer storage offer) = fetchOffer(_exchange.offerId);
IBosonVoucher bosonVoucher = IBosonVoucher(protocolLookups().cloneAddress[offer.sellerId]);
bosonVoucher.burnVoucher(_exchange.id);
uint256 tokenId = _exchange.id + (_exchange.offerId << 128);
bosonVoucher.burnVoucher(tokenId);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion contracts/protocol/bases/OfferBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ contract OfferBase is ProtocolBase, IBosonOfferEvents {
require(offer.quantityAvailable >= _length, INVALID_RANGE_LENGTH);

// Prevent reservation of too large range, since it affects exchangeId
require(_length < (1 << 128), INVALID_RANGE_LENGTH);
require(_length < (1 << 64), INVALID_RANGE_LENGTH);

// Get starting token id
ProtocolLib.ProtocolCounters storage pc = protocolCounters();
Expand Down
91 changes: 33 additions & 58 deletions contracts/protocol/clients/voucher/BosonVoucher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@ contract BosonVoucherBase is
// Map an offerId to a Range for pre-minted offers
mapping(uint256 => Range) private _rangeByOfferId;

// All ranges as an array
uint256[] private _rangeOfferIds;

// Premint status, used only temporarly in transfers
PremintStatus private _premintStatus;

Expand All @@ -74,7 +71,7 @@ contract BosonVoucherBase is
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[43] private __gap;
uint256[44] private __gap;
mischat marked this conversation as resolved.
Show resolved Hide resolved

/**
* @notice Initializes the voucher.
Expand Down Expand Up @@ -173,22 +170,13 @@ contract BosonVoucherBase is
// Prevent reservation of an empty range
require(_length > 0, INVALID_RANGE_LENGTH);

// Adjust start id to include offer id
require(_start > 0, INVALID_RANGE_START);
_start += (_offerId << 128);

// Prevent overflow in issueVoucher and preMint
require(_start <= type(uint256).max - _length, INVALID_RANGE_LENGTH);

// Make sure that ranges are in ascending order
uint256 rangeOfferIdsLength = _rangeOfferIds.length;
if (rangeOfferIdsLength > 0) {
// Get latest registered range
Range storage lastRange = _rangeByOfferId[_rangeOfferIds[rangeOfferIdsLength - 1]];

// New range should start after the end of last range
require(_start >= lastRange.start + lastRange.length, INVALID_RANGE_START);
} else {
// Make sure range start is valid
require(_start > 0, INVALID_RANGE_START);
}

// Get storage slot for the range
Range storage range = _rangeByOfferId[_offerId];

Expand All @@ -199,7 +187,6 @@ contract BosonVoucherBase is
range.start = _start;
range.length = _length;
range.owner = _to;
_rangeOfferIds.push(_offerId);
mischat marked this conversation as resolved.
Show resolved Hide resolved

emit RangeReserved(_offerId, range);
}
Expand Down Expand Up @@ -328,6 +315,9 @@ contract BosonVoucherBase is
end = start + maxPremintedVouchers;
}

// Update last burned token id
range.lastBurnedTokenId = end - 1;

// Burn the range
address seller = owner();
uint256 burned;
Expand All @@ -339,9 +329,6 @@ contract BosonVoucherBase is
}
}

// Update last burned token id
range.lastBurnedTokenId = end - 1;

// Update seller's total balance
getERC721UpgradeableStorage()._balances[seller] -= burned;
}
Expand Down Expand Up @@ -532,7 +519,8 @@ contract BosonVoucherBase is
override(ERC721Upgradeable, IERC721MetadataUpgradeable)
returns (string memory)
{
(bool exists, Offer memory offer) = getBosonOfferByExchangeId(_tokenId);
uint256 exchangeId = _tokenId & type(uint128).max;
(bool exists, Offer memory offer) = getBosonOfferByExchangeId(exchangeId);

if (!exists) {
(bool committable, uint256 offerId, ) = getPreMintStatus(_tokenId);
Expand Down Expand Up @@ -679,7 +667,8 @@ contract BosonVoucherBase is
returns (address receiver, uint256 royaltyAmount)
{
// get offer
(bool offerExists, Offer memory offer) = getBosonOfferByExchangeId(_tokenId);
uint256 exchangeId = _tokenId & type(uint128).max;
(bool offerExists, Offer memory offer) = getBosonOfferByExchangeId(exchangeId);

if (offerExists) {
(, Seller memory seller) = getBosonSeller(offer.sellerId);
Expand Down Expand Up @@ -816,42 +805,28 @@ contract BosonVoucherBase is
)
{
// Not committable if _committed already or if token has an owner

if (!_committed[_tokenId] && !_exists(_tokenId)) {
// If are reserved ranges, search them
uint256 length = _rangeOfferIds.length;
if (length > 0) {
// Binary search the ranges array
uint256 low = 0; // Lower bound of search (array index)
uint256 high = length; // Upper bound of search

while (low < high) {
// Calculate the current midpoint and get the offer id
uint256 mid = (high + low) / 2;
uint256 currentOfferId = _rangeOfferIds[mid];

// Get the range stored at the midpoint
Range storage range = _rangeByOfferId[currentOfferId];

// Get the beginning of the range once for reference
uint256 start = range.start;

if (start > _tokenId) {
// Split low and search again if target too high
high = mid;
} else if (start + range.length - 1 >= _tokenId) {
// Is token in target's reserved range?

if (start + range.minted - 1 >= _tokenId && _tokenId > range.lastBurnedTokenId) {
// Has it been pre-minted and not burned yet?
committable = true;
offerId = currentOfferId;
owner = range.owner;
}
break; // Found!
} else {
// No? It may be in a higher range
low = mid + 1;
}
// it might be a pre-minted token. Preminted tokens have offerId in the upper 128 bits
offerId = _tokenId >> 128;

if (offerId > 0) {
// Get the range stored at the midpoint
Range storage range = _rangeByOfferId[offerId];

// Get the beginning of the range once for reference
uint256 start = range.start;
// _tokenId = _tokenId & type(uint128).max;
anajuliabit marked this conversation as resolved.
Show resolved Hide resolved

if (
start > 0 &&
start <= _tokenId &&
start + range.minted - 1 >= _tokenId &&
_tokenId > range.lastBurnedTokenId
) {
// Has it been pre-minted and not burned yet?
anajuliabit marked this conversation as resolved.
Show resolved Hide resolved
committable = true;
owner = range.owner;
}
}
}
Expand Down
54 changes: 51 additions & 3 deletions contracts/protocol/facets/ExchangeHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ import { IERC20 } from "../../interfaces/IERC20.sol";
contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
using Address for address;

uint256 private immutable EXCHANGE_ID_2_2_0;

/**
* @notice After v2.2.0, token ids are derived from offerId and exchangeId.
* EXCHANGE_ID_2_2_0 is the first exchange id to use for 2.2.0.
* Set EXCHANGE_ID_2_2_0 in the constructor.
*
* @param _firstExchangeId2_2_0 - the first exchange id to use for 2.2.0
*/
constructor(uint256 _firstExchangeId2_2_0) {
EXCHANGE_ID_2_2_0 = _firstExchangeId2_2_0;
}

/**
* @notice Initializes facet.
* This function is callable only once.
Expand Down Expand Up @@ -114,6 +127,7 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
require(msg.sender == protocolLookups().cloneAddress[offer.sellerId], ACCESS_DENIED);

// Exchange must not exist already
_exchangeId = extractExchangeId(_exchangeId);
(bool exists, ) = fetchExchange(_exchangeId);
require(!exists, EXCHANGE_ALREADY_EXISTS);

Expand Down Expand Up @@ -216,7 +230,8 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
lookups.voucherCount[buyerId]++;
if (!_isPreminted) {
IBosonVoucher bosonVoucher = IBosonVoucher(lookups.cloneAddress[_offer.sellerId]);
bosonVoucher.issueVoucher(_exchangeId, _buyer);
uint256 tokenId = _exchangeId + (_offerId << 128);
bosonVoucher.issueVoucher(tokenId, _buyer);
}
}

Expand All @@ -239,6 +254,7 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
*/
function completeExchange(uint256 _exchangeId) public override exchangesNotPaused nonReentrant {
// Get the exchange, should be in redeemed state
_exchangeId = extractExchangeId(_exchangeId);
(Exchange storage exchange, Voucher storage voucher) = getValidExchange(_exchangeId, ExchangeState.Redeemed);
uint256 offerId = exchange.offerId;

Expand Down Expand Up @@ -307,6 +323,8 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
* @param _exchangeId - the id of the exchange
*/
function revokeVoucher(uint256 _exchangeId) external override exchangesNotPaused nonReentrant {
_exchangeId = extractExchangeId(_exchangeId);

// Get the exchange, should be in committed state
(Exchange storage exchange, ) = getValidExchange(_exchangeId, ExchangeState.Committed);

Expand Down Expand Up @@ -340,6 +358,8 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
* @param _exchangeId - the id of the exchange
*/
function cancelVoucher(uint256 _exchangeId) external override exchangesNotPaused nonReentrant {
_exchangeId = extractExchangeId(_exchangeId);

// Get the exchange, should be in committed state
(Exchange storage exchange, ) = getValidExchange(_exchangeId, ExchangeState.Committed);

Expand Down Expand Up @@ -367,6 +387,8 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
* @param _exchangeId - the id of the exchange
*/
function expireVoucher(uint256 _exchangeId) external override exchangesNotPaused nonReentrant {
_exchangeId = extractExchangeId(_exchangeId);

// Get the exchange, should be in committed state
(Exchange storage exchange, Voucher storage voucher) = getValidExchange(_exchangeId, ExchangeState.Committed);

Expand Down Expand Up @@ -399,6 +421,8 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
* @param _validUntilDate - the new voucher expiry date
*/
function extendVoucher(uint256 _exchangeId, uint256 _validUntilDate) external exchangesNotPaused nonReentrant {
_exchangeId = extractExchangeId(_exchangeId);

// Get the exchange, should be in committed state
(Exchange storage exchange, Voucher storage voucher) = getValidExchange(_exchangeId, ExchangeState.Committed);

Expand Down Expand Up @@ -445,6 +469,8 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
*/
function redeemVoucher(uint256 _exchangeId) external override exchangesNotPaused nonReentrant {
// Get the exchange, should be in committed state
_exchangeId = extractExchangeId(_exchangeId);

(Exchange storage exchange, Voucher storage voucher) = getValidExchange(_exchangeId, ExchangeState.Committed);
uint256 offerId = exchange.offerId;

Expand Down Expand Up @@ -502,6 +528,8 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
// Cache protocol lookups for reference
ProtocolLib.ProtocolLookups storage lookups = protocolLookups();

_exchangeId = extractExchangeId(_exchangeId);

// Get the exchange, should be in committed state
(Exchange storage exchange, Voucher storage voucher) = getValidExchange(_exchangeId, ExchangeState.Committed);

Expand Down Expand Up @@ -542,6 +570,7 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
*/
function isExchangeFinalized(uint256 _exchangeId) public view override returns (bool exists, bool isFinalized) {
Exchange storage exchange;
_exchangeId = extractExchangeId(_exchangeId);

// Get the exchange
(exists, exchange) = fetchExchange(_exchangeId);
Expand Down Expand Up @@ -586,6 +615,7 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
Voucher memory voucher
)
{
_exchangeId = extractExchangeId(_exchangeId);
(exists, exchange) = fetchExchange(_exchangeId);
voucher = fetchVoucher(_exchangeId);
}
Expand All @@ -598,6 +628,7 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
* @return state - the exchange state. See {BosonTypes.ExchangeStates}
*/
function getExchangeState(uint256 _exchangeId) external view override returns (bool exists, ExchangeState state) {
_exchangeId = extractExchangeId(_exchangeId);
Exchange storage exchange;
(exists, exchange) = fetchExchange(_exchangeId);
if (exists) state = exchange.state;
Expand Down Expand Up @@ -677,9 +708,13 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
lookups.voucherCount[_exchange.buyerId]--;

// Burn the voucher
(, Offer storage offer) = fetchOffer(_exchange.offerId);
uint256 offerId = _exchange.offerId;
(, Offer storage offer) = fetchOffer(offerId);
IBosonVoucher bosonVoucher = IBosonVoucher(lookups.cloneAddress[offer.sellerId]);
bosonVoucher.burnVoucher(_exchange.id);

uint256 tokenId = _exchange.id;
if (tokenId >= EXCHANGE_ID_2_2_0) tokenId += (offerId << 128);
bosonVoucher.burnVoucher(tokenId);
}

/**
Expand Down Expand Up @@ -959,6 +994,8 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
* @return receipt - the receipt for the exchange. See {BosonTypes.Receipt}
*/
function getReceipt(uint256 _exchangeId) external view returns (Receipt memory receipt) {
_exchangeId = extractExchangeId(_exchangeId);

// Get the exchange
(bool exists, Exchange storage exchange) = fetchExchange(_exchangeId);
require(exists, NO_SUCH_EXCHANGE);
Expand Down Expand Up @@ -1028,4 +1065,15 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
receipt.condition = condition;
}
}

/**
* @notice Returns lower 128 bits of the given exchange id.
* Needed in case if tokenId is passed in istead of an exchangeId.
anajuliabit marked this conversation as resolved.
Show resolved Hide resolved
*
* @param _exchangeId - the input exchange id
* @return exchangeId - the exchange id
*/
function extractExchangeId(uint256 _exchangeId) internal pure returns (uint256 exchangeId) {
return _exchangeId & type(uint128).max; // take lower 128 right bits
}
}
5 changes: 3 additions & 2 deletions scripts/config/facet-deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ async function getFacets(config) {
return acc;
}, {});

facetArgs["ConfigHandlerFacet"] = ConfigHandlerFacetInitArgs;
facetArgs["MetaTransactionsHandlerFacet"] = [MetaTransactionsHandlerFacetInitArgs];
facetArgs["ConfigHandlerFacet"] = { init: ConfigHandlerFacetInitArgs };
facetArgs["MetaTransactionsHandlerFacet"] = { init: [MetaTransactionsHandlerFacetInitArgs] };
facetArgs["ExchangeHandlerFacet"] = { constructorArgs: [protocolConfig.EXCHANGE_ID_2_2_0[network]] };

return facetArgs;
}
Expand Down
6 changes: 6 additions & 0 deletions scripts/config/protocol-parameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,10 @@ module.exports = {
mumbai: "0x4102621Ac55e068e148Da09151ce92102c952aab", //dummy
polygon: "0x17CDD65bebDe68cd8A4045422Fcff825A0740Ef9", //dummy
},

EXCHANGE_ID_2_2_0: {
hardhat: 1,
mumbai: 1, // TODO: adjust for actual deployment
polygon: 2, // TODO: adjust for actual deployment
mischat marked this conversation as resolved.
Show resolved Hide resolved
},
};
Loading