Дії та Blinks

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

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

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

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

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

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

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

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

  • переконайтеся, що ваш застосунок має коректний файл actions.json у кореневій директорії вашого домену
  • переконайтеся, що ваш застосунок відповідає з необхідними заголовками Cross-Origin на всіх ендпоінтах Actions, включно з файлом actions.json
  • тестуйте та налагоджуйте ваші blinks/actions за допомогою Blinks Inspector

Якщо вам потрібне натхнення для створення Actions та blinks, перегляньте репозиторій Awesome Blinks із роботами спільноти та навіть ідеями для нових.

Actions

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

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

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

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

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

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

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

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

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

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

Наприклад, якщо є ендпоінт Action, що забезпечує голосування за пропозицію управління DAO, вікно голосування для якої закрилося, початковий GET-запит може повернути повідомлення про помилку «Термін голосування за цю пропозицію завершено» та кнопки «Голосувати За» і «Голосувати Проти» як «вимкнені».

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

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

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

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

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

Щоб будь-який клієнтський застосунок став blink:

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

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

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

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

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

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

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

  1. Надсилання явного URL Action: solana-action:https://actions.alice.com/donate

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

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

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

  3. Вбудовування URL Action у URL «проміжного» сайту, який уміє аналізувати Actions.

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

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

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

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

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

Використовуйте інструмент Blinks Inspector для аналізу, налагодження та тестування ваших blinks та actions безпосередньо у браузері. Ви можете переглядати GET і POST корисні навантаження відповідей, заголовки відповідей, а також тестувати всі вхідні дані кожної з ваших пов'язаних Actions.

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

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

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

Реєстр Actions від Dialect

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

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

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

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

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

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

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

Типи та інтерфейси, оголошені в цих файлах readme, часто є спрощеною версією типів для кращої читабельності.

Для кращої типобезпеки та покращеного досвіду розробника пакет @solana/actions-spec містить складніші визначення типів. Ви можете знайти вихідний код тут.

Схема URL

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

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

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

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

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

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

Відповідь OPTIONS

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

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

Мінімально необхідні HTTP-заголовки включають:

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

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

Заголовки Cross-Origin для actions.json

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

Дивіться actions.json нижче для отримання додаткової інформації.

GET-запит

Клієнт Action (наприклад, гаманець, розширення браузера тощо) повинен виконати HTTP-запит GET JSON до кінцевої точки URL Action.

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

GET-відповідь

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

Відповіді з помилками (тобто HTTP-статуси 4xx та 5xx) повинні повертати тіло відповіді JSON відповідно до ActionError, щоб відобразити корисне повідомлення про помилку користувачам. Дивіться Помилки Action.

Тіло 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 — тип дії, що надається користувачу. За замовчуванням — action. Початковий ActionGetResponse повинен мати тип action.

    • action — стандартна дія, що дозволяє користувачу взаємодіяти з будь-якою з LinkedActions
    • completed — використовується для оголошення стану "завершено" в ланцюжку дій.
  • 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 — необов'язкова індикація помилки для некритичних помилок. Якщо присутня, клієнт повинен відобразити її користувачу. Якщо встановлена, вона не повинна перешкоджати клієнту інтерпретувати або відображати дію користувачу (дивіться Помилки Action). Наприклад, помилку можна використовувати разом із disabled для відображення причини, як-от бізнес-обмеження, авторизація, стан або помилка зовнішнього ресурсу.

  • links.actions — необов'язковий масив пов'язаних дій для кінцевої точки. Користувачам повинен відображатися інтерфейс для кожної з перелічених дій, і очікується, що вони виконають лише одну. Наприклад, кінцева точка дії голосування може повертати три варіанти для користувача: "Vote Yes", "Vote No" та "Abstain from Vote".

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

    • Якщо links.actions вказано, клієнт повинен рендерити лише кнопки та поля введення на основі елементів, перелічених у полі links.actions. Клієнт не повинен рендерити кнопку для вмісту кореневого label.

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 дозволяє оголошувати, які вхідні дані Action API запитує від користувача:

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

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

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

Значення min та max дозволяють встановити нижню та/або верхню межу введених даних від користувача (тобто мінімальне/максимальне число та/або мінімальну/максимальну довжину символів) і мають використовуватися для валідації на стороні клієнта. Для полів type зі значенням date або datetime-local ці значення мають бути рядками дат. Для інших рядкових type-полів значення мають бути числами, що представляють мінімальну/максимальну довжину символів.

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

Поле type дозволяє Action API оголошувати більш конкретні поля введення користувача, забезпечуючи кращу валідацію на стороні клієнта та покращуючи користувацький досвід. У багатьох випадках цей тип буде нагадувати стандартний елемент HTML input.

ActionParameterType можна спростити до наступного типу:

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

Кожне зі значень type зазвичай повинно призводити до поля введення користувача, що нагадує стандартний HTML-елемент input відповідного type (тобто <input type="email" />) для кращої валідації на стороні клієнта та покращення користувацького досвіду:

  • text — еквівалент HTML-елемента введення "text"
  • email — еквівалент HTML-елемента введення "email"
  • url — еквівалент HTML-елемента введення "url"
  • number — еквівалент HTML-елемента введення "number"
  • date — еквівалент HTML-елемента введення "date"
  • datetime-local — еквівалент HTML-елемента введення "datetime-local"
  • checkbox — еквівалент групи стандартних HTML-елементів введення "checkbox". Action API повинен повертати options, як описано нижче. Користувач повинен мати можливість вибрати кілька з наданих варіантів прапорця.
  • radio — еквівалент групи стандартних HTML-елементів введення "radio". Action API повинен повертати options, як описано нижче. Користувач повинен мати можливість вибрати лише один із наданих варіантів radio.
  • Інші еквіваленти типів HTML-введення, не зазначені вище (hidden, button, submit, file тощо), наразі не підтримуються.

На додаток до елементів, що нагадують типи HTML-введення вище, також підтримуються наступні елементи введення користувача:

  • textarea — еквівалент HTML-елемента textarea. Дозволяє користувачу вводити багаторядковий текст.
  • select — еквівалент HTML-елемента select, що дозволяє користувачу працювати з полем у стилі «випадаючого списку». Action API має повертати options, як описано нижче.

Якщо type встановлено як select, checkbox або radio, то Action API повинен включати масив options, кожен з яких містить щонайменше label і value. Кожен параметр також може мати значення selected, щоб повідомити blink-клієнту, який із варіантів має бути обрано за замовчуванням для користувача (різниця описана в checkbox і radio).

Цей ActionParameterSelectable можна спростити до наступного визначення типу:

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

Якщо type не встановлено або задано невідоме/непідтримуване значення, blink-клієнти повинні за замовчуванням використовувати text і відображати просте текстове поле введення.

Action API все одно відповідає за валідацію та санітизацію всіх даних із параметрів введення користувача, застосовуючи будь-які «обов'язкові» поля введення за потреби.

Для платформ, відмінних від HTML/веб (наприклад, нативних мобільних), слід використовувати відповідний нативний компонент введення, щоб досягти аналогічного досвіду та клієнтської валідації, як у типах HTML/веб-введення, описаних вище.

Приклад відповіді 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-запитом (можна використовувати як query-параметр, так і підшлях):

{
"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-відповідь

Кінцева точка POST дії має відповідати HTTP-відповіддю OK у форматі JSON (з валідними даними в тілі) або відповідною HTTP-помилкою.

Відповіді з помилками (тобто HTTP-коди стану 4xx та 5xx) мають повертати тіло відповіді JSON за схемою ActionError, щоб надати користувачу корисне повідомлення про помилку. Дивіться Помилки дій.

Тіло 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 та десеріалізувати її.

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

  • links.next — необов'язкове значення для «ланцюжка» кількох дій послідовно. Після підтвердження включеної transaction у блокчейні клієнт може отримати та відобразити наступну дію. Дивіться Ланцюжок дій для отримання додаткових відомостей.

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

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

POST-відповідь — транзакція

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

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

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

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

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

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

Помилки дій

Actions API повинні повертати помилки у форматі ActionError, щоб надавати корисні повідомлення про помилки користувачу. Залежно від контексту, ця помилка може бути критичною або некритичною.

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

Коли Actions API відповідає з HTTP-кодом стану помилки (тобто 4xx та 5xx), тіло відповіді має бути JSON-навантаженням за схемою ActionError. Помилка вважається критичною, і включене message має бути показане користувачу.

Для відповідей API, які підтримують необов'язковий атрибут error (як ActionGetResponse), помилка вважається некритичною і включене message має бути показане користувачу.

Ланцюжок дій

Дії Solana можна «об'єднувати в ланцюжок» послідовно. Після підтвердження транзакції дії у блокчейні можна отримати та представити користувачу наступну дію.

Ланцюжок дій дозволяє розробникам створювати більш складні та динамічні взаємодії у blinks, зокрема:

  • надання користувачу кількох транзакцій (і в майбутньому підписання повідомлень)
  • налаштування метаданих дії на основі адреси гаманця користувача
  • оновлення метаданих blink після успішної транзакції
  • отримання API-колбеку з підписом транзакції для додаткової валідації та логіки на сервері Action API
  • налаштування повідомлень про «успіх» шляхом оновлення відображуваних метаданих (наприклад, нове зображення та опис)

Щоб об'єднати кілька дій у ланцюжок, у будь-якому ActionPostResponse включіть links.next одного з типів:

  • PostNextActionLink — посилання POST-запиту з URL зворотного виклику того самого походження для отримання signature та account користувача в тілі. Цей URL зворотного виклику має повертати NextAction.
  • InlineNextActionLink — вбудовані метадані для наступної дії, що буде представлена користувачу одразу після підтвердження транзакції. Жодного зворотного виклику не буде здійснено.
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

Після того як включена в ActionPostResponse transaction підписана користувачем і підтверджена у блокчейні, blink-клієнт повинен:

  • виконати запит зворотного виклику для отримання та відображення NextAction, або
  • якщо NextAction вже надано через links.next, blink-клієнт має оновити відображувані метадані та не робити жодного запиту зворотного виклику

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

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

Залежно від type, наступна дія має бути представлена користувачу через blink-клієнти одним із наступних способів:

  • action — (за замовчуванням) Стандартна дія, яка дозволяє користувачу бачити включені метадані дії, взаємодіяти з наданими LinkedActions та продовжувати ланцюжок наступних дій.

  • completed — Кінцевий стан ланцюжка дій, який може оновити інтерфейс blink із включеними метаданими дії, але не дозволить користувачу виконувати подальші дії.

Якщо links.next не надано, blink-клієнти повинні вважати поточну дію фінальною в ланцюжку та відображати стан «завершено» після підтвердження транзакції.

actions.json

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

Потрібні заголовки Cross-Origin

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

Дивіться відповідь OPTIONS вище для отримання додаткових відомостей.

Файл actions.json має зберігатися та бути загальнодоступним у кореневому каталозі домену.

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

Правила

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

Тип: Array із 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

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

  • Точний збіг: Відповідає точному шляху URL.

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

    • Приклад: /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/**

Правила - параметри запиту

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

Правила - зіставлення шляхів

Наступна таблиця описує синтаксис шаблонів зіставлення шляхів:

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

Приклади правил

Наступний приклад демонструє правило точного збігу для відображення запитів до /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/ з кореня вашого сайту на себе:

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

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

Ідентифікатор дії

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

Ідентифікатор дії — це keypair, що використовується для підпису спеціально відформатованого повідомлення, яке включається до транзакції за допомогою інструкції Memo. Це Ідентифікаційне повідомлення може бути верифіковано атрибутоване конкретному ідентифікатору дії, і, відповідно, транзакції — конкретному постачальнику дій.

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

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

Ідентифікаційне повідомлення дії

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

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

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

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

Жодні інші дані не повинні включатись в інструкцію Memo ідентифікаційного повідомлення.

identity та reference слід включати як ключі keys лише для читання без підпису у транзакції в інструкції, яка НЕ є інструкцією Memo ідентифікаційного повідомлення.

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

Верифікація ідентифікатора дії

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

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

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

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

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

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

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

Is this page helpful?