{"id":26651706,"url":"https://github.com/dialectlabs/smart-messaging","last_synced_at":"2025-04-11T05:08:41.608Z","repository":{"id":63365935,"uuid":"561124601","full_name":"dialectlabs/smart-messaging","owner":"dialectlabs","description":null,"archived":false,"fork":false,"pushed_at":"2022-11-17T16:05:21.000Z","size":62,"stargazers_count":39,"open_issues_count":1,"forks_count":5,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-11T05:08:36.795Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dialectlabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-11-03T02:04:13.000Z","updated_at":"2024-07-07T09:41:13.000Z","dependencies_parsed_at":"2022-11-17T17:16:32.511Z","dependency_job_id":null,"html_url":"https://github.com/dialectlabs/smart-messaging","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dialectlabs%2Fsmart-messaging","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dialectlabs%2Fsmart-messaging/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dialectlabs%2Fsmart-messaging/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dialectlabs%2Fsmart-messaging/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dialectlabs","download_url":"https://codeload.github.com/dialectlabs/smart-messaging/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248345266,"owners_count":21088244,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-25T03:05:11.089Z","updated_at":"2025-04-11T05:08:41.583Z","avatar_url":"https://github.com/dialectlabs.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Smart Messaging\n\nSmart Messages are a URL-encoded specification for interactive messaging. Built on top of Solana Pay, smart messages allow senders to encode arbitrary actions into URLs that will then be rendered and actionable for the recipient.\n\nThese actions can be both on-chain transactions as well as off-chain, wallet-authenticated actions. They can be simple token transfers, NFT buyout offers, DAO or multisig votes, social media likes and follows, e-commerce purchasing and referrals, and more.\n\n![5 0](https://user-images.githubusercontent.com/3632945/200105159-0af75807-980c-425f-a72f-5ebe1d714b08.png)\n\nIn its minimal form, the Smart Message specification is simply the [Solana Pay transaction request spec](https://docs.solanapay.com/spec#specification-transaction-request). The full spec includes support for additional metadata in the GET and POST requests, state that is managed by an intermediate service, usage in broader environments like OpenGraph Link Previews, as well as multi-action and multi-party functionality.\n\nThis documentation covers the design of the Smart Message spec, as well as implementation details and examples.\n\n## Why Solana Pay?\n\nSolana Pay translates URL-encoded text into executable transactions. This lends itself naturally to messaging.\n\n# Minimal Specification (Solana Pay)\n\nThe Smart Message specification in its simplest form is the [Solana Pay](https://github.com/solana-labs/solana-pay) spec, with support for both Transfer Requests, Transaction Requests, and Sign Message Requests.\n\nWhen sending smart messages, messaging clients will simply append Solana Pay URLs to the message text. Then, when rendering the sent message, clients will extract Solana Pay URLs to render them in usable ways.\n\nThis extract-and-render approach is analogous to how Link Previews are handled in traditional messaging clients.\n\n## Solana Pay: Transfer Request\n\nRead the [Solana Pay Transfer Request](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#specification-transfer-request).\n\nIn the Solana Pay Transfer request, full transaction information is encoded in the url. It is a pure P2P protocol.\n\nThe encoding is as follows:\n\n```\nsolana:\u003crecipient\u003e\n  ?amount=\u003camount\u003e\n  \u0026spl-token=\u003cspl-token\u003e\n  \u0026reference=\u003creference\u003e\n  \u0026label=\u003clabel\u003e\n  \u0026message=\u003cmessage\u003e\n  \u0026memo=\u003cmemo\u003e\n```\n\n### Smart Message: Transfer Request\n\nSmart messages support Transfer Requests, but it is recommended that Transaction Requests be used, even for transfers.\n\nWhen a messaging client detects a string of the above form in a message, it is extracted from the message content and rendered in the following form.\n\n\\*rendering\n\nIn the above rendering, standard libraries are used to translate `amount` and `spl-token` to `displayAmount` and `displaySplToken`, which are the amount divided by the token's decimals, and the human-readable token name, respectively.\n\nThese values are then rendered in the button text as \"Send `\u003cdisplayAmount\u003e` `\u003cdisplaySplToken\u003e`\".\n\nWhen the button is tapped, a standard client library is used to construct the transfer transaction based on the metadata encoded in the URL. This transaction is then passed to the user's wallet's `signTransaction` method.\n\nThe wallet's `signTransaction` then prompts the user to approve or reject the transaction — whether via an RPC call to a local wallet, or embedded in the client app.\n\nOnce signed, the transaction is then submitted to the blockchain.\n\n## Solana Pay: Transaction Request\n\n![solana-pay-transaction-request](https://user-images.githubusercontent.com/3632945/200104128-f1d98665-f676-41a4-a0c6-83c6b65650b9.png)\n\nRead the [Solana Pay Transaction Request spec](https://docs.solanapay.com/spec#specification-transaction-request).\n\nThe Solana Pay transaction request allows for executing arbitrary transactions. This is done by making a pair of GET and POST requests to a URL that returns human-readable information and a transaction ready for signing and execution, respectively.\n\n```\nsolana:\u003curl\u003e\n```\n\nThe full flow for a transaction request is:\n\n1. The client executes a GET to fetch a human-readable text label and image icon for the user to consume, for educational purposes.\n2. The client executes a POST, either initiated by the user or in parallel with the GET, to fetch a base64-encoded transaction.\n3. The user then takes some action to sign the returned transaction, and submits it to the blockchain for execution.\n\nThe schemas for the steps above are described below:\n\n### GET request\n\nThe initial GET request returns\n\n`// GET \u003curl\u003e response`\n\n```json\n{ \"label\": \"\u003clabel\u003e\", \"icon\": \"\u003cicon-url\u003e\" }\n```\n\nwhere `label` is some descriptive text, and `icon` is some image url for display purposes.\n\n### POST request\n\nThe user may then execute a POST to the same url, with the payload and response\n\nPOST \u003curl\u003e payload\n\n```json\n{ \"account\": \"\u003caccount\u003e\" }\n```\n\nPOST \u003curl\u003e response\n\n```json\n{ \"transaction\": \"\u003ctransaction\u003e\" }\n```\n\n### Smart Message: Transaction Request\n\nWhen a messaging client detects a string of the form `solana:\u003curl\u003e` in a message, it is extracted and rendered according to the label and icon provided by the `GET` request.\n\n\\*rendering\n\nThe `label` should be used as the button text. The user may then tap the button to begin execution. On tap, the `POST` request is made, a transaction is returned and passed to the user's wallet's `signTransaction` method.\n\nThe wallet's `signTransaction` then prompts the user to approve or reject the transaction — whether via an RPC call to a local wallet, or embedded in the client experience.\n\nOnce signed, the transaction is then submitted to the blockchain.\n\n## Solana Pay Sign Message Request\n\n### Smart Message: Sign Message Request\n\n## Limitations of the Minimal Specification\n\n# Full Specification\n\nThe full Smart Message Specification extends Solana Pay to include support for:\n\n- Additional metadata, including state\n- State persistence\n- Use in other specifications like OpenGraph Link Previews\n- Multi-action support (e.g. \"vote yes\", \"vote no\")\n- Multi-party support (e.g. group chats)\n\nAdditionally, the full specification includes 3 main system components:\n\n1. The Client — the Dialect app or any other messaging app.\n2. The Transaction Request Service — This is a Solana Pay Transaction Request Service, or any number of them.\n3. [NEW] The State Service — Manages interactions between the Client and Transaction Request Services, while managing state.\n\nThe Client and Transaction Request services are mandatory for any implementation of Smart Messaging or Solana Pay. The State Service is only needed to persist state.\n\nLet's start by extending the metadata returned by Transaction Request services, without preserving any state.\n\n## Metadata\n\nTo provide a better user experience, Smart Message Transaction Request services may optionally extend the metadata provided by simple Solana Pay Transaction request services.\n\n### Metadata for state management\n\nRecall that the minimal Solana Pay Transaction Request service GET routes returns a `label` and `icon`, which in a Smart Message context are the button text and image preview, respectively:\n\nGET \u003curl\u003e response\n\n```json\n{ \"label\": \"\u003clabel\u003e\", \"icon\": \"\u003cicon-url\u003e\" }\n```\n\nHowever, to provide a best user experience, Transaction Request services may return metadata for every combination of `\u003ccomponent\u003e.\u003cstate\u003e.\u003cuser_role\u003e` for the components `label` or `icon`, for smart message state, and for users `sender` \u0026 `recipient`.\n\nSmart Messages support the following states:\n\n- `ready`\n- `invalidated`\n- `executing`\n- `succeeded`\n- `failed`\n\nFor example, a token transfer Transaction Request service may provide the following additional metadata:\n\n```json\n{\n  \"icon\": \"\u003cicon-url\u003e\",\n  \"label\": {\n    \"ready\": {\n      \"sender\": \"Requested 20 SOL\",\n      \"recipient\": \"Send 20 SOL\"\n    },\n    \"executing\": {\n      \"sender\": \"Receiving 20 SOL\",\n      \"recipient\": \"Sender 20 SOL\"\n    },\n    \"succeeded\": {\n      \"sender\": \"Received 20 SOL\",\n      \"recipient\": \"Sent 20 SOL\"\n    },\n    \"failed\": \"Failed\",\n    \"invalidated\": \"No longer valid\"\n  }\n}\n```\n\nNote that at the `component` or `state` level, the value can be either a string or nested JSON, depending on whether differentiation is needed.\n\nThe following defaults are used in the absence of values provided in the above:\n\n```json\n{\n  \"icon\": \"\u003cicon-url\u003e\",\n  \"label\": {\n    \"ready\": {\n      \"sender\": \"Requested\",\n      \"recipient\": \"Execute\"\n    },\n    \"executing\": \"Executing\",\n    \"succeeded\": \"Succeeded\",\n    \"failed\": \"Failed\",\n    \"invalidated\": \"No longer valid\"\n  }\n}\n```\n\n### Metadata for enhanced rendering\n\nBeyond a Solana Pay `label` and `icon`, Smart Messages may also provide the following metadata from the GET route:\n\n```json\n{\n  \"icon\": \"\u003cicon-url\u003e\",\n  \"label\": \"\u003clabel\u003e\",\n  \"title\": \"\u003ctitle\u003e\",\n  \"description\": \"\u003cdescription\u003e\"\n}\n```\n\nThe values `title` and `description` allow for Link Preview-like smart messages, which provide more content for the user. In this form, the `label` is still used as the text for the button, which is now placed beside or below the title and description. The `icon` serves as the preview image.\n\n### Transaction Request Service state\n\nTransaction Request services may optionally return a `state`, of value `ready` or `invalidated`, since both of these are possible states before any user action.\n\nFor example, if Alice receives a buyout offer smart message from Bob on an NFT, but circumstances change such that she either no longer owns the NFT, or Bob cancels the offer, the smart message metadata should reflect that the \"accept offer\" action is no longer valid.\n\n```json\n{\n  \"icon\": \"\u003cicon-url\u003e\",\n  \"label\": \"\u003clabel\u003e\",\n  \"state\": \"invalidated\"\n}\n```\n\nAdditionally, the same validation may be performed in the Transaction Request POST route, which can also return `ready` or `invalidated`.\n\nNo `state` provided at all is equivalent to `ready`.\n\nDevelopers building Transaction Request services should take care to implement validation only if it is needed for the end user experience, since this involves additional computational overhead on the GET and POST routes.\n\nTransaction Request services will never return `executing`, `succeeded`, or `failed`, as these states are specific to the smart message user interaction flow, and are handled by the state service.\n\n## State persistence\n\nSmart Messages are not ephemeral. They persist in the chat long after their use. Solana Pay is deliberately stateless, but persisting smart message state can help with usability and user experience.\n\nIn this section, we introduce the State Service, and describe how it handles requests between the Client and Transaction Request services.\n\nThe State Service supports the following routes:\n\n1. A GET route that forwards the request to the underlying Transaction Request API GET route.\n2. A POST route that forwards the request to the underlying Transaction Request API POST route.\n3. A PUT route for submitting transactions to the blockchain (to monitor for completion and update to `succeeded` or `failed`).\n4. [FUTURE] A PUT route for updating smart message state by its UUID. This route is not supported in the current Smart Message implementation.\n\n![smart-messaging-diagrams-1](https://user-images.githubusercontent.com/3632945/200104076-dd4d27db-b168-48fb-abb2-79db6dd832b8.png)\n\n### UUID Parameter\n\nTo persist data, we first need some kind of reference id. Clients who wish to persist smart message state must generate a UUID and append it as a query parameter in the url in solana:\u003curl\u003e.\n\n```\nsolana:\u003curl\u003e\u0026uuid=\u003cgenerated-uuid\u003e\n```\n\nBy using a UUID encoded in the url itself, smart message state is decoupled from any messaging-related state.\n\nThe URL still specifies the URL for the Transaction Request API. If a UUID is present in this URL, the client will interpret this as an intent to pass calls through the State Service to manage state.\n\nThis UUID parameter is then used across the smart messaging system for tracking smart message state.\n\n### Routing Transaction Request GETs \u0026 POSTs\n\n**GET**\n\nClients that detect a UUID in the URL will pass GET calls through the State Service.\n\n```\n// Client\nGET \u003cstate-service-url\u003e?url=\u003curl\u003e\n```\n\nThe request is then forwarded to the Transaction Request at the provided solana `url`.\n\n```\n// State service\nGET \u003curl\u003e\n```\n\nThis Transaction Request service may then return metadata as described above.\n\nIf no record exists with id `uuid`, a new record is created with all of the returned metadata.\n\nThe state service then returns the metadata to the client.\n\n**POST**\n\nPost requests are similarly passed through the state service using the UUID in the url, and as described for GET, may create new records based on the UUID, and may update state from `ready` to `invalidated` during validation.\n\n```\n// Client\nPOST \u003cstate-service-url\u003e?url=\u003curl\u003e\n```\n\n```\n// State service\nPOST \u003curl\u003e\n```\n\nThis route then returns the result of the POST request back to the client.\n\n### Tracking transaction state\n\nTransaction Request service POST requests return a transaction ready for signing by the client.\n\nOnce signed by the user, the transaction can be submitted back to the State Service for submission to the blockchain via a PUT request\n\nClient PUT \u003cstate-service-url\u003e\n\n```json\n{ \"transaction\": \"\u003csigned-transaction\u003e\" }\n```\n\nThe State service then manages submitting the signed transaction to the blockchain, updating smart message state to `executing`, monitoring transaction finality, and then settling state to either `succeeded` or `failed`.\n\n**Terminal states**\n\nMessage states `ready` and `executing` are non-terminal states, in that they can further transition to other states. States `succeeded`, `failed`, and `invalidated` are terminal. This will be important later.\n\n## Smart Message usage in OpenGraph Link Previews\n\n![6 0](https://user-images.githubusercontent.com/3632945/200104889-b17dc897-0384-4263-9617-b9619cf8f89f.png)\n\nDevelopers may add an additional `dialect` meta tag in the `\u003chead\u003e` section of their sites. Any Dialect compatible clients will then render a button on the link preview according to the smart message.\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003e...\u003c/title\u003e\n    \u003cmeta property=\"og:title\" content=\"...\" /\u003e\n    \u003cmeta property=\"og:description\" content=\"...\" /\u003e\n    \u003cmeta property=\"dialect\" content=\"solana:\u003curl\u003e\" /\u003e\n    ...\n  \u003c/head\u003e\n  ...\n\u003c/html\u003e\n```\n\n## Multi-Action Smart Messages\n\nFuture versions of the Smart Message specification will support multiple actions, such as vote yes or vote no on a DAO proposal or multisig.\n\n![5 0](https://user-images.githubusercontent.com/3632945/200104298-1b8a7562-dba1-4a1f-ba1b-8c240a254712.png)\n\n## Multi-Party Smart Messages\n\nSmart Message support for multi-party environments, such as group chat, will be coming in a future version of the specification.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdialectlabs%2Fsmart-messaging","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdialectlabs%2Fsmart-messaging","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdialectlabs%2Fsmart-messaging/lists"}