mirror of
https://github.com/INFTF/xahau-course.git
synced 2026-07-27 09:00:35 +00:00
Update m09-smart-contracts.js
This commit is contained in:
@@ -892,69 +892,79 @@ int64_t hook(uint32_t reserved)
|
||||
{
|
||||
id: "m8l5",
|
||||
title: {
|
||||
es: "Parámetros, funciones, namespaces y gestión de Hooks",
|
||||
es: "Parámetros, funciones y gestión de Hooks",
|
||||
en: "",
|
||||
jp: "",
|
||||
},
|
||||
theory: {
|
||||
es: `Los Hooks ofrecen varias herramientas para configuración, organización y gestión avanzada. En esta lección veremos **HookParameters**, **funciones**, **HookNamespace** en profundidad, y cómo gestionar múltiples Hooks en una cuenta.
|
||||
es: `Los Hooks disponen de múltiples funciones con propósitos distintos y de gestión. En esta lección veremos algunos de ellos.
|
||||
|
||||
### HookParameters: Introducir valores al Hook sin recompilar
|
||||
### otxn_param() Parámetros de la transacción que para el Hook
|
||||
|
||||
Los **HookParameters** permiten pasar valores a un Hook **sin necesidad de recompilarlo**. Puedes incluirlos en cualquier transacción y un Hook podrá leer estos valores si los necesita para su lógica:
|
||||
|
||||
- Cada parámetro tiene un **HookParameterName** (clave) y un **HookParameterValue** (valor)
|
||||
- Ambos son cadenas hexadecimales
|
||||
- Dentro del Hook, se leen con \`hook_param()\`
|
||||
|
||||
**Casos de uso de parámetros**:
|
||||
- Umbrales configurables (monto mínimo, máximo)
|
||||
- Direcciones de destino configurables
|
||||
- Feature flags (activar/desactivar funcionalidades)
|
||||
- Cualquier valor que quieras leer o cambiar sin recompilar el WASM
|
||||
|
||||
### hook_param() — Leer parámetros
|
||||
|
||||
Dentro del Hook, usas \`hook_param()\` para leer un parámetro por su nombre:
|
||||
\`otxn_param()\` lee parámetros incluidos **en la transacción que está ejecutando el Hook** en ese preciso momento (la transacción originante). A diferencia de \`hook_param\`, estos valores los envía quien realiza la transacción y **cambian en cada llamada**.
|
||||
|
||||
\`\`\`c
|
||||
uint8_t value[32];
|
||||
int64_t val_len = hook_param(SBUF(value), "MI_PARAM", 8);
|
||||
// Firma de la función
|
||||
int64_t otxn_param(
|
||||
uint32_t write_ptr, // buffer donde escribir el valor
|
||||
uint32_t write_len, // tamaño del buffer (≥ 32 bytes recomendado)
|
||||
uint32_t read_ptr, // buffer con el nombre del parámetro
|
||||
uint32_t read_len // longitud del nombre
|
||||
);
|
||||
\`\`\`
|
||||
|
||||
Si el parámetro existe, \`hook_param()\` devuelve la longitud del valor. Si no existe, devuelve un número negativo.
|
||||
**¿Cuándo usar otxn_param?**
|
||||
- Datos dinámicos que el emisor quiere pasar al Hook en cada transacción
|
||||
- Instrucciones de acción: "modo de operación", "identificador de referencia", "código de autorización"
|
||||
- Cualquier valor que dependa de la transacción concreta, no de la configuración del Hook
|
||||
|
||||
### HookNamespace en profundidad
|
||||
### Diferencia clave entre hook_param y otxn_param
|
||||
|
||||
El **HookNamespace** es un identificador de 32 bytes (64 caracteres hex) que:
|
||||
| | \`hook_param()\` | \`otxn_param()\` |
|
||||
|---|---|---|
|
||||
| **Origen** | SetHook (instalación) | Transacción que activa el Hook |
|
||||
| **Quién lo pone** | El instalador del Hook | El emisor de cada tx |
|
||||
| **Cuándo cambia** | Solo al actualizar el Hook | En cada transacción |
|
||||
| **Uso típico** | Configuración estática | Instrucciones dinámicas |
|
||||
|
||||
- **Aísla el estado** de cada Hook en la cuenta
|
||||
- Dos Hooks con **distinto namespace** no comparten estado
|
||||
- Dos Hooks con el **mismo namespace** comparten estado (útil para colaboración entre Hooks)
|
||||
### Cómo incluir HookParameters en una transacción desde JavaScript
|
||||
|
||||
**Cómo elegir un namespace**:
|
||||
- Usa un hash del nombre de tu Hook para namespaces únicos
|
||||
- Usa un namespace compartido si necesitas que dos Hooks lean/escriban los mismos datos
|
||||
- El namespace \`"0".repeat(64)\` es el namespace por defecto
|
||||
Los parámetros de transacción se añaden en el campo \`HookParameters\` de cualquier tx que active el Hook. El nombre y el valor deben estar en hexadecimal:
|
||||
|
||||
\`\`\`javascript
|
||||
// Nombre "ACCION" (hex: 414343494F4E) con valor "01" (hex)
|
||||
const tx = {
|
||||
TransactionType: "Payment",
|
||||
Account: wallet.address,
|
||||
Destination: hookAccount,
|
||||
Amount: "1000000",
|
||||
HookParameters: [
|
||||
{
|
||||
HookParameter: {
|
||||
HookParameterName: "414343494F4E", // "ACCION"
|
||||
HookParameterValue: "01",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
\`\`\`
|
||||
|
||||
### Recursos para hacer tu vida más sencilla usando Hooks
|
||||
|
||||
Xahau permite **hasta 10 Hooks** por cuenta:
|
||||
|
||||
A lo largo de tus primeros pasos desarrollando Hooks, te encontrarás con necesidades, como traducir parámetros a valores legibles para el entorno de desarrollo de Hooks. Aquí tienes algunas páginas web que te ayudarán:
|
||||
A lo largo de tus primeros pasos desarrollando Hooks, te encontrarás con necesidades como traducir parámetros a valores legibles. Aquí tienes algunas páginas útiles:
|
||||
- [Calculadora de HookOn](https://richardah.github.io/xrpl-hookon-calculator/): Calcula fácilmente el campo HookOn y HookCanEmit
|
||||
- [Visualizador HEX](https://transia-rnd.github.io/xrpl-hex-visualizer/): Introduce el string o hex que quieres traducir y esta página nte lo traducirá a distintos formatos.
|
||||
- [Visualizador de tiempo](https://transia-rnd.github.io/xrpl-time-visualizer/): Como ocurre en Unix, Xahau tiene su propio formato de tiempo basado en segundos desde el 1 de enero de 2000. Esta página te ayudará a traducir entre el formato de tiempo de Xahau y fechas legibles.
|
||||
- [Servicios Hooks](https://hooks.services/): Página que contiene diferentes traductores de valores y formatos relacionados con Hooks.
|
||||
- [Constructor de Transacciones](https://tx-builder.xahau.tools/): Construir en C transacciones que emitir puede resultar tedioso. Esta página te permite generar una traducción directamente en C para tus transacciones JSON.
|
||||
- [XRPLWin Hook tools](https://xahau-testnet.xrplwin.com/tools): Herramientas varias para Hooks, incluyendo distintas formas de instalar un Hook de manera visual.`,
|
||||
- [Visualizador HEX](https://transia-rnd.github.io/xrpl-hex-visualizer/): Traduce strings a hex y viceversa en múltiples formatos
|
||||
- [Visualizador de tiempo](https://transia-rnd.github.io/xrpl-time-visualizer/): Traduce entre el formato de tiempo de Xahau (Ripple Epoch) y fechas legibles
|
||||
- [Servicios Hooks](https://hooks.services/): Traductores de valores y formatos relacionados con Hooks
|
||||
- [Constructor de Transacciones](https://tx-builder.xahau.tools/): Genera código C para transacciones a emitir a partir de su JSON
|
||||
- [XRPLWin Hook tools](https://xahau-testnet.xrplwin.com/tools): Herramientas visuales para instalar y gestionar Hooks`,
|
||||
en: "",
|
||||
jp: "",
|
||||
},
|
||||
codeBlocks: [
|
||||
{
|
||||
title: {
|
||||
es: "Hook que lee un monto mínimo desde un parámetro",
|
||||
es: "Hook que lee un otxn_param y lo muestra con TRACE",
|
||||
en: "",
|
||||
jp: "",
|
||||
},
|
||||
@@ -962,88 +972,178 @@ A lo largo de tus primeros pasos desarrollando Hooks, te encontrarás con necesi
|
||||
code: `#include "hookapi.h"
|
||||
|
||||
/**
|
||||
* Hook: configurable_min.c
|
||||
* Rechaza pagos menores a un mínimo configurable.
|
||||
* El mínimo se pasa como HookParameter llamado "MIN"
|
||||
* (en hex: 4D494E).
|
||||
* El valor del parámetro son los drops en formato int64.
|
||||
* Hook: otxn_param_demo.c
|
||||
*
|
||||
* Lee el parámetro "ACCION" de la transacción que activa el Hook
|
||||
* y muestra su valor por el Debug Stream con trace().
|
||||
*
|
||||
* Para probarlo, envía una transacción con HookParameters:
|
||||
* HookParameterName: "414343494F4E" (= "ACCION" en hex)
|
||||
* HookParameterValue: "01" (cualquier valor hex)
|
||||
*
|
||||
* Convierte strings a hex en: https://hooks.services/tools/string-to-hex
|
||||
*/
|
||||
|
||||
int64_t hook(uint32_t reserved) {
|
||||
int64_t hook(uint32_t reserved)
|
||||
{
|
||||
_g(1, 1);
|
||||
|
||||
// Solo filtrar pagos (tipo 0)
|
||||
int64_t tt = otxn_type();
|
||||
if (tt != 0) {
|
||||
accept(SBUF("configurable_min: No es un pago."), __LINE__);
|
||||
}
|
||||
trace(SBUF("otxn_param_demo: hook() iniciado"), 0);
|
||||
|
||||
// Leer el parámetro "MIN" (3 bytes: 0x4D 0x49 0x4E)
|
||||
uint8_t min_buf[8] = { 0 };
|
||||
int64_t param_len = hook_param(
|
||||
SBUF(min_buf),
|
||||
"MIN", 3
|
||||
// Nombre del parámetro que queremos leer: "ACCION" → hex 414343494F4E
|
||||
uint8_t param_name[] = { 0x41U, 0x43U, 0x43U, 0x49U, 0x4FU, 0x4EU };
|
||||
uint8_t param_value[32] = { 0 };
|
||||
|
||||
// Leer el parámetro de la transacción originante
|
||||
int64_t value_len = otxn_param(
|
||||
SBUF(param_value), // buffer de salida
|
||||
SBUF(param_name) // nombre del parámetro a buscar
|
||||
);
|
||||
|
||||
// Si el parámetro no existe, usar 1 XAH por defecto
|
||||
int64_t min_drops = 1000000; // 1 XAH
|
||||
if (param_len == 8) {
|
||||
min_drops = *((int64_t*)min_buf);
|
||||
trace_num(SBUF("otxn_param_demo: bytes leidos (neg=no existe): "), value_len);
|
||||
|
||||
if (value_len == DOESNT_EXIST)
|
||||
{
|
||||
// La transacción no incluye el parámetro "ACCION"
|
||||
trace(SBUF("otxn_param_demo: parametro ACCION no encontrado"), 0);
|
||||
accept(SBUF("otxn_param_demo: sin parametro, nada que hacer"), __LINE__);
|
||||
}
|
||||
|
||||
// Obtener el monto del pago
|
||||
unsigned char amount_buf[48];
|
||||
int64_t amount_len = otxn_field(SBUF(amount_buf), sfAmount);
|
||||
|
||||
// Solo XAH nativo
|
||||
if (amount_len != 8) {
|
||||
accept(SBUF("configurable_min: No es XAH."), __LINE__);
|
||||
if (value_len < 0)
|
||||
{
|
||||
// Otro error (TOO_BIG, OUT_OF_BOUNDS, etc.)
|
||||
trace_num(SBUF("otxn_param_demo: error al leer otxn_param: "), value_len);
|
||||
rollback(SBUF("otxn_param_demo: error leyendo parametro"), __LINE__);
|
||||
}
|
||||
|
||||
int64_t drops = AMOUNT_TO_DROPS(amount_buf);
|
||||
// Parámetro encontrado — mostrarlo en el Debug Stream
|
||||
|
||||
if (drops < min_drops) {
|
||||
rollback(
|
||||
SBUF("configurable_min: Pago bajo el mínimo."),
|
||||
__LINE__
|
||||
);
|
||||
// Como texto (si el valor es un string ASCII)
|
||||
trace(SBUF("otxn_param_demo: valor del parametro ACCION (texto): "), 0);
|
||||
trace(param_value, (uint32_t)value_len, 0);
|
||||
|
||||
// Como hexadecimal (siempre legible independientemente del tipo)
|
||||
trace(SBUF("otxn_param_demo: valor del parametro ACCION (hex): "), 0);
|
||||
trace(param_value, (uint32_t)value_len, 1);
|
||||
|
||||
// Si el valor es un entero de 1 byte podemos leerlo directamente
|
||||
if (value_len == 1)
|
||||
{
|
||||
int64_t accion_num = (int64_t)param_value[0];
|
||||
trace_num(SBUF("otxn_param_demo: accion como numero: "), accion_num);
|
||||
|
||||
if (accion_num == 1)
|
||||
trace(SBUF("otxn_param_demo: accion 01 → modo activado"), 0);
|
||||
else if (accion_num == 2)
|
||||
trace(SBUF("otxn_param_demo: accion 02 → modo desactivado"), 0);
|
||||
else
|
||||
trace(SBUF("otxn_param_demo: accion desconocida"), 0);
|
||||
}
|
||||
|
||||
accept(SBUF("configurable_min: Pago aceptado."), __LINE__);
|
||||
accept(SBUF("otxn_param_demo: parametro leido y trazado"), __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t cbak(uint32_t reserved) {
|
||||
int64_t cbak(uint32_t reserved)
|
||||
{
|
||||
_g(1, 1);
|
||||
return 0;
|
||||
}`,
|
||||
},
|
||||
{
|
||||
title: {
|
||||
es: "Script para instalar un Hook con parámetros personalizados",
|
||||
es: "Enviar una transacción con HookParameters desde JavaScript",
|
||||
en: "",
|
||||
jp: "",
|
||||
},
|
||||
language: "javascript",
|
||||
code: `const { Client, Wallet } = require("xahau");
|
||||
const fs = require("fs");
|
||||
code: `require("dotenv").config();
|
||||
const { Client, Wallet } = require("xahau");
|
||||
|
||||
async function enviarConParametro() {
|
||||
const client = new Client("wss://xahau-test.net");
|
||||
await client.connect();
|
||||
|
||||
const wallet = Wallet.fromSeed(process.env.WALLET_SEED, { algorithm: "secp256k1" });
|
||||
|
||||
// La cuenta que tiene el Hook instalado (puede ser la misma u otra)
|
||||
const HOOK_ACCOUNT = "rDireccionConHookAqui";
|
||||
|
||||
// Convertir el nombre y valor del parámetro a hexadecimal
|
||||
// "ACCION" → 414343494F4E (usa https://hooks.services/tools/string-to-hex)
|
||||
const paramName = Buffer.from("ACCION").toString("hex").toUpperCase();
|
||||
const paramValue = "01"; // Valor 01 = modo activado
|
||||
|
||||
const tx = {
|
||||
TransactionType: "Payment",
|
||||
Account: wallet.address,
|
||||
Destination: HOOK_ACCOUNT,
|
||||
Amount: "1000000", // 1 XAH en drops
|
||||
HookParameters: [
|
||||
{
|
||||
HookParameter: {
|
||||
HookParameterName: paramName, // "414343494F4E"
|
||||
HookParameterValue: paramValue, // "01"
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
console.log("Enviando Payment con HookParameters...");
|
||||
console.log(" Nombre param (hex): ", paramName, " = ACCION");
|
||||
console.log(" Valor param (hex): ", paramValue);
|
||||
|
||||
const prepared = await client.autofill(tx);
|
||||
const signed = wallet.sign(prepared);
|
||||
const result = await client.submitAndWait(signed.tx_blob);
|
||||
|
||||
const txResult = result.result.meta.TransactionResult;
|
||||
console.log("Resultado:", txResult);
|
||||
|
||||
if (txResult === "tesSUCCESS") {
|
||||
console.log("TX enviada. Revisa el Debug Stream en Hooks Builder");
|
||||
console.log("Deberías ver las trazas del Hook con el valor del parámetro.");
|
||||
}
|
||||
|
||||
await client.disconnect();
|
||||
}
|
||||
|
||||
// Enviar con acción 01
|
||||
enviarConParametro();`,
|
||||
},
|
||||
{
|
||||
title: {
|
||||
es: "Script para instalar el Hook con hook_param de configuración",
|
||||
en: "",
|
||||
jp: "",
|
||||
},
|
||||
language: "javascript",
|
||||
code: `require("dotenv").config();
|
||||
const { Client, Wallet } = require("xahau");
|
||||
const fs = require("fs");
|
||||
const crypto = require("crypto");
|
||||
|
||||
async function deployHookWithParams() {
|
||||
const client = new Client("wss://xahau-test.net");
|
||||
await client.connect();
|
||||
|
||||
const wallet = Wallet.fromSeed("sEdVxxxTuSeedDeTestnet", {algorithm: 'secp256k1'});
|
||||
const wallet = Wallet.fromSeed(process.env.WALLET_SEED, { algorithm: "secp256k1" });
|
||||
|
||||
// Leer el WASM compilado
|
||||
const wasmBytes = fs.readFileSync("./build/configurable_min.wasm");
|
||||
const wasmBytes = fs.readFileSync("./build/otxn_param_demo.wasm");
|
||||
const hookBinary = wasmBytes.toString("hex").toUpperCase();
|
||||
|
||||
// Definir parámetros del Hook
|
||||
// "MIN" en hex = 4D494E
|
||||
// Valor: 5000000 drops (5 XAH) como int64 little-endian
|
||||
const minDrops = BigInt(5000000);
|
||||
const minBuffer = Buffer.alloc(8);
|
||||
minBuffer.writeBigInt64LE(minDrops);
|
||||
const minValueHex = minBuffer.toString("hex").toUpperCase();
|
||||
// Namespace a partir del nombre del Hook (SHA-256)
|
||||
const namespace = crypto
|
||||
.createHash("sha256")
|
||||
.update("otxn_param_demo")
|
||||
.digest("hex")
|
||||
.toUpperCase();
|
||||
|
||||
// HookParameters de configuración (hook_param, NO otxn_param):
|
||||
// Estos se almacenan junto al Hook y se leen con hook_param() desde el C
|
||||
// "CFG" en hex = 434647
|
||||
const cfgName = Buffer.from("CFG").toString("hex").toUpperCase();
|
||||
const cfgValue = "FF"; // valor de configuración de ejemplo
|
||||
|
||||
const setHook = {
|
||||
TransactionType: "SetHook",
|
||||
@@ -1052,16 +1152,16 @@ async function deployHookWithParams() {
|
||||
{
|
||||
Hook: {
|
||||
CreateCode: hookBinary,
|
||||
HookOn: "0000000000000000",
|
||||
HookNamespace:
|
||||
"AABBCCDD".repeat(8), // Namespace personalizado
|
||||
HookOn: "0".repeat(64),
|
||||
HookNamespace: namespace,
|
||||
HookApiVersion: 0,
|
||||
Flags: 1,
|
||||
// Estos son los hook_param (config estática)
|
||||
HookParameters: [
|
||||
{
|
||||
HookParameter: {
|
||||
HookParameterName: "4D494E", // "MIN"
|
||||
HookParameterValue: minValueHex,
|
||||
HookParameterName: cfgName, // "CFG"
|
||||
HookParameterValue: cfgValue,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1071,15 +1171,15 @@ async function deployHookWithParams() {
|
||||
};
|
||||
|
||||
const prepared = await client.autofill(setHook);
|
||||
const signed = wallet.sign(prepared);
|
||||
const result = await client.submitAndWait(signed.tx_blob);
|
||||
const signed = wallet.sign(prepared);
|
||||
const result = await client.submitAndWait(signed.tx_blob);
|
||||
|
||||
console.log("Resultado:", result.result.meta.TransactionResult);
|
||||
|
||||
if (result.result.meta.TransactionResult === "tesSUCCESS") {
|
||||
console.log("Hook desplegado con parámetro MIN =", Number(minDrops), "drops");
|
||||
console.log("(equivalente a", Number(minDrops) / 1000000, "XAH)");
|
||||
}
|
||||
console.log("Resultado SetHook:", result.result.meta.TransactionResult);
|
||||
console.log("Namespace:", namespace);
|
||||
console.log("hook_param 'CFG' configurado con valor:", cfgValue);
|
||||
console.log("");
|
||||
console.log("Ahora puedes enviar transacciones con otxn_param 'ACCION'");
|
||||
console.log("y el Hook los leerá con otxn_param() en tiempo de ejecucion.");
|
||||
|
||||
await client.disconnect();
|
||||
}
|
||||
@@ -1089,27 +1189,27 @@ deployHookWithParams();`,
|
||||
],
|
||||
slides: [
|
||||
{
|
||||
title: { es: "HookParameters", en: "", jp: "" },
|
||||
title: { es: "hook_param vs otxn_param", en: "", jp: "" },
|
||||
content: {
|
||||
es: "Configuracion sin recompilar el WASM\n\n• Se definen al instalar con SetHook\n• HookParameterName + HookParameterValue (hex)\n• Se leen con hook_param() dentro del Hook\n• Ideal para umbrales, direcciones, feature flags",
|
||||
es: "Dos sistemas de parámetros distintos:\n\nhook_param() — configuración estática\n• Se define en SetHook al instalar\n• Almacenado junto al Hook en el ledger\n• Cambia solo al actualizar el Hook\n• Ideal para umbrales, direcciones fijas\n\notxn_param() — datos dinámicos\n• Viene en la transacción que activa el Hook\n• Lo envía el emisor de cada tx\n• Cambia en cada ejecución\n• Ideal para instrucciones, modos, referencias",
|
||||
en: "",
|
||||
jp: "",
|
||||
},
|
||||
visual: "🎛️",
|
||||
},
|
||||
{
|
||||
title: { es: "Namespace y multiples Hooks", en: "", jp: "" },
|
||||
title: { es: "otxn_param: firma y retornos", en: "", jp: "" },
|
||||
content: {
|
||||
es: "HookNamespace:\n• Hash del nombre del Hook → namespace unico\n• Namespace compartido → colaboracion entre Hooks\n\nHasta 10 Hooks por cuenta:\n• Posiciones 0 a 9, ejecucion en orden\n• rollback() detiene los siguientes\n• Cada Hook con su propio HookOn",
|
||||
es: "int64_t otxn_param(\n write_ptr, write_len, // buffer salida\n read_ptr, read_len // nombre del param\n);\n\nRetornos:\n• > 0 → bytes escritos (encontrado)\n• DOESNT_EXIST → no está en la tx\n• TOO_SMALL → nombre vacío\n• TOO_BIG → nombre > 32 bytes\n• OUT_OF_BOUNDS → punteros inválidos\n\nNombre y valor en HEX en la transacción",
|
||||
en: "",
|
||||
jp: "",
|
||||
},
|
||||
visual: "📚",
|
||||
visual: "📨",
|
||||
},
|
||||
{
|
||||
title: { es: "Recursos utiles para Hooks", en: "", jp: "" },
|
||||
title: { es: "Namespace y recursos", en: "", jp: "" },
|
||||
content: {
|
||||
es: "• Calculadora HookOn/HookCanEmit\n• Visualizador HEX (string ↔ hex)\n• Visualizador de tiempo (Ripple Epoch)\n• hooks.services (traductores de valores)\n• Constructor de transacciones C\n• XRPLWin Hook tools",
|
||||
es: "HookNamespace (32 bytes hex):\n• Distinto namespace = estado aislado\n• Mismo namespace = estado compartido\n• SHA-256 del nombre → namespace único\n\nRecursos:\n• hooks.services → string ↔ hex\n• HookOn calculator\n• Visualizador tiempo (Ripple Epoch)\n• tx-builder.xahau.tools → C desde JSON",
|
||||
en: "",
|
||||
jp: "",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user