mirror of https://github.com/certusone/oyster.git
Store metadata json in update call
This commit is contained in:
parent
01bbfa5303
commit
f9bfa0fd36
|
@ -194,6 +194,17 @@ class CreateMetadataArgs {
|
|||
this.uri = args.uri;
|
||||
}
|
||||
}
|
||||
class UpdateMetadataArgs {
|
||||
instruction: number = 1;
|
||||
uri: string;
|
||||
// Not used by this app, just required for instruction
|
||||
non_unique_specific_update_authority: number;
|
||||
|
||||
constructor(args: { uri: string }) {
|
||||
this.uri = args.uri;
|
||||
this.non_unique_specific_update_authority = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class Metadata {
|
||||
updateAuthority?: PublicKey;
|
||||
|
@ -233,6 +244,17 @@ export const SCHEMA = new Map<any, any>([
|
|||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
UpdateMetadataArgs,
|
||||
{
|
||||
kind: 'struct',
|
||||
fields: [
|
||||
['instruction', 'u8'],
|
||||
['uri', 'string'],
|
||||
['non_unique_specific_update_authority', 'u8'],
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
Metadata,
|
||||
{
|
||||
|
@ -280,6 +302,68 @@ export function createMint(
|
|||
|
||||
return account;
|
||||
}
|
||||
export async function updateMetadata(
|
||||
symbol: string,
|
||||
name: string,
|
||||
uri: string,
|
||||
mintKey: PublicKey,
|
||||
updateAuthority: PublicKey,
|
||||
instructions: TransactionInstruction[],
|
||||
signers: Account[],
|
||||
) {
|
||||
const metadataProgramId = programIds().metadata;
|
||||
|
||||
const metadataAccount = (
|
||||
await PublicKey.findProgramAddress(
|
||||
[
|
||||
Buffer.from('metadata'),
|
||||
metadataProgramId.toBuffer(),
|
||||
mintKey.toBuffer(),
|
||||
],
|
||||
metadataProgramId,
|
||||
)
|
||||
)[0];
|
||||
|
||||
const metadataOwnerAccount = (
|
||||
await PublicKey.findProgramAddress(
|
||||
[
|
||||
Buffer.from('metadata'),
|
||||
metadataProgramId.toBuffer(),
|
||||
Buffer.from(name),
|
||||
Buffer.from(symbol),
|
||||
],
|
||||
metadataProgramId,
|
||||
)
|
||||
)[0];
|
||||
|
||||
const value = new UpdateMetadataArgs({ uri });
|
||||
const data = Buffer.from(serialize(SCHEMA, value));
|
||||
|
||||
const keys = [
|
||||
{
|
||||
pubkey: metadataAccount,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: updateAuthority,
|
||||
isSigner: true,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: metadataOwnerAccount,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
];
|
||||
instructions.push(
|
||||
new TransactionInstruction({
|
||||
keys,
|
||||
programId: metadataProgramId,
|
||||
data,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function createMetadata(
|
||||
symbol: string,
|
||||
|
@ -289,7 +373,7 @@ export async function createMetadata(
|
|||
mintAuthorityKey: PublicKey,
|
||||
instructions: TransactionInstruction[],
|
||||
payer: PublicKey,
|
||||
owner: PublicKey,
|
||||
updateAuthority: PublicKey,
|
||||
signers: Account[],
|
||||
) {
|
||||
const metadataProgramId = programIds().metadata;
|
||||
|
@ -347,7 +431,7 @@ export async function createMetadata(
|
|||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: payer,
|
||||
pubkey: updateAuthority,
|
||||
isSigner: true,
|
||||
isWritable: false,
|
||||
},
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
import { EventEmitter, programIds, useConnection, decodeMetadata, Metadata, getMultipleAccounts, cache, MintParser, ParsedAccount } from '@oyster/common';
|
||||
import {
|
||||
EventEmitter,
|
||||
programIds,
|
||||
useConnection,
|
||||
decodeMetadata,
|
||||
Metadata,
|
||||
getMultipleAccounts,
|
||||
cache,
|
||||
MintParser,
|
||||
ParsedAccount,
|
||||
} from '@oyster/common';
|
||||
import { MintInfo } from '@solana/spl-token';
|
||||
import BN from 'bn.js';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
|
@ -18,15 +28,18 @@ export function VinciAccountsProvider({ children = null as any }) {
|
|||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const metadataAccounts = await connection.getProgramAccounts(programIds().metadata);
|
||||
const metadataAccounts = await connection.getProgramAccounts(
|
||||
programIds().metadata,
|
||||
);
|
||||
|
||||
const mintToMetadata = new Map<string, Metadata>();
|
||||
const extendedMetadataFetch = new Map<string, Promise<any>>();
|
||||
|
||||
metadataAccounts.forEach(meta => {
|
||||
try{
|
||||
try {
|
||||
const metadata = decodeMetadata(meta.account.data);
|
||||
if(isValidHttpUrl(metadata.uri)) {
|
||||
if (isValidHttpUrl(metadata.uri)) {
|
||||
console.log('Metadata uri', metadata.uri);
|
||||
mintToMetadata.set(metadata.mint.toBase58(), metadata);
|
||||
}
|
||||
} catch {
|
||||
|
@ -35,22 +48,35 @@ export function VinciAccountsProvider({ children = null as any }) {
|
|||
}
|
||||
});
|
||||
|
||||
const mints = await getMultipleAccounts(connection, [...mintToMetadata.keys()], 'single');
|
||||
const mints = await getMultipleAccounts(
|
||||
connection,
|
||||
[...mintToMetadata.keys()],
|
||||
'single',
|
||||
);
|
||||
mints.keys.forEach((key, index) => {
|
||||
const mintAccount = mints.array[index];
|
||||
const mint = cache.add(key, mintAccount, MintParser) as ParsedAccount<MintInfo>;
|
||||
if(mint.info.supply.gt(new BN(1)) || mint.info.decimals !== 0) {
|
||||
const mint = cache.add(
|
||||
key,
|
||||
mintAccount,
|
||||
MintParser,
|
||||
) as ParsedAccount<MintInfo>;
|
||||
if (mint.info.supply.gt(new BN(1)) || mint.info.decimals !== 0) {
|
||||
// naive not NFT check
|
||||
mintToMetadata.delete(key);
|
||||
} else {
|
||||
const metadata = mintToMetadata.get(key);
|
||||
if(metadata && metadata.uri) {
|
||||
extendedMetadataFetch.set(key, fetch(metadata.uri).catch(() => {
|
||||
mintToMetadata.delete(key);
|
||||
return undefined;
|
||||
}).then(_ => {
|
||||
metadata.extended = _;
|
||||
}));
|
||||
if (metadata && metadata.uri) {
|
||||
extendedMetadataFetch.set(
|
||||
key,
|
||||
fetch(metadata.uri)
|
||||
.catch(() => {
|
||||
mintToMetadata.delete(key);
|
||||
return undefined;
|
||||
})
|
||||
.then(_ => {
|
||||
metadata.extended = _;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -61,7 +87,7 @@ export function VinciAccountsProvider({ children = null as any }) {
|
|||
|
||||
console.log([...mintToMetadata.values()]);
|
||||
})();
|
||||
}, [connection, setMetaAccounts])
|
||||
}, [connection, setMetaAccounts]);
|
||||
|
||||
return (
|
||||
<VinciAccountsContext.Provider value={{ metaAccounts }}>
|
||||
|
@ -84,5 +110,5 @@ function isValidHttpUrl(text: string) {
|
|||
return false;
|
||||
}
|
||||
|
||||
return url.protocol === "http:" || url.protocol === "https:";
|
||||
return url.protocol === 'http:' || url.protocol === 'https:';
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@ import {
|
|||
createAssociatedTokenAccountInstruction,
|
||||
createMint,
|
||||
createMetadata,
|
||||
updateMetadata,
|
||||
programIds,
|
||||
sendTransactions,
|
||||
sendTransaction,
|
||||
} from '@oyster/common';
|
||||
import { MintLayout, Token } from '@solana/spl-token';
|
||||
import { WalletAdapter } from '@solana/wallet-base';
|
||||
|
@ -12,12 +14,12 @@ import {
|
|||
Connection,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
SYSVAR_RENT_PUBKEY,
|
||||
TransactionInstruction,
|
||||
} from '@solana/web3.js';
|
||||
import crypto from 'crypto';
|
||||
import { getAssetCostToStore } from '../utils/assets';
|
||||
import { AR_SOL_HOLDER_ID } from '../utils/ids';
|
||||
import { IMetadata } from '../views/artCreate';
|
||||
|
||||
interface IArweaveResult {
|
||||
error?: string;
|
||||
|
@ -32,7 +34,7 @@ export const mintNFT = async (
|
|||
connection: Connection,
|
||||
wallet: WalletAdapter | undefined,
|
||||
files: File[],
|
||||
metadata: any,
|
||||
metadata: { name: string; symbol: string },
|
||||
): Promise<IArweaveResult> => {
|
||||
if (!wallet?.publicKey) {
|
||||
return { error: 'No wallet' };
|
||||
|
@ -99,8 +101,8 @@ export const mintNFT = async (
|
|||
);
|
||||
|
||||
await createMetadata(
|
||||
`🥭🧢#`,
|
||||
`name: jjjdsfsk🥭🧢#`,
|
||||
metadata.symbol,
|
||||
metadata.name,
|
||||
`https://google.com`,
|
||||
mintKey,
|
||||
owner.publicKey,
|
||||
|
@ -109,18 +111,6 @@ export const mintNFT = async (
|
|||
wallet.publicKey,
|
||||
signers,
|
||||
);
|
||||
|
||||
// For Jordan -> Transfer SOL
|
||||
console.log(files.length);
|
||||
// TODO:
|
||||
// instructions.push(
|
||||
// Token.createSetAuthorityInstruction(
|
||||
// TOKEN_PROGRAM_ID,
|
||||
// mintKey,
|
||||
// owner.publicKey,
|
||||
// owner.publicKey,
|
||||
// []));
|
||||
|
||||
return new Promise(async res => {
|
||||
const txId = await sendTransactions(
|
||||
connection,
|
||||
|
@ -157,6 +147,33 @@ export const mintNFT = async (
|
|||
},
|
||||
)
|
||||
).json();
|
||||
|
||||
const metadataFile = result.messages?.find(
|
||||
m => m.filename == 'metadata.json',
|
||||
);
|
||||
if (metadataFile?.transactionId && wallet.publicKey) {
|
||||
const updateInstructions: TransactionInstruction[] = [];
|
||||
const updateSigners: Account[] = [];
|
||||
|
||||
await updateMetadata(
|
||||
metadata.symbol,
|
||||
metadata.name,
|
||||
`https://arweave.net/${metadataFile.transactionId}`,
|
||||
mintKey,
|
||||
wallet.publicKey,
|
||||
updateInstructions,
|
||||
updateSigners,
|
||||
);
|
||||
|
||||
await sendTransaction(
|
||||
connection,
|
||||
wallet,
|
||||
updateInstructions,
|
||||
updateSigners,
|
||||
true,
|
||||
'singleGossip',
|
||||
);
|
||||
}
|
||||
console.log('Result', result);
|
||||
res(result);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ enum Category {
|
|||
Video = 'video',
|
||||
Image = 'image',
|
||||
}
|
||||
interface IProps {
|
||||
export interface IMetadata {
|
||||
name: string;
|
||||
symbol: string;
|
||||
description: string;
|
||||
|
@ -45,7 +45,7 @@ export const ArtCreateView = () => {
|
|||
const { wallet, connected } = useWallet();
|
||||
const [step, setStep] = useState(0);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [attributes, setAttributes] = useState<IProps>({
|
||||
const [attributes, setAttributes] = useState<IMetadata>({
|
||||
name: '',
|
||||
symbol: '',
|
||||
description: '',
|
||||
|
@ -178,8 +178,8 @@ const CategoryStep = (props: { confirm: (category: Category) => void }) => {
|
|||
};
|
||||
|
||||
const UploadStep = (props: {
|
||||
attributes: IProps;
|
||||
setAttributes: (attr: IProps) => void;
|
||||
attributes: IMetadata;
|
||||
setAttributes: (attr: IMetadata) => void;
|
||||
confirm: () => void;
|
||||
}) => {
|
||||
return (
|
||||
|
@ -232,8 +232,8 @@ const UploadStep = (props: {
|
|||
};
|
||||
|
||||
const InfoStep = (props: {
|
||||
attributes: IProps;
|
||||
setAttributes: (attr: IProps) => void;
|
||||
attributes: IMetadata;
|
||||
setAttributes: (attr: IMetadata) => void;
|
||||
confirm: () => void;
|
||||
}) => {
|
||||
const file = props.attributes.files[0];
|
||||
|
@ -319,8 +319,8 @@ const InfoStep = (props: {
|
|||
};
|
||||
|
||||
const RoyaltiesStep = (props: {
|
||||
attributes: IProps;
|
||||
setAttributes: (attr: IProps) => void;
|
||||
attributes: IMetadata;
|
||||
setAttributes: (attr: IMetadata) => void;
|
||||
confirm: () => void;
|
||||
}) => {
|
||||
const file = props.attributes.files[0];
|
||||
|
@ -371,7 +371,7 @@ const RoyaltiesStep = (props: {
|
|||
);
|
||||
};
|
||||
|
||||
const LaunchStep = (props: { confirm: () => void; attributes: IProps }) => {
|
||||
const LaunchStep = (props: { confirm: () => void; attributes: IMetadata }) => {
|
||||
const file = props.attributes.files[0];
|
||||
const metadata = {
|
||||
...(props.attributes as any),
|
||||
|
|
Loading…
Reference in New Issue