Visión General
El Servicio de Almacenamiento BaseMint proporciona una solución confiable y escalable para almacenar y gestionar metadatos de colecciones NFT. Ofrece direccionamiento determinista, entrega respaldada por CDN e integración sin fisuras con el ecosistema CreateKit.Arquitectura de Almacenamiento
Cómo Funciona el Almacenamiento
1
Envío de Metadatos
Metadatos de la colección y firmas de creadores son enviados al servicio de almacenamiento
2
Validación y Almacenamiento
El servicio valida firmas y almacena metadatos con una dirección determinista
3
Distribución CDN
Metadatos son distribuidos vía CDN para un acceso rápido y global
4
Integración con el Mercado
Colecciones son descubribles vía direcciones predichas antes del despliegue
Características Clave
Direccionamiento Determinista
Colecciones tienen direcciones predecibles para la integración con el mercado
Validación de Firmas
Verificación criptográfica de la autenticidad de la colección
Entrega CDN
Entrega de metadatos rápida y confiable a nivel mundial
Sistema de Referidos
Seguimiento y gestión de colecciones por socios de integración
Configuración del Servicio de Almacenamiento
Configuración Básica
Inicializar Servicio de Almacenamiento
Copy
Ask AI
import { BaseMintStorage } from '@b3dotfun/basemint'
// Inicializar servicio de almacenamiento
const storage = new BaseMintStorage({
baseUrl: 'https://api.basemint.fun', // URL de producción
// Para desarrollo en testnet:
// baseUrl: 'https://testnet-api.basemint.fun'
})
// Probar conexión
try {
const health = await storage.healthCheck()
console.log('✅ Servicio de almacenamiento conectado:', health)
} catch (error) {
console.error('❌ Servicio de almacenamiento no disponible:', error)
}
Configuración Basada en el Entorno
Configuración Basada en el Entorno
Copy
Ask AI
// Configuración específica del entorno
const getStorageConfig = (environment: 'development' | 'staging' | 'production') => {
const configs = {
development: {
baseUrl: 'https://testnet-api.basemint.fun',
chainId: 1993 // B3 Testnet
},
staging: {
baseUrl: 'https://staging-api.basemint.fun',
chainId: 1993
},
production: {
baseUrl: 'https://api.basemint.fun',
chainId: 8333 // B3 Mainnet
}
}
return configs[environment]
}
const config = getStorageConfig(process.env.NODE_ENV as any || 'development')
const storage = new BaseMintStorage(config)
Enviar Colecciones
Envío Básico de Colecciones
Enviar Colección
Copy
Ask AI
async function submitCollection(
collectionMetadata: any,
creatorSignature: string,
referrerId?: string
) {
try {
const response = await storage.submitCollection(
collectionMetadata,
creatorSignature,
referrerId // Opcional: rastrear colecciones por referido
)
console.log('✅ Colección enviada con éxito')
console.log('ID de la Colección:', response.collectionId)
console.log('Dirección predicha:', response.predictedAddress)
console.log('URI de Metadatos:', response.metadataUri)
return response
} catch (error: any) {
if (error.message.includes('Firma inválida')) {
console.error('❌ Verificación de firma del creador fallida')
} else if (error.message.includes('La colección existe')) {
console.error('❌ Colección con esta dirección ya existe')
} else if (error.message.includes('Referido no encontrado')) {
console.error('❌ ID de referido inválido')
} else {
console.error('❌ Envío fallido:', error.message)
}
throw error
}
}
// Ejemplo de uso
const response = await submitCollection(
collectionMetadata,
creatorSignature,
"mi-plataforma-de-juegos" // Tu ID de referido
)
Envío de Colecciones en Lote
Envío en Lote
Copy
Ask AI
async function submitMultipleCollections(
collections: Array<{
metadata: any
signature: string
referrerId?: string
}>
) {
const results = []
for (const collection of collections) {
try {
const response = await storage.submitCollection(
collection.metadata,
collection.signature,
collection.referrerId
)
results.push({
success: true,
collectionName: collection.metadata.name,
response
})
console.log(`✅ Enviado: ${collection.metadata.name}`)
} catch (error) {
results.push({
success: false,
collectionName: collection.metadata.name,
error: error.message
})
console.error(`❌ Fallido: ${collection.metadata.name}`, error)
}
}
return results
}
Consultar Colecciones
Consultas Básicas
Consultar Colecciones
Copy
Ask AI
// Consultar todas las colecciones
const allCollections = await storage.queryCollections()
console.log(`Encontradas ${allCollections.collections.length} colecciones`)
// Consultar con paginación
const paginatedResults = await storage.queryCollections({
limit: 20,
offset: 0
})
// Consultar por referido
const gameCollections = await storage.queryCollections({
referrer: "mi-plataforma-de-juegos"
})
// Consultar por creador
const creatorCollections = await storage.queryCollections({
creator: "0x1234567890123456789012345678901234567890"
})
Filtrado Avanzado
Consultas Avanzadas
Copy
Ask AI
// Consulta compleja con múltiples filtros
const advancedQuery = await storage.queryCollections({
referrer: "mi-plataforma-de-juegos",
creator: "0x1234567890123456789012345678901234567890",
tokenStandard: "ERC721",
chainId: 1993,
limit: 50,
offset: 0,
sortBy: "createdAt",
sortOrder: "desc"
})
console.log("Resultados de consulta avanzada:", {
total: advancedQuery.total,
count: advancedQuery.collections.length,
hasMore: advancedQuery.hasMore
})
// Filtrar por estado de despliegue
const undeployedCollections = advancedQuery.collections.filter(
collection => !collection.isDeployed
)
const deployedCollections = advancedQuery.collections.filter(
collection => collection.isDeployed
)
console.log(`Sin desplegar: ${undeployedCollections.length}`)
console.log(`Desplegadas: ${deployedCollections.length}`)
Funcionalidad de Búsqueda
Buscar Colecciones
Copy
Ask AI
// Buscar por nombre o símbolo
const searchResults = await storage.searchCollections({
query: "fantasía",
limit: 10
})
// Buscar con filtros
const filteredSearch = await storage.searchCollections({
query: "juego",
referrer: "mi-plataforma-de-juegos",
tokenStandard: "ERC1155"
})
console.log("Resultados de búsqueda:", searchResults.collections.map(c => ({
name: c.name,
symbol: c.symbol,
description: c.description
})))
Gestión de Referidos
Registrarse como Referido
Registro de Referidos
Copy
Ask AI
// Registra tu plataforma como referido
async function registerAsReferrer(referrerId: string, metadata?: any) {
try {
await storage.registerReferrer(referrerId, metadata)
console.log(`✅ Registrado como referido: ${referrerId}`)
} catch (error: any) {
if (error.message.includes('ya existe')) {
console.log(`ℹ️ Referido ${referrerId} ya registrado`)
} else {
console.error('❌ Registro fallido:', error)
throw error
}
}
}
// Registrar con metadatos
await registerAsReferrer("mi-plataforma-de-juegos", {
name: "Mi Plataforma de Juegos",
website: "https://mijuego.com",
contact: "dev@mijuego.com",
description: "Una plataforma de juegos para colecciones NFT"
})
Gestión de Colecciones de Referidos
Gestión de Colecciones de Referidos
Copy
Ask AI
// Obtener todas las colecciones para tu plataforma
async function getReferrerDashboard(referrerId: string) {
const collections = await storage.queryCollections({
referrer: referrerId
})
const stats = {
total: collections.total,
deployed: collections.collections.filter(c => c.isDeployed).length,
undeployed: collections.collections.filter(c => !c.isDeployed).length,
erc721: collections.collections.filter(c => c.tokenStandard === 'ERC721').length,
erc1155: collections.collections.filter(c => c.tokenStandard === 'ERC1155').length
}
console.log("Tablero de referidos:", stats)
return {
collections: collections.collections,
stats
}
}
const dashboard = await getReferrerDashboard("mi-plataforma-de-juegos")
Gestión de Colecciones
Recuperar Datos de la Colección
Obtener Detalles de la Colección
Copy
Ask AI
// Obtener colección por dirección
async function getCollectionDetails(address: string) {
try {
const collection = await storage.getCollection(address)
console.log("Detalles de la colección:", {
name: collection.name,
symbol: collection.symbol,
creator: collection.creator,
gameOwner: collection.gameOwner,
isDeployed: collection.isDeployed,
createdAt: collection.createdAt,
metadataUri: collection.metadataUri
})
return collection
} catch (error: any) {
if (error.message.includes('no encontrada')) {
console.error('❌ Colección no encontrada')
} else {
console.error('❌ Error al recuperar colección:', error)
}
throw error
}
}
// Obtener múltiples colecciones por direcciones
async function getMultipleCollections(addresses: string[]) {
const collections = await Promise.allSettled(
addresses.map(address => storage.getCollection(address))
)
const successful = collections
.filter((result): result is PromiseFulfilledResult<any> => result.status === 'fulfilled')
.map(result => result.value)
const failed = collections
.filter((result): result is PromiseRejectedResult => result.status === 'rejected')
.map(result => result.reason)
console.log(`✅ Recuperadas ${successful.length} colecciones`)
console.log(`❌ Falló la recuperación de ${failed.length} colecciones`)
return { successful, failed }
}
Actualizar Colecciones
Las actualizaciones de colecciones están limitadas a campos específicos y pueden requerir autenticación adicional.
Actualizar Colección
Copy
Ask AI
// Actualizar metadatos de la colección (campos limitados)
async function updateCollectionMetadata(
address: string,
updates: {
description?: string
image?: string
external_url?: string
animation_url?: string
}
) {
try {
const updatedCollection = await storage.updateCollection(address, updates)
console.log('✅ Colección actualizada con éxito')
return updatedCollection
} catch (error: any) {
if (error.message.includes('no autorizado')) {
console.error('❌ No autorizado para actualizar esta colección')
} else if (error.message.includes('campo inmutable')) {
console.error('❌ Intento de actualizar campo inmutable')
} else {
console.error('❌ Actualización fallida:', error)
}
throw error
}
}
Eliminar Colecciones
Eliminar Colecciones
Copy
Ask AI
// Eliminar una sola colección (solo no desplegadas)
async function deleteCollection(address: string) {
try {
await storage.deleteCollection(address)
console.log(`✅ Colección ${address} eliminada`)
} catch (error: any) {
if (error.message.includes('desplegada')) {
console.error('❌ No se puede eliminar colección desplegada')
} else if (error.message.includes('no encontrada')) {
console.error('❌ Colección no encontrada')
} else {
console.error('❌ Eliminación fallida:', error)
}
throw error
}
}
// Eliminación masiva de colecciones (solo para referidos)
async function bulkDeleteCollections(
identifiers: string[], // UUIDs o direcciones
referrerId: string
) {
try {
const result = await storage.bulkDeleteCollections(identifiers, referrerId)
console.log(`✅ Eliminadas ${result.deleted.length} colecciones`)
console.log(`❌ Falló la eliminación de ${result.failed.length} colecciones`)
return result
} catch (error) {
console.error('❌ Eliminación masiva fallida:', error)
throw error
}
}
Gestión de Metadatos
URIs de Metadatos Personalizados
Manejo de URI de Metadatos
Copy
Ask AI
// Generar URI de metadatos para una colección
function generateMetadataUri(collectionId: string, baseUrl: string): string {
return `${baseUrl}/metadata/${collectionId}`
}
// Obtener metadatos directamente
async function getCollectionMetadata(collectionId: string) {
try {
const metadataUri = generateMetadataUri(collectionId, storage.baseUrl)
const response = await fetch(metadataUri)
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const metadata = await response.json()
return metadata
} catch (error) {
console.error('❌ Falló la obtención de metadatos:', error)
throw error
}
}
// Validar formato de metadatos
function validateMetadata(metadata: any): { isValid: boolean; errors: string[] } {
const errors: string[] = []
if (!metadata.name) errors.push('Falta nombre')
if (!metadata.description) errors.push('Falta descripción')
if (!metadata.image) errors.push('Falta imagen')
// Verificar compatibilidad con OpenSea
if (metadata.attributes && !Array.isArray(metadata.attributes)) {
errors.push('Los atributos deben ser un arreglo')
}
return {
isValid: errors.length === 0,
errors
}
}
Gestión de Activos
Carga y Gestión de Activos
Copy
Ask AI
// Cargar activos al servicio de almacenamiento (si se soporta)
async function uploadAsset(
file: File | Buffer,
filename: string,
contentType: string
): Promise<string> {
try {
// Esto depende de la implementación de tu servicio de almacenamiento
const formData = new FormData()
formData.append('file', file, filename)
formData.append('contentType', contentType)
const response = await fetch(`${storage.baseUrl}/upload`, {
method: 'POST',
body: formData
})
if (!response.ok) {
throw new Error(`Carga fallida: ${response.statusText}`)
}
const result = await response.json()
return result.url
} catch (error) {
console.error('❌ Carga de activo fallida:', error)
throw error
}
}
// Optimizar imágenes para estándares NFT
function getOptimizedImageUrl(
originalUrl: string,
size: 'thumbnail' | 'medium' | 'large' = 'medium'
): string {
const sizeMap = {
thumbnail: '200x200',
medium: '640x640',
large: '1200x1200'
}
// Ejemplo de transformación de URL CDN
return `${originalUrl}?size=${sizeMap[size]}&format=webp&quality=85`
}
Manejo de Errores
Manejo de Errores Integral
Patrones de Manejo de Errores
Copy
Ask AI
class StorageError extends Error {
constructor(
message: string,
public code: string,
public statusCode?: number
) {
super(message)
this.name = 'StorageError'
}
}
async function robustStorageOperation<T>(
operation: () => Promise<T>,
retries: number = 3,
delayMs: number = 1000
): Promise<T> {
let lastError: Error
for (let attempt = 1; attempt <= retries; attempt++) {
try {
return await operation()
} catch (error: any) {
lastError = error
// No reintentar ciertos errores
if (error.message.includes('Firma inválida') ||
error.message.includes('La colección existe')) {
throw error
}
console.warn(`Intento ${attempt} fallido:`, error.message)
if (attempt < retries) {
await new Promise(resolve => setTimeout(resolve, delayMs * attempt))
}
}
}
throw new StorageError(
`Operación fallida después de ${retries} intentos: ${lastError.message}`,
'MAX_RETRIES_EXCEEDED'
)
}
// Uso
const result = await robustStorageOperation(async () => {
return await storage.submitCollection(metadata, signature)
})
Monitoreo de la Salud del Servicio
Monitoreo de Salud
Copy
Ask AI
class StorageHealthMonitor {
private storage: BaseMintStorage
private healthStatus: 'healthy' | 'degraded' | 'unhealthy' = 'healthy'