8

I cannot seem to setup a RPC call that accepts a u128 param.

I can input the following

let u128Input = new BN("10")
let output = await api.rpc.template.testRpc(u128Input);

but get an error when the number is large

let u128Input = new BN("10000000000000000")
let output = await api.rpc.template.testRpc(u128Input);

I get the following error

RPC-CORE: testRpc(amount: Balance): Balance:: -32602: Invalid params: invalid type: string "0x0000000000000000002386f26fc10000", expected u128.
RpcError: -32602: Invalid params: invalid type: string "0x0000000000000000002386f26fc10000", expected u128.

My runtime-api/src/lib.rs file looks like

sp_api::decl_runtime_apis! {
    pub trait TemplateApi<Balance> where
        Balance: Codec + MaybeDisplay,
    {
        fn test_rpc(amount: Balance) -> Result<u64, DispatchError>;
    }
}

What should I change to allow u128 inputs?

Update

NumberOrHex was exactly what I needed 🙌

I updated the rpc/runtime-api/src/lib.rs to look like

sp_api::decl_runtime_apis! {
    pub trait TemplateApi<Balance> where
        Balance: Codec,
    {
        fn test_rpc(amount: Balance) -> Result<Balance, DispatchError>;
    }
}

I updated the rpc/src/lib.rs to look like

#[rpc]
pub trait TemplateApi<BlockHash, Balance>
where
    Balance: Codec + Copy + TryFrom<NumberOrHex>,
{
    #[rpc(name = "template_testRpc")]
    fn test_rpc(&self, amount: NumberOrHex, at: Option<BlockHash>) -> Result<NumberOrHex>;
}

Also the impl had to be updated and added a custom serialization and deserialization

impl<C, Block, Balance> TemplateApi<<Block as BlockT>::Hash, Balance> for Api<C, Block>
where
    Block: BlockT,
    C: Send + Sync + 'static,
    C: ProvideRuntimeApi<Block>,
    C: HeaderBackend<Block>,
    C::Api: TemplateRuntimeApi<Block, Balance>,
    Balance: Codec + Copy + TryFrom<NumberOrHex> + Into<NumberOrHex> + std::fmt::Display,
{
    fn test_rpc(
        &self,
        amount: NumberOrHex,
        at: Option<<Block as BlockT>::Hash>,
    ) -> Result<NumberOrHex> {
        let api = self.client.runtime_api();
        let at: BlockId<Block> = BlockId::hash(at.unwrap_or(self.client.info().best_hash));

        let try_into_rpc_balance = |value: Balance| {
            value.try_into().map_err(|_| RpcError {
                code: ErrorCode::InvalidParams,
                message: format!("{} doesn't fit in NumberOrHex representation", value),
                data: None,
            })
        };

        let res = api
            .test_rpc(&at, decode_hex(amount, "balance")?)
            .map_err(runtime_error_into_rpc_error)?
            .map_err(test_rpc_error)?;

        try_into_rpc_balance(res)
    }
}

...

fn decode_hex<H: std::fmt::Debug + Copy, T: TryFrom<H>>(from: H, name: &str) -> Result<T> {
    from.try_into().map_err(|_| RpcError {
        code: ErrorCode::InvalidParams,
        message: format!("{:?} does not fit into the {} type", from, name),
        data: None,
    })
}
1
  • Can you try converting to a byte array (u8a) before you send it into the RPC?
    – Shawn Tabrizi
    Mar 31, 2022 at 21:52

1 Answer 1

8

For the RPCs, I believe NumberOrHex is what you are after. Here is an example from the contracts RPC, where both the value & gas_limit are defined as this type.

I believe it is possibly something like (untested and I'm certainly not a Rust dev...) -

use sp_rpc::number::NumberOrHex;

...

pub trait TemplateApi<Balance> where
        Balance: Codec + Copy + TryFrom<NumberOrHex>,
    {
        fn test_rpc(amount: Balance) -> Result<u64, DispatchError>;
    }
1
  • this was super helpful thanks Jaco!
    – drbh
    Apr 8, 2022 at 14:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.