Cómo usar el protocolo FeatherScript desde aplicaciones externas, indexadores o scripts.
Integración directa con viem
import { createPublicClient, createWalletClient, http, stringToHex } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { robinhoodChain } from './chains';
import { INSCRIPTION_REGISTRY_ABI, INSCRIPTION_REGISTRY_ADDRESS } from './contract';
const publicClient = createPublicClient({
chain: robinhoodChain,
transport: http(),
});
const walletClient = createWalletClient({
chain: robinhoodChain,
transport: http(),
account: privateKeyToAccount('0x...'),
});
// Inscribir
const hash = await walletClient.writeContract({
address: INSCRIPTION_REGISTRY_ADDRESS!,
abi: INSCRIPTION_REGISTRY_ABI,
functionName: 'inscribe',
args: [stringToHex('Hello, permanent world!')],
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
// Leer
const [creator, timestamp, blockNumber, data] = await publicClient.readContract({
address: INSCRIPTION_REGISTRY_ADDRESS!,
abi: INSCRIPTION_REGISTRY_ABI,
functionName: 'getInscription',
args: [1n],
});
Integración con ethers.js v6
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://rpc.mainnet.chain.robinhood.com');
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const abi = [
'function inscribe(bytes calldata data)',
'function getInscription(uint256) view returns (address, uint256, uint256, bytes)',
'function inscriptionCount() view returns (uint256)',
'event Inscribed(uint256 indexed inscriptionId, address indexed creator, bytes data, uint256 timestamp, uint256 blockNumber)',
];
const registry = new ethers.Contract(CONTRACT_ADDRESS, abi, wallet);
// Inscribir
const tx = await registry.inscribe(ethers.toUtf8Bytes('My inscription'));
const receipt = await tx.wait();
// Leer
const count = await registry.inscriptionCount();
const [creator, timestamp, blockNumber, data] = await registry.getInscription(count);
Indexador de eventos
Para historial completo con txHash, implementa un indexador que escuche Inscribed:
import { createPublicClient, http, parseAbiItem } from 'viem';
import { robinhoodChain } from './chains';
const client = createPublicClient({
chain: robinhoodChain,
transport: http(),
});
const logs = await client.getLogs({
address: REGISTRY_ADDRESS,
event: parseAbiItem(
'event Inscribed(uint256 indexed inscriptionId, address indexed creator, bytes data, uint256 timestamp, uint256 blockNumber)'
),
fromBlock: DEPLOY_BLOCK,
toBlock: 'latest',
});
for (const log of logs) {
const { inscriptionId, creator, data, timestamp, blockNumber } = log.args;
const txHash = log.transactionHash;
const blockHash = log.blockHash;
// Guardar en DB: { inscriptionId, creator, data, timestamp, blockNumber, txHash }
}
Esquema sugerido para base de datos
CREATE TABLE inscriptions (
inscription_id BIGINT PRIMARY KEY,
creator VARCHAR(42) NOT NULL,
data BYTEA NOT NULL,
timestamp BIGINT NOT NULL,
block_number BIGINT NOT NULL,
tx_hash VARCHAR(66) NOT NULL UNIQUE,
block_hash VARCHAR(66),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_inscriptions_creator ON inscriptions(creator);
CREATE INDEX idx_inscriptions_timestamp ON inscriptions(timestamp DESC);
API REST (ejemplo conceptual)
Si construyes un backend sobre el indexador:
GET /api/inscriptions → Lista paginada
GET /api/inscriptions/:id → Detalle por ID
GET /api/inscriptions/creator/:address → Por creador
GET /api/stats → Total, últimas 24h, etc.
Integración desde otra dApp
Solo lectura (sin wallet)
Cualquier app puede leer inscripciones con un RPC público:
const count = await publicClient.readContract({
address: REGISTRY_ADDRESS,
abi: INSCRIPTION_REGISTRY_ABI,
functionName: 'inscriptionCount',
});
No necesitas wallet ni permisos.
Escritura (con wallet del usuario)
Usa wagmi hooks o wallet connect estándar:
import { useWriteContract } from 'wagmi';
const { writeContract } = useWriteContract();
writeContract({
address: REGISTRY_ADDRESS,
abi: INSCRIPTION_REGISTRY_ABI,
functionName: 'inscribe',
args: [data],
});
Formatos de datos recomendados (off-chain)
El contrato no impone formato. Convenciones sugeridas para interoperabilidad:
| Tipo | Formato sugerido | Ejemplo |
|---|---|---|
| Texto plano | UTF-8 bytes | "Hello world" |
| JSON | UTF-8 JSON string | {"type":"message","body":"..."} |
| Hash de archivo | UTF-8 hex string | "sha256:abc123..." |
| Imagen pequeña | Raw bytes | PNG/JPG < 24KB |
Puedes definir un prefijo opcional para tipado:
fs:v1:text:Hello world
fs:v1:json:{"key":"value"}
Esto es convención off-chain — el contrato lo almacena tal cual.
SDK futuro (roadmap)
Posibles extensiones del protocolo:
@featherscript/sdk— npm package con tipos y helpers- Subgraph The Graph para queries GraphQL
- CLI
featherscript inscribe "message" - Widget embebible
<FeatherScriptInscribe />
El contrato actual ya es suficiente como base estable para todas estas extensiones.