diff --git a/.changeset/chilled-seahorses-protect.md b/.changeset/chilled-seahorses-protect.md new file mode 100644 index 000000000..b2640f0a0 --- /dev/null +++ b/.changeset/chilled-seahorses-protect.md @@ -0,0 +1,17 @@ +--- +"@zoralabs/protocol-sdk": patch +--- + +`MintAPIClient` is now a class, that takes a chain id and httpClient in the constructor, enabling the httpClient methods `fetch`, `post`, and `retries` to be overridden. + +new methods on `MintAPIClient`: + +`getMintableForToken` - takes a token id and token contract address and returns the mintable for it. Easier to use for fetching specific tokens than `getMintable`. + +`MintClient` now takes the optional `PublicClient` in the constructor instead of in each function, and stores it or creates a default one if none is provided in the constructor. It also takes an optional `httpClient` param in the constructor, allowing the `fetch`, `post`, and `retries` methods to be overridden when using the api. It now internally creates the MintAPIClient. + +`MintClient.makePrepareMintTokenParams` has the following changes: + * returns a `SimulateContractParams`, instead of an object containing it indexed by key + * no longer takes a `PublicClient` as an argument (it should be specified in the constructor instead) + +new function `MintClient.getMintCosts` takes a mintable and quantity to mint and returns the mintFee, paidMintPrice, and totalCost. \ No newline at end of file diff --git a/packages/protocol-sdk/README.md b/packages/protocol-sdk/README.md index 4e5fad4ea..bab6150ad 100644 --- a/packages/protocol-sdk/README.md +++ b/packages/protocol-sdk/README.md @@ -1,6 +1,6 @@ -# Premint SDK +# Zora Protocol SDK -Protocol SDK allows users to manage zora mints and collects. +Protocol SDK allows users to create tokens using the Zora Protocol, and mint them. ## Installing @@ -11,28 +11,175 @@ Protocol SDK allows users to manage zora mints and collects. ### Creating a mint from an on-chain contract: +#### Using viem + ```ts -import { createMintClient } from "@zoralabs/protocol-sdk"; -import type { Address, WalletClient } from "viem"; +import {createMintClient} from "@zoralabs/protocol-sdk"; +import type {Address, PublicClient, WalletClient} from "viem"; -async function mintNFT( - walletClient: WalletClient, - address: Address, - tokenId: bigint, -) { - const mintAPI = createMintClient({ chain: walletClient.chain }); - await mintAPI.mintNFT({ - walletClient, - address, +async function mintNFT({ + walletClient, + publicClient, + tokenContract, + tokenId, + mintToAddress, + quantityToMint, + mintReferral, +}: { + // wallet client that will submit the transaction + walletClient: WalletClient; + // public client that will simulate the transaction + publicClient: PublicClient; + // address of the token contract + tokenContract: Address; + // id of the token to mint + tokenId: bigint; + // address that will receive the minted token + mintToAddress: Address; + // quantity of tokens to mint + quantityToMint: number; + // optional address that will receive a mint referral reward + mintReferral?: Address; +}) { + const mintClient = createMintClient({chain: walletClient.chain!}); + + // get mintable information about the token. + const mintable = await mintClient.getMintable({ + tokenContract, tokenId, + }); + + // prepare the mint transaction, which can be simulated via an rpc with the public client. + const prepared = await mintClient.makePrepareMintTokenParams({ + // token to mint + mintable, mintArguments: { - quantityToMint: 23, - mintComment: "Helo", + // address that will receive the token + mintToAddress, + // quantity of tokens to mint + quantityToMint, + // comment to include with the mint + mintComment: "My comment", + // optional address that will receive a mint referral reward + mintReferral, }, + // account that is to invoke the mint transaction + minterAccount: walletClient.account!.address, }); + + // simulate the transaction and get any validation errors + const { request } = await publicClient.simulateContract(prepared); + + // submit the transaction to the network + const txHash = await walletClient.writeContract(request); + + // wait for the transaction to be complete + await publicClient.waitForTransactionReceipt({hash: txHash}); } ``` +#### Using wagmi + +```tsx +import {createMintClient, Mintable} from "@zoralabs/protocol-sdk"; +import {useEffect, useMemo, useState} from "react"; +import {BaseError, SimulateContractParameters, stringify} from "viem"; +import {Address, useAccount, useContractWrite, useNetwork, usePrepareContractWrite, usePublicClient, useWaitForTransaction} from "wagmi"; + +// custom hook that gets the mintClient for the current chain +const useMintClient = () => { + const publicClient = usePublicClient(); + + const {chain} = useNetwork(); + + const mintClient = useMemo(() => chain && createMintClient({chain, publicClient}), [chain, publicClient]); + + return mintClient; +}; + +export const Mint = ({tokenId, tokenContract}: {tokenId: string; tokenContract: Address}) => { + // call custom hook to get the mintClient + const mintClient = useMintClient(); + + // value will be set by the form + const [quantityToMint, setQuantityToMint] = useState(1); + + // fetched mintable info from the sdk + const [mintable, setMintable] = useState(); + + useEffect(() => { + // fetch the mintable token info + const fetchMintable = async () => { + if (mintClient) { + const mintable = await mintClient.getMintable({tokenId, tokenContract}); + setMintable(mintable); + } + }; + + fetchMintable(); + }, [mintClient, tokenId, tokenContract]); + + // params for the prepare contract write hook + const [params, setParams] = useState(); + + const {address} = useAccount(); + + useEffect(() => { + if (!mintable || !mintClient || !address) return; + + const makeParams = async () => { + // make the params for the prepare contract write hook + const params = await mintClient.makePrepareMintTokenParams({ + mintable, + minterAccount: address, + mintArguments: { + mintToAddress: address, + quantityToMint, + }, + }); + setParams(params); + }; + + makeParams(); + }, [mintable, mintClient, address, quantityToMint]); + + const {config} = usePrepareContractWrite(params); + + const {write, data, error, isLoading, isError} = useContractWrite(config); + const {data: receipt, isLoading: isPending, isSuccess} = useWaitForTransaction({hash: data?.hash}); + + return ( + <> +

Mint a token

+
{ + e.preventDefault(); + write?.(); + }} + > + {/* input for quantity to mint: */} + setQuantityToMint(Number(e.target.value))} /> + +
+ + {isLoading &&
Check wallet...
} + {isPending &&
Transaction pending...
} + {isSuccess && ( + <> +
Transaction Hash: {data?.hash}
+
+ Transaction Receipt:
{stringify(receipt, null, 2)}
+
+ + )} + {isError &&
{(error as BaseError)?.shortMessage}
} + + ); +}; +``` + ### Creating an 1155 contract: If an object with {name, uri} is passed in to this helper, it uses the creatorAccount and those values to either 1) create or 2) mint to that existing contract. diff --git a/packages/protocol-sdk/src/apis/http-api-base.ts b/packages/protocol-sdk/src/apis/http-api-base.ts index bb2df588d..1f0e9c4b6 100644 --- a/packages/protocol-sdk/src/apis/http-api-base.ts +++ b/packages/protocol-sdk/src/apis/http-api-base.ts @@ -91,3 +91,15 @@ export const retries = async ( throw err; } }; + +export interface IHttpClient { + get: typeof get; + post: typeof post; + retries: typeof retries; +} + +export const httpClient: IHttpClient = { + get, + post, + retries, +}; diff --git a/packages/protocol-sdk/src/mint/mint-api-client.ts b/packages/protocol-sdk/src/mint/mint-api-client.ts index 3918ab3e2..14ed5cdaa 100644 --- a/packages/protocol-sdk/src/mint/mint-api-client.ts +++ b/packages/protocol-sdk/src/mint/mint-api-client.ts @@ -1,6 +1,11 @@ -import { retries, get, post } from "../apis/http-api-base"; +import { + httpClient as defaultHttpClient, + IHttpClient, +} from "../apis/http-api-base"; import { paths } from "../apis/generated/discover-api-types"; import { ZORA_API_BASE } from "../constants"; +import { NetworkConfig, networkConfigByChain } from "src/apis/chain-constants"; +import { Address } from "viem"; export type MintableGetToken = paths["/mintables/{chain_name}/{collection_address}"]; @@ -15,38 +20,70 @@ function encodeQueryParameters(params: Record) { return new URLSearchParams(params).toString(); } -const getMintable = async ( - path: MintableGetTokenPathParameters, - query: MintableGetTokenGetQueryParameters, -): Promise => - retries(() => { - return get( - `${ZORA_API_BASE}discover/mintables/${path.chain_name}/${ - path.collection_address - }${query?.token_id ? `?${encodeQueryParameters(query)}` : ""}`, - ); - }); - -export const getSalesConfigFixedPrice = async ({ - contractAddress, - tokenId, - subgraphUrl, -}: { - contractAddress: string; - tokenId: string; - subgraphUrl: string; -}): Promise => - retries(async () => { - const response = await post(subgraphUrl, { - query: - "query($id: ID!) {\n zoraCreateToken(id: $id) {\n id\n salesStrategies{\n fixedPrice {\n address\n }\n }\n }\n}", - variables: { id: `${contractAddress.toLowerCase()}-${tokenId}` }, +export const getApiNetworkConfigForChain = (chainId: number): NetworkConfig => { + if (!networkConfigByChain[chainId]) { + throw new Error(`chain id ${chainId} network not configured `); + } + return networkConfigByChain[chainId]!; +}; + +export class MintAPIClient { + httpClient: IHttpClient; + networkConfig: NetworkConfig; + + constructor(chainId: number, httpClient?: IHttpClient) { + this.httpClient = httpClient || defaultHttpClient; + this.networkConfig = getApiNetworkConfigForChain(chainId); + } + + async getMintable( + path: MintableGetTokenPathParameters, + query: MintableGetTokenGetQueryParameters, + ): Promise { + const httpClient = this.httpClient; + return httpClient.retries(() => { + return httpClient.get( + `${ZORA_API_BASE}discover/mintables/${path.chain_name}/${ + path.collection_address + }${query?.token_id ? `?${encodeQueryParameters(query)}` : ""}`, + ); }); - return response.zoraCreateToken?.salesStrategies?.find(() => true) - ?.fixedPriceMinterAddress; - }); + } -export const MintAPIClient = { - getMintable, - getSalesConfigFixedPrice, -}; + async getSalesConfigFixedPrice({ + contractAddress, + tokenId, + }: { + contractAddress: string; + tokenId: bigint; + }): Promise { + const { retries, post } = this.httpClient; + return retries(async () => { + const response = await post(this.networkConfig.subgraphUrl, { + query: + "query($id: ID!) {\n zoraCreateToken(id: $id) {\n id\n salesStrategies{\n fixedPrice {\n address\n }\n }\n }\n}", + variables: { + id: `${contractAddress.toLowerCase()}-${tokenId.toString()}`, + }, + }); + return response.zoraCreateToken?.salesStrategies?.find(() => true) + ?.fixedPriceMinterAddress; + }); + } + + async getMintableForToken({ + tokenContract, + tokenId, + }: { + tokenContract: Address; + tokenId?: bigint | number | string; + }) { + return await this.getMintable( + { + chain_name: this.networkConfig.zoraBackendChainName, + collection_address: tokenContract, + }, + { token_id: tokenId?.toString() }, + ); + } +} diff --git a/packages/protocol-sdk/src/mint/mint-client.test.ts b/packages/protocol-sdk/src/mint/mint-client.test.ts index 696e5ac0a..c0996b417 100644 --- a/packages/protocol-sdk/src/mint/mint-client.test.ts +++ b/packages/protocol-sdk/src/mint/mint-client.test.ts @@ -23,19 +23,17 @@ describe("mint-helper", () => { const targetTokenId = 1n; const minter = createMintClient({ chain: zora }); - const { simulateContractParameters: params } = - await minter.makePrepareMintTokenParams({ - publicClient, - minterAccount: creatorAccount, - mintable: await minter.getMintable({ - tokenId: targetTokenId, - tokenContract: targetContract, - }), - mintArguments: { - mintToAddress: creatorAccount, - quantityToMint: 1, - }, - }); + const params = await minter.makePrepareMintTokenParams({ + minterAccount: creatorAccount, + mintable: await minter.getMintable({ + tokenId: targetTokenId, + tokenContract: targetContract, + }), + mintArguments: { + mintToAddress: creatorAccount, + quantityToMint: 1, + }, + }); const oldBalance = await publicClient.readContract({ abi: zoraCreator1155ImplABI, @@ -75,19 +73,17 @@ describe("mint-helper", () => { const targetTokenId = undefined; const minter = createMintClient({ chain: zora }); - const { simulateContractParameters: prepared } = - await minter.makePrepareMintTokenParams({ - mintable: await minter.getMintable({ - tokenContract: targetContract, - tokenId: targetTokenId, - }), - publicClient, - minterAccount: creatorAccount, - mintArguments: { - mintToAddress: creatorAccount, - quantityToMint: 1, - }, - }); + const params = await minter.makePrepareMintTokenParams({ + mintable: await minter.getMintable({ + tokenContract: targetContract, + tokenId: targetTokenId, + }), + minterAccount: creatorAccount, + mintArguments: { + mintToAddress: creatorAccount, + quantityToMint: 1, + }, + }); const oldBalance = await publicClient.readContract({ abi: erc721ABI, address: targetContract, @@ -95,7 +91,7 @@ describe("mint-helper", () => { args: [creatorAccount], }); - const simulated = await publicClient.simulateContract(prepared); + const simulated = await publicClient.simulateContract(params); const hash = await walletClient.writeContract(simulated.request); diff --git a/packages/protocol-sdk/src/mint/mint-client.ts b/packages/protocol-sdk/src/mint/mint-client.ts index c56b27fb4..0930d05c2 100644 --- a/packages/protocol-sdk/src/mint/mint-client.ts +++ b/packages/protocol-sdk/src/mint/mint-client.ts @@ -2,12 +2,14 @@ import { Address, Chain, PublicClient, + createPublicClient, encodeAbiParameters, parseAbi, parseAbiParameters, zeroAddress, + http, } from "viem"; -import { ClientBase } from "../apis/client-base"; +import { IHttpClient } from "../apis/http-api-base"; import { MintAPIClient, MintableGetTokenResponse } from "./mint-api-client"; import { SimulateContractParameters } from "viem"; import { @@ -30,30 +32,31 @@ type MintArguments = { mintToAddress: Address; }; -type MintParameters = { - mintArguments: MintArguments; - publicClient: PublicClient; - mintable: MintableGetTokenResponse; - sender: Address; -}; - const zora721Abi = parseAbi([ "function mintWithRewards(address recipient, uint256 quantity, string calldata comment, address mintReferral) external payable", "function zoraFeeForAmount(uint256 amount) public view returns (address, uint256)", ] as const); -class MintClient extends ClientBase { - apiClient: typeof MintAPIClient; - - constructor(chain: Chain, apiClient?: typeof MintAPIClient) { - super(chain); +class MintClient { + readonly apiClient: MintAPIClient; + readonly publicClient: PublicClient; - if (!apiClient) { - apiClient = MintAPIClient; - } - this.apiClient = apiClient; + constructor( + chain: Chain, + publicClient?: PublicClient, + httpClient?: IHttpClient, + ) { + this.apiClient = new MintAPIClient(chain.id, httpClient); + this.publicClient = + publicClient || createPublicClient({ chain, transport: http() }); } + /** + * Gets mintable information for a given token + * @param param0.tokenContract The contract address of the token to mint. + * @param param0.tokenId The token id to mint. + * @returns + */ async getMintable({ tokenContract, tokenId, @@ -61,168 +64,333 @@ class MintClient extends ClientBase { tokenContract: Address; tokenId?: bigint | number | string; }) { - return this.apiClient.getMintable( - { - chain_name: this.network.zoraBackendChainName, - collection_address: tokenContract, - }, - { token_id: tokenId?.toString() }, - ); + return await this.apiClient.getMintableForToken({ + tokenContract, + tokenId: tokenId?.toString(), + }); } + /** + * Returns the parameters needed to prepare a transaction mint a token. + * @param param0.minterAccount The account that will mint the token. + * @param param0.mintable The mintable token to mint. + * @param param0.mintArguments The arguments for the mint (mint recipient, comment, mint referral, quantity to mint) + * @returns + */ async makePrepareMintTokenParams({ - publicClient, - minterAccount, - mintable, - mintArguments, + ...rest }: { - publicClient: PublicClient; - mintable: MintableGetTokenResponse; minterAccount: Address; + mintable: MintableGetTokenResponse; mintArguments: MintArguments; - }): Promise<{ simulateContractParameters: SimulateContractParameters }> { - if (!mintable) { - throw new MintError("No mintable found"); - } + }): Promise { + return makePrepareMintTokenParams({ + ...rest, + apiClient: this.apiClient, + publicClient: this.publicClient, + }); + } - if (!mintable.is_active) { - throw new MintInactiveError("Minting token is inactive"); - } + /** + * Returns the mintFee, sale fee, and total cost of minting x quantities of a token. + * @param param0.mintable The mintable token to mint. + * @param param0.quantityToMint The quantity of tokens to mint. + * @returns + */ + async getMintCosts({ + mintable, + quantityToMint, + }: { + mintable: Mintable; + quantityToMint: number | bigint; + }): Promise { + const mintContextType = validateMintableAndGetContextType(mintable); - if (!mintable.mint_context) { - throw new MintError("No minting context data from zora API"); + if (mintContextType === "zora_create_1155") { + return await get1155MintCosts({ + mintable, + publicClient: this.publicClient, + quantityToMint: BigInt(quantityToMint), + }); } - - if ( - !["zora_create", "zora_create_1155"].includes( - mintable.mint_context?.mint_context_type!, - ) - ) { - throw new MintError( - `Mintable type ${mintable.mint_context.mint_context_type} is currently unsupported.`, - ); + if (mintContextType === "zora_create") { + return await get721MintCosts({ + mintable, + publicClient: this.publicClient, + quantityToMint: BigInt(quantityToMint), + }); } - const thisPublicClient = this.getPublicClient(publicClient); - - if (mintable.mint_context.mint_context_type === "zora_create_1155") { - return { - simulateContractParameters: await this.prepareMintZora1155({ - publicClient: thisPublicClient, - mintArguments, - sender: minterAccount, - mintable, - }), - }; - } - if (mintable.mint_context.mint_context_type === "zora_create") { - return { - simulateContractParameters: await this.prepareMintZora721({ - publicClient: thisPublicClient, - mintArguments, - sender: minterAccount, - mintable, - }), - }; - } + throw new MintError( + `Mintable type ${mintContextType} is currently unsupported.`, + ); + } +} - throw new Error("Mintable type not found or recognized."); +/** + * Creates a new MintClient. + * @param param0.chain The chain to use for the mint client. + * @param param0.publicClient Optional viem public client + * @param param0.httpClient Optional http client to override post, get, and retry methods + * @returns + */ +export function createMintClient({ + chain, + publicClient, + httpClient, +}: { + chain: Chain; + publicClient?: PublicClient; + httpClient?: IHttpClient; +}) { + return new MintClient(chain, publicClient, httpClient); +} + +export type TMintClient = ReturnType; + +function validateMintableAndGetContextType( + mintable: MintableGetTokenResponse | undefined, +) { + if (!mintable) { + throw new MintError("No mintable found"); } - private async prepareMintZora1155({ - mintable, - sender, - publicClient, - mintArguments, - }: MintParameters) { - const mintQuantity = BigInt(mintArguments.quantityToMint); + if (!mintable.is_active) { + throw new MintInactiveError("Minting token is inactive"); + } - const address = mintable.collection.address as Address; + if (!mintable.mint_context) { + throw new MintError("No minting context data from zora API"); + } - const mintFee = await publicClient.readContract({ - abi: zoraCreator1155ImplABI, - functionName: "mintFee", - address, + if ( + !["zora_create", "zora_create_1155"].includes( + mintable.mint_context?.mint_context_type!, + ) + ) { + throw new MintError( + `Mintable type ${mintable.mint_context.mint_context_type} is currently unsupported.`, + ); + } + + return mintable.mint_context.mint_context_type; +} + +async function makePrepareMintTokenParams({ + publicClient, + mintable, + apiClient, + ...rest +}: { + publicClient: PublicClient; + minterAccount: Address; + mintable: MintableGetTokenResponse; + mintArguments: MintArguments; + apiClient: MintAPIClient; +}): Promise { + const mintContextType = validateMintableAndGetContextType(mintable); + + const thisPublicClient = publicClient; + + if (mintContextType === "zora_create_1155") { + return makePrepareMint1155TokenParams({ + apiClient, + publicClient: thisPublicClient, + mintable, + mintContextType, + ...rest, + }); + } + if (mintContextType === "zora_create") { + return makePrepareMint721TokenParams({ + publicClient: thisPublicClient, + mintable, + mintContextType, + ...rest, }); + } - const tokenFixedPriceMinter = await this.apiClient.getSalesConfigFixedPrice( - { - contractAddress: mintable.contract_address, - tokenId: mintable.token_id!, - subgraphUrl: this.network.subgraphUrl, - }, - ); + throw new MintError( + `Mintable type ${mintContextType} is currently unsupported.`, + ); +} + +async function get721MintCosts({ + mintable, + publicClient, + quantityToMint, +}: { + mintable: MintableGetTokenResponse; + publicClient: PublicClient; + quantityToMint: bigint; +}): Promise { + const address = mintable.collection.address as Address; - const result: SimulateContractParameters< - typeof zoraCreator1155ImplABI, - "mintWithRewards" - > = { - abi: zoraCreator1155ImplABI, - functionName: "mintWithRewards", - account: sender, - value: (mintFee + BigInt(mintable.cost.native_price.raw)) * mintQuantity, - address, - /* args: minter, tokenId, quantity, minterArguments, mintReferral */ - args: [ - (tokenFixedPriceMinter || - zoraCreatorFixedPriceSaleStrategyAddress[999]) as Address, - BigInt(mintable.token_id!), - mintQuantity, - encodeAbiParameters(parseAbiParameters("address, string"), [ - mintArguments.mintToAddress, - mintArguments.mintComment || "", - ]), - mintArguments.mintReferral || zeroAddress, - ], - }; - - return result; + const [_, mintFee] = await publicClient.readContract({ + abi: zora721Abi, + address, + functionName: "zoraFeeForAmount", + args: [BigInt(quantityToMint)], + }); + + const tokenPurchaseCost = + BigInt(mintable.cost.native_price.raw) * quantityToMint; + return { + mintFee, + tokenPurchaseCost, + totalCost: mintFee + tokenPurchaseCost, + }; +} + +async function makePrepareMint721TokenParams({ + publicClient, + minterAccount, + mintable, + mintContextType, + mintArguments, +}: { + publicClient: PublicClient; + mintable: MintableGetTokenResponse; + mintContextType: ReturnType; + minterAccount: Address; + mintArguments: MintArguments; +}): Promise> { + if (mintContextType !== "zora_create") { + throw new Error("Minted token type must be for 1155"); } - private async prepareMintZora721({ - mintable, + const mintValue = ( + await get721MintCosts({ + mintable, + publicClient, + quantityToMint: BigInt(mintArguments.quantityToMint), + }) + ).totalCost; + + const result = { + abi: zora721Abi, + address: mintable.contract_address as Address, + account: minterAccount, + functionName: "mintWithRewards", + value: mintValue, + args: [ + mintArguments.mintToAddress, + BigInt(mintArguments.quantityToMint), + mintArguments.mintComment || "", + mintArguments.mintReferral || zeroAddress, + ], + } satisfies SimulateContractParameters; + + return result; +} + +async function get1155MintFee({ + collectionAddress, + publicClient, +}: { + collectionAddress: Address; + publicClient: PublicClient; +}) { + return await publicClient.readContract({ + abi: zoraCreator1155ImplABI, + functionName: "mintFee", + address: collectionAddress, + }); +} + +export type MintCosts = { + mintFee: bigint; + tokenPurchaseCost: bigint; + totalCost: bigint; +}; + +export async function get1155MintCosts({ + mintable, + publicClient, + quantityToMint, +}: { + mintable: MintableGetTokenResponse; + publicClient: PublicClient; + quantityToMint: bigint; +}): Promise { + const address = mintable.collection.address as Address; + + const mintFee = await get1155MintFee({ + collectionAddress: address, publicClient, - sender, - mintArguments, - }: MintParameters) { - const [_, mintFee] = await publicClient.readContract({ - abi: zora721Abi, - address: mintable.contract_address as Address, - functionName: "zoraFeeForAmount", - args: [BigInt(mintArguments.quantityToMint)], - }); + }); - const result: SimulateContractParameters< - typeof zora721Abi, - "mintWithRewards" - > = { - abi: zora721Abi, - address: mintable.contract_address as Address, - account: sender, - functionName: "mintWithRewards", - value: - mintFee + - BigInt(mintable.cost.native_price.raw) * - BigInt(mintArguments.quantityToMint), - /* args: mint recipient, quantity to mint, mint comment, mintReferral */ - args: [ - mintArguments.mintToAddress, - BigInt(mintArguments.quantityToMint), - mintArguments.mintComment || "", - mintArguments.mintReferral || zeroAddress, - ], - }; + const mintFeeForTokens = mintFee * quantityToMint; + const tokenPurchaseCost = + BigInt(mintable.cost.native_price.raw) * quantityToMint; - return result; - } + return { + mintFee: mintFeeForTokens, + tokenPurchaseCost, + totalCost: mintFeeForTokens + tokenPurchaseCost, + }; } -export function createMintClient({ - chain, - mintAPIClient, +async function makePrepareMint1155TokenParams({ + apiClient, + publicClient, + minterAccount, + mintable, + mintContextType, + mintArguments, }: { - chain: Chain; - mintAPIClient?: typeof MintAPIClient; + apiClient: Pick; + publicClient: PublicClient; + mintable: Mintable; + mintContextType: ReturnType; + minterAccount: Address; + mintArguments: MintArguments; }) { - return new MintClient(chain, mintAPIClient); + if (mintContextType !== "zora_create_1155") { + throw new Error("Minted token type must be for 1155"); + } + + const mintQuantity = BigInt(mintArguments.quantityToMint); + + const address = mintable.collection.address as Address; + + const mintValue = ( + await get1155MintCosts({ + mintable, + publicClient, + quantityToMint: mintQuantity, + }) + ).totalCost; + + const tokenFixedPriceMinter = await apiClient.getSalesConfigFixedPrice({ + contractAddress: address, + tokenId: BigInt(mintable.token_id!), + }); + + const result = { + abi: zoraCreator1155ImplABI, + functionName: "mintWithRewards", + account: minterAccount, + value: mintValue, + address, + /* args: minter, tokenId, quantity, minterArguments, mintReferral */ + args: [ + (tokenFixedPriceMinter || + zoraCreatorFixedPriceSaleStrategyAddress[999]) as Address, + BigInt(mintable.token_id!), + mintQuantity, + encodeAbiParameters(parseAbiParameters("address, string"), [ + mintArguments.mintToAddress, + mintArguments.mintComment || "", + ]), + mintArguments.mintReferral || zeroAddress, + ], + } satisfies SimulateContractParameters< + typeof zoraCreator1155ImplABI, + "mintWithRewards" + >; + + return result; } + +export type Mintable = MintableGetTokenResponse;