metaplex-foundation / metaplex-program-library

Smart contracts maintained by the Metaplex team
Other
588 stars 513 forks source link

[Feature]: Method for adding token MetaData #748

Closed Bhaney44 closed 2 years ago

Bhaney44 commented 2 years ago

Which package is this feature request for?

token-metadata

Feature

I'm struggling to find a straightforward method to add token MetaData. I created an asset, associated token account, and developed the MetaData .json files. However, I'm still unsure of two important things that I have yet to find an answer for.

1) How to verify the MetaData I added was correct, particularly with respect to the TOKEN_URI.json. It appears the TOKEN_URI.json is a separate file, which is referenced in the META_DATA.json file. But, a clear description for what this is would be greatly appreciated. 2) How to add the MetaData to the token. So, there are a lot of pointers to different documentation for adding MetaData, but none of them actually provide a path to adding the data once it's created. For example, I created this GitHub Directory with the files I want to add, but the problem is the MetaPlex Documentation is lacking in providing a method for how to add the data to the asset.

Standard Change?

No

Ideal solution or implementation

An ideal solution would be a directory with a step by step guide on how to:

  1. generate the needed metadata files, with descriptions and examples for each variable.
  2. a description on how to add the metadata to the SPL token.

Alternative solutions or implementations

There is a lot of documentation out there and even applications for adding metadata. For example, the Token Standard Docs, the MetaData API Docs, and the token-metad data GitHub. But, the problem is none of these resources actually provide a method for adding metadata.

Other context

Additionally, I posted Issue #2248 to find some answers on this. Nonetheless, any advice or suggestions in the meantime would be greatly appreciated.

Bhaney44 commented 2 years ago

For example, the documentation instructs to install mpl-token-metadata:

npm install @metaplex-foundation/mpl-token-metadata --save

However, after the installation instruction. There is no further guidance provided on how to add the token metadata. So, it appears to be a dead end in the documentation. Thanks again for your time and review - as well as any suggestions on how to proceed.

samuelvanderwaal commented 2 years ago

For example, the documentation instructs to install mpl-token-metadata:

npm install @metaplex-foundation/mpl-token-metadata --save

However, after the installation instruction. There is no further guidance provided on how to add the token metadata. So, it appears to be a dead end in the documentation. Thanks again for your time and review - as well as any suggestions on how to proceed.

Have you looked at the JS SDK yet?

Bhaney44 commented 2 years ago

For example, the documentation instructs to install mpl-token-metadata: npm install @metaplex-foundation/mpl-token-metadata --save However, after the installation instruction. There is no further guidance provided on how to add the token metadata. So, it appears to be a dead end in the documentation. Thanks again for your time and review - as well as any suggestions on how to proceed.

Have you looked at the JS SDK yet?

Yes. I'm really struggling with it. It isn't clear to me how to effectively utilize the JS SDK. I'm getting a lot of error returns running the code in the tutorial.

Bhaney44 commented 2 years ago

For example, the documentation instructs to install mpl-token-metadata: npm install @metaplex-foundation/mpl-token-metadata --save However, after the installation instruction. There is no further guidance provided on how to add the token metadata. So, it appears to be a dead end in the documentation. Thanks again for your time and review - as well as any suggestions on how to proceed.

Have you looked at the JS SDK yet?

I'm getting a key error using the useExistingMint method:

useExistingMint: new PublicKey(''),

error:

ReferenceError: PublicKey is not defined

samuelvanderwaal commented 2 years ago

@Bhaney44

Can you share the code you're using?

useExistingMint: new PublicKey(''),

If this is literal code you're using it's going to fail because you're not putting in the public key for the existing mint.

Bhaney44 commented 2 years ago

@Bhaney44

Can you share the code you're using?

useExistingMint: new PublicKey(''),

If this is literal code you're using it's going to fail because you're not putting in the public key for the existing mint.

Hi yes. I was able to get past the PublicKey('') issue by installing in my local directory.

npm install @metaplex-foundation/js @solana/web3.js

Apologies, that was a noob error on my part. I am now working through a new bug. Here is my complete code.

import { Metaplex, keypairIdentity, bundlrStorage } from "@metaplex-foundation/js";
import { Connection, clusterApiUrl, Keypair } from "@solana/web3.js";

const connection = new Connection(clusterApiUrl("mainnet-beta"));
const metaplex = new Metaplex(connection);

const { sft } = await metaplex
.nfts()
.createSft({
    name: 'Choice Coin',
    uri: 'https://github.com/ChoiceCoin/converter/blob/main/spl-token-metadata/TOKEN_URI.json',
    sellerFeeBasisPoints: 0, 
    useExistingMint: 'A3uJtVMsC8s1VGtQArAuhS8viftRpVx8DAFBpppHZ8pC',
})
.run();

Here is the bug I am getting:

(base) fortiorb@FortiorBs-MacBook-Pro SPL_Token % node metadata.js
file:///Users/fortiorb/Desktop/Programming/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/pdas.mjs:9
  return Pda.find(programId, [Buffer.from('metadata', 'utf8'), programId.toBuffer(), mint.toBuffer()]);
                                                                                          ^

TypeError: mint.toBuffer is not a function
    at findMetadataPda (file:///Users/fortiorb/Desktop/Programming/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/pdas.mjs:9:91)
    at createSftBuilder (file:///Users/fortiorb/Desktop/Programming/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/operations/createSft.mjs:97:23)
    at async Object.handle (file:///Users/fortiorb/Desktop/Programming/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/operations/createSft.mjs:53:21)
    at async file:///Users/fortiorb/Desktop/Programming/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/utils/Task.mjs:58:23
    at async Disposable.run (file:///Users/fortiorb/Desktop/Programming/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/utils/Disposable.mjs:22:14)

There are two major issues here. The first is that there appears to be a bug in nftModule/pdas.mjs:9. The second is that this method for adding token metadata, if it worked, doesn't have a method for checking signature authority. If the code only requires a public key, then couldn't anyone change the metadata for any token?

Thanks in advance for your time and help - it is sincerely appreciated.

Brian

samuelvanderwaal commented 2 years ago

There are two major issues here. The first is that there appears to be a bug in nftModule/pdas.mjs:9. The second is that this method for adding token metadata, if it worked, doesn't have a method for checking signature authority. If the code only requires a public key, then couldn't anyone change the metadata for any token?

This is because you haven't setup the Identity Driver for the keypair. Carefully read through the Setup and KeypairIdentity sections of the README. This keypair should be the same authority you used to create the mint that you are creating the SFT from.

Bhaney44 commented 2 years ago

There are two major issues here. The first is that there appears to be a bug in nftModule/pdas.mjs:9. The second is that this method for adding token metadata, if it worked, doesn't have a method for checking signature authority. If the code only requires a public key, then couldn't anyone change the metadata for any token?

This is because you haven't setup the Identity Driver for the keypair. Carefully read through the Setup and KeypairIdentity sections of the README. This keypair should be the same authority you used to create the mint that you are creating the SFT from.

Ok I will do this and get back to you with updates. Thanks for the help @samuelvanderwaal.

samuelvanderwaal commented 2 years ago

If the code only requires a public key, then couldn't anyone change the metadata for any token?

BTW, it's important to keep in mind the blockchain model when thinking about these kinds of things. The JS SDK lets you write client side code, whether on a server via Node or in a browser, that submits transactions to the blockchain. This kind of behavior is ultimately policed by the on-chain rules encoded into the blockchain, so even if the JS SDK did the wrong thing here (it doesn't), the token metadata program would reject transactions that don't have proper authority signatures.

Bhaney44 commented 2 years ago

There are two major issues here. The first is that there appears to be a bug in nftModule/pdas.mjs:9. The second is that this method for adding token metadata, if it worked, doesn't have a method for checking signature authority. If the code only requires a public key, then couldn't anyone change the metadata for any token?

This is because you haven't setup the Identity Driver for the keypair. Carefully read through the Setup and KeypairIdentity sections of the README. This keypair should be the same authority you used to create the mint that you are creating the SFT from.

Ok I setup the keypair file. This makes a lot more sense now. One final question before I run this, is GitHub a valid place to store a TOKEN_URI.json file? The docs say it can be stored on chain or off chain, but I'm not sure if it will be able to read the GitHub file from the link or whether there is a better storage format. I'm indifferent to where the data is stored, I'm just looking for the simplest option.

Bhaney44 commented 2 years ago

There are two major issues here. The first is that there appears to be a bug in nftModule/pdas.mjs:9. The second is that this method for adding token metadata, if it worked, doesn't have a method for checking signature authority. If the code only requires a public key, then couldn't anyone change the metadata for any token?

This is because you haven't setup the Identity Driver for the keypair. Carefully read through the Setup and KeypairIdentity sections of the README. This keypair should be the same authority you used to create the mint that you are creating the SFT from.

Ok I am now getting ReferenceError: fs is not defined.

const keypairFile = fs.readFileSync('/Users/username/.config/solana/id.json');

This error makes sense because fs is not defined or imported. I looked through the docs on this and there isn't anything explaining fs or how to install it. Any suggestions here would be appreciated, thank you.

fs is short for filesystem. Module. Install.

New Bug.

ReferenceError: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and '/Users/Programming/SPL_Token/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.

Another issue is whether developers should use fs or fs-js. It appears fs may be depricated.

I switched the file name from metadata.js to metadata.cjs according to the error code, but it yielded another error.

import { Metaplex, keypairIdentity, bundlrStorage } from "@metaplex-foundation/js";
^^^^^^

SyntaxError: Cannot use import statement outside a module

So, I think I'm going to stick with metadata.js. But, I need more information on how to use the fs module within the context of the Solana JS-SDK.

Please let me know what you think. Thanks! @samuelvanderwaal @lorisleiva

Bhaney44 commented 2 years ago

I found this possible solution on StackOverflow. But, when I changed module to Commonjs in the package.json file I got a new error.

(node:2913) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/SPL_Token/metadata.js:1
import { Metaplex, keypairIdentity } from "@metaplex-foundation/js";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at Object.compileFunction (node:vm:360:18)
    at wrapSafe (node:internal/modules/cjs/loader:1055:15)
    at Module._compile (node:internal/modules/cjs/loader:1090:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1180:10)
    at Module.load (node:internal/modules/cjs/loader:1004:32)
    at Function.Module._load (node:internal/modules/cjs/loader:839:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47
samuelvanderwaal commented 2 years ago

@Bhaney44 these are pretty generic JS issues with how to set up a JavaScript project and not particularly relevant to the JS SDK.

What worked for me was this package.json:

{
  "name": "sft",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "type": "module",
  "dependencies": {
    "@metaplex-foundation/js": "^0.15.0",
    "@solana/web3.js": "^1.63.0"
  }
}

Notice the "type": "module" line.

and this import code:

import {
  Metaplex,
  keypairIdentity,
  bundlrStorage
} from "@metaplex-foundation/js";
import { Connection, clusterApiUrl, Keypair, PublicKey } from "@solana/web3.js";
import fs from "fs";

I then used it like this:

let rawdata = fs.readFileSync("/Users/samuel/.config/solana/devnet.json");
let key = JSON.parse(rawdata);
const wallet = Keypair.fromSecretKey(Uint8Array.from(key));

I then ran it with Node. That should get you started on showing how to use the JS SDK and you'll have to figure out your specific setup from there.

I hope that helps, but I'm closing this issue as there doesn't appear to be an actual bug in the JS SDK at this point. You can reopen it if there's an issue specific to the JS SDK.

Bhaney44 commented 2 years ago

@Bhaney44 these are pretty generic JS issues with how to set up a JavaScript project and not particularly relevant to the JS SDK.

What worked for me was this package.json:

{
  "name": "sft",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "type": "module",
  "dependencies": {
    "@metaplex-foundation/js": "^0.15.0",
    "@solana/web3.js": "^1.63.0"
  }
}

Notice the "type": "module" line.

and this import code:

import {
  Metaplex,
  keypairIdentity,
  bundlrStorage
} from "@metaplex-foundation/js";
import { Connection, clusterApiUrl, Keypair, PublicKey } from "@solana/web3.js";
import fs from "fs";

I then used it like this:

let rawdata = fs.readFileSync("/Users/samuel/.config/solana/devnet.json");
let key = JSON.parse(rawdata);
const wallet = Keypair.fromSecretKey(Uint8Array.from(key));

I then ran it with Node. That should get you started on showing how to use the JS SDK and you'll have to figure out your specific setup from there.

I hope that helps, but I'm closing this issue as there doesn't appear to be an actual bug in the JS SDK at this point. You can reopen it if there's an issue specific to the JS SDK.

Hi, Thanks for following up. It looks like you changed a lot of the code and are using a different method, wallet instead of keypair. Could you please share the complete code that worked for you?

The issue with the JS-SDK is that there isn't a method for adding token metadata. I understand the metaplex developers have a way to do it, but that information isn't public or documented anywhere.

Bhaney44 commented 2 years ago

@Bhaney44 these are pretty generic JS issues with how to set up a JavaScript project and not particularly relevant to the JS SDK.

What worked for me was this package.json:

{
  "name": "sft",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "type": "module",
  "dependencies": {
    "@metaplex-foundation/js": "^0.15.0",
    "@solana/web3.js": "^1.63.0"
  }
}

Notice the "type": "module" line.

and this import code:

import {
  Metaplex,
  keypairIdentity,
  bundlrStorage
} from "@metaplex-foundation/js";
import { Connection, clusterApiUrl, Keypair, PublicKey } from "@solana/web3.js";
import fs from "fs";

I then used it like this:

let rawdata = fs.readFileSync("/Users/samuel/.config/solana/devnet.json");
let key = JSON.parse(rawdata);
const wallet = Keypair.fromSecretKey(Uint8Array.from(key));

I then ran it with Node. That should get you started on showing how to use the JS SDK and you'll have to figure out your specific setup from there.

I hope that helps, but I'm closing this issue as there doesn't appear to be an actual bug in the JS SDK at this point. You can reopen it if there's an issue specific to the JS SDK.

I ran a modified version with your edited package.json file.


% node metadata.js
file:///Users/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/pdas.mjs:9
  return Pda.find(programId, [Buffer.from('metadata', 'utf8'), programId.toBuffer(), mint.toBuffer()]);
                                                                                          ^

TypeError: mint.toBuffer is not a function
    at findMetadataPda (file:///Users/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/pdas.mjs:9:91)
    at createSftBuilder (file:///Users/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/operations/createSft.mjs:97:23)
    at async Object.handle (file:///Users/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/plugins/nftModule/operations/createSft.mjs:53:21)
    at async file:///Users/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/utils/Task.mjs:58:23
    at async Disposable.run (file:///Users/SPL_Token/node_modules/@metaplex-foundation/js/dist/esm/utils/Disposable.mjs:22:14)

But, I am still getting an error with MetaPlex: TypeError: mint.toBuffer is not a function.

You should re-open this because it isn't solved. I've worked very hard on solving this Issue for over a week and it is premature to close an Issue before it is solved. The problem is I am having to create a completely new code base from scratch because when the token MetaData program switched from Solana-Labs to MetaPlex, MetaPlex never provided or adopted a method to add token data to SPL Tokens. It's an important problem that needs to be addressed to enable developers to build on Solana. @samuelvanderwaal

samuelvanderwaal commented 2 years ago

You should re-open this because it isn't solved. I've worked very hard on solving this Issue for over a week and it is premature to close an Issue before it is solved. The problem is I am having to create a completely new code base from scratch because when the token MetaData program switched from Solana-Labs to MetaPlex, MetaPlex never provided or adopted a method to add token data to SPL Tokens. It's an important problem that needs to be addressed to enable developers to build on Solana.

The issues you are having are not bugs in the Metaplex JS SDK or in the metaplex-program-library, though, so are not appropriate for a Github issue. Please check out our Discord for further help. We have some community developers who might be able to help you with some of the JavaScript issues you're having.

samuelvanderwaal commented 2 years ago

BTW, here's my full working example:

import {
  Metaplex,
  keypairIdentity,
  bundlrStorage
} from "@metaplex-foundation/js";
import { Connection, clusterApiUrl, Keypair, PublicKey } from "@solana/web3.js";
import fs from "fs";

const connection = new Connection(clusterApiUrl("devnet"));

let rawdata = fs.readFileSync("/Users/samuel/.config/solana/devnet.json");
let key = JSON.parse(rawdata);
const wallet = Keypair.fromSecretKey(Uint8Array.from(key));

const metaplex = Metaplex.make(connection)
  .use(keypairIdentity(wallet))
  .use(bundlrStorage());

const mint = new PublicKey("HWJxcnpsSbAAQJygtroCJfkyr56JnrNs8E3Uz5B1Ztde");

const { sft } = await metaplex
  .nfts()
  .createSft({
    name: "Choice Coin",
    uri: "https://raw.githubusercontent.com/ChoiceCoin/converter/main/spl-token-metadata/TOKEN_URI.json",
    sellerFeeBasisPoints: 0,
    useExistingMint: mint
  })
  .run();

If look up the token you'll see it was successfully decorated with metadata though your image link isn't working as Github isn't the best method to store that data.