Skip to content

Commit 6b48b92

Browse files
[SOL] solana dashboard integration (#138)
1 parent 9d74a43 commit 6b48b92

File tree

11 files changed

+133
-110
lines changed

11 files changed

+133
-110
lines changed

.changeset/tiny-apes-trade.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@thirdweb-dev/solana": patch
3+
---
4+
5+
Optimize Drop creation flow, cleanup types

packages/solana/src/classes/claim-conditions.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { TransactionResult } from "../types/common";
22
import {
3-
NFTDropClaimInput,
4-
NFTDropClaimSchema,
5-
NFTDropClaimOutput,
3+
NFTDropConditionsOutputSchema,
4+
NFTDropMetadataInput,
65
} from "../types/contracts/nft-drop";
76
import { Metaplex } from "@metaplex-foundation/js";
87
import { PublicKey } from "@solana/web3.js";
@@ -16,21 +15,21 @@ export class ClaimConditions {
1615
this.metaplex = metaplex;
1716
}
1817

19-
async get(): Promise<NFTDropClaimOutput> {
18+
async get(): Promise<NFTDropMetadataInput> {
2019
const candyMachine = await this.getCandyMachine();
2120

2221
return {
23-
price: BigInt(candyMachine.price.basisPoints.toNumber()),
24-
sellerFeeBasisPoints: BigInt(candyMachine.sellerFeeBasisPoints),
25-
itemsAvailable: BigInt(candyMachine.itemsAvailable.toNumber()),
22+
price: candyMachine.price.basisPoints.toNumber(),
23+
sellerFeeBasisPoints: candyMachine.sellerFeeBasisPoints,
24+
itemsAvailable: candyMachine.itemsAvailable.toNumber(),
2625
goLiveDate: candyMachine.goLiveDate
2726
? new Date(candyMachine.goLiveDate.toNumber() * 1000)
2827
: undefined,
2928
};
3029
}
3130

32-
async set(metadata: NFTDropClaimInput): Promise<TransactionResult> {
33-
const parsed = NFTDropClaimSchema.parse(metadata);
31+
async set(metadata: NFTDropMetadataInput): Promise<TransactionResult> {
32+
const parsed = NFTDropConditionsOutputSchema.parse(metadata);
3433

3534
const result = await this.metaplex
3635
.candyMachines()

packages/solana/src/classes/deployer.ts

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,24 @@ import {
55
TokenMetadataInputSchema,
66
} from "../types/contracts";
77
import {
8-
NFTDropContractSchema,
8+
NFTDropConditionsOutputSchema,
99
NFTDropMetadataInput,
1010
} from "../types/contracts/nft-drop";
1111
import { enforceCreator } from "./helpers/creators-helper";
12-
import { findMetadataPda, Metaplex, token } from "@metaplex-foundation/js";
12+
import {
13+
findMetadataPda,
14+
Metaplex,
15+
sol,
16+
toBigNumber,
17+
token,
18+
} from "@metaplex-foundation/js";
1319
import {
1420
createCreateMetadataAccountV2Instruction,
1521
DataV2,
1622
} from "@metaplex-foundation/mpl-token-metadata";
17-
import { Keypair, PublicKey } from "@solana/web3.js";
23+
import { Keypair } from "@solana/web3.js";
1824
import { IStorage } from "@thirdweb-dev/storage";
25+
import BN from "bn.js";
1926

2027
export class Deployer {
2128
private metaplex: Metaplex;
@@ -94,23 +101,52 @@ export class Deployer {
94101
}
95102

96103
async createNftDrop(metadata: NFTDropMetadataInput): Promise<string> {
97-
const parsed = NFTDropContractSchema.parse(metadata);
104+
const collectionInfo = NFTCollectionMetadataInputSchema.parse(metadata);
105+
const candyMachineInfo = NFTDropConditionsOutputSchema.parse(metadata);
106+
const uri = await this.storage.uploadMetadata(collectionInfo);
98107

99-
// TODO make it a single tx
100-
const collection = await this.createNftCollection(metadata);
108+
const collectionMint = Keypair.generate();
109+
const collectionTx = await this.metaplex
110+
.nfts()
111+
.builders()
112+
.create({
113+
useNewMint: collectionMint,
114+
name: collectionInfo.name,
115+
symbol: collectionInfo.symbol,
116+
sellerFeeBasisPoints: 0,
117+
uri,
118+
isCollection: true,
119+
creators: enforceCreator(
120+
collectionInfo.creators,
121+
this.metaplex.identity().publicKey,
122+
),
123+
});
101124

102-
const { candyMachine: nftDrop } = await this.metaplex
125+
const candyMachineKeypair = Keypair.generate();
126+
const candyMachineTx = await this.metaplex
103127
.candyMachines()
128+
.builders()
104129
.create({
105-
...parsed,
106-
collection: new PublicKey(collection),
130+
...candyMachineInfo,
131+
price: candyMachineInfo.price || sol(0),
132+
sellerFeeBasisPoints: candyMachineInfo.sellerFeeBasisPoints || 0,
133+
itemsAvailable: candyMachineInfo.itemsAvailable || toBigNumber(0),
134+
candyMachine: candyMachineKeypair,
135+
collection: collectionMint.publicKey,
107136
creators: enforceCreator(
108-
parsed.creators,
137+
collectionInfo.creators,
109138
this.metaplex.identity().publicKey,
110139
),
111-
})
112-
.run();
140+
});
141+
142+
const result = await collectionTx
143+
.add(candyMachineTx)
144+
.sendAndConfirm(this.metaplex);
145+
146+
if (!result.response.signature) {
147+
throw new Error("Transaction failed");
148+
}
113149

114-
return nftDrop.address.toBase58();
150+
return candyMachineKeypair.publicKey.toBase58();
115151
}
116152
}

packages/solana/src/classes/registry.ts

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
CandyMachine,
44
Metadata,
55
Metaplex,
6-
TokenMetadataProgram,
6+
TokenProgram,
77
} from "@metaplex-foundation/js";
88
import { TokenStandard } from "@metaplex-foundation/mpl-token-metadata";
99
import { PublicKey } from "@solana/web3.js";
@@ -17,48 +17,46 @@ export class Registry {
1717

1818
public async getAccountType(address: string) {
1919
try {
20-
const metadata = await this.metaplex
21-
.nfts()
22-
.findByMint({ mintAddress: new PublicKey(address) })
20+
const candyMachine = await this.metaplex
21+
.candyMachines()
22+
.findByAddress({ address: new PublicKey(address) })
2323
.run();
24-
if (metadata) {
25-
if (metadata.collectionDetails) {
26-
return "nft-collection";
27-
} else {
28-
if (metadata.tokenStandard === TokenStandard.Fungible) {
29-
return "token";
30-
}
31-
}
24+
if (candyMachine) {
25+
return "nft-drop";
3226
}
33-
} catch (e) {
34-
try {
35-
const candyMachine = await this.metaplex
36-
.candyMachines()
37-
.findByAddress({ address: new PublicKey(address) })
38-
.run();
39-
if (candyMachine) {
40-
return "nft-drop";
27+
} catch (err) {
28+
// ignore and try next
29+
}
30+
const metadata = await this.metaplex
31+
.nfts()
32+
.findByMint({ mintAddress: new PublicKey(address) })
33+
.run();
34+
35+
if (metadata) {
36+
if (metadata.collectionDetails) {
37+
return "nft-collection";
38+
} else {
39+
if (metadata.tokenStandard === TokenStandard.Fungible) {
40+
return "token";
4141
}
42-
} catch (err) {
43-
return undefined;
4442
}
4543
}
46-
return undefined;
44+
throw new Error("Unknown account type");
4745
}
4846

4947
public async getAccountsForWallet(
5048
walletAddress: string,
5149
): Promise<WalletAccount[]> {
52-
const pubKeys = await this.getMetadataAddressesForWallet(walletAddress);
50+
const mints = await this.getOwnedTokenAccountsForWallet(walletAddress);
5351
const metadatas = await this.metaplex
5452
.nfts()
55-
.findAllByMintList({ mints: pubKeys })
53+
.findAllByMintList({ mints })
5654
.run();
5755

5856
const candyMachines = await this.metaplex
5957
.candyMachines()
6058
.findAllBy({
61-
type: "wallet",
59+
type: "authority",
6260
publicKey: new PublicKey(walletAddress),
6361
})
6462
.run();
@@ -106,10 +104,10 @@ export class Registry {
106104
);
107105
}
108106

109-
public async getMetadataAddressesForWallet(walletAddress: string) {
110-
return await TokenMetadataProgram.metadataV1Accounts(this.metaplex)
107+
private async getOwnedTokenAccountsForWallet(walletAddress: string) {
108+
return await TokenProgram.tokenAccounts(this.metaplex)
111109
.selectMint()
112-
.whereCreator(1, new PublicKey(walletAddress))
110+
.whereOwner(new PublicKey(walletAddress))
113111
.getDataAsPublicKeys();
114112
}
115113
}

packages/solana/src/contracts/nft-collection.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ export class NFTCollection {
2222
private metaplex: Metaplex;
2323
private storage: IStorage;
2424
private nft: NFTHelper;
25-
collectionMintAddress: PublicKey;
25+
public publicKey: PublicKey;
26+
public accountType = "nft-collection" as const;
2627

2728
constructor(
2829
collectionMintAddress: string,
@@ -32,13 +33,13 @@ export class NFTCollection {
3233
this.storage = storage;
3334
this.metaplex = metaplex;
3435
this.nft = new NFTHelper(metaplex);
35-
this.collectionMintAddress = new PublicKey(collectionMintAddress);
36+
this.publicKey = new PublicKey(collectionMintAddress);
3637
}
3738

3839
async getMetadata(): Promise<NFTCollectionMetadata> {
3940
const metadata = await this.metaplex
4041
.nfts()
41-
.findByMint({ mintAddress: this.collectionMintAddress })
42+
.findByMint({ mintAddress: this.publicKey })
4243
.run();
4344

4445
return this.nft.toNFTMetadata(metadata);
@@ -52,7 +53,7 @@ export class NFTCollection {
5253
const allSignatures: ConfirmedSignatureInfo[] = [];
5354
// This returns the first 1000, so we need to loop through until we run out of signatures to get.
5455
let signatures = await this.metaplex.connection.getSignaturesForAddress(
55-
this.collectionMintAddress,
56+
this.publicKey,
5657
);
5758

5859
allSignatures.push(...signatures);
@@ -61,7 +62,7 @@ export class NFTCollection {
6162
before: signatures[signatures.length - 1]?.signature,
6263
};
6364
signatures = await this.metaplex.connection.getSignaturesForAddress(
64-
this.collectionMintAddress,
65+
this.publicKey,
6566
options,
6667
);
6768
allSignatures.push(...signatures);
@@ -213,7 +214,7 @@ export class NFTCollection {
213214
name: metadata.name || "",
214215
uri,
215216
sellerFeeBasisPoints: 0,
216-
collection: this.collectionMintAddress,
217+
collection: this.publicKey,
217218
collectionAuthority: this.metaplex.identity(),
218219
tokenOwner: new PublicKey(to),
219220
// Always sets max supply to unlimited so editions can be minted

packages/solana/src/contracts/nft-drop.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ export class NFTDrop {
1616
private metaplex: Metaplex;
1717
private storage: IStorage;
1818
private nft: NFTHelper;
19-
public dropMintAddress: PublicKey;
19+
public accountType = "nft-drop" as const;
20+
public publicKey: PublicKey;
2021
public claimConditions: ClaimConditions;
2122

2223
constructor(dropMintAddress: string, metaplex: Metaplex, storage: IStorage) {
2324
this.storage = storage;
2425
this.metaplex = metaplex;
2526
this.nft = new NFTHelper(metaplex);
26-
this.dropMintAddress = new PublicKey(dropMintAddress);
27+
this.publicKey = new PublicKey(dropMintAddress);
2728
this.claimConditions = new ClaimConditions(dropMintAddress, metaplex);
2829
}
2930

@@ -57,7 +58,7 @@ export class NFTDrop {
5758
async getAllClaimed(): Promise<NFTMetadata[]> {
5859
const nfts = await this.metaplex
5960
.candyMachines()
60-
.findMintedNfts({ candyMachine: this.dropMintAddress })
61+
.findMintedNfts({ candyMachine: this.publicKey })
6162
.run();
6263

6364
const metadatas = nfts.map((nft) => this.nft.toNFTMetadata(nft));
@@ -128,7 +129,7 @@ export class NFTDrop {
128129
private async getCandyMachine() {
129130
return this.metaplex
130131
.candyMachines()
131-
.findByAddress({ address: this.dropMintAddress })
132+
.findByAddress({ address: this.publicKey })
132133
.run();
133134
}
134135
}

packages/solana/src/contracts/token.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,26 @@ export class Token {
2222
private connection: Connection;
2323
private metaplex: Metaplex;
2424
private storage: IStorage;
25-
tokenMintAddress: PublicKey;
25+
public accountType = "token" as const;
26+
public publicKey: PublicKey;
2627

2728
constructor(tokenMintAddress: string, metaplex: Metaplex, storage: IStorage) {
2829
this.storage = storage;
2930
this.metaplex = metaplex;
3031
this.connection = metaplex.connection;
31-
this.tokenMintAddress = new PublicKey(tokenMintAddress);
32+
this.publicKey = new PublicKey(tokenMintAddress);
3233
}
3334

3435
async getMint() {
3536
return await this.metaplex
3637
.tokens()
37-
.findMintByAddress({ address: this.tokenMintAddress })
38+
.findMintByAddress({ address: this.publicKey })
3839
.run(); // TODO abstract types away
3940
}
4041

4142
async getMetadata(): Promise<TokenMetadata> {
4243
const mint = await this.getMint();
43-
const addr = findMetadataPda(this.tokenMintAddress);
44+
const addr = findMetadataPda(this.publicKey);
4445
const account = await this.metaplex.rpc().getAccount(addr);
4546
const meta = toMetadata(toMetadataAccount(account));
4647
return {
@@ -66,7 +67,7 @@ export class Token {
6667
.tokens()
6768
.mint({
6869
amount: token(amountParsed, info.decimals),
69-
mintAddress: this.tokenMintAddress,
70+
mintAddress: this.publicKey,
7071
})
7172
.run();
7273
return {
@@ -82,7 +83,7 @@ export class Token {
8283
const result = await this.metaplex
8384
.tokens()
8485
.send({
85-
mintAddress: this.tokenMintAddress,
86+
mintAddress: this.publicKey,
8687
amount: token(amount, info.decimals),
8788
toOwner: new PublicKey(receiverAddress),
8889
})
@@ -99,7 +100,7 @@ export class Token {
99100
async balanceOf(walletAddress: string): Promise<CurrencyValue> {
100101
const mint = await this.getMint();
101102
const addr = await getAssociatedTokenAddress(
102-
this.tokenMintAddress,
103+
this.publicKey,
103104
new PublicKey(walletAddress),
104105
);
105106
try {

packages/solana/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ export {
1212
TokenMetadataInputSchema,
1313
NFTCollectionMetadataInputSchema,
1414
} from "./types/contracts";
15-
export { NFTDropContractSchema } from "./types/contracts/nft-drop";
15+
export { NFTDropContractInputSchema } from "./types/contracts/nft-drop";

0 commit comments

Comments
 (0)