-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into raribleIngestor
- Loading branch information
Showing
14 changed files
with
1,159 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
export const FOUNDATION_MINT_ABI = [ | ||
{ | ||
inputs: [{ internalType: 'address', name: 'nftContract', type: 'address' }], | ||
name: 'getFixedPriceSaleV2', | ||
outputs: [ | ||
{ internalType: 'address payable', name: 'seller', type: 'address' }, | ||
{ internalType: 'uint256', name: 'price', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'limitPerAccount', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'numberOfTokensAvailableToMint', type: 'uint256' }, | ||
{ internalType: 'bool', name: 'marketCanMint', type: 'bool' }, | ||
{ internalType: 'uint256', name: 'generalAvailabilityStartTime', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'earlyAccessStartTime', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'mintFeePerNftInWei', type: 'uint256' }, | ||
], | ||
stateMutability: 'view', | ||
type: 'function', | ||
}, | ||
{ | ||
inputs: [{ internalType: 'address', name: 'nftContract', type: 'address' }], | ||
name: 'getDutchAuctionV2', | ||
outputs: [ | ||
{ internalType: 'uint256', name: 'maxPrice', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'minPrice', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'limitPerAccount', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'startTime', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'endTime', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'totalAvailableSupply', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'totalMintedCount', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'lastSalePrice', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'currentPrice', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'mintFeePerNftInWei', type: 'uint256' }, | ||
], | ||
stateMutability: 'view', | ||
type: 'function', | ||
}, | ||
{ | ||
inputs: [ | ||
{ internalType: 'address', name: 'nftContract', type: 'address' }, | ||
{ internalType: 'uint256', name: 'count', type: 'uint256' }, | ||
{ internalType: 'address', name: 'nftRecipient', type: 'address' }, | ||
], | ||
name: 'mintFromDutchAuctionV2', | ||
outputs: [{ internalType: 'uint256', name: 'firstTokenId', type: 'uint256' }], | ||
stateMutability: 'payable', | ||
type: 'function', | ||
}, | ||
{ | ||
inputs: [ | ||
{ internalType: 'address', name: 'nftContract', type: 'address' }, | ||
{ internalType: 'uint16', name: 'count', type: 'uint16' }, | ||
{ internalType: 'address', name: 'nftRecipient', type: 'address' }, | ||
{ internalType: 'address payable', name: 'buyReferrer', type: 'address' }, | ||
], | ||
name: 'mintFromFixedPriceSaleV2', | ||
outputs: [{ internalType: 'uint256', name: 'firstTokenId', type: 'uint256' }], | ||
stateMutability: 'payable', | ||
type: 'function', | ||
}, | ||
{ | ||
inputs: [ | ||
{ internalType: 'address', name: 'nftContract', type: 'address' }, | ||
{ internalType: 'uint256', name: 'count', type: 'uint256' }, | ||
{ internalType: 'address', name: 'nftRecipient', type: 'address' }, | ||
{ internalType: 'address payable', name: 'buyReferrer', type: 'address' }, | ||
{ internalType: 'bytes32[]', name: 'proof', type: 'bytes32[]' }, | ||
], | ||
name: 'mintFromFixedPriceSaleWithEarlyAccessAllowlistV2', | ||
outputs: [{ internalType: 'uint256', name: 'firstTokenId', type: 'uint256' }], | ||
stateMutability: 'payable', | ||
type: 'function', | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
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 { getFoundationMintPriceInWei } from './onchain-metadata'; | ||
import { getFoundationMintByAddress } from './offchain-metadata'; | ||
import { FOUNDATION_MINT_ABI } from './abi'; | ||
|
||
const CONTRACT_ADDRESS = '0x62037b26ffF91929655AA3A060F327b47d1e2b3e'; | ||
|
||
const getChainId = (chain: string) => { | ||
switch (chain) { | ||
case 'base': | ||
return 8453; | ||
case 'eth': | ||
default: | ||
return 1; | ||
} | ||
}; | ||
|
||
export class FoundationIngestor implements MintIngestor { | ||
async supportsUrl(_resources: MintIngestorResources, url: string): Promise<boolean> { | ||
const urlSplit = url.split('/'); | ||
const slug = urlSplit.pop(); | ||
if (!slug || slug.length !== 42 || !slug.startsWith('0x')) { | ||
return false; | ||
} | ||
const chain = urlSplit.pop(); | ||
if (chain !== 'base') { | ||
return false; | ||
} | ||
return new URL(url).hostname === 'www.foundation.app' || new URL(url).hostname === 'foundation.app'; | ||
} | ||
|
||
async supportsContract(resources: MintIngestorResources, contractOptions: MintContractOptions): Promise<boolean> { | ||
if (contractOptions.chainId !== 8453) { | ||
return false; | ||
} | ||
const contract = await getFoundationMintByAddress(resources, contractOptions); | ||
if (!contract) { | ||
return false; | ||
} | ||
if (contract.contractType !== 'FND_BATCH_MINT_REVEAL') { | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
async createMintForContract( | ||
resources: MintIngestorResources, | ||
contractOptions: MintContractOptions, | ||
): Promise<MintTemplate> { | ||
|
||
const isCompatible = await this.supportsContract(resources, contractOptions); | ||
if (!isCompatible) { | ||
throw new MintIngestorError(MintIngestionErrorName.IncompatibleUrl, 'Contract not supported'); | ||
} | ||
|
||
const mintBuilder = new MintTemplateBuilder() | ||
.setMintInstructionType(MintInstructionType.EVM_MINT) | ||
.setPartnerName('Foundation'); | ||
|
||
if (contractOptions.url) { | ||
mintBuilder.setMarketingUrl(contractOptions.url); | ||
} | ||
|
||
const collection = await getFoundationMintByAddress(resources, contractOptions); | ||
|
||
if (!collection) { | ||
throw new MintIngestorError(MintIngestionErrorName.CouldNotResolveMint, 'Collection not found'); | ||
} | ||
|
||
const contractAddress = collection.contractAddress; | ||
|
||
const description = collection?.description; | ||
const image = collection?.media.url; | ||
|
||
mintBuilder.setName(collection.name).setDescription(description).setFeaturedImageUrl(image); | ||
mintBuilder.setMintOutputContract({ chainId: 8453, address: contractAddress }); | ||
|
||
if (collection.coverImageUrl) { | ||
mintBuilder.addImage(collection.coverImageUrl, 'cover-image'); | ||
} | ||
|
||
if (!collection.creator) { | ||
throw new MintIngestorError(MintIngestionErrorName.MissingRequiredData, 'Error finding creator'); | ||
} | ||
|
||
mintBuilder.setCreator({ | ||
name: collection.creator.name, | ||
walletAddress: collection.creator.publicKey, | ||
}); | ||
|
||
const totalPriceWei = await getFoundationMintPriceInWei( | ||
8453, | ||
CONTRACT_ADDRESS, | ||
contractAddress, | ||
resources.alchemy, | ||
collection.saleType, | ||
); | ||
|
||
if (!totalPriceWei) { | ||
throw new MintIngestorError(MintIngestionErrorName.MissingRequiredData, 'Price not available'); | ||
} | ||
|
||
mintBuilder.setMintInstructions({ | ||
chainId: 8453, | ||
contractAddress: CONTRACT_ADDRESS, | ||
contractMethod: | ||
collection.saleType === 'FIXED_PRICE_DROP' | ||
? 'mintFromFixedPriceSaleV2' | ||
: 'mintFromDutchAuctionV2', | ||
contractParams: | ||
collection.saleType === 'FIXED_PRICE_DROP' | ||
? `["${collection.contractAddress}", 1, address, "0x0000000000000000000000000000000000000000"]` | ||
: `["${collection.contractAddress}", 1, address]`, | ||
abi: FOUNDATION_MINT_ABI, | ||
priceWei: totalPriceWei, | ||
}); | ||
|
||
const now = new Date(); | ||
const nowUTCString = now.toISOString(); | ||
|
||
const liveDate = | ||
new Date() > collection.generalAvailabilityStartTime | ||
? new Date(nowUTCString) | ||
: new Date(collection.generalAvailabilityStartTime); | ||
mintBuilder | ||
.setAvailableForPurchaseStart(new Date(collection.generalAvailabilityStartTime ? `${collection.generalAvailabilityStartTime}Z` : nowUTCString)) | ||
.setAvailableForPurchaseEnd(new Date(collection.endTime ? `${collection.endTime}Z` : '2030-01-01T00:00:00Z')) | ||
.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://foundation.app/mint/base/0x0C92Ce2aECc651Dd3733008A301f126662ae4A50 | ||
const splits = url.split('/'); | ||
const tokenAddress = splits.pop(); | ||
const chain = splits.pop(); | ||
|
||
if (!chain || !tokenAddress) { | ||
throw new MintIngestorError(MintIngestionErrorName.CouldNotResolveMint, 'Url error'); | ||
} | ||
|
||
const chainId = getChainId(chain); | ||
return this.createMintForContract(resources, { chainId, contractAddress: tokenAddress, url}); | ||
} | ||
} |
Oops, something went wrong.