-
Notifications
You must be signed in to change notification settings - Fork 75
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
[Fix] merge metadata by tag #416
Conversation
Fixes: - preserve metadata with multiple assets minting Features: - Transaction's setMetadata(), MeshTxBuilder's metadataValue(): 3rd argument to recursively merge new metadata with existing metadata entries under that tag Thank you for contributing to Mesh! We appreciate your effort and dedication to improving this project. To ensure that your contribution is in line with the project's guidelines and can be reviewed efficiently, please fill out the template below.
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
@d-Bharti001 is attempting to deploy a commit to the MeshJS Team on Vercel. A member of the Team first needs to authorize it. |
As far as I know, the function call at Edit: I was wrong about this, the serializer actually does insertion with a key based on tag. Unfortunately, this issue actually stems from the maintainers of this library being several different people with different design philosophies. The idea from the serializer perspective, is that what's being called is However, the design philosophy of the The reason I prefer this, is because when there are multiple metadata fields, you have to start guessing how the users want to combine them. This is the reason why I would be reluctant to approve this PR, while this merging of metadata tags may work for your structure of metadata, it is still a guess on how the user wants to structure their metadata. How would you feel if we simply removed this behaviour of the |
Upon further thought and investigation, your solution seems more and more reasonable. I'd like to point some things out, to see if we can gain some insight into what's the best solution. I still think there's something fundamentally wrong with the current implementation of Metadatum in the txBuilder. The trouble is that the resulting Metadatum is a mapping, while the Metadatum field in the txBuilder is a list, which ultimately makes them difficult to resolve. The conversion from a list to a map inherently requires some guesswork as to how to combine the list. My proposal would be to change this type into an actual map, taking the implementation of
We could then replace the current This would require some work to overhaul the current |
@twwu123 I agree with your second comment, where
One question.. should the key be a export declare type TxMetadata = Map<bigint, Metadatum>; Does that Should I proceed with making this upgrade? If you have any suggestions and inputs, e.g. regarding the names of functions and arguments, please share. |
I'll start working on this change. |
I think the key should be a string, on Rust side, the serializer calls I personally think that making breaking changes is fine here, because the old method of setting metadatums has been an issue for some time, as you pointed out, multiple metadata of the same tag get replaced. It's been reported by quite a few users at this point, so it would be good to overhaul the implementation in order to make it more permanent. Setting metadata in one go makes a lot more sense in my opinion, it results in no ambiguity in reconciling a list of metadata, and gives users fine control over how they want to structure it. Thanks a lot for this, this work is really appreciated. |
@twwu123 cool! I'm on it. |
0cecc60
to
0b89619
Compare
359d1fb
to
e9d0bc7
Compare
e9d0bc7
to
dc77004
Compare
Hi @twwu123 I've pushed the updated code where tx. metadata is a Map. I agree with the point that the user should prepare the whole metadata from their side, which is a lot clearer and straigtforward. However, for simple tasks like if I'm minting multiple NFTs by calling |
Thank you, it looks mostly good, will be looking in more detail in this week and get this merged. |
Hi @twwu123 thanks a lot for reviewing and doing the necessary changes. In the function |
Hi @twwu123 I have one concern: if (mint.label === "721" || mint.label === "20") {
let currentMetadata = this.txBuilder.meshTxBuilderBody.metadata;
if (currentMetadata.size === 0) {
this.setMetadata(Number(mint.label), {
[policyId]: { [mint.assetName]: mint.metadata },
});
} else {
let metadataMap = metadataObjToMap({
[policyId]: { [mint.assetName]: mint.metadata },
} as object);
let newMetadata = mergeContents(
currentMetadata.get(BigInt(mint.label)) as Metadatum,
metadataMap,
mint.label === "721" ? 2 : 0,
);
this.setMetadata(Number(mint.label), newMetadata);
}
} mergeContents() is used in line 484. Would it work if P.S.: I don't have much experience with TypeScript |
That's a good point, it probably wouldn't work |
This would be way better, that's my mistake Thanks for pointing it out |
@twwu123 that looks good! thanks. |
Changes:
meshTxBuilderBody.metadata
is changed to a map instead of an arrayTransaction
'ssetMetadata()
:MeshTxBuilder
'smetadataValue()
:metadataValue()
function is called, the first parameter is changed to a number type. Documents as well as Mesh's other componentsFixes:
Summary
The problem
On building a transaction (
tx.build()
ortxBuilder.complete()
), some of the transaction metadata was getting lost.In the metadata array (
meshTxBuilderBody.metadata
), multiple entries (of type{ tag: string, metadata: string }
) with the same tag can be present.When
csl.js_serialize_tx_body()
is called to gettxBuildResult
in line 72 ofmesh-core-csl/src/core/serializer.ts
(inside the functionserializeTxBody
ofCSLSerializer
)the serializer function overrides the metadata of a tag in case there are multiple entries belonging to the same tag.
An example when this metadata-loss becomes an obvious problem is when multiple assets are minted using
Transaction.mintAsset()
(e.g. minting multiple CIP-25 NFTs under the same policy ID in one transaction).meshTxBuilderBody.metadata
array contains entries for all the assets, but when the transaction is built at the end, only the metadata of the last asset remains, and all of the rest are discarded by the serializer.To avoid losing metadata, the user has to manually build a metadata object in their code and set the transaction's metadata by calling
tx.setMetadata(721, metadataObj)
. But this also wastes the action of theTransaction.mintAsset()
to automatically add the asset metadata.The solution
-- Updated solution --
Updated metadata structure
As suggested by a maintainer,
meshTxBuilderBody.metadata
is updated to be a Map now (typeTxMetadata
)Earlier it was of type
Metadata[]
, which is now being used only inside the serializer (mesh-core-csl/src/core/serializer.ts --> CSLSerializer --> serializeTxBody()
), i.e. by callingmeshTxBuilderBodyToObj()
(from insideserializeTxBody()
), the metadata is converted to the earlier type of[{ tag: string, metadata: <JSONbig stringified item> }]
Metadata merge feature
Merging of metadata is supported in a recursive manner upto a specified depth. For merging CIP-25 standard metadata, this depth should be set to 2.
When
tx.mintAsset()
is called, the metadata of the current asset is merged with the existing metadata under the related label with the strategy described above.Added functionality
There is an additional possibility added to both
Transaction.setMetadata()
andMeshTxBuilder.metadataValue()
:Example implementation of the added functionality:
tx.mintAsset()
one or more number of times, I want to add a version.721
metadata by calling:true
sets the merge depth to1
by default (a number can also be supplied directly as the required merge depth). So, the existing metadata object under the tag721
(which is currently containing all the policy IDs) gets one more key-value pair, i.e.version: "1.0"
. Now the721
metadata contains all the policy IDs, all NFTs, and a version.Affect components
@meshsdk/common
@meshsdk/contract
@meshsdk/core
@meshsdk/core-csl
@meshsdk/core-cst
@meshsdk/provider
@meshsdk/react
@meshsdk/svelte
@meshsdk/transaction
@meshsdk/wallet
Type of Change
Related Issues
Not any
Checklist
npm run test
)npm run build
)Additional Information
For compatibility with the previous codes which used Mesh library, these steps are taken:
MeshTxBuilder.metadataValue()
label
(tag) is now preferred as a Number, which was String earlier, it is now explicitly converted to a BigInt now. String inputs would also work.For compatibility with transaction serialization, the new Map metadata is still converted to the same old type Metadata[]. So no change should be seen in the final transaction body built by
tx.build()
ortxBuilder.complete()
.Have a look at the unit test cases:
packages/mesh-transaction/test/transaction/txMetadata.test.ts