From 7d2cca00a260121a215ff4c45daca34b90c4a178 Mon Sep 17 00:00:00 2001 From: Nida Ali Date: Fri, 3 Jul 2026 14:44:24 -0500 Subject: [PATCH 1/3] Enforce allow-list schema for account preferences write (BFP-4644) Replace the PUT /api/v1/account/preferences request model with a strict allow-list to match the server-side mass-assignment fix. The write model now accepts only favorites, functionBarMode, onboardingCompleted and termsAccepted; unknown fields (language/theme/market and any extra key) are no longer emitted and are rejected by the server with 400. - spec: UpdateAccountPreferenceRequest -> additionalProperties: false, 4 optional fields + functionBarMode enum ("all"|"Popular"|"Favorites") - ts: drop the index signature (no passthrough), add enum - python: drop additional_properties, add enum validator, aliased fields - rust: new fields + FunctionBarMode enum - update example, docs, python test fixture - read model AccountPreference (GET) left permissive/unchanged - major version bump (breaking): ts 3.0.0, python/rust 2.0.0 - add CHANGELOG; refresh .apigen-state hashes Co-Authored-By: Claude Opus 4.8 (1M context) --- .apigen-state | 6 +-- CHANGELOG.md | 34 +++++++++++++ python/sdk/config.yaml | 2 +- python/sdk/pyproject.toml | 2 +- .../docs/UpdateAccountPreferenceRequest.md | 7 +-- .../update_account_preference_request.py | 49 ++++++++----------- .../test_update_account_preference_request.py | 13 +++-- resources/account-data-api.yaml | 38 +++++++++----- rust/Cargo.lock | 4 +- rust/Cargo.toml | 4 +- rust/examples/account-preferences.rs | 9 ++-- rust/gen/bluefin_api/Cargo.toml | 2 +- .../docs/UpdateAccountPreferenceRequest.md | 7 +-- .../update_account_preference_request.rs | 45 ++++++++++++----- rust/gen/config.yaml | 2 +- ts/sdk/package.json | 2 +- ts/sdk/src/api.ts | 35 ++++++++----- .../docs/UpdateAccountPreferenceRequest.md | 14 +++--- 18 files changed, 178 insertions(+), 97 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.apigen-state b/.apigen-state index ed6b8961..17ea257d 100644 --- a/.apigen-state +++ b/.apigen-state @@ -1,9 +1,9 @@ { "version": "1", "algorithm": "sha256", - "generated_at": "2026-03-26T16:48:24.691481Z", + "generated_at": "2026-07-03T00:00:00.000000Z", "spec_files": { - "resources/account-data-api.yaml": "9739df11b0d8c26bc3eea5135e524b7e1ade4192867569896a817bed380aaff0", + "resources/account-data-api.yaml": "ca145d50d48e0f3709130af89cf0feb3416c36adbd54b5ad40740f55217f94af", "resources/auth-api.yaml": "01e54ca1a13d2d6307e0d3a9ea38f816902521e7c85998f0f21a1353cc54fb76", "resources/bluefin-api.yaml": "fa46dd3fa556baa1d81567b9d21ea81e948dd0e596bfdc368e0eb1e8fdad3fe1", "resources/common.yaml": "71dc565fccebcc9e71b49b48448d2583903aec7e2d16ef1d69972b50f2e4250f", @@ -14,5 +14,5 @@ "resources/vera-api.yaml": "f4c51b5f6614dd4e3b9f5f1f01f945e06d9090849530ecb916ad4ae18d8f61a0", "resources/websocket-api.yaml": "c2c51ceea701bff0b7e911e9afdb0addeb4d7f173b2a25b996d13f5462cb535b" }, - "combined_hash": "f62934225656afb7209eb2f7582f9a72120d1cee6149c4fa6c1b1dcd2e772fcb" + "combined_hash": "4cd30de0eebb653543aed47a5a8f1167f29f0fee206b25065ff2f44146c91f59" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..d529bfd6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog + +All notable changes to the Bluefin Pro SDK are documented here. + +## [TypeScript 3.0.0 / Python 2.0.0 / Rust 2.0.0] - 2026-07-03 + +### Changed — BREAKING + +- **`PUT /api/v1/account/preferences` write model replaced with a strict allow-list** (BFP-4644). + The `UpdateAccountPreferenceRequest` body was rewritten to match the server's + new allow-list schema (a mass-assignment security fix). The request now accepts + only these optional fields, and any unknown field is rejected by the server with + a `400` naming the offending field: + - `favorites: string[]` — favorite market symbols (e.g. `["BTC-PERP","SUI-PERP"]`). + Full array each time; to remove a favorite, send the array without it. + - `functionBarMode: "all" | "Popular" | "Favorites"` — note the mixed casing + (`all` is lowercase; `Popular` and `Favorites` are capitalized). + - `onboardingCompleted: boolean` + - `termsAccepted: boolean` + + The write is a **full replace** — always send the complete object; the server + overwrites the stored preferences. A successful call returns `204 No Content` + (no response body). + + **Removed from the write model:** `language`, `theme`, and `market`. Any code + still sending these (or any other extra key) through this endpoint will now + receive a `400`. The serializer no longer emits unknown/extra keys. + +### Unchanged + +- `GET /api/v1/account/preferences` (`AccountPreference`) is intentionally left + permissive (`additionalProperties: true`) and still exposes the legacy typed + `language` / `theme` / `market` fields. GET and PUT are deliberately asymmetric: + the new write fields round-trip through GET as untyped extra keys. diff --git a/python/sdk/config.yaml b/python/sdk/config.yaml index 6d635d58..c390a1c4 100644 --- a/python/sdk/config.yaml +++ b/python/sdk/config.yaml @@ -2,4 +2,4 @@ additionalProperties: library: asyncio generateSourceCodeOnly: true useOneOfDiscriminatorLookup: true - packageVersion: 1.17.0 + packageVersion: 2.0.0 diff --git a/python/sdk/pyproject.toml b/python/sdk/pyproject.toml index 747f2e33..f5147291 100644 --- a/python/sdk/pyproject.toml +++ b/python/sdk/pyproject.toml @@ -21,7 +21,7 @@ description = "Python Boilerplate contains all the boilerplate you need to creat name = "bluefin_pro_sdk" readme = "README.rst" requires-python = ">=3.9.2,<3.14.0" -version = "1.17.0" +version = "2.0.0" [[project.authors]] email = "audreyr@example.com" diff --git a/python/sdk/src/openapi_client/docs/UpdateAccountPreferenceRequest.md b/python/sdk/src/openapi_client/docs/UpdateAccountPreferenceRequest.md index 28d27fca..b0479d71 100644 --- a/python/sdk/src/openapi_client/docs/UpdateAccountPreferenceRequest.md +++ b/python/sdk/src/openapi_client/docs/UpdateAccountPreferenceRequest.md @@ -5,9 +5,10 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**language** | **str** | User preferred language. | [optional] -**theme** | **str** | User preferred theme. | [optional] -**market** | [**List[AccountMarketPreference]**](AccountMarketPreference.md) | | [optional] +**favorites** | **List[str]** | Favorite market symbols. Send the full array each time; to remove a favorite, send the array without it. | [optional] +**function_bar_mode** | **str** | Function bar display mode. Note the mixed casing: `all` is lowercase while `Popular` and `Favorites` are capitalized. | [optional] +**onboarding_completed** | **bool** | Whether the user has completed onboarding. | [optional] +**terms_accepted** | **bool** | Whether the user has accepted the terms of service. | [optional] ## Example diff --git a/python/sdk/src/openapi_client/models/update_account_preference_request.py b/python/sdk/src/openapi_client/models/update_account_preference_request.py index dd05eb55..2a7e9079 100644 --- a/python/sdk/src/openapi_client/models/update_account_preference_request.py +++ b/python/sdk/src/openapi_client/models/update_account_preference_request.py @@ -17,9 +17,8 @@ import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict, Field, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr, field_validator from typing import Any, ClassVar, Dict, List, Optional -from openapi_client.models.account_market_preference import AccountMarketPreference from typing import Optional, Set from typing_extensions import Self @@ -27,11 +26,21 @@ class UpdateAccountPreferenceRequest(BaseModel): """ UpdateAccountPreferenceRequest """ # noqa: E501 - language: Optional[StrictStr] = Field(default=None, description="User preferred language.") - theme: Optional[StrictStr] = Field(default=None, description="User preferred theme.") - market: Optional[List[AccountMarketPreference]] = None - additional_properties: Dict[str, Any] = {} - __properties: ClassVar[List[str]] = ["language", "theme", "market"] + favorites: Optional[List[StrictStr]] = Field(default=None, description="Favorite market symbols. Send the full array each time; to remove a favorite, send the array without it. ") + function_bar_mode: Optional[StrictStr] = Field(default=None, description="Function bar display mode. Note the mixed casing: `all` is lowercase while `Popular` and `Favorites` are capitalized. ", alias="functionBarMode") + onboarding_completed: Optional[StrictBool] = Field(default=None, description="Whether the user has completed onboarding.", alias="onboardingCompleted") + terms_accepted: Optional[StrictBool] = Field(default=None, description="Whether the user has accepted the terms of service.", alias="termsAccepted") + __properties: ClassVar[List[str]] = ["favorites", "functionBarMode", "onboardingCompleted", "termsAccepted"] + + @field_validator('function_bar_mode') + def function_bar_mode_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['all', 'Popular', 'Favorites']): + raise ValueError("must be one of enum values ('all', 'Popular', 'Favorites')") + return value model_config = ConfigDict( populate_by_name=True, @@ -63,10 +72,8 @@ def to_dict(self) -> Dict[str, Any]: * `None` is only added to the output dict for nullable fields that were set at model initialization. Other fields with value `None` are ignored. - * Fields in `self.additional_properties` are added to the output dict. """ excluded_fields: Set[str] = set([ - "additional_properties", ]) _dict = self.model_dump( @@ -74,18 +81,6 @@ def to_dict(self) -> Dict[str, Any]: exclude=excluded_fields, exclude_none=True, ) - # override the default output from pydantic by calling `to_dict()` of each item in market (list) - _items = [] - if self.market: - for _item_market in self.market: - if _item_market: - _items.append(_item_market.to_dict()) - _dict['market'] = _items - # puts key-value pairs in additional_properties in the top level - if self.additional_properties is not None: - for _key, _value in self.additional_properties.items(): - _dict[_key] = _value - return _dict @classmethod @@ -98,15 +93,11 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ - "language": obj.get("language"), - "theme": obj.get("theme"), - "market": [AccountMarketPreference.from_dict(_item) for _item in obj["market"]] if obj.get("market") is not None else None + "favorites": obj.get("favorites"), + "functionBarMode": obj.get("functionBarMode"), + "onboardingCompleted": obj.get("onboardingCompleted"), + "termsAccepted": obj.get("termsAccepted") }) - # store additional fields in additional_properties - for _key in obj.keys(): - if _key not in cls.__properties: - _obj.additional_properties[_key] = obj.get(_key) - return _obj diff --git a/python/sdk/src/openapi_client/test/test_update_account_preference_request.py b/python/sdk/src/openapi_client/test/test_update_account_preference_request.py index 03dda2a3..6eb7dce1 100644 --- a/python/sdk/src/openapi_client/test/test_update_account_preference_request.py +++ b/python/sdk/src/openapi_client/test/test_update_account_preference_request.py @@ -35,13 +35,12 @@ def make_instance(self, include_optional) -> UpdateAccountPreferenceRequest: model = UpdateAccountPreferenceRequest() if include_optional: return UpdateAccountPreferenceRequest( - language = 'en', - theme = 'dark', - market = [ - openapi_client.models.account_market_preference.AccountMarketPreference( - margin_type = 'CROSS', - set_leverage = 100, ) - ] + favorites = [ + 'BTC-PERP' + ], + function_bar_mode = 'Favorites', + onboarding_completed = True, + terms_accepted = True ) else: return UpdateAccountPreferenceRequest( diff --git a/resources/account-data-api.yaml b/resources/account-data-api.yaml index c55b084b..a23b5e4a 100644 --- a/resources/account-data-api.yaml +++ b/resources/account-data-api.yaml @@ -359,20 +359,36 @@ components: UpdateAccountPreferenceRequest: type: object - additionalProperties: true + # Strict allow-list: unknown fields are rejected with 400 (mass-assignment fix). + # This is a full replace - always send the complete object. + additionalProperties: false properties: - language: - type: string - description: User preferred language. - example: en - theme: - type: string - description: User preferred theme. - example: dark - market: + favorites: type: array items: - $ref: "#/components/schemas/AccountMarketPreference" + type: string + description: | + Favorite market symbols. Send the full array each time; + to remove a favorite, send the array without it. + example: + - BTC-PERP + - SUI-PERP + functionBarMode: + type: string + enum: + - all + - Popular + - Favorites + description: | + Function bar display mode. Note the mixed casing: `all` is + lowercase while `Popular` and `Favorites` are capitalized. + example: Favorites + onboardingCompleted: + type: boolean + description: Whether the user has completed onboarding. + termsAccepted: + type: boolean + description: Whether the user has accepted the terms of service. AccountPreference: type: object diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 5e6da69c..fa7d1366 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -121,7 +121,7 @@ dependencies = [ [[package]] name = "bluefin-pro" -version = "1.17.0" +version = "2.0.0" dependencies = [ "base64", "bcs", @@ -146,7 +146,7 @@ dependencies = [ [[package]] name = "bluefin_api" -version = "1.17.0" +version = "2.0.0" dependencies = [ "reqwest", "serde", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b70537bf..8d05022a 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -9,7 +9,7 @@ sha2 = "0.10.9" [dependencies.bluefin_api] path = "gen/bluefin_api" -version = "1.17.0" +version = "2.0.0" [dependencies.ed25519-dalek] features = ["rand_core"] @@ -93,7 +93,7 @@ keywords = [ license = "Unlicense" name = "bluefin-pro" repository = "https://github.com/fireflyprotocol/pro-sdk" -version = "1.17.0" +version = "2.0.0" [workspace] members = [ diff --git a/rust/examples/account-preferences.rs b/rust/examples/account-preferences.rs index 109fa766..9af3e306 100644 --- a/rust/examples/account-preferences.rs +++ b/rust/examples/account-preferences.rs @@ -1,5 +1,6 @@ use bluefin_api::apis::account_data_api::{get_account_preferences, put_account_preferences}; use bluefin_api::apis::configuration::Configuration; +use bluefin_api::models::update_account_preference_request::FunctionBarMode; use bluefin_api::models::LoginRequest; use bluefin_api::models::{AccountPreference, UpdateAccountPreferenceRequest}; use bluefin_pro::prelude::*; @@ -62,10 +63,12 @@ async fn main() -> Result<()> { println!("{account:#?}"); println!("Updating preferences..."); + // Full replace: always send the complete object. Unknown fields are rejected with 400. let preference = UpdateAccountPreferenceRequest { - language: Some("en".into()), - theme: Some("dark".into()), - market: None, + favorites: Some(vec!["BTC-PERP".into(), "SUI-PERP".into()]), + function_bar_mode: Some(FunctionBarMode::Favorites), + onboarding_completed: Some(true), + terms_accepted: Some(true), }; send_update_request(&auth_token, preference).await?; diff --git a/rust/gen/bluefin_api/Cargo.toml b/rust/gen/bluefin_api/Cargo.toml index 01271bfa..66a30eee 100644 --- a/rust/gen/bluefin_api/Cargo.toml +++ b/rust/gen/bluefin_api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bluefin_api" -version = "1.17.0" +version = "2.0.0" authors = ["OpenAPI Generator team and contributors"] description = "Bluefin API" license = "MIT OR Apache-2.0" diff --git a/rust/gen/bluefin_api/docs/UpdateAccountPreferenceRequest.md b/rust/gen/bluefin_api/docs/UpdateAccountPreferenceRequest.md index d1a359aa..6d4162b8 100644 --- a/rust/gen/bluefin_api/docs/UpdateAccountPreferenceRequest.md +++ b/rust/gen/bluefin_api/docs/UpdateAccountPreferenceRequest.md @@ -4,9 +4,10 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**language** | Option<**String**> | User preferred language. | [optional] -**theme** | Option<**String**> | User preferred theme. | [optional] -**market** | Option<[**Vec**](AccountMarketPreference.md)> | | [optional] +**favorites** | Option<**Vec**> | Favorite market symbols. Send the full array each time; to remove a favorite, send the array without it. | [optional] +**function_bar_mode** | Option<**String**> | Function bar display mode. Note the mixed casing: `all` is lowercase while `Popular` and `Favorites` are capitalized. | [optional] +**onboarding_completed** | Option<**bool**> | Whether the user has completed onboarding. | [optional] +**terms_accepted** | Option<**bool**> | Whether the user has accepted the terms of service. | [optional] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/rust/gen/bluefin_api/src/models/update_account_preference_request.rs b/rust/gen/bluefin_api/src/models/update_account_preference_request.rs index 07dac157..d360140f 100644 --- a/rust/gen/bluefin_api/src/models/update_account_preference_request.rs +++ b/rust/gen/bluefin_api/src/models/update_account_preference_request.rs @@ -4,7 +4,7 @@ * Bluefin API * * The version of the OpenAPI document: 1.0.0 - * + * * Generated by: https://openapi-generator.tech */ @@ -13,23 +13,44 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct UpdateAccountPreferenceRequest { - /// User preferred language. - #[serde(rename = "language", skip_serializing_if = "Option::is_none")] - pub language: Option, - /// User preferred theme. - #[serde(rename = "theme", skip_serializing_if = "Option::is_none")] - pub theme: Option, - #[serde(rename = "market", skip_serializing_if = "Option::is_none")] - pub market: Option>, + /// Favorite market symbols. Send the full array each time; to remove a favorite, send the array without it. + #[serde(rename = "favorites", skip_serializing_if = "Option::is_none")] + pub favorites: Option>, + /// Function bar display mode. Note the mixed casing: `all` is lowercase while `Popular` and `Favorites` are capitalized. + #[serde(rename = "functionBarMode", skip_serializing_if = "Option::is_none")] + pub function_bar_mode: Option, + /// Whether the user has completed onboarding. + #[serde(rename = "onboardingCompleted", skip_serializing_if = "Option::is_none")] + pub onboarding_completed: Option, + /// Whether the user has accepted the terms of service. + #[serde(rename = "termsAccepted", skip_serializing_if = "Option::is_none")] + pub terms_accepted: Option, } impl UpdateAccountPreferenceRequest { pub fn new() -> UpdateAccountPreferenceRequest { UpdateAccountPreferenceRequest { - language: None, - theme: None, - market: None, + favorites: None, + function_bar_mode: None, + onboarding_completed: None, + terms_accepted: None, } } } +/// Function bar display mode. Note the mixed casing: `all` is lowercase while `Popular` and `Favorites` are capitalized. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum FunctionBarMode { + #[serde(rename = "all")] + All, + #[serde(rename = "Popular")] + Popular, + #[serde(rename = "Favorites")] + Favorites, +} + +impl Default for FunctionBarMode { + fn default() -> FunctionBarMode { + Self::All + } +} diff --git a/rust/gen/config.yaml b/rust/gen/config.yaml index 30ca8892..af0f5d8b 100644 --- a/rust/gen/config.yaml +++ b/rust/gen/config.yaml @@ -2,5 +2,5 @@ additionalProperties: hideGenerationTimestamp: 'true' avoidBoxedModels: 'true' packageName: bluefin_api - packageVersion: 1.17.0 + packageVersion: 2.0.0 preferUnsignedInt: 'true' diff --git a/ts/sdk/package.json b/ts/sdk/package.json index 3aaa70c2..b51c3388 100644 --- a/ts/sdk/package.json +++ b/ts/sdk/package.json @@ -56,5 +56,5 @@ "prepare": "npm run build" }, "types": "./dist/index.d.ts", - "version": "2.0.0" + "version": "3.0.0" } diff --git a/ts/sdk/src/api.ts b/ts/sdk/src/api.ts index 1c4baab1..470a15fd 100644 --- a/ts/sdk/src/api.ts +++ b/ts/sdk/src/api.ts @@ -5753,29 +5753,42 @@ export type TransactionType = typeof TransactionType[keyof typeof TransactionTyp * @interface UpdateAccountPreferenceRequest */ export interface UpdateAccountPreferenceRequest { - [key: string]: any; - /** - * User preferred language. - * @type {string} + * Favorite market symbols. Send the full array each time; to remove a favorite, send the array without it. + * @type {Array} * @memberof UpdateAccountPreferenceRequest */ - 'language'?: string; + 'favorites'?: Array; /** - * User preferred theme. + * Function bar display mode. Note the mixed casing: `all` is lowercase while `Popular` and `Favorites` are capitalized. * @type {string} * @memberof UpdateAccountPreferenceRequest */ - 'theme'?: string; + 'functionBarMode'?: UpdateAccountPreferenceRequestFunctionBarModeEnum; /** - * - * @type {Array} + * Whether the user has completed onboarding. + * @type {boolean} * @memberof UpdateAccountPreferenceRequest */ - 'market'?: Array; + 'onboardingCompleted'?: boolean; + /** + * Whether the user has accepted the terms of service. + * @type {boolean} + * @memberof UpdateAccountPreferenceRequest + */ + 'termsAccepted'?: boolean; } + +export const UpdateAccountPreferenceRequestFunctionBarModeEnum = { + All: 'all', + Popular: 'Popular', + Favorites: 'Favorites' +} as const; + +export type UpdateAccountPreferenceRequestFunctionBarModeEnum = typeof UpdateAccountPreferenceRequestFunctionBarModeEnum[keyof typeof UpdateAccountPreferenceRequestFunctionBarModeEnum]; + /** - * + * * @export * @interface UpdateAffiliateEmberRefferalShareRequest */ diff --git a/ts/sdk/src/docs/UpdateAccountPreferenceRequest.md b/ts/sdk/src/docs/UpdateAccountPreferenceRequest.md index d9a0536c..70a3b4c4 100644 --- a/ts/sdk/src/docs/UpdateAccountPreferenceRequest.md +++ b/ts/sdk/src/docs/UpdateAccountPreferenceRequest.md @@ -5,9 +5,10 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**language** | **string** | User preferred language. | [optional] [default to undefined] -**theme** | **string** | User preferred theme. | [optional] [default to undefined] -**market** | [**Array<AccountMarketPreference>**](AccountMarketPreference.md) | | [optional] [default to undefined] +**favorites** | **Array<string>** | Favorite market symbols. Send the full array each time; to remove a favorite, send the array without it. | [optional] [default to undefined] +**functionBarMode** | **string** | Function bar display mode. Note the mixed casing: `all` is lowercase while `Popular` and `Favorites` are capitalized. | [optional] [default to undefined] +**onboardingCompleted** | **boolean** | Whether the user has completed onboarding. | [optional] [default to undefined] +**termsAccepted** | **boolean** | Whether the user has accepted the terms of service. | [optional] [default to undefined] ## Example @@ -15,9 +16,10 @@ Name | Type | Description | Notes import { UpdateAccountPreferenceRequest } from '@bluefin/api-client'; const instance: UpdateAccountPreferenceRequest = { - language, - theme, - market, + favorites, + functionBarMode, + onboardingCompleted, + termsAccepted, }; ``` From 1f4bca38f4a1de31218242f51c61d0372aaac50f Mon Sep 17 00:00:00 2001 From: Nida Ali Date: Fri, 3 Jul 2026 14:46:31 -0500 Subject: [PATCH 2/3] Remove CHANGELOG.md (not used elsewhere in the repo) Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index d529bfd6..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,34 +0,0 @@ -# Changelog - -All notable changes to the Bluefin Pro SDK are documented here. - -## [TypeScript 3.0.0 / Python 2.0.0 / Rust 2.0.0] - 2026-07-03 - -### Changed — BREAKING - -- **`PUT /api/v1/account/preferences` write model replaced with a strict allow-list** (BFP-4644). - The `UpdateAccountPreferenceRequest` body was rewritten to match the server's - new allow-list schema (a mass-assignment security fix). The request now accepts - only these optional fields, and any unknown field is rejected by the server with - a `400` naming the offending field: - - `favorites: string[]` — favorite market symbols (e.g. `["BTC-PERP","SUI-PERP"]`). - Full array each time; to remove a favorite, send the array without it. - - `functionBarMode: "all" | "Popular" | "Favorites"` — note the mixed casing - (`all` is lowercase; `Popular` and `Favorites` are capitalized). - - `onboardingCompleted: boolean` - - `termsAccepted: boolean` - - The write is a **full replace** — always send the complete object; the server - overwrites the stored preferences. A successful call returns `204 No Content` - (no response body). - - **Removed from the write model:** `language`, `theme`, and `market`. Any code - still sending these (or any other extra key) through this endpoint will now - receive a `400`. The serializer no longer emits unknown/extra keys. - -### Unchanged - -- `GET /api/v1/account/preferences` (`AccountPreference`) is intentionally left - permissive (`additionalProperties: true`) and still exposes the legacy typed - `language` / `theme` / `market` fields. GET and PUT are deliberately asymmetric: - the new write fields round-trip through GET as untyped extra keys. From 2628568804b0bdc4021be75345a20c0320f5e15f Mon Sep 17 00:00:00 2001 From: Nida Ali Date: Fri, 3 Jul 2026 14:55:25 -0500 Subject: [PATCH 3/3] Use minor version bump for preferences change (ts 2.1.0, py/rust 1.18.0) The preferences write endpoint is effectively only consumed by our own FE and vera, so treat this as a minor bump (matching apigen's convention) rather than a major, despite the strict schema change. Co-Authored-By: Claude Opus 4.8 (1M context) --- python/sdk/config.yaml | 2 +- python/sdk/pyproject.toml | 2 +- rust/Cargo.lock | 4 ++-- rust/Cargo.toml | 4 ++-- rust/gen/bluefin_api/Cargo.toml | 2 +- rust/gen/config.yaml | 2 +- ts/sdk/package.json | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/python/sdk/config.yaml b/python/sdk/config.yaml index c390a1c4..2e1c0555 100644 --- a/python/sdk/config.yaml +++ b/python/sdk/config.yaml @@ -2,4 +2,4 @@ additionalProperties: library: asyncio generateSourceCodeOnly: true useOneOfDiscriminatorLookup: true - packageVersion: 2.0.0 + packageVersion: 1.18.0 diff --git a/python/sdk/pyproject.toml b/python/sdk/pyproject.toml index f5147291..8b96a482 100644 --- a/python/sdk/pyproject.toml +++ b/python/sdk/pyproject.toml @@ -21,7 +21,7 @@ description = "Python Boilerplate contains all the boilerplate you need to creat name = "bluefin_pro_sdk" readme = "README.rst" requires-python = ">=3.9.2,<3.14.0" -version = "2.0.0" +version = "1.18.0" [[project.authors]] email = "audreyr@example.com" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index fa7d1366..f6dcbbd3 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -121,7 +121,7 @@ dependencies = [ [[package]] name = "bluefin-pro" -version = "2.0.0" +version = "1.18.0" dependencies = [ "base64", "bcs", @@ -146,7 +146,7 @@ dependencies = [ [[package]] name = "bluefin_api" -version = "2.0.0" +version = "1.18.0" dependencies = [ "reqwest", "serde", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8d05022a..5230399d 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -9,7 +9,7 @@ sha2 = "0.10.9" [dependencies.bluefin_api] path = "gen/bluefin_api" -version = "2.0.0" +version = "1.18.0" [dependencies.ed25519-dalek] features = ["rand_core"] @@ -93,7 +93,7 @@ keywords = [ license = "Unlicense" name = "bluefin-pro" repository = "https://github.com/fireflyprotocol/pro-sdk" -version = "2.0.0" +version = "1.18.0" [workspace] members = [ diff --git a/rust/gen/bluefin_api/Cargo.toml b/rust/gen/bluefin_api/Cargo.toml index 66a30eee..04bb8098 100644 --- a/rust/gen/bluefin_api/Cargo.toml +++ b/rust/gen/bluefin_api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bluefin_api" -version = "2.0.0" +version = "1.18.0" authors = ["OpenAPI Generator team and contributors"] description = "Bluefin API" license = "MIT OR Apache-2.0" diff --git a/rust/gen/config.yaml b/rust/gen/config.yaml index af0f5d8b..d5e672b1 100644 --- a/rust/gen/config.yaml +++ b/rust/gen/config.yaml @@ -2,5 +2,5 @@ additionalProperties: hideGenerationTimestamp: 'true' avoidBoxedModels: 'true' packageName: bluefin_api - packageVersion: 2.0.0 + packageVersion: 1.18.0 preferUnsignedInt: 'true' diff --git a/ts/sdk/package.json b/ts/sdk/package.json index b51c3388..d8d77dd0 100644 --- a/ts/sdk/package.json +++ b/ts/sdk/package.json @@ -56,5 +56,5 @@ "prepare": "npm run build" }, "types": "./dist/index.d.ts", - "version": "3.0.0" + "version": "2.1.0" }