NEAR智能合约中u128/Balance类型与JavaScript交互的大数格式问题咨询
I've run into this exact problem before when working with NEAR contracts—balancing easy arithmetic in Rust with precise number handling in JavaScript can be tricky, but there are two solid solutions that let you keep using u128/Balance for storage while returning precise string-formatted numbers to JS.
Option 1: Use a View-Specific DTO (Data Transfer Object)
This approach keeps your core storage struct clean for arithmetic, while creating a separate struct tailored for API responses that converts u128 values to U128 (which serializes to strings automatically).
Step-by-Step Implementation:
- Define a view-only struct that mirrors your
Projectbut swapsu128fields forU128:
#[derive(Serialize, Deserialize, Debug)] #[serde(crate = "near_sdk::serde")] pub struct ProjectView { pub id: u64, pub owner: AccountId, pub name: String, pub description: String, pub supporters: HashMap<AccountId, Supporter>, pub balance: U128, pub goal: U128, pub end_time: u64, pub status: ProjectStatus, pub plan: SupporterPlans, pub level_amounts: HashMap<SupporterType, U128>, pub images: Vec<String>, }
- Update your view method to convert stored
Projectinstances toProjectView:
pub fn get_all_projects(&self) -> Vec<ProjectView> { self.projects.values() .map(|project| ProjectView { id: project.id, owner: project.owner.clone(), name: project.name.clone(), description: project.description.clone(), supporters: project.supporters.clone(), balance: U128(project.balance), goal: U128(project.goal), end_time: project.end_time, status: project.status.clone(), plan: project.plan.clone(), level_amounts: project.level_amounts.iter() .map(|(k, v)| (k.clone(), U128(*v))) .collect(), images: project.images.clone(), }) .collect() }
Pros: Clear separation between storage and API layers; easy to adjust view fields without modifying core state.
Cons: Requires maintaining a parallel struct—you’ll need to update both if your Project schema changes.
Option 2: Custom Serde Serialization (No Extra Structs)
If you want to avoid duplicate structs, you can add serde annotations to your original Project struct to automatically serialize u128 fields as strings.
Step-by-Step Implementation:
- Add custom serialization functions and annotate your
Projectfields:
#[derive(Serialize, Deserialize, BorshDeserialize, BorshSerialize, Debug)] #[serde(crate = "near_sdk::serde")] pub struct Project { pub id: u64, pub owner: AccountId, pub name: String, pub description: String, pub supporters: HashMap<AccountId, Supporter>, #[serde(serialize_with = "u128_to_string")] pub balance: Balance, #[serde(serialize_with = "u128_to_string")] pub goal: Balance, pub end_time: u64, pub status: ProjectStatus, pub plan: SupporterPlans, #[serde(serialize_with = "hashmap_u128_to_string")] pub level_amounts: HashMap<SupporterType, Balance>, pub images: Vec<String>, } // Helper to serialize u128 as a string fn u128_to_string<S>(value: &u128, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { serializer.serialize_str(&value.to_string()) } // Helper to serialize HashMap values as strings fn hashmap_u128_to_string<S>(value: &HashMap<SupporterType, u128>, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { let mapped: HashMap<_, _> = value.iter().map(|(k, v)| (k, v.to_string())).collect(); serde::Serialize::serialize(&mapped, serializer) }
Pros: No extra structs to maintain; keeps your code concise.
Cons: Ties serialization behavior directly to your storage struct—if you ever need to serialize Project with raw u128 values internally, you’ll need to adjust this.
Why This Works
Both solutions leverage NEAR SDK’s serde integration:
U128(or custom string serialization) ensures large numbers are sent to JavaScript as plain strings, avoiding scientific notation and precision loss.- You keep using
u128/Balancefor all internal arithmetic, so no tedious conversions during contract logic.
In JavaScript, you can easily convert these strings to BigInt for calculations (e.g., BigInt(project.balance)), or keep them as strings for display.
内容的提问来源于stack exchange,提问作者user9114012

