{"id":20272310,"url":"https://github.com/steemit/steem-uri-spec","last_synced_at":"2025-10-25T01:05:18.885Z","repository":{"id":72308941,"uuid":"135360867","full_name":"steemit/steem-uri-spec","owner":"steemit","description":"steem:// signing spec and reference implementation","archived":false,"fork":false,"pushed_at":"2018-06-05T10:20:46.000Z","size":19,"stargazers_count":14,"open_issues_count":2,"forks_count":13,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-03-25T02:51:13.654Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/steemit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-05-29T22:54:59.000Z","updated_at":"2023-02-15T17:50:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"a72eccc9-93fb-44a7-91df-b7c71babb482","html_url":"https://github.com/steemit/steem-uri-spec","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/steemit%2Fsteem-uri-spec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steemit%2Fsteem-uri-spec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steemit%2Fsteem-uri-spec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steemit%2Fsteem-uri-spec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/steemit","download_url":"https://codeload.github.com/steemit/steem-uri-spec/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248345281,"owners_count":21088242,"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":"2024-11-14T12:42:48.386Z","updated_at":"2025-10-25T01:05:18.875Z","avatar_url":"https://github.com/steemit.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\nSteem URI protocol\n==================\n\nProtocol facilitating signing of steem transactions. Meant to be implemented by secure Steem wallet applications.\n\nThis repository contains both the specification and a zero dependency reference implementation that works in node.js and most browsers.\n\n\nInstallation\n------------\n\nVia npm or yarn:\n\n```\nnpm install steem-uri\nyarn add steem-uri\n```\n\nManually: clone the repository and run `make`, this will place the built lib in `lib/index.js`.\n\n\nExample usage\n-------------\n\nEncoding operations:\n\n```js\nconst steemuri = require('steem-uri')\n\nsteemuri.encodeOp(['vote', {voter: 'foo', author: 'bar', permlink: 'baz', weight: 10000}])\n// steem://sign/op/WyJ2b3RlIix7InZvdGVyIjoiZm9vIiwiYXV0aG9yIjoiYmFyIiwicGVybWxpbmsiOiJiYXoiLCJ3ZWlnaHQiOjEwMDAwfV0.\n\nsteemuri.encodeOps([\n    ['vote', {voter: 'foo', author: 'bar', permlink: 'baz', weight: 10000}],\n    ['transfer', {from: 'foo', to: 'bar', amount: '10.000 STEEM', memo: 'baz'}]\n], {callback: 'https://example.com/wallet?tx={{id}}'})\n// steem://sign/ops/W1sidm90ZSIseyJ2b3RlciI6ImZvbyIsImF1dGhvciI6ImJhciIsInBlcm1saW5rIjoiYmF6Iiwid2VpZ2h0IjoxMDAwMH1dLFsidHJhbnNmZXIiLHsiZnJvbSI6ImZvbyIsInRvIjoiYmFyIiwiYW1vdW50IjoiMTAuMDAwIFNURUVNIiwibWVtbyI6ImJheiJ9XV0.?cb=aHR0cHM6Ly9leGFtcGxlLmNvbS93YWxsZXQ_dHg9e3tpZH19\n```\n\nDecoding and resolving steem:// links (for wallet implementers):\n\n```js\nconst steemuri = require('steem-uri')\n\n// parse the steem:// link\nconst parsed = steemuri.decode(link)\n\n// resolve the decoded tx and params to a signable tx\nlet {tx, signer} = steemuri.resolveTransaction(parsed.tx, parsed.params, {\n    // e.g. from a get_dynamic_global_properties call\n    ref_block_num: 1234,\n    ref_block_prefix: 5678900,\n    expiration: '2020-01-01T00:00:00',\n    // accounts we are able to sign for\n    signers: ['foo', 'bar'],\n    // selected signer if none is asked for by the params\n    preferred_signer: 'foo',\n})\n\n// sign broadcast the transaction to the network\nlet signature = signTx(tx, myKeys[signer])\nlet confirmation\nif (!parsed.params.no_broadcast) {\n    tx.signatures = [signature]\n    confirmation = broadcastTx(tx)\n}\n\n// redirect to the callback if set\nif (parsed.params.callback) {\n    let url = steemuri.resolveCallback(parsed.params.callback, {\n        sig: signature,\n        id: confirmation.id,\n        block: confirmation.block_num,\n        txn: confirmation.txn_num,\n    })\n    redirectTo(url)\n}\n```\n\n---\n\nSpecification\n=============\n\nA protocol that allows Steem transactions and operations to be encoded into links that can be shared across applications and devices to sign transactions without implementers having to reveal their private key.\n\n\nActions\n-------\n\n  * `steem://sign/tx/\u003cbase64u(JSON-encoded tx)\u003e`\n    Sign an arbitrary transaction.\n  * `steem://sign/op/\u003cbase64u(JSON-encoded op)\u003e`\n    As above but constructs a transaction around the operation before signing.\n  * `steem://sign/ops/\u003cbase64u(JSON-encoded op array)\u003e`\n    As above but allows multiple operations as an array.\n  * `steem://sign/\u003coperation_name\u003e[/operation_params..]`\n    Action aliases, see the \"Specialized actions\" section for more info.\n\nTo facilitate re-usable signing URIs the implementation allows for a set of placeholder variables that can be used in a signing payload.\n\n  * `__signer` - Replaced with the username of the signer\n  * `__expiration` - Replaced with current time plus some padding to allow the transaction to be broadcast*\n  * `__ref_block_num` - Reference block number*\n  * `__ref_block_prefix` - Reference block id*\n\n*Reasonable values are up to the implementer, suggested expiry time is 60 seconds ahead of rpc node time and the reference block set to the current head block.\n\n\n\nParameters\n----------\n\nParams are global to all actions and encoded as query string params.\n\n  * `s` (signer) - Preferred signer, if the implementer has multiple signing keys available it should pre-fill the correct authority. If the implementer does not have a key for the preferred signer a warning should be shown to the user. If omitted the implementer may auto select a signer based on the transaction parameters.\n\n  * `nb` (no_broadcast) - If set the implementer should only sign the transaction and pass the signature back in the callback.\n\n  * `cb` (callback) - Base64u encoded url that will be redirected to when the transaction has been signed. The url also allows some templating, see the callback section below for more info.\n\nParams uses short names to save space in encoded URIs.\n\n\nCallbacks\n---------\n\nCallbacks should be redirected to once the transaction has been accepted by the network. If the callback url is a web link only `https` should be allowed.\n\nThe callbacks also allow simple templating with some response parameters, the templating format is `{{\u003cparam_name\u003e}}`, e.g. `https://myapp.com/wallet?tx={{id}}\u0026included_in={{block}}` or `mymobileapp://signed/{{sig}}`\n\nCallback template params:\n\n  * `sig` - Hex-encoded string containing the 65-byte transaction signature\n  * `id` - Hex-encoded string containing the 20-byte transaction hash*\n  * `block` - The block number the transaction was included in*\n  * `txn` - The block transaction index*\n\n*Will not be available if the `nb` param was set for the action.\n\n\n\nBase64u\n-------\n\nAn URL-safe version base64 where `+` is replaced by `-`, `/` by `_` and the `=` padding by `.`.\n\n```\nbase64\nSGn+dGhlcmUh/k5pY2X+b2b+eW91/nRv/mRlY29kZf5tZf46KQ==\nbase64u\nSGn-dGhlcmUh_k5pY2X-b2b-eW91_nRv_mRlY29kZf5tZf46KQ..\n```\n\nJavaScript implementation:\n\n```js\nb64u_lookup = {'/': '_', '_': '/', '+': '-', '-': '+', '=': '.', '.': '='}\nb64u_enc = (str) =\u003e btoa(str).replace(/(\\+|\\/|=)/g, (m) =\u003e b64u_lookup[m])\nb64u_dec = (str) =\u003e atob(str.replace(/(-|_|\\.)/g, (m) =\u003e b64u_lookup[m]))\n```\n\n\nSpecialized actions\n-------------------\n\nTo keep the length of the URIs short, and the QR code size manageable, some common operations have aliases. See list below for supported aliases, params noted in operation payloads using the format `{{param_name}}` and optional (`[param_name]`) params should be filled with empty strings unless otherwise specified:\n\n### Transfer tokens\n\nAction: `steem://sign/transfer/\u003cusername\u003e/\u003camount\u003e[/memo]`\n\nParams:\n\n  * `username` - User that should be followed by `__signer`\n  * `amount` - Amount to transfer, e.g. `1.000 STEEM`\n  * `memo` - Base64u encoded memo, optional.\n\nOperation:\n\n```json\n[\"transfer\", {\n  \"from\": \"__signer\",\n  \"to\": \"{{username}}\",\n  \"amount\": \"{{amount}}\",\n  \"memo\": \"{{memo}}\"\n}]\n```\n\n### Follow user\n\nAction: `steem://sign/follow/\u003cusername\u003e`\n\nParams:\n\n  * `\u003cusername\u003e` - User that should be followed by `__signer`.\n\nOperation:\n\n```json\n[\"custom_json\", {\n  \"required_auths\": [],\n  \"required_posting_auths\": [\"__signer\"],\n  \"id\": \"follow\",\n  \"json\": \"[\\\"follow\\\",{\\\"follower\\\":\\\"__signer\\\",\\\"following\\\":\\\"{{username}}\\\",\\\"what\\\":[\\\"blog\\\"]}]\"\n}]\n```\n\n\nExamples\n--------\n\nExample usage of the protocol along with data for every step that can be used in tests. Examples assumes a `__signer` of `foo` a `__expiration` of `1970-01-01T00:00:00` and `__ref_block_num`, `__ref_block_prefix` set to `0` unless otherwise stated.\n\n### Send a limit order\n\nMight be requested from a trading app, here we don't use any templating since we never want the transaction to be reusable.\n\nTransaction:\n\n```json\n{\n  \"ref_block_num\": 48872,\n  \"ref_block_prefix\": 1543858519,\n  \"expiration\": \"2018-05-29T13:17:39\",\n  \"extensions\": [],\n  \"operations\": [\n    [\"limit_order_create2\", {\n      \"owner\": \"foo\",\n      \"orderid\": 1,\n      \"amount_to_sell\": \"10.000 STEEM\",\n      \"fill_or_kill\": false,\n      \"exchange_rate\": {\"base\": \"1.000 STEEM\", \"quote\": \"0.420 SBD\"},\n      \"expiration\": \"2018-05-30T00:00:00\"\n    }]\n  ]\n}\n```\n\nParameters:\n\n```json\n{\n  \"signer\": \"foo\",\n  \"callback\": \"https://steem.trader/sign_callback?id={{id}}\"\n}\n```\n\nEncoded:\n\n```\nsteem://sign/tx/eyJyZWZfYmxvY2tfbnVtIjo0ODg3MiwicmVmX2Jsb2NrX3ByZWZpeCI6MTU0Mzg1ODUxOSwiZXhwaXJhdGlvbiI6IjIwMTgtMDUtMjlUMTM6MTc6MzkiLCJleHRlbnNpb25zIjpbXSwib3BlcmF0aW9ucyI6W1sibGltaXRfb3JkZXJfY3JlYXRlMiIseyJvd25lciI6ImZvbyIsIm9yZGVyaWQiOjEsImFtb3VudF90b19zZWxsIjoiMTAuMDAwIFNURUVNIiwiZmlsbF9vcl9raWxsIjpmYWxzZSwiZXhjaGFuZ2VfcmF0ZSI6eyJiYXNlIjoiMS4wMDAgU1RFRU0iLCJxdW90ZSI6IjAuNDIwIFNCRCJ9LCJleHBpcmF0aW9uIjoiMjAxOC0wNS0zMFQwMDowMDowMCJ9XV19?s=foo\u0026cb=aHR0cHM6Ly9zdGVlbS50cmFkZXIvc2lnbl9jYWxsYmFjaz9pZD17e2lkfX0.\n```\n\n### Witness vote\n\nReusable witness vote URI, e.g. for a \"Vote for me!\" QR code t-shirt.\n\nOperation:\n\n```json\n[\"account_witness_vote\", {\n  \"account\": \"__signer\",\n  \"witness\": \"jesta\",\n  \"approve\": true\n}]\n```\n\nEncoded:\n\n```\nsteem://sign/op/WyJhY2NvdW50X3dpdG5lc3Nfdm90ZSIseyJhY2NvdW50IjoiX19zaWduZXIiLCJ3aXRuZXNzIjoiamVzdGEiLCJhcHByb3ZlIjp0cnVlfV0.\n```\n\n\n### Multisig\n\nTo sign for an account setup with multiple authorities a central service can act as a transaction facilitator using the `nb` (no_broadcast) option.\n\nIn the following scenario the account `foo` is setup with an active authority that has three account auths belonging to `bob`, `alice` and `picard`, the weights are setup so that two of those three accounts needs to sign.\n\n`bob` wants to transfer `150.000 STEEM` from the `foo` account to himself so he submits an operation to the signing service:\n\n```json\n[\"transfer\", {\n  \"from\": \"foo\",\n  \"to\": \"bob\",\n  \"amount\": \"150.000 STEEM\",\n  \"memo\": \"Bob's boat needs plastic padding\"\n}]\n```\n\nThe service then generates a signing URI with that operation and the following options:\n\n```json\n{\n  \"no_broadcast\": true,\n  \"callback\": \"https://sign.steem.vc/collect?id=123\u0026sig={{sig}}\"\n}\n```\n\n```\nsteem://sign/op/WyJ0cmFuc2ZlciIseyJmcm9tIjoiZm9vIiwidG8iOiJib2IiLCJhbW91bnQiOiIxNTAuMDAwIFNURUVNIiwibWVtbyI6IkJvYidzIGJvYXQgbmVlZHMgcGxhc3RpYyBwYWRkaW5nIn1d?nb=\u0026cb=aHR0cHM6Ly9zaWduLnN0ZWVtLnZjL2NvbGxlY3Q_aWQ9MTIzJnNpZz17e3NpZ319\n```\n\n`bob` then signs the transaction using the URI, the service callback is pinged and the service now has his signature. Then he sends the URI to `alice` and `picard` and when one of them signs it the service has enough signatures it broadcasts the transaction.\n\nThe UX of a service like this can be excellent with the help of QR codes and collecting emails for signers so they can be notified when a signature is needed and when the transaction is broadcast.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteemit%2Fsteem-uri-spec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsteemit%2Fsteem-uri-spec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteemit%2Fsteem-uri-spec/lists"}