Actions et Blinks
Les Actions Solana sont des API conformes aux spécifications qui retournent des transactions sur la blockchain Solana pour être prévisualisées, signées et envoyées via un certain nombre de contextes différents tels que les QR codes, les boutons + widgets et les sites Internet. Les Actions permettent aux développeurs d'intégrer facilement dans leur environnement les choses qu'il est possible de faire dans l'écosystème Solana, ce qui leur permet d'effectuer des transactions blockchain sans avoir à naviguer vers une autre application ou une autre page web.
Les liens blockchain – ou blinks – transforment toute Action Solana en un lien partageable et contenant des métadonnées. Les blinks permettent aux clients sensibles aux Actions (portefeuilles de navigateur, robots) d'afficher des fonctionnalités supplémentaires pour l'utilisateur. Par exemple, sur un site web, un blink peut immédiatement déclencher une prévisualisation de la transaction dans un portefeuille sans passer par une application décentralisée ou encore sur Discord où un robot peut étendre le blink en un ensemble de boutons interactif. La possibilité d'interagir avec la chaîne est ainsi étendue à toute interface web capable d'afficher une URL.
Pour commencer #
Pour commencer rapidement à créer des actions Solana personnalisées :
npm install @solana/actions
- installez le SDK Solana Actions dans votre application
- construisez un point de terminaison API pour la requête GET qui retourne les métadonnées de votre Action
- créez un point de terminaison API qui accepte la requête POST et retourne la transaction signable par l'utilisateur
Regardez ce tutoriel vidéo sur
comment construire une Action Solana
en utilisant le SDK @solana/actions
.
Vous pouvez également trouver le code source pour une Action qui effectue un transfert de SOL natif ici et plusieurs autres exemples d'Actions dans ce dépôt.
Lorsque vous déployez vos Actions Solana personnalisées en production :
- assurez-vous que votre application possède un fichier actions.json valide à la racine de votre domaine
- assurez-vous que votre application répond avec les
en-têtes Cross-Origin requis à tous les points de
terminaison de l'Action, y compris le fichier
actions.json
- testez et déboguez vos blinks/actions en utilisant l'Inspecteur de Blinks
Si vous cherchez de l'inspiration pour construire des Actions et des blinks, consultez le dépôt [Awesome Blinks](https://github. om/solana-developers/awesome-blinks) pour des créations de la communauté et même des idées pour de nouvelles créations.
Actions #
La spécification des Actions Solana utilise un ensemble d'API standard pour retourner des transactions pouvant être signées (et éventuellement des messages pouvant être signés) à partir d'une application directement à un utilisateur. Ils sont hébergés sur des URL accessibles au public et sont donc accessibles par leur URL à tout client qui souhaite interagir avec eux.
Vous pouvez considérer les Actions comme un point de terminaison de l'API qui retournera des métadonnées et quelque chose qu'un utilisateur pourra signer (une transaction ou un message d'authentification) avec son portefeuille blockchain.
L'API Actions consiste à effectuer de simples requêtes GET
et POST
à
l'adresse URL d'une Action et à traiter les réponses conformément à l'interface
Actions.
- la requête GET retourne des métadonnées qui fournissent au client des informations lisibles par l'homme sur les actions disponibles à cette URL, ainsi qu'une liste facultative d'actions associées.
- 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.
Exécution et Cycle de vie des Actions #
En pratique, l'interaction avec les Actions ressemble beaucoup à l'interaction avec une API REST classique :
- un client fait une requête
GET
initiale à une URL d'Action afin d'obtenir les métadonnées sur les actions disponibles - le point de terminaison retourne une réponse qui contient les métadonnées relatives au point de terminaison (comme le titre et l'icône de l'application) et une liste des actions disponibles pour ce point de terminaison
- l'application client (comme un portefeuille mobile, un chat bot ou un site web) affiche une interface utilisateur permettant à l'utilisateur d'effectuer l'une des actions
- après que l'utilisateur a sélectionné une action (en cliquant sur un bouton),
le client envoie une requête
POST
au point de terminaison afin d'obtenir la transaction à faire signer par l'utilisateur - le portefeuille permet à l'utilisateur de signer la transaction et envoie finalement la transaction à la blockchain pour confirmation
Solana Actions Execution and Lifecycle
Lorsqu'ils reçoivent des transactions d'une URL d'Actions, les clients doivent se charger de la soumission de ces transactions à la blockchain et gérer le cycle de vie de leur état.
Les Actions supportent également un certain niveau d'invalidation avant
l'exécution. Les requêtes GET
et POST
peuvent retourner des métadonnées
indiquant si l'action peut être réalisée (comme avec le champ disabled
).
Par exemple, s'il existe un point de terminaison Action qui permet de voter sur une proposition de gouvernance d'une DAO dont la fenêtre de vote est fermée, la requête GET initiale peut retourner le message d'erreur "Cette proposition n'est plus soumise au vote" et les boutons "Voter Oui" et "Voter Non" comme étant "désactivés".
Blinks #
Les Blinks (liens blockchain) sont des applications client qui analysent les API d'Action et construisent des interfaces utilisateur permettant d'interagir avec les Actions et de les exécuter.
Les applications client qui prennent en charge les blinks ne font que détecter les URL compatibles avec les Actions, les analyser et permettre aux utilisateurs d'interagir avec eux au moyen d'interfaces utilisateur standardisées.
Toute application client qui analyse en profondeur une API d'Actions pour en construire une interface complète est un blink. Par conséquent, tous les clients qui utilisent des API d'Actions ne sont pas forcément des blinks.
Spécification d'une URL Blink #
Une URL blink décrit une application client qui permet à un utilisateur d'effectuer le cycle de vie complet de l'exécution d'une Action, y compris la signature avec son portefeuille.
https://example.domain/?action=<action_url>
Pour qu'une application client devienne un blink :
-
L'URL blink doit contenir un paramètre de requête
action
dont la valeur est une URL d'Action encodée. Cette valeur doit être encodée pour ne pas entrer en conflit avec d'autres paramètres du protocole. -
L'application client doit décoder le paramètre de requête
action
et analyser le lien de l'API Action fourni (voir schéma d'URL d'Action). -
Le client doit afficher une interface utilisateur riche qui permet à un utilisateur d'effectuer le cycle de vie complet de l'exécution d'une Action, y compris la signature avec son portefeuille.
Toutes les applications client blink (ex. sites Web ou dApps) ne prendront pas en charge toutes les Actions. En effet, les développeurs d'applications peuvent choisir les Actions qu'ils souhaitent prendre en charge dans leurs interfaces blink.
L'exemple suivant montre une URL blink valide avec une valeur action
égale à
solana-action:https://actions.alice.com/donate
qui est encodée dans l'URL :
https://example.domain/?action=solana-action%3Ahttps%3A%2F%2Factions.alice.com%2Fdonate
Détection des Actions via Blinks #
Les blinks peuvent être liés à des Actions d'au moins 3 façons :
-
Partage d'une URL d'Action explicite :
solana-action:https://actions.alice.com/donate
Dans ce cas, seuls les clients pris en charge peuvent rendre le blink. Il n'y aura pas de prévisualisation de lien de remplacement, ni de site pouvant être visité en dehors du client non supporté.
-
Partage d'un lien vers un site web lié à une API Actions via un fichier
actions.json
présent à la racine du domaine du site web.Par exemple,
https://alice.com/actions.json
associehttps://alice.com/donate
, URL d'un site web où les utilisateurs peuvent faire un don à Alice, à l'URL de l'APIhttps://actions.alice.com/donate
, où sont hébergées les Actions permettant de faire un don à Alice. -
Intégration d'une URL d'Action dans l'URL d'un site "interstitiel" qui comprend comment analyser les Actions.
https://example.domain/?action=<action_url>
Les clients qui prennent en charge les blinks doivent être en mesure d'accepter l'un des formats ci-dessus et d'afficher correctement une interface pour faciliter l'exécution de l'action directement dans le client.
Pour les clients qui ne prennent pas en charge les blinks, il doit y avoir un site web sous-jacent (le navigateur devenant ainsi la solution de remplacement universelle).
Si un utilisateur appuie sur un élément du client qui n'est pas un bouton d'action ou un champ de saisie de texte, il doit être redirigé vers le site sous-jacent.
Test et Vérification d'un Blink #
Bien que les Actions Solana et les blinks soient un protocole/une spécification ne nécessitant pas de permission (permissionless), les applications client et les portefeuilles sont toujours nécessaires pour faciliter la signature des transactions par les utilisateurs.
Utilisez l'Inspecteur de Blinks pour inspecter, déboguer et tester vos blinks et actions directement dans votre navigateur. Vous pouvez afficher les blocs de réponse GET et POST, les en-têtes de réponse et tester toutes les entrées vers chacune de vos Actions liées.
Chaque application client ou portefeuille peut avoir des exigences différentes en ce qui concerne les points de terminaison d'Action que ses clients déploieront automatiquement et afficheront immédiatement à leurs utilisateurs sur les plateformes de médias sociaux.
Par exemple, certains clients peuvent fonctionner selon une approche de "liste d'autorisation" qui peut nécessiter une vérification avant que le client ne déploie une Action pour des utilisateurs tel que le Registre d'Actions de Dialect (détaillé ci-dessous).
Tous les blinks seront toujours affichés et permettront de signer sur le site interstitiel de blinks dial.to de Dialect avec leur statut d'enregistrement affiché dans le blink.
Registre d'Actions de Dialect #
En tant que bien public pour l'écosystème Solana, Dialect maintient un registre public - avec l'aide de la Fondation Solana et d'autres membres de la communauté - des liens blockchain qui ont été pré-vérifiés à partir de sources connues. Dès le lancement, seules les Actions enregistrées dans le registre de Dialect se déploieront dans le fil Twitter lorsqu'elles seront publiées.
Les applications client et les portefeuilles peuvent librement choisir d'utiliser ce registre public ou une autre solution pour garantir la sécurité des utilisateurs. S'il n'est pas vérifié par le registre Dialect, le lien blockchain ne sera pas touché par le client blink et sera rendu comme une URL classique.
Les développeurs peuvent demander à être vérifiés par Dialect ici : dial.to/register
Spécification #
La spécification des Actions Solana consiste en des sections clés qui font partie d'un flux d'interaction de requête/réponse :
- Schéma d'URL d'Action Solana fournissant une URL d'Action
- Réponse OPTIONS à une URL d'Action pour passer les exigences CORS
- Requête GET à une URL d'Action
- Réponse GET du serveur
- Requête POST à une URL d'Action
- Réponse POST du serveur
Chacune de ces requêtes est effectuée par le client d'Action (par exemple application de portefeuille, extension de navigateur, dApp, site web, etc.) afin de recueillir des métadonnées spécifiques pour les interfaces utilisateur enrichies et pour faciliter la saisie de l'utilisateur dans l'API d'Actions.
Chacune des réponses est élaborée par une application (site web, serveur backend, etc.) et renvoyée au client d'Action. Finalement, elle fournit une transaction ou un message pouvant être signé par un portefeuille et invite l'utilisateur à l'approuver, le ou la signer et à l'envoyer à la blockchain.
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.
Schéma d'URL #
Une URL d'Action Solana décrit une requête interactive pour une transaction ou
un message Solana pouvant être signé, en utilisant le protocole solana-action
.
La requête est interactive car les paramètres de l'URL sont utilisés par un client pour effectuer une série de requêtes HTTP standardisées afin de composer une transaction ou un message pouvant être signé par l'utilisateur à l'aide de son portefeuille.
solana-action:<link>
-
Un seul champ
link
est requis comme chemin d'accès. La valeur doit être une URL HTTPS absolue encodée de manière conditionnelle. -
Si l'URL contient des paramètres de requête, elle doit être encodée. L'encodage de la valeur permet d'éviter tout conflit avec les paramètres du protocole Actions qui peuvent être ajoutés via la spécification du protocole.
-
Si l'URL ne contient pas de paramètres de requête, elle ne doit pas être encodée. Il en résulte une URL plus courte et un QR code moins dense.
Dans les deux cas, les clients doivent décoder la valeur. Cela n'a aucun effet si la valeur n'est pas encodée. Si la valeur décodée n'est pas une URL HTTPS absolue, le portefeuille doit la rejeter comme étant malformée.
Réponse OPTIONS #
Afin de permettre le Cross-Origin Resource Sharing
(CORS) dans les
clients Actions (y compris les blinks), tous les points de terminaison Action
doivent répondre aux requêtes HTTP par la méthode OPTIONS
avec des en-têtes
valides ce qui permettra aux clients de passer les contrôles CORS pour toutes
les requêtes ultérieures provenant du même domaine d'origine.
Un client Actions peut effectuer des demandes de
"contrôle préalable"
au point de terminaison de l'URL d'Action afin de vérifier si la demande GET
ultérieure à l'URL d'Action passera toutes les vérifications CORS. Ces contrôles
préalables CORS sont effectués à l'aide de la méthode HTTP OPTIONS
et doivent
répondre avec tous les en-têtes HTTP nécessaires pour permettre aux clients
Action (comme les blinks) d'effectuer correctement toutes les requêtes
ultérieures à partir de leur domaine d'origine.
Au minimum, les en-têtes HTTP requis sont les suivants :
Access-Control-Allow-Origin
avec une valeur de*
- cela permet de s'assurer que tous les clients Action peuvent passer les contrôles CORS en toute sécurité afin d'effectuer toutes les requêtes nécessaires
Access-Control-Allow-Methods
avec une valeur deGET,POST,PUT,OPTIONS
- cela permet de s'assurer que toutes les méthodes de requête HTTP requises sont prises en charge par les Actions
Access-Control-Allow-Headers
avec une valeur minimum deContent-Type, Authorization, Content-Encoding, Accept-Encoding
Pour des raisons de simplicité, les développeurs devraient envisager de
retourner la même réponse et les mêmes en-têtes aux requêtes OPTIONS
que leur
réponse GET
.
La réponse du fichier actions.json
doit également retourner des en-têtes
Cross-Origin valides pour les requêtes GET
et OPTIONS
, en particulier la
valeur d'en-tête Access-Control-Allow-Origin
de *
.
Voir actions.json ci-dessous pour plus de détails.
Requête GET #
Le client d'Action (par exemple portefeuille, extension de navigateur, etc.)
doit effectuer une requête JSON HTTP GET
au point de terminaison de l'URL
d'Action.
- La requête ne doit pas identifier le portefeuille ou l'utilisateur.
- Le client doit faire la requête avec un
en-tête
Accept-Encoding
. - Le client doit afficher le domaine de l'URL lors de la requête.
Réponse GET #
Le point de terminaison de l'URL d'Action (par exemple application ou serveur
backend) doit répondre par une réponse JSON HTTP OK
(avec une charge utile
valide dans le corps) ou par une erreur HTTP appropriée.
-
Le client doit gérer les erreurs de client HTTP, les erreurs de serveur et les réponses de redirection.
-
Le point de terminaison doit répondre avec un en-tête
Content-Encoding
pour la compression HTTP. -
Le point de terminaison doit répondre avec un en-tête
Content-Type
de typeapplication/json
. -
Le client ne doit pas mettre la réponse en cache, sauf si les en-têtes de réponse HTTP de mise en cache l'indiquent.
-
Le client doit afficher le
title
et rendre l'imageicon
à l'utilisateur.
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.
Corps de la réponse GET #
Une réponse GET
avec une réponse JSON HTTP OK
doit inclure dans le corps une
charge utile conforme à la spécification de l'interface :
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
- La valeur doit être une URL HTTP ou HTTPS absolue d'une image d'icône. Le fichier doit être une image SVG, PNG ou WebP, sinon le client/le portefeuille doit le rejeter comme étant malformé. -
title
- La valeur doit être une chaîne UTF-8 qui représente la source de la requête d'action. Il peut s'agir, par exemple, du nom d'une marque, d'un magasin, d'une application ou de la personne qui fait la requête. -
description
- La valeur doit être une chaîne UTF-8 qui fournit des informations sur l'action. La description doit être affichée à l'utilisateur. -
label
- La valeur doit être une chaîne UTF-8 qui sera affichée sur un bouton sur lequel l'utilisateur pourra cliquer. Toutes les étiquettes (labels) ne doivent pas dépasser 5 mots et doivent commencer par un verbe pour renforcer l'action que vous voulez que l'utilisateur fasse. Par exemple, "Mint NFT", "Votez Oui" ou "Staker 1 SOL". -
disabled
- La valeur doit être un booléen pour représenter l'état désactivé du bouton affiché (qui affiche la chaînelabel
). Si aucune valeur n'est fournie, la valeur par défaut dedisabled
estfalse
(c'est-à-dire activée par défaut). Par exemple, si le point de terminaison de l'action concerne un vote de gouvernance qui a été clôturé, définissezdisabled=true
et lelabel
pourrait être "Vote Clôturé". -
error
- Une indication d'erreur facultative pour les erreurs non fatales. Si l'indication est présente, le client doit l'afficher à l'utilisateur. 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 tableau optionnel d'actions associées au point de terminaison. Une interface utilisateur doit présenter aux utilisateurs chacune des actions listées et il doit leur être demandé de n'en effectuer qu'une seule. Par exemple, un point de terminaison d'action de vote de gouvernance peut renvoyer trois options à l'utilisateur : "Voter Oui", "Voter Non" et "S'abstenir de Voter".-
Si aucun
links.actions
n'est fourni, le client doit afficher un seul bouton en utilisant la chaînelabel
de la racine et envoyer une requête POST au même point de terminaison de l'URL d'action que la requête GET initiale. -
Si des
links.actions
sont fournis, le client ne doit afficher les boutons et les champs de saisie qu'en fonction des éléments listés dans le champlinks.actions
. Le client ne doit donc pas afficher de bouton pour le contenu dulabel
de la racine.
-
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 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 to 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.
Exemple de réponse GET #
L'exemple de réponse suivant fournit une action "root" unique qui doit être présentée à l'utilisateur sous la forme d'un bouton unique intitulé "Claim Access Token" (Réclamer un Jeton d'Accès) :
{
"title": "HackerHouse Events",
"icon": "<url-to-image>",
"description": "Claim your Hackerhouse access token.",
"label": "Claim Access Token" // button text
}
L'exemple de réponse suivant fournit 3 liens d'action associée qui permettent à l'utilisateur de cliquer sur l'un des 3 boutons pour voter lors d'une proposition de DAO :
{
"title": "Realms DAO Platform",
"icon": "<url-to-image>",
"description": "Vote on DAO governance proposals #1234.",
"label": "Vote",
"links": {
"actions": [
{
"label": "Vote Yes", // button text
"href": "/api/proposal/1234/vote?choice=yes"
},
{
"label": "Vote No", // button text
"href": "/api/proposal/1234/vote?choice=no"
},
{
"label": "Abstain from Vote", // button text
"href": "/api/proposal/1234/vote?choice=abstain"
}
]
}
}
Exemple de réponse GET avec Paramètres #
Les exemples de réponse suivants montrent comment accepter une saisie de texte
de la part de l'utilisateur (via des parameters
(paramètres)) et inclure cette
saisie dans le point de terminaison de la requête POST finale (via le champ
href
d'une LinkedAction
) :
L'exemple de réponse suivant fournit à l'utilisateur 3 actions associées pour staker des SOL : un bouton libellé "Staker 1 SOL", un autre bouton libellé "Staker 5 SOL" et un champ de saisie de texte qui permet à l'utilisateur d'entrer une valeur spécifique de "amount" (montant) qui sera envoyée à l'API d'Action :
{
"title": "Stake-o-matic",
"icon": "<url-to-image>",
"description": "Stake SOL to help secure the Solana network.",
"label": "Stake SOL", // not displayed since `links.actions` are provided
"links": {
"actions": [
{
"label": "Stake 1 SOL", // button text
"href": "/api/stake?amount=1"
// no `parameters` therefore not a text input field
},
{
"label": "Stake 5 SOL", // button text
"href": "/api/stake?amount=5"
// no `parameters` therefore not a text input field
},
{
"label": "Stake", // button text
"href": "/api/stake?amount={amount}",
"parameters": [
{
"name": "amount", // field name
"label": "SOL amount" // text input placeholder
}
]
}
]
}
}
L'exemple de réponse suivant fournit un champ de saisie unique permettant à
l'utilisateur d'entrer un amount
(montant) qui est envoyé avec la requête POST
(soit en tant que paramètre de requête, soit en tant que sous-chemin) :
{
"icon": "<url-to-image>",
"label": "Donate SOL",
"title": "Donate to GoodCause Charity",
"description": "Help support this charity by donating SOL.",
"links": {
"actions": [
{
"label": "Donate", // button text
"href": "/api/donate/{amount}", // or /api/donate?amount={amount}
"parameters": [
// {amount} input field
{
"name": "amount", // input field name
"label": "SOL amount" // text input placeholder
}
]
}
]
}
}
Requête POST #
Le client doit faire une requête JSON HTTP POST
à l'URL de l'action avec dans
le corps une charge utile contenant :
{
"account": "<account>"
}
account
- La valeur doit être la clé publique encodée en base58 d'un compte qui peut signer la transaction.
Le client doit faire la requête avec un en-tête Accept-Encoding et l'application peut répondre avec un en-tête Content-Encoding pour la compression HTTP.
Le client doit afficher le domaine de l'URL de l'action lorsque la requête est
effectuée. Si une requête GET
a été faite, le client doit également afficher
le title
et afficher l'image icon
de cette réponse GET.
Réponse POST #
Le point de terminaison POST
de l'Action doit répondre par une réponse JSON
HTTP OK
(avec une charge utile valide dans le corps) ou par une erreur HTTP
appropriée.
- Le client doit gérer les erreurs de client HTTP, les erreurs de serveur et les réponses de redirection.
- Le point de terminaison doit répondre avec un
en-tête
Content-Type
de typeapplication/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.
Corps de la réponse POST #
Une réponse POST
avec une réponse JSON HTTP OK
doit inclure dans le corps de
la charge utile :
/**
* 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
- La valeur doit être une chaîne UTF-8 décrivant la nature de la transaction incluse dans la réponse. Le client doit afficher cette valeur à l'utilisateur. Il peut s'agir, par exemple, du nom d'un article acheté, d'une réduction appliquée lors d'un achat ou d'une note de remerciement. -
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. -
Le client et l'application doivent autoriser des champs supplémentaires dans le corps de la requête et dans le corps de la réponse qui pourront être ajoutés par de futures mises à jour de la spécification.
L'application peut répondre par une transaction partiellement ou totalement signée. Le client et le portefeuille doivent valider la transaction comme étant non fiable.
Réponse POST - Transaction #
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. - Le client doit sérialiser et désérialiser la transaction avant de la signer. Cela permet d'assurer un ordre cohérent des clés de compte et constitue une solution de contournement à ce problème.
Si la transaction a été partiellement signée :
- The client must NOT alter the
feePayer
orrecentBlockhash
as this would invalidate any existing signatures. - Le client doit vérifier les signatures existantes, et si l'une d'entre elles n'est pas valide, il doit rejeter la transaction comme étant malformée.
Le client ne doit signer la transaction qu'avec le account
indiqué dans la
requête, et ne doit le faire que si une signature pour le account
indiqué dans
la requête est attendue.
Si une signature autre que celle du account
présent dans la requête est
attendue, le client doit rejeter la transaction comme étant malveillante.
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 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 #
L'objectif du fichier actions.json
est de permettre à une
application d'indiquer aux clients quelles URL de sites web prennent en charge
les Actions Solana et de fournir un mappage qui peut être utilisé pour effectuer
des requêtes GET à un serveur API d'Actions.
La réponse du fichier actions.json
doit également retourner des en-têtes
Cross-Origin valides pour les requêtes GET
et OPTIONS
, en particulier la
valeur d'en-tête Access-Control-Allow-Origin
de *
.
Voir Réponse OPTIONS ci-dessus pour plus de détails.
Le fichier actions.json
doit être stocké et universellement accessible à la
racine du domaine.
Par exemple, si votre application web est déployée à l'adresse my-site.com
alors le fichier actions.json
doit être accessible à l'adresse
https://my-site.com/actions.json
. Ce fichier doit également être Cross-Origin
accessible via n'importe quel navigateur en ayant une valeur d'en-tête
Access-Control-Allow-Origin
de *
.
Règles #
Le champ rules
(règles) permet à l'application de mapper un ensemble de
chemins d'accès relatifs d'un site web à un ensemble d'autres chemins d'accès.
Type: Array
de ActionRuleObject
.
interface ActionRuleObject {
/** relative (preferred) or absolute path to perform the rule mapping from */
pathPattern: string;
/** relative (preferred) or absolute path that supports Action requests */
apiPath: string;
}
-
pathPattern
- Un modèle qui correspond à chaque chemin d'accès entrant. -
apiPath
- Une destination définie comme un chemin d'accès absolu ou une URL externe.
Règles - pathPattern #
Un modèle qui correspond à chaque chemin d'accès entrant. Il peut s'agir d'un chemin absolu ou relatif et prend en charge les formats suivants :
-
Exact Match (Correspondance Exacte) : Correspond au chemin d'accès exact de l'URL.
- Exemple :
/exact-path
- Exemple :
https://website.com/exact-path
- Exemple :
-
Wildcard Match (Correspondance avec des Caractères Génériques) : Utilise des caractères génériques pour correspondre à n'importe quelle séquence de caractères dans le chemin d'accès de l'URL. Il peut s'agir d'un seul segment (en utilisant
*
) ou de plusieurs segments (en utilisant**
). (voir Correspondance des Chemins ci-dessous).- Exemple :
/trade/*
correspondra à/trade/123
et à/trade/abc
, capturant seulement le premier segment après/trade/
. - Exemple :
/category/*/item/**
correspondra à/category/123/item/456
et à/category/abc/item/def
. - Exemple :
/api/actions/trade/*/confirm
correspondra à/api/actions/trade/123/confirm
.
- Exemple :
Règles - apiPath #
Le chemin de destination de la requête d'action. Il peut être défini comme un nom de chemin absolu ou une URL externe.
- Exemple :
/api/exact-path
- Exemple :
https://api.example.com/v1/donate/*
- Exemple :
/api/category/*/item/*
- Exemple :
/api/swap/**
Règles - Paramètres de Requête #
Les paramètres de requête de l'URL d'origine sont toujours conservés et ajoutés à l'URL mappée.
Règles - Correspondance des Chemins #
Le tableau suivant présente la syntaxe des modèles de correspondance de chemin :
Opérateur | Correspondance |
---|---|
* | Un seul segment de chemin sans inclure le séparateur de chemin / caractères environnant. |
** | Correspond à zéro ou plusieurs caractères, y compris tout séparateur de chemin / caractères entre plusieurs segments de chemin. Si d'autres opérateurs sont inclus, l'opérateur ** doit être le dernier opérateur. |
? | Modèle non pris en charge. |
Exemples de règles #
The following example demonstrates an exact match rule to map requests to /buy
from your site's root to the exact path /api/buy
relative to your site's root:
{
"rules": [
{
"pathPattern": "/buy",
"apiPath": "/api/buy"
}
]
}
L'exemple suivant utilise la correspondance des chemins d'accès avec des
caractères génériques pour faire correspondre les requêtes à n'importe quel
chemin d'accès (en excluant les sous-répertoires) sous /actions/
depuis la
racine de votre site à un chemin d'accès correspondant sous /api/actions/
relatif à la racine de votre site :
{
"rules": [
{
"pathPattern": "/actions/*",
"apiPath": "/api/actions/*"
}
]
}
L'exemple suivant utilise la correspondance des chemins d'accès avec des
caractères génériques pour faire correspondre les requêtes à n'importe quel
chemin d'accès (en excluant les sous-répertoires) sous /donate/
depuis la
racine de votre site à un chemin d'accès absolu correspondant,
https://api.dialect.com/api/v1/donate/\`, sur un site externe :
{
"rules": [
{
"pathPattern": "/donate/*",
"apiPath": "https://api.dialect.com/api/v1/donate/*"
}
]
}
L'exemple suivant utilise la correspondance des chemins d'accès avec des
caractères génériques pour une règle idempotente pour faire correspondre les
requêtes à n'importe quel chemin d'accès (y compris les sous-répertoires) sous
/api/actions/
de la racine de votre site à lui-même :
Les règles idempotentes permettent aux clients blink de déterminer plus
facilement si un chemin donné prend en charge les requêtes de l'API Action
sans avoir à être préfixé par l'URI solana-action:
ou à effectuer des tests
de réponse supplémentaires.
{
"rules": [
{
"pathPattern": "/api/actions/**",
"apiPath": "/api/actions/**"
}
]
}
Identité d'Action #
Les points de terminaison d'Action peuvent inclure une Action Identity (Identité d'Action) dans les transactions qui sont retournées dans leur réponse POST que l'utilisateur doit signer. Cela permet aux indexeurs et aux plateformes d'analyse d'attribuer facilement et de manière vérifiable l'activité sur la chaîne à un Action Provider (Fournisseur d'Action) en particulier (c'est-à-dire un service).
L'Identité d'Action est une paire de clés utilisée pour signer un message spécifiquement formaté qui est inclus dans la transaction à l'aide d'une instruction Memo. Ce Identifier Message (Message d'Identification) peut être attribué de manière vérifiable à une Identité d'Action en particulier, et donc attribuer des transactions à un Fournisseur d'Action en particulier.
La paire de clés n'est pas nécessaire pour signer la transaction. Cela permet aux portefeuilles et aux applications d'améliorer la délivrabilité des transactions lorsqu'aucune autre signature ne figure dans la transaction retournée à l'utilisateur (voir Réponse POST - Transaction).
Si le cas d'utilisation d'un Fournisseur d'Action nécessite que ses services backend pré-signent la transaction avant l'utilisateur, il doit utiliser cette paire de clés comme Identité d'Action. Cela permettra d'inclure un compte de moins dans la transaction, réduisant la taille totale de la transaction de 32 octets.
Message d'Identification d'Action #
Le Message d'Identification d'Action est une chaîne UTF-8 séparée par deux points, incluse dans une transaction à l'aide d'une seule instruction SPL Memo.
protocol:identity:reference:signature
protocol
- La valeur du protocole utilisé (fixé àsolana-action
selon le Schéma d'URL ci-dessus)identity
- La valeur doit être l'adresse de la clé publique encodée en base58 de la paire de clés de l'Identité d'Actionreference
- La valeur doit être un tableau de 32 octets encodé en base58. Il peut s'agir ou non de clés publiques, sur ou en dehors de la courbe, et correspondre ou non à des comptes sur Solana.signature
- signature encodée en base58 créée à partir de la paire de clés de l'Identité d'Action et signant uniquement la valeurreference
.
La valeur reference
ne doit être utilisée qu'une seule fois et dans une seule
transaction. Afin d'associer des transactions à un Fournisseur d'Actions, seule
la première utilisation de la valeur reference
est considérée comme valide.
Les transactions peuvent avoir plusieurs instructions Mémo. Lors de l'exécution
d'un
getSignaturesForAddress
,
le champ memo
résultant retournera le message de chaque instruction memo sous
la forme d'une chaîne unique, chaque instruction étant séparée par un
point-virgule.
Aucune autre donnée ne doit être incluse dans l'instruction Memo du Message d'Identification.
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.
L'instruction Memo du Message d'Identification doit avoir aucun compte fourni. Si des comptes sont fournis, le programme Memo exige que ces comptes soient des signataires valides. Pour identifier les actions, cela limite la flexibilité et peut dégrader l'expérience utilisateur. C'est pourquoi il est considéré comme un anti-modèle et doit être évité.
Vérification de l'Identité d'Action #
Toute transaction qui inclut le compte identity
peut être associée de manière
vérifiable au Fournisseur d'Actions grâce à un processus en plusieurs étapes :
- Obtenir toutes les transactions pour une
identity
donnée. - Analyser et vérifier la chaîne mémo de chaque transaction, en s'assurant que
la
signature
est valide pour lareference
stockée. - Vérifier que la transaction en question est la première occurrence sur la
chaine de la
reference
:- If this transaction is the first occurrence, the transaction is considered verified and can be safely attributed to the Action Provider.
- Si cette transaction n'est PAS la première occurrence, elle est considérée comme invalide et n'est donc pas attribuée au Fournisseur d'Actions.
Comme les validateurs Solana indexent les transactions selon les clés de compte,
la méthode RPC
getSignaturesForAddress
peut être utilisée pour localiser toutes les transactions incluant le compte
identity
.
La réponse de cette méthode RPC inclut toutes les données Memo dans le champ
memo
. Si plusieurs instructions Memo ont été utilisées dans la transaction,
chaque message memo sera inclus dans le champ memo
et devra être analysé en
conséquence par le vérificateur afin d'obtenir le Identity Verification Message
(Message de Vérification d'Identité).
Ces transactions doivent être initialement considérées comme NON VÉRIFIÉES.
Cela est dû au fait que l'identity
n'est pas requise pour signer la
transaction, ce qui permet à toute transaction d'inclure ce compte en tant que
non-signataire. Cela peut ainsi potentiellement gonfler artificiellement le
nombre d'attributions et d'utilisations.
Le Message de Vérification d'Identité doit être vérifié pour s'assurer que la
signature
a été créée par l'identity
qui signe la reference
. Si la
vérification de la signature échoue, la transaction n'est pas valide et doit
être attribuée au Fournisseur d'Action.
Si la vérification de la signature réussie, le vérificateur doit s'assurer que
cette transaction est la première occurrence sur la chaîne de la reference
. Si
ce n'est pas le cas, la transaction est considérée comme invalide.