Skip to content

Commit b85deca

Browse files
Merge pull request #741 from ArtBlocks/hide-l2
l2-ifying artbot
2 parents 1a163f4 + 5ede15c commit b85deca

File tree

12 files changed

+208
-248
lines changed

12 files changed

+208
-248
lines changed

src/Classes/APIBots/OpenSeaEventsPollBot.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,10 @@ export class OpenSeaEventsPollBot extends APIPollBot {
349349
const seller = saleEvent.seller || ''
350350
const buyer = saleEvent.buyer || ''
351351

352+
// API poll bot currently only queries Ethereum mainnet contracts
352353
const normalized: NormalizedOpenSeaSale = {
353354
source: 'api',
355+
chainId: 1,
354356
osAssetId: `ethereum/${contractAddress}/${tokenId}`,
355357
contractAddress,
356358
tokenId,

src/Classes/APIBots/OpenSeaListBot.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,12 @@ export class OpenSeaListBot {
107107

108108
try {
109109
// Get Art Blocks metadata response for the item (same as ReservoirListBot)
110-
const tokenApiUrl = getTokenApiUrl(contractAddress, tokenId)
110+
const tokenApiUrl = getTokenApiUrl(nftInfo.chainId, contractAddress, tokenId)
111111
const artBlocksResponse = await axios.get(tokenApiUrl)
112112
const artBlocksData = artBlocksResponse?.data as ArtBlocksTokenData
113113
const tokenUrl = getTokenUrl(
114114
artBlocksData.external_url ?? '',
115+
nftInfo.chainId,
115116
contractAddress,
116117
tokenId
117118
)

src/Classes/APIBots/OpenSeaSaleBot.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { artIndexerBot } from '../..'
2929

3030
export interface NormalizedOpenSeaSale {
3131
source: 'stream' | 'api'
32+
chainId: number
3233
osAssetId: string
3334
contractAddress: string
3435
tokenId: string
@@ -95,6 +96,7 @@ export class OpenSeaSaleBot {
9596
)
9697
const normalizedSale: NormalizedOpenSeaSale = {
9798
source: 'stream',
99+
chainId: nftInfo.chainId,
98100
osAssetId: event.payload.item.nft_id.toLowerCase(),
99101
contractAddress: nftInfo.contractAddress,
100102
tokenId: nftInfo.tokenId,
@@ -183,19 +185,20 @@ export class OpenSeaSaleBot {
183185

184186
try {
185187
// Get Art Blocks metadata response for the item (same as ReservoirSaleBot)
186-
const tokenApiUrl = getTokenApiUrl(contractAddress, tokenId)
188+
const tokenApiUrl = getTokenApiUrl(sale.chainId, contractAddress, tokenId)
187189
const artBlocksResponse = await axios.get(tokenApiUrl)
188190
const artBlocksData = artBlocksResponse?.data as ArtBlocksTokenData
189191

190192
const tokenUrl = getTokenUrl(
191193
artBlocksData.external_url ?? '',
194+
sale.chainId,
192195
contractAddress,
193196
tokenId
194197
)
195198

196199
let sellerText = await ensOrAddress(seller)
197200
let buyerText = await ensOrAddress(buyer)
198-
const platformUrl = buildArtBlocksTokenURL(contractAddress, tokenId)
201+
const platformUrl = buildArtBlocksTokenURL(sale.chainId, contractAddress, tokenId)
199202

200203
// Add OpenSea usernames if available (same logic as ReservoirSaleBot)
201204
if (!sellerText.includes('.eth')) {

src/Classes/APIBots/utils.ts

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as dotenv from 'dotenv'
22

33
import {
4-
ARBITRUM_CONTRACTS,
54
COLLAB_CONTRACTS,
65
ENGINE_CONTRACTS,
76
STUDIO_CONTRACTS,
@@ -28,7 +27,6 @@ const publicClient = createPublicClient({
2827
]),
2928
})
3029

31-
const STAGING_CONTRACTS = require('../../ProjectConfig/stagingContracts.json')
3230
const EXPLORATIONS_CONTRACTS = require('../../ProjectConfig/explorationsContracts.json')
3331

3432
const CORE_CONTRACTS = require('../../ProjectConfig/coreContracts.json')
@@ -260,22 +258,12 @@ export function getVerticalName(msg: string): string {
260258
}
261259

262260
export function getTokenApiUrl(
261+
chainId: number,
263262
contractAddress: string,
264263
tokenId: string
265264
): string {
266265
contractAddress = contractAddress.toLowerCase()
267-
if (
268-
Object.values(CORE_CONTRACTS).includes(contractAddress) ||
269-
contractAddress === ''
270-
) {
271-
return `https://token.artblocks.io/${tokenId}`
272-
} else if (Object.values(STAGING_CONTRACTS).includes(contractAddress)) {
273-
return `https://token.staging.artblocks.io/${contractAddress}/${tokenId}`
274-
} else if (isArbitrumContract(contractAddress)) {
275-
return `https://token.arbitrum.artblocks.io/${contractAddress}/${tokenId}`
276-
} else {
277-
return `https://token.artblocks.io/${contractAddress}/${tokenId}`
278-
}
266+
return `https://token.artblocks.io/${chainId}/${contractAddress}/${tokenId}`
279267
}
280268

281269
export function isExplorationsContract(contractAddress: string): boolean {
@@ -292,10 +280,6 @@ export function isEngineContract(contractAddress: string): boolean {
292280
return ENGINE_CONTRACTS.includes(contractAddress.toLowerCase())
293281
}
294282

295-
export function isArbitrumContract(contractAddress: string): boolean {
296-
return ARBITRUM_CONTRACTS.includes(contractAddress.toLowerCase())
297-
}
298-
299283
export function isCoreContract(contractAddress: string): boolean {
300284
return Object.values(CORE_CONTRACTS).includes(contractAddress.toLowerCase())
301285
}
@@ -324,28 +308,50 @@ export async function getCollectionType(
324308

325309
export function getTokenUrl(
326310
external_url: string,
311+
chainId: number,
327312
contractAddr: string,
328313
tokenId: string
329314
): string {
330315
if (external_url && !external_url.includes('generator.artblocks.io')) {
331316
return external_url
332317
}
333-
return buildArtBlocksTokenURL(contractAddr, tokenId)
318+
return buildArtBlocksTokenURL(chainId, contractAddr, tokenId)
334319
}
335320

336321
export function getProjectSlugUrl(slug: string): string {
337322
return `https://www.artblocks.io/collection/${slug}`
338323
}
339324

340-
export function getProjectUrl(contractAddr: string, projectId: string): string {
341-
return `https://www.artblocks.io/collection/${contractAddr}-${projectId}`
342-
}
343-
344325
export function buildArtBlocksTokenURL(
326+
chainId: number,
345327
contractAddr: string,
346328
tokenId: string
347329
): string {
348-
return `https://www.artblocks.io/token/${contractAddr}/${tokenId}`
330+
return `https://www.artblocks.io/token/${chainId}/${contractAddr}/${tokenId}`
331+
}
332+
333+
/**
334+
* Build generator (live script) URL from chain + contract + token.
335+
*/
336+
export function buildGeneratorUrl(
337+
chainId: number,
338+
contractAddress: string,
339+
tokenId: string
340+
): string {
341+
contractAddress = contractAddress.toLowerCase()
342+
return `https://generator.artblocks.io/${chainId}/${contractAddress}/${tokenId}`
343+
}
344+
345+
/**
346+
* Build media/preview image URL from chain + contract + token.
347+
*/
348+
export function buildMediaUrl(
349+
chainId: number,
350+
contractAddress: string,
351+
tokenId: string
352+
): string {
353+
contractAddress = contractAddress.toLowerCase()
354+
return `https://media-proxy.artblocks.io/${chainId}/${contractAddress}/${tokenId}.png`
349355
}
350356

351357
export function buildOpenseaURL(contractAddr: string, tokenId: string): string {
@@ -439,16 +445,29 @@ export const CLEANUP_INTERVAL_MS = 60 * 60 * 1000
439445
* @param nftId - Format: "ethereum/0x2308742aa28cc460522ff855d24a365f99deba7b/7111"
440446
* @returns {contractAddress, tokenId} or null if invalid format
441447
*/
448+
const CHAIN_NAME_TO_ID: Record<string, number> = {
449+
ethereum: 1,
450+
arbitrum: 42161,
451+
base: 8453,
452+
}
453+
442454
export function parseNftId(
443455
nftId: string
444-
): { contractAddress: string; tokenId: string } | null {
456+
): { chainId: number; contractAddress: string; tokenId: string } | null {
445457
const parts = nftId.split('/')
446-
if (parts.length !== 3 || parts[0] !== 'ethereum') {
458+
if (parts.length !== 3) {
447459
console.warn('Invalid NFT ID format:', nftId)
448460
return null
449461
}
450462

463+
const chainId = CHAIN_NAME_TO_ID[parts[0]]
464+
if (chainId === undefined) {
465+
console.warn('Unknown chain in NFT ID:', parts[0], nftId)
466+
return null
467+
}
468+
451469
return {
470+
chainId,
452471
contractAddress: parts[1].toLowerCase(),
453472
tokenId: parts[2],
454473
}

src/Classes/ArtIndexerBot.ts

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import {
2828
getVerticalName,
2929
isVerticalName,
3030
resolveEnsName,
31-
getProjectUrl,
3231
getProjectSlugUrl,
3332
} from './APIBots/utils'
3433
import { randomColor } from '../Utils/smartBotResponse'
@@ -138,19 +137,11 @@ export class ArtIndexerBot {
138137

139138
async buildContracts() {
140139
try {
141-
const arbContractsArr = await getAllContracts(true)
142-
for (let i = 0; i < arbContractsArr.length; i++) {
143-
const name = arbContractsArr[i].name
140+
const contractsArr = await getAllContracts()
141+
for (let i = 0; i < contractsArr.length; i++) {
142+
const name = contractsArr[i].name
144143
if (typeof name === 'string') {
145-
this.contracts[name.toLowerCase()] = arbContractsArr[i]
146-
}
147-
}
148-
149-
const ethContractsArr = await getAllContracts(false)
150-
for (let i = 0; i < ethContractsArr.length; i++) {
151-
const name = ethContractsArr[i].name
152-
if (typeof name === 'string') {
153-
this.contracts[name.toLowerCase()] = ethContractsArr[i]
144+
this.contracts[name.toLowerCase()] = contractsArr[i]
154145
}
155146
}
156147
} catch (e) {
@@ -199,8 +190,10 @@ export class ArtIndexerBot {
199190
)
200191
const newBot = new ProjectBot({
201192
id: project.id,
193+
chainId: project.chain_id,
202194
projectNumber: parseInt(project.project_id),
203195
coreContract: project.contract_address,
196+
slug: project.slug,
204197
editionSize: project.invocations,
205198
maxEditionSize: project.max_invocations,
206199
projectName: project.name ?? 'unknown',
@@ -997,22 +990,16 @@ export class ArtIndexerBot {
997990
const projectPrices: {
998991
price: number
999992
name: string
1000-
contractAddress: string
1001-
projectId: string
1002-
slug?: string
993+
slug: string
1003994
}[] = []
1004995
const projectsWithoutListings: {
1005996
name: string
1006-
contractAddress: string
1007-
projectId: string
1008-
slug?: string
997+
slug: string
1009998
}[] = []
1010999

10111000
for (const bucket of validBuckets) {
10121001
if (bucket.project) {
10131002
const projectName = bucket.project.name || 'Unknown Project'
1014-
const contractAddress = bucket.project.contract_address
1015-
const projectId = bucket.project.project_id
10161003
const slug = bucket.project.slug
10171004

10181005
if (bucket.project.lowest_listing) {
@@ -1022,16 +1009,11 @@ export class ArtIndexerBot {
10221009
projectPrices.push({
10231010
price,
10241011
name: projectName,
1025-
contractAddress,
1026-
projectId,
10271012
slug,
10281013
})
10291014
} else {
1030-
// Project exists but has no listings
10311015
projectsWithoutListings.push({
10321016
name: projectName,
1033-
contractAddress,
1034-
projectId,
10351017
slug,
10361018
})
10371019
}
@@ -1109,23 +1091,19 @@ export class ArtIndexerBot {
11091091
// Top 3 cheapest (already sorted ascending)
11101092
const top3Cheapest = sortedProjectPrices.slice(0, 3)
11111093
const cheapestText = top3Cheapest
1112-
.map((p) => {
1113-
const projectUrl = p.slug
1114-
? getProjectSlugUrl(p.slug)
1115-
: getProjectUrl(p.contractAddress, p.projectId)
1116-
return `— [${p.name}](${projectUrl}) • ${formatPrice(p.price)} Ξ`
1117-
})
1094+
.map(
1095+
(p) =>
1096+
`— [${p.name}](${getProjectSlugUrl(p.slug)}) • ${formatPrice(p.price)} Ξ`
1097+
)
11181098
.join('\n')
11191099

11201100
// Top 3 priciest (reverse order - most expensive first)
11211101
const top3Priciest = sortedProjectPrices.slice(-3).reverse()
11221102
const priciestText = top3Priciest
1123-
.map((p) => {
1124-
const projectUrl = p.slug
1125-
? getProjectSlugUrl(p.slug)
1126-
: getProjectUrl(p.contractAddress, p.projectId)
1127-
return `— [${p.name}](${projectUrl}) • ${formatPrice(p.price)} Ξ`
1128-
})
1103+
.map(
1104+
(p) =>
1105+
`— [${p.name}](${getProjectSlugUrl(p.slug)}) • ${formatPrice(p.price)} Ξ`
1106+
)
11291107
.join('\n')
11301108

11311109
embedContent.addFields({
@@ -1144,12 +1122,7 @@ export class ArtIndexerBot {
11441122
const firstFive = projectsWithoutListings.slice(0, 5)
11451123
const noListingsText =
11461124
firstFive
1147-
.map((p) => {
1148-
const projectUrl = p.slug
1149-
? getProjectSlugUrl(p.slug)
1150-
: getProjectUrl(p.contractAddress, p.projectId)
1151-
return `[${p.name}](${projectUrl})`
1152-
})
1125+
.map((p) => `[${p.name}](${getProjectSlugUrl(p.slug)})`)
11531126
.join(' • ') +
11541127
(projectsWithoutListings.length > 5
11551128
? ` and ${projectsWithoutListings.length - 5} more`

0 commit comments

Comments
 (0)