Skip to content

feat: ERC-7858 and some optimize #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 60 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
8bc5d5f
chore: change file name to 7858
MASDXI Jan 11, 2025
1f8c794
Merge branch 'main' into develop/masdxi
MASDXI Jan 12, 2025
1c3a9a3
refactor: lightweight sorted list
MASDXI Jan 13, 2025
ed9475a
test: finish test fo sorted list
MASDXI Jan 13, 2025
f8c2147
more clean up
MASDXI Jan 13, 2025
2c16f4d
refactor: remove unused function in ERC721EXP
MASDXI Jan 13, 2025
229c6a7
Merge branch 'main' into develop/masdxi
MASDXI Jan 13, 2025
c9bbde4
Merge branch 'main' into develop/masdxi
MASDXI Jan 13, 2025
719d3a3
refactor: remove unused file
MASDXI Jan 13, 2025
4074523
feat: update ERC721EXP contracts
MASDXI Jan 14, 2025
6c25951
prettier code
MASDXI Jan 14, 2025
29302af
chore: change path
MASDXI Jan 14, 2025
61b9c75
chore: adding interfaces ERC-7858Epoch
MASDXI Jan 15, 2025
5e36db4
refactor: adding ERC721Epoch abstract
MASDXI Jan 15, 2025
42cb5d2
refactor: ERC721Epoch
MASDXI Jan 16, 2025
0a11af0
Merge branch 'main' into develop/masdxi
MASDXI Jan 16, 2025
dfa8282
refactor: 💡 ERC20NearestExpiry func name and ERC721Epoch
MASDXI Jan 16, 2025
d113b75
chore: 🤖 generate copyright/license header
MASDXI Jan 17, 2025
e62e8c1
chore: 🤖 merge from main to develop/masdxi
MASDXI Jan 28, 2025
03e87f4
Merge branch 'main' into develop/masdxi
MASDXI Jan 30, 2025
5a70919
docs: ✏️ adding funding section to README.md
MASDXI Feb 18, 2025
4da6a51
Merge branch 'main' into develop/masdxi
MASDXI Feb 20, 2025
431003e
fix: 🐛 missing args in insert
MASDXI Feb 20, 2025
fe08f3a
refactor: 💡 adding logic to ERC721EXPEpochBase
MASDXI Feb 20, 2025
13d5eac
refactor: 💡 adding logic to ERC721EpochBase
MASDXI Feb 21, 2025
7ed8b47
refactor: 💡 validBalanceOf function
MASDXI Feb 22, 2025
3db857e
refactor: 💡 remove unused line
MASDXI Feb 22, 2025
5fb373e
feat: 🎸 add getInitialBlockNumber
ADISAKBOONMARK Feb 23, 2025
72ca613
feat: 🎸 add getInitialTimestamp
ADISAKBOONMARK Feb 23, 2025
fe1faa8
feat: 🎸 add getInitialPointer
ADISAKBOONMARK Feb 23, 2025
497cccd
refactor: 💡 change blockLengthCache to pointerLengthCache
ADISAKBOONMARK Feb 23, 2025
7f294be
docs: ✏️ update NatSpec
ADISAKBOONMARK Feb 23, 2025
b3822f9
docs: ✏️ update NatSpec
ADISAKBOONMARK Feb 23, 2025
0c67c6d
docs: ✏️ update NatSpec
ADISAKBOONMARK Feb 23, 2025
6d92a87
fix: 🐛 _update handling transfer expired tokenId
MASDXI Feb 24, 2025
4e9e12b
docs: ✏️ update ERC-7858 usage pattern
MASDXI Feb 24, 2025
914d534
refactor: 💡 change function name in ERC-7858 interface
MASDXI Feb 25, 2025
4944ab0
refactor: 💡 handle burn and re-mint
MASDXI Feb 26, 2025
8f6e096
refactor: 💡 handling token timestamp check
MASDXI Feb 26, 2025
9ea61e2
fix: 🐛 logic error compare start and end
MASDXI Feb 26, 2025
998257e
fix: 🐛 startTime and endTime should revert if id not exist
MASDXI Feb 26, 2025
8cc98b8
test: 💍 template test ERC7858 base
MASDXI Feb 27, 2025
8531b83
test: 💍 update testing for ERC7858
MASDXI Feb 28, 2025
f0c1d68
test: 💍 update test case for ERC7858
MASDXI Mar 1, 2025
6ffd7e4
test: 💍 use expectBalance var instead of inscope function
MASDXI Mar 1, 2025
3db8610
chore: 🤖 remove unused files
MASDXI Mar 1, 2025
76a819b
refactor: 💡 implementing missing function from interface
MASDXI Mar 1, 2025
96bca9f
refactor: 💡 add transfer logic to ERC7858
MASDXI Mar 2, 2025
6516a06
refactor: 💡 change files name and init test for erc7858 epoch
MASDXI Mar 3, 2025
b1cb4aa
test: 💍 adding test case to erc7858 and fix nonexistence token
MASDXI Mar 3, 2025
e515d2a
fix: 🐛 remove duplicate function with same business logic
parametprame Mar 3, 2025
9325b32
refactor: 💡 refactor isEpochExpired testcase
MASDXI Mar 3, 2025
8e9d2b8
test: 💍 adding testcase to erc7858
MASDXI Mar 3, 2025
5a7ce18
test: 💍 adding testcase to erc7858
MASDXI Mar 3, 2025
84c0c2c
test: 💍 adding test case to erc7858
MASDXI Mar 4, 2025
4e0dd59
fix: 🐛 incorrect interfaceId erc7858epoch
MASDXI Mar 5, 2025
5c45082
refactor: 💡 increase reability and ensure ganularity balanceOf
MASDXI Mar 5, 2025
377b7a0
refactor: 💡 increase readability of erc7858
MASDXI Mar 5, 2025
d8f46c5
perf: ⚡️ optimize update fucn erc7858
MASDXI Mar 5, 2025
c68007b
fix: 🐛 case next element infinity loop
MASDXI Mar 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ Check out the contribution [guide](CONTRIBUTING.md)

For support or any inquiries, feel free to reach out to us at [github-issue](https://github.com/Kiwari-Labs/kiwari-labs-contracts/issues) or [email protected]

## Funding

If you value our work and would like to support Kiwari Labs, please visit our [Open Collective](https://opencollective.com/kiwari-labs) page to make a donation. Thank you!

## License

This repository is released under the [Apache-2.0](LICENSE).
Expand Down
8 changes: 8 additions & 0 deletions contracts/abstracts/AbstractBLSW.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ abstract contract AbstractBLSW {
return _window.windowSize();
}

/**
* @notice Returns the initial block number of the sliding window.
* @return The initial block number.
*/
function _getInitialBlockNumber() internal view returns (uint256) {
return _window.getInitialBlockNumber();
}

/// @notice Returns the number of blocks in each epoch.
/// @return The number of blocks in an epoch.
function _blocksInEpoch() internal view returns (uint40) {
Expand Down
8 changes: 8 additions & 0 deletions contracts/abstracts/AbstractTLSW.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ abstract contract AbstractTLSW {
return _window.windowSize();
}

/**
* @notice Returns the initial timestamp of the sliding window.
* @return The initial timestamp.
*/
function _getInitialTimestamp() internal view returns (uint256) {
return _window.getInitialTimestamp();
}

/// @notice Returns the number of seconds in an epoch.
/// @return The number of seconds in an epoch.
function _secondsInEpoch() internal view returns (uint40) {
Expand Down
4 changes: 4 additions & 0 deletions contracts/tokens/ERC20/ERC20BLSW.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ abstract contract ERC20BLSW is IERC7818, ERC20EXPBase, BLSW {
bool development_
) ERC20EXPBase(name_, symbol_) BLSW(initialBlockNumber_, blocksPerEpoch_, windowSize_, development_) {}

function _getInitialPointer() internal view virtual override returns (uint256) {
return _getInitialBlockNumber();
}

function _epochType() internal pure virtual override returns (EPOCH_TYPE) {
return EPOCH_TYPE.BLOCKS_BASED;
}
Expand Down
87 changes: 54 additions & 33 deletions contracts/tokens/ERC20/ERC20EXPBase.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0 <0.9.0;

import {SCDLL} from "../../utils/datastructures/LSCDLL.sol";
/*
* @title ERC20EXP Base
* @author Kiwari Labs
*/

import {SortedList} from "../../utils/datastructures/SortedList.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
Expand All @@ -12,15 +17,15 @@ import {IERC7818} from "./interfaces/IERC7818.sol";
* @author Kiwari Labs
*/
abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC7818 {
using SCDLL for SCDLL.List;
using SortedList for SortedList.List;

string private _name;
string private _symbol;

struct Epoch {
uint256 totalBalance;
mapping(uint256 account => uint256 balance) balances;
SCDLL.List list;
mapping(uint256 => uint256) balances;
SortedList.List list;
}

mapping(uint256 epoch => mapping(address account => Epoch)) private _balances;
Expand Down Expand Up @@ -85,8 +90,8 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
*/
function _refreshBalanceAtEpoch(address account, uint256 epoch, uint256 pointer, uint256 duration) private {
Epoch storage _account = _balances[epoch][account];
if (_account.list.size() > 0) {
uint256 element = _account.list.head();
if (!_account.list.isEmpty()) {
uint256 element = _account.list.front();
uint256 balance;
unchecked {
while (pointer - element >= duration) {
Expand All @@ -95,7 +100,7 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
}
}
if (balance > 0) {
_account.list.lazyShrink(element);
_account.list.shrink(element);
_account.totalBalance -= balance;
}
}
Expand Down Expand Up @@ -133,11 +138,11 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
_recipient.balances[pointer] += value;
_worldStateBalances[pointer] += value;
}
_recipient.list.insert(pointer);
_recipient.list.insert(pointer, false);
} else {
uint256 blockLengthCache = _getPointersInWindow();
uint256 pointerLengthCache = _getPointersInWindow();
(uint256 fromEpoch, uint256 toEpoch) = _getWindowRage(pointer);
_refreshBalanceAtEpoch(from, fromEpoch, pointer, blockLengthCache);
_refreshBalanceAtEpoch(from, fromEpoch, pointer, pointerLengthCache);
uint256 balance = _computeBalanceOverEpochRange(fromEpoch, toEpoch, from);
if (balance < value) {
revert ERC20InsufficientBalance(from, balance, value);
Expand All @@ -147,7 +152,7 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
// burn token from
while (fromEpoch <= toEpoch && pendingValue > 0) {
Epoch storage _spender = _balances[fromEpoch][from];
uint256 element = _spender.list.head();
uint256 element = _spender.list.front();
while (element > 0 && pendingValue > 0) {
balance = _spender.balances[element];
if (balance <= pendingValue) {
Expand Down Expand Up @@ -177,7 +182,7 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
while (fromEpoch <= toEpoch && pendingValue > 0) {
Epoch storage _spender = _balances[fromEpoch][from];
Epoch storage _recipient = _balances[fromEpoch][to];
uint256 element = _spender.list.head();
uint256 element = _spender.list.front();
while (element > 0 && pendingValue > 0) {
balance = _spender.balances[element];
if (balance <= pendingValue) {
Expand All @@ -188,7 +193,7 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
_recipient.totalBalance += balance;
_recipient.balances[element] += balance;
}
_recipient.list.insert(element);
_recipient.list.insert(element, false);
element = _spender.list.next(element);
_spender.list.remove(_spender.list.previous(element));
} else {
Expand All @@ -198,7 +203,7 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
_recipient.totalBalance += pendingValue;
_recipient.balances[element] += pendingValue;
}
_recipient.list.insert(element);
_recipient.list.insert(element, false);
pendingValue = 0;
}
}
Expand All @@ -213,26 +218,27 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
}

/**
* @notice Finds the index of the first valid block balance in a sorted list of block numbers.
* A block balance index is considered valid if the difference between the current pointer
* and the block number at the index (key) is less than the duration.
* @dev This function is used to determine the first valid block balance index within a sorted circular doubly linked list.
* It iterates through the list starting from the head and stops when it finds a valid index or reaches the end of the list.
* @param account The account address.
* @notice Finds the index of the first valid balance in a sorted list based on a given pointer.
* @dev Determines the first valid balance index in a sorted circular doubly linked list.
* A balance index is considered valid if the difference between the current pointer
* and the index (key) is less than the specified duration.
* Iterates through the list starting from the front until it finds a valid index or reaches the end.
* @param account The address of the account.
* @param epoch The epoch number.
* @param pointer The current block number.
* @param duration The maximum allowed difference between pointer and the key.
* @return element The index of the first valid block balance.
* @param pointer The reference point used for validation.
* @param duration The maximum allowed difference between the pointer and the key.
* @return element The index of the first valid balance.
* @return value The balance associated with the valid index.
*/
function _findValidBalance(
address account,
uint256 epoch,
uint256 pointer,
uint256 duration
) internal view returns (uint256 element, uint256 value) {
SCDLL.List storage list = _balances[epoch][account].list;
if (list.size() > 0) {
element = list.head();
SortedList.List storage list = _balances[epoch][account].list;
if (!list.isEmpty()) {
element = list.front();
unchecked {
while (pointer - element >= duration) {
element = list.next(element);
Expand Down Expand Up @@ -310,7 +316,7 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
_recipient.totalBalance += balance;
_recipient.balances[element] += balance;
}
_recipient.list.insert(element);
_recipient.list.insert(element, false);
element = _spender.list.next(element);
_spender.list.remove(_spender.list.previous(element));
} else {
Expand All @@ -320,7 +326,7 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
_recipient.totalBalance += pendingValue;
_recipient.balances[element] += pendingValue;
}
_recipient.list.insert(element);
_recipient.list.insert(element, false);
pendingValue = 0;
}
}
Expand Down Expand Up @@ -449,10 +455,10 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
}

/**
* @notice Retrieves the total balance stored at a specific block.
* @dev This function returns the balance of the given block from the internal `_worldStateBalances` mapping.
* @param pointer The block number for which the balance is being queried.
* @return balance The total balance stored at the given block number.
* @notice Retrieves the total balance stored at a specific pointer.
* @dev This function returns the balance of the given pointer from the internal `_worldStateBalances` mapping.
* @param pointer The reference point for which the balance is being queried.
* @return balance The total balance stored at the given pointer.
*/
function getWorldStateBalance(uint256 pointer) external view virtual returns (uint256) {
return _worldStateBalances[pointer];
Expand All @@ -462,7 +468,12 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
* @custom:gas-inefficiency if not limit the size of array
*/
function tokenList(address account, uint256 epoch) external view virtual returns (uint256[] memory list) {
list = _balances[epoch][account].list.ascending();
list = _balances[epoch][account].list.toArray();
}

/// @custom:gas-inefficiency if not limit the size of array
function tokenList(address account, uint256 epoch, uint256 start) external view virtual returns (uint256[] memory list) {
list = _balances[epoch][account].list.toArray(start);
}

/**
Expand Down Expand Up @@ -513,6 +524,14 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
return balance;
}

/**
* @notice Returns the initial pointer of the token.
* @return The initial pointer value.
*/
function getInitialPointer() public view virtual returns (uint256) {
return _getInitialPointer();
}

/**
* @dev See {IERC20-allowance}.
*/
Expand Down Expand Up @@ -623,6 +642,8 @@ abstract contract ERC20EXPBase is Context, IERC20Errors, IERC20Metadata, IERC781
return true;
}

function _getInitialPointer() internal view virtual returns (uint256) {}

function _epochType() internal pure virtual returns (EPOCH_TYPE) {}

function _getEpoch(uint256 pointer) internal view virtual returns (uint256) {}
Expand Down
4 changes: 4 additions & 0 deletions contracts/tokens/ERC20/ERC20TLSW.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ abstract contract ERC20TLSW is IERC7818, ERC20EXPBase, TLSW {
bool development_
) ERC20EXPBase(name_, symbol_) TLSW(initialBlockTimestamp_, secondsPerEpoch_, windowSize_, development_) {}

function _getInitialPointer() internal view virtual override returns (uint256) {
return _getInitialTimestamp();
}

function _epochType() internal pure virtual override returns (EPOCH_TYPE) {
return EPOCH_TYPE.TIME_BASED;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ abstract contract ERC7818NearestExpiryQuery is ERC20EXPBase {
* @return value The balance value associated with the nearest expiry.
* @return estimateExpiry The estimated expiry timestamp or block number for the account's balance.
*/
function nearestExpiryOf(address account) public view virtual returns (uint256 value, uint256 estimateExpiry) {
function getNearestExpiryOf(address account) public view returns (uint256 value, uint256 estimateExpiry) {
uint256 pointer = _pointerProvider();
(uint256 fromEpoch, ) = _getWindowRage(pointer);
uint256 duration = _getPointersInWindow();
Expand Down
26 changes: 26 additions & 0 deletions contracts/tokens/ERC721/ERC721B.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0 <0.9.0;

/**
* @title ERC721EXP using Block-Height-Based
* @dev ERC721EXP Base contract each token have individual expiration date.
* @author Kiwari Labs
*/

import {ERC721EXPBase} from "./ERC721EXPBase.sol";

abstract contract ERC721B is ERC721EXPBase {
constructor(string memory name_, string memory symbol_) ERC721EXPBase(name_, symbol_) {}

function _blockNumberProvider() internal view virtual returns (uint256) {
return block.number;
}

function _expiryType() internal pure virtual override returns (EXPIRY_TYPE) {
return EXPIRY_TYPE.BLOCKS_BASED;
}

function _pointerProvider() internal view virtual override returns (uint256) {
return _blockNumberProvider();
}
}
Loading