mirror of https://github.com/certusone/oyster.git
feat: minting
This commit is contained in:
parent
91b8870952
commit
6bc32a4c6e
|
@ -45,10 +45,12 @@ export interface IMetadataExtension {
|
|||
export class Metadata {
|
||||
key: number;
|
||||
updateAuthority?: PublicKey;
|
||||
|
||||
mint: PublicKey;
|
||||
name: string;
|
||||
symbol: string;
|
||||
uri: string;
|
||||
|
||||
extended?: IMetadataExtension;
|
||||
|
||||
constructor(args: {
|
||||
|
@ -166,6 +168,55 @@ export const SCHEMA = new Map<any, any>([
|
|||
export const decodeMetadata = (buffer: Buffer) => {
|
||||
return deserializeBorsh(SCHEMA, Metadata, buffer) as Metadata;
|
||||
};
|
||||
|
||||
export async function transferMetadata(
|
||||
symbol: string,
|
||||
name: string,
|
||||
currentUpdateAuthority: PublicKey,
|
||||
newUpdateAuthority: PublicKey,
|
||||
instructions: TransactionInstruction[],
|
||||
signers: Account[],
|
||||
) {
|
||||
const metadataProgramId = programIds().metadata;
|
||||
|
||||
const metadataOwnerAccount = (
|
||||
await PublicKey.findProgramAddress(
|
||||
[
|
||||
Buffer.from('metadata'),
|
||||
metadataProgramId.toBuffer(),
|
||||
Buffer.from(name),
|
||||
Buffer.from(symbol),
|
||||
],
|
||||
metadataProgramId,
|
||||
)
|
||||
)[0];
|
||||
|
||||
const keys = [
|
||||
{
|
||||
pubkey: metadataOwnerAccount,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: currentUpdateAuthority,
|
||||
isSigner: true,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: newUpdateAuthority,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
];
|
||||
instructions.push(
|
||||
new TransactionInstruction({
|
||||
keys,
|
||||
programId: metadataProgramId,
|
||||
data: Buffer.from([]),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function updateMetadata(
|
||||
symbol: string,
|
||||
name: string,
|
||||
|
@ -228,6 +279,7 @@ export async function updateMetadata(
|
|||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function createMetadata(
|
||||
symbol: string,
|
||||
name: string,
|
||||
|
@ -296,7 +348,7 @@ export async function createMetadata(
|
|||
},
|
||||
{
|
||||
pubkey: updateAuthority,
|
||||
isSigner: true,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -17,8 +17,6 @@ export const CurrentUserBadge = (props: { showBalance?: boolean, showAddress?: b
|
|||
return null;
|
||||
}
|
||||
|
||||
// should use SOL ◎ ?
|
||||
|
||||
const iconStyle: React.CSSProperties = props.showAddress ?
|
||||
{
|
||||
marginLeft: '0.5rem',
|
||||
|
|
|
@ -330,12 +330,14 @@ export const sendTransaction = async (
|
|||
awaitConfirmation = true,
|
||||
commitment: Commitment = 'singleGossip',
|
||||
includesFeePayer: boolean = false,
|
||||
block?: BlockhashAndFeeCalculator
|
||||
) => {
|
||||
let transaction = new Transaction();
|
||||
instructions.forEach(instruction => transaction.add(instruction));
|
||||
transaction.recentBlockhash = (
|
||||
transaction.recentBlockhash = (block || (
|
||||
await connection.getRecentBlockhash(commitment)
|
||||
).blockhash;
|
||||
)).blockhash;
|
||||
|
||||
if(includesFeePayer) {
|
||||
transaction.setSigners(
|
||||
...signers.map(s => s.publicKey),
|
||||
|
|
|
@ -2,24 +2,21 @@ import {
|
|||
createAssociatedTokenAccountInstruction,
|
||||
createMint,
|
||||
createMetadata,
|
||||
updateMetadata,
|
||||
transferMetadata,
|
||||
programIds,
|
||||
sendTransactions,
|
||||
sendTransaction,
|
||||
notify,
|
||||
ENV,
|
||||
updateMetadata,
|
||||
} from '@oyster/common';
|
||||
import React from 'react';
|
||||
import { MintLayout, Token } from '@solana/spl-token';
|
||||
import { WalletAdapter } from '@solana/wallet-base';
|
||||
import {
|
||||
Account,
|
||||
Commitment,
|
||||
Connection,
|
||||
LAMPORTS_PER_SOL,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
Transaction,
|
||||
TransactionInstruction,
|
||||
} from '@solana/web3.js';
|
||||
import crypto from 'crypto';
|
||||
|
@ -36,6 +33,7 @@ interface IArweaveResult {
|
|||
error?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export const mintNFT = async (
|
||||
connection: Connection,
|
||||
wallet: WalletAdapter | undefined,
|
||||
|
@ -68,10 +66,6 @@ export const mintNFT = async (
|
|||
const instructions: TransactionInstruction[] = [...pushInstructions];
|
||||
const signers: Account[] = [...pushSigners, owner];
|
||||
|
||||
|
||||
await connection.requestAirdrop(payer.publicKey, LAMPORTS_PER_SOL * 2);
|
||||
const updateInstructions: TransactionInstruction[] = [];
|
||||
const updateSigners: Account[] = [payer, owner];
|
||||
const mintKey = createMint(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
|
@ -82,35 +76,6 @@ export const mintNFT = async (
|
|||
signers,
|
||||
);
|
||||
|
||||
// TODO: connect to testnet arweave
|
||||
const arweaveLink = `https://arweave.net/`;
|
||||
await createMetadata(
|
||||
metadata.symbol,
|
||||
metadata.name,
|
||||
arweaveLink,
|
||||
false,
|
||||
wallet.publicKey,
|
||||
mintKey,
|
||||
owner.publicKey,
|
||||
updateInstructions,
|
||||
payer.publicKey,
|
||||
updateSigners,
|
||||
);
|
||||
|
||||
debugger;
|
||||
await sendTransactionX(
|
||||
connection,
|
||||
wallet,
|
||||
updateInstructions,
|
||||
updateSigners,
|
||||
true,
|
||||
'singleGossip',
|
||||
true
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
const recipientKey: PublicKey = (
|
||||
await PublicKey.findProgramAddress(
|
||||
[
|
||||
|
@ -142,15 +107,37 @@ export const mintNFT = async (
|
|||
);
|
||||
|
||||
const block = await connection.getRecentBlockhash('singleGossip');
|
||||
const rentExempt = await connection.getMinimumBalanceForRentExemption(1000);
|
||||
|
||||
await createMetadata(
|
||||
metadata.symbol,
|
||||
metadata.name,
|
||||
`https://arweave.net/rfX69WKd7Bin_RTbcnH4wM3BuWWsR_ZhWSSqZBLYdMY`,
|
||||
false,
|
||||
payer.publicKey,
|
||||
mintKey,
|
||||
owner.publicKey,
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
signers,
|
||||
);
|
||||
|
||||
instructions.push(
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: wallet.publicKey,
|
||||
toPubkey: payer.publicKey,
|
||||
lamports: block.feeCalculator.lamportsPerSignature * 2,
|
||||
lamports: block.feeCalculator.lamportsPerSignature * 3,
|
||||
}));
|
||||
|
||||
const response = await sendTransaction(connection, wallet, instructions, signers, true, 'max');
|
||||
const response = await sendTransaction(
|
||||
connection,
|
||||
wallet,
|
||||
instructions,
|
||||
signers,
|
||||
true,
|
||||
'max',
|
||||
false,
|
||||
block);
|
||||
|
||||
// this means we're done getting AR txn setup. Ship it off to ARWeave!
|
||||
const data = new FormData();
|
||||
|
@ -191,21 +178,27 @@ export const mintNFT = async (
|
|||
|
||||
// TODO: connect to testnet arweave
|
||||
const arweaveLink = `https://arweave.net/${metadataFile.transactionId}`;
|
||||
await createMetadata(
|
||||
await updateMetadata(
|
||||
metadata.symbol,
|
||||
metadata.name,
|
||||
arweaveLink,
|
||||
false,
|
||||
wallet.publicKey,
|
||||
mintKey,
|
||||
owner.publicKey,
|
||||
updateInstructions,
|
||||
payer.publicKey,
|
||||
updateInstructions,
|
||||
updateSigners,
|
||||
);
|
||||
|
||||
await transferMetadata(
|
||||
metadata.symbol,
|
||||
metadata.name,
|
||||
payer.publicKey,
|
||||
wallet.publicKey,
|
||||
updateInstructions,
|
||||
updateSigners);
|
||||
|
||||
debugger;
|
||||
await sendTransactionX(
|
||||
|
||||
const txid = await sendTransaction(
|
||||
connection,
|
||||
wallet,
|
||||
updateInstructions,
|
||||
|
@ -215,74 +208,23 @@ export const mintNFT = async (
|
|||
true
|
||||
);
|
||||
|
||||
console.log("transaction id: ", txid);
|
||||
|
||||
notify({
|
||||
message: 'Art created on Solana',
|
||||
description: <a href={arweaveLink} target="_blank" >Arweave Link</a>,
|
||||
type: 'success',
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: refund funds
|
||||
|
||||
// send transfer back to user
|
||||
}
|
||||
// TODO:
|
||||
// 1. Jordan: --- upload file and metadata to storage API
|
||||
// 2. pay for storage by hashing files and attaching memo for each file
|
||||
};
|
||||
|
||||
export const sendTransactionX = async (
|
||||
connection: Connection,
|
||||
wallet: any,
|
||||
instructions: TransactionInstruction[],
|
||||
signers: Account[],
|
||||
awaitConfirmation = true,
|
||||
commitment: Commitment = 'singleGossip',
|
||||
includesFeePayer: boolean = false,
|
||||
) => {
|
||||
let transaction = new Transaction();
|
||||
instructions.forEach(instruction => transaction.add(instruction));
|
||||
transaction.recentBlockhash = (
|
||||
await connection.getRecentBlockhash(commitment)
|
||||
).blockhash;
|
||||
if(includesFeePayer) {
|
||||
transaction.setSigners(
|
||||
...signers.map(s => s.publicKey),
|
||||
);
|
||||
} else {
|
||||
transaction.setSigners(
|
||||
// fee payed by the wallet owner
|
||||
wallet.publicKey,
|
||||
...signers.map(s => s.publicKey),
|
||||
);
|
||||
}
|
||||
|
||||
if (signers.length > 0) {
|
||||
transaction.partialSign(...signers);
|
||||
}
|
||||
if(!includesFeePayer) {
|
||||
transaction = await wallet.signTransaction(transaction);
|
||||
}
|
||||
|
||||
const rawTransaction = transaction.serialize();
|
||||
let options = {
|
||||
skipPreflight: true,
|
||||
commitment,
|
||||
};
|
||||
|
||||
const txid = await connection.sendRawTransaction(rawTransaction, options);
|
||||
let slot = 0;
|
||||
|
||||
if (awaitConfirmation) {
|
||||
const confirmation = await connection.confirmTransaction(
|
||||
txid,
|
||||
options && (options.commitment as any),
|
||||
);
|
||||
const status = confirmation.value;
|
||||
slot = confirmation.context.slot;
|
||||
|
||||
|
||||
}
|
||||
|
||||
return { txid, slot };
|
||||
};
|
||||
|
||||
export const prepPayForFilesTxn = async (
|
||||
wallet: WalletAdapter,
|
||||
files: File[],
|
||||
|
|
|
@ -285,6 +285,7 @@ const InfoStep = (props: {
|
|||
<label className="action-field">
|
||||
<span className="field-title">Title</span>
|
||||
<Input
|
||||
autoFocus
|
||||
className="input"
|
||||
placeholder="Max 50 characters"
|
||||
allowClear
|
||||
|
@ -374,6 +375,7 @@ const RoyaltiesStep = (props: {
|
|||
<label className="action-field">
|
||||
<span className="field-title">Royalty Percentage</span>
|
||||
<InputNumber
|
||||
autoFocus
|
||||
min={0}
|
||||
max={100}
|
||||
placeholder="Between 0 and 100"
|
||||
|
|
Loading…
Reference in New Issue