Acciones y Blinks
Solana Actions o Acciones son APIs que cumplen con especificaciones que devuelven transacciones en la cadena de bloques de Solana para ser previsualizadas, firmadas y enviadas a través de un número diverso de contextos, incluyendo códigos QR, botones + widgets y sitios web a través de Internet. Las Acciones facilitan a los desarrolladores la integración de todo lo que puedes hacer en el ecosistema de Solana directo en su entorno actual, permitiéndoles realizar transacciones en la cadena de bloques sin la necesidad de navegar a otra aplicación o página web.
Los enlaces Blockchain – o blinks – convierten cualquier Solana Action en un enlace compartible y rico en metadatos. Los Blinks permiten a los clientes compatibles con Acciones (como extensiones de billetera del navegador o bots) mostrar capacidades adicionales para el usuario. En un sitio web, un blink podría activar inmediatamente una vista previa de la transacción en una billetera sin tener que ir a una aplicación descentralizada; en Discord, un bot podría ampliar el blink en un conjunto interactivo de botones. Esto brinda la capacidad de interactuar con la cadena de bloques a cualquier superficie web capaz de mostrar una URL.
Primeros Pasos #
Para comenzar rápidamente a crear Acciones personalizadas en Solana:
npm install @solana/actions
- instala el Solana Actions SDK en tu aplicación
- crea un endpoint para la solicitud
GET
que devuelve los metadatos de tu Acción - crea un endpoint que acepte la solicitud
POST
y devuelva la transacción firmable al usuario
Echa un vistazo a este tutorial de
cómo construir una Acción de Solana
usando el SDK @solana/actions
.
También puedes encontrar el código fuente para una Acción que realiza una transferencia SOL nativa y varias otras Acciones de ejemplo en este repo.
Al desplegar tus Acciones personalizadas de Solana en producción:
- asegúrate de que tu aplicación tiene un archivo actions.json válido en la raíz de tu dominio
- asegúrate de que tu aplicación responda a las
cabeceras de Origen Cruzado obligatorias en todos los
endpoints de Acciones, incluyendo el archivo
actions.json
- test and debug your blinks/actions using the Blinks Inspector
Si estás buscando inspiración para construir Acciones y Blinks, revisa el repositorio "Awesome Blinks" que contiene algunas creaciones de la comunidad e incluso ideas para otras nuevas.
Acciones #
La especificación de Solana Actions utiliza un conjunto de APIs estándar para entregar transacciones que pueden ser firmadas (y eventualmente mensajes firmables) desde una aplicación directamente a un usuario. Están alojadas en URLs de acceso público y, por tanto, son accesibles por su URL para que cualquier cliente pueda interactuar con ellas.
Puedes pensar en Acciones como un endpoint de la API que devolverá metadatos y algo para que un usuario firme (ya sea una transacción o un mensaje de autenticación) con su billetera de la cadena de bloques.
La API de Acciones consiste en realizar solicitudes simples GET
y POST
al
endpoint URL de una Acción y gestionar las respuestas que cumplen con la
interfaz de Acciones.
- La solicitud
GET
devuelve metadatos que proporcionan información legible para el cliente sobre qué acciones están disponibles en esta URL, y una lista opcional de acciones relacionadas. - the POST request returns a signable transaction or message that the client then prompts the user's wallet to sign and execute on the blockchain or in another offchain service.
Ejecución y ciclo de vida de las Acciones #
En la práctica, interactuar con Acciones se parece mucho a interactuar con una API REST típica:
- Un cliente realiza una solicitud inicial
GET
a la URL de una Acción para obtener metadatos sobre las Acciones disponibles - El endpoint devuelve una respuesta que incluye metadatos de la acción (como el título y el icono de la aplicación) y una lista de las acciones disponibles para este endpoint
- La aplicación cliente (como una billetera móvil, un chatbot o un sitio web) muestra una interfaz de usuario para que el usuario realice una de las acciones
- Después de que el usuario seleccione una acción (haciendo clic en un botón),
el cliente realiza una solicitud
POST
al endpoint para obtener la transacción que el usuario debe firmar - La billetera facilita al usuario la firma de la transacción y, en última instancia, envía la transacción a la cadena de bloques para su confirmación
Solana Actions Execution and Lifecycle
Al recibir transacciones de una URL de Acciones, los clientes deben gestionar el envío de estas transacciones a la cadena de bloques y gestionar su ciclo de vida.
Las Acciones también admiten cierto nivel de invalidación antes de la ejecución.
Las peticiones GET
y POST
pueden devolver algunos metadatos que indiquen si
la acción puede realizarse (como el campo disabled
).
Por ejemplo: si hubiera una Acción que facilitara la votación de una propuesta
de gobernanza de una DAO cuya ventana de votación se hubiera cerrado, la
solicitud GET
inicial podría devolver el mensaje de error
"Esta propuesta ya no está disponible para votación" y los botones "Votar sí" y
"Votar no" se mostrarían como "desactivados".
Blinks #
Los Blinks (enlaces de blockchain) son aplicaciones cliente que introspeccionan las API de las Acciones y construyen interfaces de usuario en torno a la interacción y ejecución de las Acciones.
Las aplicaciones cliente que admiten blinks simplemente detectan las URL compatibles con Acciones, las analizan y permiten a los usuarios interactuar con ellas en interfaces de usuario estandarizadas.
Cualquier aplicación cliente que realice una introspección completa de una API de Acciones para construir una interfaz completa para ella es un blink. Por lo tanto, no todos los clientes que consumen una API de Acciones son blinks.
Especificación URL de blink #
Una URL blink describe una aplicación cliente que permite a un usuario completar el ciclo de vida de una Acción, incluyendo la firma con su billetera.
https://example.domain/?action=<action_url>
Para que cualquier aplicación cliente se convierta en un blink:
-
La URL de un blink debe contener el parámetro
action
cuyo valor sea una URL codificada como URL de una Acción. Este valor debe ser codificado como URL para no entrar en conflicto con ningún otro parámetro del protocolo. -
La aplicación cliente debe decodificar la URL del parámetro
action
e introspeccionar la API de acción correspondiente (véase URL de una Acción). -
El cliente debe ofrecer una interfaz de usuario que permita completar el ciclo de vida de una Acción, incluida la firma con su billetera.
No todas las aplicaciones cliente blink (por ejemplo: sitios web o dApps) soportarán todas las Acciones. Los desarrolladores de aplicaciones pueden elegir las Acciones que desean admitir en sus interfaces blink.
El siguiente ejemplo muestra una URL blink con el valor action
de
solana-action:https://actions.alice.com/donar
codificado como URL:
https://example.domain/?action=solana-action%3Ahttps%3A%2F%2Factions.alice.com%2Fdonate
Detectando Acciones a través de Blinks #
Los blinks pueden ser enlazados a las Acciones en al menos 3 formas:
-
Compartir una URL de acción explícita:
solana-action:https://actions.alice.com/donar
En este caso, solo los clientes compatibles pueden renderizar el blink. No habrá enlace de previsualización alternativa, o sitio que pueda ser visitado fuera del cliente compatible.
-
Compartir un enlace a un sitio web vinculado a una API de Acciones a través de un archivo
actions.json
en la raíz del dominio del sitio web.Por ejemplo:
https://alice.com/actions.json
asignahttps://alice.com/donar
, la URL de un sitio web en el que los usuarios pueden donar a Alice, a la URL de la APIhttps://actions.alice.com/donar
, en la que se alojan las Acciones para donar a Alice. -
Incrustar una URL de Acción en la URL de un sitio “interstitial” que sepa cómo interpretar las Acciones.
https://example.domain/?action=<action_url>
Los clientes que admiten blinks deben ser capaces de tomar cualquiera de los formatos anteriores y renderizar una interfaz para facilitar la ejecución de la acción directamente en el cliente.
Para los clientes que no admiten blinks, debe haber un sitio web subyacente (haciendo que el navegador se convierta en la alternativa universal).
Si un usuario pulsa en cualquier parte de un cliente que no sea un botón de acción o un campo de entrada de texto debe ir al sitio web subyacente.
Comprobación y verificación de un Blink #
Mientras que las Acciones y Blinks de Solana son un protocolo/especificación que no requieren permisos, las aplicaciones cliente y las billeteras siguen siendo requeridas para facilitar a los usuarios firmar la transacción.
Use the Blinks Inspector tool to inspect, debug, and test your blinks and actions directly in your browser. You can view the GET and POST response payloads, response headers, and test all inputs to each of your linked Actions.
Each client application or wallets may have different requirements on which Action endpoints their clients will automatically unfurl and immediately display to their users on social media platforms.
Por ejemplo: algunos clientes pueden operar con un enfoque de "lista de permitidos" que puede requerir verificación antes de que su cliente implemente una Acción para usuarios como el Registro de Acciones de Dialect (detallado a continuación).
Todos los Blinks seguirán mostrándose y permitiendo firmar en el sitio intersticial de Dialect dial.to, con su estado de registro mostrado en el Blink.
Registro de Acciones de Dialect #
Como un bien público para el ecosistema de Solana, Dialect mantiene un registro público — junto con la ayuda de la Fundación Solana y otros miembros de la comunidad — de enlaces blockchain que tienen procedencia de fuentes conocidas previamente verificadas. A partir del lanzamiento, solo las Acciones que se hayan registrado en el registro de Dialect se desplegarán en Twitter cuando se publique.
Las aplicaciones cliente y billeteras pueden elegir libremente utilizar este registro público u otra solución para ayudar a garantizar la seguridad del usuario. Si no se verifica a través del registro de Dialect, el enlace blockchain no será tocado por el cliente Blink y se representará como una URL típica.
Los desarrolladores pueden solicitar ser verificados por Dialect aquí.
Especificación #
La especificación de Solana Actions consta de secciones clave que forman parte de un flujo de interacción solicitud/respuesta:
- Esquema de URL de Solana Actions que proporciona la URL de una Acción
- Respuesta OPTIONS para pasar los requisitos CORS de la URL de una Acción
- Solicitud GET a la URL de una Acción
- Respuesta GET del servidor
- Solicitud POST a la URL de una Acción
- Respuesta POST del servidor
Cada una de estas solicitudes las realiza el Cliente de Acciones (por ejemplo: aplicación de una billetera, extensión de navegador, dApp, sitio web, etc.) para recopilar metadatos específicos para interfaces de usuario y facilitar la entrada de datos del usuario en la API de Acciones.
Cada una de las respuestas es elaborada por una aplicación (por ejemplo: sitio web, servidor backend, etc.) y devuelta al Cliente de Acciones. En última instancia, proporciona una transacción o mensaje firmable para que una billetera pida al usuario que lo apruebe, firme y se envíe a la cadena de bloques.
The types and interfaces declared within this readme files are often the simplified version of the types to aid in readability.
For better type safety and improved developer experience, the
@solana/actions-spec
package contains more complex type definitions. You can
find the
source code for them here.
Esquema de URL #
Una URL de Acción de Solana describe una solicitud interactiva de una
transacción o mensaje de Solana firmable mediante el protocolo solana-action
.
La solicitud es interactiva porque los parámetros de la URL son utilizados por un cliente para realizar una serie de solicitudes HTTP estandarizadas para componer una transacción o mensaje firmable para que el usuario lo firme con su billetera.
solana-action:<link>
-
Se requiere un único campo
link
como nombre de ruta. El valor debe ser un URL HTTPS absoluto condicionalmente codificado. -
Si la URL contiene parámetros de consulta, deben estar codificados como URL. La codificación URL del valor evita conflictos con cualquier parámetro del protocolo que puede añadirse a través de la especificación.
-
Si la URL no contiene parámetros de consulta, no debe codificarse como URL. Esto produce una URL más corta y un código QR menos denso.
En ambos casos, los clientes deben decodificar el valor. Esto no tiene ningún efecto si el valor no está codificado como URL. Si el valor decodificado no es una URL HTTPS absoluta, la billetera debe rechazarlo como malformado.
Respuesta OPTIONS #
Para permitir el Intercambio de Recursos entre Orígenes
(o CORS por sus siglas en inglés)
dentro de Acciones clientes (incluidos los blinks), todos los endpoints de
Acción deben responder a las solicitudes HTTP para el método OPTIONS
con
cabeceras válidas que permitirán a los clientes pasar comprobaciones CORS para
todas las solicitudes posteriores de su mismo dominio de origen.
Un cliente de Acciones puede realizar solicitudes de
"verificación previa"
al endpoint de la URL de Acción para verificar si la solicitud GET subsecuente a
la URL de Acción pasará todas las comprobaciones de CORS. Estas verificaciones
previas de CORS se realizan utilizando el método HTTP OPTIONS
y deben
responder con todas las cabeceras HTTP requeridas que permitirán a los clientes
de Acción (como los blinks) realizar correctamente todas las solicitudes
posteriores desde su dominio de origen.
Como mínimo, las cabeceras HTTP requeridas incluyen:
Access-Control-Allow-Origin
con el valor*
- esto garantiza que todos los clientes de Acción puedan pasar las comprobaciones CORS de forma segura para realizar todas las solicitudes requeridas
Access-Control-Allow-Methods
con el valorGET,POST,PUT,OPTIONS
- asegura que todos los métodos requeridos de peticiones HTTP sean compatibles con Acciones
Access-Control-Allow-Headers
con un valor mínimo deContent Type, Authorization, Content-Encoding, Accept-Encoding
Por simplicidad, los desarrolladores deben considerar devolver la misma
respuesta y cabecera a las solicitudes OPTIONS
como su respuesta
GET
.
La respuesta del archivo actions.json
también debe devolver cabeceras de
origen cruzado válidas para las solicitudes GET
y OPTIONS
, específicamente
el valor de la cabecera Access-Control-Allow-Origin
de *
.
Consulte actions.json abajo para más detalles.
Solicitud GET #
El cliente de la Acción (por ejemplo: una billetera, extensión del navegador,
etc.) debe realizar una solicitud HTTP GET
JSON al endpoint URL de la Acción.
- La solicitud no debe identificar la billetera ni al usuario.
- El cliente debe realizar la solicitud con una cabecera
Accept-Encoding
. - El cliente debe mostrar el dominio de la URL mientras se realiza la solicitud.
Respuesta GET #
El endpoint de la URL de la Acción (por ejemplo: aplicación o servidor backend)
debe responder con una respuesta HTTP OK
JSON (con una información válida en
el cuerpo) o un error HTTP apropiado.
-
El cliente debe manejar errores HTTP del cliente, errores del servidor, y redireccionamiento de mensajes.
-
El endpoint debe responder con una cabecera
Content-Encoding
para la compresión HTTP. -
El endpoint debe responder con una cabecera
Content-Type:
header deapplication/json
. -
El cliente no debe almacenar en la caché la respuesta excepto según las instrucciones de las cabeceras de caché HTTP.
-
El cliente debe mostrar el
title
(título) y renderizar la imagen delicon
(icono) al usuario.
Error responses (i.e. HTTP 4xx and 5xx status codes) should return a JSON
response body following ActionError
to present a helpful error message to
users. See Action Errors.
Cuerpo de la respuesta GET #
Una respuesta GET
con una respuesta HTTP OK
JSON debe incluir un cuerpo que
siga la especificación de la interfaz:
export type ActionType = "action" | "completed";
export type ActionGetResponse = Action<"action">;
export interface Action<T extends ActionType> {
/** type of Action to present to the user */
type: T;
/** image url that represents the source of the action request */
icon: string;
/** describes the source of the action request */
title: string;
/** brief summary of the action to be performed */
description: string;
/** button text rendered to the user */
label: string;
/** UI state for the button being rendered to the user */
disabled?: boolean;
links?: {
/** list of related Actions a user could perform */
actions: LinkedAction[];
};
/** non-fatal error message to be displayed to the user */
error?: ActionError;
}
-
type
- The type of action being given to the user. Defaults toaction
. The initialActionGetResponse
is required to have a type ofaction
.action
- Standard action that will allow the user to interact with any of theLinkedActions
completed
- Used to declare the "completed" state within action chaining.
-
icon
- El valor debe ser una URL HTTP o HTTPS absoluta de una imagen que representa un icono. El archivo debe ser una imagen SVG, PNG o WebP, o el cliente/billetera debe rechazarlo como malformado. -
title
- El valor debe ser una cadena de caracteres UTF-8 que represente el origen de la solicitud de la acción. Por ejemplo: podría ser el nombre de una marca, tienda, aplicación o persona que realiza la solicitud. -
description
- El valor debe ser una cadena de caracteres UTF-8 que proporcione información sobre la acción. La descripción debe mostrarse al usuario. -
label
- El valor debe ser una cadena de caracteres UTF-8 que se mostrará en un botón para que el usuario haga clic. Ninguna de las etiquetas deben superar las 5 palabras y deben comenzar con un verbo para informar al usuario de la acción a realizar. Por ejemplo: "Votar Si" o "Depositar fondos". -
disabled
- El valor debe ser booleano para representar el estado desactivado del botón renderizado (que muestra la etiqueta). Si no se proporciona ningún valor,disabled
debe ser por defectofalse
(es decir, activado por defecto). Por ejemplo: si el endpoint de la acción es para una votación de gobernanza que se ha cerrado, establezcadisabled=true
y lalabel
podría ser "Voto cerrado". -
error
- Una indicación de error opcional para errores no fatales. Si está presente, el cliente debe mostrarlo al usuario. If set, it should not prevent the client from interpreting the action or displaying it to the user (see Action Errors). For example, the error can be used together withdisabled
to display a reason like business constraints, authorization, the state, or an error of external resource. -
links.actions
- Un arreglo opcional de acciones relacionadas para el endpoint. Se debe mostrar a los usuarios una UI para cada una de las acciones enlistadas y esperar que solo una sea seleccionada para realizarse. Por ejemplo: un endpoint de acción de votación de gobernanza puede devolver tres opciones al usuario como "Votar Sí", "Votar No" y "Abstenerse de votar".-
Si no se proporciona
links.actions
, el cliente debe mostrar un solo botón usando el string raízlabel
y realizar la solicitud POST al mismo endpoint que la solicitud GET inicial. -
Si se proporciona algún
links.actions
, el cliente solo debe mostrar botones y campos de entrada basados en los elementos enumerados en el campolinks.actions
. El cliente no debe mostrar un botón para el contenido dellabel
raíz.
-
export interface LinkedAction {
/** URL endpoint for an action */
href: string;
/** button text rendered to the user */
label: string;
/**
* Parameters to accept user input within an action
* @see {ActionParameter}
* @see {ActionParameterSelectable}
*/
parameters?: Array<TypedActionParameter>;
}
The ActionParameter
allows declaring what input the Action API is requesting
from the user:
/**
* Parameter to accept user input within an action
* note: for ease of reading, this is a simplified type of the actual
*/
export interface ActionParameter {
/** input field type */
type?: ActionParameterType;
/** parameter name in url */
name: string;
/** placeholder text for the user input field */
label?: string;
/** declare if this field is required (defaults to `false`) */
required?: boolean;
/** regular expression pattern to validate user input client side */
pattern?: string;
/** human-readable description of the `type` and/or `pattern`, represents a caption and error, if value doesn't match */
patternDescription?: string;
/** the minimum value allowed based on the `type` */
min?: string | number;
/** the maximum value allowed based on the `type` */
max?: string | number;
}
The pattern
should be a string equivalent of a valid regular expression. This
regular expression pattern should by used by blink-clients to validate user
input before before making the POST request. If the pattern
is not a valid
regular expression, it should be ignored by clients.
The patternDescription
is a human readable description of the expected input
requests from the user. If pattern
is provided, the patternDescription
is
required to be provided.
The min
and max
values allows the input to set a lower and/or upper bounds
of the input requested from the user (i.e. min/max number and or min/max
character length), and should be used for client side validation. For input
type
s of date
or datetime-local
, these values should be a string dates.
For other string based input type
s, the values should be numbers representing
their min/max character length.
If the user input value is not considered valid per the pattern
, the user
should receive a client side error message indicating the input field is not
valid and displayed the patternDescription
string.
The type
field allows the Action API to declare more specific user input
fields, providing better client side validation and improving the user
experience. In many cases, this type will resemble the standard
HTML input element.
The ActionParameterType
can be simplified to the following type:
/**
* Input field type to present to the user
* @default `text`
*/
export type ActionParameterType =
| "text"
| "email"
| "url"
| "number"
| "date"
| "datetime-local"
| "checkbox"
| "radio"
| "textarea"
| "select";
Each of the type
values should normally result in a user input field that
resembles a standard HTML input
element of the corresponding type
(i.e.
<input type="email" />
) to provide better client side validation and user
experience:
text
- equivalent of HTML “text” input elementemail
- equivalent of HTML “email” input elementurl
- equivalent of HTML “url” input elementnumber
- equivalent of HTML “number” input elementdate
- equivalent of HTML “date” input elementdatetime-local
- equivalent of HTML “datetime-local” input elementcheckbox
- equivalent to a grouping of standard HTML “checkbox” input elements. The Action API should returnoptions
as detailed below. The user should be able to select multiple of the provided checkbox options.radio
- equivalent to a grouping of standard HTML “radio” input elements. The Action API should returnoptions
as detailed below. The user should be able to select only one of the provided radio options.- Other HTML input type equivalents not specified above (
hidden
,button
,submit
,file
, etc) are not supported at this time.
In addition to the elements resembling HTML input types above, the following user input elements are also supported:
textarea
- equivalent of HTML textarea element. Allowing the user provide multi-line input.select
- equivalent of HTML select element, allowing the user to experience a “dropdown” style field. The Action API should returnoptions
as detailed below.
When type
is set as select
, checkbox
, or radio
then the Action API
should include an array of options
that each provide a label
and value
at
a minimum. Each option may also have a selected
value to inform the
blink-client which of the options should be selected by default for the user
(see checkbox
and radio
for differences).
This ActionParameterSelectable
can be simplified to the following type
definition:
/**
* note: for ease of reading, this is a simplified type of the actual
*/
interface ActionParameterSelectable extends ActionParameter {
options: Array<{
/** displayed UI label of this selectable option */
label: string;
/** value of this selectable option */
value: string;
/** whether or not this option should be selected by default */
selected?: boolean;
}>;
}
If no type
is set or an unknown/unsupported value is set, blink-clients should
default to text
and render a simple text input.
The Action API is still responsible to validate and sanitize all data from the user input parameters, enforcing any “required” user input as necessary.
For platforms other that HTML/web based ones (like native mobile), the equivalent native user input component should be used to achieve the equivalent experience and client side validation as the HTML/web input types described above.
Ejemplo de respuesta GET #
El siguiente ejemplo de respuesta proporciona una única acción "raíz" que se espera que presente al usuario un único botón con la etiqueta "Reclamar token de acceso":
{
"title": "Eventos HackerHouse,
"icon": "<url-to-image>",
"description": "Reclama tu token de acceso para la Hackerhouse.",
"label": "Reclamar token de acceso" // texto del botón
}
El siguiente ejemplo de respuesta proporciona 3 enlaces de acción relacionados que permiten al usuario hacer clic en uno de los 3 botones para emitir su voto para una propuesta en una DAO:
{
"title": "Plataforma Realms para DAOs",
"icon": "<url-to-image>",
"description": "Vota en la propuesta de gobernanza #1234 de la DAO.",
"label": "Votar",
"links": {
"actions": [
{
"label": "Votar Si", // texto del botón
"href": "/api/propuesta/1234/votar?respuesta=si"
},
{
"label": "Votar No", // texto del botón
"href": "/api/propuesta/1234/votar?respuesta=no"
},
{
"label": "Abstenerse de votar", // texto del botón
"href": "/api/propuesta/1234/votar?respuesta=nulo"
}
]
}
}
Ejemplo de respuesta GET con parámetros #
Los siguientes ejemplos demuestran cómo aceptar la entrada de texto del usuario
(a través de parameters
) e incluir esa entrada en la petición POST
(a través
del campo href
dentro de un LinkedAction
):
El siguiente ejemplo de respuesta proporciona al usuario 3 acciones vinculadas para hacer stake de SOL: un botón denominado "Stake 1 SOL", otro botón denominado "Stake 5 SOL" y un campo de entrada de texto que permite al usuario introducir un valor específico que se enviará a la API de Acciones:
{
"title": "Stake-o-matic",
"icon": "<url-to-image>",
"description": "Stake SOL para ayudar a asegurar la red de Solana.",
"label": "Stake SOL", // no se muestra ya que `links.actions` existe
"links": {
"actions": [
{
"label": "Stake 1 SOL", // texto del boton
"href": "/api/stake?amount=1"
// no hay `parameters` por lo que no es campo de texto
},
{
"label": "Stake 5 SOL", // button text
"href": "/api/stake?amount=5"
// no hay `parameters` por lo que no es campo de texto
},
{
"label": "Stake", // texto del boton
"href": "/api/stake?amount={amount}",
"parameters": [
{
"name": "cantidad", // nombre del campo
"label": "cantidad en SOL" // placeholder del campo de texto
}
]
}
]
}
}
El siguiente ejemplo de respuesta proporciona un único campo de entrada para que
el usuario introduzca un monto
que se envía con la solicitud POST (como
parámetro de consulta o puede utilizarse una sub-ruta):
{
"icon": "<url-to-image>",
"label": "Donar SOL",
"title": "Dona a la Caridad",
"description": "Apoya a esta fundación por medio de donaciones en SOL.",
"links": {
"actions": [
{
"label": "Donar", // texto del botón
"href": "/api/donar/{monto}", // o /api/donar?monto={monto}
"parameters": [
// {monto} campo de entrada
{
"name": "monto", // nombre del campo de la entrada
"label": "Monto en SOL" // placeholder de la entrada de texto
}
]
}
]
}
}
Solicitud POST #
El cliente debe hacer una petición HTTP POST
JSON a la URL de la acción con un
body de:
{
"account": "<account>"
}
account
- El valor debe ser la clave pública codificada en base58 de una cuenta que pueda firmar la transacción.
El cliente debe realizar la solicitud con una
cabecera Accept-Encoding
y la aplicación puede responder con una
cabecera Content-Encoding
para la compresión HTTP.
El cliente debe mostrar el dominio de la URL de la acción mientras se realiza la
solicitud. Si se ha realizado una petición GET
, el cliente también debe
mostrar el title
y renderizar la imagen icon
de esa respuesta GET.
Respuesta POST #
El endpoint POST
de la Acción debe responder con una respuesta JSON HTTP OK
(con un payload válido en el cuerpo) o un error HTTP apropiado.
- El cliente debe gestionar errores HTTP del cliente, errores del servidor, y redirección de mensajes.
- El endpoint debe responder con una cabecera
Content-Type
deapplication/json
.
Error responses (i.e. HTTP 4xx and 5xx status codes) should return a JSON
response body following ActionError
to present a helpful error message to
users. See Action Errors.
Cuerpo de la respuesta POST #
Una respuesta POST
con una respuesta HTTP OK
JSON debe incluir un cuerpo de:
/**
* Response body payload returned from the Action POST Request
*/
export interface ActionPostResponse<T extends ActionType = ActionType> {
/** base64 encoded serialized transaction */
transaction: string;
/** describes the nature of the transaction */
message?: string;
links?: {
/**
* The next action in a successive chain of actions to be obtained after
* the previous was successful.
*/
next: NextActionLink;
};
}
-
transaction
- The value must be a base64-encoded serialized transaction. The client must base64-decode the transaction and deserialize it. -
message
- El valor debe ser una cadena de caracteres UTF-8 que describa la naturaleza de la transacción incluida en la respuesta. El cliente debe mostrar este valor al usuario. Por ejemplo: puede ser el nombre de un artículo que se compra, un descuento aplicado a una compra o una nota de agradecimiento. -
links.next
- An optional value use to "chain" multiple Actions together in series. After the includedtransaction
has been confirmed on-chain, the client can fetch and render the next action. See Action Chaining for more details. -
El cliente y la aplicación deben permitir campos adicionales en el cuerpo de la solicitud y en el cuerpo de la respuesta, que pueden ser añadidos por futuras actualizaciones de la especificación.
La aplicación puede responder con una transacción parcial o totalmente firmada. El cliente y la billetera deben validar la transacción como no confiable.
Respuesta POST - Transacción #
If the transaction
signatures
are empty or the transaction has NOT been partially signed:
- The client must ignore the
feePayer
in the transaction and set thefeePayer
to theaccount
in the request. - The client must ignore the
recentBlockhash
in the transaction and set therecentBlockhash
to the latest blockhash. - El cliente debe serializar y deserializar la transacción antes de firmarla. Esto garantiza un orden coherente de las cuentas, como solución para este problema.
Si la transacción ha sido parcialmente firmada:
- The client must NOT alter the
feePayer
orrecentBlockhash
as this would invalidate any existing signatures. - El cliente debe verificar las firmas existentes y, si alguna no es válida, el cliente debe rechazar la transacción como mal formada.
El cliente sólo debe firmar la transacción con el account
en la solicitud, y
debe hacerlo sólo si se espera una firma para ese account
en la solicitud.
Si se espera alguna firma excepto la firma del account
en la solicitud, el
cliente debe rechazar la transacción como maliciosa.
Action Errors #
Actions APIs should return errors using ActionError
in order to present
helpful error messages to the user. Depending on the context, this error could
be fatal or non-fatal.
export interface ActionError {
/** simple error message to be displayed to the user */
message: string;
}
When an Actions API responds with an HTTP error status code (i.e. 4xx and 5xx),
the response body should be a JSON payload following ActionError
. The error is
considered fatal and the included message
should be presented to the user.
For API responses that support the optional error
attribute (like
ActionGetResponse
), the error is considered non-fatal and the
included message
should be presented to the user.
Action Chaining #
Solana Actions can be "chained" together in a successive series. After an Action's transaction is confirmed on-chain, the next action can be obtained and presented to the user.
Action chaining allows developers to build more complex and dynamic experiences within blinks, including:
- providing multiple transactions (and eventually sign message) to a user
- customized action metadata based on the user's wallet address
- refreshing the blink metadata after a successful transaction
- receive an API callback with the transaction signature for additional validation and logic on the Action API server
- customized "success" messages by updating the displayed metadata (e.g. a new image and description)
To chain multiple actions together, in any ActionPostResponse
include a
links.next
of either:
PostNextActionLink
- POST request link with a same origin callback url to receive thesignature
and user'saccount
in the body. This callback url should respond with aNextAction
.InlineNextActionLink
- Inline metadata for the next action to be presented to the user immediately after the transaction has confirmed. No callback will be made.
export type NextActionLink = PostNextActionLink | InlineNextActionLink;
/** @see {NextActionPostRequest} */
export interface PostNextActionLink {
/** Indicates the type of the link. */
type: "post";
/** Relative or same origin URL to which the POST request should be made. */
href: string;
}
/**
* Represents an inline next action embedded within the current context.
*/
export interface InlineNextActionLink {
/** Indicates the type of the link. */
type: "inline";
/** The next action to be performed */
action: NextAction;
}
NextAction #
After the ActionPostResponse
included transaction
is signed by the user and
confirmed on-chain, the blink client should either:
- execute the callback request to fetch and display the
NextAction
, or - if a
NextAction
is already provided vialinks.next
, the blink client should update the displayed metadata and make no callback request
If the callback url is not the same origin as the initial POST request, no callback request should be made. Blink clients should display an error notifying the user.
/** The next action to be performed */
export type NextAction = Action<"action"> | CompletedAction;
/** The completed action, used to declare the "completed" state within action chaining. */
export type CompletedAction = Omit<Action<"completed">, "links">;
Based on the type
, the next action should be presented to the user via blink
clients in one of the following ways:
-
action
- (default) A standard action that will allow the user to see the included Action metadata, interact with the providedLinkedActions
, and continue to chain any following actions. -
completed
- The terminal state of an action chain that can update the blink UI with the included Action metadata, but will not allow the user to execute further actions.
If no links.next
is not provided, blink clients should assume the current
action is final action in the chain, presenting their "completed" UI state after
the transaction is confirmed.
actions.json #
El propósito del archivo actions.json
es permitirle a una
aplicación indicar a los clientes qué URL de sitios web admiten Acciones de
Solana y proporcionar una asignación que pueda utilizarse para realizar
solicitudes GET a un servidor de API de Acciones.
La respuesta del archivo actions.json
también debe devolver cabeceras de
Origen Cruzado válidas para las solicitudes GET
y OPTIONS
, específicamente
el valor de la cabecera Access-Control-Allow-Origin
de *
.
Vea Respuesta OPTIONS arriba para más detalles.
El archivo actions.json
debe almacenarse y ser accesible universalmente en la
raíz del dominio.
Por ejemplo: si su aplicación web se despliega en mi-sitio.com
, el archivo
actions.json
debe estar accesible en https://mi-sitio.com/actions.json
. Este
archivo también debe ser accesible desde orígenes cruzados a través de cualquier
navegador teniendo un valor de cabecera Access-Control-Allow-Origin
de *
.
Reglas #
El campo rules
(reglas) permite a la aplicación asignar un conjunto de rutas
relativas de un sitio web a un conjunto de otras rutas.
Type: Array
de ActionRuleObject
.
interface ActionRuleObject {
/** ruta relativa (preferida) o absoluta para realizar la asignación de reglas */
pathPattern: string;
/** ruta relativa (preferida) o absoluta que admita solicitudes de acción */
apiPath: string;
}
-
pathPattern
- Un patrón que coincide con cada nombre de ruta entrante. -
apiPath
- Un destino de ubicación definido como una ruta absoluta o una URL externa.
Reglas - pathPattern #
Un patrón que coincide con cada nombre de ruta entrante. Puede ser una ruta absoluta o relativa y admite los siguientes formatos:
-
Coincidencia Exacta: Coincide con la ruta URL exacta.
- Ejemplo:
/exact-path
- Ejemplo:
https://website.com/exact-path
- Ejemplo:
-
Coincidencia por Comodín: Utiliza comodines para hacer coincidir cualquier secuencia de caracteres en la ruta URL. Puede coincidir con segmentos individuales (utilizando
*
) o múltiples (utilizando**
). (Véase Coincidencia de Rutas más abajo).- Ejemplo:
/intercambiar/*
coincidirá con/intercambiar/123
y/intercambiar/abc
, capturando sólo el primer segmento después de/intercambiar/
. - Ejemplo:
/categoria/*/objeto/**
coincidirá con/categoria/123/objeto/456
y/categoria/abc/objeto/def
. - Ejemplo:
/api/actions/intercambiar/*/confirmar
coincidirá con/api/actions/intercambiar/123/confirmar
.
- Ejemplo:
Reglas - apiPath #
La ruta de destino de la acción solicitada. Puede definirse como una ruta absoluta o una URL externa.
- Ejemplo:
/api/categoria/*/objeto/*
- Ejemplo:
https://api.ejemplo.com/v1/donar/*
- Ejemplo:
/api/categoria/*/objeto/*
- Ejemplo:
/api/swap/**
Reglas - Parámetros de Consulta #
Los Parámetros de Consulta de la URL original siempre se conservan y se añaden a la URL asignada.
Reglas - Coincidencia de Rutas #
La siguiente tabla muestra la sintaxis de los patrones de coincidencia de ruta:
Operador | Coincidencias |
---|---|
* | Un único segmento de ruta, sin incluir los caracteres / separadores de ruta. |
** | Coincide con cero o más caracteres, incluido cualquier separador de ruta / entre varios segmentos de ruta. Si se incluyen otros operadores, el operador ** debe ser el último. |
? | Patrón no soportado. |
Ejemplos de reglas #
El siguiente ejemplo muestra una regla de coincidencia exacta para asignar
solicitudes a /comprar
desde la raíz de su sitio a la ruta exacta
/api/comprar
relativa a la raíz de su sitio:
{
"rules": [
{
"pathPattern": "/comprar",
"apiPath": "/api/comprar"
}
]
}
El siguiente ejemplo utiliza la coincidencia de rutas con comodines para asignar
solicitudes a cualquier ruta (excluyendo subdirectorios) en /actions/
desde la
raíz de su sitio a una ruta correspondiente en /api/actions/
relativa a la
raíz de su sitio:
{
"rules": [
{
"pathPattern": "/actions/*",
"apiPath": "/api/actions/*"
}
]
}
El siguiente ejemplo utiliza una coincidencia de rutas con comodines para
asignar solicitudes a cualquier ruta (excluyendo subdirectorios) en /donate/
desde la raíz de su sitio a una ruta absoluta correspondiente
https://api.dialect.com/api/v1/donate/
en un sitio externo:
{
"rules": [
{
"pathPattern": "/donar/*",
"apiPath": "https://api.dialect.com/api/v1/donar/*"
}
]
}
El siguiente ejemplo utiliza la coincidencia de rutas con comodines para una
regla idempotente que asigna solicitudes a cualquier ruta (incluidos los
subdirectorios) en /api/actions/
desde la raíz de su sitio hasta sí mismo:
Las reglas idempotentes permiten a los clientes Blink determinar fácilmente si
una ruta admite solicitudes de API de Acción sin tener que tener el prefijo
solana-action:
ni realizar pruebas adicionales.
{
"rules": [
{
"pathPattern": "/api/actions/**",
"apiPath": "/api/actions/**"
}
]
}
Identidad de la Acción #
La transacción retornada en la respuesta del POST puede incluir una Identidad. Esto permite a los indexadores y plataformas de análisis saber fácilmente y con veracidad qué Proveedor de Acciones (es decir, un servicio) originó la actividad.
La Identidad de la Acción es un par de llaves que se utiliza para firmar un mensaje que tiene un formato especial y se incluye en la transacción mediante una instrucción Memo. Este Mensaje Identificador garantiza la veracidad de la Identidad de la Acción y, por lo tanto, permite atribuir transacciones a un Proveedor de Acciones específico.
No se requiere que el par de llaves firme la transacción en sí. Esto permite que las billeteras y aplicaciones mejoren la capacidad de entrega de las transacciones cuando no hay otras firmas en la transacción devuelta al usuario (consulte transacción de respuesta POST).
Si el caso de uso de un Proveedor de Acciones requiere que su servicio firme previamente la transacción antes de que lo haga el usuario, deben usar este par de llaves como su Identidad de Acción. Esto permitirá incluir una cuenta menos en la transacción, lo que reducirá el tamaño total de las transacciones en 32 bytes.
Mensaje de Identificación de la Acción #
El Mensaje de Identificación de la Acción es una cadena de caracteres UTF-8 separada por dos puntos incluida en una transacción mediante una única instrucción SPL Memo.
protocol:identity:reference:signature
protocol
: el valor del protocolo que se utiliza (establecido comosolana-action
según el esquema URL anterior)identity
: el valor debe ser la dirección de llave pública codificada en base58 del par de llaves de la Identidad de la Acciónreference
: el valor debe ser un arreglo de 32 bytes codificado en base58. Estas pueden ser o no llaves públicas, dentro o fuera de la curva, y pueden corresponder o no con cuentas de Solana.signature
: firma codificada en base58 creada a partir del par de llaves de la Identidad de la Acción que firma solo el valor dereference
.
El valor de reference
debe usarse solo una vez y en una sola transacción. A
los efectos de asociar transacciones con un Proveedor de Acciones, solo se
considera válido el primer uso del valor de reference
.
Las transacciones pueden tener múltiples instrucciones Memo. Al realizar un
getSignaturesForAddress
,
el campo memo
de los resultados devolverá un mensaje que incluye todos los
memos, cada uno separado por punto y coma.
No se deben incluir otros datos en la instrucción Memo del Mensaje de Identificación.
The identity
and the reference
should be included as read-only, non-signer
keys
in the transaction on an instruction that is NOT the Identifier Message Memo
instruction.
La instrucción Memo del Mensaje de Identificación no debe tener ninguna cuenta. Si se proporciona alguna cuenta, el programa Memo requiere que todas esas cuentas provean una firma válida. A los efectos de identificar acciones, esto restringe la flexibilidad y puede degradar la experiencia del usuario. Por lo tanto, se considera un antipatrón y debe evitarse.
Verificación de identidad de la Acción #
Cualquier transacción que incluya la cuenta identity
se puede asociar de
manera verificable con el Proveedor de Acciones en un proceso de varios pasos:
- Obtenga todas las transacciones para una
identity
determinada. - Analice y verifique la cadena de caracteres del memo de cada transacción,
asegurando que
signature
es una firma válida para lareference
. - Verifique que la transacción sea la primera ocurrencia de
reference
en la cadena de bloques:- Si esta transacción es la primera ocurrencia, la transacción se considera verificada y puede atribuirse de manera segura al Proveedor de Acciones.
- Si esta transacción NO es la primera ocurrencia, se considera inválida y, por lo tanto, no se atribuye al Proveedor de Acciones.
Debido a que los validadores de Solana indexan las transacciones según las
llaves de las cuentas, se puede utilizar el método RPC
getSignaturesForAddress
para obtener todas las transacciones que incluyen la cuenta que corresponde a
identity
.
La respuesta de este método del RPC incluye toda la información del campo
memo
. Si se utilizaron varias instrucciones Memo en la transacción, cada
mensaje memo se incluirá en este campo memo
y el verificador deberá analizarlo
para obtener el Mensaje de Verificación de Identidad.
Estas transacciones deben considerarse inicialmente NO VERIFICADAS. Esto se
debe a que no se requiere la identity
para firmar la transacción, lo que
permite que cualquier transacción incluya esta cuenta sin su firma.
Potencialmente inflando de manera artificial el consumo de recursos.
Se debe verificar el Mensaje de Verificación de Identidad para garantizar que la
signature
fue creada por la identity
que firma la reference
. Si la
verificación de la firma falla, la transacción es inválida y debe atribuirse al
Proveedor de Acciones.
Si la verificación de la firma es exitosa, el verificador debe asegurarse de que
esta transacción sea la primera aparición reference
en la cadena de bloques.
Si no es así, la transacción se considera inválida.