{"id":13392949,"url":"https://github.com/an-lee/mixin_bot","last_synced_at":"2026-05-24T08:03:07.167Z","repository":{"id":51563322,"uuid":"148621169","full_name":"an-lee/mixin_bot","owner":"an-lee","description":"A simple API wrapper for Mixin Network in Ruby","archived":false,"fork":false,"pushed_at":"2026-05-23T22:18:29.000Z","size":6786,"stargazers_count":16,"open_issues_count":2,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-23T22:23:41.454Z","etag":null,"topics":["api-wrapper","gem","mixin","mixin-sdk-ruby","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/an-lee.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"MIT-LICENSE","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-09-13T10:20:34.000Z","updated_at":"2026-05-23T22:16:24.000Z","dependencies_parsed_at":"2024-01-13T17:11:12.974Z","dependency_job_id":"2eaec278-a646-4c41-af3b-7b48a92e2ac4","html_url":"https://github.com/an-lee/mixin_bot","commit_stats":{"total_commits":281,"total_committers":5,"mean_commits":56.2,"dds":0.06761565836298933,"last_synced_commit":"7ef783415b35ab3ecbac365f02ef866999c648ea"},"previous_names":[],"tags_count":73,"template":false,"template_full_name":null,"purl":"pkg:github/an-lee/mixin_bot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/an-lee%2Fmixin_bot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/an-lee%2Fmixin_bot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/an-lee%2Fmixin_bot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/an-lee%2Fmixin_bot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/an-lee","download_url":"https://codeload.github.com/an-lee/mixin_bot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/an-lee%2Fmixin_bot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33426013,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T22:14:44.296Z","status":"online","status_checked_at":"2026-05-24T02:00:06.296Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["api-wrapper","gem","mixin","mixin-sdk-ruby","rails","ruby"],"created_at":"2024-07-30T17:00:39.915Z","updated_at":"2026-05-24T08:03:07.155Z","avatar_url":"https://github.com/an-lee.png","language":"Ruby","funding_links":[],"categories":["SDK"],"sub_categories":["3rd-party SDK"],"readme":"# MixinBot\n\n[![CI](https://github.com/an-lee/mixin_bot/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/an-lee/mixin_bot/actions/workflows/ci.yml)\n\nRuby SDK and CLI for [Mixin Network](https://developers.mixin.one/docs): authenticated REST calls, **Safe** UTXO transfers, Blaze messaging, network asset catalog, inscriptions, invoices and mix addresses, transaction encoding, and optional **MVM** (Mixin Virtual Machine) helpers.\n\nThe gem aims for **parity with the official [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client)** Go SDK and **[bot-api-nodejs-client](https://github.com/MixinNetwork/bot-api-nodejs-client)** Node SDK. See [API_COVERAGE.md](API_COVERAGE.md) for the full mapping; run `rake mixin_bot:api_coverage` to confirm no gaps are marked missing.\n\nCurrent gem version: **2.2.1** (see [CHANGELOG.md](CHANGELOG.md) for breaking changes and deprecations).\n\n## Requirements\n\n- **Ruby** ≥ 3.2 (CI runs 3.2, 3.3, and 4.0).\n- **Bundler** 2.5+ recommended, especially on Ruby 4.\n- Optional: the **`mixin`** CLI in `PATH` if you use `MixinBot::API#encode_raw_transaction_native` / `#decode_raw_transaction_native` or the experimental `MixinBot::NodeCLI` helpers.\n\n## Installation\n\nAdd to your Gemfile:\n\n```ruby\ngem 'mixin_bot'\n```\n\nThen:\n\n```bash\nbundle install\n```\n\nOr install the gem directly:\n\n```bash\ngem install mixin_bot\n```\n\nThe gem ships the `mixinbot` executable (see [CLI](#cli)).\n\n## Quick start\n\n### 1. Configure credentials\n\nSet the fields your flows need. **Safe** transfers and signing require a **spend key** (`spend_key`) in addition to session material.\n\n```ruby\nrequire 'mixin_bot'\n\nMixinBot.configure do\n  self.app_id = 'your-app-uuid'\n  self.client_secret = 'your-client-secret' # OAuth flows\n  self.session_id = 'your-session-uuid'\n  self.session_private_key = '...' # seed or full Ed25519 private key; Base64 or hex\n  self.server_public_key = '...'   # pin token / server public key\n  self.spend_key = '...'           # Ed25519 spend private key for Safe UTXO signing\n  # self.pin = self.spend_key        # optional; used where PIN material is required\n  # self.api_host = 'api.mixin.one'\n  # self.blaze_host = 'blaze.mixin.one'\n  # self.debug = true               # Faraday response logging\nend\n```\n\n`MixinBot::Configuration` accepts common aliases: `client_id` → `app_id`, `private_key` → `session_private_key`, `pin_token` → `server_public_key`. Keys are normalized (e.g. 32-byte Ed25519 seeds expanded to 64-byte signing keys) where appropriate.\n\n### 2. Call the API\n\n```ruby\napi = MixinBot.api # singleton using global config, or MixinBot::API.new(...)\n\napi.me['full_name']\napi.assets\napi.network_asset('c6d0c728-2624-429b-8e0d-d9d19b6592fa') # public network catalog\napi.fetch_user_sessions(['user-uuid'])\n```\n\n### 3. Send assets (Safe API, recommended)\n\n`create_transfer` with keyword arguments runs the **Safe** pipeline (UTXO select → build → verify → sign → submit). You can also call `create_safe_transfer` explicitly. Configure **`spend_key`** first.\n\n```ruby\nresult = MixinBot.api.create_transfer(\n  members: '6ae1c7ae-1df1-498e-8f21-d48cb6d129b5',\n  asset_id: '965e5c6e-434c-3fa9-b780-c50f43cd955c',\n  amount: '0.01',\n  memo: 'payment',\n  trace_id: SecureRandom.uuid\n)\n\n# Multisig: 2-of-3\nMixinBot.api.create_safe_transfer(\n  members: %w[uuid-1 uuid-2 uuid-3],\n  threshold: 2,\n  asset_id: '965e5c6e-434c-3fa9-b780-c50f43cd955c',\n  amount: '0.01'\n)\n```\n\nLower-level steps: `build_utxos`, `build_safe_transaction`, `verify_raw_transaction` / `create_safe_transaction_request`, `sign_safe_transaction`, `send_safe_transaction` (batch via `requests:`).\n\nAliases aligned with the Go SDK include `send_transaction`, `send_transfer_transaction`, and `get_transaction_by_id` (→ `safe_transaction`).\n\n### 4. Legacy `POST /transfers`\n\nIf the first argument is a **PIN string** and you pass `opponent_id:` (old Messenger transfer shape), `create_transfer` delegates to **`create_legacy_transfer`** (deprecated, warns once). Prefer Safe transfers for new code.\n\n```ruby\n# Legacy only — deprecated\nMixinBot.api.create_legacy_transfer(\n  pin,\n  asset_id: '...',\n  opponent_id: '...',\n  amount: 0.00000001,\n  trace_id: SecureRandom.uuid\n)\n```\n\n## HTTP responses (`ApiEnvelope`)\n\n`MixinBot::Client` returns **`MixinBot::Models::ApiEnvelope`** for REST calls. It wraps the raw JSON so you can use either envelope or flattened shapes:\n\n```ruby\nres = MixinBot.api.client.get('/me')\nres['data']['user_id'] # envelope\nres['user_id']          # delegated lookup into `data` when present\nres.to_h                # raw Hash\n```\n\nMany convenience methods on `MixinBot::API` still return the **inner `data` hash** where that was the historical contract (e.g. `#me`).\n\n## Library layout\n\n### `MixinBot::API` modules\n\n`MixinBot::API` composes one module per API area (all methods are available on `MixinBot.api`):\n\n| Module | Examples |\n|--------|----------|\n| **Me** | `me`, `safe_me`, `update_me`, `friends`, `update_preferences`, `relationship` |\n| **User** | `user`, `fetch_users`, `search_user`, `create_user`, `safe_register`, `migrate_to_safe` |\n| **Session** | `fetch_user_sessions` |\n| **LegacyUser** | `upgrade_legacy_user` |\n| **Asset** | `assets`, `asset`, `ticker`, `fetch_assets`, `asset_fee`, `asset_balance` |\n| **NetworkAsset** | `network_asset`, `network_ticker`, `network_asset_search` |\n| **Network** | `network_assets`, `network_assets_top` |\n| **Fiat** | `fiats` |\n| **Chain** | `network_chain`, `network_chains`, `chain_name`, `chain_id?` |\n| **Transfer** | `create_transfer`, `create_safe_transfer`, `build_utxos`, `send_transaction`, … |\n| **Transaction** | `create_safe_keys`, `build_safe_transaction`, `verify_raw_transaction`, `sign_safe_transaction`, `build_object_transaction`, `create_object_storage_transaction`, … |\n| **Output** | `safe_outputs`, `safe_output`, `build_threshold_script` |\n| **Deposit** | `pending_safe_deposits` |\n| **Address** (deposit entries) | `safe_deposit_entries` |\n| **Snapshot** | `safe_snapshots`, `safe_snapshot`, `create_safe_snapshot_notification` |\n| **LegacySnapshot** | `snapshots`, `snapshot`, `snapshot_by_trace_id`, `network_snapshots`, … |\n| **Payment** | `safe_pay_url` |\n| **LegacyPayment** | `pay_url`, `verify_payment` (deprecated) |\n| **Multisig** | `create_safe_multisig_request`, `safe_multisig_request`, `create_multisig_raw_tx` |\n| **LegacyMultisig** | `create_multisig_request`, `cancel_multisig_request`, … |\n| **LegacyOutput** | `legacy_outputs`, `read_multisigs`, `create_output`, … |\n| **LegacyTransfer** | `create_legacy_transfer`, `legacy_transfer` |\n| **LegacyTransaction** | `build_raw_transaction`, `create_multisig_transaction`, … |\n| **Inscription** | `collection`, `collectible`, `build_inscribe_transaction`, … |\n| **LegacyCollectible** | legacy collectible requests (deprecated) |\n| **Withdraw** | `withdrawals`, `create_withdraw_address`, `check_address`, `withdraw_addresses` |\n| **Conversation** | `conversation`, `create_group_conversation`, `join_conversation`, … |\n| **Message** | `send_message`, `send_plain_messages`, Blaze helpers |\n| **EncryptedMessage** | `send_encrypted_*`, `encrypt_message`, `decrypt_message` |\n| **Blaze** | `blaze`, `start_blaze_connect`, `blaze_send_plain_text`, … |\n| **Attachment** | `create_attachment`, `upload_attachment` |\n| **Auth** | `oauth_token`, `authorize_code`, `access_token`, `sign_oauth_access_token` |\n| **Pin** / **Tip** | `verify_pin`, `update_pin`, `update_tip_pin`, `encrypt_tip_pin`, `get_tip_node`, `tip_body_for_*` |\n| **App** | `favorite_apps`, `transfer_app_ownership` (`migrate`) |\n| **Code** | `read_code`, `read_multisig_by_code` |\n| **Turn** | `turn_servers` |\n| **Rpc** | `rpc_proxy`, `send_raw_transaction`, `get_transaction`, … |\n| **ComputerApi** | delegates to `MixinBot::Computer` (`get_computer_info`, `register_computer`, …) |\n\nTop-level helpers on **`MixinBot::API`**: `access_token`, `encode_raw_transaction`, `decode_raw_transaction`, native variants via `mixin` CLI.\n\n### Other libraries\n\n| Area | Description |\n|------|-------------|\n| **`MixinBot::Client`** | Faraday HTTP client (JSON, retries, optional debug). |\n| **`MixinBot::Configuration`** | Credentials and hosts. |\n| **`MixinBot::Utils`** | Crypto, JWT, encoding, `unique_object_id`, `generate_user_checksum`, … |\n| **`MixinBot::Transaction`** | Encode/decode raw Safe transactions. |\n| **`MixinBot::MixAddress`** | Parse/build `MIX…` addresses; `request_or_generate_ghost_keys`. |\n| **`MixinBot::Invoice`** | `MIN…` payment invoices. |\n| **`MixinBot::UrlScheme`** | `mixin://` deep links (`scheme_users`, `scheme_pay`, …). |\n| **`MixinBot::Computer`** | [Mixin Computer](https://computer.mixin.one) HTTP API (separate host). |\n| **`MixinBot::BotAuth`** | Sign bot-platform requests (`BotAuth::Client#sign_request`). |\n| **`MixinBot::Monitor`** | YAML monitor messages, `report_to_monitor`, `check_retryable_error`. |\n| **`MixinBot::UUID`**, **`MixinBot::Nfo`** | UUID and NFT memo helpers. |\n| **`MVM`** | Optional MVM namespace: `MVM::Bridge`, `MVM::Nft`, `MVM::Scan`, `MVM::Registry`. |\n\n### Errors\n\nCustom errors under `MixinBot::` include `ResponseError`, `UnauthorizedError`, `InsufficientBalanceError`, `UtxoInsufficientError`, `PinError`, and `InvalidInvoiceFormatError`. See `lib/mixin_bot/errors.rb`.\n\n### Multiple bots\n\n```ruby\nbot_a = MixinBot::API.new(\n  app_id: '...',\n  session_id: '...',\n  session_private_key: '...',\n  server_public_key: '...',\n  spend_key: '...'\n)\n\nbot_b = MixinBot::API.new(...) # separate configuration\n\nbot_a.me\nbot_b.me\n```\n\n## Blaze (WebSocket)\n\nBlaze uses a WebSocket after JWT auth. `start_blaze_connect` yields a `Faye::WebSocket::Client`; define `on_open` / `on_message` / `on_error` / `on_close` on the receiver (see `examples/blaze.rb`). Run an event loop (e.g. **EventMachine**) in your app.\n\n```ruby\nrequire 'eventmachine'\nrequire 'mixin_bot'\n\nEM.run do\n  MixinBot.api.start_blaze_connect do\n    def on_open(blaze, _event)\n      blaze.send list_pending_message\n    end\n\n    def on_message(blaze, event)\n      raw = JSON.parse ws_message(event.data)\n      blaze.send acknowledge_message_receipt(raw['data']['message_id']) if raw.dig('data', 'message_id')\n    end\n  end\nend\n```\n\nFor outbound messages over an open socket, use `blaze_send_plain_text`, `blaze_send_contact`, `blaze_send_app_card`, and related helpers (parity with Go `BlazeClient`).\n\n## Deep links and bot auth\n\n```ruby\nMixinBot::UrlScheme.scheme_pay(\n  asset_id: '...',\n  trace_id: SecureRandom.uuid,\n  recipient_id: '...',\n  memo: 'hello',\n  amount: '0.01'\n)\n\nclient = MixinBot::BotAuth.new_client(MixinBot.api)\ntoken = client.sign_request(Time.now.to_i, bot_user_id, 'GET', '/some/path')\n```\n\n## CLI\n\nInvoke **`mixinbot`** (global options: `-a` / `--apihost`, `-o` / `--output pretty|json|yaml`, `-r` / `--pretty`).\n\nWhen stdout is piped, output defaults to JSON. Use `mixinbot schema -o json` for machine-readable command discovery. See [docs/agent/cli.md](docs/agent/cli.md).\n\nSubcommands that talk to the API accept **`-k`** / **`--keystore`**: path to a JSON file **or** inline JSON (`app_id`, `session_id`, `session_private_key`, `server_public_key`, `spend_key`, `client_secret`, `pin`, etc.). Without `-k`, the CLI uses global `MixinBot.configure` credentials.\n\n| Command | Purpose |\n|---------|---------|\n| `mixinbot call METHOD` | Invoke any `MixinBot::API` method (`-d` JSON kwargs, optional positional args). |\n| `mixinbot list [FILTER]` | List callable API methods (grouped by module). |\n| `mixinbot api PATH` | Signed `GET`/`POST` via `MixinBot::Client` (`-m`, `-d`, `-p`, `-t`). |\n| `mixinbot transfer USER_ID` | Safe transfer (`create_safe_transfer`; `--asset`, `--amount`, …). |\n| `mixinbot legacy-transfer USER_ID` | Deprecated `POST /transfers`. |\n| `mixinbot safetransfer USER_ID` | Alias for `transfer` (deprecated name). |\n| `mixinbot authcode` | OAuth authorize code (`-c`, `-s`). |\n| `mixinbot encrypt PIN` / `verifypin` / `updatetip` | PIN/TIP helpers. |\n| `mixinbot saferegister` | Safe registration (`--spend_key`). |\n| `mixinbot pay` | Safe payment URL. |\n| `mixinbot utils call METHOD` | Invoke any `MixinBot.utils` method (`-d` JSON kwargs). |\n| `mixinbot utils list [FILTER]` | List utils methods. |\n| `mixinbot unique UUID …` | Deterministic UUID. |\n| `mixinbot generatetrace HASH` | Trace UUID from tx hash. |\n| `mixinbot decodetx HEX` | Decode raw transaction. |\n| `mixinbot nftmemo` | NFT mint memo. |\n| `mixinbot rsa` / `ed25519` | Key generation. |\n| `mixinbot version` | Gem version. |\n\nExamples:\n\n```bash\nmixinbot call me -k ~/.mixinbot/keystore.json\nmixinbot call safe_outputs -k keystore.json -d '{\"asset\":\"965e5c6e-434c-3fa9-b780-c50f43cd955c\",\"state\":\"unspent\"}'\nmixinbot transfer USER_ID -k keystore.json --asset ASSET_ID --amount 0.01\n```\n\nRun `mixinbot help` and `mixinbot help COMMAND` for details.\n\n## For AI agents / LLMs\n\n- **[llms.txt](llms.txt)** — curated documentation index ([llmstxt.org](https://llmstxt.org/) format)\n- **[AGENTS.md](AGENTS.md)** — repository layout, conventions, and workflows for coding agents\n- **[docs/agent/cli.md](docs/agent/cli.md)** — structured `mixinbot` output, schema introspection, JSON examples\n- **[docs/agent/cookbook.md](docs/agent/cookbook.md)** — task recipes (transfers, auth, messaging)\n\nRun `mixinbot schema -o json` to discover CLI commands programmatically.\n\n## Documentation\n\n- **API coverage** — [API_COVERAGE.md](API_COVERAGE.md) vs [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client).\n- **RDoc** — `rake rdoc` → `doc/index.html`.\n- **Online** — [RubyDoc.info](https://www.rubydoc.info/gems/mixin_bot).\n- **Changelog** — [CHANGELOG.md](CHANGELOG.md).\n\n## Development \u0026 tests\n\n```bash\ngit clone https://github.com/an-lee/mixin_bot.git\ncd mixin_bot\nbundle install\n```\n\n- **Default suite** (offline WebMock stubs):\n\n  ```bash\n  rake test\n  ```\n\n- **API coverage check**:\n\n  ```bash\n  rake mixin_bot:api_coverage\n  ```\n\n- **RuboCop** — `rake` runs tests + RuboCop.\n\n- **Live API** (optional):\n\n  ```bash\n  cp test/config.yml.example test/config.yml\n  LIVE=1 rake test\n  # or: rake test_live\n  ```\n\nExamples under `examples/` expect `examples/config.yml` (copy from `examples/config.yml.example`).\n\n### CI\n\nGitHub Actions runs on every pull request and on pushes to `main`:\n\n- **Test** — Ruby 3.2, 3.3, and 4.0: `bundle exec rake test`\n- **RuboCop** — Ruby 3.3: `bundle exec rake rubocop`\n- **API coverage** — `bundle exec rake mixin_bot:api_coverage`\n\n### Release\n\nPublishing to [RubyGems.org](https://rubygems.org/gems/mixin_bot) is automated when a version tag is pushed:\n\n1. Bump `MixinBot::VERSION` in `lib/mixin_bot/version.rb` and update `CHANGELOG.md`.\n2. Commit and push to `main`.\n3. Create and push a tag matching the gem version (e.g. `v2.1.0` for version `2.1.0`):\n\n   ```bash\n   git tag v2.1.0\n   git push origin v2.1.0\n   ```\n\nThe [Release workflow](.github/workflows/release.yml) builds the gem, publishes to RubyGems.org via [trusted publishing](https://guides.rubygems.org/trusted-publishing/) (GitHub OIDC; trusted publisher for workflow `release.yml` on `an-lee/mixin_bot`), and creates a GitHub Release with notes from `CHANGELOG.md` and the `.gem` attached. To build without publishing, run the Release workflow manually with **dry run** enabled.\n\n## References\n\n- [Mixin developers documentation](https://developers.mixin.one/docs)\n- [Mixin API overview](https://developers.mixin.one/api)\n- [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client) (official Go SDK; parity reference)\n- [mixin_client_demo (Python)](https://github.com/myrual/mixin_client_demo)\n- [mixin-node (Node.js)](https://github.com/virushuo/mixin-node)\n\n## License\n\nMIT — see [MIT-LICENSE](MIT-LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fan-lee%2Fmixin_bot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fan-lee%2Fmixin_bot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fan-lee%2Fmixin_bot/lists"}