123 lines
4.2 KiB
Solidity
123 lines
4.2 KiB
Solidity
// SPDX-License-Identifier: CC0-1.0
|
|
|
|
pragma solidity ^0.8.0;
|
|
|
|
import "./ERC3525.sol";
|
|
import "./interface/IERC3525SlotEnumerable.sol";
|
|
|
|
abstract contract ERC3525SlotEnumerable is ERC3525, IERC3525SlotEnumerable {
|
|
struct SlotData {
|
|
uint256 slot;
|
|
uint256[] slotTokens;
|
|
// mapping(uint256 => uint256) slotTokensIndex;
|
|
}
|
|
|
|
// slot => tokenId => index
|
|
mapping(uint256 => mapping(uint256 => uint256)) private _slotTokensIndex;
|
|
|
|
SlotData[] private _allSlots;
|
|
|
|
// slot => index
|
|
mapping(uint256 => uint256) private _allSlotsIndex;
|
|
|
|
function slotCount() public view virtual override returns (uint256) {
|
|
return _allSlots.length;
|
|
}
|
|
|
|
function slotByIndex(uint256 index_) public view virtual override returns (uint256) {
|
|
require(
|
|
index_ < ERC3525SlotEnumerable.slotCount(),
|
|
"ERC3525SlotEnumerable: slot index out of bounds"
|
|
);
|
|
return _allSlots[index_].slot;
|
|
}
|
|
|
|
function _slotExists(uint256 slot_) internal view virtual returns (bool) {
|
|
return
|
|
_allSlots.length != 0 &&
|
|
_allSlots[_allSlotsIndex[slot_]].slot == slot_;
|
|
}
|
|
|
|
function tokenSupplyInSlot(uint256 slot_) public view virtual override returns (uint256) {
|
|
if (!_slotExists(slot_)) {
|
|
return 0;
|
|
}
|
|
return _allSlots[_allSlotsIndex[slot_]].slotTokens.length;
|
|
}
|
|
|
|
function tokenInSlotByIndex(uint256 slot_, uint256 index_) public view virtual override returns (uint256) {
|
|
require(
|
|
index_ < ERC3525SlotEnumerable.tokenSupplyInSlot(slot_),
|
|
"ERC3525SlotEnumerable: slot token index out of bounds"
|
|
);
|
|
return _allSlots[_allSlotsIndex[slot_]].slotTokens[index_];
|
|
}
|
|
|
|
function _tokenExistsInSlot(uint256 slot_, uint256 tokenId_) private view returns (bool) {
|
|
SlotData storage slotData = _allSlots[_allSlotsIndex[slot_]];
|
|
return
|
|
slotData.slotTokens.length > 0 &&
|
|
slotData.slotTokens[_slotTokensIndex[slot_][tokenId_]] == tokenId_;
|
|
}
|
|
|
|
function _beforeValueTransfer( address from_, address to_, uint256 fromTokenId_, uint256 toTokenId_, uint256 slot_,
|
|
uint256 value_) internal virtual override {
|
|
if (from_ == address(0) && fromTokenId_ == 0 && !_slotExists(slot_)) {
|
|
SlotData memory slotData = SlotData({
|
|
slot: slot_,
|
|
slotTokens: new uint256[](0)
|
|
});
|
|
_addSlotToAllSlotsEnumeration(slotData);
|
|
}
|
|
|
|
//Shh - currently unused
|
|
to_;
|
|
toTokenId_;
|
|
value_;
|
|
}
|
|
|
|
function _afterValueTransfer( address from_, address to_, uint256 fromTokenId_, uint256 toTokenId_, uint256 slot_,
|
|
uint256 value_) internal virtual override {
|
|
if (
|
|
from_ == address(0) &&
|
|
fromTokenId_ == 0 &&
|
|
!_tokenExistsInSlot(slot_, toTokenId_)
|
|
) {
|
|
_addTokenToSlotEnumeration(slot_, toTokenId_);
|
|
} else if (
|
|
to_ == address(0) &&
|
|
toTokenId_ == 0 &&
|
|
_tokenExistsInSlot(slot_, fromTokenId_)
|
|
) {
|
|
_removeTokenFromSlotEnumeration(slot_, fromTokenId_);
|
|
}
|
|
|
|
//Shh - currently unused
|
|
value_;
|
|
}
|
|
|
|
function _addSlotToAllSlotsEnumeration(SlotData memory slotData) private {
|
|
_allSlotsIndex[slotData.slot] = _allSlots.length;
|
|
_allSlots.push(slotData);
|
|
}
|
|
|
|
function _addTokenToSlotEnumeration(uint256 slot_, uint256 tokenId_) private {
|
|
SlotData storage slotData = _allSlots[_allSlotsIndex[slot_]];
|
|
_slotTokensIndex[slot_][tokenId_] = slotData.slotTokens.length;
|
|
slotData.slotTokens.push(tokenId_);
|
|
}
|
|
|
|
function _removeTokenFromSlotEnumeration(uint256 slot_, uint256 tokenId_) private {
|
|
SlotData storage slotData = _allSlots[_allSlotsIndex[slot_]];
|
|
uint256 lastTokenIndex = slotData.slotTokens.length - 1;
|
|
uint256 lastTokenId = slotData.slotTokens[lastTokenIndex];
|
|
uint256 tokenIndex = slotData.slotTokens[tokenId_];
|
|
|
|
slotData.slotTokens[tokenIndex] = lastTokenId;
|
|
_slotTokensIndex[slot_][lastTokenId] = tokenIndex;
|
|
|
|
delete _slotTokensIndex[slot_][tokenId_];
|
|
slotData.slotTokens.pop();
|
|
}
|
|
}
|