From 6bc32a4c6ecff439241d961e824a2c9ef8d38e6a Mon Sep 17 00:00:00 2001 From: bartosz-lipinski <264380+bartosz-lipinski@users.noreply.github.com> Date: Fri, 9 Apr 2021 18:21:52 -0500 Subject: [PATCH] feat: minting --- packages/common/src/actions/metadata.ts | 54 ++++++- .../src/components/CurrentUserBadge/index.tsx | 2 - packages/common/src/contexts/connection.tsx | 6 +- packages/metavinci/src/models/nft.tsx | 148 ++++++------------ .../metavinci/src/views/artCreate/index.tsx | 2 + 5 files changed, 104 insertions(+), 108 deletions(-) diff --git a/packages/common/src/actions/metadata.ts b/packages/common/src/actions/metadata.ts index a586a9b..04c24da 100644 --- a/packages/common/src/actions/metadata.ts +++ b/packages/common/src/actions/metadata.ts @@ -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([ 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, }, { diff --git a/packages/common/src/components/CurrentUserBadge/index.tsx b/packages/common/src/components/CurrentUserBadge/index.tsx index 418263e..5ef692d 100644 --- a/packages/common/src/components/CurrentUserBadge/index.tsx +++ b/packages/common/src/components/CurrentUserBadge/index.tsx @@ -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', diff --git a/packages/common/src/contexts/connection.tsx b/packages/common/src/contexts/connection.tsx index c10ec42..21601a6 100644 --- a/packages/common/src/contexts/connection.tsx +++ b/packages/common/src/contexts/connection.tsx @@ -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), diff --git a/packages/metavinci/src/models/nft.tsx b/packages/metavinci/src/models/nft.tsx index 7f0340d..4ac1eac 100644 --- a/packages/metavinci/src/models/nft.tsx +++ b/packages/metavinci/src/models/nft.tsx @@ -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: Arweave Link, 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[], diff --git a/packages/metavinci/src/views/artCreate/index.tsx b/packages/metavinci/src/views/artCreate/index.tsx index aba2f27..b92a471 100644 --- a/packages/metavinci/src/views/artCreate/index.tsx +++ b/packages/metavinci/src/views/artCreate/index.tsx @@ -285,6 +285,7 @@ const InfoStep = (props: {