Con el programa Token Extension, puedes crear NFTs y activos digitales usando las extensiones de metadatos. Juntas, estas extensiones (metadata pointer y token metadata) te permiten almacenar cualquier metadato deseado de forma nativa en la cadena. Todo dentro de un almacén de datos clave-valor (key-value) personalizable directamente en el mint account del token, reduciendo costos y complejidad.
Estos pueden ser especialmente útiles para juegos web3, ya que ahora podemos tener estos "campos de metadatos adicionales" dentro de un almacén clave-valor (key-value) en la cadena, permitiendo a los juegos guardar y acceder a estados únicos dentro del propio NFT (como las estadísticas o el inventario de un personaje del juego).
Construcción del programa en la cadena
En esta guía para desarrolladores, demostraremos cómo construir estos NFTs basados en Token Extension y metadatos personalizados usando un programa Anchor. Este programa guardará el nivel y los recursos recolectados de un jugador dentro de un NFT.
Este NFT será creado por el programa Anchor, por lo que es muy fácil de acuñar desde el cliente JavaScript. Cada NFT tendrá una estructura básica proporcionada a través de la interfaz Token Metadata:
- campos predeterminados en la cadena:
name,symbolyuri- el
uries un enlace a un archivo json offchain que contiene los metadatos fuera de la cadena del NFT
- el
- también tendremos "campos adicionales" personalizados que definiremos nosotros
Todos estos campos se guardan usando la extensión de metadatos que apunta al mint account del NFT, haciéndolos accesibles para cualquier persona o programa.
Video y Código Fuente
Puedes encontrar un video explicativo de este ejemplo en el canal de Youtube de Solana Foundation:
Otros casos de uso en juegos
Este tipo de NFTs con metadatos en la cadena personalizables abre muchas posibilidades interesantes para los desarrolladores de juegos. Especialmente porque estos metadatos pueden ser directamente manipulados o gestionados por un programa en la cadena.
Algunos de estos casos de uso relacionados con juegos incluyen:
- guardar el nivel y la XP del jugador
- el arma y la armadura actuales
- la misión actual
- ¡y la lista continúa!
Acuñación del NFT
Para crear el NFT necesitamos realizar los siguientes pasos:
- Crear un mint account
- Inicializar el mint account
- Crear una cuenta de puntero de metadatos
- Inicializar la cuenta de puntero de metadatos
- Crear la cuenta de metadatos
- Inicializar la cuenta de metadatos
- Crear el associated token account
- Acuñar el token en el associated token account
- Congelar la autoridad de acuñación
Código del programa en Rust
Aquí está el código Rust utilizado para acuñar el NFT usando el programa Token Extension:
// calculate the space need for the mint account with the desired extensionslet space = ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::MetadataPointer]).unwrap();// This is the space required for the metadata account.// We put the metadata into the mint account at the end so we// don't need to create and additional account.// Then the metadata pointer points back to the mint account.// Using this technique, only one account is needed for both the mint// information and the metadata.let meta_data_space = 250;let lamports_required = (Rent::get()?).minimum_balance(space + meta_data_space);msg!("Create Mint and metadata account size and cost: {} lamports: {}",space as u64,lamports_required);system_program::create_account(CpiContext::new(ctx.accounts.token_program.to_account_info(),system_program::CreateAccount {from: ctx.accounts.signer.to_account_info(),to: ctx.accounts.mint.to_account_info(),},),lamports_required,space as u64,&ctx.accounts.token_program.key(),)?;// Assign the mint to the token programsystem_program::assign(CpiContext::new(ctx.accounts.token_program.to_account_info(),system_program::Assign {account_to_assign: ctx.accounts.mint.to_account_info(),},),&token_2022::ID,)?;// Initialize the metadata pointer (Need to do this before initializing the mint)let init_meta_data_pointer_ix =spl_token_2022::extension::metadata_pointer::instruction::initialize(&Token2022::id(),&ctx.accounts.mint.key(),Some(ctx.accounts.nft_authority.key()),Some(ctx.accounts.mint.key()),).unwrap();invoke(&init_meta_data_pointer_ix,&[ctx.accounts.mint.to_account_info(),ctx.accounts.nft_authority.to_account_info()],)?;// Initialize the mint cpilet mint_cpi_ix = CpiContext::new(ctx.accounts.token_program.to_account_info(),token_2022::InitializeMint2 {mint: ctx.accounts.mint.to_account_info(),},);token_2022::initialize_mint2(mint_cpi_ix,0,&ctx.accounts.nft_authority.key(),None).unwrap();// We use a PDA as a mint authority for the metadata account because// we want to be able to update the NFT from the program.let seeds = b"nft_authority";let bump = ctx.bumps.nft_authority;let signer: &[&[&[u8]]] = &[&[seeds, &[bump]]];msg!("Init metadata {0}", ctx.accounts.nft_authority.to_account_info().key);// Init the metadata accountlet init_token_meta_data_ix =&spl_token_metadata_interface::instruction::initialize(&spl_token_2022::id(),ctx.accounts.mint.key,ctx.accounts.nft_authority.to_account_info().key,ctx.accounts.mint.key,ctx.accounts.nft_authority.to_account_info().key,"Beaver".to_string(),"BVA".to_string(),"https://arweave.net/MHK3Iopy0GgvDoM7LkkiAdg7pQqExuuWvedApCnzfj0".to_string(),);invoke_signed(init_token_meta_data_ix,&[ctx.accounts.mint.to_account_info().clone(), ctx.accounts.nft_authority.to_account_info().clone()],signer,)?;// Update the metadata account with an additional metadata field in this case the player levelinvoke_signed(&spl_token_metadata_interface::instruction::update_field(&spl_token_2022::id(),ctx.accounts.mint.key,ctx.accounts.nft_authority.to_account_info().key,spl_token_metadata_interface::state::Field::Key("level".to_string()),"1".to_string(),),&[ctx.accounts.mint.to_account_info().clone(),ctx.accounts.nft_authority.to_account_info().clone(),],signer)?;// Create the associated token accountassociated_token::create(CpiContext::new(ctx.accounts.associated_token_program.to_account_info(),associated_token::Create {payer: ctx.accounts.signer.to_account_info(),associated_token: ctx.accounts.token_account.to_account_info(),authority: ctx.accounts.signer.to_account_info(),mint: ctx.accounts.mint.to_account_info(),system_program: ctx.accounts.system_program.to_account_info(),token_program: ctx.accounts.token_program.to_account_info(),},))?;// Mint one token to the associated token account of the playertoken_2022::mint_to(CpiContext::new_with_signer(ctx.accounts.token_program.to_account_info(),token_2022::MintTo {mint: ctx.accounts.mint.to_account_info(),to: ctx.accounts.token_account.to_account_info(),authority: ctx.accounts.nft_authority.to_account_info(),},signer),1,)?;// Freeze the mint authority so no more tokens can be minted to make it an NFTtoken_2022::set_authority(CpiContext::new_with_signer(ctx.accounts.token_program.to_account_info(),token_2022::SetAuthority {current_authority: ctx.accounts.nft_authority.to_account_info(),account_or_mint: ctx.accounts.mint.to_account_info(),},signer),AuthorityType::MintTokens,None,)?;
Código del cliente JavaScript
Llamar al método de acuñación del NFT desde el cliente es muy sencillo:
const nftAuthority = PublicKey.findProgramAddressSync([Buffer.from("nft_authority")],program.programId);const mint = new Keypair();const destinationTokenAccount = getAssociatedTokenAddressSync(mint.publicKey,publicKey,false,TOKEN_2022_PROGRAM_ID);const transaction = await program.methods.mintNft().accounts({signer: publicKey,systemProgram: SystemProgram.programId,tokenProgram: TOKEN_2022_PROGRAM_ID,tokenAccount: destinationTokenAccount,mint: mint.publicKey,rent: web3.SYSVAR_RENT_PUBKEY,associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,nftAuthority: nftAuthority[0]}).signers([mint]).transaction();console.log("transaction", transaction);const txSig = await sendTransaction(transaction, connection, {signers: [mint],skipPreflight: true});console.log(`https://explorer.solana.com/tx/${txSig}?cluster=devnet`);
Ejemplo de inicio rápido
El ejemplo anterior está basado en el Solana Games Preset, que genera un scaffold que incluye un cliente JavaScript y Unity para este juego, incluyendo la configuración para interactuar con el programa Anchor de Solana.
Puedes ejecutarlo tú mismo con el siguiente comando:
npx create-solana-game gameName
Configura tu entorno local
Para ejecutar este ejemplo localmente, deberás asegurarte de haber configurado tu entorno local para el desarrollo en Solana, incluyendo la instalación y configuración del CLI de Anchor. Si aún no lo has hecho, puedes seguir la guía de configuración mencionada anteriormente.
Estructura del proyecto
El proyecto Anchor está estructurado de la siguiente manera:
El punto de entrada está en el archivo lib.rs. Aquí definimos el id del programa y las instrucciones. Las instrucciones están definidas en la carpeta instructions. El estado está definido en la carpeta state.
Las llamadas llegan al archivo lib.rs y luego se reenvían a las instrucciones. Las instrucciones luego acceden al estado para obtener los datos y actualizarlos.
Puedes encontrar la instrucción de acuñación del NFT en la carpeta instructions.
├── src│ ├── instructions│ │ ├── chop_tree.rs│ │ ├── init_player.rs│ │ ├── mint_nft.rs│ │ └── update_energy.rs│ ├── state│ │ ├── game_data.rs│ │ ├── mod.rs│ │ └── player_data.rs│ ├── lib.rs│ └── constants.rs│ └── errors.rs
Programa Anchor
Para terminar de configurar el programa Anchor generado con la herramienta create-solana-game:
cd programpara entrar al directorio del programa- Ejecuta
anchor buildpara compilar el programa - Ejecuta
anchor deploypara desplegar el programa - Copia el id del programa desde la terminal en
lib.rs,anchor.tomly dentro del proyecto Unity enAnchorServicey, si usas JavaScript, en el archivoanchor.ts - Compila y despliega nuevamente
Cliente NextJS
Para terminar de configurar el cliente NextJS generado con la herramienta create-solana-game:
- Copia el
programIdenapp/utils/anchor.ts cd apppara entrar al directorio de la aplicación- Ejecuta
yarn installpara instalar las dependencias de Node - Ejecuta
yarn devpara iniciar el cliente - Después de realizar cambios en el programa Anchor, asegúrate de copiar los tipos
del programa al cliente para poder usarlos. Puedes encontrar los tipos de
TypeScript en la carpeta
target/idl.
Ejecutar este ejemplo localmente
Usar el comando test de Anchor con el flag --detach iniciará y configurará
tu validator de pruebas local de Solana con el programa desplegado (y mantendrá el
validator en ejecución después de que las pruebas finalicen):
cd programanchor test --detach
Luego puedes configurar el Explorador de Solana para usar tu
validator de pruebas local (que se inicia al ejecutar el comando anchor test) para
poder ver las transacciones:
https://explorer.solana.com/?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899
El programa también está desplegado en la red, por lo que puedes probarlo en devnet.
El cliente JavaScript también tiene un botón para acuñar el NFT. Para iniciar el cliente JavaScript:
cd appyarn installyarn dev
Abrir el proyecto Unity
Primero abre el proyecto Unity con Unity Version 2021.3.32.f1 (o similar), luego
abre la escena GameScene o LoginScene y presiona play. Usa el botón de inicio de sesión del editor
en la esquina inferior izquierda.
Si no puedes obtener SOL de devnet, puedes copiar tu dirección desde la consola y seguir las instrucciones de esta guía sobre cómo obtener SOL de devnet
Conectar al validator de pruebas de Solana en Unity
Si quieres evitar tener que preocuparte por mantener SOL en devnet, puedes conectarte a tu validator de pruebas local en ejecución desde dentro de Unity. Simplemente agrega estos enlaces en el objeto de juego wallet holder:
http://localhost:8899ws://localhost:8900
Ejecutar el cliente JavaScript
Para iniciar el cliente JavaScript y poder interactuar con el juego y el programa desde tu navegador web:
- abre el directorio
appdentro del repositorio - instala las dependencias de Node
- ejecuta el comando
devpara iniciar el servidor de desarrollo
cd appyarn installyarn dev
Para comenzar a modificar el programa y conectarte a tu propio programa, sigue los pasos a continuación.
Is this page helpful?