Actions та Blinks

Solana Actions - це API, що відповідають специфікації, які повертають транзакції в блокчейні Solana для попереднього перегляду, підпису та відправлення в різних контекстах, включаючи QR-коди, кнопки + віджети, а також вебсайти в інтернеті. Actions роблять простою інтеграцію можливостей екосистеми Solana у ваше середовище розробники, дозволяючи виконувати блокчейн-транзакції без необхідності переходу до іншого додатка або вебсторінки.

Blockchain links – або blinks – перетворюють будь-який Solana Action на посилання, яким можна поділитися і яке містить багато метаданих. Blinks дозволяють клієнтам, що підтримують Action (браузерні гаманці, боти), відображати додаткові можливості для користувача. На вебсайті blink може одразу ж викликати попередній перегляд транзакції в гаманці без переходу до децентралізованого додатка; у Discord бот, blink може бути розгорнутий у наборі інтерактивних кнопок. Це дозволяє взаємодіяти з блокчейном на будь-якій вебплатформі, здатній відображати URL.

Початок роботи #

Щоб швидко розпочати створення Solana Actions:

npm install @solana/actions
  • встановіть Solana Actions SDK у вашому додатку
  • побудуйте API ендпоінт для GET запиту, який повертає метадані про ваш Action
  • створіть API ендпоінт, який приймає POST запит і повертає транзакцію, яку користувач може підписати
Info

Перегляньте цей відеоурок про те, як створити Solana Action за допомогою SDK @solana/actions.

Ви також можете знайти вихідний код для Action, який виконує нативний переказ SOL, тут, а також кілька інших прикладів Action у цьому репозиторії.

Під час розгортання ваших Solana Action у продакшн:

Якщо ви шукаєте натхнення для створення Action та blink, перегляньте репозиторій Awesome Blinks, де можна знатий творіння спільноти та навіть ідеї для нових blink.

Actions #

Специфікація Solana Actions використовує набір стандартних API для надання транзакцій, які можна підписати (а згодом і повідомлень, що також можна підписати), безпосередньо від додатка до користувача. Вони розміщуються за загальнодоступними URL-адресами й відповідно доступні для будь-якого клієнта за своїм URL.

Info

Ви можете розглядати Actions як API ендпоінт, який повертає метадані та щось для підпису користувачем (транзакцію або повідомлення для аутентифікації) за допомогою їх блокчейн-гаманця.

API Actions полягає у здійсненні простих GET та POST запитів до Action URL ендпоінту й обробці відповідей, що відповідають інтерфейсу Actions.

  1. GET запит повертає метадані, які надають клієнту читабельну інформацію про те, які дії доступні за цією URL-адресою, а також необов'язковий список пов'язаних дій.
  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.

Виконання та життєвий цикл Action #

На практиці взаємодія з Actions дуже схожа на взаємодію зі звичайним REST API:

  • клієнт робить початковий GET запит до URL-адреси Action, щоб отримати метадані про доступні дії
  • ендпоінт повертає відповідь, яка містить метадані про нього (такі як заголовок та іконка додатка) та перелік доступних дій для цього ендпоінта
  • клієнтський додаток (наприклад, мобільний гаманець, чат-бот або вебсайт) відображає інтерфейс для користувача для виконання однієї з дій
  • після того як користувач обрав дію (натиснувши кнопку), клієнт виконує POST запит до ендпоінту, щоб отримати транзакцію для підпису користувачем
  • гаманець допомагає користувачеві підписати транзакцію і відправляє її до блокчейну для підтвердження

Виконання Solana Actions та життєвий циклВиконання Solana Actions та життєвий цикл

Отримуючи транзакції з Actions URL, клієнти повинні обробляти відправку цих транзакцій до блокчейну і керувати їх життєвим циклом.

Actions також підтримують певний рівень валідації перед виконанням. Запити GET і POST можуть повертати деякі метадані, які вказують, чи можна виконати дію (наприклад, з полем disabled).

Наприклад, якщо існує Action ендпоінт, що дозволяє голосувати за пропозицію управління DAO, але період голосування закритий, початковий GET запит може повернути повідомлення про помилку "This proposal is no longer up for a vote", а кнопки "Vote Yes" та "Vote No" будуть відключені.

Blinks (blockchain links) - це клієнтські додатки, які аналізують Action API та створюють користувацькі інтерфейси для взаємодії та виконання Actions.

Клієнтські додатки, які підтримують blink, просто виявляють сумісні Action URL, розбирають їх і дозволяють користувачам взаємодіяти з ними у стандартизованих користувацьких інтерфейсах.

Info

Будь-який клієнтський додаток, який повністю аналізує Actions API для побудови повного інтерфейсу для нього, є blink. Тому не всі клієнти, який використовує Actions API, є blink.

URL blink описує клієнтський додаток, яка дозволяє користувачеві завершити повний цикл виконання Action, включаючи підпис з їх гаманцем.

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

Для того щоб будь-яка клієнтська програма могла використовувати blink:

  • Blink URL повинна містити параметр запиту action, значення якого буде URL закодованим для Action URL. Це значення повинно бути закодовано, щоб уникнути конфліктів з іншими параметрами протоколу.

  • Клієнтський додаток повинний декодувати URL параметр запиту action і інтроспектувати надане посилання Action API (див. Action URL scheme).

  • Клієнт повинен відображати користувацький інтерфейс, який дозволяє завершити весь життєвий цикл виконання Action, включаючи підпис з їх гаманця.

Info

Не всі клієнтські blink програми (наприклад, вебсайти або dApps) підтримують усі Actions. Розробники додатків можуть вибирати, які Actions вони хочуть підтримувати у своїх blink інтерфейсах.

Наведений приклад демонструє дійсну blink URL-адресу зі значенням параметра action, який URL закодовано solana-action:https://actions.alice.com/donate:

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

Blink можуть бути пов'язані з Actions принаймні трьома способами:

  1. Поширення за допомогою явного Action URL: solana-action:https://actions.alice.com/donate

    У цьому випадку лише підтримувані клієнти можуть відтворювати blink. Не буде доступно жодного альтернативного попереднього перегляду посилання або сайту, який можна було б відвідати за межами непідтримуваного клієнта.

  2. Поширення посилання на вебсайт, який пов'язаний з Actions API за допомогою actions.json файлуу кореневій директорії домену вебсайту.

    Наприклад, https://alice.com/actions.json відображає посилання https://alice.com/donate на веб-сайті, де користувачі можуть здійснити пожертвування на користь Alice, на API URL https://actions.alice.com/donate\`, де знаходяться Actions для здійснення пожертвування Alice.

  3. Вбудовування Action URL в "проміжний" URL веб-сайту, який розуміє, як парсити Actions.

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

Клієнти, які підтримують блінки, повинні мати можливість приймати будь-який з вищезазначених форматів і правильно відображати інтерфейс для безпосереднього виконання дії в клієнті.

Для клієнтів, які не підтримують blink, повинен бути доступний базовий вебсайт (що робить браузер універсальним резервом).

Якщо користувач натискає на будь-яке місце в клієнтській програмі, крім кнопки дії або поля введення тексту, його повинно перенаправити на базовий сайт.

Хоча Solana Actions та blinks є протоколом/специфікацією без права доступу, клієнтські додатки та гаманці все одно повинні забезпечувати користувачам можливість підписувати транзакції.

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.

Наприклад, деякі клієнти можуть працювати за принципом "дозволеного списку", що може вимагати перевірки перед тим, як їхній клієнт розгорне Action для користувачів, таких як Dialect's Actions Registry (детально описаний нижче).

Усі blinks все ще будуть відображатися та дозволяти підписання на сайті проміжної сторінки Dialect's dial.to, з вказанням їх статусу в реєстрі blink.

Dialect's Actions Registry #

З користю для екосистеми Solana, Dialect разом із допомогою Solana Foundation та інших членів спільноти підтримує публічний реєстр блокчейн-посилань, які були попередньо перевірені відомими джерелами. На момент запуску, лише Actions зареєстровані в реєстрі Dialect, будуть розгортатися в стрічці Twitter після публікації.

Клієнтські додатки та гаманці можуть вільно вибирати чи використовувати цей публічний реєстр або інше рішення, щоб забезпечити безпеку та захист користувачів. Якщо посилання на блокчейн не підтверджено через реєстр Dialect, blink клієнт його ігнорує і відображає як звичайний URL.

Розробники можуть подати заявку на верифікацію через Dialect за цим посиланням: dial.to/register

Специфікація #

Специфікація Solana Actions складається з ключових розділів, які є частиною процесу взаємодії запит/відповідь:

Кожен із цих запитів робиться Action клієнтом (наприклад, додатком гаманця, браузерним розширенням, dApp, вебсайтом тощо), щоб зібрати специфічні метадані для багатих користувацьких інтерфейсів і для полегшення введення користувача в Actions API.

Кожна з відповідей формується додатком (наприклад, вебсайтом, серверною частиною, тощо) і повертається клієнту Action. Зрештою, надаючи транзакцію або повідомлення для підпису, щоб гаманець запропонував користувачу підтвердити, підписати та відправити у блокчейн.

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 Scheme #

Solana Action URL описує інтерактивний запит для транзакції або повідомлення Solana, яке можна підписати, використовуючи протокол solana-action.

Запит є інтерактивним через те, що параметри в URL використовуються клієнтом для створення серії стандартизованих HTTP-запитів для складання транзакції або повідомлення, яке користувач може підписати за допомогою свого гаманця.

solana-action:<link>
  • Єдине поле link є обов’язковим як шлях. Значення повинно бути умовно URL закодованим абсолютним HTTPS URL.

  • Якщо URL містить параметри запиту, вони повинні бути URL закодовані. Кодування значень дозволяє уникнути конфліктів з параметрами протоколу Actions, які можуть бути додані за допомогою специфікації протоколу.

  • Якщо URL не містить параметрів запиту, його не потрібно кодувати. Це забезпечує коротший URL і менш щільний QR-код.

У будь-якому випадку клієнти повинні URL декодувати значення. Це не вплине на значення, якщо воно не закодоване. Якщо декодоване значення не є абсолютним HTTPS URL адресою, гаманець повинен відхилити його як неправильний формат.

OPTIONS відповідь #

Для забезпечення міжсайтового обміну ресурсами (CORS) у клієнтах Actions (включаючи blink), всі Actions ендпоінти повинні відповідати на HTTP-запити для методу OPTIONS з коректними заголовками, що дозволяють клієнтам успішно проходити перевірку CORS для всіх наступних запитів з їхнього власного початкового домену.

Action клієнт може виконувати "попередні" запити до енпоінта Action URL, щоб перевірити, чи пройде наступний GET запит на Action URL всі перевірки CORS. Ці попередні перевірки CORS виконуються за допомогою HTTP-методу OPTIONS і повинні відповідати з усіма необхідними HTTP-заголовками, що дозволяють Action клієнтам (таким як blink) належним чином виконувати всі наступні запити з їх вихідного домену.

Мінімальний набір обов'язкових HTTP заголовків включає:

  • Access-Control-Allow-Origin зі значенням *
    • це забезпечує можливість всім Action клієнтам безпечно проходити перевірку CORS для здійснення всіх необхідних запитів
  • Access-Control-Allow-Methods зі значенням "GET,POST,PUT,OPTIONS"
    • це забезпечує підтримку всіх необхідних HTTP методів для Action
  • Access-Control-Allow-Headers з мінімальним значенням Content-Type, Authorization, Content-Encoding, Accept-Encodding

Для спрощення, розробники повинні очікувати повернення такої самої відповіді та заголовків на запити методу OPTIONS, як на їхні відповіді запиту методу GET.

Cross-Origin headers for actions.json

Відповідь файлу actions.json також повинна містити дійсні заголовки Cross-Origin для запитів GET і OPTIONS, зокрема значення заголовка Access-Control-Allow-Origin повинно бути *.

Дивіться нижче для деталей щодо actions.json.

GET запит #

Action клієнт (наприклад, гаманець, браузерне розширення тощо) повинен здійснювати HTTP GET JSON запит до Action's URL ендпоінт.

  • Запит не повинен ідентифікувати гаманець або користувача.
  • Клієнт повинен робити запит з заголовком Accept-Encoding.
  • Клієнт повинен відображати домен URL під час виконання запиту.

Відповідь на GET запит #

Action's URL ендпоінт (наприклад, додаток або серверна частина) повинна відповісти HTTP-статусом OK із JSON-відповіддю (з коректним вмістом тіла) або відповідною HTTP-помилкою.

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 запиту #

Відповідь на GET запит з HTTP OK JSON повинна містити тіло з вмістом, що відповідає специфікації інтерфейсу:

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 - Значення повинно бути абсолютною HTTP або HTTPS URL-адресою зображення іконки. Файл повинен бути зображенням у форматі SVG, PNG або WebP, інакше клієнт/гаманець повинен відхилити його як неправильний формат.

  • title - Значення повинно бути рядком у форматі UTF-8, що представляє джерело запиту дії. Наприклад, це може бути назва бренду, магазину, додатка або особи, яка робить запит.

  • description - Значення повинно бути рядком у форматі UTF-8, що надає інформацію про дію. Опис повинен бути відображений користувачу.

  • label - Значення повинно бути рядком у форматі UTF-8, який буде відображено на кнопці для натискання користувачем. Усі мітки не повинні перевищувати 5 слів і мають починатися з дієслова, щоб чітко позначити дію, яку ви хочете, щоб користувач виконав. Наприклад, "Mint NFT", "Vote Yes" або "Stake 1 SOL".

  • disabled - Значення повинно бути булевим для відображення стану відключення кнопки (яка показує рядок з підписом label). Якщо значення не вказано, параметр disabled повинен за замовчуванням бути false (тобто ввімкнено за замовчуванням). Наприклад, якщо кінцевий ендпоінт призначений для голосування в управлінні, яке вже завершилося, встановіть disabled=true, а значення label може бути "Vote Closed".

  • error - Опційне вказівне поле для відображення не критичних помилок. Якщо воно присутнє, клієнт повинен відображати його користувачу. 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 - Опціональний масив пов'язаних дій для ендпоінта. Користувачам повинно відображатися інтерфейсне вікно для кожної з перелічених дій і очікувати, що вони виконають лише одну. Наприклад, ендпоінт дії голосування в управлінні може повертати три варіанти для користувача: "Vote Yes", "Vote No" і "Abstain from Vote".

    • Якщо не надано links.actions, клієнт повинен відобразити одну кнопку з кореневим рядком мітки label і виконати POST запит до того ж URL-ендпоінту дії, що й початковий GET запит.

    • Якщо надано будь-які links.actions, клієнт повинен відображати лише кнопки і поля введення на основі елементів, перераховані у полі links.actions. Клієнт не повинен відображати кнопку для перегляду вмісту кореневого поля 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.

Приклад GET відповіді #

Приклад відповіді, яка надає одну "кореневу" дію, що має бути представлена користувачу у вигляді однієї кнопки з написом "Claim Access Token":

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

Приклад відповіді, яка надає 3 пов'язаних посилання на дії, що дозволяють користувачу натиснути одну з 3 кнопок для голосування за пропозицію 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"
      }
    ]
  }
}

Приклад GET відповіді з параметрами #

Наступний приклад відповіді демонструє, як приймати текстове введення від користувача (через 'parameters') і включати це введення в ендпоінт 'POST' запиту (через поле 'href' у 'LinkedAction'):

Приклад відповіді, яка надає користувачеві 3 пов'язаних дії для стейкінгу SOL: кнопку з написом "Stake 1 SOL", іншу кнопку з написом "Stake 5 SOL" і текстове поле введення, яке дозволяє користувачеві ввести конкретне значення "amount", яке буде надіслано до 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
          }
        ]
      }
    ]
  }
}

Приклад відповіді, яка надає єдине поле введення для користувача, щоб ввести значення amount, яке буде відправлено з POST-запитом (або як параметр запиту, або як підшлях):

{
  "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 запит #

Клієнт повинен виконати HTTP POST JSON-запит до URL дії з тілом запиту:

{
  "account": "<account>"
}
  • account - Значення повинно бути закодованим у форматі Base58 публічним ключем облікового запису, який може підписати транзакцію.

Клієнт повинен робити запит з заголовком Accept-Encoding, а додаток може відповісти з заголовком Content-Encoding для HTTP стиснення.

Клієнт повинен відображати домен URL адреси дії під час виконання запиту. Якщо був зроблений GET запит, клієнт також повинен відображати title і відтворювати зображення icon з отриманої GET відповіді.

POST відповідь #

Ендпоінт Action's POST повинен відповісти з HTTP OK JSON-відповіддю (з дійсним вмістом у тілі) або відповідною HTTP-помилкою.

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 відповіді #

Відповідь на POST запит з HTTP OK JSON повинна містити тіло з вмістом:

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 - Значення повинно бути закодованою у форматі base64 серіалізованої транзакції. Клієнт повинен декодувати транзакцію з base64 і провести її [десеріалізацію] (https://solana-labs.github.io/solana-web3.js/classes/Transaction.html#from).

  • message - Значення повинно бути рядком UTF-8, який описує характер транзакції, включеної в відповідь. Клієнт повинен відображати це значення користувачеві. Наприклад, це може бути назва товару, що придбана, знижка на покупку або подяка.

  • 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.

  • Клієнт та додаток повинні дозволяти додаткові поля у тілі запиту та тілі відповіді, які можуть бути додані у майбутніх оновленнях специфікації.

Info

Додаток може відповісти частково або повністю підписаною транзакцією. Клієнт і гаманець повинні позначити транзакцію як ненадійну.

POST Відповідь - Транзакція #

Якщо підписи транзакції порожні або транзакція не була частково підписана:

  • Клієнт повинен ігнорувати feePayer у транзакції і встановити feePayer на обліковий запис у запиті.
  • Клієнт повинен ігнорувати recentBlockhash у транзакції і встановити recentBlockhash на останній блокхеш.
  • Клієнт повинен серіалізувати та десеріалізувати транзакцію перед її підписанням. Це забезпечує послідовний порядок ключів облікових записів, як обхідне рішення для цієї проблеми.

Якщо транзакцію було частково підписано:

  • Клієнт не повинен змінювати feePayer або recentBlockhash, оскільки це може призвести до недійсності наявних підписів.
  • Клієнт повинен перевірити наявні підписи і якщо який-небудь з них недійсний, клієнт повинен відхилити транзакцію як неправильно сформовану.

Клієнт повинен підписувати транзакцію лише з обліковим записом у запиті і робити це тільки в тому випадку, якщо очікується підпис для облікового запису у запиті.

Якщо очікується будь-який підпис, крім підпису для облікового запису у запиті, клієнт повинен відхилити транзакцію як зловмисну.

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 #

Мета файлу actions.json file полягає в тому, щоб додаток міг вказати клієнтам, які веб-сайти підтримують Solana Action і надавати відображення, яке можна використовувати для виконання GET запитів до сервера Actions API.

Cross-Origin headers are required

Відповідь файлу actions.json також повинна містити дійсні заголовки Cross-Origin для запитів GET і OPTIONS, зокрема значення заголовка Access-Control-Allow-Origin повинно бути *.

Дивіться вище для деталей щодо відповіді OPTIONS.

Файл 'actions.json' повинен бути збережений і доступний у кореневій директорії домену.

Наприклад, якщо ваш веб-додаток розгорнуто на my-site.com, то файл actions.json повинен бути доступний за адресою https://my-site.com/actions.json. Цей файл також повинен бути доступним через Cross-Origin у будь-якому браузері за допомогою заголовка Access-Control-Allow-Origin зі значенням *.

Rules #

Поле rules дозволяє додатку зіставляти набір відносних маршрутів вебсайту з іншими шляхами.

Type: Array of 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 - Шаблон, який відповідає кожному вхідному шляху.

  • apiPath - Місцезнаходження, визначене як абсолютний шлях або зовнішній URL.

Правила - pathPattern #

Шаблон, який відповідає кожному вхідному шляху. Це може бути абсолютним або відносним шляхом і підтримує наступні формати:

  • Exact Match: відповідає точному шляху URL.

    • Приклад: /exact-path
    • Приклад: https://website.com/exact-path
  • Wildcard Match: використовує підстановки для відповідності будь-якій послідовності символів у шляху URL. Це може відповідати одному (за допомогою *) або декільком сегментам (за допомогою **). (див. розділ Path Matching нижче).

    • Приклад: /trade/* відповідатиме /trade/123 та /trade/abc, захоплюючи лише перший сегмент після /trade/.
    • Приклад: /category/*/item/** відповідатиме /category/123/item/456 та /category/abc/item/def.
    • Приклад: /api/actions/trade/*/confirm відповідатиме /api/actions/trade/123/confirm.

Правила - apiPath #

Цільовий шлях для запиту на дію. Це може бути визначено як абсолютний шлях або зовнішній URL.

  • Приклад: /api/exact-path
  • Приклад: https://api.example.com/v1/donate/*
  • Приклад: /api/category/*/item/*
  • Приклад: /api/swap/**

Правила - Query Parameters #

Параметри запиту з оригінального URL завжди зберігаються і додаються до відображеного URL.

Правила - Path Matching #

Наступна таблиця наводить синтаксис для шаблонів відповідності шляхів:

ОператорMatches
*Один сегмент шляху, не включаючи оточуючих символів роздільника шляху / символів.
**Відповідає нулю або більше символам, включаючи будь-які символи роздільника шляху / між кількома сегментами шляху. Якщо використовуються інші оператори, оператор ** повинен бути останнім оператором.
?Непідтримуваний шаблон.

Rules Приклади #

Наступний приклад демонструє правило точного збігу для зіставлення запитів до '/buy' з кореня вашого сайту з точним шляхом '/api/buy' відносно кореня вашого сайту:

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

Наступний приклад використовує шаблонне зіставлення шляхів для відображення запитів до будь-якого шляху (крім підкаталогів) під /actions/ в корені вашого сайту на відповідний шлях під /api/actions/ відносно кореня вашого сайту:

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

Наступний приклад використовує шаблонне зіставлення шляхів для відображення запитів до будь-якого шляху (крім підкаталогів) під /donate/ в корені вашого сайту на відповідний абсолютний шлях https://api.dialect.com/api/v1/donate/ на зовнішньому сайті:

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

Наступний приклад використовує збіг шляхів з підстановкою для ідемпотентного правила зіставлення запитів до будь-якого шляху (включаючи підкаталоги) під /api/actions/ з кореня вашого сайту на цей же шлях:

Info

Ідемпотентні правила дозволяють blink клієнтам легше визначити, чи підтримує заданий шлях запити Action API, без необхідності додавання префікса solana-action: URI або виконання додаткового тестування відповідей.

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

Action Identity #

Action ендпоінти можуть включати Action Identity в транзакціях, які повертаються у їхній POST відповіді для підпису користувачем. Це дозволяє індексаторам та аналітичним платформам легко та надійно приписувати активність у блокчейні до конкретного Action Provider (тобто сервісу) у перевірюваний спосіб.

Action Identity — це пара ключів, яка використовується для підпису спеціально сформованого повідомлення, що включається у транзакцію за допомогою інструкції Memo. Це Identifier Message можна достовірно приписати певному Action Identity, а отже, і транзакціям, певного Action Provider.

Пара ключів не обов'язково повинна підписувати саму транзакцію. Це дозволяє гаманцям та додаткам покращити доставку транзакцій, коли на транзакції, повернутій користувачеві, немає інших підписів (див. Транзакція POST відповіді).

Якщо для випадку використання Action Provider потрібно, щоб його бекенд-сервіси попередньо підписали транзакцію перед користувачем, вони повинні використовувати цю пару ключів як свій Action Identity. Це дозволить зменшити кількість облікових записів, які включаються в транзакцію, що знижує загальний розмір транзакції на 32 байти.

Action Identity повідомлення #

Повідомлення Action Identity є рядком UTF-8, розділеним двокрапками, яке включається у транзакцію за допомогою єдиної інструкції SPL Memo.

protocol:identity:reference:signature
  • protocol - Значення протоколу, що використовується (встановлене як solana-action) відповідно до URL Scheme вище
  • identity - Значення повинно бути публічною адресою ключа Action Identity, закодованою у форматі base58
  • reference - Значення повинно бути масивом із 32 байтів, закодованим у форматі base58. Це можуть бути або не бути публічні ключі, у межах або поза кривою, і можуть або не можуть відповідати обліковим записам у Solana.
  • signature - Підпис, закодований у форматі base58, створений від підпису пари ключів Action Identity тільки значенням reference.

Значення reference повинно бути використане лише один раз і лише в одній транзакції. Для приписування транзакцій Action Provider лише перше використання значення reference вважається дійсним.

Транзакції можуть мати кілька інструкцій Memo. Під час виконання getSignaturesForAddress, поле результатів memo поверне кожну інструкцію Memo як окремий рядок, розділений крапкою з комою.

Інструкція Memo повинна містити лише Identifier Message і не повинна містити інших даних.

Параметри identity і reference повинні бути включені як ключі типу read-only, non-signer в транзакцію для інструкції, яка НЕ є інструкцією Memo для повідомлення ідентифікатора.

Інструкція Memo Identifier Message не повинна мати жодних наданих облікових записів. Якщо будь-які облікові записи надані, програма Memo вимагає, щоб ці облікові записи були дійсними підписниками. Для цілей ідентифікації дій це обмежує гнучкість і може погіршити досвід користувача. Тому це вважається антипаттерном і повинно бути уникнене.

Перевірка Action Identity #

Будь-яка транзакція, яка включає обліковий запис identity, може бути надійно пов'язана з Action Provider у багатокроковому процесі:

  1. Отримайте всі транзакції для даного identity.
  2. Проаналізуйте та перевірте рядок memo кожної транзакції, переконуючись, що підпис signature є дійсним для збереженого значення reference.
  3. Перевірте, що конкретна транзакція є першим випадком reference на ланцюжку:
    • Якщо ця транзакція є першим випадком reference, транзакція вважається перевіреною і може бути безпечно приписана Action Provider.
    • Якщо ця транзакція НЕ є першим випадком reference, вона вважається недійсною і, отже, не приписується Action Provider.

Оскільки валідатори Solana індексують транзакції за ключами облікових записів, метод RPC getSignaturesForAddress може бути використаний для пошуку всіх транзакцій, які включають обліковий запис identity.

Відповідь цього методу RPC містить усі дані Memo у полі memo. Якщо у транзакції використовувалися кілька інструкцій Memo, кожне повідомлення memo буде включено в це поле memo, і його слід відповідно проаналізувати перевіряючому для отримання Identity Verification Message.

Ці транзакції повинні спочатку вважатися НЕПЕРЕВІРЕНІ. Це пов’язано з тим, що для підписання транзакції не потрібен identity, що дозволяє будь-якій транзакції включати цей обліковий запис, як такий, що не підписує. Це потенційно може штучно збільшувати кількість приписувань та використань.

Identity Verification Message слід перевірити, щоб забезпечити, що підпис був створений identity, який підписав reference. Якщо перевірка цього підпису не вдалася, транзакцію вважають недійсною і вона не повинна приписуватися Action Provider.

Якщо перевірка підпису успішна, перевіряючий повинен переконатися, що ця транзакція є першим випадком reference на ланцюжку. Якщо це не так, транзакцію вважають недійсною.