If `[u8; 16]` works for you, then you can trick Anchor into treating it as a `u128` by creating a custom type with the same name, `"u128"`. To not pollute the outer scope, you declare it in a module. Like so: ``` mod custom_scope { use anchor_lang::prelude::*; #[derive(Copy, Clone, bytemuck::Zeroable, bytemuck::Pod, Debug)] #[repr(C)] pub struct u128(pub [u8; 16]); impl u128 { pub fn as_u128(&self) -> core::primitive::u128 { core::primitive::u128::from_le_bytes(self.0) } } #[account(zero_copy)] #[repr(C)] pub struct SomeStruct { pub authority: Pubkey, pub some_value: u128, // anchor will think this `u128` is a primitive and not a custom type pub data: u64, } } ``` It is key to also declare `SomeStruct` in the same scope so that we can do `pub some_value: u128` because this is what tricks Anchor into treating it as a regular `u128` and not a custom type. And now, when your client sends data as `u128` you can simply cast it into the type the account expects, with `custom_scope::u128(some_value.to_le_bytes())`. Or if you want your `my_account.some_value` to act as a `u128`, you can simply do `my_account.some_value.as_u128()`. Here's the demo: https://beta.solpg.io/65184223fb53fa325bfd0bc9 Check the test file and notice it treats the `someValue` field in the account as `BN` without additional conversion. And in the IDL that the above link generates during build, it will show ``` { "name": "someValue", "type": "u128" } ```