DCIPs/assets/eip-3525/contracts/ERC3525SlotEnumerable.sol

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();
}
}