{"author":{"address":null,"user":"https://learnblockchain.cn/people/18968"},"content":{"body":"原文链接：[https://solana.com/docs/advanced/actions](https://solana.com/docs/advanced/actions)\r\n\r\n[Solana Actions](https://solana.com/docs/advanced/actions#actions)是符合规范的应用程序接口，可返回 Solana 区块链上的交易，以便在 二维码、按钮 + 小工具和互联网网站等各种环境中进行预览、签名和发送。Actions 使开发人员可以轻松地将 Solana 生态系统中的各种操作集成到自己的环境中，允许你在不需要跳转到其他应用或网页的情况下执行区块链交易。\r\n\r\n[区块链链接](https://solana.com/docs/advanced/actions#blinks)（或 blinks）可将任何 Solana Action 转化为可共享、元数据丰富的链接。Blinks 允许支持 Action 的客户端（钱包浏览器插件、机器人）为用户显示额外的功能。在网站上，Blink 可能会立即触发钱包中的交易预览，而无需进入去中心化应用程序；在 Discord 中，机器人可能会将 Blink 扩展为一组交互式按钮。这使得在任何能够显示 URL 的网页界面上进行链上交互成为可能。\r\n\r\n## 开始使用[#](https://solana.com/docs/advanced/actions#get-started)\r\n\r\n快速开始创建自定义 Solana Action：\r\n\r\n```\r\nnpm install @solana/actions\r\n```\r\n\r\n-   在应用程序中安装[Solana Actions SDK](https://www.npmjs.com/package/@solana/actions)\r\n-   创建一个 API 端点，接收[GET 请求](https://solana.com/docs/advanced/actions#get-request)返回有关 Action 的元数据\r\n-   创建一个 API 端点，接收[POST 请求](https://solana.com/docs/advanced/actions#post-request)并返回用户可签署交易\r\n\r\n```\r\n提示\r\n\r\n观看视频教程，了解如何[使用 @solana/actions SDK](https://www.youtube.com/watch?v=kCht01Ycif0) 创建 Solana 操作。\r\n\r\n您还可以在此处找到执行本地 SOL 转账操作的[源代码](https://github.com/solana-developers/solana-actions/blob/main/examples/next-js/src/app/api/actions/transfer-sol/route.ts)，并在此[软件仓库](https://github.com/solana-developers/solana-actions/tree/main/examples)中找到其他几个操作示例。\r\n```\r\n\r\n将自定义 Solana Action 部署到生产环境：\r\n\r\n-   确保您的应用程序在域根目录下有一个有效的 [actions.json 文件](https://solana.com/docs/advanced/actions#actionsjson)\r\n-   确保应用程序在所有 Action 端点（包括`actions.json`文件）响应时包含[跨域 Headers](https://solana.com/docs/advanced/actions#options-response)\r\n\r\n如果您正在寻找有关构建 Action 和 Blink 的灵感，请查看[Awesome Blinks](https://github.com/solana-developers/awesome-blinks)资源库中的一些社区作品，以及[新创意作品](https://github.com/solana-developers/awesome-blinks/discussions/categories/ideas-for-blinks)。\r\n\r\n## Actions[#](https://solana.com/docs/advanced/actions#actions)\r\n\r\nSolana Actions 规范使用一套标准 API，将可签名交易（以及最终需签名数据）从应用程序直接传送给用户。它们托管在可公开访问的 URL 上，任何客户端都可以通过 URL 与之交互。\r\n\r\n```\r\n提示\r\n\r\n您可以将 Action 视为一个应用程序接口端点，它会返回元数据和一些需用户使用区块链钱包签名（交易或认证信息）的内容。\r\n```\r\n\r\nAction API 包括向 Action URL 端点发出简单的`GET`和`POST`请求，以及遵循 Actions 接口规范处理响应。\r\n\r\n1.  [GET 请求](https://solana.com/docs/advanced/actions#get-request)会返回元数据，为客户端提供有关该 URL 上可用 Action 的可读信息，以及相关操作的可选列表。\r\n2.  [POST 请求](https://solana.com/docs/advanced/actions#post-request)会返回一个可签名的交易或数据，然后客户端会提示用户使用钱包签名，并在区块链或其他链外服务中执行。\r\n\r\n### Action 执行和生命周期[#](https://solana.com/docs/advanced/actions#action-execution-and-lifecycle)\r\n\r\n实际上，与 Actions 的交互和与常见的 REST API 的交互非常相似：\r\n\r\n-   客户端向 Action URL 发出初始`GET`请求，以获取可用 Action 的元数据\r\n-   端点返回的响应包括端点的元数据（如应用程序的标题和图标）以及该端点的可用 Action 列表\r\n-   客户端应用程序（如移动钱包、聊天机器人或网站）显示用户界面，供用户执行操作\r\n-   用户选择操作（点击按钮）后，客户端会向端点发出`POST`请求，发送交易数据供用户签名\r\n-   用户使用钱包签署交易，并最终将交易发送至区块链进行确认\r\n\r\n![Solana Actions Execution and Lifecycle](https://solana-developer-content.vercel.app/assets/docs/action-execution-and-lifecycle.png) Solana Action 的执行和生命周期\r\n\r\n从 Action URL 接收交易时，客户端应将这些交易提交到区块链，并管理其状态生命周期。\r\n\r\nAction 还支持在执行前进行一定程度的失效验证。`GET`和`POST`请求可能会返回一些元数据，说明该操作是否可以被执行（如`disabled`字段）。\r\n\r\n例如，如果有一个 Action 端点用于 DAO 治理提案进行投票，但投票窗口已关闭，那么初始[GET 请求](https://solana.com/docs/advanced/actions#get-request)可能会返回错误信息 \"此提案不再进行投票\"，并且 \"投赞成票 \"和 \"投反对票 \"按钮被 \"禁用\"。\r\n\r\n## Blinks[#](https://solana.com/docs/advanced/actions#blinks)\r\n\r\nBlinks（区块链链接）是客户端应用程序，可分析 Action 应用程序接口，并基于 Action 交互和执行构建用户界面。\r\n\r\n支持 Blinks 的客户端应用程序检测与 Action 兼容的 URL，对其进行解析，并允许用户在标准化界面上与之交互。\r\n\r\n```\r\n提示\r\n\r\n任何完整分析 Action API 以构建其完整界面的客户端程序被称为 Blink。并非所有使用 Action API 的客户端都是 Blink。\r\n```\r\n\r\n### Blink URL 规范[#](https://solana.com/docs/advanced/actions#blink-url-specification)\r\n\r\nBlink URL 描述的是一个客户端应用程序，它能让用户完成[执行 Action](https://solana.com/docs/advanced/actions#action-execution-and-lifecycle)的整个生命周期，包括使用钱包进行签名。\r\n\r\n```\r\nhttps://example.domain/?action=\u003caction_url\u003e\r\n```\r\n\r\n客户端应用程序成为 Blink 的条件：\r\n\r\n-   Blink URL 必须包含一个`action`查询参数，其值为 URL 编码的 [Action URL](https://solana.com/docs/advanced/actions#url-scheme)。该值必须进行 [URL 编码](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent)，以免与其他协议参数冲突。\r\n\r\n-   客户端应用程序必须对`action`查询参数进行 URL [解码](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent)，并分析所提供的 Action API（请参阅 [Action URL 协议](https://solana.com/docs/advanced/actions#url-scheme)）。\r\n\r\n-   客户端必须提供丰富的用户界面，使用户能够完成[执行 Action ](https://solana.com/docs/advanced/actions#action-execution-and-lifecycle)的整个生命周期，包括使用钱包进行签名。\r\n\r\n```\r\n提示\r\n并非所有 Blink 客户端应用程序（如网站或 dApps）都支持全部 Action。应用程序开发人员可以选择在其 Blink 界面中支持哪些 Action。\r\n```\r\n\r\n下面的示例演示了一个有效的 blink URL，其`action`值为 `solana-action:https://actions.alice.com/donate` 编码后的数据：\r\n\r\n```\r\nhttps://example.domain/?action=solana-action%3Ahttps%3A%2F%2Factions.alice.com%2Fdonate\r\n```\r\n\r\n### Blinks 识别 Action [#](https://solana.com/docs/advanced/actions#detecting-actions-via-blinks)\r\n\r\nBlinks 至少可以通过三种方式识别 Action：\r\n\r\n1. 显示展示 Action URL：`solana-action:https://actions.alice.com/donate`\r\n   \r\n    在这种情况下，只有支持的客户端才能正常展示。不会有 fallback 链接预览，也不会有可以在非支持客户端之外访问的网站。\r\n\r\n2. 在网站域名根目录上放置 [`actions.json`文件](https://solana.com/docs/advanced/actions#actionsjson)。\r\n   \r\n    例如，https://alice.com/actions.json 将 https://alice.com/donate （向 Alice 捐赠的网页 URL）映射到 API URL https://actions.alice.com/donate （向 Alice 捐赠的 Action 托管在该 URL）。\r\n\r\n4. 在 \"插页 \"网站 URL 中嵌入 Action URL，该 URL 支持解析 Action。\r\n\r\n    ```\r\n    https://example.domain/?action=\u003caction_url\u003e\r\n    ```\r\n\r\n\r\n支持 Blink 的客户端应能接受上述任何格式，并正确呈现界面，以便直接在客户端执行操作。\r\n\r\n对于不支持 Blink 的客户端，应该有一个底层网页（使浏览器成为通用的备用工具）。\r\n\r\n如果用户在客户端上点击不是 Action 按钮或文本输入框的任何地方，会进入底层网页。\r\n\r\n### Blink 测试与验证[#](https://solana.com/docs/advanced/actions#blink-testing-and-verification)\r\n\r\n虽然 Solana Action和Blink是一种无权限协议/规范，但仍需客户端应用程序和钱包来最终帮助用户签署交易。\r\n\r\n不同的客户端应用程序或钱包中的可能有不同的要求，即他们的客户端将自动展开哪些 Action 端点，在社交媒体上直接向用户展示哪些内容。\r\n\r\n例如，有些客户端可能会采用 \"白名单 \"方法，在为用户展开 Action（如 Dialect 的 Action 注册表（详见下文））之前可能需要验证。\r\n\r\n所有的 blink 会在 Dialect 的 [dial.to](https://dial.to/) blinks 中间页上呈现，并允许签名，同时它们的注册状态会显示在 blink 中。\r\n\r\n\r\n### Dialect Actions 注册表[#](https://solana.com/docs/advanced/actions#dialect's-actions-registry)\r\n\r\nDialect Actions 注册表是 Solana 生态系统的公共产品，[Dialect](https://dialect.to/)在 Solana 基金会和其他社区成员的帮助下，其维护了一个公共注册表，包含已验证的区块链链接。自发布之日起，只有在 Dialect 注册表中注册的 Action 才能在 Twitter feed 中发布。\r\n\r\n客户端应用程序和钱包可以自由选择使用该公共注册表或其他解决方案，以确保用户资产安全。如果未通过 Dialect 注册表验证，区块链链接将不会被 blink 客户端解析，而是展示为一个普通 URL。\r\n\r\n开发人员可在此处申请 Dialect 验证：[dial.to/register](https://dial.to/register)\r\n\r\n## 规范[#](https://solana.com/docs/advanced/actions#specification)\r\n\r\nSolana Action 规范由请求/响应交互流程中的如下关键部分组成\r\n\r\n-   Solana Action URL [协议](https://solana.com/docs/advanced/actions#url-scheme)\r\n-   Action URL 的 [OPTIONS 响应](https://solana.com/docs/advanced/actions#options-response)，满足 CORS 要求\r\n-   向 Action URL 发送 [GET 请求](https://solana.com/docs/advanced/actions#get-request)\r\n-   服务器的 [GET 响应](https://solana.com/docs/advanced/actions#get-response)\r\n-   向 Action URL [发送 POST 请求](https://solana.com/docs/advanced/actions#post-request)\r\n-   服务器的 [POST 响应](https://solana.com/docs/advanced/actions#post-response)\r\n\r\nAction 客户端（如钱包应用、浏览器扩展、dApp、网站等）发送请求，收集用户界面需要的元数据，并实现用户输入接入 Action API。\r\n\r\n应用程序（如网站、服务器后台等）生成需签名的交易或数据，并返回给 Action 客户端，提示用户确认、签名并发送到区块链。\r\n\r\n### URL 协议[#](https://solana.com/docs/advanced/actions#url-scheme)\r\n\r\nSolana Action URL 定义了使用`solana-action`协议发起交互式请求。\r\n\r\n请求是交互式的，客户端会使用 URL 中的参数发出一系列标准化 HTTP 请求，以创建可签名的交易或信息，供用户使用钱包签名。\r\n\r\n-   需要一个单独的`link`字段作为路径名。该值需要是按需进行 [URL 编码](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) 的 HTTPS 绝对路径。\r\n\r\n-   如果 URL 包含查询参数，则必须进行 URL 编码。对 URL 值进行编码可避免与其他 Actions 协议参数发生冲突，协议参数可能随着协议规范更新而添加。\r\n\r\n-   如果 URL 不包含查询参数，则不应进行 URL 编码。这样可以生成更短的 URL 和更简洁的二维码。\r\n\r\n\r\n无论哪种情况，客户端都必须对值进行 [URL 解码](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent)。如果值未进行 URL 编码，也不会有副作用。如果解码后不是 HTTPS 绝对路径，钱包必须将其视为**格式错误**，不能进行处理。\r\n\r\n### OPTIONS 响应[#](https://solana.com/docs/advanced/actions#options-response)\r\n\r\n为了允许在 Action 客户端（包括 Blink）内进行跨源资源共享[(CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS))，所有 Action 端点都应使用有效的标头来响应`OPTIONS`方法的 HTTP 请求，从而允许客户端通过 CORS 检查，处理其所有后续请求。\r\n\r\nAction 客户端可向 Action URL 端点发起[\"预检](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests)\"请求，以检查对 Action URL 的后续 GET 请求是否能通过所有 CORS 检查。这些 CORS 预检检查是使用`OPTIONS`HTTP 方法进行的，并响应所有必要的 HTTP headers，以便 Action 客户端（如 blinks）从其源域正确发出所有后续请求。\r\n\r\n所需的 HTTP headers 至少包括\r\n\r\n-   `Access-Control-Allow-Origin` : `*`。\r\n    -   确保所有 Action 客户端发出的请求都能正常通过 CORS 检查\r\n-   `Access-Control-Allow-Methods` : `GET、POST、PUT、OPTIONS`\r\n    -   确保 Action 支持所有必要的 HTTP 请求方法\r\n-   `Access-Control-Allow-Headers` : 至少包含`Content-Type、Authorization、Content-Encoding、Accept-Encoding`\r\n\r\n为简单起见，开发人员可考虑对`OPTIONS`请求返回与[`GET`](https://solana.com/docs/advanced/actions#get-response) 响应相同的响应和 headers。\r\n\r\n```\r\nactions.json 的跨源 Header\r\n\r\n对于`actions.json`文件的`GET`和`OPTIONS`请求，响应还必须返回有效的 Cross-Origin headers，特别是`Access-Control-Allow-Origin` headers 值为`*`。\r\n\r\n更多详情，请参阅下面的[actions.json](https://solana.com/docs/advanced/actions#actionsjson)。\r\n```\r\n\r\n### GET 请求[#](https://solana.com/docs/advanced/actions#get-request)\r\n\r\nAction 客户端（如钱包、浏览器扩展等）向 Action URL 端点发出 HTTP`GET`JSON 请求。\r\n\r\n-   请求不应指定钱包或用户。\r\n-   发起请求时应包含[`Accept-Encoding` Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding)。\r\n-   发起请求时需显示 URL 域名。\r\n\r\n### GET 响应[#](https://solana.com/docs/advanced/actions#get-response)\r\n\r\nAction URL 端点（如应用程序或服务器后端）应返回 HTTP`OK`JSON 响应（正文中包含有效的 Payload）或 HTTP 错误码。\r\n\r\n-   客户端必须处理 HTTP [客户端错误](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses)、[服务器错误](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#server_error_responses)和[重定向响应](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages)。\r\n\r\n-   返回值需包含[`Content-Encoding` Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding)，以便进行 HTTP 压缩。\r\n\r\n-   返回中的[`Content-Type` Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) 应为`application/json`。\r\n\r\n-   除非 [HTTP 缓存](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#controlling_caching)响应 Header 明确设置，客户端不应缓存响应。\r\n\r\n-   客户端应向用户显示`title`和`icon`图像。\r\n\r\n\r\n#### GET 响应正文[#](https://solana.com/docs/advanced/actions#get-response-body)\r\n\r\n带有 HTTP`OK`JSON 响应的`GET`响应应包括遵循接口规范的正文有效 Payload：\r\n\r\n```\r\nexport interface ActionGetResponse {\r\n  /** image url that represents the source of the action request */\r\n  icon: string;\r\n  /** describes the source of the action request */\r\n  title: string;\r\n  /** brief summary of the action to be performed */\r\n  description: string;\r\n  /** button text rendered to the user */\r\n  label: string;\r\n  /** UI state for the button being rendered to the user */\r\n  disabled?: boolean;\r\n  links?: {\r\n    /** list of related Actions a user could perform */\r\n    actions: LinkedAction[];\r\n  };\r\n  /** non-fatal error message to be displayed to the user */\r\n  error?: ActionError;\r\n}\r\n```\r\n\r\n-   `icon`\\- 图标的 HTTP 或 HTTPS 绝对路径。格式支持 SVG、PNG 或 WebP，其他格式客户端/钱包必须将其作为**格式错误**图标拒绝。\r\n\r\n-   `title`\\- 表示 Action 请求来源的 UTF-8 字符串。例如，发起请求的品牌、商店、应用程序或个人的名称。\r\n\r\n-   `description`\\- 提供 Action 信息的 UTF-8 字符串。需要显示给用户。\r\n\r\n-   `label`\\- 一个 UTF-8 字符串，将显示在按钮上供用户点击。所有标签不应超过 5 个词组，并应以动词开头，以明确用户希望采取的操作。例如，\"Mint NFT\"、\"Vote Yes \"或 \"Stake 1 SOL\"。\r\n\r\n-   `disabled`\\- 布尔值，表示渲染按钮（显示`label`字符串）的禁用状态。如果返回中不包含该值，则`disabled`默认为`false`（即默认为启用）。例如，如果治理投票 Action 端点已关闭，则设置`disabled=true`，`label`就会显示 \"Vote Closed（投票已关闭）\"。\r\n\r\n-   `error`\\- 用于非致命错误的可选错误指示。如果存在，客户端应将其显示给用户，但不应阻止 Action 的解析与显示。错误可与`disabled `一起用于显示错误原因，例如：业务限制、权限、状态或外部资源异常等\r\n\r\n\r\n```\r\nexport interface ActionError {\r\n  /** non-fatal error message to be displayed to the user */\r\n  message: string;\r\n}\r\n```\r\n\r\n-   `links.actions`\\- 端点相关操作的可选数组。应为用户展示出所有列表，并要求只执行其中一项。例如，治理投票 Action 端点可能会向用户返回三个选项：\"投赞成票\"、\"投反对票 \"和 \"弃权票\"。\r\n\r\n    -   如果未提供`links.actions`，客户端应使用根`label`字符串渲染单个按钮，并向与 GET 请求相同的 Action URL 端点发出 POST 请求。\r\n\r\n    -   如果提供了`links.action`，客户端应仅根据`links.actions`字段中列出的项目显示按钮和输入字段。客户端不应为根`label`的内容显示按钮。\r\n\r\n\r\n```\r\nexport interface LinkedAction {\r\n  /** URL endpoint for an action */\r\n  href: string;\r\n  /** button text rendered to the user */\r\n  label: string;\r\n  /** Parameter to accept user input within an action */\r\n  parameters?: [ActionParameter];\r\n}\r\n \r\n/** Parameter to accept user input within an action */\r\nexport interface ActionParameter {\r\n  /** parameter name in url */\r\n  name: string;\r\n  /** placeholder text for the user input field */\r\n  label?: string;\r\n  /** declare if this field is required (defaults to `false`) */\r\n  required?: boolean;\r\n}\r\n```\r\n\r\n#### GET 响应示例[#](https://solana.com/docs/advanced/actions#example-get-response)\r\n\r\n下面的响应示例提供了一个单一的 Action 操作，向用户展示一个标签为 \"申请访问令牌 \"的单一按钮：\r\n\r\n```\r\n{\r\n  \"title\": \"HackerHouse Events\",\r\n  \"icon\": \"\u003curl-to-image\u003e\",\r\n  \"description\": \"Claim your Hackerhouse access token.\",\r\n  \"label\": \"Claim Access Token\" // button text\r\n}\r\n```\r\n\r\n下面的响应示例提供了 3 个相关操作链接，允许用户点击 3 个按钮中的一个，为 DAO 提案投票：\r\n\r\n```\r\n{\r\n  \"title\": \"Realms DAO Platform\",\r\n  \"icon\": \"\u003curl-to-image\u003e\",\r\n  \"description\": \"Vote on DAO governance proposals #1234.\",\r\n  \"label\": \"Vote\",\r\n  \"links\": {\r\n    \"actions\": [\r\n      {\r\n        \"label\": \"Vote Yes\", // button text\r\n        \"href\": \"/api/proposal/1234/vote?choice=yes\"\r\n      },\r\n      {\r\n        \"label\": \"Vote No\", // button text\r\n        \"href\": \"/api/proposal/1234/vote?choice=no\"\r\n      },\r\n      {\r\n        \"label\": \"Abstain from Vote\", // button text\r\n        \"href\": \"/api/proposal/1234/vote?choice=abstain\"\r\n      }\r\n    ]\r\n  }\r\n}\r\n```\r\n\r\n#### 带参数的 GET 响应示例[#](https://solana.com/docs/advanced/actions#example-get-response-with-parameters)\r\n\r\n下面的响应示例演示了如何接受用户的文本输入（通过`parameters`参数），并将该输入包含在最终的`POST`请求端点中（通过`LinkedAction` 中的`href`字段）：\r\n\r\n下面的响应示例为用户提供了 3 个链接的操作，以质押 SOL：一个标有 \"Stake 1 SOL \"的按钮，另一个标有 \"Stake 5 SOL \"的按钮，以及一个允许用户输入特定 \"金额\"值的文本输入框，该值会被发送到 Action API：\r\n\r\n```\r\n{\r\n  \"title\": \"Stake-o-matic\",\r\n  \"icon\": \"\u003curl-to-image\u003e\",\r\n  \"description\": \"Stake SOL to help secure the Solana network.\",\r\n  \"label\": \"Stake SOL\", // not displayed since `links.actions` are provided\r\n  \"links\": {\r\n    \"actions\": [\r\n      {\r\n        \"label\": \"Stake 1 SOL\", // button text\r\n        \"href\": \"/api/stake?amount=1\"\r\n        // no `parameters` therefore not a text input field\r\n      },\r\n      {\r\n        \"label\": \"Stake 5 SOL\", // button text\r\n        \"href\": \"/api/stake?amount=5\"\r\n        // no `parameters` therefore not a text input field\r\n      },\r\n      {\r\n        \"label\": \"Stake\", // button text\r\n        \"href\": \"/api/stake?amount={amount}\",\r\n        \"parameters\": [\r\n          {\r\n            \"name\": \"amount\", // field name\r\n            \"label\": \"SOL amount\" // text input placeholder\r\n          }\r\n        ]\r\n      }\r\n    ]\r\n  }\r\n}\r\n```\r\n\r\n下面的响应示例提供了一个输入框，供用户输入`金额`，该值在 POST 请求中使用（可以作为查询参数，也可以使用 URL 路径）：\r\n\r\n```\r\n{\r\n  \"icon\": \"\u003curl-to-image\u003e\",\r\n  \"label\": \"Donate SOL\",\r\n  \"title\": \"Donate to GoodCause Charity\",\r\n  \"description\": \"Help support this charity by donating SOL.\",\r\n  \"links\": {\r\n    \"actions\": [\r\n      {\r\n        \"label\": \"Donate\", // button text\r\n        \"href\": \"/api/donate/{amount}\", // or /api/donate?amount={amount}\r\n        \"parameters\": [\r\n          // {amount} input field\r\n          {\r\n            \"name\": \"amount\", // input field name\r\n            \"label\": \"SOL amount\" // text input placeholder\r\n          }\r\n        ]\r\n      }\r\n    ]\r\n  }\r\n}\r\n```\r\n\r\n### POST 请求[#](https://solana.com/docs/advanced/actions#post-request)\r\n\r\n客户端向 ACTION URL 发出 HTTP`POST`JSON 请求，其正文有效 Payload 为\r\n```\r\n{\r\n  \"account\": \"\u003caccount\u003e\"\r\n}\r\n```\r\n\r\n-   `account`\\- 可以签署交易的账户公钥 base58 编码。\r\n\r\n客户端应使用 [Accept-Encoding Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) 发出请求，应用程序可使用 [Content-Encoding Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding) 响应 HTTP 压缩。\r\n\r\n客户端应在发出请求时显示 Action URL 的域名。如果发出的是`GET`请求，客户端还应显示`标题`，并从渲染 GET 响应中的`图标`。\r\n\r\n### POST 响应[#](https://solana.com/docs/advanced/actions#post-response)\r\n\r\nAction `POST 端点`应返回 HTTP`OK`JSON 响应（正文中包含有效的有效 Payload）或恰当的 HTTP 错误码。\r\n\r\n-   客户端必须处理 HTTP [客户端错误](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses)、[服务器错误](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#server_error_responses)和[重定向响应](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages)。\r\n-   响应包含[`Content-Type` Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)，值为`application/json`。\r\n\r\n#### POST 响应正文[#](https://solana.com/docs/advanced/actions#post-response-body)\r\n\r\n带有 HTTP`OK`JSON 响应的`POST`响应应包含以下内容的正文有效 Payload：\r\n\r\n```\r\nexport interface ActionPostResponse {\r\n  /** base64 encoded serialized transaction */\r\n  transaction: string;\r\n  /** describes the nature of the transaction */\r\n  message?: string;\r\n}\r\n```\r\n\r\n-   `transaction`\\- base64 编码的[序列化数据](https://solana-labs.github.io/solana-web3.js/classes/Transaction.html#serialize)。客户端需对交易进行 base64 解码并[反序列化](https://solana-labs.github.io/solana-web3.js/classes/Transaction.html#from)。\r\n\r\n-   `message`\\-  UTF-8 字符串，用于描述响应中包含的交易的说明。客户端应将此值显示给用户。例如，所购物品的名称、可用购买折扣或感谢信。\r\n\r\n-   客户端和应用程序应允许在请求正文和响应正文中增加额外字段，这些字段可能会在未来的规范更新中添加。\r\n\r\n```\r\n提示\r\n\r\n应用程序可能会响应部分或完全签名的交易。客户端和钱包必须将该交易视为不可信。\r\n```\r\n\r\n#### POST 响应 - 交易[#](https://solana.com/docs/advanced/actions#post-response-transaction)\r\n\r\n如果交易[`签名`](https://solana-labs.github.io/solana-web3.js/classes/Transaction.html#signatures)为空，或者交易没有被签名：\r\n\r\n-   客户端必须忽略交易中的[`feePayer`](https://solana-labs.github.io/solana-web3.js/classes/Transaction.html#feePayer)，并将`feePayer`设置为请求中的`account`。\r\n-   客户端必须忽略交易中的[`recentBlockhash`](https://solana-labs.github.io/solana-web3.js/classes/Transaction.html#recentBlockhash)，并将`recentBlockhash`设置为[最新的 blockhash](https://solana-labs.github.io/solana-web3.js/classes/Connection.html#getLatestBlockhash)。\r\n-   客户端必须在签署前序列化和反序列化交易，以确保账户密钥顺序的一致性，从而解决[这一问题](https://github.com/solana-labs/solana/issues/21722)。\r\n\r\n如果交易已部分签署：\r\n\r\n-   客户不得更改[`feePayer`](https://solana-labs.github.io/solana-web3.js/classes/Transaction.html#feePayer)或[`recentBlockhash`](https://solana-labs.github.io/solana-web3.js/classes/Transaction.html#recentBlockhash)，否则将导致所有现有签名无效。\r\n-   客户机必须验证现有签名，如果有任一签名无效，客户机必须将其视为**格式错误**交易予以拒绝。\r\n\r\n客户端必须仅使用请求中的`account`对交易进行签名，且仅在预期需要该账户的签名时才能这样做。\r\n\r\n如果请求中需要除`account`签名之外的任何签名，则客户端必须拒绝该交易，将其视为**恶意**交易。\r\n\r\n## actions.json[#](https://solana.com/docs/advanced/actions#actionsjson)\r\n\r\n[`actions.json`文件](https://solana.com/docs/advanced/actions#actionsjson)的目的是让应用程序指示客户端哪些 URL 支持 Solana 操作，并提供可用于向 Action API 服务器执行 [GET 请求](https://solana.com/docs/advanced/actions#get-request)的映射。\r\n\r\n```\r\nCROSS-ORIGIN HEADERS\r\n\r\n对于`GET`和`OPTIONS`请求，`actions.json`文件响应还必须返回有效的 Cross-Origin 标头，特别是`Access-Control-Allow-Origin`值为`*`。\r\n\r\n更多详情，请参阅上文的 [OPTIONS 响应](https://solana.com/docs/advanced/actions#options-response)。\r\n```\r\n\r\n`actions.json`文件应存储在域的根目录下，并可开放访问。\r\n\r\n例如，如果您的网络应用程序部署在`my-site.com`，那么`actions.json`文件应可通过`https://my-site.com/actions.json 访问。`该文件还应可通过任何浏览器跨源访问，`Access-Control-Allow-Origin` Header 值应为`*`。\r\n\r\n### 规则[#](https://solana.com/docs/advanced/actions#rules)\r\n\r\n`规则`字段允许应用程序将一组相对路径映射到一组其他路径。\r\n\r\n**类型：** `ActionRuleObject数组`。\r\n\r\n```\r\ninterface ActionRuleObject {\r\n  /** relative (preferred) or absolute path to perform the rule mapping from */\r\n  pathPattern: string;\r\n  /** relative (preferred) or absolute path that supports Action requests */\r\n  apiPath: string;\r\n}\r\n```\r\n\r\n-   [`pathPattern`](https://solana.com/docs/advanced/actions#rules-pathpattern)\\- 输入路径匹配表达式。\r\n\r\n-   [`apiPath`](https://solana.com/docs/advanced/actions#rules-apipath)\\- 映射后的绝对路径或外部 URL 地址。\r\n\r\n\r\n#### 规则 - pathPattern[#](https://solana.com/docs/advanced/actions#rules-pathpattern)\r\n\r\n匹配输入路径的表达式。可以是绝对路径或相对路径，并支持以下格式：\r\n\r\n-   **精确匹配**：精确匹配 URL 路径。\r\n\r\n    -   示例`/exact-path`\r\n    -   示例`：https://website.com/exact-path`\r\n-   **通配符匹配**：使用通配符匹配 URL 路径中的字符序列。可以匹配单个（使用`*`）或多个字符（使用`**`）（请参阅下面的[路径匹配](https://solana.com/docs/advanced/actions#rules-path-matching)）。\r\n\r\n    -   示例`/trade/*`将匹配`/trade/123` 和`/trade/abc`，通配符只匹配`/trade/` 后的第一段。\r\n    -   示例`/category/*/item/**`将匹配`/category/123/item/456`和`/category/abc/item/def`。\r\n    -   示例`/api/actions/trade/*/confirm`将匹配`/api/actions/trade/123/confirm`。\r\n\r\n#### 规则 - apiPath[#](https://solana.com/docs/advanced/actions#rules-apipath)\r\n\r\nAction 请求的目标路径。可以是绝对路径或外部 URL。\r\n\r\n-   示例`/api/exact-path`\r\n-   示例`https://api.example.com/v1/donate/*`\r\n-   示例`/api/category/*/item/*`\r\n-   示例`/api/swap/**`\r\n\r\n#### 规则 - 查询参数[#](https://solana.com/docs/advanced/actions#rules-query-parameters)\r\n\r\n原始 URL 中的查询参数会附加到映射的 URL 中。\r\n\r\n#### 规则 - 路径匹配[#](https://solana.com/docs/advanced/actions#rules-path-matching)\r\n\r\n下表概述了路径匹配模式的语法：\r\n\r\n| 操作符 | 匹配内容 |\r\n|---| --- |\r\n| `*` | 单个路径段，不包括路径分隔符/。 |\r\n| `**` | 匹配零个或多个字符，包括多个路径段之间的路径分隔符/。如果包含其他操作符，`**`操作符必须是最后一个操作符。 |\r\n| `?` | 不支持 |\r\n\r\n### 规则示例[#](https://solana.com/docs/advanced/actions#rules-examples)\r\n\r\n下面的示例演示了一个精确匹配规则，将`/buy`请求映射到相对于网站根目录的精确路径`/api/buy`：\r\n\r\n```\r\n{\r\n  \"rules\": [\r\n    {\r\n      \"pathPattern\": \"/buy\",\r\n      \"apiPath\": \"/api/buy\"\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n下面的示例使用通配符路径匹配，将请求`/actions/`下的任何路径（不包括子路径）映射到相对于网站根目录的`/api/actions/`下的相应路径：\r\n\r\n```\r\n{\r\n  \"rules\": [\r\n    {\r\n      \"pathPattern\": \"/actions/*\",\r\n      \"apiPath\": \"/api/actions/*\"\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n下面的示例使用通配符路径匹配，将`/donate/`下任何路径（不包括子路径）的请求映射到外部网站上相应的绝对路径`https://api.dialect.com/api/v1/donate/`\r\n\r\n```\r\n{\r\n  \"rules\": [\r\n    {\r\n      \"pathPattern\": \"/donate/*\",\r\n      \"apiPath\": \"https://api.dialect.com/api/v1/donate/*\"\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n下面的示例使用通配符路径匹配幂等规则，将请求从网站根目录映射到`/api/actions/`下的任何路径（包括子路径）：\r\n\r\n```\r\n提示\r\n\r\n幂等规则使 blink 客户端更轻松地确定给定路径是否支持 Action API 请求，而无需使用`solana-action.`URI 作为前缀或执行额外的响应测试。 \r\n```\r\n\r\n```\r\n{\r\n  \"rules\": [\r\n    {\r\n      \"pathPattern\": \"/api/actions/**\",\r\n      \"apiPath\": \"/api/actions/**\"\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n## Action 身份[#](https://solana.com/docs/advanced/actions#action-identity)\r\n\r\nAction 端点可在其 [POST 响应](https://solana.com/docs/advanced/actions#post-response)中包含一个 Action 身份，供用户签名。这样，索引器和分析平台就能以可验证的方式轻松地将链上活动归属于特定的 Action 提供者（即服务）。\r\n\r\n[Action 身份](https://solana.com/docs/advanced/actions#action-identity)是一个密钥对，用于签署使用备忘指令包含在交易中的特殊格式信息。该身份信息可验证归属于特定的 Action 身份，从而将交易归属于特定的 Action 提供者。\r\n\r\n交易本身不需要密钥对签名。这样，当返回给用户的交易上没有其他签名时，钱包和应用程序就能提高交易的可交付性（参见 [POST 响应交易](https://solana.com/docs/advanced/actions#post-response-transaction)）。\r\n\r\n如果 Action 提供者的用例要求其后端服务在用户之前对交易进行预签名，则应使用此密钥对作为其 Action 身份。这样，交易中就可以少包含一个账户，使交易数据量减少 32 字节。\r\n\r\n### Action 身份信息[#](https://solana.com/docs/advanced/actions#action-identifier-message)\r\n\r\nAction 身份信息是使用单条 [SPL 备忘录](https://spl.solana.com/memo)指令包含在交易中的冒号分隔的 UTF-8 字符串。\r\n\r\n-   `protocol`\\- 使用的协议值（根据上述[URL 协议](https://solana.com/docs/advanced/actions#url-scheme)设置为`solana-action`）。\r\n-   `identity`\\- Action 身份密钥对公钥的 base58 编码\r\n-   `reference`\\- Base58 编码的 32 字节数组。这个数组可能是公钥或非公钥，可能与 Solana 上的账户对应，也可能不对应。\r\n-   `signature`\\- 由 Action 身份密钥对创建的 base58 编码签名，仅对`reference`签名。\r\n\r\n`reference`在单个交易中只能使用一次。在将交易与 Action 提供程序关联时，只有第一次使用`reference`才被视为有效。\r\n\r\n交易可能有多个备忘录指令。执行[`getSignaturesForAddress`](https://solana.com/docs/rpc/http/getsignaturesforaddress) 时，结果中`memo`字段将以单个字符串形式返回每个备忘录指令的信息，每个信息之间用分号隔开。\r\n\r\n身份识别信息的备忘录指令不应包含其他数据。\r\n\r\n`identity`和`reference`应作为只读、非签名[密钥](https://solana-labs.github.io/solana-web3.js/classes/TransactionInstruction.html#keys)包含在交易中，这些密钥所在的指令不应是标识消息备忘录指令。\r\n\r\n标识符消息备忘录指令必须不提供任何账户。如果提供了任何账户，备忘录程序要求这些账户必须是有效的签名者。为了识别操作的目的，却限制了灵活性并可能降低用户体验。因此，这被视为一种反模式，必须避免使用。\r\n\r\n\r\n\r\n### Action 身份验证[#](https://solana.com/docs/advanced/actions#action-identity-verification)\r\n\r\n任何包含`identity`账户的交易都可以通过多个步骤通过 Action 提供者进行验证\r\n\r\n1.  获取给定`identity`的所有交易。\r\n2.  解析并验证每笔交易的备注字符串，确保`signature`对所存储的`reference`有效。\r\n3.  验证特定交易是否是链上`reference`的首次出现：\r\n    -   如果该交易是首次出现，则认为该交易已通过验证，可以安全地归属于 \"Action 提供者\"。\r\n    -   如果该交易不是首次出现，则视为无效，因此不能归属于 \"Action 提供者\"。\r\n\r\n由于 Solana 验证器根据账户密钥对交易进行索引，因此可以使用[`getSignaturesForAddress`](https://solana.com/docs/rpc/http/getsignaturesforaddress)RPC 方法定位包括`identity`账户在内的所有交易。\r\n\r\n此 RPC 方法的响应包括`memo`字段中的所有备忘录数据。如果交易中使用了多个备忘指令，则每个备忘信息都将包含在此`memo`字段中，验证程序必须对其进行相应的解析，以获得身份验证信息。\r\n\r\n这些交易最初应视为**未验证**。这是因为不需要`identity`信息来签署交易，从而允许任何交易将此账户作为非签署者。可能会人为夸大归属和使用次数。\r\n\r\n需检查确保`signature`是由签署`reference`的`identity`创建的。如果签名验证失败，则交易无效，应归属于 Action 提供者。\r\n\r\n如果签名验证成功，验证者应确保该交易是`reference`在链上的首次出现。如果不是，则认为该交易无效。","title":"Solana Actions and Blinks"},"history":null,"timestamp":1720052404,"version":1}