El equipo editorial de Bitaigen considera que este artículo organiza de manera sistemática los pasos completos para escribir, desplegar y acuñar un NFT en los entornos Flow e IPFS. Desde la instalación de herramientas hasta la implementación del contrato en Cadence, explicamos cada punto para que los desarrolladores puedan ponerse en marcha rápidamente. Los casos prácticos que seguirán serán aún más interesantes, por lo que recomendamos una lectura cuidadosa.
Tutorial para crear contratos NFT y acuñar tokens con Flow e IPFS
En la blockchain Flow combinada con IPFS, es posible escribir un contrato en Cadence, desplegarlo en un simulador o en la testnet, subir los activos a Pinata para obtener el CID y, finalmente, acuñar el NFT vinculando sus metadatos al token. Todo el proceso queda cubierto en este tutorial paso a paso.
Configuración del entorno
- Instalar Flow CLI
- macOS
```bash
brew install flow-cli
```
- Linux
```bash
sh -ci "$(curl -fsSL https://storage.googleapis.com/flow-cli/install.sh)"
```
- Windows
```powershell
iex "& { $(irm 'https://storage.googleapis.com/flow-cli/install.ps1') }"
```
- Preparar el almacenamiento de activos en IPFS
- Regístrate en Pinata con una cuenta gratuita y obtén tu *API Key*. En la segunda parte del tutorial se usará la API de Pinata; en esta sección solo subiremos los archivos manualmente a través del sitio web.
- Instalar Node.js y un editor de código
- Recomendamos Visual Studio Code y la instalación del plugin de sintaxis para Cadence, lo que facilita el resaltado de código de contratos inteligentes.
- Crear el directorio del proyecto
```bash
mkdir pinata-party
cd pinata-party
flow project init
```
Al inicializar el proyecto se genera un archivo `flow.json`, que configuraremos más adelante.
- Organizar la estructura de archivos
```
pinata-party/
├─ cadence/
│ └─ contracts/
│ └─ PinataPartyContract.cdc
├─ transactions/
└─ scripts/
```
Todo el código relacionado con Flow se coloca en `cadence/contracts`, mientras que los scripts de transacciones y de lectura se ubican en `transactions` y `scripts`, respectivamente.
Nota de localización (Pagos y KYC)
Para la adquisición de servicios de hosting, dominios o herramientas de desarrollo, los usuarios en Latinoamérica pueden utilizar los siguientes métodos de pago locales:
- México: SPEI (transferencia bancaria)
- Colombia: PSE o Nequi
- Argentina: Mercado Pago
En cuanto a la verificación de identidad (KYC), se recomienda presentar:
- México: INE (credencial para votar)
- Otros países LATAM: DNI (documento nacional de identidad)
Si en algún momento necesitas comprar recursos pagados en dólares, recuerda convertir el importe a la moneda local usando tasas aproximadas: 1 USD ≈ 18 MXN, 1 USD ≈ 4 000 COP, 1 USD ≈ 1 000 ARS. Además, mantén presente tus obligaciones fiscales; en México, por ejemplo, los ingresos por la venta de NFTs pueden estar sujetos al ISR y al IVA, por lo que es aconsejable consultar a un contador.
Configurar `flow.json` para el simulador
Agrega la ruta del contrato dentro de `flow.json`:
```json
"contracts": {
"PinataPartyContract": "./cadence/contracts/PinataPartyContract.cdc"
},
"deployments": {
"emulator": {
"emulator-account": ["PinataPartyContract"]
}
}
```
Con esta configuración, el Flow CLI sabrá que debe desplegar `PinataPartyContract` en el simulador local.
Escribir el contrato NFT
Estructura central del contrato
```cadence
pub contract PinataPartyContract {
// Definición del recurso NFT
pub resource NFT {
pub let id: UInt64
init(initID: UInt64) {
self.id = initID
}
}
// Interfaz para recibir NFTs
pub resource interface NFTReceiver {
pub fun deposit(token: @NFT, metadata: {String: String})
pub fun getIDs(): [UInt64]
pub fun idExists(id: UInt64): Bool
pub fun getMetadata(id: UInt64): {String: String}
}
// Implementación de la colección de NFTs
pub resource Collection: NFTReceiver {
// Almacén de NFTs
pub var ownedNFT: @{UInt64: NFT}
// Mapeo de metadatos asociados
pub var metadataObjs: {UInt64: {String: String}}
init() {
self.ownedNFT <- {}
self.metadataObjs = {}
}
// Retirar NFT
pub fun withdraw(withdrawID: UInt64): @NFT {
let token <- self.ownedNFT.remove(key: withdrawID)!
return <-token
}
// Depositar NFT y enlazar metadatos
pub fun deposit(token: @NFT, metadata: {String: String}) {
self.ownedNFT[token.id] <-! token
self.metadataObjs[token.id] = metadata
}
pub fun idExists(id: UInt64): Bool {
return self.ownedNFT[id] != nil
}
pub fun getIDs(): [UInt64] {
return self.ownedNFT.keys
}
pub fun getMetadata(id: UInt64): {String: String} {
return self.metadataObjs[id]!
}
destroy() {
destroy self.ownedNFT
}
}
// Función de fábrica para crear una colección vacía
pub fun createEmptyCollection(): @Collection {
return <-create Collection()
}
// Recurso minter (acuñador) de NFTs
pub resource NFTMinter {
pub var idCount: UInt64
init() {
self.idCount = 1
}
pub fun mintNFT(): @NFT {
let newNFT <- create NFT(initID: self.idCount)
self.idCount = self.idCount + 1
return <-newNFT
}
}
// Inicializador del contrato: despliega la colección, publica la interfaz y guarda el minter
init() {
// Crear y guardar una colección vacía para el deployer
self.account.save(<-self.createEmptyCollection(), to: /storage/NFTCollection)
// Publicar la colección como capacidad NFTReceiver
self.account.link<&Collection{NFTReceiver}>(/public/NFTReceiver, target: /storage/NFTCollection)
// Guardar el recurso NFTMinter; solo el creador del contrato tiene acceso
self.account.save(<-create NFTMinter(), to: /storage/NFTMinter)
}
}
```
Puntos clave
- El recurso NFT solo contiene el identificador único `id`.
- La interfaz NFTReceiver expone cuatro métodos que pueden ser invocados desde fuera: `deposit`, `getIDs`, `idExists` y `getMetadata`.
- La Collection implementa esa interfaz y mantiene un diccionario `metadataObjs` para almacenar los metadatos de cada NFT.
- El recurso NFTMinter se encarga de incrementar `idCount` y generar nuevos NFTs.
- En el bloque `init()` se crean automáticamente la colección vacía, la capacidad pública y el minter al desplegar el contrato.
Desplegar el contrato
- Usa Flow Playground o el simulador local para desplegar.
- En el simulador local ejecuta:
```bash
flow project start-emulator # Inicia el simulador local
flow project deploy # Despliega el contrato
```
Si todo sale bien, verás en la consola algo similar a:
```
Deploying 1 contracts for accounts: emulator-account.PinataPartyContract -> 0xf8d6e0586b0a20c7
```
Acuñar un NFT (script de transacción)
Crear el archivo de transacción
En el directorio `transactions` crea `MintPinataParty.cdc` con el siguiente contenido:
```cadence
import PinataPartyContract from 0xf8d6e058b0a20c7
transaction {
// Referencias al receptor y al minter
let receiverRef: &{PinataPartyContract.NFTReceiver}
let minterRef: &PinataPartyContract.NFTMinter
prepare(acct: AuthAccount) {
// Obtener la capacidad pública NFTReceiver
self.receiverRef = acct.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver)
.borrow()
?? panic("Could not borrow receiver reference")
// Obtener el recurso privado NFTMinter
self.minterRef = acct.borrow<&PinataPartyContract.NFTMinter>(from: /storage/NFTMinter)
?? panic("Could not borrow minter reference")
}
execute {
// Ejemplo de metadatos (reemplaza el CID por el que corresponda)
let metadata: {String: String} = {
"name": "The Big Swing",
"swing_velocity": "29",
"swing_angle": "45",
"rating": "5",
"uri": "ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6"
}
// Acuñar NFT y depositarlo en la colección
let newNFT <- self.minterRef.mintNFT()
self.receiverRef.deposit(token: <-newNFT, metadata: metadata)
log("NFT minted and deposited")
}
}
```
Observaciones
- La dirección en la línea `import` debe coincidir con la que obtuvo tu despliegue.
- En el campo `uri` del objeto `metadata` se incluye el prefijo `ipfs://` seguido del CID que Pinata te entregó al subir el archivo.
- La llamada a `deposit` escribe simultáneamente el NFT y sus metadatos en la colección del usuario.
Generar claves de cuenta y actualizar `flow.json`
```bash
flow keys generate
```
El comando devolverá una `privateKey` y una `publicKey`. Copia la clave privada en la sección correspondiente de `flow.json`:
```json
"accounts": {
"emulator-account": {
"address": "0xf8d6e0586b0a20c7",
"privateKey": "TU_CLAVE_PRIVADA_AQUI",
"chain": "flow-emulator",
"sigAlgorithm": "ECDSA_P256",
"hashAlgorithm": "SHA3_256"
}
}
```
Recomendación de seguridad: nunca publiques tu clave privada en repositorios públicos. Añade `flow.json` a tu archivo `.gitignore` para evitar filtraciones accidentales.
Enviar la transacción
```bash
flow transactions send --code ./transactions/MintPinataParty.cdc --signer emulator-account
```
Si la operación es exitosa, la CLI mostrará un ID de transacción y el NFT quedará guardado en la colección de la cuenta firmante.
Consultar metadatos de un NFT (script de solo lectura)
Crea en `scripts` el archivo `CheckTokenMetadata.cdc`:
```cadence
import PinataPartyContract from 0xf8d6e0586b0a20c7
pub fun main(): {String: String} {
let nftOwner = getAccount(0xf8d6e0586b0a20c7)
let capability = nftOwner.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver)
let receiverRef = capability.borrow()
?? panic("Could not borrow the receiver reference")
return receiverRef.getMetadata(id: 1)
}
```
Ejecuta el script:
```bash
flow scripts execute ./scripts/CheckTokenMetadata.cdc
```
La salida esperada será similar a:
```
{"name":"The Big Swing","swing_velocity":"29","swing_angle":"45","rating":"5","uri":"ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6"}
```
Con esto, has completado todo el flujo: creación, acuñación y consulta de un NFT en Flow enlazado a un recurso almacenado en IPFS.
---
Resumen
- Se escribe un contrato NFT en Cadence siguiendo el estándar de Flow.
- Se suben los archivos multimedia a Pinata, se obtiene el CID y se inserta en el campo `uri` del metadata.
- Los scripts de transacción escriben el NFT y sus metadatos en la cadena; los scripts de lectura permiten recuperar esa información.
- Este tutorial cubre la configuración del entorno, desarrollo del contrato, despliegue, acuñación y consulta, sentando las bases para crear interfaces front‑end o integrar mercados secundarios.

---
*Esta traducción fue patrocinada por Cell Network. Enlace al artículo original: https://medium.com/pinata/how-to-create-nfts-like-nba-top-shot-with-flow-and-ipfs-701296944bf*
Lectura Relacionada
- GameFi: origen, mecánicas, DeFi, NFT y monedas con potencial
- GameFi, Metaverso y NFT: Inversión en Solana y Avalanche
- Guía para comprar y vender NFT en la testnet de OpenSea
💡 Regístrate en Binance con el código B2345 para el descuento máximo en comisiones. Ver guía completa Binance.