Skip to content
This repository was archived by the owner on Apr 14, 2026. It is now read-only.

Commit 3d07fa6

Browse files
committed
Resolve protocol links in profiles via public gateways.
1 parent 40f3101 commit 3d07fa6

4 files changed

Lines changed: 41 additions & 6 deletions

File tree

environment.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ interface EnvironmentVariables {
1515
readonly POAP_API_TOKEN: string
1616
readonly REDIS_URL: string
1717
readonly ENS_API_URL: string
18+
readonly IPFS_GATEWAY_URL: string
19+
readonly ARWEAVE_GATEWAY_URL: string
1820
}
1921

2022
// Cloudflare Workers

src/service/ens-metadata/service.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { database } from '#/database'
44
import { apiLogger } from '#/logger'
55
import type { Address, DB } from '#/types'
66
import type { Environment } from '#/types/index'
7-
import { arrayToChunks, isAddress, raise } from '#/utilities.ts'
7+
import { arrayToChunks, isAddress, raise, resolveDecentralizedURI } from '#/utilities.ts'
88
import type { ENSProfile } from './types'
99

1010
export type ENSProfileResponse = ENSProfile & { type: 'error' | 'success' }
@@ -29,11 +29,13 @@ type Row = {
2929
export class ENSMetadataService implements IENSMetadataService {
3030
readonly #db: Kysely<DB>
3131
readonly #url: string
32+
readonly #gateways: { ipfs: string; arweave: string }
3233

3334
// biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
3435
constructor(env: Env) {
3536
this.#db = database(env)
3637
this.#url = env.ENS_API_URL
38+
this.#gateways = { ipfs: env.IPFS_GATEWAY_URL, arweave: env.ARWEAVE_GATEWAY_URL }
3739
}
3840

3941
async getAddress(ensNameOrAddress: Address | string): Promise<Address> {
@@ -118,7 +120,7 @@ export class ENSMetadataService implements IENSMetadataService {
118120
})
119121
} catch (_error) {}
120122

121-
return ensProfileData as ENSProfile
123+
return this.#resolveRecordURIs(ensProfileData) as ENSProfile
122124
} catch (error) {
123125
console.log('error', error)
124126
}
@@ -194,7 +196,7 @@ export class ENSMetadataService implements IENSMetadataService {
194196
} catch (error) {
195197
console.log('cache failed', error)
196198
}
197-
return ensProfileData as ENSProfile
199+
return this.#resolveRecordURIs(ensProfileData) as ENSProfile
198200
} catch (error) {
199201
console.log('error', error)
200202
}
@@ -206,7 +208,7 @@ export class ENSMetadataService implements IENSMetadataService {
206208
formattedSecondTry.records = formattedSecondTry?.records
207209
? (JSON.parse(formattedSecondTry?.records) as string)
208210
: ''
209-
return secondTry as ENSProfile
211+
return this.#resolveRecordURIs(secondTry as ENSProfile)
210212
}
211213

212214
return {
@@ -221,7 +223,7 @@ export class ENSMetadataService implements IENSMetadataService {
221223
if (cachedProfile) {
222224
returnedRecord.records = returnedRecord?.records ? (JSON.parse(returnedRecord?.records) as string) : ''
223225
}
224-
return returnedRecord as ENSProfile
226+
return this.#resolveRecordURIs(returnedRecord)
225227
}
226228

227229
/**
@@ -289,13 +291,15 @@ export class ENSMetadataService implements IENSMetadataService {
289291
if (record.name) {
290292
await this.cacheRecord(record)
291293
record.records = JSON.parse(record?.records || '') as string
294+
this.#resolveRecordURIs(record)
292295
}
293296
// record.records = JSON.parse(record?.records || '') as string;
294297
}
295298

296299
for (const record of filteredCache) {
297300
if (record.name) {
298301
record.records = JSON.parse(record?.records || '') as string
302+
this.#resolveRecordURIs(record)
299303
}
300304
}
301305

@@ -334,6 +338,19 @@ export class ENSMetadataService implements IENSMetadataService {
334338
}, {})
335339
}
336340

341+
#resolveRecordURIs(profile: ENSProfile): ENSProfile {
342+
if (profile.records && typeof profile.records === 'object') {
343+
const records = profile.records as unknown as Record<string, unknown>
344+
for (const key of Object.keys(records)) {
345+
const value = records[key]
346+
if (typeof value === 'string') {
347+
records[key] = resolveDecentralizedURI(value, this.#gateways)
348+
}
349+
}
350+
}
351+
return profile
352+
}
353+
337354
static #toTableRow(namedata: ENSProfile): {
338355
name: string
339356
address: string

src/utilities.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ export function arrayToChunks<T>(array: T[], chunkSize: number): T[][] {
2323
return chunks
2424
}
2525

26+
export function resolveDecentralizedURI(
27+
uri: string | undefined | null,
28+
gateways: { ipfs: string; arweave: string }
29+
): string | undefined | null {
30+
if (!uri) return uri
31+
if (uri.startsWith('ipfs://')) {
32+
const gateway = gateways.ipfs.endsWith('/') ? gateways.ipfs : `${gateways.ipfs}/`
33+
return uri.replace('ipfs://', gateway)
34+
}
35+
if (uri.startsWith('ar://')) {
36+
const gateway = gateways.arweave.endsWith('/') ? gateways.arweave : `${gateways.arweave}/`
37+
return uri.replace('ar://', gateway)
38+
}
39+
return uri
40+
}
41+
2642
// removed properties with undefined values from object
2743
export function removeUndefined<T>(object: T): T {
2844
return JSON.parse(JSON.stringify(object)) as T

wrangler.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ placement = { mode = "smart" }
1414
compatibility_date = "2023-10-30"
1515
# end of globally inheritable configuration
1616
#
17-
vars = { ENVIRONMENT = "development" }
17+
vars = { ENVIRONMENT = "development", IPFS_GATEWAY_URL = "https://ipfs.io/ipfs/", ARWEAVE_GATEWAY_URL = "https://arweave.net/" }
1818
services = [{ binding = "ens", service = "ens" }]
1919
kv_namespaces = [
2020
{ binding = "EFP_DATA_CACHE", id = "5092581c2d524711a04560d335966a60", preview_id = "608971607bd2469e8972a0811f8de589" },

0 commit comments

Comments
 (0)