Actions e Blinks

Solana Actions sono API conformi alle specifiche che restituiscono transazioni sulla blockchain Solana da visualizzare in anteprima, firmare e inviare in numerosi contesti diversi, tra cui codici QR, pulsanti + widget e siti web su internet. Le Actions semplificano per gli sviluppatori l'integrazione di ciò che è possibile fare nell'ecosistema Solana direttamente nel proprio ambiente, consentendo di eseguire transazioni blockchain senza dover navigare verso un'altra app o pagina web.

Blockchain links – o blinks – trasformano qualsiasi Solana Action in un link condivisibile e ricco di metadati. I Blinks consentono ai client compatibili con le Actions (wallet come estensioni browser, bot) di visualizzare funzionalità aggiuntive per l'utente. Su un sito web, un blink potrebbe attivare immediatamente un'anteprima della transazione in un wallet senza accedere a un'app decentralizzata; su Discord, un bot potrebbe espandere il blink in un insieme interattivo di pulsanti. Questo sposta la capacità di interagire on-chain su qualsiasi superficie web in grado di visualizzare un URL.

Inizia

Per iniziare rapidamente a creare Solana Actions personalizzate:

npm install @solana/actions
  • installa il Solana Actions SDK nella tua applicazione
  • crea un endpoint API per la richiesta GET che restituisce i metadati relativi alla tua Action
  • crea un endpoint API che accetta la richiesta POST e restituisce la transazione firmabile per l'utente

Guarda questo video tutorial su come costruire una Solana Action utilizzando l'SDK @solana/actions.

Puoi anche trovare il codice sorgente di una Action che esegue un trasferimento nativo di SOL qui e diverse altre Actions di esempio in questo repository.

Quando distribuisci le tue Solana Actions personalizzate in produzione:

  • assicurati che la tua applicazione abbia un file actions.json valido nella root del tuo dominio
  • assicurati che la tua applicazione risponda con gli header Cross-Origin richiesti su tutti gli endpoint delle Actions, incluso il file actions.json
  • testa e fai il debug dei tuoi blinks/actions utilizzando il Blinks Inspector

Se stai cercando ispirazione per creare Actions e blinks, consulta il repository Awesome Blinks per alcune creazioni della community e anche idee per nuove ones.

Actions

La specifica Solana Actions utilizza un insieme di API standard per fornire transazioni firmabili (e in futuro messaggi firmabili) direttamente da un'applicazione a un utente. Sono ospitate su URL pubblicamente accessibili e sono quindi accessibili tramite il loro URL da qualsiasi client.

Puoi pensare alle Actions come a un endpoint API che restituisce metadati e qualcosa che un utente deve firmare (una transazione o un messaggio di autenticazione) con il proprio wallet blockchain.

L'API Actions consiste nell'effettuare semplici richieste GET e POST a un endpoint URL di un'Action e nel gestire le risposte conformi all'interfaccia delle Actions.

  1. la richiesta GET restituisce metadati che forniscono informazioni leggibili al client sulle azioni disponibili a questo URL e un elenco opzionale di azioni correlate.
  2. la richiesta POST restituisce una transazione o un messaggio firmabile che il client richiede al wallet dell'utente di firmare ed eseguire sulla blockchain o in un altro servizio off-chain.

Esecuzione e Ciclo di Vita delle Actions

In pratica, interagire con le Actions è molto simile a interagire con una tipica API REST:

  • il client effettua la richiesta GET iniziale a un URL di un'Action per recuperare i metadati sulle Actions disponibili
  • l'endpoint restituisce una risposta che include i metadati sull'endpoint (come il titolo e l'icona dell'applicazione) e un elenco delle azioni disponibili per questo endpoint
  • l'applicazione client (come un wallet mobile, un chat bot o un sito web) mostra un'interfaccia utente per consentire all'utente di eseguire una delle azioni
  • dopo che l'utente seleziona un'azione (cliccando un pulsante), il client effettua una richiesta POST all'endpoint per ottenere la transazione che l'utente deve firmare
  • il wallet facilita la firma della transazione da parte dell'utente e infine invia la transazione alla blockchain per la conferma

Esecuzione e Ciclo di Vita delle Solana ActionsEsecuzione e Ciclo di Vita delle Solana Actions

Quando si ricevono transazioni da un URL di Actions, i client dovrebbero gestire la sottomissione di queste transazioni alla blockchain e gestire il loro ciclo di vita dello stato.

Le Actions supportano anche un certo livello di invalidazione prima dell'esecuzione. La richiesta GET e POST può restituire alcuni metadati che indicano se l'azione è in grado di essere eseguita (come con il campo disabled).

Ad esempio, se esistesse un endpoint di Action che facilita il voto su una proposta di governance DAO il cui periodo di votazione è chiuso, la richiesta GET iniziale potrebbe restituire il messaggio di errore "Questa proposta non è più in votazione" e i pulsanti "Vota Sì" e "Vota No" come "disabilitati".

I Blinks (blockchain links) sono applicazioni client che esaminano le API delle Actions e costruiscono interfacce utente per interagire con ed eseguire le Actions.

Le applicazioni client che supportano i blinks rilevano semplicemente gli URL compatibili con le Actions, li analizzano e consentono agli utenti di interagire con essi in interfacce utente standardizzate.

Qualsiasi applicazione client che esamina completamente un'API Actions per costruire un'interfaccia completa è un blink. Pertanto, non tutti i client che consumano le API Actions sono blinks.

Un URL blink descrive un'applicazione client che consente a un utente di completare l'intero ciclo di vita dell'esecuzione di un'Action, compresa la firma con il proprio wallet.

https://example.domain/?action=<action_url>

Affinché qualsiasi applicazione client diventi un blink:

  • L'URL del blink deve contenere un parametro query action il cui valore è un URL Action codificato come URL. Questo valore deve essere codificato come URL per non entrare in conflitto con altri parametri del protocollo.

  • L'applicazione client deve decodificare l'URL del parametro query action ed esaminare il link all'API Action fornita (vedi schema URL Action).

  • Il client deve rendere un'interfaccia utente ricca che consenta a un utente di completare l'intero ciclo di vita dell'esecuzione di un'Action, compresa la firma con il proprio wallet.

Non tutte le applicazioni client blink (ad es. siti web o dApp) supporteranno tutte le Actions. Gli sviluppatori di applicazioni possono scegliere quali Actions supportare nelle proprie interfacce blink.

L'esempio seguente mostra un URL blink valido con un valore action di solana-action:https://actions.alice.com/donate codificato come URL:

https://example.domain/?action=solana-action%3Ahttps%3A%2F%2Factions.alice.com%2Fdonate

I Blinks possono essere collegati alle Actions in almeno 3 modi:

  1. Condividendo un URL Action esplicito: solana-action:https://actions.alice.com/donate

    In questo caso, solo i client supportati possono visualizzare il blink. Non ci sarà alcuna anteprima del link di fallback, né un sito visitabile al di fuori del client non supportato.

  2. Condividendo un link a un sito web collegato a un'API Actions tramite un file actions.json nella root del dominio del sito web.

    Ad esempio, https://alice.com/actions.json mappa https://alice.com/donate, un URL di sito web in cui gli utenti possono donare ad Alice, all'URL dell'API https://actions.alice.com/donate, dove sono ospitate le Actions per donare ad Alice.

  3. Incorporando un URL Action in un sito URL "interstitial" che sa come analizzare le Actions.

    https://example.domain/?action=<action_url>

I client che supportano i blinks dovrebbero essere in grado di prendere qualsiasi dei formati sopra indicati e visualizzare correttamente un'interfaccia per facilitare l'esecuzione dell'azione direttamente nel client.

Per i client che non supportano i blinks, dovrebbe esserci un sito web sottostante (rendendo il browser il fallback universale).

Se un utente tocca un punto qualsiasi di un client che non è un pulsante di azione o un campo di input testuale, dovrebbe essere reindirizzato al sito sottostante.

Sebbene Solana Actions e i blinks siano un protocollo/specifica senza permessi, le applicazioni client e i wallet sono comunque tenuti a facilitare in ultima istanza agli utenti la firma della transazione.

Utilizza lo strumento Blinks Inspector per esaminare, fare il debug e testare i tuoi blinks e actions direttamente nel tuo browser. Puoi visualizzare i payload delle risposte GET e POST, gli header delle risposte, e testare tutti gli input per ciascuna delle tue Actions collegate.

Ogni applicazione client o wallet può avere requisiti diversi su quali endpoint di Actions i propri client espanderanno automaticamente e visualizzeranno immediatamente agli utenti sulle piattaforme di social media.

Ad esempio, alcuni client potrebbero operare con un approccio "allow list" che potrebbe richiedere una verifica prima che il client espanda un'Action per gli utenti, come il Registro delle Actions di Dialect (descritto di seguito).

Tutti i blinks verranno comunque visualizzati e consentiranno la firma sul sito Interstitial blinks dial.to di Dialect, con il loro stato nel registro visualizzato nel blink.

Registro delle Actions di Dialect

Come bene pubblico per l'ecosistema Solana, Dialect mantiene un registro pubblico — insieme all'aiuto della Solana Foundation e di altri membri della community — di blockchain links provenienti da fonti preventivamente verificate e note. Al momento del lancio, solo le Actions registrate nel registro di Dialect verranno espanse nel feed di Twitter quando pubblicate.

Le applicazioni client e i wallet possono scegliere liberamente di utilizzare questo registro pubblico o un'altra soluzione per garantire la sicurezza degli utenti. Se non verificato tramite il registro di Dialect, il blockchain link non verrà elaborato dal client blink e verrà visualizzato come un URL normale.

Gli sviluppatori possono richiedere la verifica da parte di Dialect qui: dial.to/register

Specifica

La specifica Solana Actions è composta da sezioni chiave che fanno parte di un flusso di interazione richiesta/risposta:

Ciascuna di queste richieste viene effettuata dal client Action (ad es. app wallet, estensione del browser, dApp, sito web, ecc.) per raccogliere metadati specifici per interfacce utente avanzate e per facilitare l'input dell'utente verso l'API Actions.

Ciascuna risposta viene creata da un'applicazione (ad es. sito web, backend server, ecc.) e restituita al client Action. In ultima analisi, fornisce una transazione o un messaggio firmabile affinché un wallet chieda all'utente di approvare, firmare e inviare alla blockchain.

I tipi e le interfacce dichiarati all'interno di questi file readme sono spesso la versione semplificata dei tipi, per favorire la leggibilità.

Per una maggiore sicurezza dei tipi e una migliore esperienza per gli sviluppatori, il pacchetto @solana/actions-spec contiene definizioni di tipo più complesse. Puoi trovare il codice sorgente corrispondente qui.

Schema URL

Un URL Solana Action descrive una richiesta interattiva per una transazione o un messaggio Solana firmabile utilizzando il protocollo solana-action.

La richiesta è interattiva perché i parametri nell'URL vengono usati da un client per effettuare una serie di richieste HTTP standardizzate al fine di comporre una transazione o un messaggio firmabile che l'utente possa firmare con il proprio wallet.

solana-action:<link>
  • È richiesto un singolo campo link come pathname. Il valore deve essere un URL HTTPS assoluto codificato come URL in modo condizionale.

  • Se l'URL contiene parametri di query, deve essere codificato come URL. La codifica URL del valore evita conflitti con eventuali parametri del protocollo Actions, che possono essere aggiunti tramite la specifica del protocollo.

  • Se l'URL non contiene parametri di query, non dovrebbe essere codificato come URL. Questo produce un URL più breve e un codice QR meno denso.

In entrambi i casi, i client devono decodificare l'URL del valore. Questo non ha effetto se il valore non è codificato come URL. Se il valore decodificato non è un URL HTTPS assoluto, il wallet deve rifiutarlo come malformato.

Risposta OPTIONS

Per consentire il Cross-Origin Resource Sharing (CORS) nei client Actions (inclusi i blinks), tutti gli endpoint Action dovrebbero rispondere alle richieste HTTP per il metodo OPTIONS con header validi che permettano ai client di superare i controlli CORS per tutte le richieste successive provenienti dallo stesso dominio di origine.

Un client Actions può eseguire richieste "preflight" all'endpoint URL dell'Action per verificare se la successiva richiesta GET all'URL dell'Action supererà tutti i controlli CORS. Questi controlli CORS preflight vengono effettuati utilizzando il metodo HTTP OPTIONS e dovrebbero rispondere con tutti gli header HTTP richiesti che consentiranno ai client Action (come i blinks) di effettuare correttamente tutte le richieste successive dal proprio dominio di origine.

Come minimo, gli header HTTP richiesti includono:

  • Access-Control-Allow-Origin con valore *
    • questo garantisce che tutti i client Action possano superare in sicurezza i controlli CORS per effettuare tutte le richieste necessarie
  • Access-Control-Allow-Methods con valore GET,POST,PUT,OPTIONS
    • garantisce che tutti i metodi di richiesta HTTP necessari siano supportati per le Actions
  • Access-Control-Allow-Headers con un valore minimo di Content-Type, Authorization, Content-Encoding, Accept-Encoding

Per semplicità, gli sviluppatori dovrebbero considerare di restituire alle richieste OPTIONS la stessa risposta e gli stessi header della loro risposta GET.

Header Cross-Origin per actions.json

La risposta del file actions.json deve inoltre restituire header Cross-Origin validi per le richieste GET e OPTIONS, in particolare il valore * per l'header Access-Control-Allow-Origin.

Consulta actions.json di seguito per maggiori dettagli.

Richiesta GET

Il client Action (ad es. wallet, estensione del browser, ecc.) dovrebbe effettuare una richiesta JSON HTTP GET all'endpoint URL dell'Action.

  • La richiesta non dovrebbe identificare il wallet o l'utente.
  • Il client dovrebbe effettuare la richiesta con un header Accept-Encoding.
  • Il client dovrebbe mostrare il dominio dell'URL mentre la richiesta viene eseguita.

Risposta GET

L'endpoint URL dell'Action (ad es. applicazione o backend server) dovrebbe rispondere con una risposta JSON HTTP OK (con un payload valido nel body) o con un errore HTTP appropriato.

Le risposte di errore (ovvero i codici di stato HTTP 4xx e 5xx) dovrebbero restituire un body di risposta JSON conforme a ActionError per presentare agli utenti un messaggio di errore utile. Vedi Errori Action.

Body della risposta GET

Una risposta GET con una risposta JSON HTTP OK dovrebbe includere un payload nel body che rispetti la specifica dell'interfaccia:

ActionGetResponse
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 - Il tipo di action fornita all'utente. Il valore predefinito è action. La ActionGetResponse iniziale deve avere un tipo pari a action.

    • action - Action standard che consentirà all'utente di interagire con una qualsiasi delle LinkedActions
    • completed - Utilizzato per dichiarare lo stato "completed" all'interno del concatenamento di action.
  • icon - Il valore deve essere un URL HTTP o HTTPS assoluto di un'immagine icona. Il file deve essere un'immagine SVG, PNG o WebP, altrimenti il client/wallet deve rifiutarlo come malformato.

  • title - Il valore deve essere una stringa UTF-8 che rappresenta la fonte della richiesta di action. Ad esempio, potrebbe essere il nome di un brand, di un negozio, di un'applicazione o di una persona che effettua la richiesta.

  • description - Il valore deve essere una stringa UTF-8 che fornisce informazioni sull'action. La descrizione dovrebbe essere mostrata all'utente.

  • label - Il valore deve essere una stringa UTF-8 che verrà renderizzata su un pulsante su cui l'utente potrà fare clic. Tutte le etichette non dovrebbero superare frasi di 5 parole e dovrebbero iniziare con un verbo per rendere chiara l'azione che vuoi che l'utente compia. Ad esempio, "Conia NFT", "Vota Sì" o "Metti in stake 1 SOL".

  • disabled - Il valore deve essere booleano per rappresentare lo stato disabilitato del pulsante renderizzato (che mostra la stringa label). Se non viene fornito alcun valore, disabled dovrebbe essere impostato di default su false (ovvero abilitato per impostazione predefinita). Ad esempio, se l'endpoint dell'action riguarda una votazione di governance ormai chiusa, imposta disabled=true e la label potrebbe essere "Votazione chiusa".

  • error - Un'indicazione di errore opzionale per errori non fatali. Se presente, il client dovrebbe mostrarla all'utente. Se impostata, non dovrebbe impedire al client di interpretare l'action o di mostrarla all'utente (vedi Errori Action). Ad esempio, l'errore può essere utilizzato insieme a disabled per mostrare un motivo come vincoli di business, autorizzazione, lo stato o un errore di una risorsa esterna.

  • links.actions - Un array opzionale di action correlate per l'endpoint. Agli utenti dovrebbe essere mostrata un'interfaccia utente per ciascuna delle action elencate e ci si aspetta che ne eseguano una sola. Ad esempio, un endpoint di action per una votazione di governance può restituire tre opzioni per l'utente: "Vota Sì", "Vota No" e "Astieniti dal voto".

    • Se non viene fornito alcun links.actions, il client dovrebbe renderizzare un singolo pulsante usando la stringa label root ed effettuare la richiesta POST allo stesso endpoint URL dell'action della richiesta GET iniziale.

    • Se vengono forniti uno o più links.actions, il client dovrebbe renderizzare solo i pulsanti e i campi di input in base agli elementi elencati nel campo links.actions. Il client non dovrebbe renderizzare un pulsante per il contenuto della label root.

LinkedAction
export interface LinkedAction {
/** Type of action to be performed by user */
type: LinkedActionType;
/** 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>;
}

ActionParameter consente di dichiarare quale input l'API Action sta richiedendo all'utente:

ActionParameter
/**
* 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;
}

Il pattern dovrebbe essere una stringa equivalente a un'espressione regolare valida. Questo pattern di espressione regolare dovrebbe essere usato dai blink-client per convalidare l'input dell'utente prima di effettuare la richiesta POST. Se il pattern non è un'espressione regolare valida, dovrebbe essere ignorato dai client.

patternDescription è una descrizione leggibile da una persona degli input attesi richiesti all'utente. Se viene fornito pattern, è obbligatorio fornire patternDescription.

I valori min e max consentono all'input di impostare limiti inferiori e/o superiori dell'input richiesto all'utente (ovvero numero min/max e/o lunghezza min/max in caratteri) e dovrebbero essere usati per la convalida lato client. Per gli input type di date o datetime-local, questi valori dovrebbero essere date in formato stringa. Per altri type di input basati su stringhe, i valori dovrebbero essere numeri che rappresentano la loro lunghezza minima/massima in caratteri.

Se il valore di input dell'utente non è considerato valido in base al pattern, l'utente dovrebbe ricevere un messaggio di errore lato client che indichi che il campo di input non è valido e che mostri la stringa patternDescription.

Il campo type consente all'API Action di dichiarare campi di input utente più specifici, offrendo una migliore convalida lato client e migliorando l'esperienza utente. In molti casi, questo tipo sarà simile all'elemento input HTML standard.

ActionParameterType può essere semplificato nel seguente tipo:

ActionParameterType
/**
* Input field type to present to the user
* @default `text`
*/
export type ActionParameterType =
| "text"
| "email"
| "url"
| "number"
| "date"
| "datetime-local"
| "checkbox"
| "radio"
| "textarea"
| "select";

Ciascuno dei valori type dovrebbe normalmente produrre un campo di input utente che assomiglia a un elemento HTML input standard del type corrispondente (ovvero <input type="email" />) per offrire una migliore convalida lato client e una migliore esperienza utente:

  • text - equivalente dell'elemento HTML input “text”
  • email - equivalente dell'elemento HTML input “email”
  • url - equivalente dell'elemento HTML input “url”
  • number - equivalente dell'elemento HTML input “number”
  • date - equivalente dell'elemento HTML input “date”
  • datetime-local - equivalente dell'elemento HTML input “datetime-local”
  • checkbox - equivalente a un raggruppamento di elementi HTML standard input “checkbox”. L'API Action dovrebbe restituire options come descritto di seguito. L'utente dovrebbe poter selezionare più opzioni checkbox tra quelle fornite.
  • radio - equivalente a un raggruppamento di elementi HTML standard input “radio”. L'API Action dovrebbe restituire options come descritto di seguito. L'utente dovrebbe poter selezionare una sola delle opzioni radio fornite.
  • Gli altri equivalenti di tipo input HTML non specificati sopra (hidden, button, submit, file, ecc.) non sono attualmente supportati.

Oltre agli elementi che assomigliano ai tipi di input HTML sopra indicati, sono supportati anche i seguenti elementi di input utente:

  • textarea - equivalente dell'elemento HTML textarea. Consente all'utente di fornire input su più righe.
  • select - equivalente dell'elemento HTML select, che permette all'utente di interagire con un campo in stile "a tendina". L'Action API deve restituire le options come descritto di seguito.

Quando type è impostato su select, checkbox o radio, l'Action API dovrebbe includere un array di options, ciascuna delle quali fornisce almeno un label e un value. Ogni opzione può inoltre avere un valore selected per indicare al blink-client quale opzione debba essere selezionata per impostazione predefinita per l'utente (vedere checkbox e radio per le differenze).

Questo ActionParameterSelectable può essere semplificato nella seguente definizione di tipo:

ActionParameterSelectable
/**
* 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;
}>;
}

Se nessun type è impostato o viene impostato un valore sconosciuto/non supportato, i blink-client dovrebbero usare text come impostazione predefinita e visualizzare un semplice campo di testo.

L'Action API è comunque responsabile della validazione e della sanitizzazione di tutti i dati provenienti dai parametri di input dell'utente, applicando ove necessario qualsiasi input utente contrassegnato come "required".

Per piattaforme diverse da quelle basate su HTML/web (come i dispositivi mobili nativi), il corrispondente componente di input utente nativo dovrebbe essere utilizzato per ottenere un'esperienza equivalente e una validazione lato client analoga a quella dei tipi di input HTML/web descritti sopra.

Esempio di Risposta GET

Il seguente esempio di risposta fornisce una singola azione "root" che dovrebbe essere presentata all'utente come un singolo pulsante con l'etichetta "Claim Access Token":

{
"title": "HackerHouse Events",
"icon": "<url-to-image>",
"description": "Claim your Hackerhouse access token.",
"label": "Claim Access Token" // button text
}

Il seguente esempio di risposta fornisce 3 link di azione correlati che permettono all'utente di fare clic su uno di 3 pulsanti per esprimere il proprio voto su una proposta 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"
}
]
}
}

Esempio di Risposta GET con Parametri

Il seguente esempio di risposta mostra come accettare input di testo dall'utente (tramite parameters) e includere tale input nella richiesta POST finale (tramite il campo href all'interno di una LinkedAction):

Il seguente esempio di risposta fornisce all'utente 3 azioni collegate per fare staking di SOL: un pulsante con l'etichetta "Stake 1 SOL", un altro con l'etichetta "Stake 5 SOL" e un campo di input testuale che consente all'utente di inserire un valore specifico per "amount" da inviare all'Action API:

{
"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
}
]
}
]
}
}

Il seguente esempio di risposta fornisce un singolo campo di input in cui l'utente può inserire un amount che viene inviato con la richiesta POST (è possibile utilizzare sia un parametro di query che un sottopercorso):

{
"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
}
]
}
]
}
}

Richiesta POST

Il client deve effettuare una richiesta HTTP POST JSON all'URL dell'azione con un corpo con payload di:

{
"account": "<account>"
}
  • account - Il valore deve essere la chiave pubblica codificata in base58 di un account che può firmare la transazione.

Il client dovrebbe effettuare la richiesta con un header Accept-Encoding e l'applicazione può rispondere con un header Content-Encoding per la compressione HTTP.

Il client dovrebbe visualizzare il dominio dell'URL dell'azione mentre la richiesta è in corso. Se è stata effettuata una richiesta GET, il client dovrebbe anche visualizzare il title e renderizzare l'immagine icon da quella risposta GET.

Risposta POST

L'endpoint POST dell'Action dovrebbe rispondere con una risposta JSON HTTP OK (con un payload valido nel corpo) o con un errore HTTP appropriato.

Le risposte di errore (ovvero i codici di stato HTTP 4xx e 5xx) dovrebbero restituire un corpo di risposta JSON conforme ad ActionError per presentare un messaggio di errore utile agli utenti. Vedere Errori Action.

Corpo della Risposta POST

Una risposta POST con risposta JSON HTTP OK dovrebbe includere un payload nel corpo di:

ActionPostResponse
/**
* 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 - Il valore deve essere una transazione serializzata codificata in base64. Il client deve decodificare in base64 la transazione e deserializzarla.

  • message - Il valore deve essere una stringa UTF-8 che descrive la natura della transazione inclusa nella risposta. Il client dovrebbe mostrare questo valore all'utente. Ad esempio, potrebbe trattarsi del nome di un articolo acquistato, di uno sconto applicato a un acquisto o di un messaggio di ringraziamento.

  • links.next - Un valore opzionale utilizzato per "concatenare" più Action in serie. Dopo che la transaction inclusa è stata confermata on-chain, il client può recuperare e visualizzare l'azione successiva. Vedere Action Chaining per ulteriori dettagli.

  • Il client e l'applicazione dovrebbero consentire campi aggiuntivi nel corpo della richiesta e nel corpo della risposta, che potranno essere aggiunti da futuri aggiornamenti delle specifiche.

L'applicazione può rispondere con una transazione parzialmente o completamente firmata. Il client e il wallet devono validare la transazione come non attendibile.

Risposta POST - Transazione

Se le signatures della transazione sono vuote o la transazione NON è stata parzialmente firmata:

  • Il client deve ignorare il feePayer nella transazione e impostare il feePayer sull'account nella richiesta.
  • Il client deve ignorare il recentBlockhash nella transazione e impostare il recentBlockhash sul blockhash più recente.
  • Il client deve serializzare e deserializzare la transazione prima di firmarla. Ciò garantisce un ordinamento coerente delle chiavi degli account, come soluzione alternativa a questo problema.

Se la transazione è stata parzialmente firmata:

  • Il client NON deve alterare il feePayer né il recentBlockhash poiché ciò invaliderebbe le firme esistenti.
  • Il client deve verificare le firme esistenti e, se una qualsiasi di esse non è valida, il client deve rifiutare la transazione come malformata.

Il client deve firmare la transazione esclusivamente con l'account nella richiesta, e deve farlo solo se è prevista una firma per l'account nella richiesta.

Se è prevista una firma diversa da quella per l'account nella richiesta, il client deve rifiutare la transazione come malevola.

Errori Action

Le Action API dovrebbero restituire gli errori utilizzando ActionError al fine di presentare messaggi di errore utili all'utente. A seconda del contesto, questo errore potrebbe essere fatale o non fatale.

ActionError
export interface ActionError {
/** simple error message to be displayed to the user */
message: string;
}

Quando un'Actions API risponde con un codice di stato HTTP di errore (ovvero 4xx e 5xx), il corpo della risposta dovrebbe essere un payload JSON conforme ad ActionError. L'errore è considerato fatale e il message incluso dovrebbe essere presentato all'utente.

Per le risposte API che supportano l'attributo opzionale error (come ActionGetResponse), l'errore è considerato non fatale e il message incluso dovrebbe essere presentato all'utente.

Action Chaining

Le Solana Actions possono essere "concatenate" in una serie successiva. Dopo che la transazione di un'Action è stata confermata on-chain, è possibile ottenere e presentare all'utente l'azione successiva.

L'Action Chaining consente agli sviluppatori di creare esperienze più complesse e dinamiche all'interno dei blink, tra cui:

  • fornire più transazioni (e in futuro la firma dei messaggi) a un utente
  • metadati dell'azione personalizzati in base all'indirizzo del wallet dell'utente
  • aggiornamento dei metadati del blink dopo una transazione riuscita
  • ricevere un callback API con la firma della transazione per ulteriori validazioni e logiche sul server Action API
  • messaggi di "successo" personalizzati aggiornando i metadati visualizzati (ad es. una nuova immagine e descrizione)

Per concatenare più azioni insieme, in qualsiasi ActionPostResponse includere un links.next di uno dei seguenti tipi:

  • PostNextActionLink - Link di richiesta POST con un URL di callback della stessa origine per ricevere la signature e l'account dell'utente nel corpo. Questo URL di callback dovrebbe rispondere con una NextAction.
  • InlineNextActionLink - Metadati inline per la prossima azione da presentare all'utente immediatamente dopo che la transazione è stata confermata. Non verrà effettuato alcun callback.
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

Dopo che la transaction inclusa nell'ActionPostResponse è stata firmata dall'utente e confermata on-chain, il blink client dovrebbe:

  • eseguire la richiesta di callback per recuperare e visualizzare la NextAction, oppure
  • se una NextAction è già fornita tramite links.next, il blink client dovrebbe aggiornare i metadati visualizzati senza effettuare alcuna richiesta di callback

Se l'URL di callback non è della stessa origine della richiesta POST iniziale, non dovrebbe essere effettuata alcuna richiesta di callback. I blink client dovrebbero visualizzare un errore che notifichi l'utente.

NextAction
/** 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">;

In base al type, l'azione successiva dovrebbe essere presentata all'utente tramite i blink client in uno dei seguenti modi:

  • action - (predefinito) Un'azione standard che consentirà all'utente di vedere i metadati Action inclusi, interagire con le LinkedActions fornite e continuare a concatenare le azioni seguenti.

  • completed - Lo stato terminale di una catena di azioni che può aggiornare la UI del blink con i metadati Action inclusi, ma non consentirà all'utente di eseguire ulteriori azioni.

Se links.next non è fornito, i blink client dovrebbero assumere che l'azione corrente sia l'azione finale della catena, presentando il proprio stato UI "completato" dopo che la transazione è confermata.

actions.json

Lo scopo del file actions.json consente a un'applicazione di istruire i client su quali URL del sito web supportano le Solana Actions e di fornire una mappatura utilizzabile per eseguire richieste GET a un server Action API.

Sono richiesti gli header Cross-Origin

Anche la risposta del file actions.json deve restituire header Cross-Origin validi per le richieste GET e OPTIONS, in particolare il valore dell'header Access-Control-Allow-Origin pari a *.

Vedere la risposta OPTIONS sopra per ulteriori dettagli.

Il file actions.json dovrebbe essere memorizzato e accessibile universalmente nella directory radice del dominio.

Ad esempio, se la tua applicazione web è distribuita su my-site.com, allora il file actions.json dovrebbe essere accessibile all'indirizzo https://my-site.com/actions.json. Questo file dovrebbe inoltre essere accessibile in modo Cross-Origin da qualsiasi browser tramite un header Access-Control-Allow-Origin con valore *.

Regole

Il campo rules consente all'applicazione di mappare un insieme di percorsi relativi del sito web a un insieme di altri percorsi.

Tipo: Array di ActionRuleObject.

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 pattern che corrisponde a ogni pathname in entrata.

  • apiPath - Una destinazione definita come pathname assoluto o URL esterno.

Regole - pathPattern

Un pattern che corrisponde a ogni pathname in entrata. Può essere un percorso assoluto o relativo e supporta i seguenti formati:

  • Corrispondenza esatta: Corrisponde esattamente al percorso URL.

    • Esempio: /exact-path
    • Esempio: https://website.com/exact-path
  • Corrispondenza con caratteri jolly: Utilizza caratteri jolly per corrispondere a qualsiasi sequenza di caratteri nel percorso URL. Può corrispondere a segmenti singoli (usando *) o multipli (usando **). (vedi Path Matching di seguito).

    • Esempio: /trade/* corrisponderà a /trade/123 e /trade/abc, catturando solo il primo segmento dopo /trade/.
    • Esempio: /category/*/item/** corrisponderà a /category/123/item/456 e /category/abc/item/def.
    • Esempio: /api/actions/trade/*/confirm corrisponderà a /api/actions/trade/123/confirm.

Regole - apiPath

Il percorso di destinazione per la richiesta dell'azione. Può essere definito come pathname assoluto o URL esterno.

  • Esempio: /api/exact-path
  • Esempio: https://api.example.com/v1/donate/*
  • Esempio: /api/category/*/item/*
  • Esempio: /api/swap/**

Regole - Parametri di Query

I parametri di query dell'URL originale vengono sempre preservati e aggiunti all'URL mappato.

Regole - Path Matching

La tabella seguente illustra la sintassi per i pattern di corrispondenza dei percorsi:

OperatoreCorrisponde a
*Un singolo segmento di percorso, esclusi i caratteri separatori di percorso /.
**Corrisponde a zero o più caratteri, inclusi i caratteri separatori di percorso / tra più segmenti. Se sono inclusi altri operatori, l'operatore ** deve essere l'ultimo.
?Pattern non supportato.

Esempi di Regole

L'esempio seguente mostra una regola di corrispondenza esatta per mappare le richieste a /buy dalla radice del sito al percorso esatto /api/buy relativo alla radice del sito:

actions.json
{
"rules": [
{
"pathPattern": "/buy",
"apiPath": "/api/buy"
}
]
}

L'esempio seguente utilizza la corrispondenza con caratteri jolly per mappare le richieste a qualsiasi percorso (escluse le sottodirectory) sotto /actions/ dalla radice del sito a un percorso corrispondente sotto /api/actions/ relativo alla radice del sito:

actions.json
{
"rules": [
{
"pathPattern": "/actions/*",
"apiPath": "/api/actions/*"
}
]
}

L'esempio seguente utilizza la corrispondenza con caratteri jolly per mappare le richieste a qualsiasi percorso (escluse le sottodirectory) sotto /donate/ dalla radice del sito al percorso assoluto https://api.dialect.com/api/v1/donate/ su un sito esterno:

actions.json
{
"rules": [
{
"pathPattern": "/donate/*",
"apiPath": "https://api.dialect.com/api/v1/donate/*"
}
]
}

L'esempio seguente utilizza la corrispondenza con caratteri jolly per una regola idempotente, mappando le richieste a qualsiasi percorso (incluse le sottodirectory) sotto /api/actions/ dalla radice del sito a se stesso:

Le regole idempotenti consentono ai client blink di determinare più facilmente se un determinato percorso supporta le richieste Action API senza dover essere preceduto dall'URI solana-action: o eseguire ulteriori test di risposta.

actions.json
{
"rules": [
{
"pathPattern": "/api/actions/**",
"apiPath": "/api/actions/**"
}
]
}

Identità dell'Action

Gli endpoint delle Action possono includere un'Identità Action nelle transazioni restituite nella loro risposta POST per la firma da parte dell'utente. Ciò consente a indicizzatori e piattaforme di analisi di attribuire in modo semplice e verificabile l'attività onchain a uno specifico Action Provider (ovvero servizio) in modo verificabile.

L'Identità Action è un keypair utilizzato per firmare un messaggio in un formato speciale incluso nella transazione tramite un'istruzione Memo. Questo Messaggio Identificatore può essere attribuito in modo verificabile a una specifica Identità Action, e quindi attribuire le transazioni a uno specifico Action Provider.

Il keypair non è obbligatorio per firmare la transazione stessa. Ciò consente a wallet e applicazioni di migliorare la consegnabilità delle transazioni quando non sono presenti altre firme nella transazione restituita all'utente (vedi transazione risposta POST).

Se il caso d'uso di un Action Provider richiede che i propri servizi backend pre-firmino la transazione prima dell'utente, è consigliabile utilizzare questo keypair come Identità Action. Ciò consentirà di includere un account in meno nella transazione, riducendo la dimensione totale della transazione di 32 byte.

Messaggio Identificatore dell'Action

Il Messaggio Identificatore dell'Action è una stringa UTF-8 separata da due punti inclusa in una transazione tramite una singola istruzione SPL Memo.

protocol:identity:reference:signature
  • protocol - Il valore del protocollo utilizzato (impostato su solana-action secondo lo Schema URL sopra indicato)
  • identity - Il valore deve essere l'indirizzo della chiave pubblica del keypair dell'Identità Action codificato in base58
  • reference - Il valore deve essere un array di 32 byte codificato in base58. Può corrispondere o meno a chiavi pubbliche, sulla curva o fuori dalla curva, e può corrispondere o meno ad account su Solana.
  • signature - firma codificata in base58 creata dal keypair dell'Identità Action firmando solo il valore reference.

Il valore reference deve essere utilizzato una sola volta e in un'unica transazione. Ai fini dell'associazione delle transazioni a un Action Provider, è considerato valido solo il primo utilizzo del valore reference.

Le transazioni possono contenere più istruzioni Memo. Quando si esegue getSignaturesForAddress, il campo memo dei risultati restituirà il messaggio di ciascuna istruzione Memo come stringa unica, con ciascuno separato da un punto e virgola.

Nessun altro dato deve essere incluso nell'istruzione Memo del Messaggio Identificatore.

L'identity e il reference devono essere inclusi come chiavi di sola lettura e non firmatarie nella transazione, in un'istruzione che NON sia l'istruzione Memo del Messaggio Identificatore.

L'istruzione Memo del Messaggio Identificatore non deve avere account forniti. Se vengono forniti account, il programma Memo richiede che tali account siano firmatari validi. Ai fini dell'identificazione delle azioni, ciò limita la flessibilità e può degradare l'esperienza utente. Pertanto è considerato un anti-pattern e deve essere evitato.

Verifica dell'Identità Action

Qualsiasi transazione che include l'account identity può essere associata in modo verificabile all'Action Provider tramite un processo in più fasi:

  1. Recupera tutte le transazioni per un dato identity.
  2. Analizza e verifica la stringa memo di ciascuna transazione, assicurando che la signature sia valida per il reference memorizzato.
  3. Verifica che la transazione specifica sia la prima occorrenza onchain del reference:
    • Se questa transazione è la prima occorrenza, la transazione è considerata verificata e può essere attribuita in modo sicuro all'Action Provider.
    • Se questa transazione NON è la prima occorrenza, è considerata non valida e pertanto non attribuita all'Action Provider.

Poiché i validator di Solana indicizzano le transazioni per chiavi account, il metodo RPC getSignaturesForAddress può essere utilizzato per individuare tutte le transazioni che includono l'account identity.

La risposta di questo metodo RPC include tutti i dati Memo nel campo memo. Se nella transazione sono state utilizzate più istruzioni Memo, ogni messaggio memo verrà incluso in questo campo memo e dovrà essere analizzato di conseguenza dal verificatore per ottenere il Messaggio di Verifica dell'Identità.

Queste transazioni devono essere inizialmente considerate NON VERIFICATE. Ciò è dovuto al fatto che l'identity non è obbligatoria per firmare la transazione, il che consente a qualsiasi transazione di includere questo account come non firmatario, gonfiando potenzialmente in modo artificiale i conteggi di attribuzione e utilizzo.

Il Messaggio di Verifica dell'Identità deve essere controllato per assicurarsi che la signature sia stata creata dall'identity che firma il reference. Se questa verifica della firma fallisce, la transazione non è valida e non deve essere attribuita all'Action Provider.

Se la verifica della firma ha esito positivo, il verificatore deve assicurarsi che questa transazione sia la prima occorrenza onchain del reference. In caso contrario, la transazione è considerata non valida.

Is this page helpful?

© 2026 Solana Foundation. Tutti i diritti riservati.