Actions und Blinks

Solana Actions sind spezifikationskonforme APIs, die Transaktionen auf der Solana Blockchain zurückgeben. Diese Transaktionen können in verschiedenen Kontexten angezeigt, signiert und mit QR-Codes, Buttons, Widgets und Webseiten über das Internet versendet werden. Actions vereinfachen es Entwicklern, die verschiedenen Dinge, die man im Solana-Ökosystem tun kann, direkt in ihre eigene Umgebung zu integrieren. Dies ermöglicht es, Blockchain-Transaktionen durchzuführen, ohne eine andere App oder Webseite aufrufen zu müssen.

Blockchain Links – oder Blinks – verwandeln jede Solana Action in einen teilbaren, metadatenreichen Link. Blinks ermöglichen es Clients, die ‚Actions‘ verstehen (wie Browser-Erweiterungen für Wallets und Bots), zusätzliche Funktionen für den Benutzer anzuzeigen. Ein Blink würde zum Beispiel auf einer Website direkt die Vorschau einer Transaktion in einer Wallet anzeigen, ohne dass der Benutzer zu einer dezentralisierten App wechseln muss. In Discord könnte ein Bot einen Blink um ein Set aus interaktiven Buttons erweitern. Auf diese Weise wird die Möglichkeit der On-Chain-Interaktion auf jede Web-Oberfläche ausgedehnt, die eine URL anzeigen kann.

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
Info

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:

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.

Actions #

Die Solana Actions-Spezifikation verwendet eine Reihe von Standard-APIs, um signierbare Transaktionen (und letztendlich signierbare Nachrichten) direkt von einer Anwendung an den Benutzer zu übermitteln. Sie sind auf öffentlich zugänglichen URLs gehostet, sodass jeder Client über deren URL interagieren kann.

Info

Du kannst Actions als einen API-Endpunkt betrachten, der Metadaten und Inhalte zurückgibt, die der Benutzer mit seinem Blockchain-Wallet signieren soll (Transaktionen oder Authentifizierungsnachrichten).

Die Actions-API besteht aus einfachen GET und POST Anfragen an den URL-Endpunkt von Actions und der Verarbeitung von Antworten, die dem Actions-Interface entsprechen.

  1. Die GET-Anfrage liefert Metadaten, die dem Client menschenlesbare Informationen darüber bereitstellen, welche Actions unter dieser URL verfügbar sind, sowie eine optionale Liste der zugehörigen Actions.
  2. 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.

Ausführung und Lebenszyklus von Actions #

In der Praxis ähnelt die Interaktion mit Actions stark der Interaktion mit einer typischen REST-API:

  • Ein Client sendet eine initiale GET-Anfrage an eine Actions-URL, um Metadaten über die verfügbaren Actions zu erhalten
  • Der Endpunkt sendet eine Antwort zurück, die Metadaten über den Endpunkt (z.B. den Titel der Anwendung und das Icon) sowie eine Liste der verfügbaren Actions enthält
  • Die Client-Anwendung (z.B. eine mobile Wallet, ein Chatbot oder eine Webseite) zeigt eine Benutzeroberfläche an, damit der Nutzer eine der Actions ausführen kann
  • Nachdem der Nutzer eine Action ausgewählt hat (durch Klicken auf einen Button), sendet der Client eine POST-Anfrage an den Endpunkt, um die Transaktion zum Signieren zu erhalten
  • Das Wallet erleichtert dem Benutzer das Signieren der Transaktion und sendet diese schließlich zur Bestätigung an die Blockchain

Solana Actions Execution and LifecycleSolana Actions Execution and Lifecycle

Beim Empfang von Transaktionen über eine Actions-URL sollten die Clients deren Übermittlung an die Blockchain verarbeiten und den Statuslebenszyklus verwalten.

Actions unterstützen auch eine gewisse Form der Invalidierung vor der Ausführung. Die GET und POST- Anfragen können auch Metadaten zurückgeben, die anzeigen, ob eine Action ausgeführt werden kann (z.B. mit dem disabled Feld).

Wenn zum Beispiel ein Action-Endpunkt die Abstimmung über einen DAO-Governance-Vorschlag erleichtern soll, dessen Abstimmungsfenster bereits geschlossen ist, könnte die initiale GET-Anfrage eine Fehlermeldung wie "Diese Abstimmung ist nicht mehr gültig" zurückgeben und die "Ja" und "Nein"-Buttons als deaktiviert anzeigen.

Blinks (Blockchain Links) sind Client-Anwendungen, die die Action-APIs überprüfen und Benutzeroberflächen für die Interaktion und Ausführung von Actions erstellen.

Client-Anwendungen, die Blinks unterstützen, erkennen Action-kompatible URLs, analysieren sie und ermöglichen es Nutzern, mit ihnen in standardisierten Benutzeroberflächen zu interagieren.

Info

Jede Client-Anwendung, die eine Actions-API vollständig inspiziert, um eine umfassende Benutzeroberfläche dafür zu erstellen, ist ein Blink. Daher sind nicht alle Clients, die Actions APIs nutzen, auch Blinks.

Eine Blink-URL beschreibt eine Client-Anwendung, die es einem Benutzer ermöglicht, den vollständigen Lebenszyklus der Ausführung einer Action abzuschließen, einschließlich der Signatur mit seiner Wallet.

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

Um zu einem Blink zu werden, muss jede Client-Anwendung:

  • Die Blink-URL muss einen Abfrageparameter action enthalten, dessen Wert eine URL-codierte Aktions-URL ist. Dieser Wert muss URL-kodiert sein, um nicht mit anderen Protokollparametern in Konflikt zu geraten.

  • Die Client-Anwendung muss den Query-Parameter Action URL-dekodieren und den bereitgestellten Action-API-Link prüfen (siehe Action-URL-Schema).

  • Der Client muss eine umfassende Benutzeroberfläche rendern, die es einem Nutzer ermöglicht, den vollständigen Lebenszyklus der Ausführung einer Action abzuschließen, einschließlich der Signatur mit seiner Wallet.

Info

Nicht alle Blink-Client-Anwendungen (z.B. Websites oder dApps) werden alle Aktionen unterstützen. Anwendungsentwickler können auswählen, welche Actions sie innerhalb ihrer Blink-Schnittstellen unterstützen möchten.

Das folgende Beispiel zeigt eine gültige Blink-URL mit einem action-Wert von solana-action:https://actions.alice.com/donate, der URL-kodiert ist:

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

Blinks können auf mindestens drei Arten mit Actions verknüpft werden:

  1. Teilen einer expliziten Action-URL: solana-action:https://actions.alice.com/donate

    In diesem Fall können nur unterstützte Clients den Blink darstellen. Es wird weder eine alternative Linkvorschau geben noch eine Webseite, die außerhalb des nicht unterstützenden Clients besucht werden kann.

  2. Das Teilen eines Links zu einer Website, die über eine actions.json Datei im Rootverzeichnis der Website mit einer Actions-API verknüpft ist.

    Zum Beispiel ordnet die Datei https://alice.com/actions.json die Website-URL https://alice.com/donate der API-URL https://actions.alice.com/donate zu. Benutzer können über die Website-URL an Alice spenden, während die API-URL die entsprechenden Aktionen für das Spenden bereitstellt.

  3. Einbetten einer Actions-URL in eine „Zwischenseite“-URL, die versteht, wie man Aktionen analysiert.

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

Clients, die Blinks unterstützen, sollten in der Lage sein, jedes der oben genannten Formate zu übernehmen und eine Benutzeroberfläche korrekt darzustellen, um die Ausführung der Aktion direkt im Client zu ermöglichen.

Für Clients, die Blinks nicht unterstützen, sollte es eine zugrunde liegende Website geben (wodurch der Browser zum universellen Fallback wird).

Wenn ein Benutzer in einem Client auf eine Stelle tippt, die weder ein Actions-Button noch ein Texteingabefeld ist, sollte er zur zugrunde liegenden Webseite weitergeleitet werden.

While Solana Actions and blinks are a permissionless protocol/specification, client applications and wallets are still required to ultimately facilitate users to sign the transaction.

Info

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.

For example, some clients may operate on an "allow list" approach that may require verification prior to their client unfurling an Action for users such as Dialect's Actions Registry (detailed below).

All blinks will still render and allow for signing on Dialect's dial.to blinks Interstitial site, with their registry status displayed in the blink.

Dialect's Actions Registry #

As a public good for the Solana ecosystem, Dialect maintains a public registry — together with the help of Solana Foundation and other community members — of blockchain links that have are from pre-verified from known sources. As of launch, only Actions that have been registered in the Dialect registry will unfurl in the Twitter feed when posted.

Client applications and wallets can freely choose to use this public registry or another solution to help ensure user security and safety. If not verified through the Dialect registry, the blockchain link will not be touched by the blink client, and will be rendered as a typical URL.

Developers can apply to be verified by Dialect here: dial.to/register

Spezifikation #

Die Solana Actions-Spezifikation besteht aus wichtigen Abschnitten, die Teil eines Anfrage-/Antwort-Interaktionsflusses sind:

Jede dieser Anfragen wird vom Action-Client (z.B. Wallet-App, Browser-Erweiterung, dApp, Website usw.) gestellt, um spezifische Metadaten für umfangreiche Benutzeroberflächen zu sammeln und die Benutzereingabe an die Actions-API zu erleichtern.

Jede der Antworten wird von einer Anwendung (z. B. Website, Server-Backend usw.) erstellt und an den Action Client zurückgegeben. Letztendlich wird eine signierbare Transaktion oder Nachricht für eine Wallet bereitgestellt, um den Benutzer zur Genehmigung, Signierung und zum Senden an die Blockchain aufzufordern.

Info

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.

URL-Schema #

Eine Solana Action URL beschreibt eine interaktive Anfrage für eine signierbare Solana-Transaktion oder -Nachricht unter Verwendung des solana-action-Protokolls.

Die Anfrage ist interaktiv, da die Parameter in der URL vom Client verwendet werden, um eine Reihe von standardisierten HTTP-Anfragen zu stellen, um eine signierbare Transaktion oder Nachricht für den Benutzer zu erstellen, die dieser mit seiner Wallet signieren kann.

solana-action:<link>
  • Ein einzelnes link-Feld ist als Pfadname erforderlich. Der Wert muss eine bedingt URL-kodierte absolute HTTPS-URL sein.

  • Wenn die URL Abfrageparameter enthält, muss sie URL-kodiert sein. Die URL-Kodierung des Werts verhindert Konflikte mit jeglichen Actions-Protokoll-Parametern, die über die Protokollspezifikation hinzugefügt werden können.

  • Falls die URL keine Abfrageparameter enthält, sollte sie nicht URL-kodiert sein. Dadurch entsteht eine kürzere URL und ein besser lesbarer QR-Code.

In beiden Fällen müssen Clients den Wert URL-dekodieren. Dies hat keine Auswirkungen, wenn der Wert nicht URL-kodiert ist. Wenn der dekodierte Wert keine absolute HTTPS-URL ist, muss die Wallet ihn als fehlerhaft ablehnen.

OPTIONS response #

In order to allow Cross-Origin Resource Sharing (CORS) within Actions clients (including blinks), all Action endpoints should respond to HTTP requests for the OPTIONS method with valid headers that will allow clients to pass CORS checks for all subsequent requests from their same origin domain.

An Actions client may perform "preflight" requests to the Action URL endpoint in order check if the subsequent GET request to the Action URL will pass all CORS checks. These CORS preflight checks are made using the OPTIONS HTTP method and should respond with all required HTTP headers that will allow Action clients (like blinks) to properly make all subsequent requests from their origin domain.

At a minimum, the required HTTP headers include:

  • Access-Control-Allow-Origin with a value of *
    • this ensures all Action clients can safely pass CORS checks in order to make all required requests
  • Access-Control-Allow-Methods with a value of GET,POST,PUT,OPTIONS
    • ensures all required HTTP request methods are supported for Actions
  • Access-Control-Allow-Headers with a minimum value of Content-Type, Authorization, Content-Encoding, Accept-Encoding

For simplicity, developers should consider returning the same response and headers to OPTIONS requests as their GET response.

Cross-Origin headers for actions.json

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.

GET Anfrage #

Der Action Client (z.B. Wallet, Browser-Erweiterung, etc.) sollte eine HTTP GET JSON-Anfrage an den URL-Endpunkt der Action senden.

  • Die Anfrage sollte weder die Wallet noch den Benutzer identifizieren.
  • Der Client sollte die Anfrage mit einem Accept-Encoding-Header stellen.
  • Der Client sollte die Domain der URL anzeigen, während die Anfrage gestellt wird.

GET Antwort (GET Response) #

Der URL-Endpunkt der Action (z.B. Anwendung oder Server-Backend) sollte mit einer HTTP OK JSON-Antwort (mit einer gültigen Payload im Body) oder einem entsprechenden HTTP-Fehler antworten.

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.

GET Response Body #

Eine GET-Antwort mit einem HTTP-Statuscode OK sollte einen JSON Body-Payload enthalten, die der folgenden Schnittstellenspezifikation entspricht:

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 - The type of action being given to the user. Defaults to action. The initial ActionGetResponse is required to have a type of action.

    • action - Standard action that will allow the user to interact with any of the LinkedActions
    • completed - Used to declare the "completed" state within action chaining.
  • icon - Der Wert muss eine absolute HTTP- oder HTTPS-URL eines Icon-Bildes sein. Die Datei muss ein SVG-, PNG- oder WebP-Bild sein, andernfalls muss der Client/die Wallet sie als malformed ablehnen.

  • title - Der Wert muss ein UTF-8-String sein, der die Quelle der Action-Anfrage darstellt. Zum Beispiel könnte dies der Name einer Marke, eines Geschäfts, einer Anwendung oder einer Person sein, die die Anfrage stellt.

  • description - Der Wert muss ein UTF-8-String sein, der Informationen über die Action liefert. Diese Informationen sollten dem Benutzer angezeigt werden.

  • label - Der Wert muss ein UTF-8-String sein, der auf einem Button für den Benutzer zum Anklicken angezeigt wird. Alle Labels sollten nicht länger als 5 Wörter sein und mit einem Verb beginnen, um die gewünschte Handlung des Benutzers zu verdeutlichen. Zum Beispiel "Mint NFT", "Stimme mit Ja" oder "Stake 1 SOL".

  • disabled - Der Wert muss ein boolescher Wert sein (true/false), um den deaktivierten Zustand des angezeigten Buttons (mit dem label-Text) darzustellen. Falls kein Wert angegeben wird, sollte disabled standardmäßig auf false gesetzt werden (d. h. standardmäßig aktiviert sein). Zum Beispiel, wenn der Action-Endpunkt für eine Governance-Abstimmung ist, die bereits geschlossen wurde, wird disabled=true gesetzt und das Label könnte "Abstimmung beendet" sein.

  • error - Ein optionaler Fehlerhinweis für nicht schwerwiegende Fehler. Falls vorhanden, sollte der Client diesen dem Benutzer anzeigen. 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 with disabled to display a reason like business constraints, authorization, the state, or an error of external resource.

  • links.actions - Ein optionales Array verwandter Actions für den Endpunkt. Benutzern sollte eine Benutzeroberfläche für jede der aufgelisteten Aktionen angezeigt werden, wobei erwartet wird, dass sie nur eine Aktion ausführen. Zum Beispiel könnte ein Action-Endpunkt für eine Governance-Abstimmung drei Optionen für den Benutzer zurückgeben: "Stimme mit Ja", "Stimme mit Nein" und "Enthalten".

    • If no links.actions is provided, the client should render a single button using the root label string and make the POST request to the same action URL endpoint as the initial GET request.

    • If any links.actions are provided, the client should only render buttons and input fields based on the items listed in the links.actions field. The client should not render a button for the contents of the root label.

LinkedAction
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:

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;
}

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 types of date or datetime-local, these values should be a string dates. For other string based input types, 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:

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";

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 element
  • email - equivalent of HTML “email” input element
  • url - equivalent of HTML “url” input element
  • number - equivalent of HTML “number” input element
  • date - equivalent of HTML “date” input element
  • datetime-local - equivalent of HTML “datetime-local” input element
  • checkbox - equivalent to a grouping of standard HTML “checkbox” input elements. The Action API should return options 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 return options 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 return options 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:

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;
  }>;
}

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.

Beispiel einer GET-Antwort #

Das folgende Antwortbeispiel liefert eine einzelne "Root"-Action, die dem Benutzer als einzelner Button mit der Beschriftung "Zugangstoken anfordern" präsentiert werden soll:

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

Die folgende Beispielantwort enthält 3 verknüpfte Action-Links, die es dem Benutzer erlauben, auf eine von 3 Schaltflächen zu klicken, um ihre Stimme für einen DAO-Vorschlag abzugeben:

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

Beispiel einer GET-Antwort mit Parametern #

Die folgende Beispielantwort zeigt, wie man Texteingaben des Nutzers (über parameters) entgegennimmt und diese Eingabe in den finalen POST-Anfrage-Endpunkt (über das href-Feld innerhalb einer LinkedAction) einbindet:

Die folgende Beispielantwort stellt dem Nutzer 3 verknüpfte Actions zum Staken von SOL zur Verfügung: einen Button mit der Bezeichnung "1 SOL staken", einen weiteren Button mit der Bezeichnung "5 SOL staken" und ein Texteingabefeld, in das der Nutzer einen bestimmten "Betrag" eingeben kann, der an die Actions-API gesendet wird:

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

Die folgende Beispielantwort bietet dem Benutzer ein einzelnes Eingabefeld zur Eingabe eines Betrags, der mit der POST-Anfrage gesendet wird (entweder als Abfrageparameter oder als Unterpfad):

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

POST-Anfrage #

Der Client muss eine HTTP POST JSON-Anfrage an die Actions-URL mit folgendem Body-Payload senden:

{
  "account": "<account>"
}
  • account - Der Wert muss der base58-kodierte öffentliche Schlüssel (public key) eines Accounts sein, der die Transaktion signieren kann.

The client should make the request with an Accept-Encoding header and the application may respond with a Content-Encoding header for HTTP compression.

Der Client sollte die Domain der Actions-URL anzeigen, während die Anfrage gestellt wird. Wenn eine GET-Anfrage gestellt wurde, sollte der Client auch den title und das icon-Bild von dieser GET-Antwort anzeigen.

POST-Antwort #

Der POST-Endpunkt der Action sollte mit einer HTTP OK JSON-Antwort (mit einem gültigen Payload im Body) oder einem entsprechenden HTTP-Fehler antworten.

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.

POST Response Body #

Eine POST Antwort mit einer HTTP OK JSON-Antwort sollte folgenden Body-Payload enthalten:

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 - Der Wert muss eine base64-kodierte, serialisierte Transaktion sein. The client must base64-decode the transaction and deserialize it.

  • message - Der Wert muss ein UTF-8-String sein, der die Art der Transaktion beschreibt, die in der Antwort enthalten ist. The client should display this value to the user. Zum Beispiel könnte dies der Name eines gekauften Artikels, ein auf einen Kauf angewendeter Rabatt oder eine Dankesnachricht sein.

  • links.next - An optional value use to "chain" multiple Actions together in series. After the included transaction has been confirmed on-chain, the client can fetch and render the next action. See Action Chaining for more details.

  • Der Client und die Anwendung sollten zusätzliche Felder im Body der Anfrage und im Body der Antwort zulassen, die durch zukünftige Aktualisierungen der Spezifikation hinzugefügt werden können.

Info

Die Anwendung kann mit einer teilweise oder vollständig signierten Transaktion antworten. The client and wallet must validate the transaction as untrusted.

POST-Antwort - Transaktion #

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 the feePayer to the account in the request.
  • The client must ignore the recentBlockhash in the transaction and set the recentBlockhash to the latest blockhash.
  • Der Client muss die Transaktion vor dem Signieren serialisieren und deserialisieren. Dies gewährleistet eine einheitliche Reihenfolge der Kontoschlüssel (account keys) und dient als Workaround für dieses Problem.

If the transaction has been partially signed:

  • The client must NOT alter the feePayer or recentBlockhash as this would invalidate any existing signatures.
  • The client must verify existing signatures, and if any are invalid, the client must reject the transaction as malformed.

The client must only sign the transaction with the account in the request, and must do so only if a signature for the account in the request is expected.

If any signature except a signature for the account in the request is expected, the client must reject the transaction as malicious.

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.

ActionError
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 the signature and user's account in the body. This callback url should respond with a NextAction.
  • 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 via links.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.

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">;

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 provided LinkedActions, 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 #

Der Zweck der Datei actions.json besteht darin, einer Anwendung zu ermöglichen, Clients über die Website-URLs zu informieren, die Solana Actions unterstützen. Außerdem wird ein Mapping zur Verfügung gestellt, um GET-Anfragen an einen Actions-API-Server zu senden.

Cross-Origin headers are required

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.

Die Datei actions.json sollte im Stammverzeichnis der Domain gespeichert und allgemein zugänglich sein.

Wenn deine Webanwendung beispielsweise unter my-site.com bereitgestellt wird, sollte die Datei actions.json unter https://my-site.com/actions.json erreichbar sein. This file should also be Cross-Origin accessible via any browser by having a Access-Control-Allow-Origin header value of *.

Regeln (Rules) #

Das Feld rules erlaubt der Anwendung, eine Reihe relativer Routenpfade einer Webseite einer Reihe anderer Pfade zuzuordnen.

Typ: Array von 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 - Ein Muster, das jedem eingehenden Pfadnamen entspricht.

  • apiPath - Ein Zielort, der als absoluter Pfadname oder externe URL definiert ist.

Rules - pathPattern #

Ein Muster, das jedem eingehenden Pfadnamen entspricht. Es kann ein absoluter oder relativer Pfad sein und unterstützt die folgenden Formate:

  • Exakte Übereinstimmung: Stimmt genau mit dem URL-Pfad überein.

    • Beispiel: /exact-path
    • Beispiel: https://website.com/exact-path
  • Platzhalter-Übereinstimmung (Wildcard Match): Verwendet Platzhalter, um eine beliebige Abfolge von Zeichen im URL-Pfad abzugleichen. Dabei können einzelne Segmente (mit *) oder mehrere Segmente (mit **) abgeglichen werden. (siehe Path Matching weiter unten).

    • Beispiel: /trade/* stimmt mit /trade/123 und /trade/abc überein und erfasst nur das erste Segment hinter /trade/.
    • Beispiel: /category/*/item/** stimmt mit /category/123/item/456 und /category/abc/item/def überein.
    • Beispiel: /api/actions/trade/*/confirm stimmt mit /api/actions/trade/123/confirm überein.

Rules - apiPath #

Der Zielpfad für die Action-Anfrage. Er kann als absoluter Pfadname oder als externe URL angegeben werden.

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

Rules – Abfrageparameter (Query-Parameter) #

Abfrageparameter aus der ursprünglichen URL werden immer beibehalten und an die zugeordnete URL angehängt.

Rules - Pfadabgleich (Path Matching) #

Die folgende Tabelle zeigt die Syntax für Pfadmusterabgleiche (path matching patterns):

OperatorTreffer
*Ein einzelnes Pfadsegment, ohne die umgebenden Trennzeichen /.
**Stimmt mit keinem oder mehreren Zeichen überein, einschließlich aller Pfadtrennzeichen / zwischen mehreren Pfadsegmenten. Wenn andere Operatoren eingebunden sind, muss der ** Operator der letzte Operator sein.
?Nicht unterstütztes Muster.

Regeln Beispiele #

Das folgende Beispiel zeigt eine exakte Übereinstimmungsregel, um Anfragen an /buy vom Rootverzeichnis deiner Website auf den exakten Pfad /api/buy relativ zum Rootverzeichnis deiner Website zuzuordnen:

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

The following example uses wildcard path matching to map requests to any path (excluding subdirectories) under /actions/ from your site's root to a corresponding path under /api/actions/ relative to your site's root:

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

The following example uses wildcard path matching to map requests to any path (excluding subdirectories) under /donate/ from your site's root to a corresponding absolute path https://api.dialect.com/api/v1/donate/ on an external site:

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

Das folgende Beispiel verwendet Musterbasierten Pfadabgleich für eine idempotente Regel, um Anfragen an beliebige Pfade (einschließlich Unterverzeichnisse) unter /api/actions/ vom Rootverzeichnis Ihrer Website auf sich selbst abzubilden:

Info

Idempotente Regeln ermöglichen es Blink Clients, einfacher zu bestimmen, ob ein bestimmter Pfad Action API-Anfragen unterstützt, ohne dass dieser mit dem solana-action: URI vorangestellt werden muss oder zusätzliche Antworttests durchgeführt werden müssen.

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

Action Identity #

Action-Endpunkte können in den Transaktionen, die in ihrer POST-Antwort zum Signieren an den Benutzer zurückgegeben werden, eine Aktions-Identität (Action Identity) einfügen. Dadurch können Indexer und Analyseplattformen On-Chain-Aktivitäten einfach und nachprüfbar einem bestimmten Action-Anbieter (d.h. Dienst) zuordnen.

Die Action Identity ist ein Schlüsselpaar, das verwendet wird, um eine speziell formatierte Nachricht zu signieren, die mittels einer Memo-Anweisung in die Transaktion eingefügt wird. Diese Identifizierungsnachricht (Identifier Message) kann nachweislich einer bestimmten Action Identity zugeordnet werden und somit Transaktionen einem bestimmten Action Provider zuschreiben.

Das Schlüsselpaar muss nicht für die Signierung der Transaktion selbst verwendet werden. Dies ermöglicht es Wallets und Anwendungen, die Zustellbarkeit von Transaktionen zu verbessern, wenn keine anderen Signaturen in der an einen Benutzer zurückgegebenen Transaktion vorhanden sind (siehe POST Antwort Transaktion).

Falls der Anwendungsfall eines Action Providers erfordert, dass seine Backend-Dienste die Transaktion vorab signieren, bevor der Benutzer dies tut, sollten sie dieses Schlüsselpaar als ihre Action Identity verwenden. Dadurch kann ein Account weniger in die Transaktion aufgenommen werden, was die Gesamtgröße der Transaktion um 32 Bytes reduziert.

Action Identifier Message #

Die Action Identifier Message ist eine durch Doppelpunkte getrennte UTF-8-Zeichenkette, die mithilfe einer einzigen SPL Memo-Anweisung in eine Transaktion aufgenommen wird.

protocol:identity:reference:signature
  • protokoll - Der Wert des verwendeten Protokolls (gemäß dem URL-Schema oben auf solana-action gesetzt)
  • identity - Der Wert muss die Base58-kodierte öffentliche Schlüsseladresse des Action Identity Schlüsselpaares sein
  • reference - Der Wert muss ein Base58-kodiertes 32-Byte-Array sein. Dies können öffentliche Schlüssel sein oder auch nicht, auf oder außerhalb der Kurve, und sie können mit Accounts auf Solana übereinstimmen oder auch nicht.
  • signature - Base58-kodierte Signatur, die vom Action Identity-Schlüsselpaar erstellt wurde, wobei nur der reference-Wert signiert wird.

Der reference-Wert darf nur einmal und in einer einzigen Transaktion verwendet werden. Für die Zuordnung von Transaktionen zu einem Action Provider wird nur die erste Verwendung des reference-Werts als gültig betrachtet.

Transaktionen können mehrere Memo-Anweisungen enthalten. Bei der Ausführung einer getSignaturesForAddress-Abfrage wird das memo-Feld in den Ergebnissen die Nachricht jeder Memo-Anweisung als einen einzelnen String zurückgeben, wobei die Nachrichten durch ein Semikolon getrennt sind.

Keine weiteren Daten sollten in der Memo-Anweisung der Identifier Message (Identifizierungsnachricht) enthalten sein.

Die identity und die reference sollten als schreibgeschützte, nicht-signierende Schlüssel in der Transaktion bei einer Anweisung, die NICHT die Identifier Message Memo Anweisung ist, enthalten sein.

Die Identifier Message Memo-Anweisung muss ohne bereitgestellte Accounts erfolgen. Wenn Accounts bereitgestellt werden, erfordert das Memo-Programm, dass diese Konten gültige Signierer sind. Für den Zweck der Action-Identifizierung schränkt dies die Flexibilität ein und kann die Benutzererfahrung beeinträchtigen. Daher wird dies als Antimuster betrachtet und muss vermieden werden.

Action Identity Verifizierung #

Jede Transaktion, die den identity-Account einschließt, kann in einem mehrstufigen Prozess nachweislich mit dem Action Provider in Verbindung gebracht werden:

  1. Alle Transaktionen für eine gegebene identity abrufen.
  2. Analysiere und verifiziere jede Transaktions-Memo-Zeichenfolge, um sicherzustellen, dass die signature für die gespeicherte reference gültig ist.
  3. Verifiziere, dass die spezifische Transaktion das erste Vorkommen der reference auf der Blockchain ist:
    • Wenn es sich bei dieser Transaktion um das erste Vorkommen handelt, gilt die Transaktion als verifiziert und kann sicher dem Action Provider zugeordnet werden.
    • Wenn diese Transaktion NICHT das erste Ereignis ist, gilt sie als ungültig und wird daher nicht dem Action Provider zugeordnet.

Weil Solana-Validatoren Transaktionen nach den Account-Schlüsseln indizieren, kann die RPC-Methode getSignaturesForAddress verwendet werden, um alle Transaktionen zu finden, die das identity Konto beinhalten.

Die Antwort dieser RPC-Methode enthält alle Memo-Daten im Feld memo. Wenn mehrere Memo-Anweisungen in der Transaktion verwendet wurden, wird jede Memo-Nachricht in diesem memo-Feld enthalten sein und muss entsprechend vom Verifizierer analysiert werden, um die Identitätsverifizierungsnachricht zu erhalten.

Diese Transaktionen sollten zunächst als UNVERIFIZIERT betrachtet werden. Dies liegt daran, dass die identity nicht verpflichtet ist, die Transaktion zu signieren, was es jeder Transaktion ermöglicht, dieses Konto als Nicht-Signierer einzubinden. Potenziell künstliche Erhöhung der Zuordnungs- und Nutzungszahlen.

Die Identitätsprüfungsnachricht sollte überprüft werden, um sicherzustellen, dass die signature von der identity durch Signieren der reference erstellt wurde. Falls diese Signaturprüfung fehlschlägt, ist die Transaktion ungültig und sollte dem Action Provider nicht zugerechnet werden.

Wenn die Verifizierung der Signatur erfolgreich ist, sollte der Verifizierer sicherstellen, dass diese Transaktion das erste Auftreten der reference in der Blockchain ist. Ist dies nicht der Fall, wird die Transaktion als ungültig betrachtet.