From 1ca1ce35455b3c2b5a1f474c57c3e30af1ecd549 Mon Sep 17 00:00:00 2001 From: vivian Date: Mon, 28 Apr 2025 23:49:59 +0900 Subject: [PATCH 01/45] Review: Initial translations for Japanese and Spanish --- .env | 4 + es/guides/analytics-guide.mdx | 362 ++ es/guides/build-embedding-wallet.mdx | 4 + es/guides/building-relaying-server.mdx | 301 ++ ...ing-transaction-heavy-games-with-unity.mdx | 48 + es/guides/custom-marketplace.mdx | 732 +++ es/guides/databeat-integration.mdx | 192 + es/guides/guide-overview.mdx | 60 + es/guides/jelly-forest-unity-guide.mdx | 345 ++ es/guides/metadata-guide.mdx | 238 + es/guides/mint-collectibles-serverless.mdx | 444 ++ es/guides/primary-drop-sales-erc721.mdx | 161 + es/guides/primary-sales.mdx | 126 + es/guides/telegram-integration.mdx | 104 + es/guides/template-overview.mdx | 86 + .../template-embedded-wallet-react-native.mdx | 111 + .../templates/template-embedded-wallet.mdx | 47 + es/guides/treasure-chest-guide.mdx | 712 +++ es/guides/typed-on-chain-signatures.mdx | 321 ++ es/guides/unity-primary-sales.mdx | 130 + es/guides/unity-webgl-telegram.mdx | 102 + es/guides/unreal-ew-guide.mdx | 80 + es/guides/using-unity-iap-to-sell-nfts.mdx | 115 + es/guides/webgl-guide.mdx | 606 +++ frenglish.config.json | 31 + ja/guides/analytics-guide.mdx | 361 ++ ja/guides/build-embedding-wallet.mdx | 4 + ja/guides/building-relaying-server.mdx | 301 ++ ...ing-transaction-heavy-games-with-unity.mdx | 48 + ja/guides/custom-marketplace.mdx | 730 +++ ja/guides/databeat-integration.mdx | 192 + ja/guides/guide-overview.mdx | 60 + ja/guides/jelly-forest-unity-guide.mdx | 345 ++ ja/guides/metadata-guide.mdx | 238 + ja/guides/mint-collectibles-serverless.mdx | 444 ++ ja/guides/primary-drop-sales-erc721.mdx | 161 + ja/guides/primary-sales.mdx | 126 + ja/guides/telegram-integration.mdx | 104 + ja/guides/template-overview.mdx | 86 + .../template-embedded-wallet-react-native.mdx | 111 + .../templates/template-embedded-wallet.mdx | 47 + ja/guides/treasure-chest-guide.mdx | 712 +++ ja/guides/typed-on-chain-signatures.mdx | 321 ++ ja/guides/unity-primary-sales.mdx | 130 + ja/guides/unity-webgl-telegram.mdx | 102 + ja/guides/unreal-ew-guide.mdx | 81 + ja/guides/using-unity-iap-to-sell-nfts.mdx | 115 + ja/guides/webgl-guide.mdx | 605 +++ new-frenglish-translations.yaml | 169 + package.json | 9 +- pnpm-lock.yaml | 4247 ++++++++++++++++- pnpm-workspace.yaml | 3 + 52 files changed, 15242 insertions(+), 72 deletions(-) create mode 100644 .env create mode 100644 es/guides/analytics-guide.mdx create mode 100644 es/guides/build-embedding-wallet.mdx create mode 100644 es/guides/building-relaying-server.mdx create mode 100644 es/guides/building-transaction-heavy-games-with-unity.mdx create mode 100644 es/guides/custom-marketplace.mdx create mode 100644 es/guides/databeat-integration.mdx create mode 100644 es/guides/guide-overview.mdx create mode 100644 es/guides/jelly-forest-unity-guide.mdx create mode 100644 es/guides/metadata-guide.mdx create mode 100644 es/guides/mint-collectibles-serverless.mdx create mode 100644 es/guides/primary-drop-sales-erc721.mdx create mode 100644 es/guides/primary-sales.mdx create mode 100644 es/guides/telegram-integration.mdx create mode 100644 es/guides/template-overview.mdx create mode 100644 es/guides/templates/template-embedded-wallet-react-native.mdx create mode 100644 es/guides/templates/template-embedded-wallet.mdx create mode 100644 es/guides/treasure-chest-guide.mdx create mode 100644 es/guides/typed-on-chain-signatures.mdx create mode 100644 es/guides/unity-primary-sales.mdx create mode 100644 es/guides/unity-webgl-telegram.mdx create mode 100644 es/guides/unreal-ew-guide.mdx create mode 100644 es/guides/using-unity-iap-to-sell-nfts.mdx create mode 100644 es/guides/webgl-guide.mdx create mode 100644 frenglish.config.json create mode 100644 ja/guides/analytics-guide.mdx create mode 100644 ja/guides/build-embedding-wallet.mdx create mode 100644 ja/guides/building-relaying-server.mdx create mode 100644 ja/guides/building-transaction-heavy-games-with-unity.mdx create mode 100644 ja/guides/custom-marketplace.mdx create mode 100644 ja/guides/databeat-integration.mdx create mode 100644 ja/guides/guide-overview.mdx create mode 100644 ja/guides/jelly-forest-unity-guide.mdx create mode 100644 ja/guides/metadata-guide.mdx create mode 100644 ja/guides/mint-collectibles-serverless.mdx create mode 100644 ja/guides/primary-drop-sales-erc721.mdx create mode 100644 ja/guides/primary-sales.mdx create mode 100644 ja/guides/telegram-integration.mdx create mode 100644 ja/guides/template-overview.mdx create mode 100644 ja/guides/templates/template-embedded-wallet-react-native.mdx create mode 100644 ja/guides/templates/template-embedded-wallet.mdx create mode 100644 ja/guides/treasure-chest-guide.mdx create mode 100644 ja/guides/typed-on-chain-signatures.mdx create mode 100644 ja/guides/unity-primary-sales.mdx create mode 100644 ja/guides/unity-webgl-telegram.mdx create mode 100644 ja/guides/unreal-ew-guide.mdx create mode 100644 ja/guides/using-unity-iap-to-sell-nfts.mdx create mode 100644 ja/guides/webgl-guide.mdx create mode 100644 new-frenglish-translations.yaml create mode 100644 pnpm-workspace.yaml diff --git a/.env b/.env new file mode 100644 index 00000000..f55e9eaa --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +# prod +FRENGLISH_API_KEY = $2b$10$sWXauOt1hTqnoDSiTaPc3.XzC1OFUww.qydrD4r2ZbwqtvhpH5pw. +#dev +#FRENGLISH_API_KEY = $2b$10$sWJ3wrTwGSipkWneZOcrKOFvgorgaw3QsOt/gML7YHKTMpcVohZ7q \ No newline at end of file diff --git a/es/guides/analytics-guide.mdx b/es/guides/analytics-guide.mdx new file mode 100644 index 00000000..e513dde6 --- /dev/null +++ b/es/guides/analytics-guide.mdx @@ -0,0 +1,362 @@ +--- +title: API de Sequence Analytics en Dune +description: Aprenda a usar la función de analítica de Sequence Builder para consultar datos de usuarios del proyecto mediante un Cloudflare Worker. +sidebarTitle: Aproveche la API de Sequence Analytics en Dune +--- + +Tiempo estimado: 20 a 30 minutos + +En esta guía, mostraremos cómo utilizar la función de analítica de [Sequence Builder](https://sequence.build/) para consultar información sobre el uso de los usuarios en el proyecto. Para ello, utilizaremos un [Cloudflare Worker](https://www.cloudflare.com/). + +Presente a su comunidad el rendimiento del proyecto a través de un panel de [Dune](https://dune.com/) para fomentar el sentido de conexión, o utilice la API generada para incorporar bucles de retroalimentación inteligente en su juego impulsados por analíticas de usuarios. + +Puede ver un ejemplo del resultado de esta guía [aquí](https://dune.com/mmhorizon/dungeon-minter-analytics) +1. Gestión de claves de acceso: Solicite una clave de acceso secreta para interactuar con el stack de Sequence +2. Cloudflare Worker: Cree una función que consulte el stack de Sequence y genere datos específicos de su proyecto +3. Panel de Dune: Cree una visualización de los datos en un panel de control compartible + + + Puede encontrar una referencia al código de plantilla + [aquí](https://github.com/0xsequence-demos/template-cloudflare-worker-wallets-analytics) + + +## 1. Gestión de claves de acceso +Necesitará obtener una clave de acceso secreta para el proyecto con el fin de autenticar la aplicación con el stack de Sequence. Siga estos pasos: + +### Creación de clave de acceso secreta + + + + Primero, acceda a la configuración y seleccione la tarjeta de claves API: + + + ![builder settings access keys](/images/builder/builder_settings_access_keys.png) + + + + + Desplácese hacia abajo y seleccione `+ Add Service Account`: + + + ![builder settings add service account](/images/builder/builder_settings_add_service_account.png) + + + + + Luego cambie el permiso a `Write`, haga clic en `+ Add Service Account` y seleccione `Confirm`: + + + ![builder settings add service account](/images/builder/builder_settings_add_service_account_confirm.png) + + + Finalmente, copie la clave y guárdela en un lugar seguro, ya que no podrá acceder a ella nuevamente desde Sequence Builder. + + + +## 2. Cloudflare Worker +En este ejemplo, utilizamos un Cloudflare Worker para aprovechar el escalado automático según el uso del panel de control y facilitar los despliegues desde la línea de comandos. También puede utilizar su propio backend u otras alternativas sin servidor. + + + + Para crear el proyecto desde cero, primero cree una carpeta con `mkdir`, ingrese a la carpeta con `cd` y ejecute `pnpm init` para crear un `package.json`. + + + + Asegúrese de tener instalado wrangler cli en su proyecto y configure la palabra clave `wrangler` como un alias en su sesión local de bash. + + ```shell + pnpm install wrangler --save-dev + alias wrangler='./node_modules/.bin/wrangler' + ``` + + Cree una cuenta en el [sitio de Cloudflare](https://cloudflare.com/) e inicie sesión en su panel de Cloudflare para conectar la plataforma Cloudflare con su entorno de desarrollo local. + + ```shell + wrangler login + ``` + + Una vez conectado, inicialice el proyecto en el directorio con el comando `wrangler init` y acepte uno de los nombres de carpeta generados aleatoriamente que prefiera. Siga las indicaciones para inicializar su aplicación typescript `"Hello World" Worker` con control de versiones en git. + + ```shell + wrangler init + ``` + + Para completar este paso, presione enter 4 veces después de `wrangler init`, respondiendo `No` en los dos últimos pasos para rechazar el control de versiones con git y el despliegue. + + Se clonará un repositorio inicial que puede usar para desplegar código en la nube. + + + Pruebas locales de la API
+ En cualquier momento de la guía, puede usar el comando `wrangler dev` en la carpeta del proyecto para realizar pruebas locales +
+ + #### Despliegue de prueba + + Finalmente, cambie al directorio del proyecto generado aleatoriamente con `cd` y ejecute el comando `wrangler deploy`. + + Esto mostrará una URL, la cual puede ingresar en su navegador en la dirección `https://..workers.dev` para ver el resultado `Hello World!`. +
+ + + Una vez que tenga el proyecto configurado, actualice su `wrangler.toml` con las siguientes variables, donde `DAYS` es el periodo de tiempo que desea analizar: + + ```shell + [vars] + SECRET_API_ACCESS_KEY = "" + PROJECT_ID = + DAYS = + ``` + + Luego incluya el tipo `Env` con las variables en `index.ts`: + + ```ts + export interface Env { + PROJECT_ID: number; + SECRET_API_ACCESS_KEY: string; + DAYS: number; + } + ``` + + Reemplace la función `fetch` existente por las siguientes llamadas a funciones simuladas: + + ```ts + export default { + async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { + const url = new URL(request.url); + + // Handle different endpoints + if (url.pathname === "/dailyActiveUsers") { + return handleDailyWallets(env, request); + } else if (url.pathname === "/totalTransactionsSent") { + return handleTotalTxns(env, request); + } else { + return new Response("No function for this URL", {status: 405}); + } + } + }; + ``` + + Utilizando las siguientes funciones: + + ```ts + const handleDailyWallets = async (env: Env, request: Request) => { + return new Response(JSON.stringify({endpoint: "daily"}), {status: 200}); + }; + + const handleTotalTxns = async (env: Env, request: Request) => { + return new Response(JSON.stringify({endpoint: "total"}), {status: 200}); + }; + ``` + + + + A continuación, incluya las siguientes funciones utilitarias para analizar la fecha correcta a partir del valor actualizado en el `wrangler.toml` para la variable `DAYS`: + + ```ts + const endDate = () => { + const today = new Date(); + return today.toISOString().substring(0, 10); // only including the YYYY-MM-DD date + }; + + const startDate = (env: Env) => { + const today = new Date(); + + // Format today's date as a string + const daysBefore = new Date(today); + daysBefore.setDate(daysBefore.getDate() - env.DAYS); + + // Format the date 7 days before as a string by only including the YYYY-MM-DD date + const daysBeforeString = daysBefore.toISOString().substring(0, 10); + return daysBeforeString; + }; + ``` + + + + Ahora, gestione la solicitud de `Daily Active Users` usando la siguiente función, que llama a la API de Sequence Analytics: + + ```ts + const handleDailyWallets = async (env: Env, request: Request) => { + const resp = await fetch(`https://api.sequence.build/rpc/Analytics/WalletsDaily`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${env.SECRET_API_ACCESS_KEY}` + }, + body: JSON.stringify({ + filter: { + dateInterval: "DAY", + endDate: endDate(), + projectId: env.PROJECT_ID, + startDate: startDate(env) + } + }) + }); + + const data: any = await resp.json(); + return new Response(JSON.stringify(data.walletStats), {status: 200}); + }; + ``` + + + + Por último, agregue la siguiente función para el Total de transacciones enviadas: + + ```ts + const handleTotalTxns = async (env: Env, request: Request) => { + const resp = await fetch( + `https://api.sequence.build/rpc/Analytics/WalletsTxnSentTotal`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${env.SECRET_API_ACCESS_KEY}` + }, + body: JSON.stringify({ + filter: { + dateInterval: "DAY", + endDate: endDate(), + projectId: env.PROJECT_ID, + startDate: startDate(env) + } + }) + } + ); + + const data: any = await resp.json(); + return new Response(JSON.stringify(data.walletStats), {status: 200}); + }; + ``` + + + + La API de Sequence Analytics excluye los días sin actividad en la respuesta. Sin embargo, si desea incluir días sin datos en sus consultas de Dune para mostrar los días sin datos en el intervalo de tiempo, puede usar la siguiente función para completar los días que no muestran datos en el formato de fecha correcto: + + ```typescript + const fillMissingDates = (data: any[], startDate: string, endDate: string) => { + const filledData: {value: number; label: string}[] = []; + const start = new Date(startDate); + const end = new Date(endDate); + + for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) { + const dateString = d.toISOString().substring(0, 10); + const existingData = data.find((entry) => entry.label === dateString); + if (existingData) { + filledData.push(existingData); + } else { + filledData.push({value: 0, label: dateString}); + } + } + + return filledData; + }; + ``` + + Luego, para **ambas** respuestas de llamada, incluya la siguiente llamada al método con los datos de `walletStats` como parámetro: + + ```typescript + ... + const data: any = await resp.json(); + const filledData = fillMissingDates(data.walletStats, startDate(env), endDate()); + return new Response(JSON.stringify(filledData), { status: 200 }); + } + ``` + +
+ +Ahora puede probar su API accediendo a las rutas correspondientes en el nombre de host (`/dailyActiveUsers` y `/totalTransactionsSent`) una vez que haya vuelto a desplegar con `wrangler deploy`. + + + Para más ejemplos de endpoints disponibles a través de la API de Analytics, consulte la página de + [Descripción general](/api-references/analytics/overview). + + +## 3. Panel de Dune + + + + Primero, cree una cuenta en [Dune](https://dune.com/) + + + + Ingrese a su cuenta en `https://dune.com/` y seleccione el botón `Create` y luego `New query`. + + + ![dune create query](/images/guides/analytics/dune_create_query.png) + + + + + Ingrese la siguiente consulta SQL en la consola y seleccione `Run`: + + ```sql + SELECT + t.label as "Date", -- converting the label to "Date" + t.value as "Count" -- converting the value field to "Count" + FROM UNNEST( + TRY_CAST( + JSON_PARSE(HTTP_GET('https:///dailyActiveUsers')) AS ARRAY(ROW(label VARCHAR, value DOUBLE)) + ) + ) AS t(label, value) + + ``` + + Una vez que vea los resultados, cree una `New visualization`. + + Luego, seleccione `Add visualization` después de que el `Bar chart` predeterminado esté seleccionado en el menú desplegable (o personalícelo si lo prefiere). + + + ![add visualization](/images/guides/analytics/dune_add_visualization.png) + + + Finalmente, haga clic en `Save` y asigne un nombre a su consulta. + + + ![save query](/images/guides/analytics/dune_query_save.png) + + + + + Repita los pasos del [paso anterior](/guides/analytics-guide#create-query) y utilice la siguiente consulta SQL: + + ```sql + SELECT + t.label, + t.value + FROM UNNEST( + TRY_CAST( + JSON_PARSE(HTTP_GET('https:///totalTransactionsSent')) AS ARRAY(ROW(label VARCHAR, value DOUBLE)) + ) + ) AS t(label, value) + + ``` + + Una vez que vea los resultados, cree una `New visualization`. + + Luego, seleccione `Add visualization` y desplácese hasta `Counter` para crear un widget contador que muestre el total absoluto devuelto por la API. + + + + Acceda a los botones que dicen `Create` > `New dashboard` e ingrese un nombre para el nuevo panel de control. + + + ![dune create dashboard](/images/guides/analytics/dune_create_dashboard.png) + + + Después de crearlo, agregue las dos consultas anteriores seleccionando `Edit` y luego `Add visualization`. + + + ![dune edit dashboard](/images/guides/analytics/dune_edit_dashboard.png) + + + + ![dune add visualization from dashboard](/images/guides/analytics/dune_add_visualization_from_dashboard.png) + + + Para cada consulta, busque el nombre en la ventana emergente, seleccione `Add` para cada una, luego `Done` en la ventana y `Done` en el panel. + + + +¡Felicidades! Ahora puedes compartir los datos de uso de tu proyecto con tu equipo o comunidad. Finaliza haciendo clic en el botón `Share`. + +![dune share dashboard](/images/guides/analytics/dune_share_dashboard.png) \ No newline at end of file diff --git a/es/guides/build-embedding-wallet.mdx b/es/guides/build-embedding-wallet.mdx new file mode 100644 index 00000000..11b942a8 --- /dev/null +++ b/es/guides/build-embedding-wallet.mdx @@ -0,0 +1,4 @@ +--- +sidebarTitle: Crea un servicio de vinculación de billetera integrada +url: /solutions/wallets/link-wallets/integration-guide +--- \ No newline at end of file diff --git a/es/guides/building-relaying-server.mdx b/es/guides/building-relaying-server.mdx new file mode 100644 index 00000000..f984f2bc --- /dev/null +++ b/es/guides/building-relaying-server.mdx @@ -0,0 +1,301 @@ +--- +title: Construye un servidor de retransmisión de transacciones +description: Aprenda a construir un servidor de retransmisión de transacciones con Sequence, permitiendo que su servidor envíe transacciones en nombre de los usuarios de manera fluida. +sidebarTitle: Cree un servicio de transacciones backend +--- + +Con Sequence, puedes crear una billetera de contrato inteligente que tu servidor puede usar para enviar transacciones para tus usuarios sin tener que preocuparte por la velocidad de las transacciones, el rendimiento o las reorganizaciones (reorgs). + +La única diferencia respecto al uso de una billetera Sequence típica al enviar transacciones a la blockchain es que, a nivel de contrato inteligente, el `msg.sender` es una de las direcciones de billetera de Sequence Relayers. Para los contratos estándar de Sequence Builder, esto no es un problema cuando se combina con una solicitud de la API de transacciones retransmitidas. + + + Por defecto, las transacciones de Sequence se ejecutarán de forma secuencial. + + +Los siguientes pasos te guiarán sobre cómo crear tu servidor y acuñar coleccionables a una dirección de billetera: +1. Configuración del entorno con servidor Express: Cree un servidor basado en NodeJs usando la librería Express para aceptar solicitudes HTTP +2. Gestión de proyecto y clave de acceso: Solicite una clave de acceso pública para interactuar con el stack de Sequence +3. Despliegue del contrato de coleccionables: Implemente un contrato de coleccionables para poder enviar transacciones a la blockchain y acuñar tokens a una dirección de billetera +4. Construya un relayer patrocinado con la API de Transacciones: Cree una función para usar en una ruta de Express que llame a la API de Transacciones de Sequence desde un contrato patrocinado + +Funciones adicionales: +- (Opcional) Retransmisión con moneda que posee la billetera: Crea una función para usar en una ruta de Express que llame a la API de Transacciones de Sequence pagada usando una moneda que posea la billetera +- (Opcional) Retransmisión de transacciones en paralelo: Realiza agrupamiento de transacciones para enviar una moneda + + + + Asegúrese de tener instalado `pnpm` (u otro gestor de paquetes de node) con el siguiente comando: + + ```shell + curl -fsSL https://get.pnpm.io/install.sh | sh - + ``` + + Luego, clone el [siguiente código de plantilla de express](https://github.com/0xsequence-demos/template-node-transactions-backend/tree/template-starter) + + + Express es un framework minimalista y flexible para aplicaciones web en Node.js que ofrece un conjunto robusto de funciones para aplicaciones web y móviles, que usaremos en esta guía. + + + Una vez que el código esté en tu máquina local, ejecuta tu servidor y cliente con el siguiente comando: + + ```shell + pnpm run start + ``` + + Dentro del código hay una ruta llamada `/mint` que puedes llamar desde la línea de comandos para hacer pruebas. + + Pruebe con este ejemplo de solicitud curl: + + ```shell + curl -X POST http://localhost:3000/mint -d '{"tokenID": 0, "address": "0x"}' + ``` + + Debería ver la siguiente salida: + + ```shell + {"txHash":"0x"} + ``` + + + + Primero, siga [esta guía](/support/builder/project-management) para registrarse en [Sequence Builder](https://sequence.build/) y aprender cómo crear un proyecto. + + Luego, para usar la API de Transacciones, deberá actualizar su facturación a `Developer`, lo cual puede hacer siguiendo [esta guía](/support/builder/project-settings#5-billing-settings). + + Finalmente, se requiere una `Public Access Key` para la API de Transacciones, que puede obtener siguiendo [esta guía](/solutions/builder/getting-started#claim-an-api-access-key). + + Por último, actualice el archivo `.env.example` a `.env` con lo siguiente: + + ```shell + CHAIN_HANDLE='' # e.g. `mainnet`, `xr-sepolia`, etc. + PROJECT_ACCESS_KEY='' + ``` + + + + Siga [esta guía](/solutions/collectibles/contracts/deploy-an-item-collection) para desplegar un contrato de coleccionables. + + + Si está usando una red que no es de pruebas y necesita patrocinar su contrato, puede hacerlo siguiendo [esta guía]() + + + Finalmente, actualice el archivo `.env` con su contrato de coleccionables desplegado: + + ```shell + ... + COLLECTIBLE_CONTRACT_ADDRESS="
" + ``` + + ## Cree un relayer patrocinado con la API de Transacciones + + + El código completo para esta sección se encuentra [aquí](https://github.com/0xsequence-demos/template-node-transactions-backend) + + + Primero, usando el código de plantilla proporcionado en el paso #1, necesitaremos agregar algunos paquetes + + ```typescript + import { Session } from '@0xsequence/auth' + import { findSupportedNetwork, NetworkConfig } from '@0xsequence/network' + ``` + + Luego, su servidor necesitará una billetera EOA que pueda firmar mensajes. Será el propietario de la billetera Sequence de su servidor, la cual se usará para enviar transacciones. + + + Abrir una sesión puede desencadenar una migración de su billetera Sequence a una nueva versión. Esto podría ser de `v1` a `v2` o de `v2` a versiones futuras. + + La migración es un proceso irreversible; una vez que su billetera se migra, no puede volver a una versión anterior. + + Para detectar cualquier migración no deseada, puede usar el callback `onMigration`. + + + Para implementar la función `callContract`, incluya el siguiente código que utiliza un solo firmante para retransmitir transacciones: + + ```typescript + const callContract = async (address: string, tokenID: number): Promise => { + + const chainConfig: NetworkConfig = findSupportedNetwork(process.env.CHAIN_HANDLE!)! + const provider = new ethers.providers.StaticJsonRpcProvider({ + url: chainConfig.rpcUrl + }) + + const walletEOA = new ethers.Wallet(process.env.PKEY!, provider); + const relayerUrl = `https://${chainConfig.name}-relayer.sequence.app` + + // Create a single signer sequence wallet session + const session = await Session.singleSigner({ + signer: walletEOA, + projectAccessKey: process.env.PROJECT_ACCESS_KEY! + }) + + const signer = session.account.getSigner(chainConfig.chainId) + + // Standard interface for ERC1155 contract deployed via Sequence Builder + const collectibleInterface = new ethers.Interface([ + 'function mint(address to, uint256 tokenId, uint256 amount, bytes data)' + ]) + + const data = collectibleInterface.encodeFunctionData( + 'mint', [`${address}`, `${tokenID}`, "1", "0x00"] + ) + + const txn = { + to: process.env.COLLECTIBLE_CONTRACT_ADDRESS, + data: data + } + + try { + return await signer.sendTransaction(txn) + } catch (err) { + console.error(`ERROR: ${err}`) + throw err + } + } + ``` + + Por último, actualice el archivo `.env` con una clave privada para una billetera que puede generarse desde [esta aplicación](https://sequence-ethauthproof-viewer.vercel.app/) (solo para fines de demostración). Para producción, recomendamos generar claves privadas de forma segura en su computadora local usando [este script de ejemplo](https://github.com/0xsequence-demos/script-generate-evm-private-key). + + Luego, actualice la variable `PKEY` con la clave: + + ```shell + ... + PKEY='' + ``` + + ### Conceder el rol de minter a la dirección de la billetera relayer + + Debe actualizar el acceso de roles del contrato en Builder para aceptar solicitudes solo desde la dirección de la billetera minter. + + En Sequence Builder, otorgue el permiso de minter a su dirección de la API de transacciones de Sequence. + + Para hacerlo, abra su proyecto, vaya a la página de `Contracts`, seleccione sus `Linked contracts` y, en la pestaña `Write Contract`, expanda el método `grantRole`. + + Complete con los siguientes datos: + + `bytes32 role`: `0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6` + + `address account`: `` + + + Conceder un rol al relayer + + + Donde la cadena `role` ingresada es el resultado de `keccak256("MINTER_ROLE")` en solidity o `ethers.solidityPackedKeccak256(ethers.toUtf8Bytes("MINTER_ROLE"))` en javascript + + Esto hace que solo su dirección específica pueda acuñar desde el contrato; de lo contrario, generará un error. + + Complete la actualización del rol haciendo clic en `write` y firme la transacción patrocinada. + + Su aplicación ya está lista para que envíe una transacción de prueba desde el frontend del cliente iniciando sesión en su billetera y haciendo clic en `mint`. + + ¡Pruébelo! + + + No olvide actualizar la clave de acceso en el cliente en la función `initWallet` + + + + +## (Opcional) Retransmisión con moneda disponible en la billetera +También puede forzar una forma específica de pagar las comisiones de gas: + +```ts +import { Session } from '@0xsequence/auth' +import { ethers } from 'ethers' + +// where the corresponds to https://docs.sequence.xyzhttps://status.sequence.info/ +const provider = new ethers.providers.JsonRpcProvider('https://nodes.sequence.app/'); + +// Create your server EOA +const walletEOA = new ethers.Wallet(serverPrivateKey, provider) + +// Open a Sequence session, this will find or create +// a Sequence wallet controlled by your server EOA +const session = await Session.singleSigner({ + signer: walletEOA, + projectAccessKey: '' + // OPTIONAL: Multiple wallets could be found for the same EOA + // to enforce a specific wallet you can use the following callback + selectWallet: async (wallets: string[]) => { + const found = wallets.find(w => w === EXPECTED_WALLET_ADDRESS) + if (!found) throw Error('wallet not found') + // Returning the wallet address will make the session use it + // returning undefined will make the session create a new wallet + return found + } +}) + +const signer = session.account.getSigner(137, { + // OPTIONAL: You can also enforce a specific way to pay for gas fees + // if not provided the sdk will select one for you + selectFee: async ( + _txs: any, + options: FeeOption[] + ) => { + // Find the option to pay with native tokens + const found = options.find(o => !o.token.contractAddress) + if (!found) throw Error('fee option not found') + return found + } +}) + +// Initialize the contract +const usdc = new ethers.Contract( + '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC on Polygon + ERC_20_ABI, + signer +) + +// Send the transaction +const txnResponse = await usdc.transfer(recipient, 1) + +// Check if transaction was successful +if (txnReceipt.status != 1) { + console.log(`Unexpected status: ${txnReceipt.status}`) +} +``` + +## (Opcional) Relayer de transacciones en paralelo +Si desea enviar múltiples transacciones independientes sin necesidad de agruparlas, también puede enviarlas en espacios de nonce distintos (diferentes valores de nonce). + +Usar espacios de nonce distintos para sus transacciones le indica a la API de transacciones que no hay dependencia entre ellas y que pueden ejecutarse en la blockchain en cualquier orden. + +Esto permite que las transacciones se envíen de forma inmediata, sin esperar a un lote completo. + +Aquí tiene un ejemplo de cómo hacerlo: + +```js +// Generate random nonce spaces with ~0% probability of collision +const randomNonceSpace1 = ethers.BigNumber.from( + ethers.hexlify(ethers.randomBytes(20)) +); +const randomNonceSpace2 = ethers.BigNumber.from( + ethers.hexlify(ethers.randomBytes(20)) +); + +// Create signers for each nonce space +const signer1 = session.account.getSigner(137, { + nonceSpace: randomNonceSpace1, +}); + +const signer2 = session.account.getSigner(137, { + nonceSpace: randomNonceSpace2, +}); + +// Generate transactions +const txn1 = { + to: tokenContract.address, + data: erc20Interface.encodeFunctionData("transfer", [recipient1, amount1]), +}; + +const txn2 = { + to: tokenContract.address, + data: erc20Interface.encodeFunctionData("transfer", [recipient2, amount2]), +}; + +// Dispatch transactions, which can now be executed in parallel +await Promise.all([ + signer1.sendTransaction(txn1), + signer2.sendTransaction(txn2), +]); +``` \ No newline at end of file diff --git a/es/guides/building-transaction-heavy-games-with-unity.mdx b/es/guides/building-transaction-heavy-games-with-unity.mdx new file mode 100644 index 00000000..847d1f07 --- /dev/null +++ b/es/guides/building-transaction-heavy-games-with-unity.mdx @@ -0,0 +1,48 @@ +--- +title: Creación de juegos con muchas transacciones en Unity +description: Algunos juegos guardan el estado del juego con frecuencia. Al trabajar con blockchain, esto puede ser costoso y generar mucha latencia. Esta guía explica cómo superar estas limitaciones sin afectar la experiencia del usuario ni incrementar en exceso sus costos operativos. +sidebarTitle: Creación de juegos con muchas transacciones en Unity +--- + +## Introducción +A diferencia de otras formas de bases de datos, cada escritura en una blockchain (transacción) cuesta dinero en forma de [comisiones de gas](https://ethereum.org/en/developers/docs/gas/). Al crear juegos blockchain/web3, se deben considerar las comisiones de gas. Aunque [el patrocinio de gas de Sequence](/solutions/builder/gas-tank) se encarga de gran parte de la complejidad para sus usuarios finales, como desarrollador de juegos, aún debe considerar algunos aspectos relacionados con las comisiones de gas. + + + Al desarrollar su juego, debe considerar la **_frecuencia_** con la que envía transacciones a la blockchain para mantener los costos de operación al mínimo. + + +Una complejidad adicional de trabajar con blockchain, que no existe en todos los sistemas de almacenamiento de datos, es que escribir en la base de datos blockchain (es decir, hacer una transacción) es una operación asincrónica y no instantánea que requiere conexión a la red. + + + Las transacciones pueden fallar por varias razones: falta de internet, fondos insuficientes, etc. + + +Primero, debe considerar qué propiedades tokenizables (por ejemplo, objetos, potenciadores, desbloqueos, etc.) deberían tokenizarse en la blockchain. + +Luego, debe considerar los "tipos" de transacciones que realizará su juego. Probablemente pueda agrupar las transacciones en diferentes categorías. Por ejemplo, algunas de estas categorías pueden incluir: recoger objetos (como monedas), crear, intercambiar, vender, comprar, etc. + +Una vez que haya categorizado cada una de sus transacciones, considere las expectativas de sus usuarios finales respecto a esas transacciones, así como sus propias expectativas como desarrollador. ¿Cuánta demora es aceptable para el usuario final al procesar una transacción? ¿Puede asumir que una transacción tendrá éxito para dar retroalimentación instantánea al usuario y, si es así, ¿puede recuperarse si la transacción falla sin afectar negativamente al jugador o a la rentabilidad? + +El autor de esta guía suele clasificar las transacciones como de alto valor o bajo valor. + +Las **transacciones de alto valor** normalmente requieren confirmación antes de proporcionar retroalimentación al usuario final. Las transacciones pueden fallar por varias razones (sin internet, gas insuficiente, supuestos inválidos, etc.). Si asumimos que una transacción de alto valor será exitosa y damos retroalimentación inmediata al usuario, pero luego la transacción falla, no podremos recuperarnos sin afectar negativamente al usuario o a nuestros resultados financieros. Por ejemplo, una tienda dentro del juego. Si la transacción de "comprar espada" de un usuario falla, tendríamos que revocar la espada de su cuenta (afectando la experiencia del jugador) o perder el ingreso de la venta (afectando los resultados financieros). Afortunadamente, la mayoría de las transacciones de alto valor corresponden a actividades en las que los usuarios ya están acostumbrados a esperar brevemente en juegos tradicionales, como tiendas, creación de objetos, mejoras, etc. + +Las **transacciones de bajo valor** pueden, y a menudo deberían, proporcionar retroalimentación inmediata al usuario. No es necesario esperar la confirmación de la transacción antes de mostrar la retroalimentación en el juego. Si la transacción falla, normalmente podemos recuperarnos fácilmente sin afectar la experiencia del jugador ni el resultado financiero. Los jugadores suelen estar acostumbrados a recibir retroalimentación instantánea para estas acciones en juegos tradicionales. Por ejemplo: cuando un usuario recoge una moneda en un juego de plataformas (o similar), espera ver reflejada la moneda recolectada en la interfaz de inmediato. Es poco probable que el jugador recuerde el total exacto de monedas en la siguiente sesión de juego y/o es poco probable que esto afecte al desarrollador si almacena localmente las monedas recolectadas y reenvía la transacción cuando se resuelvan los problemas de red (o similar). + +Por último, considera con qué frecuencia tu juego debe realizar transacciones. Algunos juegos hacen que el usuario realice muchas acciones que afectan el estado del juego en poco tiempo. Imagina enviar una transacción a la blockchain cada vez que Mario recoge una moneda... Los costos se volverían rápidamente prohibitivos. ¡Agrupa esas transacciones de bajo valor! + +## ¿Cómo implementar esto con Unity? +Primero, construye un caché local de lo que el usuario tiene en la blockchain. Esto es sencillo: simplemente [lee desde la blockchain](/sdk/unity/power/read-from-blockchain) y almacena localmente los saldos de tokens del usuario en el formato que sea más conveniente. Si estás convirtiendo un juego existente o un prototipo que ya usa un sistema de almacenamiento local (como PlayerPrefs) o un sistema de almacenamiento remoto (como un [RDBMS](https://en.wikipedia.org/wiki/List_of_relational_database_management_systems)), probablemente ya tengas implementado un caché local y solo necesites crear un adaptador. + +Luego, probablemente querrás utilizar el `TransactionQueuer` y sus derivados que proporciona el SDK de Unity. Los `TransactionQueuer` son altamente configurables y están diseñados para apoyar el desarrollo de juegos donde los jugadores realizan muchas acciones que modifican el estado. Por ejemplo, si tu juego implica recolectar muchas monedas (o similares) como transacciones de bajo valor, probablemente querrás usar el `PermissionedMinterTransactionQueuer` (asumiendo que tu función `mint` tiene permisos, que es lo predeterminado, y estás acuñando desde un servidor) o el `SequenceWalletTransactionQueuer` (si cualquiera puede acuñar). Usando estos, puedes simplemente poner en cola varias transacciones; estas transacciones se combinarán automáticamente si es posible (por ejemplo, en vez de tener 'mint(amount: 5, tokenId: 11)' y 'mint(amount: 3, tokenId: 11)', se combinarían en 'mint(amount: 8, tokenId: 11)'). Luego, puedes hacer que tus transacciones se envíen cada x segundos o cuando se haga una llamada a una función, pero no antes de cada y segundos (esto se puede modificar para transacciones de alto valor), etc. Para obtener más información sobre cómo trabajar con el `TransactionQueuer`, consulta [este documento](/sdk/unity/power/write-to-blockchain#transaction-queuers). + +Por último, verifica si hay fallos en tus transacciones y maneja los errores de manera adecuada. + +```csharp +if (transactionReturn is FailedTransactionReturn) { + // Handle the failed transaction +} +``` + +## Ejemplo +Para ver un ejemplo de estos conceptos en acción en nuestro SDK de Unity, consulte nuestra [Guía de Jelly Forest](/guides/jelly-forest-unity-guide#5-mint-in-game-tokens-to-the-players-inventory) y [código de ejemplo](https://github.com/0xsequence/sequence-unity-demo/tree/master/Scripts). \ No newline at end of file diff --git a/es/guides/custom-marketplace.mdx b/es/guides/custom-marketplace.mdx new file mode 100644 index 00000000..71f77da3 --- /dev/null +++ b/es/guides/custom-marketplace.mdx @@ -0,0 +1,732 @@ +--- +title: Transacciones de libro de órdenes +description: Esta guía cubre la creación de un mercado personalizado utilizando herramientas del stack de Sequence. Incluye pasos para acuñar tokens, autenticación de billeteras, consultas a la blockchain, tipos de billeteras múltiples, creación de solicitudes, aceptación de órdenes y la integración opcional de una billetera embebida. +sidebarTitle: Crea un mercado personalizado +--- + +En esta guía, repasaremos el proceso de crear un mercado personalizado usando unas pocas herramientas sencillas del stack de Sequence. + +Estas herramientas le permitirán realizar: +1. [Acuñación](/guides/custom-marketplace#1-minting): Acuñar tokens a tu billetera desde Sequence Builder +2. [Autenticación de billetera](/guides/custom-marketplace#2-wallet-authentication): Uso del Web SDK para autenticar a un usuario +3. [Consultas a la blockchain](/guides/custom-marketplace#3-blockchain-queries): Consultar saldos de tokens usando el Indexer +4. [Tipos de billeteras múltiples](/guides/custom-marketplace#4-multi-wallet-types): Permitir que los usuarios usen una Sequence Wallet o una EOA +5. [Creación de solicitudes](/guides/custom-marketplace#5-request-creation): Crear solicitudes de venta en el Sequence Market Protocol +6. [Aceptación de órdenes](/guides/custom-marketplace#6-order-accepting): Aceptar las mejores órdenes del mercado +7. [(Opcional) Habilitar Billetera Embebida](/guides/custom-marketplace#7-optional-integrate-embedded-wallet-into-sequence-kit): Añada una experiencia de usuario más fluida con transacciones sin confirmación + + + Vea un ejemplo de [dapp de marketplace simplificado](https://simple-marketplace-boilerplate.pages.dev/) que permite a los usuarios acuñar coleccionables, venderlos con Sequence Marketplace Protocol y realizar compras con USDC en `base-sepolia` obteniendo la mejor orden del Marketplace. + + El código se puede encontrar [aquí](https://github.com/0xsequence-demos/simple-marketplace-boilerplate) + + +## 1. Minting +El primer paso es crear un coleccionable desde Sequence Builder y acuñar algunos tokens, lo cual puede lograrse con esta [guía](/solutions/collectibles/contracts/deploy-an-item-collection) y usar el `tokenId` que acuñó en los siguientes pasos para consultar y completar órdenes. + +## 2. Autenticación de Billetera +Para el proyecto, necesita una forma de autenticar al usuario con una billetera. + +En el stack de Sequence puede elegir usar una [Embedded Wallet](/sdk/headless-wallet/quickstart) para una experiencia similar a web2, sin interfaz propia, o una [Ecosystem Wallet](/solutions/wallets/ecosystem/overview) con [Web SDK](/solutions/wallets/overview) para llegar a más tipos de billeteras. + +Para esta guía usaremos una `Universal Sequence Wallet` con conector `Web SDK` (con opción de `Embedded Wallet`), que puede autenticar usuarios usando Google o Apple, además de billeteras que el usuario ya posea, como Coinbase o Metamask. + +### Instalar Paquetes +Puede crear un proyecto vanilla js/html/css desde un [template como este](https://github.com/moskalyk/vanilla-js-sequence-kit-starter) para una configuración rápida, o aquí te mostraremos cómo usar React desde cero. + +Comience creando un proyecto en una carpeta con el nombre que prefiera: + +``` +mkdir +cd +npx create-react-app . --template=typescript +``` + +Luego, instale los paquetes requeridos en la carpeta `` + +``` +pnpm install @0xsequence/kit @0xsequence/kit-connectors wagmi ethers viem 0xsequence @tanstack/react-query +``` + +Después, en `src`, junto a `index.tsx` en la carpeta, cree un archivo `config.ts` con el siguiente contenido: + +```js +import { arbitrumSepolia, Chain } from 'wagmi/chains' +import { getDefaultConnectors } from '@0xsequence/kit-connectors' +import { createConfig, http } from 'wagmi' + +const chains = [arbitrumSepolia] as [Chain, ...Chain[]] + +const projectAccessKey = process.env.REACT_APP_PROJECTACCESSKEY!; +const walletConnectProjectId = process.env.REACT_APP_WALLETCONNECTID!; + +const connectors = getDefaultConnectors( "universal", { + walletConnectProjectId: walletConnectProjectId, + defaultChainId: 421614, + appName: 'demo app', + projectAccessKey +}) + +const transports: any = {} + +chains.forEach(chain => { + transports[chain.id] = http() +}) + +const config = createConfig({ + transports, + connectors, + chains +}) + +export { config } + +``` + + + Asegúrese de incluir un archivo `.env` en la raíz de su proyecto para incluir los secretos del cliente + + +Luego, importe el `config` para ser usado por el `WagmiProvider` en el `index.tsx` + +```js +import ReactDOM from "react-dom/client"; +import { KitProvider } from "@0xsequence/kit"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { WagmiProvider } from "wagmi"; +import App from './App' + +import { config } from "./config"; + +const root = ReactDOM.createRoot( + document.getElementById("root") as HTMLElement, +); + +const queryClient = new QueryClient(); + +function Dapp() { + return ( + + + + + + + + ); +} + +root.render( + +); +``` + +Y finalmente, agregue un botón en `App.tsx` para que aparezca el modal del Web SDK + +```js +import { useOpenConnectModal, useKitWallets } from "@0xsequence/kit"; + +function App() { + const { address } = useAccount(); + const { setOpenConnectModal } = useOpenConnectModal(); + const { + wallets, // Array of connected wallets + linkedWallets, // Array of linked wallets (for embedded wallets) + setActiveWallet, // Function to set a wallet as active + disconnectWallet, // Function to disconnect a wallet + } = useKitWallets(); + + const isConnected = wallets.length; + + const connect = async () => { + setOpenConnectModal(true); + }; + + return ( + <> + {!isConnected && } + {address && address} + + ); +} + +export default App; +``` + +¡Listo! Ahora tienes una aplicación que puede autorizar a un usuario y devolver una dirección de billetera. + +Ahora puede probarlo con: + +``` +pnpm run start +``` + +## 3. Consultas a la Blockchain +Una vez que tenga uno o varios coleccionables creados, puede consultar los datos desde la dirección del contrato de su despliegue, que puede encontrar aquí: + + + ![copiar dirección del contrato](/images/marketplace/copy_contract.png) + + +Puede consultar datos usando el indexer, con este código donde una dirección de cuenta y una dirección de contrato (obtenidas del contrato desplegado desde Sequence Builder) se ingresan en la API del indexer + +Esto será importante cuando determine un `tokenID` para crear una orden en el marketplace. Para esta demostración, asumiremos que trabaja con un solo `tokenID`. + +```js +// Works in both a Webapp (browser) or Node.js: +import { SequenceIndexer } from "@0xsequence/indexer"; + +const indexer = new SequenceIndexer( + "https://arbitrum-sepolia-indexer.sequence.app", + "" +); + +// try any contract and account address you'd like :), as an example +const contractAddress = " + - `contractType` (string): el tipo de contrato (por ejemplo, ERC20, ERC721 o ERC1155) + - `contractAddress` (string): la dirección del contrato del token + - `accountAddress` (string): la dirección de la cuenta que desplegó el contrato + - `tokenID` (string): el tokenID del token (siempre 0 si es ERC20) + - `balance` (string): el saldo del token + - `blockHash` (string): el hash de merkle de la transacción del bloque cuando se desplegó el token + - `blockNumber` (number): el número de bloque en el que se desplegó el token + - `chainId` (number): el id de la red del token + - `contractType` + - `chainId` (number): el id de la red del token + - `address` (string): la dirección del token + - `name` (string): nombre del token a nivel de contrato + - `type` (string): el tipo de contrato (por ejemplo, ERC20, ERC721 o ERC1155) + - `symbol` (string): el símbolo del token + - `decimals` (number): la cantidad de decimales que tiene el token + - `logoURI` (string): el logo del token que se muestra en sequence.app + - `deployed` (boolean): indica si el token está desplegado + - `bytecodeHash` (string): hash del bytecode de un contrato inteligente desplegado en la blockchain + - `extensions` + - `link` (string): el sitio web asociado para enlazar al proyecto + - `description` (string): la descripción de metadatos del token + - `ogImage` (string): la imagen de banner del token, mostrada en sequence.app + - `originChainId` (number): el id de la red de origen que representa el token + - `originAddress` (string): la dirección del contrato de origen que representa el token + - `verified` (boolean): indica si el token está verificado y es confiable + - `verifiedBy` (string): la fuente de verificación que explica por qué esto no es spam + - `updatedAt` (date): la última vez que se actualizó el indexador + - `tokenMetadata` + - `tokenId` (string): el tokenID del token (siempre 0 si es ERC20) + - `contractAddress` (string): la dirección del contrato del token + - `name` (string): nombre a nivel de token + - `description` (string): la descripción del token + - `image` (string): la imagen como url del token + - `decimals` (string): la cantidad de decimales del token + - `properties` (object): un objeto que contiene las propiedades de los metadatos del token + - `external_url` (string): una url externa donde encontrar el token o más detalles + - `updatedAt` (date): la última vez que se actualizaron los metadatos del token + + +## 4. Tipos de billetera múltiple +Debido a que en este ejemplo usamos el `Web SDK`, que permite usar una billetera Sequence además de su propia `EOA wallet`. El envío de transacciones a la blockchain será diferente, ya que con una `Sequence wallet` puede enviar transacciones en lote para optimizar los costos de gas. En cambio, con `wagmi` usando una EOA solo puede enviar una transacción a la vez. + +Para lograr esto, seguimos algunos pasos para crear una variable de estado local que verifica qué billetera está autorizada. + +```ts +import { useEffect } from "react"; +import { useConnect, useAccount } from "wagmi"; + +function App() { + const { isConnected } = useAccount(); + const { connectors } = useConnect(); + const [isSequence, setIsSequence] = useState(false); + + useEffect(() => { + connectors.map(async (connector) => { + if ((await connector.isAuthorized()) && connector.id === "sequence") { + setIsSequence(true); + } + }); + }, [isConnected]); +} +``` + + + En el protocolo Sequence Market, cuando crea una publicación, se denomina una `request`, y cuando acepta una solicitud se denomina un `order`. + + +## 5. Creación de solicitudes +Para este ejemplo, usaremos `Arbitrum Sepolia USDC` del [faucet de la comunidad](https://faucet.circle.com/) + +Primero, diríjase allí para obtener algunos tokens y así poder crear una publicación con su solicitud. + +--- + +Luego, para crear una solicitud para el libro de órdenes, primero debemos asegurarnos de habilitar el contrato del libro de órdenes del marketplace con aprobación para transferir sus tokens. + +Primero, verificamos que el marketplace esté aprobado para el contrato, mediante cierta lógica. + +```js +const ERC1155Contract = '0x1693ffc74edbb50d6138517fe5cd64fd1c917709' +const MarketPlaceContract = '0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712' + +function App() { + + async function checkERC1155Approval(ownerAddress: string, operatorAddress: string) { + const abi = [ + "function isApprovedForAll(address account, address operator) external view returns (bool)" + ]; + const provider = new ethers.providers.JsonRpcProvider(`https://nodes.sequence.app/arbitrum-sepolia/${process.env.REACT_APP_PROJECT_ACCESSKEY}`); + const contract = new ethers.Contract(ERC1155Contract, abi, provider); + return await contract.isApprovedForAll(ownerAddress, operatorAddress); + } + + const createRequest = async () => { + ... + if(await checkERC1155Approval(address!,MarketPlaceContract)){ + // is approved and only requires a single transaction + ... + } else { // is not approved, so requires multiple transactions + + if(isSequence) { .. perform multi-batch transactions + ... + } else { // is not a sequence wallet + ... + } + } + }; + +} +``` + +Después, necesitaremos crear la transacción con el ABI correcto para generar el calldata esperado para los diferentes casos: aprobado o no aprobado, y si se trata de una billetera Sequence o no. + +```ts +const [requestData, setRequestData] = useState(null); + +const createRequest = async () => { + const sequenceMarketInterface = new ethers.Interface([ + "function createRequest(tuple(bool isListing, bool isERC1155, address tokenContract, uint256 tokenId, uint256 quantity, uint96 expiry, address currency, uint256 pricePerToken)) external nonReentrant returns (uint256 requestId)", + ]); + + const amountBigNumber = ethers.parseUnits(String("0.01"), 6); // ensure to use the proper decimals + + const request = { + isListing: true, + isERC1155: true, + tokenContract: ERC1155Contract, + tokenId: 1, + quantity: 1, + expiry: Date.now() + 7 * 24 * 60 * 60 * 1000, // 1 day + currency: ArbSepoliaUSDCContract, + pricePerToken: amountBigNumber, + }; + + const data = sequenceMarketInterface.encodeFunctionData("createRequest", [ + request, + ]); + + setRequestData(data); // we'll need this in the next step + + if (await checkERC1155Approval(address!, MarketPlaceContract)) { + // is approved and only requires a single transaction + + sendTransaction({ + to: MarketPlaceContract, + data: `0x${data.slice(2, data.length)}`, + gas: null, + }); + } else { + // is not approved, so requires multiple transactions + + const erc1155Interface = new ethers.Interface([ + "function setApprovalForAll(address _operator, bool _approved) returns ()", + ]); + + // is not approved + const dataApprove = erc1155Interface.encodeFunctionData( + "setApprovalForAll", + ["0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712", true] + ); + + const txApprove = { + to: ERC1155Contract, + data: dataApprove, + }; + + const tx = { + to: MarketPlaceContract, + data: data, + }; + + if (isSequence) { + const wallet = sequence.getWallet(); + const signer = wallet.getSigner(421614); + + try { + const res = signer.sendTransaction([txApprove, tx]); + console.log(res); + } catch (err) { + console.log(err); + console.log("user closed the wallet, or, an error occured"); + } + } else { + // is not a sequence wallet + // todo: implement mutex + + sendTransaction({ + to: ERC1155Contract, + data: `0x${dataApprove.slice(2, data.length)}`, + gas: null, + }); + // still need to send acceptRequest transaction + } + } +}; +``` + +Finalmente, en el caso donde la transacción no se realiza desde una billetera sequence y no está aprobada, debemos enviar una transacción una vez que haya un recibo de transacción del hook `useSendTransaction` usando un mutex para confirmar de qué transacción proviene el hash. Esto se hace en una función `useEffect` de React. + + + En programación, una exclusión mutua (mutex) es un objeto de programa que + impide que varios hilos accedan al mismo recurso compartido + simultáneamente. + + +```ts +import { useSendTransaction } from 'wagmi' +import { useMutex } from 'react-context-mutex'; + +function App() { + ... + const [requestData, setRequestData] = useState(null) + const { data: hash, sendTransaction } = useSendTransaction() + const MutexRunner = useMutex(); + const mutexApproveERC1155 = new MutexRunner('sendApproveERC1155'); + + const createRequest = async () => { + ... + if(await checkERC1155Approval(address!,MarketPlaceContract)){ + ... + } else { + if (isSequence) { // is a sequence wallet + ... + } else { // is not a sequence wallet + mutexApproveERC1155.lock() + sendTransaction({ + to: ERC1155Contract, + data: `0x${dataApprove.slice(2,data.length)}`, + gas: null + }) + } + } + }; + + useEffect(() => { + if (mutexApproveERC1155.isLocked() && hash) { + sendTransaction({ + to: MarketPlaceContract, + data: `0x${requestData.slice(2, requestData.length)}`, + gas: null, + }); + mutexApproveERC1155.unlock(); + } + }, [requestData, hash]); +``` + +¡Listo! Ha terminado de crear solicitudes para el protocolo Sequence Market, ahora puede implementar un botón y probar el flujo. + +## 6. Aceptación de órdenes +Ahora que tenemos una orden en el marketplace, necesitamos hacer algunas cosas: +- `Consultar el Marketplace`: consultar el marketplace para obtener un `orderId` para aceptar una orden +- `Saldo de moneda`: verificar el saldo de la moneda usando el indexador +- `Aprobación de token`: verificar la aprobación de la moneda para que el marketplace pueda transferir tokens + +#### Consultar el Marketplace +Consultemos el libro de órdenes del marketplace para obtener el `pricePerToken` y el `orderId` correspondientes. + +```ts + const getTopOrder = async (tokenID: string) => { + const res = await fetch( + "https://marketplace-api.sequence.app/arbitrum-sepolia/rpc/Marketplace/GetTopOrders", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + collectionAddress: ERC1155Contract, + currencyAddresses: [ArbSepoliaUSDCContract], + orderbookContractAddress: MarketPlaceContract, + tokenIDs: [tokenID], + isListing: true, + priceSort: "DESC", // descending based on price to get lowest offer first + }), + }, + ); + const result = await res.json(); + return result.orders[0] // getting the first order from the list + } + + const acceptOrder = async () => { + const tokenID = '1' + const topOrder: any = await getTopOrder(tokenID) + const requiredAmount = topOrder.pricePerToken + ... + if(await checkERC20Balance(requiredAmount)){ + ... + } else { + ... + } + } +``` + +#### Saldo de moneda +Usaremos el indexador para consultar el saldo y ver si el usuario tiene suficientes tokens para pagar la orden. Esto se puede hacer con el siguiente código: + + + Asegúrese de que, al comparar la dirección del contrato del token, esta esté en minúsculas. + + +```js +import { SequenceIndexer } from '@0xsequence/indexer' +... +const checkERC20Balance = async (requiredAmount: any) => { + const indexer = new SequenceIndexer('https://arbitrum-sepolia-indexer.sequence.app', process.env.REACT_APP_PROJECT_ACCESSKEY) + + const contractAddress = ArbSepoliaUSDCContract + const accountAddress = address + + const tokenBalances = await indexer.getTokenBalances({ + contractAddress: contractAddress, + accountAddress: accountAddress, + }) + + let hasEnoughBalance = false + + tokenBalances.balances.map((token) => { + const tokenBalanceBN = ethers.BigNumber.from(token.balance); + const requiredAmountBN = ethers.BigNumber.from(requiredAmount); + if(token.contractAddress == ArbSepoliaUSDCContract && tokenBalanceBN.gte(requiredAmountBN)){ + hasEnoughBalance = true + } + }) + + return hasEnoughBalance + +} + +const acceptOrder = async () => { + const tokenID = '1' + const topOrder: any = await getTopOrder(tokenID) + const requiredAmount = topOrder.pricePerToken + ... + if(await checkERC20Balance(requiredAmount)){ + ... + } else { + ... // provide prompt on screen that user does not have balance + } +} + +``` + +#### Aprobación de token +Luego, verificaremos la aprobación del token de moneda para que el Marketplace pueda transferirlo. + +```ts + const checkERC20Approval = async (ownerAddress: string, spenderAddress: string, tokenContractAddress: string, requiredAmount: string) => { + const abi = [ + "function allowance(address owner, address spender) external view returns (uint256)" + ]; + + const provider = new ethers.providers.JsonRpcProvider(`https://nodes.sequence.app/arbitrum-sepolia/${process.env.REACT_APP_PROJECT_ACCESSKEY}`); + const contract = new ethers.Contract(tokenContractAddress, abi, provider); + const allowance = await contract.allowance(ownerAddress, spenderAddress); + + const requiredAmountBN = ethers.BigNumber.from(requiredAmount); + const allowanceBN = ethers.BigNumber.from(allowance); + + return allowanceBN.gte(requiredAmountBN); + } + + const acceptOrder = async () => { + const tokenID = '1' + const topOrder: any = await getTopOrder(tokenID) + const requiredAmount = topOrder.pricePerToken + + if(await checkERC20Balance(requiredAmount)){ + if(!(await checkERC20Approval(address!,MarketPlaceContract,ArbSepoliaUSDCContract,requiredAmount))){ + ... + } else { + + } + else { + + } + } + } +``` + +Finalmente, completaremos la lógica necesaria enviando realmente una transacción a la blockchain + +Comenzamos con el mismo flujo que antes, considerando enviar una transacción por lotes si se trata de una billetera sequence y no está aprobada, o, si el Marketplace está aprobado para gastar sus tokens, solo se envía una transacción única. + +```ts + ... + const mutexApproveERC20 = new MutexRunner('sendApproveERC20'); + ... + const acceptOrder = async () => { + const topOrder: any = await getTopOrder('1') + const requiredAmount = topOrder.pricePerToken + + const sequenceMarketInterface = new ethers.Interface([ + "function acceptRequest(uint256 requestId, uint256 quantity, address recipient, uint256[] calldata additionalFees, address[] calldata additionalFeeRecipients)", + ]); + + const quantity = 1 + const data = sequenceMarketInterface.encodeFunctionData( + "acceptRequest", + [topOrder.orderId, quantity, address, [], []], + ); + + setAcceptData(data) // we'll need this later, only for Web SDK enabled transactions + + const tx = { + to: MarketPlaceContract, // 0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712 + data: data + } + + if(await checkERC20Balance(requiredAmount)){ + if((await checkERC20Approval(address!,MarketPlaceContract,ArbSepoliaUSDCContract,requiredAmount))){ + sendTransaction({ + to: MarketPlaceContract, + data: `0x${data.slice(2,data.length)}`, + gas: null + }) + } else { + ... + const erc20Interface = new ethers.Interface([ + "function approve(address spender, uint256 amount) external returns (bool)" + ]); + + const spenderAddress = "0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712"; + const maxUint256 = ethers.constants.MaxUint256; + const dataApprove = erc20Interface.encodeFunctionData("approve", [spenderAddress, maxUint256]); + + if(isSequence){ + const wallet = sequence.getWallet() + const signer = wallet.getSigner(421614) + + const txApprove = { + to: ArbSepoliaUSDCContract, // The contract address of the ERC-20 token, replace with actual contract address + data: dataApprove + }; + + try { + const res = await signer.sendTransaction([txApprove, tx]) + console.log(res) + } catch (err) { + console.log(err) + console.log('user closed the wallet, or, an error occured') + } + } else { + mutexApproveERC20.lock() + + sendTransaction({ + to: ArbSepoliaUSDCContract, + data: `0x${dataApprove.slice(2,dataApprove.length)}`, + gas: null + }) + } + } + } +``` + +Luego, en el flujo para no ser una billetera sequence y requerir aprobación, incluiremos otro `useEffect` con la verificación de mutex como antes. + +```ts + ... + const { data: hash, sendTransaction } = useSendTransaction() + ... + useEffect(() => { + if (acceptData && mutexApproveERC20.isLocked()) { + sendTransaction({ + to: MarketPlaceContract, + data: `0x${acceptData.slice(2, acceptData.length)}`, + gas: null, + }); + mutexApproveERC20.unlock(); + } + }, [hash, acceptData]); +``` + +¡Perfecto! Todo está listo si añade el controlador de clics de la función a un botón. + +## 7. (Opcional) Integrar Embedded Wallet en Web SDK +Para que el conector Web SDK sea compatible con [Embedded Wallet](/sdk/headless-wallet/quickstart), necesitaremos instalar algunos paquetes y actualizar nuestro `config.ts` que usamos al principio de la guía. + +La función Embedded Wallet permite transacciones sin confirmación, lo que puede crear una experiencia de usuario más fluida. + +``` +pnpm i @0xsequence/kit@2.0.5-beta.9 @0xsequence/kit-connectors@2.0.5-beta.9 +``` + +```ts +// config.ts +import { arbitrumSepolia, Chain } from "wagmi/chains"; +import { getDefaultWaasConnectors } from "@0xsequence/kit-connectors"; // updated +import { createConfig, http } from "wagmi"; +import { getKitConnectWallets } from "@0xsequence/kit"; // updated + +const chains = [arbitrumSepolia] as [Chain, ...Chain[]]; + +// added environment variables +const projectAccessKey = process.env.REACT_APP_PROJECTACCESSKEY!; +const waasConfigKey = process.env.REACT_APP_WAASCONFIGKEY!; +const googleClientId = process.env.REACT_APP_GOOGLECLIENTID!; +const appleClientId = process.env.REACT_APP_APPLECLIENTID!; +const walletConnectProjectId = process.env.REACT_APP_WALLETCONNECTID!; +const appleRedirectURI = "https://" + window.location.host; // note: update slug to include correct homepage + +const connectors = [ + ...getDefaultWaasConnectors({ + // updated connector type + walletConnectProjectId: walletConnectProjectId, + defaultChainId: 421614, + waasConfigKey, + googleClientId, + appleClientId, + appleRedirectURI, + appName: "demo app", + projectAccessKey, + enableConfirmationModal: false, + }), + ...getKitConnectWallets(projectAccessKey, []), +]; + +const transports: any = {}; + +chains.forEach((chain) => { + transports[chain.id] = http(); +}); + +const config = createConfig({ + transports, + connectors, + chains, +}); + +export { config }; +``` + +El último paso es asegurarse de actualizar a nuestro equipo con las URLs autorizadas de Google y Apple (por ejemplo, [http://localhost:3000](http://localhost:3000)) para llamar al flujo de inicio de sesión de Embedded Wallet. \ No newline at end of file diff --git a/es/guides/databeat-integration.mdx b/es/guides/databeat-integration.mdx new file mode 100644 index 00000000..1dd41ad8 --- /dev/null +++ b/es/guides/databeat-integration.mdx @@ -0,0 +1,192 @@ +--- +title: Guía de integración de Databeat +description: Aprenda a usar el seguimiento de Databeat con aplicaciones Sequence +--- + +# Integración de Databeat +Esta guía explica cómo usar el seguimiento de Databeat, que ya está integrado en el sitio de documentación de Sequence. + +## Seguimiento automático +La documentación de Sequence realiza un seguimiento automático de las vistas de página con Databeat. Este seguimiento se implementa usando JavaScript puro y es completamente del lado del cliente. + +## Seguimiento manual de eventos +También puede rastrear manualmente eventos personalizados usando Databeat en sus propias aplicaciones. Así es como se hace: + +```javascript +// Track a custom event +databeat.track({ + event: 'BUTTON_CLICK', + source: window.location.pathname, + props: { + button_id: 'login-button', + page: 'login' + } +}); + +// Track a view (shorthand method) +databeat.trackView({ + section: 'user-profile', + page_number: 1 +}); + +// Set a custom user session +databeat.session('user-123', { hash: true, agentSalt: true }); +``` + +## Detalles de implementación +Nuestra integración con Databeat se implementa usando JavaScript puro. Así es como funciona: +1. Cargamos el script de Databeat de forma asíncrona +2. Lo inicializamos con nuestro endpoint de host y token de autenticación +3. Rastreamos automáticamente las vistas de página +4. Configuramos listeners para eventos de navegación en SPA + +Puede ver la implementación completa en el archivo [databeat-tracker.js](/snippets/databeat-tracker.js). + +## Uso de Databeat en la aplicación +Para integrar Databeat en su propia aplicación: +1. Instale el paquete: + +```bash +npm install @databeat/tracker@0.9.3 +``` + +2. Inicialice en su JavaScript: + +```javascript +// Using ES modules +import { Databeat } from '@databeat/tracker'; + +// Initialize with your host endpoint and auth token +const databeat = new Databeat( + 'https://databeat.sequence.app', // Host endpoint + 'your-auth-token', // Auth token + { + flushInterval: 1000, // Flush the event queue every 1000ms + defaultEnabled: true, + sessionIds: { hash: true, agentSalt: true }, + initProps: () => { + // Add any default properties for all events + return { + app: 'your-app-name', + environment: window.location.hostname.includes('localhost') ? 'development' : 'production' + }; + } + } +); + +// Track events +databeat.track({ + event: 'VIEW', + source: window.location.pathname, + props: { + title: document.title + } +}); +``` + +3. Para JS puro sin herramientas de construcción (usando CDN): + +```html + + + + + + + +``` + +## Seguimiento de eventos personalizados +Puede rastrear cualquier evento personalizado que sea relevante para su aplicación: + +```javascript +// Track game events +databeat.track({ + event: 'GAME_STARTED', + source: 'game-client', + props: { + level: 1, + character: 'warrior' + } +}); + +databeat.track({ + event: 'LEVEL_COMPLETED', + source: 'game-client', + props: { + level: 1, + score: 1000, + time_spent: 120 + } +}); + +// Track marketplace interactions +databeat.track({ + event: 'ITEM_VIEWED', + source: 'marketplace', + props: { + item_id: '123', + item_name: 'Rare Sword', + category: 'weapons' + } +}); + +databeat.track({ + event: 'PURCHASE_COMPLETED', + source: 'marketplace', + props: { + transaction_id: 'tx-abc123', + amount: 100, + currency: 'USD', + items: [{ id: '123', name: 'Rare Sword', price: 100 }] + } +}); +``` + +## Gestión de sesiones +Databeat le permite gestionar sesiones de usuario: + +```javascript +// Set a custom session ID (will be hashed for privacy) +databeat.session('user@example.com'); + +// Check if tracking is enabled +const isEnabled = databeat.isEnabled(); + +// Enable tracking +databeat.enable(); + +// Disable tracking +databeat.disable(); + +// Get the current session ID +const sessionId = databeat.getSessionId(); + +// Check if the session is anonymous +const isAnon = databeat.isAnon(); +``` + +## Vaciado manual de eventos +Databeat agrupa los eventos y los envía periódicamente, pero se puede vaciar manualmente la cola de eventos: + +```javascript +// Manually flush the event queue +await databeat.flush(); +``` + +Esto ayuda a verificar que los eventos se estén rastreando correctamente durante el desarrollo. \ No newline at end of file diff --git a/es/guides/guide-overview.mdx b/es/guides/guide-overview.mdx new file mode 100644 index 00000000..aea293e1 --- /dev/null +++ b/es/guides/guide-overview.mdx @@ -0,0 +1,60 @@ +--- +title: Guías +description: Resumen de las guías para la infraestructura de Sequence para juegos web3. +mode: wide +sidebarTitle: Resumen +--- + +Siga nuestras guías paso a paso y plantillas de código abierto para acelerar la salida al mercado. + +## Desarrolladores de videojuegos + + + + Aprenda a crear un juego atractivo para iOS y Android que integra Sequence Embedded Wallets para ofrecer un mercado y moneda dentro del juego. + + + + Siga nuestra guía de integración para aprender cómo incorporar una Sequence Embedded Wallet en una aplicación de Telegram y así brindar soporte a los usuarios en cadenas EVM. + + + + Utilice el SDK de Unreal de Sequence para mostrar información de la billetera integrada, firmar mensajes y enviar transacciones. + + + + Con este tutorial, cree un laberinto web donde los artículos de lootbox se generan mediante IA y se acuñan automáticamente en la billetera universal del jugador. + + + + Siga una guía paso a paso para crear una demo de juego web que aprovecha Sequence Embedded Wallet con tokens de logros personalizados dentro del juego. + + + + Impulsa el crecimiento del juego vendiendo artículos directamente a los jugadores. En esta guía, revisaremos los pasos para desplegar un contrato de venta primaria usando cualquier moneda personalizada o existente para una tienda web que utiliza artículos del juego de un contrato ERC1155. + + + + Esta guía cubre la creación de una venta primaria utilizando el SDK de Unity de Sequence. + + + +## Web3 + + + + Aprovechando la API de transacciones de Sequence y un entorno serverless, se crea un servicio de acuñación escalable para NFTs u otras transacciones que maneja automáticamente las complejidades de blockchain como reorganizaciones, gestión de nonce y paralelización de transacciones. + + + + Cree un mercado impulsado por API donde los jugadores puedan acuñar, vender o comprar artículos usando una interfaz web personalizada que utiliza las APIs de Sequence Orderbook. + + + + Guía para consultar información sobre el uso de los usuarios para un proyecto específico utilizando un Cloudflare Worker serverless. + + + + Utilizando la API de metadatos de Sequence, se puede crear, gestionar y almacenar metadatos asociados a NFTs de manera programática desde casi cualquier entorno. Se explica cómo utilizar estas REST-APIs para organizar las colecciones para un juego o experiencia. + + \ No newline at end of file diff --git a/es/guides/jelly-forest-unity-guide.mdx b/es/guides/jelly-forest-unity-guide.mdx new file mode 100644 index 00000000..e7a48b31 --- /dev/null +++ b/es/guides/jelly-forest-unity-guide.mdx @@ -0,0 +1,345 @@ +--- +title: Introducción a Jelly Forest - Guía de juego en Unity +description: La guía de introducción a Jelly Forest - Unity presenta un juego runner 2D con funciones blockchain como inicio de sesión social, mejoras y artículos cosméticos almacenados en una billetera inteligente. +sidebarTitle: Cree un juego en Unity +--- + +Jelly Forest es un juego runner 2D habilitado para blockchain. El juego incluye inicio de sesión social, mejoras en varios niveles (donde los niveles superiores requieren mejoras de nivel inferior como insumos para crear/acuñar), y mejoras cosméticas, todo almacenado en una billetera inteligente no custodial integrada. No hay ventanas emergentes de firma de transacciones ni requisitos de pago de gas para los jugadores. + + +