Actions e Blinks
Solana Actions são APIs que seguem especificações e retornam transações na blockchain da Solana para serem visualizadas, assinadas e enviadas em diversos contextos, incluindo QR codes, botões, widgets e sites na internet. Actions facilitam a vida dos desenvolvedores ao permitir que eles integrem as funcionalidades do ecossistema Solana diretamente em seus ambientes, possibilitando a realização de transações blockchain sem precisar sair do aplicativo ou página atual.
Links de blockchain – ou blinks – transformam qualquer Solana Action em um link compartilhável e rico em metadados. Blinks permitem que clients que entendem Actions (como carteiras de extensão de navegador e bots) exibam capacidades adicionais para o usuário. Em um site, um blink pode imediatamente acionar uma pré-visualização de transação em uma carteira sem ir para um aplicativo descentralizado; no Discord, um bot pode expandir o blink em um conjunto interativo de botões. Isso leva a capacidade de interação on-chain para qualquer superfície web capaz de exibir uma URL.
Get Started #
To quickly get started with creating custom Solana Actions:
npm install @solana/actions
- install the Solana Actions SDK in your application
- build an API endpoint for the GET request that returns the metadata about your Action
- create an API endpoint that accepts the POST request and returns the signable transaction for the user
Checkout this video tutorial on
how to build a Solana Action
using the @solana/actions
SDK.
You can also find the source code for an Action that performs a native SOL transfer here and several other example Actions in this repo.
When deploying your custom Solana Actions to production:
- ensure your application has a valid actions.json file at the root of your domain
- ensure your application responds with the
required Cross-Origin headers on all Action endpoints,
including the
actions.json
file - test and debug your blinks/actions using the Blinks Inspector
If you are looking for inspiration around building Actions and blinks, checkout the Awesome Blinks repository for some community creations and even ideas for new ones.
Action #
A especificação Solana Actions usa um conjunto de APIs padrão para entregar transações assináveis (e eventualmente mensagens assináveis) de um aplicativo diretamente para um usuário. Elas são hospedadas em URLs acessíveis publicamente e, portanto, acessíveis por qualquer client via URL para interação.
Você pode pensar em Actions como um endpoint de API que retornará metadados e algo para o usuário assinar (uma transação ou uma mensagem de autenticação) com sua carteira blockchain.
A API Actions consiste em fazer requisições simples GET
e POST
para o
endpoint da URL de uma Action e tratar as respostas que seguem a interface de
Actions.
- A requisição GET retorna metadados que fornecem informações legíveis para o client sobre quais ações estão disponíveis nessa URL, e uma lista opcional de ações 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.
Execução e Ciclo de Vida de uma Action #
Na prática, interagir com Actions é bem parecido com interagir com uma API REST típica:
- um client faz a requisição inicial
GET
para uma URL Action, para obter metadados sobre as Actions disponíveis - o endpoint retorna uma resposta que inclui metadados sobre o endpoint (como o título e o ícone do aplicativo) e uma lista das ações disponíveis para esse endpoint
- o aplicativo client (como uma carteira móvel, bot de chat ou site) exibe uma interface para o usuário realizar uma das ações
- depois que o usuário seleciona uma ação (clicando em um botão), o client faz
uma requisição
POST
para o endpoint obter a transação que o usuário deve assinar - a carteira facilita a assinatura da transação pelo usuário e, finalmente, envia a transação para a blockchain para confirmação
Execução e Ciclo de Vida da Solana Actions
Ao receber transações de uma URL de Actions, os clients devem gerenciar o envio dessas transações para a blockchain e gerenciar seu ciclo de vida de estado.
Actions também suportam algum nível de invalidação antes da execução. As
requisições GET
e POST
podem retornar alguns metadados que informam se a
ação pode ser realizada (como com o campo disabled
).
Por exemplo, se houver um endpoint de Action que facilita a votação em uma proposta de governança de DAO cujo período de votação já encerrou, a requisição GET inicial pode retornar a mensagem de erro "Esta proposta não está mais aberta para votação" e os botões "Votar Sim" e "Votar Não" como "desabilitados".
Blinks #
Blinks (links de blockchain) são aplicações client que inspecionam APIs de Action e constroem interfaces de usuário para interagir e executar Actions.
Aplicações client que suportam blinks simplesmente detectam URLs compatíveis com Actions, as interpretam e permitem que os usuários interajam com elas em interfaces de usuário padronizadas.
Qualquer aplicação client que inspecione completamente uma API de Actions para construir uma interface completa para ela é um blink. Portanto, nem todos os clients que consomem APIs Actions são blinks.
Especificação de URL de Blink #
Uma URL de blink descreve uma aplicação client que permite ao usuário completar todo o ciclo de vida da execução de uma Action, incluindo assinar com sua carteira.
https://example.domain/?action=<action_url>
Para qualquer aplicação client se tornar um blink:
-
A URL blink deve conter um parâmetro de consulta
action
cujo valor é uma URL de Action codificada (URL da ação](#url-scheme). Este valor deve ser URL-encoded para não conflitar com outros parâmetros de protocolo. -
A aplicação client deve URL-decode o parâmetro de consulta
action
e analisar o link da API Action fornecido (veja esquema de URL Action). -
O client deve renderizar uma interface de usuário rica que permita ao usuário completar o ciclo completo de execução de uma Action(#action-execution-and-lifecycle), incluindo a assinatura com sua carteira.
Nem todas as aplicações client de blink (por exemplo, sites ou dApps) suportarão todas as Actions. Os desenvolvedores de aplicativos podem escolher quais Actions desejam suportar dentro de suas interfaces de blink.
O exemplo a seguir demonstra uma URL de blink válida com um valor action
de
solana-action:https://actions.alice.com/donate
que está codificado na URL:
https://example.domain/?action=solana-action%3Ahttps%3A%2F%2Factions.alice.com%2Fdonate
Detectando Actions via Blinks #
Blinks podem ser vinculados aos Actions de pelo menos 3 maneiras:
-
Compartilhando uma URL Action explícita: solana-action:https://actions.alice.com/donate
Nesse caso, apenas clients compatíveis podem renderizar o blink. Não haverá uma pré-visualização do link ou um site que possa ser visitado fora do client não compatível.
-
Compartilhando um link para um site que esteja vinculado a uma API de Actions por meio de um arquivo
actions.json
file na raiz do domínio do site.Por exemplo,
https://alice.com/actions.json
mapeiahttps://alice.com/donate
, uma URL de site onde os usuários podem doar para Alice, para a URL da APIhttps://actions.alice.com/donate
, onde Actions para doações a Alice estão hospedadas. -
Incorporando uma URL de Action em uma URL de site "intermediário" que entende como analisar Actions.
https://example.domain/?action=<action_url>
Clients que suportam blinks devem ser capazes de usar qualquer um dos formatos acima e renderizar corretamente uma interface para facilitar a execução da ação diretamente no client.
Para clients que não suportam blinks, deve haver um site subjacente (fazendo com que o navegador se torne a solução universal).
Se um usuário tocar em qualquer lugar do client que não seja um botão de ação ou campo de entrada de texto, ele deve ser levado ao site subjacente.
Teste e Verificação de Blinks #
Embora Solana Actions e blinks sejam um protocolo/especificação sem permissões, aplicações client e carteiras ainda são necessárias para facilitar a assinatura da transação pelos usuários.
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 exemplo, alguns clients podem operar com uma abordagem de "lista de permissão" que pode exigir verificação antes de seus clients expandirem uma Action para os usuários, como o Registro de Actions da Dialect (detalhado abaixo).
Todos os blinks ainda serão renderizados e permitirão a assinatura no dial.to site intersticial de blinks da Dialect, com seu status de registro exibido no blink.
Registro de Actions da Dialect #
Como um bem público para o ecossistema Solana, a Dialect mantém um registro público — juntamente com a ajuda da Solana Foundation e outros membros da comunidade — de links de blockchain que foram pré-verificados de fontes conhecidas. Desde o lançamento, apenas Actions registradas no registro da Dialect serão expandidas no feed do Twitter quando postadas.
Aplicações client e carteiras podem escolher livremente usar esse registro público ou outra solução para ajudar a garantir a segurança e a proteção dos usuários. Se não for verificado pelo registro da Dialect, o link de blockchain não será tocado pelo client blink e será renderizado como uma URL típica.
Os desenvolvedores podem se inscrever para serem verificados pela Dialect aqui: dial.to/register
Especificação #
A especificação do Solana Actions é composta por seções principais que fazem parte do fluxo de interação de requisição/resposta:
- Solana Action URL scheme fornecendo uma URL de Action
- Resposta OPTIONS para uma URL de Action para atender aos requisitos de CORS
- Requisição GET para uma URL Action
- Resposta GET do servidor
- Requisição POST para uma URL Action
- Resposta POST do servidor
Cada uma dessas requisições é feita pelo Action client (por exemplo, aplicativo de carteira, extensão de navegador, dApp, site, etc) para coletar metadados específicos e facilitar a interação do usuário com a API de Actions.
Cada uma das respostas é elaborada por uma aplicação (por exemplo, site, backend de servidor, etc) e retornada ao Action client. O objetivo final é fornecer uma transação ou mensagem assinável para que a carteira do usuário solicite a aprovação, assinatura e envio para a 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.
Esquema da URL #
Uma URL de Action da Solana descreve uma requisição interativa para uma
transação ou mensagem assinável da Solana usando o protocolo solana-action
.
A requisição é interativa porque os parâmetros na URL são usados por um client para fazer uma série de requisições HTTP padronizadas para compor uma transação ou mensagem assinável para o usuário assinar com sua carteira.
solana-action:<link>
-
Um único campo de
link
é necessário como o caminho. O valor deve ser uma URL HTTPS absoluta condicionalmente codificada na URL-encoded. -
Se a URL contiver parâmetros de consulta, ela deve ser codificada. Codificar a URL evita conflitos com quaisquer parâmetros de protocolo de Actions, que podem ser adicionados via especificação de protocolo.
-
Se a URL não contiver parâmetros de consulta, ela não deve ser codificada. Isso produz uma URL mais curta e um código QR menos denso.
Em ambos os casos, os clients devem decodificar o valor da URL-decode. Isso não tem efeito se o valor não estiver como URL codificado. Se o valor decodificado não for uma URL HTTPS absoluta, a carteira deve rejeitá-lo como malformado.
Resposta OPTIONS #
Para permitir o Cross-Origin Resource Sharing
(CORS) dentro dos
clients de Actions (incluindo blinks), todos os endpoints de Action devem
responder a requisições HTTP para o método OPTIONS
com cabeçalhos válidos que
permitirão que os clients passem nas verificações de CORS para todas as
requisições subsequentes do mesmo domínio de origem.
Um client de Actions pode realizar requisições de
"preflight"
para o endpoint da URL de Action a fim de verificar se a requisição GET
subsequente para a URL de Action passará em todas as verificações de CORS. Essas
verificações de preflight CORS são feitas usando o método HTTP OPTIONS
e devem
responder com todos os cabeçalhos HTTP necessários que permitirão que os clients
de Actions (como blinks) façam adequadamente todas as requisições subsequentes
do seu domínio de origem.
No mínimo, os cabeçalhos HTTP necessários incluem:
Access-Control-Allow-Origin
com um valor de*
- isso garante que todos os clients de Actions possam passar nas verificações de CORS para fazer todas as requisições necessárias
Access-Control-Allow-Methods
com um valor deGET,POST,PUT,OPTIONS
- garante que todos os métodos de requisição HTTP necessários sejam suportados para Actions
Access-Control-Allow-Headers
com um valor mínimo deContent-Type, Authorization, Content-Encoding, Accept-Encoding
Para simplicidade, os desenvolvedores devem considerar retornar a mesma resposta
e cabeçalhos para as requisições OPTIONS que para as respostas
GET
response.
The actions.json
file response must also return valid Cross-Origin headers for
GET
and OPTIONS
requests, specifically the Access-Control-Allow-Origin
header value of *
.
See actions.json below for more details.
Requisição GET #
O Action client (por exemplo, carteira, extensão de navegador, etc) deve fazer uma requisição HTTP GET JSON para o endpoint da URL da Action.
- A requisição não deve identificar a carteira ou o usuário.
- O client deve fazer a requisição com um cabeçalho Accept-Encoding.
- O client deve exibir o domínio da URL enquanto a requisição está sendo feita.
Resposta GET #
O endpoint da URL da Action (por exemplo, aplicação ou backend de servidor) deve responder com uma resposta HTTP OK JSON (com um payload válido no corpo) ou um erro HTTP apropriado.
-
O client deve lidar com erros HTTP client errors, server errors, e redirect responses.
-
O endpoint deve responder com um cabeçalho
Content-Encoding
header para compressão HTTP. -
O endpoint deve responder com um cabeçalho
Content-Type
header de application/json. -
O client não deve armazenar em cache a resposta, exceto conforme instruído pelos cabeçalhos de resposta de HTTP caching.
-
O client deve exibir o
title
e renderizar a imagem doicon
para o usuário.
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.
Corpo da Resposta GET #
Uma resposta GET
com uma resposta HTTP OK
JSON deve incluir um payload no
corpo que siga a especificação da 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
- O valor deve ser uma URL HTTP ou HTTPS absoluta de uma imagem de ícone. O arquivo deve ser uma imagem SVG, PNG ou WebP, ou o client/carteira deve rejeitá-lo como malformado. -
title
- O valor deve ser uma string UTF-8 que representa a origem da requisição de ação. Por exemplo, isso pode ser o nome de uma marca, loja, aplicativo ou pessoa que está fazendo a solicitação. -
description
- O valor deve ser uma string UTF-8 que fornece informações sobre a ação. A descrição deve ser exibida para o usuário. -
label
- O valor deve ser uma string UTF-8 que será renderizada em um botão para o usuário clicar. Todos os rótulos não devem exceder frases de 5 palavras e devem começar com um verbo para solidificar a ação que você deseja que o usuário realize. Por exemplo, "Mint NFT", "Vote Yes" ou "Stake 1 SOL". -
disabled
- O valor deve ser booleano para representar o estado desabilitado do botão renderizado (que exibe o stringlabel
). Se nenhum valor for fornecido,disabled
deve ser padrão comofalse
(ou seja, habilitado por padrão). Por exemplo, se o endpoint de ação for para uma votação de governança que foi encerrada, definadisabled=true
e olabel
pode ser "Vote Closed". -
error
- Uma indicação opcional de erro não fatal. Se presente, o client deve exibi-la para o usuário. 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
- Um array opcional de ações relacionadas para o endpoint. Os usuários devem ver a UI para cada uma das ações listadas e devem realizar apenas uma. Por exemplo, um endpoint de ação de votação de governança pode retornar três opções para o usuário: "Vote Yes", "Vote No" e "Abstain from Vote".-
Se nenhum
links.actions
for fornecido, o client deve renderizar um único botão usando a stringlabel
raiz e fazer a requisição POST para o mesmo endpoint de URL de ação que a requisição GET inicial. -
Se quaisquer
links.actions
forem fornecidos, o client deve renderizar apenas botões e campos de entrada baseados nos itens listados no campolinks.actions
. O client não deve renderizar um botão para o conteúdo dolabel
raiz.
-
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.
Exemplo de Resposta GET #
A resposta de exemplo a seguir fornece uma única ação "root" que deve ser apresentada ao usuário como um único botão com o rótulo "Claim Access Token":
{
"title": "HackerHouse Events",
"icon": "<url-to-image>",
"description": "Claim your Hackerhouse access token.",
"label": "Claim Access Token" // button text
}
A resposta de exemplo a seguir fornece 3 links de ações relacionadas que permitem ao usuário clicar em um dos 3 botões para votar em uma proposta de DAO:
{
"title": "Realms DAO Platform",
"icon": "<url-to-image>",
"description": "Vote nas propostas de governança DAO #1234.",
"label": "Vote",
"links": {
"actions": [
{
"label": "Vote Yes", // texto do botão
"href": "/api/proposal/1234/vote?choice=yes"
},
{
"label": "Vote No", // texto do botão
"href": "/api/proposal/1234/vote?choice=no"
},
{
"label": "Abstain from Vote", // texto do botão
"href": "/api/proposal/1234/vote?choice=abstain"
}
]
}
}
Exemplo de Resposta GET com Parâmetros #
A resposta de exemplo a seguir demonstra como aceitar entrada de texto do
usuário (via parâmetros
) e incluir essa entrada no endpoint da requisição
POST
final (via o campo href
dentro de um LinkedAction
):
A resposta de exemplo a seguir fornece ao usuário 3 ações vinculadas para fazer stake de SOL: um botão rotulado "Stake 1 SOL", outro botão rotulado "Stake 5 SOL" e um campo de entrada de texto que permite ao usuário inserir um valor específico "amount" que será enviado para a API Action:
{
"title": "Stake-o-matic",
"icon": "<url-to-image>",
"description": "Stake SOL para ajudar manter a rede da Solana segura",
"label": "Stake SOL", // não exibido, pois `links.actions` são fornecidos
"links": {
"actions": [
{
"label": "Stake 1 SOL", // texto do botão
"href": "/api/stake?amount=1"
// sem `parameters`, portanto, não é um campo de entrada de texto
},
{
"label": "Stake 5 SOL", // texto do botão
"href": "/api/stake?amount=5"
// sem `parameters`, portanto, não é um campo de entrada de texto
},
{
"label": "Stake", // texto do botão
"href": "/api/stake?amount={amount}",
"parameters": [
{
"name": "amount", // nome do campo
"label": "Quantidade de SOL" // texto de placeholder
}
]
}
]
}
}
A resposta de exemplo a seguir fornece um único campo de entrada para o usuário
inserir um amount
que é enviado com a requisição POST (podendo ser usado como
um parâmetro de consulta ou um subcaminho):
{
"icon": "<url-to-image>",
"label": "Donate SOL",
"title": "Doe para a GoodCause Charity",
"description": "Ajude a apoiar esta instituição de caridade doando SOL.",
"links": {
"actions": [
{
"label": "Donate", // texto do botão
"href": "/api/donate/{amount}", // ou /api/donate?amount={amount}
"parameters": [
// campo de entrada {amount}
{
"name": "amount", // nome do campo de entrada
"label": "Quantidade de SOL" // texto do placeholder
}
]
}
]
}
}
Requisição POST #
O client deve fazer uma requisição HTTP POST
JSON para a URL da ação com um
payload no corpo de:
{
"account": "<account>"
}
account
- O valor deve ser a chave pública codificada em base58 de uma conta que pode assinar a transação.
O client deve fazer a requisição com um cabeçalho Accept-Encoding header e a aplicação pode responder com um cabeçalho Content-Encoding header para compressão HTTP.
O client deve exibir o domínio da URL da ação enquanto a requisição está sendo
feita. Se uma requisição GET
foi feita, o client também deve exibir o title e
renderizar a imagem do icon
dessa resposta GET.
Resposta POST #
O endpoint POST
da Action deve responder com uma resposta HTTP OK
JSON (com
um payload válido no corpo) ou um erro HTTP apropriado.
- O client deve lidar com erros HTTP client errors, server errors, e redirect responses.
- O endpoint deve responder com um cabeçalho
Content-Type
header 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.
Corpo da Resposta POST #
Uma resposta POST
com uma resposta HTTP OK
JSON deve incluir um payload no
corpo 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
- O valor deve ser uma transação transação serializada codificada em base64. O client deve decodificar a transação de base64 e desserializá-la. -
message
- O valor deve ser uma string UTF-8 que descreve a natureza da transação incluída na resposta. O client deve exibir esse valor para o usuário. Por exemplo, pode ser o nome de um item sendo comprado, um desconto aplicado a uma compra ou uma nota de agradecimento. -
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. -
O client e a aplicação devem permitir campos adicionais no corpo da requisição e no corpo da resposta, que podem ser adicionados por atualizações futuras da especificação.
A aplicação pode responder com uma transação parcialmente ou totalmente assinada. O client e a carteira devem validar a transação como não confiável.
Resposta POST - Transação #
Se as
assinaturas
na transação estiverem vazias ou a transação NÃO tiver sido parcialmente
assinada:
- O client deve ignorar o
feePayer
na transação e definir ofeePayer
como aaccount
na requisição. - O client deve ignorar o
recentBlockhash
na transação e definir orecentBlockhash
como o último blockhash. - O client deve serializar e desserializar a transação antes de assiná-la. Isso garante a ordenação consistente das chaves de conta, como uma solução para este problema.
Se a transação foi parcialmente assinada:
- O client NÃO deve alterar o
feePayer
ou orecentBlockhash
, pois isso invalidaria quaisquer assinaturas existentes. - O client deve verificar as assinaturas existentes e, se alguma for inválida, o client deve rejeitar a transação como malformada.
O client deve assinar a transação apenas com a account
na requisição, e deve
fazê-lo apenas se uma assinatura para a account
na requisição for esperada.
Se qualquer assinatura, exceto uma assinatura para a account
na requisição,
for esperada, o client deve rejeitar a transação 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 #
O propósito do
arquivo actions.json
permite que uma aplicação instrua clients sobre quais URLs de site suportam
Solana Actions e forneça um mapeamento que pode ser usado para realizar
requisições GET
para um servidor de API de Actions.
The actions.json
file response must also return valid Cross-Origin headers for
GET
and OPTIONS
requests, specifically the Access-Control-Allow-Origin
header value of *
.
See OPTIONS response above for more details.
O arquivo actions.json
deve ser armazenado e acessível universalmente na raiz
do domínio.
Por exemplo, se sua aplicação web estiver implantada em my-site.com
, então o
arquivo actions.json
deve ser acessível em https://my-site.com/actions.json
.
This file should also be Cross-Origin accessible via any browser by having a
Access-Control-Allow-Origin
header value of *
.
Regras #
O campo rules
permite que a aplicação mapeie um conjunto de caminhos relativos
de rota de um site para um conjunto de outros caminhos.
Tipo: Array
de ActionRuleObject
.
interface ActionRuleObject {
/** caminho relativo (preferido) ou absoluto para realizar o mapeamento de regra */
pathPattern: string;
/** caminho relativo (preferido) ou absoluto que suporta requisições action */
apiPath: string;
}
-
pathPattern
- Um padrão que corresponde a cada caminho de entrada. -
apiPath
- Um destino definido como um caminho absoluto ou URL externa.
Regras - pathPattern #
Um padrão que corresponde a cada caminho de entrada. Pode ser um caminho absoluto ou relativo e suporta os seguintes formatos:
-
Correspondência Exata: Corresponde exatamente ao caminho da URL.
- Exemplo:
/exact-path
- Exemplo:
https://website.com/exact-path
- Exemplo:
-
Correspondência Coringa: Usa curingas para corresponder a qualquer sequência de caracteres no caminho da URL. Isso pode corresponder a segmentos únicos (usando
*
) ou múltiplos segmentos (usando**
). (veja Correspondência de Caminhos abaixo).- Exemplo:
/trade/*
corresponderá a/trade/123
e/trade/abc
, capturando apenas o primeiro segmento após/trade/
. - Exemplo:
/category/*/item/**
corresponderá a/category/123/item/456
e/category/abc/item/def
. - Exemplo:
/api/actions/trade/*/confirm
corresponderá a/api/actions/trade/123/confirm
.
- Exemplo:
Regras - apiPath #
O caminho de destino para a requisição action. Pode ser definido como um caminho absoluto ou uma URL externa.
- Exemplo:
/api/exact-path
- Exemplo:
https://api.example.com/v1/donate/*
- Exemplo:
/api/category/*/item/*
- Exemplo:
/api/swap/**
Regras - Parâmetros de Consulta #
Os parâmetros de consulta da URL original são sempre preservados e anexados à URL mapeada.
Regras - Correspondência de Caminho #
A tabela a seguir descreve a sintaxe para padrões de correspondência de caminho:
Operador | Corresponde |
---|---|
* | Um único segmento de caminho, não incluindo o caractere separador de caminho circundante /. |
** | ``` |
Corresponde a zero ou mais caracteres, incluindo qualquer caractere separador de
caminho / entre múltiplos segmentos de caminho. Se outros operadores estiverem
incluídos, o operador **
deve ser o último operador.
| `?` | Padrão não suportado. |
### Exemplos de Regras
O exemplo a seguir demonstra uma regra de correspondência exata para mapear requisições para `/buy` da raiz do seu site para o caminho exato `/api/buy` relativo à raiz do seu site:
```json filename="actions.json"
{
"rules": [
{
"pathPattern": "/buy",
"apiPath": "/api/buy"
}
]
}
O exemplo a seguir usa correspondência de caminho coringa para mapear
requisições para qualquer caminho (excluindo subdiretórios) sob /actions/
da
raiz do seu site para um caminho correspondente sob /api/actions/
relativo à
raiz do seu site:
{
"rules": [
{
"pathPattern": "/actions/*",
"apiPath": "/api/actions/*"
}
]
}
O exemplo a seguir usa correspondência de caminho coringa para mapear
requisições para qualquer caminho (excluindo subdiretórios) sob /donate/
da
raiz do seu site para um caminho absoluto correspondente
https://api.dialect.com/api/v1/donate/
em um site externo:
{
"rules": [
{
"pathPattern": "/donate/*",
"apiPath": "https://api.dialect.com/api/v1/donate/*"
}
]
}
O exemplo a seguir usa correspondência de caminho com coringa para uma regra
idempotente para mapear requisições para qualquer caminho (incluindo
subdiretórios) sob /api/actions/
da raiz do seu site para ele mesmo:
Regras idempotentes permitem que clients de blink determinem mais facilmente
se um determinado caminho suporta requisições de API action sem precisar ser
prefixado com o URI solana-action:
ou realizar testes de resposta adicionais.
{
"rules": [
{
"pathPattern": "/api/actions/**",
"apiPath": "/api/actions/**"
}
]
}
Identidade do Action #
Os endpoints de Action podem incluir uma Identidade de Ação nas transações que são retornadas na resposta POST para o usuário assinar. Isso permite que indexadores e plataformas de análise atribuam facilmente e de maneira verificável a atividade on-chain a um Provedor de Ação específico (ou seja, serviço) de forma verificável.
A Identidade da Action é um par de chaves usado para assinar uma mensagem formatada especialmente que é incluída na transação usando uma instrução de Memo. Essa Identifier Message pode ser atribuída de forma verificável a uma Identidade da Ação específica, e, portanto, atribuir transações a um Provedor de Ação específico.
O par de chaves não é necessário para assinar a própria transação. Isso permite que carteiras e aplicativos melhorem a entregabilidade das transações quando nenhuma outra assinatura está na transação retornada para um usuário (veja transação de resposta POST).
Se o caso de uso de um Provedor de Ação exigir que seus serviços de backend pré-assinem a transação antes do usuário, eles devem usar este par de chaves como sua Identidade de Ação. Isso permitirá que uma conta a menos seja incluída na transação, reduzindo o tamanho total das transações em 32 bytes.
Mensagem de Identificação de Ação #
A Mensagem de Identificação de Ação é uma string UTF-8 separada por dois pontos incluída em uma transação usando uma única instrução SPL Memo.
protocol:identity:reference:signature
protocol
- O valor do protocolo sendo usado (definido comosolana-action
conforme o Esquema de URL acima)identity
- O valor deve ser o endereço da chave pública codificado em base58 do par de chaves da Identidade de Açãoreference
- O valor deve ser uma array de 32 bytes codificado em base58. Isso pode ou não ser chaves públicas, on ou off the curve, e pode ou não corresponder a contas na Solana.signature
- assinatura codificada em base58 criada a partir do par de chaves da Identidade de Ação assinando apenas o valorreference
.
O valor reference
deve ser usado apenas uma vez e em uma única transação. Para
o propósito de associar transações a um Provedor de Ação, apenas o primeiro uso
do valor reference
é considerado válido.
As transações podem ter múltiplas instruções Memo. Ao realizar um
getSignaturesForAddress
,
o campo memo
dos resultados retornará a mensagem de cada instrução memo como
uma única string com cada uma separada por ponto e vírgula.
Nenhum outro dado deve ser incluído na instrução Memo da Mensagem de Identificação.
A identity
e a reference
devem ser incluídas como chaves de leitura apenas,
keys
não assinantes na transação em uma instrução que NÃO seja a instrução Memo da
Mensagem de Identificação.
A instrução Memo da Mensagem de Identificação deve ter zero contas fornecidas. Se quaisquer contas forem fornecidas, o programa Memo exige que essas contas sejam assinantes válidos. Para fins de identificação de ações, isso restringe a flexibilidade e pode degradar a experiência do usuário. Portanto, é considerado um anti-padrão e deve ser evitado.
Verificação de Identidade do Action #
Qualquer transação que inclua a conta identity
pode ser associada
verificadamente ao Provedor do Action em um processo de múltiplas etapas:
- Obtenha todas as transações para uma determinada
identity
. - Analise e verifique a string de memo de cada transação, garantindo que a
signature
é válida para oreference
armazenado. - Verifique se a transação específica é a primeira ocorrência on-chain do
reference
:- Se esta transação for a primeira ocorrência, a transação é considerada verificada e pode ser atribuída com segurança ao Provedor do Action.
- Se esta transação NÃO for a primeira ocorrência, ela é considerada inválida e, portanto, não atribuída ao Provedor do Action.
Como os validadores da Solana indexam as transações pelas chaves das contas, o
método RPC
getSignaturesForAddress
pode ser usado para localizar todas as transações que incluem a conta
identity
.
A resposta deste método RPC inclui todos os dados do Memo no campo memo
. Se
várias instruções de Memo foram usadas na transação, cada mensagem de memo será
incluída neste campo memo
e deve ser analisada adequadamente pelo verificador
para obter a Mensagem de Verificação de Identidade.
Essas transações devem ser inicialmente consideradas NÃO VERIFICADAS. Isso
ocorre porque a identity
não é obrigatória para assinar a transação, o que
permite que qualquer transação inclua essa conta como não-signatária.
Potencialmente inflando artificialmente os contadores de atribuição e uso.
A Mensagem de Verificação de Identidade deve ser verificada para garantir que a
signature
foi criada pela identity
assinando o reference
. Se essa
verificação de assinatura falhar, a transação é inválida e não deve ser
atribuída ao Provedor do Action.
Se a verificação da assinatura for bem-sucedida, o verificador deve garantir que
esta transação seja a primeira ocorrência on-chain do reference
. Se não for, a
transação é considerada inválida.