feat: minting

This commit is contained in:
bartosz-lipinski 2021-04-09 18:21:52 -05:00
parent 91b8870952
commit 6bc32a4c6e
5 changed files with 104 additions and 108 deletions

View File

@ -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,
},
{

View File

@ -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',

View File

@ -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),

View File

@ -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[],

View 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"