diff --git a/packages/sdk-viem/src/client/edition/mint.ts b/packages/sdk-viem/src/client/edition/mint.ts index 1191f68b..f3734cba 100644 --- a/packages/sdk-viem/src/client/edition/mint.ts +++ b/packages/sdk-viem/src/client/edition/mint.ts @@ -77,7 +77,8 @@ async function mintHelper( } } - const value = BigInt(mintSchedule.price * BigInt(quantity)) + mintSchedule.platformTransactionFee + const value = + (mintSchedule.price + mintSchedule.platformPerTokenFee) * BigInt(quantity) + mintSchedule.platformPerTransactionFee const txnOverrides = { gas, @@ -289,7 +290,8 @@ async function mintToHelper( } } - const value = BigInt(mintSchedule.price * BigInt(quantity)) + mintSchedule.platformTransactionFee + const value = + (mintSchedule.price + mintSchedule.platformPerTokenFee) * BigInt(quantity) + mintSchedule.platformPerTransactionFee const txnOverrides = { gas, diff --git a/packages/sdk-viem/src/client/edition/schedules.ts b/packages/sdk-viem/src/client/edition/schedules.ts index d27542f3..b3c66ca9 100644 --- a/packages/sdk-viem/src/client/edition/schedules.ts +++ b/packages/sdk-viem/src/client/edition/schedules.ts @@ -3,7 +3,17 @@ import { type Address } from 'viem' import { minterModuleV2_1Abi } from '../../abi/minter-module-v2_1' import { soundEditionV1_2Abi } from '../../abi/sound-edition-v1_2' import { UnsupportedMinterError } from '../../errors' -import { type BlockOrBlockTag, isHandledMinterInterfaceId, type MintSchedule } from '../../types' +import { + type BlockOrBlockTag, + isHandledMinterInterfaceId, + type MintSchedule, + type RangeEditionV1Schedule, + type RangeEditionV2Schedule, + type RangeEditionV2_1Schedule, + type MerkleDropV1Schedule, + type MerkleDropV2Schedule, + type MerkleDropV2_1Schedule, +} from '../../types' import { minterAbiMap } from '../../utils/constants' import { exhaustiveGuard } from '../../utils/helpers' import { LazyPromise } from '../../utils/promise' @@ -293,56 +303,100 @@ export async function mintInfosFromMinter( }) }) + if (!isHandledMinterInterfaceId(interfaceId)) { + throw new UnsupportedMinterError({ interfaceId }) + } + + const { readContract, multicall } = await clientPromise + return Promise.all( mintIds.map(async (mintId): Promise => { - if (!isHandledMinterInterfaceId(interfaceId)) { - throw new UnsupportedMinterError({ interfaceId }) - } + switch (interfaceId) { + case interfaceIds.IRangeEditionMinter: { + const scheduleInfo = await readContract({ + abi: minterAbiMap[interfaceId], + address: minterAddress, + functionName: 'mintInfo', + args: [editionAddress, BigInt(mintId)], + }) - const { readContract } = await clientPromise + return { + mintType: 'RangeEdition', + affiliateFeeBPS: scheduleInfo.affiliateFeeBPS, + cutoffTime: scheduleInfo.cutoffTime, + editionAddress: editionAddress, + endTime: scheduleInfo.endTime, + interfaceId, + maxMintable: (unixTimestamp?: number) => + (unixTimestamp || Math.floor(Date.now() / 1000)) < scheduleInfo.cutoffTime + ? scheduleInfo.maxMintableUpper + : scheduleInfo.maxMintableLower, + maxMintableLower: scheduleInfo.maxMintableLower, + maxMintablePerAccount: scheduleInfo.maxMintablePerAccount, + maxMintableUpper: scheduleInfo.maxMintableUpper, + minterAddress, + mintId: BigInt(mintId), + mintPaused: scheduleInfo.mintPaused, + price: scheduleInfo.price, + startTime: scheduleInfo.startTime, + totalMinted: scheduleInfo.totalMinted, + platformPerTokenFee: 0n, + platformPerTransactionFee: 0n, + } satisfies RangeEditionV1Schedule + } + case interfaceIds.IRangeEditionMinterV2: { + const [scheduleInfo, platformPerTokenFee] = await multicall({ + contracts: [ + { + abi: minterAbiMap[interfaceId], + address: minterAddress, + functionName: 'mintInfo', + args: [editionAddress, BigInt(mintId)], + }, + { + abi: minterAbiMap[interfaceId], + address: minterAddress, + functionName: 'platformFlatFee', + }, + ], + allowFailure: false, + }) + + return { + mintType: 'RangeEdition', + affiliateFeeBPS: scheduleInfo.affiliateFeeBPS, + cutoffTime: scheduleInfo.cutoffTime, + editionAddress: editionAddress, + endTime: scheduleInfo.endTime, + interfaceId, + maxMintable: (unixTimestamp?: number) => + (unixTimestamp || Math.floor(Date.now() / 1000)) < scheduleInfo.cutoffTime + ? scheduleInfo.maxMintableUpper + : scheduleInfo.maxMintableLower, + maxMintableLower: scheduleInfo.maxMintableLower, + maxMintablePerAccount: scheduleInfo.maxMintablePerAccount, + maxMintableUpper: scheduleInfo.maxMintableUpper, + minterAddress, + mintId: BigInt(mintId), + mintPaused: scheduleInfo.mintPaused, + price: scheduleInfo.price, + startTime: scheduleInfo.startTime, + totalMinted: scheduleInfo.totalMinted, + platformPerTokenFee, + platformPerTransactionFee: 0n, + } satisfies RangeEditionV2Schedule + } - switch (interfaceId) { - case interfaceIds.IRangeEditionMinter: - case interfaceIds.IRangeEditionMinterV2: case interfaceIds.IRangeEditionMinterV2_1: { - const scheduleWithPlatformFee = - interfaceId === interfaceIds.IRangeEditionMinterV2_1 - ? await readContract({ - abi: minterAbiMap[interfaceId], - address: minterAddress, - args: [editionAddress, BigInt(mintId)], - functionName: 'mintInfo', - }) - : null - - const scheduleInfo = - scheduleWithPlatformFee != null - ? scheduleWithPlatformFee - : interfaceId === interfaceIds.IRangeEditionMinter - ? await readContract({ - abi: minterAbiMap[interfaceId], - address: minterAddress, - args: [editionAddress, BigInt(mintId)], - functionName: 'mintInfo', - }) - : interfaceId === interfaceIds.IRangeEditionMinterV2 - ? await readContract({ - abi: minterAbiMap[interfaceId], - address: minterAddress, - args: [editionAddress, BigInt(mintId)], - functionName: 'mintInfo', - }) - : null - - if (!scheduleInfo) { - throw new UnsupportedMinterError({ - interfaceId, - }) - } + const scheduleInfo = await readContract({ + abi: minterAbiMap[interfaceId], + address: minterAddress, + args: [editionAddress, BigInt(mintId)], + functionName: 'mintInfo', + }) return { mintType: 'RangeEdition', - affiliateFeeBPS: scheduleInfo.affiliateFeeBPS, cutoffTime: scheduleInfo.cutoffTime, editionAddress: editionAddress, @@ -361,47 +415,89 @@ export async function mintInfosFromMinter( price: scheduleInfo.price, startTime: scheduleInfo.startTime, totalMinted: scheduleInfo.totalMinted, + platformPerTokenFee: scheduleInfo.platformFlatFee, + platformPerTransactionFee: scheduleInfo.platformPerTxFlatFee, + } satisfies RangeEditionV2_1Schedule + } + case interfaceIds.IMerkleDropMinter: { + const scheduleInfo = await readContract({ + abi: minterAbiMap[interfaceId], + address: minterAddress, + functionName: 'mintInfo', + args: [editionAddress, BigInt(mintId)], + }) + + return { + mintType: 'MerkleDrop', + + affiliateFeeBPS: scheduleInfo.affiliateFeeBPS, + editionAddress: editionAddress, + endTime: scheduleInfo.endTime, + interfaceId, + + maxMintablePerAccount: scheduleInfo.maxMintablePerAccount, + minterAddress, + mintId: BigInt(mintId), + mintPaused: scheduleInfo.mintPaused, + price: scheduleInfo.price, + startTime: scheduleInfo.startTime, + totalMinted: scheduleInfo.totalMinted, + maxMintable: scheduleInfo.maxMintable, + + merkleRoot: scheduleInfo.merkleRootHash, + + platformPerTokenFee: 0n, + platformPerTransactionFee: 0n, + } satisfies MerkleDropV1Schedule + } + case interfaceIds.IMerkleDropMinterV2: { + const [scheduleInfo, platformPerTokenFee] = await multicall({ + contracts: [ + { + abi: minterAbiMap[interfaceId], + address: minterAddress, + functionName: 'mintInfo', + args: [editionAddress, BigInt(mintId)], + }, + { + abi: minterAbiMap[interfaceId], + address: minterAddress, + functionName: 'platformFlatFee', + }, + ], + allowFailure: false, + }) + + return { + mintType: 'MerkleDrop', + + affiliateFeeBPS: scheduleInfo.affiliateFeeBPS, + editionAddress: editionAddress, + endTime: scheduleInfo.endTime, + interfaceId, + + maxMintablePerAccount: scheduleInfo.maxMintablePerAccount, + minterAddress, + mintId: BigInt(mintId), + mintPaused: scheduleInfo.mintPaused, + price: scheduleInfo.price, + startTime: scheduleInfo.startTime, + totalMinted: scheduleInfo.totalMinted, + maxMintable: scheduleInfo.maxMintable, + + merkleRoot: scheduleInfo.merkleRootHash, - platformTransactionFee: scheduleWithPlatformFee?.platformPerTxFlatFee ?? 0n, - } + platformPerTokenFee, + platformPerTransactionFee: 0n, + } satisfies MerkleDropV2Schedule } - case interfaceIds.IMerkleDropMinter: - case interfaceIds.IMerkleDropMinterV2: case interfaceIds.IMerkleDropMinterV2_1: { - const scheduleWithPlatformFee = - interfaceId === interfaceIds.IMerkleDropMinterV2_1 - ? await readContract({ - abi: minterAbiMap[interfaceId], - address: minterAddress, - args: [editionAddress, BigInt(mintId)], - functionName: 'mintInfo', - }) - : null - - const scheduleInfo = - scheduleWithPlatformFee != null - ? scheduleWithPlatformFee - : interfaceId === interfaceIds.IMerkleDropMinter - ? await readContract({ - abi: minterAbiMap[interfaceId], - address: minterAddress, - args: [editionAddress, BigInt(mintId)], - functionName: 'mintInfo', - }) - : interfaceId === interfaceIds.IMerkleDropMinterV2 - ? await readContract({ - abi: minterAbiMap[interfaceId], - address: minterAddress, - args: [editionAddress, BigInt(mintId)], - functionName: 'mintInfo', - }) - : null - - if (!scheduleInfo) { - throw new UnsupportedMinterError({ - interfaceId, - }) - } + const scheduleInfo = await readContract({ + abi: minterAbiMap[interfaceId], + address: minterAddress, + args: [editionAddress, BigInt(mintId)], + functionName: 'mintInfo', + }) return { mintType: 'MerkleDrop', @@ -422,8 +518,9 @@ export async function mintInfosFromMinter( merkleRoot: scheduleInfo.merkleRootHash, - platformTransactionFee: scheduleWithPlatformFee?.platformPerTxFlatFee ?? 0n, - } + platformPerTokenFee: scheduleInfo.platformFlatFee, + platformPerTransactionFee: scheduleInfo.platformPerTxFlatFee, + } satisfies MerkleDropV2_1Schedule } default: { diff --git a/packages/sdk-viem/src/errors.ts b/packages/sdk-viem/src/errors.ts index 9c4d04a4..adc9528d 100644 --- a/packages/sdk-viem/src/errors.ts +++ b/packages/sdk-viem/src/errors.ts @@ -18,33 +18,6 @@ export class MissingProviderError extends Error { } } -export class MissingSignerOrProviderError extends Error { - readonly name = 'MissingSignerOrProviderError' - - constructor() { - super('Must provide signer or provider') - } -} - -export class UnsupportedNetworkError extends Error { - readonly name = 'UnsupportedNetworkError' - - readonly chainId: number - constructor({ chainId }: { chainId: number }) { - super('Unsupported network chain ID') - - this.chainId = chainId - } -} - -export class CreatorAddressMissing extends Error { - readonly name = 'CreatorAddressMissingError' - - constructor() { - super('"soundCreatorAddress" was not specified and it\'s required for the requested action') - } -} - export class UnsupportedMinterError extends Error { readonly name = 'UnsupportedMinterError' @@ -94,26 +67,6 @@ export class SoundNotFoundError extends Error { } } -export class SoundAPILoginError extends Error { - readonly name = 'SoundAPILoginError' - - readonly publicAddress: string - readonly graphqlErrors: GraphQLExecutionErrors | undefined - - constructor({ - publicAddress, - graphqlErrors, - }: { - publicAddress: string - graphqlErrors: GraphQLExecutionErrors | undefined - }) { - super('Error while trying to login into Sound.xyz API') - - this.publicAddress = publicAddress - this.graphqlErrors = graphqlErrors - } -} - export class SoundAPIGraphQLError extends Error { readonly name = 'SoundAPIGraphQLError' @@ -178,30 +131,6 @@ export class InvalidOffsetError extends Error { } } -export class InvalidTokenIdError extends Error { - readonly name = 'InvalidTokenIdError' - - readonly tokenId: string - - constructor({ tokenId }: { tokenId: string }) { - super('Must provide valid token id') - - this.tokenId = tokenId - } -} - -export class InvalidAttributonIdError extends Error { - readonly name = 'InvalidAttributonIdError' - - readonly attributonId: string - - constructor({ attributonId }: { attributonId: string }) { - super('Must provide valid BigNumber-like attributon id') - - this.attributonId = attributonId - } -} - export class NotEligibleMint extends Error { readonly name = 'NotEligibleMintError' diff --git a/packages/sdk-viem/src/types.ts b/packages/sdk-viem/src/types.ts index 945affb3..4e4113d0 100644 --- a/packages/sdk-viem/src/types.ts +++ b/packages/sdk-viem/src/types.ts @@ -18,7 +18,7 @@ type LazyOption = T | (() => T | Promise) export type ClientProvider = Pick< PublicClient, - 'chain' | 'readContract' | 'getFilterLogs' | 'createEventFilter' | 'estimateContractGas' + 'chain' | 'readContract' | 'getFilterLogs' | 'createEventFilter' | 'estimateContractGas' | 'multicall' > export type Wallet = Pick @@ -146,7 +146,8 @@ export interface MintScheduleBase { maxMintablePerAccount: number totalMinted: number affiliateFeeBPS: number - platformTransactionFee: bigint + platformPerTransactionFee: bigint + platformPerTokenFee: bigint } export interface RangeEditionSchedule extends MintScheduleBase {