Skip to content

Add Mint Club Base mint ingestor - #74

Open
EmergentKnowledgeGroup wants to merge 2 commits into
floornfts:mainfrom
EmergentKnowledgeGroup:add-mintclub-ingestor
Open

Add Mint Club Base mint ingestor#74
EmergentKnowledgeGroup wants to merge 2 commits into
floornfts:mainfrom
EmergentKnowledgeGroup:add-mintclub-ingestor

Conversation

@EmergentKnowledgeGroup

Copy link
Copy Markdown

Refs #11.

Adds a Mint Club ingestor for Base ERC1155 bonding-curve NFTs that can be minted through Mint Club's WETH zap contract.

What changed:

  • Resolves Mint Club /nft/base/:symbol URLs by reproducing Mint Club's deterministic ERC1155 Create2 address formula.
  • Supports contract ingestion for Base Mint Club ERC1155s.
  • Filters to WETH-backed ERC1155s with remaining supply, because those can be minted with a single payable mintWithEth(token, 1, receiver) transaction.
  • Builds MintTemplate output with Mint Club metadata, Base output contract token id 0, and current getReserveForToken(token, 1) ETH value including royalty.
  • Adds static eligibility evidence in docs/mintclub-base-eligibility.md.
  • Adds live tests for URL/contract support, template creation, and 10 qualifying prior Base mints.

Eligibility evidence:

Symbol Token Supply Holders
PUNKS 0x9974A5CD8C484D7df85a0C56B807E98755cD732B 100,000 381
APD 0x3FBd3D7d9e465811db58f745eA7fA42901Aa31db 70,000 408
CULT 0x0bBAa6f85ad8199302f16507ACc911aCd49E7863 10,000 420
BLOB 0x832C76B6Ec18e37A2b5B4718a843D4633efFAaB0 10,000 171
OBSIDIAN 0x3519cDa3A69Aba975065a888CD206040F5288A0b 10,000 917
EKT 0xf3ce291d8AdE6c2bf3a4431F10D1616f2BD307fa 9,995 438
TRUMPEP 0x102426Ce29AeF9C2952aa16507A6AcAf51216C69 8,888 339
EARTH 0x7f1d47133680c89138e7c04b6411b5f4Bca7eE96 8,888 301
EARLY 0x9B98A355840f01D4a6a0E97c3dF430e37A2695Dc 5,556 482
PEAKYPEPE 0x69832024e4cfcfda7BA0dc8f646e4548E24E95A7 5,555 272

Verification:

  • FLOOR_PACKAGES_AUTH_TOKEN=dummy yarn build-types
  • FLOOR_PACKAGES_AUTH_TOKEN=dummy yarn lint
  • FLOOR_PACKAGES_AUTH_TOKEN=dummy ALCHEMY_API_KEY=dummy SIMULATE_DURING_TESTS=false NODE_OPTIONS="--loader ts-node/esm" ./node_modules/.bin/mocha --no-config --require ts-node/register --extension ts --timeout 90000 --exit test/ingestors/mintclub.test.ts
  • git diff --check

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Add Mint Club Base ERC1155 bonding-curve NFT ingestor

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Adds Mint Club ingestor for Base ERC1155 bonding-curve NFTs
• Resolves Mint Club URLs by computing deterministic Create2 addresses
• Supports WETH-backed ERC1155s with remaining supply via zap contract
• Includes eligibility evidence for 10+ qualifying prior Base mints
Diagram
flowchart LR
  URL["Mint Club URL<br/>mint.club/nft/base/:symbol"]
  CONTRACT["Base ERC1155<br/>Contract Address"]
  COMPUTE["Compute Create2<br/>Address"]
  BOND["Query Bond Contract<br/>for Token Details"]
  FILTER["Filter WETH-backed<br/>ERC1155s"]
  TEMPLATE["Generate MintTemplate<br/>with Zap Instructions"]
  
  URL --> COMPUTE
  CONTRACT --> BOND
  COMPUTE --> BOND
  BOND --> FILTER
  FILTER --> TEMPLATE
Loading

Grey Divider

File Changes

1. src/ingestors/index.ts ✨ Enhancement +2/-0

Register Mint Club ingestor

• Imports new MintClubIngestor class
• Registers mintclub ingestor in ALL_MINT_INGESTORS map

src/ingestors/index.ts


2. src/ingestors/mintclub/abi.ts Configuration +25/-0

Mint Club zap contract ABI definition

• Defines ABI for Mint Club zap contract mintWithEth function
• Specifies payable function with token, quantity, and receiver parameters

src/ingestors/mintclub/abi.ts


3. src/ingestors/mintclub/index.ts ✨ Enhancement +108/-0

Main Mint Club ingestor implementation

• Implements MintClubIngestor class with URL and contract support
• Resolves Mint Club tokens via symbol and contract address
• Creates MintTemplate with Mint Club metadata and zap instructions
• Sets mint output to Base chain with token ID 0
• Configures mintWithEth payable transaction with current reserve amount

src/ingestors/mintclub/index.ts


View more (4)
4. src/ingestors/mintclub/offchain-metadata.ts ✨ Enhancement +231/-0

Mint Club metadata resolution and address computation

• Implements deterministic Create2 address computation for Mint Club ERC1155s
• Queries Bond contract for token details and reserve amounts
• Filters to WETH-backed ERC1155s with remaining supply
• Fetches metadata from Mint Club API
• Provides URL parsing and validation for mint.club domain
• Includes retry logic for RPC calls

src/ingestors/mintclub/offchain-metadata.ts


5. src/ingestors/mintclub/types.ts ✨ Enhancement +38/-0

Mint Club TypeScript type definitions

• Defines MintClubMetadata type for logo, background, website, and comments
• Defines MintClubTokenInfo type with creator, symbol, supply, and reserve details
• Defines MintClubTokenDetails type combining info, royalties, and reserve amounts
• Defines MintClubEligibilityDetails type for eligibility verification

src/ingestors/mintclub/types.ts


6. test/ingestors/mintclub.test.ts 🧪 Tests +166/-0

Mint Club ingestor test suite

• Tests URL support for mint.club/nft/base/:symbol pattern
• Tests contract support for Base chain ERC1155s
• Verifies 10 eligible WETH-backed Base mints with holder counts
• Validates MintTemplate creation with correct metadata and zap instructions
• Confirms pricing includes reserve amount and royalty

test/ingestors/mintclub.test.ts


7. docs/mintclub-base-eligibility.md 📝 Documentation +26/-0

Mint Club Base eligibility evidence documentation

• Documents eligibility evidence for Mint Club Base ERC1155 support
• Lists 10 qualifying WETH-backed tokens with supply and holder counts
• References Bond contract and Base WETH token addresses
• Captured from BaseScan holder pages and on-chain data

docs/mintclub-base-eligibility.md


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented May 12, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Hardcoded Base RPC ✓ Resolved 🐞 Bug ☼ Reliability
Description
mintclub/offchain-metadata.ts creates a module-level JsonRpcProvider pointing at
https://mainnet.base.org and a module-level Contract, bypassing the repo’s configured Alchemy
provider and causing network I/O side effects as soon as ingestors/index.ts is imported. This can
break ingestion in environments that restrict outbound RPC access or rely on Alchemy
configuration/rate limits, and it makes behavior inconsistent with other ingestors.
Code

src/ingestors/mintclub/offchain-metadata.ts[R11-31]

+const BASE_RPC_URL = 'https://mainnet.base.org';
+const MINTCLUB_HOSTS = new Set(['mint.club', 'www.mint.club']);
+
+const BOND_ABI = [
+  'function exists(address token) view returns (bool)',
+  'function getDetail(address token) view returns (tuple(uint16 mintRoyalty,uint16 burnRoyalty,tuple(address creator,address token,uint8 decimals,string symbol,string name,uint40 createdAt,uint128 currentSupply,uint128 maxSupply,uint128 priceForNextMint,address reserveToken,uint8 reserveDecimals,string reserveSymbol,string reserveName,uint256 reserveBalance) info,tuple(uint128 rangeTo,uint128 price)[] steps) detail)',
+  'function getReserveForToken(address token,uint256 tokensToMint) view returns (uint256 reserveAmount,uint256 royalty)',
+];
+
+const provider = new JsonRpcProvider(
+  BASE_RPC_URL,
+  {
+    chainId: MINTCLUB_BASE_CHAIN_ID,
+    name: 'base',
+  },
+  {
+    staticNetwork: true,
+  },
+);
+const bondContract = new Contract(MINTCLUB_BOND_ADDRESS, BOND_ABI, provider);
+
Evidence
The MintClub module creates a Base RPC provider/contract at import time, while the rest of the repo
standardizes on Alchemy’s provider. Since MintClub is imported into the global ingestors registry,
the side effect happens whenever the registry is loaded.

src/ingestors/mintclub/offchain-metadata.ts[11-31]
src/ingestors/index.ts[1-28]
src/lib/resources.ts[5-20]
src/ingestors/coinbase-wallet/onchain-metadata.ts[5-9]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`src/ingestors/mintclub/offchain-metadata.ts` instantiates a hardcoded `JsonRpcProvider` (`https://mainnet.base.org`) and a global `bondContract` at module load time. Because `src/ingestors/index.ts` imports `./mintclub`, these side effects happen whenever the ingestors registry is imported, even if MintClub is never used.
## Issue Context
Other ingestors consistently obtain an ethers provider from `resources.alchemy.config.getProvider()`, which is configured via `ALCHEMY_API_KEY`. MintClub should follow the same pattern to avoid unconfigurable RPC dependencies and import-time network coupling.
## Fix Focus Areas
- src/ingestors/mintclub/offchain-metadata.ts[11-31]
- src/ingestors/mintclub/offchain-metadata.ts[83-130]
- src/ingestors/index.ts[1-28]
### Implementation direction
- Remove the module-level `JsonRpcProvider` and `bondContract`.
- Create `bondContract` using `await resources.alchemy.config.getProvider()` inside `getMintClubTokenDetails()`/`getMintClubEligibilityDetails()` (or create a small helper that returns a cached contract per provider).
- Keep the rest of the API the same for call sites.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Flaky onchain price test ✓ Resolved 🐞 Bug ☼ Reliability
Description
mintclub.test.ts asserts an exact priceWei value from getReserveForToken(token, 1), but that
value is derived from live bonding-curve state and can change as mints occur. This will
intermittently fail CI/test runs even when the ingestor is correct.
Code

test/ingestors/mintclub.test.ts[R156-165]

+    const mintInstructions = template.mintInstructions as EVMMintInstructions;
+    expect(mintInstructions.chainId).to.equal(8453);
+    expect(mintInstructions.contractAddress).to.equal('0x91523b39813F3F4E406ECe406D0bEAaA9dE251fa');
+    expect(mintInstructions.contractMethod).to.equal('mintWithEth');
+    expect(mintInstructions.contractParams).to.equal(
+      '["0x9974A5CD8C484D7df85a0C56B807E98755cD732B", 1, address]',
+    );
+    expect(mintInstructions.priceWei).to.equal('1003000000000000');
+    expect(mintInstructions.supportsQuantity).to.be.false;
+  });
Evidence
The test’s expected price comes directly from an on-chain reserve quote, which is inherently
variable for bonding-curve assets; the same repo already uses non-equality assertions for dynamic
prices elsewhere.

test/ingestors/mintclub.test.ts[139-165]
src/ingestors/mintclub/offchain-metadata.ts[98-120]
test/ingestors/foundation.test.ts[82-101]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The MintClub test expects a hardcoded `priceWei` for a live bonding-curve token. Since `priceWei` is read from chain state (`getReserveForToken`), it will change over time and cause flaky failures.
## Issue Context
Other tests treat dynamic pricing as non-deterministic (e.g., Foundation’s dutch auction test asserts `> 0` rather than equality).
## Fix Focus Areas
- test/ingestors/mintclub.test.ts[139-165]
- src/ingestors/mintclub/offchain-metadata.ts[98-120]
### Implementation direction
- Replace the exact equality check with a robustness check, e.g.:
- `expect(BigInt(mintInstructions.priceWei)).to.be.greaterThan(0n)`
- optionally assert it’s within a reasonable upper bound to catch obvious regressions.
- Keep the rest of the instruction assertions (method, params, contract) as-is.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Eligibility evidence lacks unique collectors ✓ Resolved 📎 Requirement gap ≡ Correctness
Description
The added eligibility documentation only lists per-token Holders counts and does not provide
verifiable evidence of more than 100 unique collectors across the 10+ prior Base mints (e.g.,
reproducible query or links). This may fail the bounty eligibility requirement if collector overlap
exists or if the counts cannot be independently verified.
Code

docs/mintclub-base-eligibility.md[R5-12]

+Source:
+- Mint Club V2 Base bond contract: `0xc5a076cad94176c2996B32d8466Be1cE757FAa27`
+- Base WETH reserve token: `0x4200000000000000000000000000000000000006`
+- BaseScan token holder pages for each Mint Club ERC1155 token.
+
+Evidence was captured with a local scanner against the sources above.
+
+The scan found 1,062 WETH-backed Mint Club ERC1155 candidates on Base with more than 10 current supply and remaining mintable supply. Selected BaseScan holder-qualified results are below.
Evidence
Compliance ID 2 requires evidence of >100 unique collectors across the Base mints. The documentation
provides a table of per-token Holders and states evidence came from a "local scanner" without a
reproducible query or links, and the test asserts holders from hardcoded constants rather than
computing unique collectors across the set.

Chosen platform eligibility: at least 10 prior Base mints and over 100 unique collectors
docs/mintclub-base-eligibility.md[5-12]
docs/mintclub-base-eligibility.md[14-26]
test/ingestors/mintclub.test.ts[109-137]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Eligibility evidence does not substantiate the requirement of `>100 unique collectors across those Base mints`; it only provides per-token holder counts and a non-reproducible statement about a "local scanner".
## Issue Context
Compliance ID 2 requires evidence for (1) at least 10 prior Base mints and (2) more than 100 unique collectors across those mints, ideally via links or a reproducible query. The current doc references BaseScan holder pages but does not include direct links nor a method/query to compute *unique* collectors across the set.
## Fix Focus Areas
- docs/mintclub-base-eligibility.md[5-12]
- docs/mintclub-base-eligibility.md[14-26]
- test/ingestors/mintclub.test.ts[109-137]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Unnecessary reserve/metadata reads ✓ Resolved 🐞 Bug ➹ Performance
Description
getMintClubTokenDetails fetches getReserveForToken and Mint Club HTTP metadata before applying the
WETH/decimals/supply eligibility filter, so ineligible tokens still pay the full on-chain + HTTP
cost. This makes supportsUrl/supportsContract slower than needed because they rely on
getMintClubTokenDetails for a boolean check.
Code

src/ingestors/mintclub/offchain-metadata.ts[R83-126]

+export const getMintClubTokenDetails = async (
+  resources: MintIngestorResources,
+  tokenAddress: string,
+): Promise<MintClubTokenDetails | undefined> => {
+  const normalizedAddress = normalizeAddress(tokenAddress);
+  if (!normalizedAddress) {
+    return;
+  }
+
+  try {
+    const exists = await withRetry(() => bondContract.exists(normalizedAddress));
+    if (!exists) {
+      return;
+    }
+
+    const detail = await withRetry(() => bondContract.getDetail(normalizedAddress));
+    const reserve = await withRetry(() => bondContract.getReserveForToken(normalizedAddress, 1));
+    const info = detail.info;
+    const tokenDetails: MintClubTokenDetails = {
+      mintRoyalty: Number(detail.mintRoyalty),
+      burnRoyalty: Number(detail.burnRoyalty),
+      info: {
+        creator: info.creator,
+        token: info.token,
+        decimals: Number(info.decimals),
+        symbol: info.symbol,
+        name: info.name,
+        createdAt: Number(info.createdAt),
+        currentSupply: BigInt(info.currentSupply),
+        maxSupply: BigInt(info.maxSupply),
+        priceForNextMint: BigInt(info.priceForNextMint),
+        reserveToken: info.reserveToken,
+        reserveSymbol: info.reserveSymbol,
+      },
+      reserveAmount: BigInt(reserve.reserveAmount),
+      royaltyAmount: BigInt(reserve.royalty),
+      metadata: await getMintClubMetadata(resources, normalizedAddress),
+    };
+
+    if (!isMintableWethErc1155(tokenDetails)) {
+      return;
+    }
+
+    return tokenDetails;
Evidence
Eligibility checks only depend on fields returned by getDetail, but the current flow fetches reserve
and HTTP metadata first; supportsUrl/supportsContract call this function, so the overhead applies to
support probing too.

src/ingestors/mintclub/offchain-metadata.ts[92-124]
src/ingestors/mintclub/offchain-metadata.ts[190-204]
src/ingestors/mintclub/index.ts[24-39]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`getMintClubTokenDetails()` performs `getReserveForToken()` and `getMintClubMetadata()` even when the token will be rejected by `isMintableWethErc1155()` based solely on `getDetail()` fields (decimals, reserveToken, currentSupply, maxSupply).
## Issue Context
Both `supportsUrl()` and `supportsContract()` call into this function (via `resolveMintClubTokenForSymbol()` / directly), so this extra work impacts the primary ingestion path.
## Fix Focus Areas
- src/ingestors/mintclub/offchain-metadata.ts[83-126]
- src/ingestors/mintclub/index.ts[24-39]
### Implementation direction
- After `getDetail()`, build the minimal `info` object and apply the eligibility checks before calling `getReserveForToken()` and `getMintClubMetadata()`.
- Optionally split into two functions:
- a lightweight `getMintClubTokenInfo()` for supports checks
- a full `getMintClubTokenDetails()` for template creation (adds reserve + metadata).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread src/ingestors/mintclub/offchain-metadata.ts Outdated
Comment thread test/ingestors/mintclub.test.ts
@EmergentKnowledgeGroup

Copy link
Copy Markdown
Author

Addressed the review feedback in commit 8421d2f: removed the module-level hardcoded Base RPC/contract and now build the Mint Club Bond contract from resources.alchemy.config.getProvider(), reject ineligible tokens after getDetail before reserve/metadata reads, made dynamic bonding-curve price validation non-exact, and expanded eligibility docs/tests with BaseScan holder links plus a >100 unique-collector lower bound. Re-ran build-types, lint, diff check, and the focused Mint Club mocha suite: 11 passing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant