原文链接:https://solana.com/docs/advanced/actions
Solana Actions是符合规范的应用程序接口,可返回 Solana 区块链上的交易,以便在 二维码、按钮 + 小工具和互联网网站等各种环境中进行预览、签名和发送。Actions 使开发人员可以轻松地将 Solana 生态系统中的各种操作集成到自己的环境中,允许你在不需要跳转到其他应用或网页的情况下执行区块链交易。
区块链链接(或 blinks)可将任何 Solana Action 转化为可共享、元数据丰富的链接。Blinks 允许支持 Action 的客户端(钱包浏览器插件、机器人)为用户显示额外的功能。在网站上,Blink 可能会立即触发钱包中的交易预览,而无需进入去中心化应用程序;在 Discord 中,机器人可能会将 Blink 扩展为一组交互式按钮。这使得在任何能够显示 URL 的网页界面上进行链上交互成为可能。
快速开始创建自定义 Solana Action:
npm install @solana/actions
你还可以在此处找到执行本地 SOL 转账操作的源代码,并在此软件仓库中找到其他几个操作示例。
将自定义 Solana Action 部署到生产环境:
actions.json
文件)响应时包含跨域 Headers
如果你正在寻找有关构建 Action 和 Blink 的灵感,请查看Awesome Blinks资源库中的一些社区作品,以及新创意作品。
Solana Actions 规范使用一套标准 API,将可签名交易(以及最终需签名数据)从应用程序直接传送给用户。它们托管在可公开访问的 URL 上,任何客户端都可以通过 URL 与之交互。
提示
你可以将 Action 视为一个应用程序接口端点,它会返回元数据和一些需用户使用区块链钱包签名(交易或认证信息)的内容。
Action API 包括向 Action URL 端点发出简单的GET
和POST
请求,以及遵循 Actions 接口规范处理响应。
实际上,与 Actions 的交互和与常见的 REST API 的交互非常相似:
GET
请求,以获取可用 Action 的元数据POST
请求,发送交易数据供用户签名Solana Action 的执行和生命周期
图片来源 MasterCui
原图请看 https://solana-developer-content.vercel.app/assets/docs/action-execution-and-lifecycle.png
从 Action URL 接收交易时,客户端应将这些交易提交到区块链,并管理其状态生命周期。
Action 还支持在执行前进行一定程度的失效验证。GET
和POST
请求可能会返回一些元数据,说明该操作是否可以被执行(如disabled
字段)。
例如,如果有一个 Action 端点用于 DAO 治理提案进行投票,但投票窗口已关闭,那么初始GET 请求可能会返回错误信息 "此提案不再进行投票",并且 "投赞成票 "和 "投反对票 "按钮被 "禁用"。
Blinks(区块链链接)是客户端应用程序,可分析 Action 应用程序接口,并基于 Action 交互和执行构建用户界面。
支持 Blinks 的客户端应用程序检测与 Action 兼容的 URL,对其进行解析,并允许用户在标准化界面上与之交互。
提示
任何完整分析 Action API 以构建其完整界面的客户端程序被称为 Blink,并非所有使用 Action API 的客户端都是 Blink。
Blink URL 描述的是一个客户端应用程序,它能让用户完成执行 Action的整个生命周期,包括使用钱包进行签名。
https://example.domain/?action=<action_url>
客户端应用程序成为 Blink 的条件:
Blink URL 必须包含一个action
查询参数,其值为 URL 编码的 Action URL。该值必须进行 URL 编码,以免与其他协议参数冲突。
客户端应用程序必须对action
查询参数进行 URL 解码,并分析所提供的 Action API(请参阅 Action URL 协议)。
客户端必须提供丰富的用户界面,使用户能够完成执行 Action 的整个生命周期,包括使用钱包进行签名。
提示
并非所有 Blink 客户端应用程序(如网站或 dApps)都支持全部 Action。应用程序开发人员可以选择在其 Blink 界面中支持哪些 Action。
下面的示例演示了一个有效的 blink URL,其action
值为 solana-action:https://actions.alice.com/donate
编码后的数据:
https://example.domain/?action=solana-action%3Ahttps%3A%2F%2Factions.alice.com%2Fdonate
Blinks 至少可以通过三种方式识别 Action:
solana-action:https://actions.alice.com/donate
在这种情况下,只有支持的客户端才能正常展示。不会有 fallback 链接预览,也不会有可以在非支持客户端之外访问的网站。
例如,https://alice.com/actions.json 将 https://alice.com/donate(向 Alice 捐赠的网页 URL)映射到 API URL https://actions.alice.com/donate(向 Alice 捐赠的 Action 托管在该 URL)。
在 "插页式" 网站 URL 中嵌入 Action URL,该 URL 支持解析 Action。
https://example.domain/?action=<action_url>
支持 Blink 的客户端应能接受上述任何格式,并正确呈现界面,以便直接在客户端执行操作。
对于不支持 Blink 的客户端,应该有一个底层网页(使浏览器成为通用的备用工具)。
如果用户在客户端上点击不是 Action 按钮或文本输入框的任何地方,会进入底层网页。
虽然 Solana Action 和 Blink 是一种无权限协议/规范,但仍需客户端应用程序和钱包来最终帮助用户签署交易。
不同的客户端应用程序或钱包中的可能有不同的要求,即他们的客户端将自动展开哪些 Action 端点,在社交媒体上直接向用户展示哪些内容。
例如,有些客户端可能会采用 "白名单 "方法,在为用户展开 Action(如 Dialect 的 Action 注册表(详见下文))之前可能需要验证。
所有的 blink 会在 Dialect 的 dial.to blinks 中间页上呈现,并允许签名,同时它们的注册状态会显示在 blink 中。
Dialect Actions 注册表是 Solana 生态系统的公共产品,Dialect在 Solana 基金会和其他社区成员的帮助下,其维护了一个公共注册表,包含已验证的区块链链接。自发布之日起,只有在 Dialect 注册表中注册的 Action 才能在 Twitter feed 中发布。
客户端应用程序和钱包可以自由选择使用该公共注册表或其他解决方案,以确保用户资产安全。如果未通过 Dialect 注册表验证,区块链链接将不会被 blink 客户端解析,而是展示为一个普通 URL。
开发人员可在此处申请 Dialect 验证:https://dial.to/register
Solana Action 规范由请求/响应交互流程中的如下关键部分组成
Action 客户端(如钱包应用、浏览器扩展、dApp、网站等)发送请求,收集用户界面需要的元数据,并实现用户输入接入 Action API。
应用程序(如网站、服务器后台等)生成需签名的交易或数据,并返回给 Action 客户端,提示用户确认、签名并发送到区块链。
Solana Action URL 定义了使用solana-action
协议发起交互式请求。
请求是交互式的,客户端会使用 URL 中的参数发出一系列标准化 HTTP 请求,以创建可签名的交易或信息,供用户使用钱包签名。
需要一个单独的link
字段作为路径名。该值需要是按需进行 URL 编码 的 HTTPS 绝对路径。
如果 URL 包含查询参数,则必须进行 URL 编码。对 URL 值进行编码可避免与其他 Actions 协议参数发生冲突,协议参数可能随着协议规范更新而添加。
如果 URL 不包含查询参数,则不应进行 URL 编码。这样可以生成更短的 URL 和更简洁的二维码。
无论哪种情况,客户端都必须对值进行 URL 解码。如果值未进行 URL 编码,也不会有副作用。如果解码后不是 HTTPS 绝对路径,钱包必须将其视为格式错误,不能进行处理。
为了允许在 Action 客户端(包括 Blink)内进行跨源资源共享(CORS),所有 Action 端点都应使用有效的标头来响应OPTIONS
方法的 HTTP 请求,从而允许客户端通过 CORS 检查,处理其所有后续请求。
Action 客户端可向 Action URL 端点发起"预检"请求,以检查对 Action URL 的后续 GET 请求是否能通过所有 CORS 检查。这些 CORS 预检检查是使用OPTIONS
HTTP 方法进行的,并响应所有必要的 HTTP headers,以便 Action 客户端(如 blinks)从其源域正确发出所有后续请求。
所需的 HTTP headers 至少包括
Access-Control-Allow-Origin
: *
。Access-Control-Allow-Methods
: GET、POST、PUT、OPTIONS
Access-Control-Allow-Headers
: 至少包含Content-Type、Authorization、Content-Encoding、Accept-Encoding
为简单起见,开发人员可考虑对OPTIONS
请求返回与 GET
响应相同的响应和 headers。
actions.json 的跨源 Header
对于`actions.json`文件的`GET`和`OPTIONS`请求,响应还必须返回有效的 Cross-Origin headers,特别是`Access-Control-Allow-Origin` headers 值为`*`。
更多详情,请参阅下面的[actions.json](https://solana.com/docs/advanced/actions#actionsjson)。
Action 客户端(如钱包、浏览器扩展等)向 Action URL 端点发出 HTTPGET
JSON 请求。
Accept-Encoding
Header。Action URL 端点(如应用程序或服务器后端)应返回 HTTPOK
JSON 响应(正文中包含有效的 Payload)或 HTTP 错误码。
返回值需包含Content-Encoding
Header,以便进行 HTTP 压缩。
返回中的Content-Type
Header 应为application/json
。
除非 HTTP 缓存响应 Header 明确设置,客户端不应缓存响应。
客户端应向用户显示title
和icon
图像。
带有 HTTPOK
JSON 响应的GET
响应应包括遵循接口规范的正文有效 Payload:
export interface ActionGetResponse {
/** 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;
}
icon
- 图标的 HTTP 或 HTTPS 绝对路径。格式支持 SVG、PNG 或 WebP,其他格式客户端/钱包必须将其作为格式错误图标拒绝。
title
- 表示 Action 请求来源的 UTF-8 字符串。例如,发起请求的品牌、商店、应用程序或个人的名称。
description
- 提供 Action 信息的 UTF-8 字符串。需要显示给用户。
label
- 一个 UTF-8 字符串,将显示在按钮上供用户点击。所有标签不应超过 5 个词组,并应以动词开头,以明确用户希望采取的操作。例如,"Mint NFT"、"Vote Yes "或 "Stake 1 SOL"。
disabled
- 布尔值,表示渲染按钮(显示label
字符串)的禁用状态。如果返回中不包含该值,则disabled
默认为false
(即默认为启用)。例如,如果治理投票 Action 端点已关闭,则设置disabled=true
,label
就会显示 "Vote Closed(投票已关闭)"。
error
- 用于非致命错误的可选错误指示。如果存在,客户端应将其显示给用户,但不应阻止 Action 的解析与显示。错误可与disabled
一起用于显示错误原因,例如:业务限制、权限、状态或外部资源异常等
export interface ActionError {
/** non-fatal error message to be displayed to the user */
message: string;
}
links.actions
- 端点相关操作的可选数组。应为用户展示出所有列表,并要求只执行其中一项。例如,治理投票 Action 端点可能会向用户返回三个选项:"投赞成票"、"投反对票 "和 "弃权票"。
links.actions
,客户端应使用根label
字符串渲染单个按钮,并向与 GET 请求相同的 Action URL 端点发出 POST 请求。links.action
,客户端应仅根据links.actions
字段中列出的项目显示按钮和输入字段。客户端不应为根label
的内容显示按钮。export interface LinkedAction {
/** URL endpoint for an action */
href: string;
/** button text rendered to the user */
label: string;
/** Parameter to accept user input within an action */
parameters?: [ActionParameter];
}
/** Parameter to accept user input within an action */
export interface ActionParameter {
/** 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;
}
下面的响应示例提供了一个单一的 Action 操作,向用户展示一个标签为 "申请访问令牌 "的单一按钮:
{
"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"
}
]
}
}
下面的响应示例演示了如何接受用户的文本输入(通过parameters
参数),并将该输入包含在最终的POST
请求端点中(通过LinkedAction
中的href
字段):
下面的响应示例为用户提供了 3 个链接的操作,以质押 SOL:一个标有 "Stake 1 SOL "的按钮,另一个标有 "Stake 5 SOL "的按钮,以及一个允许用户输入特定 "金额"值的文本输入框,该值会被发送到 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
}
]
}
]
}
}
下面的响应示例提供了一个输入框,供用户输入金额
,该值在 POST 请求中使用(可以作为查询参数,也可以使用 URL 路径):
{
"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
}
]
}
]
}
}
客户端向 ACTION URL 发出 HTTPPOST
JSON 请求,其正文有效 Payload 为
{
"account": "<account>"
}
account
- 可以签署交易的账户公钥 base58 编码。客户端应使用 Accept-Encoding Header 发出请求,应用程序可使用 Content-Encoding Header 响应 HTTP 压缩。
客户端应在发出请求时显示 Action URL 的域名。如果发出的是GET
请求,客户端还应显示标题
,并从渲染 GET 响应中的图标
。
Action POST 端点
应返回 HTTPOK
JSON 响应(正文中包含有效的有效 Payload)或恰当的 HTTP 错误码。
响应包含Content-Type
Header,值为application/json
。
带有 HTTPOK
JSON 响应的POST
响应应包含以下内容的正文有效 Payload:
export interface ActionPostResponse {
/** base64 encoded serialized transaction */
transaction: string;
/** describes the nature of the transaction */
message?: string;
}
message
- UTF-8 字符串,用于描述响应中包含的交易的说明。客户端应将此值显示给用户。例如,所购物品的名称、可用购买折扣或感谢信。
客户端和应用程序应允许在请求正文和响应正文中增加额外字段,这些字段可能会在未来的规范更新中添加。
提示
应用程序可能会响应部分或完全签名的交易。客户端和钱包必须将该交易视为不可信。
如果交易签名
为空,或者交易没有被签名:
feePayer
,并将feePayer
设置为请求中的account
。recentBlockhash
,并将recentBlockhash
设置为最新的 blockhash。如果交易已部分签署:
feePayer
或recentBlockhash
,否则将导致所有现有签名无效。客户端必须仅使用请求中的account
对交易进行签名,且仅在预期需要该账户的签名时才能这样做。
如果请求中需要除account
签名之外的任何签名,则客户端必须拒绝该交易,将其视为恶意交易。
actions.json
文件的目的是让应用程序指示客户端哪些 URL 支持 Solana 操作,并提供可用于向 Action API 服务器执行 GET 请求的映射。
CROSS-ORIGIN HEADERS
对于`GET`和`OPTIONS`请求,`actions.json`文件响应还必须返回有效的 Cross-Origin 标头,特别是`Access-Control-Allow-Origin`值为`*`。
更多详情,请参阅上文的 [OPTIONS 响应](https://solana.com/docs/advanced/actions#options-response)。
actions.json
文件应存储在域的根目录下,并可开放访问。
例如,如果你的网络应用程序部署在my-site.com
,那么actions.json
文件应可通过https://my-site.com/actions.json 访问。
该文件还应可通过任何浏览器跨源访问,Access-Control-Allow-Origin
Header 值应为*
。
rules
字段允许应用程序将一组相对路径映射到一组其他路径。
类型: 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 地址。
匹配输入路径的表达式。可以是绝对路径或相对路径,并支持以下格式:
精确匹配:精确匹配 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
。Action 请求的目标路径。可以是绝对路径或外部 URL。
/api/exact-path
https://api.example.com/v1/donate/*
/api/category/*/item/*
/api/swap/**
原始 URL 中的查询参数会附加到映射的 URL 中。
下表概述了路径匹配模式的语法:
操作符 | 匹配内容 |
---|---|
* |
单个路径段,不包括路径分隔符/。 |
** |
匹配零个或多个字符,包括多个路径段之间的路径分隔符/。如果包含其他操作符,** 操作符必须是最后一个操作符。 |
? |
不支持 |
下面的示例演示了一个精确匹配规则,将/buy
请求映射到相对于网站根目录的精确路径/api/buy
:
{
"rules": [
{
"pathPattern": "/buy",
"apiPath": "/api/buy"
}
]
}
下面的示例使用通配符路径匹配,将请求/actions/
下的任何路径(不包括子路径)映射到相对于网站根目录的/api/actions/
下的相应路径:
{
"rules": [
{
"pathPattern": "/actions/*",
"apiPath": "/api/actions/*"
}
]
}
下面的示例使用通配符路径匹配,将/donate/
下任何路径(不包括子路径)的请求映射到外部网站上相应的绝对路径https://api.dialect.com/api/v1/donate/
{
"rules": [
{
"pathPattern": "/donate/*",
"apiPath": "https://api.dialect.com/api/v1/donate/*"
}
]
}
下面的示例使用通配符路径匹配幂等规则,将请求从网站根目录映射到/api/actions/
下的任何路径(包括子路径):
提示
幂等规则使 blink 客户端更轻松地确定给定路径是否支持 Action API 请求,而无需使用solana-action.
URI 作为前缀或执行额外的响应测试。
{
"rules": [
{
"pathPattern": "/api/actions/**",
"apiPath": "/api/actions/**"
}
]
}
Action 端点可在其 POST 响应中包含一个 Action 身份,供用户签名。这样,索引器和分析平台就能以可验证的方式轻松地将链上活动归属于特定的 Action 提供者(即服务)。
Action 身份是一个密钥对,用于签署使用 memo 指令包含在交易中的特殊格式信息。该身份信息可验证归属于特定的 Action 身份,从而将交易归属于特定的 Action 提供者。
交易本身不需要密钥对签名。这样,当返回给用户的交易上没有其他签名时,钱包和应用程序就能提高交易的可交付性(参见 POST 响应交易)。
如果 Action 提供者的用例要求其后端服务在用户之前对交易进行预签名,则应使用此密钥对作为其 Action 身份。这样,交易中就可以少包含一个账户,使交易数据量减少 32 字节。
Action 身份信息是使用单条 SPL memo指令包含在交易中的冒号分隔的 UTF-8 字符串。
protocol
- 使用的协议值(根据上述URL 协议设置为solana-action
)。identity
- Action 身份密钥对公钥的 base58 编码reference
- Base58 编码的 32 字节数组。这个数组可能是公钥或非公钥,可能与 Solana 上的账户对应,也可能不对应。signature
- 由 Action 身份密钥对创建的 base58 编码签名,仅对reference
签名。reference
在单个交易中只能使用一次。在将交易与 Action 提供程序关联时,只有第一次使用reference
才被视为有效。
交易可能有多个 memo 指令。执行getSignaturesForAddress
时,结果中memo
字段将以单个字符串形式返回每个 memo 指令的信息,每个信息之间用分号隔开。
身份识别信息的 memo 指令不应包含其他数据。
identity
和reference
应作为只读、非签名密钥包含在交易中,这些密钥所在的指令不应是标识消息 memo 指令。
标识符消息 memo 指令必须不提供任何账户。如果提供了任何账户,memo 程序要求这些账户必须是有效的签名者。为了识别操作的目的,却限制了灵活性并可能降低用户体验。因此,这被视为一种反模式,必须避免使用。
任何包含identity
账户的交易都可以通过多个步骤通过 Action 提供者进行验证
identity
的所有交易。signature
对所存储的reference
有效。reference
的首次出现:
由于 Solana 验证器根据账户密钥对交易进行索引,因此可以使用getSignaturesForAddress
RPC 方法定位包括identity
账户在内的所有交易。
此 RPC 方法的响应包括memo
字段中的所有 memo 数据。如果交易中使用了多个 memo 指令,则每个 memo 信息都将包含在此memo
字段中,验证程序必须对其进行相应的解析,以获得身份验证信息。
这些交易最初应视为未验证。这是因为不需要identity
信息来签署交易,从而允许任何交易将此账户作为非签署者。可能会人为夸大归属和使用次数。
需检查确保signature
是由签署reference
的identity
创建的。如果签名验证失败,则交易无效,应归属于 Action 提供者。
如果签名验证成功,验证者应确保该交易是reference
在链上的首次出现。如果不是,则认为该交易无效。