implementation of weaver idea for bridge in

Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
Uncle Stretch 2026-07-23 18:22:57 +03:00
parent ebd23d9829
commit f4801edf73
Signed by: str3tch
GPG Key ID: 84F3190747EE79AA
6 changed files with 839 additions and 4 deletions

View File

@ -1,20 +1,36 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
import {Hashes} from "./libraries/Hashes.sol";
import {IGatekeeper} from "./interfaces/IGatekeeper.sol"; import {IGatekeeper} from "./interfaces/IGatekeeper.sol";
import {IGatekeeperMetadata} from "./interfaces/IGatekeeperMetadata.sol"; import {IGatekeeperWeaver} from "./interfaces/IGatekeeperWeaver.sol";
import {IGatekeeperMetadata} from "./interfaces/IGatekeeperMetadata.sol";
import {Checkpoints} from "./libraries/Checkpoints.sol";
contract Gatekeeper is IGatekeeper, IGatekeeperMetadata, IGatekeeperWeaver {
using Checkpoints for Checkpoints.Trace256;
using Checkpoints for Checkpoints.Trace160;
contract Gatekeeper is IGatekeeper, IGatekeeperMetadata {
uint256 public constant DIVISOR = 1e6; uint256 public constant DIVISOR = 1e6;
uint256 public constant DEPTH = 8;
uint256 public constant SLOTS = 2 ** DEPTH;
uint256 public constant ENTRIES = DEPTH * SLOTS;
uint256 public override existentialDeposit; uint256 public override existentialDeposit;
uint256 public override ghostedSupply; uint256 public override ghostedSupply;
uint256 public currentSession;
address public immutable staking; // forge-lint: disable-line(screaming-snake-case-immutable) address public immutable staking; // forge-lint: disable-line(screaming-snake-case-immutable)
GatekeeperMetadata private _metadata; GatekeeperMetadata private _metadata;
uint256 private _aggregatedPublicKey; uint256 private _aggregatedPublicKey;
mapping(uint256 => mapping(uint256 => bool)) private _executedTransaction; mapping(uint256 => mapping(uint256 => bool)) private _executedTransaction;
mapping(uint256 => mapping(uint256 => Checkpoints.Trace256)) internal _treeNodes;
mapping(uint256 => mapping(uint256 => Checkpoints.Trace160)) internal _slotLenghts;
mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues;
mapping(uint256 => uint256) internal _filledEntries;
constructor( constructor(
address _staking, address _staking,
uint256 _ghostedSupply, uint256 _ghostedSupply,
@ -37,12 +53,106 @@ contract Gatekeeper is IGatekeeper, IGatekeeperMetadata {
return _metadata; return _metadata;
} }
function ghost(bytes32 receiver, uint256 amount) external override { function getSlotValues(
uint256 globalIndex,
uint256 session,
uint256 atBlock
) external view returns (bytes32[] memory) {
uint256 slotIndex = globalIndex % SLOTS;
// forge-lint: disable-next-line(unsafe-typecast)
uint256 length = _slotLenghts[session][slotIndex].upperLookupRecent(uint96(atBlock));
bytes32[] memory values = new bytes32[](length);
uint256 i;
for (; i < length; ) {
values[i] = _slotValues[session][slotIndex][i];
unchecked { ++i; }
}
return values;
}
function getRoot(uint256 session, uint256 atBlock) public view returns (bytes32, uint256) {
uint256 currentLevelCount = SLOTS >> 1;
bytes32[] memory currentLevel = new bytes32[](currentLevelCount);
uint256 i;
for (; i < currentLevelCount;) {
uint256 j = i << 1;
bytes32 left = bytes32(_treeNodes[session][j].upperLookupRecent(atBlock));
bytes32 right = bytes32(_treeNodes[session][j | 1].upperLookupRecent(atBlock));
currentLevel[i] = Hashes.efficientKeccak256(left, right);
unchecked { ++i; }
}
while (currentLevelCount > 1) {
currentLevelCount >>= 1;
i = 0;
for (; i < currentLevelCount;) {
uint256 j = i << 1;
bytes32 left = currentLevel[j];
bytes32 right = currentLevel[j | 1];
currentLevel[i] = Hashes.efficientKeccak256(left, right);
unchecked { ++i; }
}
}
return (currentLevel[0], _filledEntries[session]);
}
function getProof(
uint256 globalIndex,
uint256 session,
uint256 atBlock
) external view returns (bytes32[] memory) {
uint256 currentLevelCount = SLOTS;
uint256 currentIndex = globalIndex % SLOTS;
bytes32[] memory proof = new bytes32[](DEPTH);
bytes32[] memory currentLevel = new bytes32[](currentLevelCount);
uint256 i;
for (; i < currentLevelCount;) {
currentLevel[i] = bytes32(_treeNodes[session][i].upperLookupRecent(atBlock));
unchecked { ++i; }
}
uint256 proofIndex;
while (currentLevelCount > 1) {
currentLevelCount >>= 1;
uint256 siblingIndex = currentIndex ^ 1;
proof[proofIndex] = currentLevel[siblingIndex];
i = 0;
for (; i < currentLevelCount;) {
uint256 j = i << 1;
bytes32 left = currentLevel[j];
bytes32 right = currentLevel[j | 1];
currentLevel[i] = Hashes.efficientKeccak256(left, right);
unchecked { ++i; }
}
currentIndex >>= 1;
unchecked { ++proofIndex; }
}
return proof;
}
function ghost(bytes32 receiver, uint256 amount) external override returns (uint256) {
if (msg.sender != staking) revert NotStaking(); if (msg.sender != staking) revert NotStaking();
if (amount < existentialDeposit) revert NonExistentAmount(); if (amount < existentialDeposit) revert NonExistentAmount();
ghostedSupply += amount; ghostedSupply += amount;
_metadata.amountIn += uint104(amount); // forge-lint: disable-line(unsafe-typecast) _metadata.amountIn += uint104(amount); // forge-lint: disable-line(unsafe-typecast)
emit Ghosted(receiver, amount); emit Ghosted(receiver, amount);
return _insertTreeNode(receiver, amount);
} }
function materialize( function materialize(
@ -88,6 +198,7 @@ contract Gatekeeper is IGatekeeper, IGatekeeperMetadata {
if (_incorrectSignature(rx, s, message)) revert WrongSignature(); if (_incorrectSignature(rx, s, message)) revert WrongSignature();
_aggregatedPublicKey = aggregatedPublicKey; _aggregatedPublicKey = aggregatedPublicKey;
emit Rotated(aggregatedPublicKey); emit Rotated(aggregatedPublicKey);
} }
@ -105,4 +216,34 @@ contract Gatekeeper is IGatekeeper, IGatekeeperMetadata {
// always bad signature for now // always bad signature for now
return true; return true;
} }
function _insertTreeNode(bytes32 who, uint256 amount) private returns (uint256 globalIndex) {
uint256 session = currentSession;
globalIndex = _filledEntries[session];
if (globalIndex >= ENTRIES) {
unchecked { ++session; }
currentSession = session;
globalIndex = 0;
}
uint256 slotIndex = globalIndex % SLOTS;
uint256 valueIndex = globalIndex / SLOTS;
uint160 length = uint160(_slotValues[session][slotIndex].length + 1);
bytes32 preimage = _computeArgumentsHash(valueIndex, amount, who);
bytes32 currHash = Hashes.efficientKeccak256(preimage);
bytes32 slotHash = bytes32(_treeNodes[session][slotIndex].latest());
slotHash = Hashes.efficientKeccak256(slotHash, currHash);
_treeNodes[session][slotIndex].push(block.number, uint256(slotHash));
_slotLenghts[session][slotIndex].push(uint96(block.number), length);
_slotValues[session][slotIndex].push(preimage);
unchecked { ++_filledEntries[session]; }
}
function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) {
return Hashes.efficientKeccak256(bytes32(i), Hashes.efficientKeccak256(bytes32(a), r));
}
} }

View File

@ -13,7 +13,7 @@ interface IGatekeeper {
function ghostedSupply() external view returns (uint256); function ghostedSupply() external view returns (uint256);
function existentialDeposit() external view returns (uint256); function existentialDeposit() external view returns (uint256);
function ghost(bytes32 receiver, uint256 amount) external; function ghost(bytes32 receiver, uint256 amount) external returns (uint256);
function materialize(address receiver, uint256 amount, uint256 rx, uint256 s) external; function materialize(address receiver, uint256 amount, uint256 rx, uint256 s) external;
function rotate(uint256 aggregatedPublicKey, uint256 rx, uint256 s) external; function rotate(uint256 aggregatedPublicKey, uint256 rx, uint256 s) external;
} }

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IGatekeeperWeaver {
function getSlotValues(uint256 globalIndex, uint256 session, uint256 atBlock) external returns (bytes32[] memory);
function getProof(uint256 globalIndex, uint256 session, uint256 atBlock) external returns (bytes32[] memory);
function getRoot(uint256 session, uint256 atBlock) external returns (bytes32, uint256);
}

View File

@ -0,0 +1,601 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Math} from "./Math.sol";
library Checkpoints {
error CheckpointUnorderedInsertion();
struct Trace256 {
Checkpoint256[] _checkpoints;
}
struct Checkpoint256 {
uint256 _key;
uint256 _value;
}
function push(
Trace256 storage self,
uint256 key,
uint256 value
) internal returns (uint256 oldValue, uint256 newValue) {
return _insert(self._checkpoints, key, value);
}
function lowerLookup(Trace256 storage self, uint256 key) internal view returns (uint256) {
uint256 len = self._checkpoints.length;
uint256 index = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return index == len ? 0 : _unsafeAccess(self._checkpoints, index)._value;
}
function upperLookup(Trace256 storage self, uint256 key) internal view returns (uint256) {
uint256 len = self._checkpoints.length;
uint256 index = _upperBinaryLookup(self._checkpoints, key, 0, len);
return index == 0 ? 0 : _unsafeAccess(self._checkpoints, index - 1)._value;
}
function upperLookupRecent(Trace256 storage self, uint256 key) internal view returns (uint256) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 index = _upperBinaryLookup(self._checkpoints, key, low, high);
return index == 0 ? 0 : _unsafeAccess(self._checkpoints, index - 1)._value;
}
function latest(Trace256 storage self) internal view returns (uint256) {
uint256 len = self._checkpoints.length;
return len == 0 ? 0 : _unsafeAccess(self._checkpoints, len - 1)._value;
}
function latestCheckpoint(Trace256 storage self) internal view returns (bool exists, uint256 _key, uint256 _value) {
uint256 len = self._checkpoints.length;
if (len == 0) {
return (false, 0, 0);
} else {
Checkpoint256 storage ckpt = _unsafeAccess(self._checkpoints, len - 1);
return (true, ckpt._key, ckpt._value);
}
}
function length(Trace256 storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
function at(Trace256 storage self, uint32 index) internal view returns (Checkpoint256 memory) {
return pos(self, index);
}
function pos(Trace256 storage self, uint32 index) internal view returns (Checkpoint256 memory) {
return self._checkpoints[index];
}
function _insert(
Checkpoint256[] storage self,
uint256 key,
uint256 value
) private returns (uint256 oldValue, uint256 newValue) {
uint256 len = self.length;
if (len > 0) {
Checkpoint256 storage last = _unsafeAccess(self, len - 1);
uint256 lastKey = last._key;
uint256 lastValue = last._value;
if (lastKey > key) {
revert CheckpointUnorderedInsertion();
}
if (lastKey == key) {
last._value = value;
} else {
self.push(Checkpoint256({_key: key, _value: value}));
}
return (lastValue, value);
} else {
self.push(Checkpoint256({_key: key, _value: value}));
return (0, value);
}
}
function _upperBinaryLookup(
Checkpoint256[] storage self,
uint256 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
function _lowerBinaryLookup(
Checkpoint256[] storage self,
uint256 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
function _unsafeAccess(
Checkpoint256[] storage self,
uint256 index
) private pure returns (Checkpoint256 storage result) {
assembly {
mstore(0x00, self.slot)
result.slot := add(keccak256(0x00, 0x20), mul(index, 2))
}
}
struct Trace224 {
Checkpoint224[] _checkpoints;
}
struct Checkpoint224 {
uint32 _key;
uint224 _value;
}
function push(
Trace224 storage self,
uint32 key,
uint224 value
) internal returns (uint224 oldValue, uint224 newValue) {
return _insert(self._checkpoints, key, value);
}
function lowerLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
uint256 len = self._checkpoints.length;
uint256 index = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return index == len ? 0 : _unsafeAccess(self._checkpoints, index)._value;
}
function upperLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
uint256 len = self._checkpoints.length;
uint256 index = _upperBinaryLookup(self._checkpoints, key, 0, len);
return index == 0 ? 0 : _unsafeAccess(self._checkpoints, index - 1)._value;
}
function upperLookupRecent(Trace224 storage self, uint32 key) internal view returns (uint224) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 index = _upperBinaryLookup(self._checkpoints, key, low, high);
return index == 0 ? 0 : _unsafeAccess(self._checkpoints, index - 1)._value;
}
function latest(Trace224 storage self) internal view returns (uint224) {
uint256 len = self._checkpoints.length;
return len == 0 ? 0 : _unsafeAccess(self._checkpoints, len - 1)._value;
}
function latestCheckpoint(Trace224 storage self) internal view returns (bool exists, uint32 _key, uint224 _value) {
uint256 len = self._checkpoints.length;
if (len == 0) {
return (false, 0, 0);
} else {
Checkpoint224 storage ckpt = _unsafeAccess(self._checkpoints, len - 1);
return (true, ckpt._key, ckpt._value);
}
}
function length(Trace224 storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
function at(Trace224 storage self, uint32 index) internal view returns (Checkpoint224 memory) {
return pos(self, index);
}
function pos(Trace224 storage self, uint32 index) internal view returns (Checkpoint224 memory) {
return self._checkpoints[index];
}
function _insert(
Checkpoint224[] storage self,
uint32 key,
uint224 value
) private returns (uint224 oldValue, uint224 newValue) {
uint256 len = self.length;
if (len > 0) {
Checkpoint224 storage last = _unsafeAccess(self, len - 1);
uint32 lastKey = last._key;
uint224 lastValue = last._value;
if (lastKey > key) {
revert CheckpointUnorderedInsertion();
}
if (lastKey == key) {
last._value = value;
} else {
self.push(Checkpoint224({_key: key, _value: value}));
}
return (lastValue, value);
} else {
self.push(Checkpoint224({_key: key, _value: value}));
return (0, value);
}
}
function _upperBinaryLookup(
Checkpoint224[] storage self,
uint32 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
function _lowerBinaryLookup(
Checkpoint224[] storage self,
uint32 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
function _unsafeAccess(
Checkpoint224[] storage self,
uint256 index
) private pure returns (Checkpoint224 storage result) {
assembly {
mstore(0x00, self.slot)
result.slot := add(keccak256(0x00, 0x20), index)
}
}
struct Trace208 {
Checkpoint208[] _checkpoints;
}
struct Checkpoint208 {
uint48 _key;
uint208 _value;
}
function push(
Trace208 storage self,
uint48 key,
uint208 value
) internal returns (uint208 oldValue, uint208 newValue) {
return _insert(self._checkpoints, key, value);
}
function lowerLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {
uint256 len = self._checkpoints.length;
uint256 index = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return index == len ? 0 : _unsafeAccess(self._checkpoints, index)._value;
}
function upperLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {
uint256 len = self._checkpoints.length;
uint256 index = _upperBinaryLookup(self._checkpoints, key, 0, len);
return index == 0 ? 0 : _unsafeAccess(self._checkpoints, index - 1)._value;
}
function upperLookupRecent(Trace208 storage self, uint48 key) internal view returns (uint208) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 index = _upperBinaryLookup(self._checkpoints, key, low, high);
return index == 0 ? 0 : _unsafeAccess(self._checkpoints, index - 1)._value;
}
function latest(Trace208 storage self) internal view returns (uint208) {
uint256 len = self._checkpoints.length;
return len == 0 ? 0 : _unsafeAccess(self._checkpoints, len - 1)._value;
}
function latestCheckpoint(Trace208 storage self) internal view returns (bool exists, uint48 _key, uint208 _value) {
uint256 len = self._checkpoints.length;
if (len == 0) {
return (false, 0, 0);
} else {
Checkpoint208 storage ckpt = _unsafeAccess(self._checkpoints, len - 1);
return (true, ckpt._key, ckpt._value);
}
}
function length(Trace208 storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
function at(Trace208 storage self, uint32 index) internal view returns (Checkpoint208 memory) {
return pos(self, index);
}
function pos(Trace208 storage self, uint32 index) internal view returns (Checkpoint208 memory) {
return self._checkpoints[index];
}
function _insert(
Checkpoint208[] storage self,
uint48 key,
uint208 value
) private returns (uint208 oldValue, uint208 newValue) {
uint256 len = self.length;
if (len > 0) {
Checkpoint208 storage last = _unsafeAccess(self, len - 1);
uint48 lastKey = last._key;
uint208 lastValue = last._value;
if (lastKey > key) {
revert CheckpointUnorderedInsertion();
}
if (lastKey == key) {
last._value = value;
} else {
self.push(Checkpoint208({_key: key, _value: value}));
}
return (lastValue, value);
} else {
self.push(Checkpoint208({_key: key, _value: value}));
return (0, value);
}
}
function _upperBinaryLookup(
Checkpoint208[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
function _lowerBinaryLookup(
Checkpoint208[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
function _unsafeAccess(
Checkpoint208[] storage self,
uint256 index
) private pure returns (Checkpoint208 storage result) {
assembly {
mstore(0x00, self.slot)
result.slot := add(keccak256(0x00, 0x20), index)
}
}
struct Trace160 {
Checkpoint160[] _checkpoints;
}
struct Checkpoint160 {
uint96 _key;
uint160 _value;
}
function push(
Trace160 storage self,
uint96 key,
uint160 value
) internal returns (uint160 oldValue, uint160 newValue) {
return _insert(self._checkpoints, key, value);
}
function lowerLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
uint256 len = self._checkpoints.length;
uint256 index = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return index == len ? 0 : _unsafeAccess(self._checkpoints, index)._value;
}
function upperLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
uint256 len = self._checkpoints.length;
uint256 index = _upperBinaryLookup(self._checkpoints, key, 0, len);
return index == 0 ? 0 : _unsafeAccess(self._checkpoints, index - 1)._value;
}
function upperLookupRecent(Trace160 storage self, uint96 key) internal view returns (uint160) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 index = _upperBinaryLookup(self._checkpoints, key, low, high);
return index == 0 ? 0 : _unsafeAccess(self._checkpoints, index - 1)._value;
}
function latest(Trace160 storage self) internal view returns (uint160) {
uint256 len = self._checkpoints.length;
return len == 0 ? 0 : _unsafeAccess(self._checkpoints, len - 1)._value;
}
function latestCheckpoint(Trace160 storage self) internal view returns (bool exists, uint96 _key, uint160 _value) {
uint256 len = self._checkpoints.length;
if (len == 0) {
return (false, 0, 0);
} else {
Checkpoint160 storage ckpt = _unsafeAccess(self._checkpoints, len - 1);
return (true, ckpt._key, ckpt._value);
}
}
function length(Trace160 storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
function at(Trace160 storage self, uint32 index) internal view returns (Checkpoint160 memory) {
return pos(self, index);
}
function pos(Trace160 storage self, uint32 index) internal view returns (Checkpoint160 memory) {
return self._checkpoints[index];
}
function _insert(
Checkpoint160[] storage self,
uint96 key,
uint160 value
) private returns (uint160 oldValue, uint160 newValue) {
uint256 len = self.length;
if (len > 0) {
Checkpoint160 storage last = _unsafeAccess(self, len - 1);
uint96 lastKey = last._key;
uint160 lastValue = last._value;
if (lastKey > key) {
revert CheckpointUnorderedInsertion();
}
if (lastKey == key) {
last._value = value;
} else {
self.push(Checkpoint160({_key: key, _value: value}));
}
return (lastValue, value);
} else {
self.push(Checkpoint160({_key: key, _value: value}));
return (0, value);
}
}
function _upperBinaryLookup(
Checkpoint160[] storage self,
uint96 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
function _lowerBinaryLookup(
Checkpoint160[] storage self,
uint96 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
function _unsafeAccess(
Checkpoint160[] storage self,
uint256 index
) private pure returns (Checkpoint160 storage result) {
assembly {
mstore(0x00, self.slot)
result.slot := add(keccak256(0x00, 0x20), index)
}
}
}

19
src/libraries/Hashes.sol Normal file
View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
library Hashes {
function efficientKeccak256(bytes32 a) internal pure returns (bytes32 value) {
assembly ("memory-safe") {
mstore(0x00, a)
value := keccak256(0x00, 0x20)
}
}
function efficientKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32 value) {
assembly ("memory-safe") {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}

66
src/libraries/Math.sol Normal file
View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
library Math {
function average(uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
return (a & b) + (a ^ b) / 2;
}
}
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
if (a <= 1) {
return a;
}
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
xn = (3 * xn) >> 1;
xn = (xn + a / xn) >> 1;
xn = (xn + a / xn) >> 1;
xn = (xn + a / xn) >> 1;
xn = (xn + a / xn) >> 1;
xn = (xn + a / xn) >> 1;
xn = (xn + a / xn) >> 1;
return xn - toUint(xn > a / xn);
}
}
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}