Skip to content
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

feat: [Hardhat] implement nft support for the json rpc api library and js mock … #173

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

arianejasuwienas
Copy link
Contributor

@arianejasuwienas arianejasuwienas commented Jan 7, 2025

Description:
Implementing support for NFTs in the JS library for the JSON-RPC mock, as well as in the JSON-RPC server that utilizes this library.

The reason I've used persistent storage here:

We cannot determine which NFT numbers have been minted unless someone specifically queries them. This can be addressed in one of two ways:

  1. Dynamic loading on first access: Load each NFT's details dynamically the first time its slot is queried. Once accessed, store information about the existence of this NFT. This is my current approach. However, this may result in a memory leak/optimisation when a large number of tests or NFT requests are made, as memory is never freed (this is where the name: persistentStorage comes from).

  2. Preloading All NFTs: Load the details of all NFTs each time the token storage is accessed. While this avoids memory leaks, it could lead to excessive memory usage and generate a large number of unnecessary API requests for NFTs that we are not interested about :/ .

Why it might be an issue:

Let's consider scenario where:

  1. We have a long tokenUri that spans more than one storage slot.
  2. Someone attempts to access the value in the second slot without first querying the initial slot.

We won’t be able to provide the correct result. This is because persistentStorage is only populated when the request for the first slot of the string is made (only then we can deduce the serial NFT number from the slot address). I don't think it's not a critical issue though...

Related issue(s):

#80

Notes for reviewer:

Checklist

  • Documented (Code comments, README, etc.)
  • Tested (unit, integration, etc.)

@arianejasuwienas arianejasuwienas force-pushed the 80-add-nft-support-to-the-js-library branch from 53f31d6 to 0303268 Compare January 7, 2025 11:16
@arianejasuwienas arianejasuwienas changed the title feat: implement nft support for the json rpc api library and js mock … feat: [JS Library] implement nft support for the json rpc api library and js mock … Jan 7, 2025
@arianejasuwienas arianejasuwienas self-assigned this Jan 7, 2025
@arianejasuwienas arianejasuwienas added the New Feature A new feature, service, or documentation. Major changes that are not backwards compatible. label Jan 7, 2025
@arianejasuwienas arianejasuwienas linked an issue Jan 7, 2025 that may be closed by this pull request
@arianejasuwienas arianejasuwienas marked this pull request as ready for review January 7, 2025 11:58
@arianejasuwienas arianejasuwienas requested a review from a team as a code owner January 7, 2025 11:58
@arianejasuwienas arianejasuwienas changed the title feat: [JS Library] implement nft support for the json rpc api library and js mock … feat: [Hardhat] implement nft support for the json rpc api library and js mock … Jan 7, 2025
@acuarica acuarica added feature Enhancing an existing feature driven by business requirements. Typically backwards compatible. and removed New Feature A new feature, service, or documentation. Major changes that are not backwards compatible. labels Jan 7, 2025
Copy link
Contributor

@acuarica acuarica left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good, left some questions/comments

src/slotmap.js Outdated Show resolved Hide resolved
src/index.js Outdated
Comment on lines 242 to 244
persistentSlotMapOf(tokenId).store(nrequestedSlot, atob(metadata));
}
let unresolvedValues = persistentSlotMapOf(tokenId).load(nrequestedSlot);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moreover, not sure this works as intended, since it's going to always write when asked for token uri.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When querying the first slot holding the tokenUri, it will store the values for all slots containing this single string.

@acuarica
Copy link
Contributor

acuarica commented Jan 8, 2025

To improve the discussion regarding persistentSlotMapOf, please describe how it works in the PR.

@arianejasuwienas arianejasuwienas force-pushed the 80-add-nft-support-to-the-js-library branch from 2d33834 to d6c17f3 Compare January 8, 2025 15:04
@arianejasuwienas arianejasuwienas requested review from a team as code owners January 8, 2025 15:06
src/index.d.ts Outdated
* @param serialId The serial id of the NFT.
* @param blockNumber
*/
getNftByTokenIdAndNumber(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: to keep the naming consistent

Suggested change
getNftByTokenIdAndNumber(
getNftByTokenIdAndSerial(

src/slotmap.js Outdated Show resolved Hide resolved
src/slotmap.js Outdated
/**
* @type {Array<PersistentStorage>}
*/
const persistentStorage = [];
Copy link
Contributor

@acuarica acuarica Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually a Map, whose keys are addresses (and blockNumber, see below), right? We can use a Map or object to be more explicit and avoid doing the lookup below.

src/slotmap.js Outdated
/**
* @type {Array<PersistentStorage>}
*/
const persistentStorage = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move this global state to index.js.

src/slotmap.js Outdated
* @returns {PersistentSlotMap} An object with `load` and `store` methods.
*/
function persistentSlotMapOf(target) {
const found = persistentStorage.filter(occupiedSlot => occupiedSlot.target === target);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about blockNumber? The key should be target and blockNumber because the user can change the blockNumber.

mishomihov00
mishomihov00 previously approved these changes Jan 9, 2025
Copy link

@mishomihov00 mishomihov00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review applies to .github/workflows/test.yml

…e index.js file, making the blocknumber part of its key (#80)

Signed-off-by: Mariusz Jasuwienas <[email protected]>
@arianejasuwienas
Copy link
Contributor Author

@acuarica Thank you for the review! I’ve applied your suggestions - please let me know if everything looks good now.

Copy link
Contributor

@acuarica acuarica left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good, final touches

module.exports = { slotMapOf, packValues };
/**
* Represents the value in the persistent storage.
* Each token has its own SlotMap. Any value can be assigned to this storage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Each token has its own SlotMap. Any value can be assigned to this storage
* Each token and `blockNumber` has its own SlotMap. Any value can be assigned to this storage

Comment on lines +294 to +298
const initialized = this._map.get(key) || new SlotMap();
if (!this._map.has(key)) {
this._map.set(key, initialized);
}
return initialized;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to avoid the double guard

Suggested change
const initialized = this._map.get(key) || new SlotMap();
if (!this._map.has(key)) {
this._map.set(key, initialized);
}
return initialized;
let slotMap = this._map.get(key);
if (slotMap === undefined) {
slotMap = new SlotMap();
this._map.set(key, slotMap);
}
return slotMap;

* @param {string} tokenId
* @param {number} blockNumber
* @param {bigint} slot
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's add return type for clarity.

* @param {string} tokenId
* @param {number} blockNumber
*/
_init(tokenId, blockNumber) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_init(tokenId, blockNumber) {
_getSlotMap(tokenId, blockNumber) {

@@ -274,4 +274,54 @@ function slotMapOf(token) {
return map;
}

module.exports = { slotMapOf, packValues };
/**
* Represents the value in the persistent storage.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some information in the PR description that would be really useful here. In particular 1. of section "The reason I've used persistent storage here" and section "Why it might be an issue".

We should include that info as a JS doc here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Enhancing an existing feature driven by business requirements. Typically backwards compatible.
Projects
Status: In progress
Development

Successfully merging this pull request may close these issues.

[Hardhat] Implement HTS state emulation for NFT methods
3 participants