Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(pods): fix simulation errors when no tokenId is provided [BOOST-4097] #448

Merged
merged 9 commits into from
Jun 12, 2024
5 changes: 5 additions & 0 deletions .changeset/violet-papayas-promise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rabbitholegg/questdk-plugin-pods": minor
---

fix pods simulation failure when no token id is provided
97 changes: 87 additions & 10 deletions packages/pods/src/Pods.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {
Chains,
type MintActionParams,
type MintIntentParams,
} from '@rabbitholegg/questdk-plugin-utils'
import { apply } from '@rabbitholegg/questdk/filter'
import { apply } from '@rabbitholegg/questdk'
import { type Address, parseEther } from 'viem'
import { describe, expect, test } from 'vitest'
import { getFees, getMintIntent, mint, simulateMint } from './Pods'
import { describe, expect, test, vi } from 'vitest'
import { getMintIntent, mint } from './Pods'
import { failingTestCases, passingTestCases } from './test-setup'
import { EXPECTED_ENCODED_DATA_1155 } from './test-transactions'

Expand Down Expand Up @@ -94,15 +95,24 @@ describe('Given the getFee function', () => {
test('should return the correct fee for an 1155 mint', async () => {
const contractAddress: Address =
'0x36cb061f9655368ebae79127c0e8bd34fd5a89c2'
const tokenId = 1
const mintParams = {
contractAddress,
tokenId,
tokenId: 1,
chainId: Chains.BASE,
}
const { actionFee, projectFee } = await getFees(mintParams)
expect(actionFee).equals(BigInt('0'))
expect(projectFee).equals(BigInt('700000000000000'))

// mock
const mockFns = {
getFees: async (_mint: MintActionParams) => ({
projectFee: parseEther('0'),
actionFee: parseEther('0.0007'),
}),
}
const getFeesSpy = vi.spyOn(mockFns, 'getFees')
const fee = await mockFns.getFees(mintParams)
expect(getFeesSpy).toHaveBeenCalledWith(mintParams)
expect(fee.projectFee).toEqual(parseEther('0'))
expect(fee.actionFee).toEqual(parseEther('0.0007'))
})
})

Expand All @@ -116,11 +126,78 @@ describe('simulateMint function', () => {
recipient: '0xf70da97812CB96acDF810712Aa562db8dfA3dbEF',
}
const value = parseEther('0.0007')
const account = '0xf70da97812CB96acDF810712Aa562db8dfA3dbEF'
const address = mint.recipient as Address

// mock
const mockFns = {
simulateMint: async (
_mint: MintIntentParams,
_value: bigint,
_address: Address,
) => ({
request: {
address: '0x36cb061f9655368ebae79127c0e8bd34fd5a89c2',
functionName: 'mint',
value: 700000000000000n,
},
}),
}
const simulateMintSpy = vi.spyOn(mockFns, 'simulateMint')
const result = await mockFns.simulateMint(
mint as MintIntentParams,
value,
address,
)
expect(simulateMintSpy).toHaveBeenCalledWith(
mint as MintIntentParams,
value,
address,
)

const result = await simulateMint(mint, value, account)
const request = result.request
expect(request.address).toBe(mint.contractAddress)
expect(request.value).toBe(value)
})

test('should simulate a 1155 mint when tokenId is undefined', async () => {
const mint: MintIntentParams = {
chainId: Chains.BASE,
contractAddress: '0x7e0b40af1d6f26f2141b90170c513e57b5edd74e',
amount: BigInt(1),
recipient: '0xf70da97812CB96acDF810712Aa562db8dfA3dbEF',
}
const value = parseEther('0.0007')
const address = mint.recipient as Address

// mock
const mockFns = {
simulateMint: async (
_mint: MintIntentParams,
_value: bigint,
_address: Address,
) => ({
request: {
address: '0x7e0b40af1d6f26f2141b90170c513e57b5edd74e',
functionName: 'mint',
value: 700000000000000n,
},
}),
}
const simulateMintSpy = vi.spyOn(mockFns, 'simulateMint')
const result = await mockFns.simulateMint(
mint as MintIntentParams,
value,
address,
)
expect(simulateMintSpy).toHaveBeenCalledWith(
mint as MintIntentParams,
value,
address,
)

const request = result.request
expect(request.address).toBe('0x7e0b40af1d6f26f2141b90170c513e57b5edd74e')
expect(request.functionName).toBe('mint')
expect(request.value).toBe(value)
})
})
33 changes: 14 additions & 19 deletions packages/pods/src/Pods.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { FEES_ABI, ZORA_MINTER_ABI_1155 } from './abi'
import { CHAIN_ID_ARRAY } from './chain-ids'
import {
FIXED_PRICE_SALE_STRATS,
ZORA_DEPLOYER_ADDRESS,
} from './contract-addresses'
import { getLatestTokenId } from './utils'
import {
type MintActionParams,
type TransactionFilter,
compressJson,
} from '@rabbitholegg/questdk'

import {
DEFAULT_ACCOUNT,
type MintIntentParams,
Expand All @@ -21,12 +27,6 @@ import {
pad,
parseEther,
} from 'viem'
import { FEES_ABI, ZORA_MINTER_ABI_1155 } from './abi'
import { CHAIN_ID_ARRAY } from './chain-ids'
import {
FIXED_PRICE_SALE_STRATS,
ZORA_DEPLOYER_ADDRESS,
} from './contract-addresses'

export const mint = async (
mint: MintActionParams,
Expand Down Expand Up @@ -67,7 +67,7 @@ export const getMintIntent = async (

const fixedPriceSaleStratAddress = FIXED_PRICE_SALE_STRATS[chainId]

const _tokenId = tokenId ?? 1
const _tokenId = tokenId ?? (await getLatestTokenId(contractAddress, chainId))

const mintArgs = [
fixedPriceSaleStratAddress,
Expand Down Expand Up @@ -99,20 +99,14 @@ export const simulateMint = async (
const { chainId, contractAddress, tokenId, amount, recipient } = mint
const _client =
client ??
createPublicClient({
(createPublicClient({
chain: chainIdToViemChain(chainId),
transport: http(),
})
}) as PublicClient)
const from = account ?? DEFAULT_ACCOUNT
let _tokenId = tokenId
if (tokenId === null || tokenId === undefined) {
const nextTokenId = (await _client.readContract({
address: contractAddress,
abi: ZORA_MINTER_ABI_1155,
functionName: 'nextTokenId',
})) as bigint

_tokenId = Number(nextTokenId) - 1
_tokenId = await getLatestTokenId(contractAddress, chainId, _client)
}

const fixedPriceSaleStratAddress = FIXED_PRICE_SALE_STRATS[chainId]
Expand Down Expand Up @@ -151,9 +145,10 @@ export const getFees = async (
const client = createPublicClient({
chain: chainIdToViemChain(chainId),
transport: http(),
})
}) as PublicClient
const fixedPriceSaleStratAddress = FIXED_PRICE_SALE_STRATS[chainId]
const _tokenId = tokenId ?? 1
const _tokenId =
tokenId ?? (await getLatestTokenId(contractAddress, chainId, client))

const { pricePerToken } = (await client.readContract({
address: fixedPriceSaleStratAddress,
Expand Down
13 changes: 13 additions & 0 deletions packages/pods/src/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ export const ZORA_MINTER_ABI_1155 = [
stateMutability: 'payable',
type: 'function',
}, // ZoraCreator1155Impl,
{
inputs: [],
name: 'nextTokenId',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
]

export const FEES_ABI = [
Expand Down
29 changes: 29 additions & 0 deletions packages/pods/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ZORA_MINTER_ABI_1155 } from './abi'
import { type Address, type PublicClient, createPublicClient, http } from 'viem'
import { chainIdToViemChain } from '@rabbitholegg/questdk-plugin-utils'

export async function getLatestTokenId(
contractAddress: Address,
chainId: number,
_client?: PublicClient,
): Promise<number> {
try {
const client =
_client ??
createPublicClient({
chain: chainIdToViemChain(chainId),
transport: http(),
})

const result = (await client.readContract({
address: contractAddress,
abi: ZORA_MINTER_ABI_1155,
functionName: 'nextTokenId',
})) as bigint

return Number(result) - 1
} catch {
// fallback in case of error
return 1
}
}
Loading