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

Fix twin inefficiencies #656

Merged
merged 15 commits into from
Jul 7, 2023
1 change: 1 addition & 0 deletions contracts/domain/BosonConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ string constant VERSION_MUST_BE_SET = "Version cannot be empty";
string constant ADDRESSES_AND_CALLDATA_LENGTH_MISMATCH = "Addresses and calldata must be same length";
string constant WRONG_CURRENT_VERSION = "Wrong current protocol version";
string constant DIRECT_INITIALIZATION_NOT_ALLOWED = "Direct initializtion is not allowed";
string constant TWINS_ALREADY_EXIST = "Should not have any twins yet";

// Revert Reasons: Access related
string constant ACCESS_DENIED = "Access denied, caller doesn't have role";
Expand Down
4 changes: 2 additions & 2 deletions contracts/protocol/bases/GroupBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ contract GroupBase is ProtocolBase, IBosonGroupEvents {
/**
* @notice Validates that condition parameters make sense.
*
* A invalid condition is one that fits any of the following criteria:
* An invalid condition is one that fits any of the following criteria:
* - EvaluationMethod.None: any field different from zero
* - EvaluationMethod.Threshold:
-Token address, maxCommits, or threshold is zero.
- Token address, maxCommits, or threshold is zero.
* - TokenType is FungibleToken or NonFungibleToken and length and tokenId are not 0.
* - EvaluationMethod.Threshold:
* - token address, maxCommits or threshold is zero
Expand Down
3 changes: 2 additions & 1 deletion contracts/protocol/bases/TwinBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ contract TwinBase is ProtocolBase, IBosonTwinEvents {
TokenRange[] storage twinRanges = lookups.twinRangesBySeller[sellerId][_twin.tokenAddress];

uint256 twinRangesLength = twinRanges.length;
zajck marked this conversation as resolved.
Show resolved Hide resolved

// Checks if token range isn't being used in any other twin of seller
for (uint256 i = 0; i < twinRangesLength; i++) {
// A valid range has:
Expand All @@ -89,6 +88,8 @@ contract TwinBase is ProtocolBase, IBosonTwinEvents {
TokenRange storage tokenRange = twinRanges.push();
tokenRange.start = tokenId;
tokenRange.end = lastTokenId;

lookups.rangeIdByTwin[twinId] = ++twinRangesLength;
} else if (_twin.tokenType == TokenType.MultiToken) {
// If token is Fungible or MultiToken amount should not be zero
// Also, the amount of tokens should not be more than the available token supply.
Expand Down
47 changes: 38 additions & 9 deletions contracts/protocol/facets/ExchangeHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,7 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {
(, Twin storage twin) = fetchTwin(twinIds[i]);

bool success;

uint256 tokenId = twin.tokenId;

{
Expand Down Expand Up @@ -881,16 +882,44 @@ contract ExchangeHandlerFacet is IBosonExchangeHandler, BuyerBase, DisputeBase {

emit TwinTransferFailed(twin.id, twin.tokenAddress, _exchange.id, tokenId, twin.amount, sender);
} else {
// Store twin receipt on twinReceiptsByExchange
uint256 exchangeId = _exchange.id;
zajck marked this conversation as resolved.
Show resolved Hide resolved
TwinReceipt storage twinReceipt = lookups.twinReceiptsByExchange[exchangeId].push();
twinReceipt.twinId = twin.id;
twinReceipt.tokenAddress = twin.tokenAddress;
twinReceipt.tokenId = tokenId;
twinReceipt.amount = twin.amount;
twinReceipt.tokenType = twin.tokenType;

emit TwinTransferred(twin.id, twin.tokenAddress, exchangeId, tokenId, twin.amount, sender);
uint256 twinId = twin.id;
{
// Store twin receipt on twinReceiptsByExchange
TwinReceipt storage twinReceipt = lookups.twinReceiptsByExchange[exchangeId].push();
twinReceipt.twinId = twinId;
twinReceipt.tokenAddress = twin.tokenAddress;
twinReceipt.tokenId = tokenId;
twinReceipt.amount = twin.amount;
twinReceipt.tokenType = twin.tokenType;
}
if (twin.tokenType == TokenType.NonFungibleToken) {
// Get all ranges of twins that belong to the seller and to the same token address of the new twin to validate if range is available
TokenRange[] storage twinRanges = lookups.twinRangesBySeller[seller.id][twin.tokenAddress];

bool unlimitedSupply = twin.supplyAvailable == type(uint256).max;

uint256 rangeIndex = lookups.rangeIdByTwin[twinId] - 1;
TokenRange storage range = twinRanges[rangeIndex];

if (unlimitedSupply ? range.end == tokenId : range.start == tokenId) {
uint256 lastIndex = twinRanges.length - 1;

if (rangeIndex != lastIndex) {
// Replace range with last range
twinRanges[rangeIndex] = twinRanges[lastIndex];
}

// Remove from ranges mapping
twinRanges.pop();

// Delete rangeId from rangeIdByTwin mapping
lookups.rangeIdByTwin[twinId] = 0;
} else {
unlimitedSupply ? range.start++ : range.end--;
}
}
emit TwinTransferred(twinId, twin.tokenAddress, exchangeId, tokenId, twin.amount, sender);
}
}
}
Expand Down
15 changes: 11 additions & 4 deletions contracts/protocol/facets/ProtocolInitializationHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,24 @@ contract ProtocolInitializationHandlerFacet is IBosonProtocolInitializationHandl
*
* V2.3.0 adds the minimal resolution period. Cannot be initialized with ConfigHandlerFacet.initialize since it would reset the counters.
*
* Reverts if:
* - Current version is not 2.2.1
* - There are already twins. This version adds a new mapping for twins which make it incompatible with previous versions.
* - minResolutionPeriond is not present in _initializationData parameter
*
* @param _initializationData - data representing uint256 _minResolutionPeriod
*/
function initV2_3_0(bytes calldata _initializationData) internal {
// Current version must be 2.2.1
require(protocolStatus().version == bytes32("2.2.1"), WRONG_CURRENT_VERSION);

require(protocolCounters().nextTwinId == 1, TWINS_ALREADY_EXIST);

// Initialize limits.maxPremintedVouchers (configHandlerFacet initializer)
uint256 _minResolutionPeriod = abi.decode(_initializationData, (uint256));
require(_minResolutionPeriod != 0, VALUE_ZERO_NOT_ALLOWED);
protocolLimits().minResolutionPeriod = _minResolutionPeriod;
emit MinResolutionPeriodChanged(_minResolutionPeriod, msgSender());
uint256 minResolutionPeriod = abi.decode(_initializationData, (uint256));
require(minResolutionPeriod != 0, VALUE_ZERO_NOT_ALLOWED);
protocolLimits().minResolutionPeriod = minResolutionPeriod;
emit MinResolutionPeriodChanged(minResolutionPeriod, msgSender());
}

/**
Expand Down
22 changes: 11 additions & 11 deletions contracts/protocol/facets/TwinHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,18 @@ contract TwinHandlerFacet is IBosonTwinHandler, TwinBase {
if (twin.tokenType == TokenType.NonFungibleToken) {
TokenRange[] storage twinRanges = lookups.twinRangesBySeller[sellerId][twin.tokenAddress];
uint256 lastIndex = twinRanges.length - 1;
for (uint256 index = 0; index <= lastIndex; index++) {
if (twinRanges[index].start == twin.tokenId) {
// If not removing last element, move the last to the removed index
if (index != lastIndex) {
twinRanges[index] = twinRanges[lastIndex];
}

// Remove last element
twinRanges.pop();
break;
}

uint256 rangeIndex = lookups.rangeIdByTwin[_twinId] - 1;

if (rangeIndex != lastIndex) {
twinRanges[rangeIndex] = twinRanges[lastIndex];
}

// Remove last element
twinRanges.pop();

// Delete rangeIdByTwin mapping
delete lookups.rangeIdByTwin[_twinId];
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fun :)


// Notify watchers of state change
Expand Down
3 changes: 3 additions & 0 deletions contracts/protocol/libs/ProtocolLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,11 @@ library ProtocolLib {
mapping(uint256 => BosonTypes.AuthToken) pendingAuthTokenUpdatesBySeller;
// dispute resolver id => DisputeResolver
mapping(uint256 => BosonTypes.DisputeResolver) pendingAddressUpdatesByDisputeResolver;
// twin id => range id
mapping(uint256 => uint256) rangeIdByTwin;
// tokenId => groupId => commit count (count how many times a token has been used as gate for this group)
mapping(uint256 => mapping(uint256 => uint256)) conditionalCommitsByTokenId;
// seller id => collections
mapping(uint256 => BosonTypes.Collection[]) additionalCollections;
}

Expand Down
42 changes: 10 additions & 32 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions scripts/config/revert-reasons.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ exports.RevertReasons = {
ADDRESSES_AND_CALLDATA_MUST_BE_SAME_LENGTH: "Addresses and calldata must be same length",
WRONG_CURRENT_VERSION: "Wrong current protocol version",
DIRECT_INITIALIZATION_NOT_ALLOWED: "Direct initializtion is not allowed",
TWINS_ALREADY_EXIST: "Should not have any twins yet",

// Offer related
NOT_ASSISTANT: "Not seller's assistant",
Expand Down
Loading