-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUniqueRoyalty.sol
125 lines (94 loc) · 4.24 KB
/
UniqueRoyalty.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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
struct CrossAddress {
address eth;
uint256 sub;
}
struct UniqueRoyaltyPart {
uint8 version;
uint8 decimals;
uint64 value;
bool isPrimarySaleOnly;
CrossAddress crossAddress;
}
library CrossAddressLib {
function toAddress(CrossAddress memory crossAddress) internal pure returns (address) {
return crossAddress.eth != address(0) ? crossAddress.eth : address(uint160(crossAddress.sub >> 96));
}
}
library UniqueRoyalty {
uint private constant DECIMALS_OFFSET = 4 * 16;
uint private constant ADDRESS_TYPE_OFFSET = 4 * (16 + 2); // 0 - eth, 1 - sub
uint private constant ROYALTY_TYPE_OFFSET = 4 * (16 + 2 + 1); // 0 - default, 1 - primary sale only
uint private constant VERSION_OFFSET = 4 * (16 + 2 + 1 + 1 + 42);
uint private constant PART_LENGTH = 32 * 2;
function decode(bytes memory b) internal pure returns (UniqueRoyaltyPart[] memory) {
if (b.length == 0) return new UniqueRoyaltyPart[](0);
require((b.length % PART_LENGTH) == 0, "Invalid bytes length, expected (32 * 2) * UniqueRoyaltyParts count");
uint partsCount = b.length / PART_LENGTH;
uint numbersCount = partsCount * 2;
UniqueRoyaltyPart[] memory parts = new UniqueRoyaltyPart[](partsCount);
// need this because numbers encoded via abi.encodePacked
bytes memory prefix = new bytes(64);
assembly {
mstore(add(prefix, 32), 32)
mstore(add(prefix, 64), numbersCount)
}
uint256[] memory encoded = abi.decode(bytes.concat(prefix, b), (uint256[]));
for (uint i = 0; i < partsCount; i++) {
parts[i] = decodePart(encoded[i * 2], encoded[i * 2 + 1]);
}
return parts;
}
function encode(UniqueRoyaltyPart[] memory parts) internal pure returns (bytes memory) {
if (parts.length == 0) return "";
uint256[] memory encoded = new uint256[](parts.length * 2);
for (uint i = 0; i < parts.length; i++) {
(uint256 encodedMeta, uint256 encodedAddress) = encodePart(parts[i]);
encoded[i * 2] = encodedMeta;
encoded[i * 2 + 1] = encodedAddress;
}
return abi.encodePacked(encoded);
}
function decodePart(bytes memory b) internal pure returns (UniqueRoyaltyPart memory) {
require(b.length == PART_LENGTH, "Invalid bytes length, expected 32 * 2");
uint256[2] memory encoded = abi.decode(b, (uint256[2]));
return decodePart(encoded[0], encoded[1]);
}
function decodePart(
uint256 _meta,
uint256 _address
) internal pure returns (UniqueRoyaltyPart memory) {
uint256 version = _meta >> VERSION_OFFSET;
bool isPrimarySaleOnly = (_meta & (1 << ROYALTY_TYPE_OFFSET)) > 0;
bool isEthereumAddress = (_meta & (1 << ADDRESS_TYPE_OFFSET)) == 0;
uint256 decimals = (_meta >> 4 * 16) & 0xFF;
uint256 value = _meta & 0xFFFFFFFFFFFFFFFF;
CrossAddress memory crossAddress = isEthereumAddress
? CrossAddress({ sub: 0, eth: address(uint160(_address)) })
: CrossAddress({ sub: _address, eth: address(0) });
UniqueRoyaltyPart memory royaltyPart = UniqueRoyaltyPart({
version: uint8(version),
decimals: uint8(decimals),
value: uint64(value),
isPrimarySaleOnly: isPrimarySaleOnly,
crossAddress: crossAddress
});
return royaltyPart;
}
function encodePart(UniqueRoyaltyPart memory royaltyPart) internal pure returns (uint256, uint256) {
uint256 encodedMeta = 0;
uint256 encodedAddress = 0;
encodedMeta |= uint256(royaltyPart.version) << VERSION_OFFSET;
if (royaltyPart.isPrimarySaleOnly) encodedMeta |= 1 << ROYALTY_TYPE_OFFSET;
if (royaltyPart.crossAddress.eth == address(0x0)) {
encodedMeta |= 1 << ADDRESS_TYPE_OFFSET;
encodedAddress = royaltyPart.crossAddress.sub;
} else {
encodedAddress = uint256(uint160(royaltyPart.crossAddress.eth));
}
encodedMeta |= uint256(royaltyPart.decimals) << DECIMALS_OFFSET;
encodedMeta |= uint256(royaltyPart.value);
return (encodedMeta, encodedAddress);
}
}