/
ObjectLib32.sol
96 lines (86 loc) · 3.86 KB
/
ObjectLib32.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//SPDX-License-Identifier: MIT
// solhint-disable-next-line compiler-version
pragma solidity 0.8.2;
library ObjectLib32 {
enum Operations {ADD, SUB, REPLACE}
// Constants regarding bin or chunk sizes for balance packing
uint256 internal constant TYPES_BITS_SIZE = 32; // Max size of each object
uint256 internal constant TYPES_PER_UINT256 = 256 / TYPES_BITS_SIZE; // Number of types per uint256
uint256 internal constant TYPE_ELIMINATOR = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFF;
//
// Objects and Tokens Functions
//
/// @dev Return the bin number and index within that bin where ID is
/// @param tokenId Object type
/// @return bin Bin number.
/// @return index ID's index within that bin.
function getTokenBinIndex(uint256 tokenId) internal pure returns (uint256 bin, uint256 index) {
uint256 id = tokenId & TYPE_ELIMINATOR;
unchecked {bin = (id * TYPES_BITS_SIZE) / 256;}
index = tokenId % TYPES_PER_UINT256;
return (bin, index);
}
/**
* @dev update the balance of a type provided in binBalances
* @param binBalances Uint256 containing the balances of objects
* @param index Index of the object in the provided bin
* @param amount Value to update the type balance
* @param operation Which operation to conduct :
* Operations.REPLACE : Replace type balance with amount
* Operations.ADD : ADD amount to type balance
* Operations.SUB : Substract amount from type balance
*/
function updateTokenBalance(
uint256 binBalances,
uint256 index,
uint256 amount,
Operations operation
) internal pure returns (uint256 newBinBalance) {
uint256 objectBalance = 0;
if (operation == Operations.ADD) {
objectBalance = getValueInBin(binBalances, index);
newBinBalance = writeValueInBin(binBalances, index, objectBalance + amount);
} else if (operation == Operations.SUB) {
objectBalance = getValueInBin(binBalances, index);
require(objectBalance >= amount, "can't substract more than there is");
newBinBalance = writeValueInBin(binBalances, index, objectBalance - amount);
} else if (operation == Operations.REPLACE) {
newBinBalance = writeValueInBin(binBalances, index, amount);
} else {
revert("Invalid operation"); // Bad operation
}
return newBinBalance;
}
/*
* @dev return value in binValue at position index
* @param binValue uint256 containing the balances of TYPES_PER_UINT256 types
* @param index index at which to retrieve value
* @return Value at given index in bin
*/
function getValueInBin(uint256 binValue, uint256 index) internal pure returns (uint256) {
// Mask to retrieve data for a given binData
uint256 mask = (uint256(1) << TYPES_BITS_SIZE) - 1;
// Shift amount
uint256 rightShift = 256 - TYPES_BITS_SIZE * (index + 1);
return (binValue >> rightShift) & mask;
}
/**
* @dev return the updated binValue after writing amount at index
* @param binValue uint256 containing the balances of TYPES_PER_UINT256 types
* @param index Index at which to retrieve value
* @param amount Value to store at index in bin
* @return Value at given index in bin
*/
function writeValueInBin(
uint256 binValue,
uint256 index,
uint256 amount
) internal pure returns (uint256) {
require(amount < 2**TYPES_BITS_SIZE, "Amount to write in bin is too large");
// Mask to retrieve data for a given binData
uint256 mask = (uint256(1) << TYPES_BITS_SIZE) - 1;
// Shift amount
uint256 leftShift = 256 - TYPES_BITS_SIZE * (index + 1);
return (binValue & ~(mask << leftShift)) | (amount << leftShift);
}
}