-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathindex.ts
More file actions
150 lines (120 loc) · 5.38 KB
/
Copy pathindex.ts
File metadata and controls
150 lines (120 loc) · 5.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { MintContractOptions, MintIngestor, MintIngestorResources } from '../../lib/types/mint-ingestor';
import { MintIngestionErrorName, MintIngestorError } from '../../lib/types/mint-ingestor-error';
import { MintInstructionType, MintTemplate } from '../../lib/types/mint-template';
import { MintTemplateBuilder } from '../../lib/builder/mint-template-builder';
import {
getVvMintPriceInWei,
getVvLatestTokenId,
getVvCollectionCreator,
getVvCollectionMetadata,
} from './onchain-metadata';
import { getVvCollection } from './onchain-metadata';
import { MINT_CONTRACT_ABI } from './abi';
export class VvIngestor implements MintIngestor {
async supportsUrl(resources: MintIngestorResources, url: string): Promise<boolean> {
const splitUrl = url.split('/');
const tokenId = splitUrl.pop();
const address = splitUrl.pop();
if (!tokenId || !address) {
return false;
}
const collection = await getVvCollection(resources.alchemy, address as string, +tokenId);
if (!collection) return false;
const urlPattern = /^https:\/\/mint\.vv\.xyz\/0x[a-fA-F0-9]{40}\/\d+$/;
return (
new URL(url).hostname === 'www.mint.vv.xyz' || new URL(url).hostname === 'mint.vv.xyz' || urlPattern.test(url)
);
}
async supportsContract(resources: MintIngestorResources, contractOptions: MintContractOptions): Promise<boolean> {
if (!(contractOptions.chainId === 1 || contractOptions.chainId === 8453)) {
return false;
}
const collection = await getVvCollection(
resources.alchemy,
contractOptions.contractAddress,
contractOptions.tokenId ? +contractOptions.tokenId : undefined,
);
if (!collection) {
return false;
}
return true;
}
async createMintForContract(
resources: MintIngestorResources,
contractOptions: MintContractOptions,
): Promise<MintTemplate> {
const mintBuilder = new MintTemplateBuilder()
.setMintInstructionType(MintInstructionType.EVM_MINT)
.setPartnerName('Highlight');
if (contractOptions.url) {
mintBuilder.setMarketingUrl(contractOptions.url);
}
const { contractAddress } = contractOptions;
// Use latestTokenId as default, see: https://docs.mint.vv.xyz/guide/contracts/mint#token-count
const tokenId = contractOptions.tokenId ?? (await getVvLatestTokenId(resources.alchemy, contractAddress));
const collection = await getVvCollection(resources.alchemy, contractAddress, tokenId ? +tokenId : undefined);
if (!collection) {
throw new MintIngestorError(MintIngestionErrorName.CouldNotResolveMint, 'Collection not found');
}
const metadata = await getVvCollectionMetadata(resources.alchemy, contractAddress);
if (!metadata) {
throw new MintIngestorError(MintIngestionErrorName.CouldNotResolveMint, 'Collection metadata not found');
}
mintBuilder.setName(metadata.name).setDescription(metadata.description).setFeaturedImageUrl(metadata.image);
mintBuilder.setMintOutputContract({ chainId: contractOptions.chainId ?? 1, address: contractAddress });
const creatorData = await getVvCollectionCreator(resources.alchemy, contractAddress);
if (!creatorData) {
throw new MintIngestorError(MintIngestionErrorName.MissingRequiredData, 'Error finding creator');
}
const { creator, name: creatorName } = creatorData;
mintBuilder.setCreator({
name: creatorName || '',
walletAddress: creator,
});
mintBuilder.setMintOutputContract({ chainId: 1, address: contractAddress });
const totalPriceWei = await getVvMintPriceInWei(resources.alchemy, contractAddress, collection.mintedBlock);
if (!totalPriceWei) {
throw new MintIngestorError(MintIngestionErrorName.MissingRequiredData, 'Price not available');
}
mintBuilder.setMintInstructions({
chainId: contractOptions.chainId ?? 1,
contractAddress,
contractMethod: 'mint',
contractParams: `[${tokenId}, quantity]`,
abi: MINT_CONTRACT_ABI,
priceWei: totalPriceWei,
supportsQuantity: true,
});
const { closeAt } = collection;
// Tokens are open to be minted for 24 hours after token creation.
const startTimestamp = new Date(closeAt * 1000 - 24 * 60 * 60 * 1000);
const liveDate = +new Date() > +startTimestamp ? new Date() : startTimestamp;
mintBuilder
.setAvailableForPurchaseStart(new Date(startTimestamp || Date.now()))
.setAvailableForPurchaseEnd(new Date(closeAt * 1000))
.setLiveDate(liveDate);
return mintBuilder.build();
}
async createMintTemplateForUrl(resources: MintIngestorResources, url: string): Promise<MintTemplate> {
const isCompatible = await this.supportsUrl(resources, url);
if (!isCompatible) {
throw new MintIngestorError(MintIngestionErrorName.IncompatibleUrl, 'Incompatible URL');
}
// Example URL: https://mint.vv.xyz/0xcb52f0fe1d559cd2869db7f29753e8951381b4a3/1
const splits = url.split('/');
const id = splits.pop();
const contract = splits.pop();
if (!id || !contract) {
throw new MintIngestorError(MintIngestionErrorName.CouldNotResolveMint, 'Url error');
}
const collection = await getVvCollection(resources.alchemy, contract, +id);
if (!collection) {
throw new MintIngestorError(MintIngestionErrorName.CouldNotResolveMint, 'No such collection');
}
return this.createMintForContract(resources, {
chainId: 1,
contractAddress: contract,
url,
});
}
}