mixin_bot 1.4.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +75 -0
  3. data/API_COVERAGE.md +143 -0
  4. data/CHANGELOG.md +112 -0
  5. data/README.md +375 -0
  6. data/docs/agent/cli.md +149 -0
  7. data/docs/agent/cookbook.md +152 -0
  8. data/examples/blaze.rb +43 -0
  9. data/examples/config.yml.example +21 -0
  10. data/lib/mixin_bot/address.rb +43 -3
  11. data/lib/mixin_bot/api/app.rb +7 -0
  12. data/lib/mixin_bot/api/asset.rb +114 -3
  13. data/lib/mixin_bot/api/auth.rb +19 -10
  14. data/lib/mixin_bot/api/blaze.rb +81 -0
  15. data/lib/mixin_bot/api/chain.rb +94 -0
  16. data/lib/mixin_bot/api/code.rb +16 -0
  17. data/lib/mixin_bot/api/computer_api.rb +60 -0
  18. data/lib/mixin_bot/api/conversation.rb +7 -1
  19. data/lib/mixin_bot/api/deposit.rb +12 -0
  20. data/lib/mixin_bot/api/encrypted_message.rb +1 -1
  21. data/lib/mixin_bot/api/fiat.rb +12 -0
  22. data/lib/mixin_bot/api/inscription.rb +2 -2
  23. data/lib/mixin_bot/api/legacy_collectible.rb +26 -27
  24. data/lib/mixin_bot/api/legacy_multisig.rb +20 -21
  25. data/lib/mixin_bot/api/legacy_output.rb +10 -3
  26. data/lib/mixin_bot/api/legacy_payment.rb +2 -0
  27. data/lib/mixin_bot/api/legacy_snapshot.rb +16 -0
  28. data/lib/mixin_bot/api/legacy_transaction.rb +28 -13
  29. data/lib/mixin_bot/api/legacy_transfer.rb +11 -8
  30. data/lib/mixin_bot/api/legacy_user.rb +51 -0
  31. data/lib/mixin_bot/api/me.rb +99 -3
  32. data/lib/mixin_bot/api/message.rb +18 -27
  33. data/lib/mixin_bot/api/multisig.rb +19 -0
  34. data/lib/mixin_bot/api/network.rb +17 -0
  35. data/lib/mixin_bot/api/network_asset.rb +27 -0
  36. data/lib/mixin_bot/api/output.rb +1 -1
  37. data/lib/mixin_bot/api/pin.rb +16 -3
  38. data/lib/mixin_bot/api/pin_payload.rb +26 -0
  39. data/lib/mixin_bot/api/session.rb +14 -0
  40. data/lib/mixin_bot/api/snapshot.rb +6 -0
  41. data/lib/mixin_bot/api/tip.rb +74 -1
  42. data/lib/mixin_bot/api/transaction.rb +106 -17
  43. data/lib/mixin_bot/api/transfer.rb +141 -14
  44. data/lib/mixin_bot/api/turn.rb +12 -0
  45. data/lib/mixin_bot/api/user.rb +148 -45
  46. data/lib/mixin_bot/api/withdraw.rb +24 -23
  47. data/lib/mixin_bot/api.rb +248 -3
  48. data/lib/mixin_bot/bot_auth.rb +71 -0
  49. data/lib/mixin_bot/cli/api.rb +224 -143
  50. data/lib/mixin_bot/cli/base.rb +77 -0
  51. data/lib/mixin_bot/cli/call.rb +71 -0
  52. data/lib/mixin_bot/cli/errors.rb +56 -0
  53. data/lib/mixin_bot/cli/node.rb +9 -2
  54. data/lib/mixin_bot/cli/output.rb +196 -0
  55. data/lib/mixin_bot/cli/schema.rb +274 -0
  56. data/lib/mixin_bot/cli/schema_command.rb +21 -0
  57. data/lib/mixin_bot/cli/utils.rb +114 -18
  58. data/lib/mixin_bot/cli.rb +124 -48
  59. data/lib/mixin_bot/client/error_mapper.rb +40 -0
  60. data/lib/mixin_bot/client.rb +94 -64
  61. data/lib/mixin_bot/computer.rb +132 -0
  62. data/lib/mixin_bot/configuration.rb +108 -1
  63. data/lib/mixin_bot/errors.rb +102 -0
  64. data/lib/mixin_bot/models/address.rb +11 -0
  65. data/lib/mixin_bot/models/api_envelope.rb +67 -0
  66. data/lib/mixin_bot/models/asset.rb +11 -0
  67. data/lib/mixin_bot/models/ghost_keys.rb +14 -0
  68. data/lib/mixin_bot/models/output.rb +11 -0
  69. data/lib/mixin_bot/models/safe_multisig_request.rb +11 -0
  70. data/lib/mixin_bot/models/sequencer_transaction_request.rb +11 -0
  71. data/lib/mixin_bot/models/user.rb +11 -0
  72. data/lib/mixin_bot/models.rb +10 -0
  73. data/lib/mixin_bot/monitor.rb +77 -0
  74. data/lib/mixin_bot/transaction/buffer.rb +34 -0
  75. data/lib/mixin_bot/transaction/decoder.rb +227 -0
  76. data/lib/mixin_bot/transaction/encoder.rb +255 -0
  77. data/lib/mixin_bot/transaction.rb +6 -475
  78. data/lib/mixin_bot/url_scheme.rb +63 -0
  79. data/lib/mixin_bot/utils/address.rb +17 -80
  80. data/lib/mixin_bot/utils/crypto.rb +173 -1
  81. data/lib/mixin_bot/utils/decoder.rb +1 -1
  82. data/lib/mixin_bot/utils/encoder.rb +13 -0
  83. data/lib/mixin_bot/utils.rb +45 -0
  84. data/lib/mixin_bot/uuid.rb +78 -1
  85. data/lib/mixin_bot/version.rb +11 -1
  86. data/lib/mixin_bot.rb +172 -18
  87. data/lib/mvm/bridge.rb +46 -0
  88. data/lib/mvm/client.rb +60 -0
  89. data/lib/mvm/nft.rb +4 -2
  90. data/lib/mvm/registry.rb +2 -1
  91. data/lib/mvm.rb +93 -0
  92. data/lib/tasks/api_coverage.rake +20 -0
  93. data/llms.txt +29 -0
  94. metadata +77 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73565cefb791be042d4130d44248e575b30cac2b00d032508e6d2a215d02c845
4
- data.tar.gz: 521142f48775c4eb1166c4ccb22fe974bf91adab0c3a1b900980660b440d9606
3
+ metadata.gz: 5d03bd8cb9d6710487b3ef3c5ed57e88136a0bcb99c841d366fe7e24b3e8a43c
4
+ data.tar.gz: 5e0c70f36c6ca291a0e65e11137434474ad804829e67c553a7b8fbee651b5ea9
5
5
  SHA512:
6
- metadata.gz: c5d2729f574eca707316ea7bc05989a5f4fe411c562a81bdbf88a46b2dec855cf9f4701c33ce17016ac034ef490c42a01bddc3f9d7f3e819254fc228dc0b7b09
7
- data.tar.gz: 9a45a5441a0859dc404011788a5fac8afced8f7c1d0e9db100fe404abe05258f7364539b1417f6360367a65e861340b9b16223c7d63bb65d1e831877f0997f35
6
+ metadata.gz: a717d7b6103c5e1df8b1cb044303114c159cbc31cb47e6f0a33e9c7e7906a204c0fd3ab92507b9eb903979c603c5031e192a2f6bf392ee4d2636eea5f3f3c88c
7
+ data.tar.gz: 75a350a5053a490aff9cfac0b556e70dad2d2e20bf4cafb4408640b6d5c5dd932ec2264bf2cf952a3dec511b4e71855d4c5996e7acb32ccf6ea201b17943c80f
data/AGENTS.md ADDED
@@ -0,0 +1,75 @@
1
+ # AGENTS.md — MixinBot
2
+
3
+ Ruby gem (v2.0.0): Mixin Network REST SDK + `mixinbot` CLI. Parity target: [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client).
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ bundle install
9
+ rake # default: test + rubocop (in that order)
10
+ rake test # offline suite only
11
+ rake test_live # LIVE=1 rake test (requires test/config.yml)
12
+ rake mixin_bot:api_coverage # check API_COVERAGE.md has no missing entries
13
+ rake build # build .gem
14
+ rake rdoc # generate doc/
15
+ ```
16
+
17
+ **Single test**: `ruby -Itest -Ilib test/mixin_bot/some_test.rb`
18
+
19
+ ## Repository layout
20
+
21
+ ```
22
+ lib/mixin_bot/
23
+ api.rb + api/*.rb # One module per API domain (Transfer, Message, Blaze, …)
24
+ client.rb # Faraday HTTP client → ApiEnvelope
25
+ cli.rb + cli/*.rb # mixinbot CLI (Thor)
26
+ configuration.rb # Credentials and hosts
27
+ transaction/ # Raw transaction encode/decode
28
+ utils.rb # Crypto and encoding helpers
29
+ lib/mvm/ # MVM helpers (excluded from default rake test)
30
+ bin/mixinbot # CLI entrypoint
31
+ test/ # Minitest + WebMock (offline by default)
32
+ docs/agent/ # LLM-oriented CLI and cookbook docs
33
+ ```
34
+
35
+ ## CI and release
36
+
37
+ - **CI** (`.github/workflows/ci.yml`): `pull_request` and `push` to `main` — `rake test` on Ruby 3.2/3.3/4.0, `rake rubocop` (3.3), `rake mixin_bot:api_coverage`.
38
+ - **Release** (`.github/workflows/release.yml`): push tag `v*` (must match `MixinBot::VERSION`, e.g. tag `v2.0.0` for `VERSION = '2.0.0'`) → `rake build` → RubyGems via [trusted publishing](https://guides.rubygems.org/trusted-publishing/) (OIDC; workflow `release.yml`, no repo secret).
39
+ - **Dependabot** (`.github/dependabot.yml`): weekly Bundler and GitHub Actions updates; Dependabot PRs use the same CI workflow.
40
+
41
+ ## Conventions
42
+
43
+ - **Ruby** >= 3.2 (CI: 3.2, 3.3, 4.0)
44
+ - **HTTP responses**: `MixinBot::Models::ApiEnvelope` — use `res['data']` or delegated keys
45
+ - **Safe transfers**: require `spend_key`; prefer `create_safe_transfer` / `create_transfer` (Safe pipeline)
46
+ - **Legacy APIs**: `create_legacy_transfer`, `POST /transfers` — deprecated, warns once
47
+ - **Tests**: offline WebMock stubs by default; `LIVE=1 rake test` for integration
48
+ - **MVM tests**: excluded from `rake test`; run separately if needed
49
+ - **Lint**: RuboCop via `rake` (runs after tests in default task)
50
+
51
+ ## CLI boundaries
52
+
53
+ Interactive API methods are **excluded** from `mixinbot call` (defined in `CLIHelpers::INTERACTIVE_API_METHODS` at `lib/mixin_bot/cli/base.rb:28`):
54
+ - `start_blaze_connect`, `blaze`, `upload_attachment`
55
+
56
+ Use the Ruby API + EventMachine for Blaze (see `examples/blaze.rb`).
57
+
58
+ Agent-friendly CLI features:
59
+ - `mixinbot schema -o json` — machine-readable command schema
60
+ - `--output json|yaml|pretty` — structured stdout; auto JSON when piped
61
+ - `mixinbot list --limit N --offset N -o json` — bounded method registry
62
+
63
+ See [docs/agent/cli.md](docs/agent/cli.md).
64
+
65
+ ## Error types
66
+
67
+ Custom errors in `lib/mixin_bot/errors.rb`: `ResponseError`, `UnauthorizedError`, `InsufficientBalanceError`, `UtxoInsufficientError`, `PinError`, `NotFoundError`, etc.
68
+
69
+ CLI maps these to structured error kinds (`auth`, `api_error`, `not_found`, …) when `--output json`.
70
+
71
+ ## What not to do
72
+
73
+ - Do not commit secrets (`test/config.yml`, keystore files with real keys)
74
+ - Do not use legacy transfer APIs in new code
75
+ - Do not expect Blaze WebSocket flows to work via CLI
data/API_COVERAGE.md ADDED
@@ -0,0 +1,143 @@
1
+ # Mixin Go SDK API Coverage
2
+
3
+ Reference: [bot-api-go-client](https://github.com/MixinNetwork/bot-api-go-client) (`package bot`).
4
+
5
+ Status values: `done` | `alias` | `n/a` (CLI-only / config)
6
+
7
+ | Go symbol | Ruby method | HTTP / notes | Status |
8
+ |-----------|-------------|--------------|--------|
9
+ | **Auth** |
10
+ | SignAuthenticationToken | `API#access_token` | JWT | done |
11
+ | SignAuthenticationTokenWithoutBody | `API#access_token` | JWT | alias |
12
+ | SignAuthenticationTokenWithRequestID | `API#access_token` | JWT | alias |
13
+ | SignOauthAccessToken | `API#sign_oauth_access_token` | JWT | done |
14
+ | OAuthGetAccessToken | `API#oauth_token` | POST `/oauth/token` | done |
15
+ | **Users** |
16
+ | CreateUserSimple | `API#create_user` | POST `/users` | done |
17
+ | CreateUser | `API#create_user` | POST `/users` | done |
18
+ | GetUser | `API#user` | GET `/users/:id` | done |
19
+ | GetUsers | `API#fetch_users` | POST `/users/fetch` | done |
20
+ | SearchUser | `API#search_user` | GET `/search/:q` | done |
21
+ | UpdateTipPin | `API#update_tip_pin` | POST `/pin/update` | done |
22
+ | UpdatePin | `API#update_pin` | POST `/pin/update` | done |
23
+ | UserMe / RequestUserMe | `API#me` / `API#safe_me` | GET `/me`, `/safe/me` | done |
24
+ | UpdateUserMe | `API#update_me` | POST `/me` | done |
25
+ | UpdatePreference | `API#update_preferences` | POST `/me/preferences` | done |
26
+ | Relationship | `API#relationship` | POST `/relationships` | done |
27
+ | GenerateUserChecksum | `MixinBot.utils.generate_user_checksum` | local | done |
28
+ | RegisterSafe* | `API#safe_register`, `#migrate_to_safe` | POST `/safe/users` | done |
29
+ | UpgradeLegacyUser | `API#upgrade_legacy_user` | POST `/legacy/users` | done |
30
+ | FetchUserSession | `API#fetch_user_sessions` | POST `/sessions/fetch` | done |
31
+ | **Sessions / PIN** |
32
+ | EncryptEd25519PIN | `API#encrypt_pin` | local | done |
33
+ | VerifyPIN | `API#verify_pin` | POST `/pin/verify` | done |
34
+ | VerifyPINTip | `API#verify_pin_tip` | POST `/pin/verify` | alias |
35
+ | **Assets** |
36
+ | ListAssetWithBalance | `API#assets` | GET `/assets` | done |
37
+ | FetchAssets | `API#fetch_assets` | POST `/safe/assets/fetch` | done |
38
+ | ReadAssetFee | `API#asset_fee` | GET `/safe/assets/:id/fees` | done |
39
+ | AssetBalance* | `API#asset_balance` | derived from outputs | done |
40
+ | UserAssetBalance | `API#user_asset_balance` | GET `/safe/outputs` | done |
41
+ | ReadAsset | `API#network_asset` | GET `/network/assets/:id` | done |
42
+ | ReadAssetTicker* | `API#network_ticker` | GET `/network/ticker` | done |
43
+ | AssetSearch | `API#network_asset_search` | GET `/network/assets/search/:q` | done |
44
+ | ReadNetworkAssets | `API#network_assets` | GET `/network` | done |
45
+ | ReadNetworkAssetsTop | `API#network_assets_top` | GET `/network/assets/top` | done |
46
+ | GetFiats / Fiats | `API#fiats` | GET `/external/fiats` | done |
47
+ | **Chains** |
48
+ | ReadNetworkChainById | `API#network_chain` | GET `/network/chains/:id` | done |
49
+ | ReadNetworkChains | `API#network_chains` | GET `/network/chains` | done |
50
+ | GetChainName | `API#chain_name` | local | done |
51
+ | IsChainId | `API#chain_id?` | local | done |
52
+ | GetFullChains | `API#full_chains` | local | done |
53
+ | **Outputs / deposits** |
54
+ | ListOutputs / ListUnspentOutputs | `API#safe_outputs` | GET `/safe/outputs` | done |
55
+ | GetOutput | `API#safe_output` | GET `/safe/outputs/:id` | done |
56
+ | FetchPendingSafeDeposits | `API#pending_safe_deposits` | GET `/safe/deposits` | done |
57
+ | CreateDepositEntry | `API#safe_deposit_entries` | POST `/safe/deposit/entries` | done |
58
+ | **Transactions (Safe)** |
59
+ | SendTransaction* | `API#create_safe_transfer` | pipeline | done |
60
+ | GetTransactionById* | `API#safe_transaction` | GET `/safe/transactions/:id` | done |
61
+ | VerifyRawTransaction | `API#verify_raw_transaction` | POST `/safe/transaction/requests` | done |
62
+ | SendRawTransaction | `API#send_safe_transaction` | POST `/safe/transactions` | done |
63
+ | BuildRawTransaction | `API#build_safe_transaction` | local | done |
64
+ | RequestGhostRecipientsWithTraceId | `API#request_ghost_recipients_with_trace_id` | POST `/safe/keys` | done |
65
+ | RequestSafeGhostKeys | `API#create_safe_keys` | POST `/safe/keys` | done |
66
+ | CreateMultisigRawTx | `API#create_multisig_raw_tx` | local + keys | done |
67
+ | CreateObjectStorageTransaction | `API#create_object_storage_transaction` | pipeline | done |
68
+ | EstimateStorageCost | `API#estimate_storage_cost` | local | done |
69
+ | StorageRecipient | `API#storage_recipient` | local | done |
70
+ | SendKernelTransactionFromAccount | `API#send_kernel_transaction_from_account` | documented N/I | done |
71
+ | **Multisig** |
72
+ | CreateSafeMultisigRequest | `API#create_safe_multisig_request` | POST `/safe/multisigs` | done |
73
+ | FetchSafeMultisigRequest | `API#safe_multisig_request` | GET `/safe/multisigs/:id` | done |
74
+ | ReadMultisigs* | `API#read_multisigs` / `#legacy_outputs` | GET `/multisigs/outputs` | done |
75
+ | CreateMultisig / Sign / Unlock / Cancel | `API#create_*_multisig_*` | legacy paths | done |
76
+ | **Snapshots** |
77
+ | SafeSnapshots* | `API#safe_snapshots` | GET `/safe/snapshots` | done |
78
+ | SafeSnapshotById | `API#safe_snapshot` | GET `/safe/snapshots/:id` | done |
79
+ | SafeNotifySnapshot | `API#create_safe_snapshot_notification` | POST notifications | done |
80
+ | Snapshots / SnapshotById / SnapshotByTraceId | `API#snapshots`, `#snapshot`, `#snapshot_by_trace_id` | legacy | done |
81
+ | NetworkSnapshot* | `API#network_snapshot(s)` | legacy | done |
82
+ | **Withdrawals / addresses** |
83
+ | CreateAddress / ReadAddress / DeleteAddress | `API#create_withdraw_address`, etc. | `/addresses` | done |
84
+ | GetAddressesByAssetId | `API#withdraw_addresses` | GET `/assets/:id/addresses` | done |
85
+ | CheckAddress | `API#check_address` | GET `/external/addresses/check` | done |
86
+ | SendWithdrawal | `API#withdrawals` | POST `/withdrawals` | done |
87
+ | **Conversations / messages** |
88
+ | CreateContactConversation | `API#create_contact_conversation` | POST `/conversations` | done |
89
+ | CreateGroupConversation | `API#create_group_conversation` | POST `/conversations` | done |
90
+ | ConversationShow | `API#conversation` | GET `/conversations/:id` | done |
91
+ | JoinConversation | `API#join_conversation` | POST `.../join` | done |
92
+ | RotateConversation | `API#rotate_conversation` | POST `.../rotate` | done |
93
+ | UpdateParticipants | `API#add/remove/..._participants` | POST participants | done |
94
+ | PostMessage(s) | `API#send_message` | POST `/messages` | done |
95
+ | EncryptMessageData / DecryptMessageData | `API#encrypt_message` / `#decrypt_message` | local | done |
96
+ | BlazeClient send helpers | `API#blaze_send_*` | WebSocket | done |
97
+ | **Inscriptions** |
98
+ | ReadCollection / ReadInscription / ReadCollectionItems | `API#collection`, `#collectible`, etc. | safe inscriptions | done |
99
+ | **Apps** |
100
+ | Migrate | `API#transfer_app_ownership` | POST `/apps/:id/transfer` | done |
101
+ | **Codes** |
102
+ | ReadCode | `API#read_code` | GET `/codes/:id` | done |
103
+ | **TIP** |
104
+ | GetTipNodeByPath* | `API#get_tip_node` | GET `/external/tip/:path` | done |
105
+ | TipBodyFor* | `API#tip_body_for_*` | local | done |
106
+ | **Network misc** |
107
+ | GetTurnServer | `API#turn_servers` | GET `/turn` | done |
108
+ | CallKernelRPC | `API#rpc_proxy` | POST `/external/kernel` | done |
109
+ | **Mix / invoice** |
110
+ | NewUUIDMixAddress / NewMainnetMixAddress | `MixAddress` | local | done |
111
+ | MixAddress.RequestOrGenerateGhostKeys | `MixAddress#request_or_generate_ghost_keys` | local/API | done |
112
+ | NewMixinInvoice | `MixinBot::Invoice` | local | done |
113
+ | **URL schemes** |
114
+ | SchemeUsers / Transfer / Pay / … | `MixinBot::UrlScheme` | local | done |
115
+ | **Utils** |
116
+ | UniqueObjectId | `MixinBot.utils.unique_object_id` | local | done |
117
+ | UniqueConversationId | `MixinBot.utils.unique_uuid` | local | done |
118
+ | GroupConversationId | `MixinBot.utils.generate_group_conversation_id` | local | done |
119
+ | Chunked / MakeUniqueStringSlice | `MixinBot.utils.chunked`, `#make_unique_string_slice` | local | done |
120
+ | **Computer** |
121
+ | GetComputerInfo | `MixinBot::Computer.info` / `API#get_computer_info` | computer.mixin.one | done |
122
+ | GetComputerUser | `API#get_computer_user` | done |
123
+ | GetComputerDeployedAssets | `API#get_computer_deployed_assets` | done |
124
+ | GetComputerSystemCall | `API#get_computer_system_call` | done |
125
+ | ComputerDeployExternalAsset | `API#computer_deploy_external_asset` | done |
126
+ | LockComputerNonceAccount | `API#lock_computer_nonce_account` | done |
127
+ | GetFeeOnXINBasedOnSOL | `API#get_fee_on_xin_based_on_sol` | done |
128
+ | RegisterComputer | `API#register_computer` | done |
129
+ | ComputerUserIDToBytes | `API#computer_user_id_to_bytes` | done |
130
+ | BuildSystemCallExtra | `API#build_system_call_extra` | done |
131
+ | EncodeOperationMemo / EncodeMtgExtra / DecodeComputerExtraBase64 | `API#encode_*` | done |
132
+ | **BotAuth** |
133
+ | NewBotAuthClient / SignRequest | `MixinBot::BotAuth` | local | done |
134
+ | **Monitor** |
135
+ | ReportToMonitor | `MixinBot::Monitor.report_to_monitor` | pipeline | done |
136
+ | UnmarshalAppMessage | `MixinBot::Monitor.unmarshal_app_message` | local | done |
137
+ | CheckRetryableError | `MixinBot::Monitor.check_retryable_error` | local | done |
138
+ | **HTTP config** |
139
+ | Request / SetBaseUri / SetBlazeUri | `MixinBot::Configuration`, `Client` | config | n/a |
140
+ | NewSafeUser | `MixinBot::Configuration` | config | n/a |
141
+ | cli/*, examples/*, mixin/rpc main | `mixinbot call` / `mixinbot list` | CLI dispatch to `MixinBot::API` | done |
142
+
143
+ Update this file when adding or changing API surfaces. Run `rake mixin_bot:api_coverage` to ensure no `missing` rows remain.
data/CHANGELOG.md ADDED
@@ -0,0 +1,112 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - **`mixinbot call METHOD`** and **`mixinbot list`** — invoke any public `MixinBot::API` method from the CLI with JSON keyword arguments (`-d`).
13
+ - **`mixinbot utils call`** / **`mixinbot utils list`** — same for `MixinBot.utils` helpers.
14
+
15
+ ### Changed
16
+
17
+ - **`mixinbot transfer`** — uses Safe API (`create_safe_transfer`) instead of legacy `POST /transfers`.
18
+ - **`mixinbot api`** — routes through `MixinBot::Client` (supports JSON array POST bodies); keystore loading includes `spend_key` and `client_secret`.
19
+ - **`mixinbot updatetip`** — uses `update_tip_pin` instead of misusing `update_pin`.
20
+ - **`mixinbot safetransfer`** — delegates to `transfer` (no duplicated signing pipeline).
21
+
22
+ ### Deprecated
23
+
24
+ - **`mixinbot legacy-transfer`** — explicit legacy transfer command (replaces old default `transfer` behavior).
25
+ - **`mixinbot safetransfer`** — use `transfer` instead.
26
+
27
+ ### Fixed
28
+
29
+ - **`mixinbot nftmemo`** — calls `MixinBot.utils.nft_memo` (was a broken `nft` alias).
30
+
31
+ ## [2.0.0] - 2026-05-16
32
+
33
+ ### Added
34
+
35
+ - `MixinBot.utils.hash_members` (Go `HashMembers`) for sorted member hashing; used by `safe_outputs` and legacy output/collectible helpers.
36
+ - `MixinBot::API#tip_or_legacy_pin_payload` and adoption across legacy PIN/TIP call sites.
37
+ - Offline WebMock harness (`test/support/mixin_api_stubs.rb`), deterministic `OfflineConfig`, and `rake test_live` (runs `test` with `LIVE=1`).
38
+ - Golden-vector fixtures under `test/fixtures/golden/` and transaction hex under `test/fixtures/transactions/`.
39
+
40
+ ### Changed
41
+
42
+ - **HTTP responses** — `MixinBot::Client` returns `MixinBot::Models::ApiEnvelope` (no `merge!` of `data` into the top level). One-liners such as `#me` still return the inner `data` hash where that was the historical contract.
43
+ - **`MixinBot::API#build_safe_transaction`** — derives the mixin asset hash from each UTXO’s `asset_id` when `asset` is absent (matches API output shapes).
44
+ - **`MixinBot::Transaction#decode`** — reads the `references` section only when `references` is non-empty, matching encode behavior (fixes Safe tx round-trips).
45
+
46
+ ### Deprecated
47
+
48
+ - All `Legacy*` API modules emit `MixinBot.deprecator` warnings (silenced in the default test suite). Migrate to Safe APIs (`create_safe_transfer`, `build_safe_transaction`, `safe_outputs`, inscriptions, etc.).
49
+
50
+ ### Fixed
51
+
52
+ - **Ruby 4.0** — declare the `benchmark` gem (stdlib is no longer auto-loaded), bump **`eth` ≥ 0.5.17** (compatible `openssl` stack), add **`rdoc`** for `rake`/YARD, replace **`CGI.parse`** in offline stubs with **`URI.decode_www_form`**, and run CI on **4.0**.
53
+
54
+ ## [1.5.0] - 2026-05-15
55
+
56
+ ### Changed (breaking)
57
+
58
+ - **`MixinBot::API#safe_register`** — renamed the misleading first positional
59
+ parameter `pin` to `spend_key` and removed the previously unusable
60
+ `spend_key:` keyword argument. The method now takes a single argument:
61
+ the user's spend Ed25519 private key, accepted as raw bytes, hex, or a
62
+ Base64-encoded string. All in-tree call sites already used it positionally,
63
+ so most consumers will not need changes.
64
+
65
+ Migration:
66
+
67
+ ```ruby
68
+ # before
69
+ api.safe_register(spend_key_hex) # already worked
70
+ api.safe_register(pin, spend_key: spend_key_bytes) # never worked correctly
71
+
72
+ # after
73
+ api.safe_register(spend_key_hex)
74
+ api.safe_register(spend_key_bytes)
75
+ ```
76
+
77
+ ### Fixed
78
+
79
+ - **`MixinBot::API#create_safe_user`** — the per-instance `@__retry__`
80
+ counter leaked across calls and was not thread-safe. Replaced with a
81
+ local variable inside a private `with_safe_register_retries` helper.
82
+ - **`MixinBot::API#migrate_to_safe`** — fixed a long-standing bug where
83
+ `safe_register pin, spend_key` raised `ArgumentError` (positional vs.
84
+ keyword mismatch) and additionally passed the TIP public key hex as the
85
+ signing key. Now correctly calls `safe_register(spend_key_hex)`.
86
+ - **`MixinBot::API#safe_register`** — would crash inside
87
+ `JOSE::JWA::Ed25519.sign` when callers passed a 32-byte seed. Now
88
+ derives a normalized 64-byte signing key from the keypair before
89
+ encrypting the TIP PIN.
90
+
91
+ ### Improved
92
+
93
+ - The Safe-network registration retry now rescues only transient
94
+ `MixinBot::PinError` / `MixinBot::ResponseError` (e.g. server-side TIP
95
+ PIN propagation lag), instead of swallowing every `MixinBot::Error`
96
+ including `UnauthorizedError`, `NotFoundError`, etc.
97
+ - Added module-level constants for retry limits and propagation delay:
98
+ `SAFE_REGISTER_MAX_RETRIES`, `SAFE_REGISTER_RETRY_BASE_DELAY`,
99
+ `TIP_PIN_PROPAGATION_DELAY`.
100
+ - Added input validation: `safe_register` now raises `ArgumentError` when
101
+ the spend key cannot be decoded into at least 32 bytes.
102
+ - Added YARD documentation for `create_user`, `create_safe_user`,
103
+ `safe_register`, and `migrate_to_safe`, matching the style used in
104
+ `MixinBot::API::Me`.
105
+ - Added a `# NOTE:` comment in `safe_register` clarifying that the Go
106
+ SDK's `crypto.Sha256Hash` is misleadingly named — it actually computes
107
+ SHA3-256, so `SHA3::Digest::SHA256` is the correct Ruby match.
108
+
109
+ ## [1.4.0] - prior
110
+
111
+ See `CHANGES_SUMMARY.md` for the documentation overhaul that preceded this
112
+ changelog.