Search code examples
nearprotocol

Can view methods be used for cross contract calls? (Getting error HostError(ProhibitedInView { method_name: "promise_batch_create"})


I've been working on cross contract calls and simulation testing. I gnereally have things working when I use function calls but I'm seeing a wasm execution error "HostError(ProhibitedInView { method_name: "promise_batch_create") when trying to use a 'view' to call a method that generates a cross contract call to get its information.

contract1 has a method (indirect_num_entries) that uses a cross contract call to a method (num_entries) in contract2 that returns acount (with no modifications)

const contract1 = new nearAPI.Contract(
  account1, // the account object that is connecting
  "example-contract.testnet",
  {
    // name of contract you're connecting to
    viewMethods: ["indirect_num_entries"], // view methods do not change state but usually return a value
    changeMethods: ["indirect_add_entry"], // change methods modify state
    sender: account, // account object to initialize and sign transactions.
  }
);

and for contract2

const contract2 = new nearAPI.Contract(
  account2, // the account object that is connecting
  "example-contract.testnet",
  {
    // name of contract you're connecting to
    viewMethods: ["num_entries"], // view methods do not change state but usually return a value
    changeMethods: ["add_entry"], // change methods modify state
    sender: account, // account object to initialize and sign transactions.
  }
);

All of that works correctly via the Javascript SDK for both adding and getting the number of entries.

However I ran into a problem while trying to create a simulation test and found that trying to use view on indirect_num_entries caused an error:

let x : u64 =  view!(contract1.indirect_num_entries()).unwrap_json();  //error

caused the following runtime error:

An error occurred
Error: Querying [object Object] failed: wasm execution failed with error: FunctionCallError(HostError(ProhibitedInView { method_name: "promise_batch_create" })).

Playing around a bit I found that replacing the view! with a call! worked -- i.e.

let x : u64 =  call!(root, contract1.indirect_num_entries()).unwrap_json();  // works

I also found that if I use the near-cli I see the same behavior i.e.

near call contract1 indirect_num_entries "{}" --accountI contract1 (works)
near view contract1 indirect_num_entries                        (errors)

whereas for views directly to the contract2 work just fine

near view contract2 num_entries  (works)
let x : u64 =  view!(contract2.num_entries()).unwrap_json();  // works

Is this as expected and view is only valid for methods that access (not modify) the local contract data and shouldn't be used for methods that issue a cross contract call? (Or am I doing something incorrectly?)

I can certainly imagine that there could be reasons for needing to use a call (e.g. perhaps the request for a cross contract call needs to be signed) but I don't recall anything/haven't found anything that seems to describes this. Is there a description or a explanation somewhere?

Thanks!


Solution

  • This article explains the issue https://docs.near.org/docs/develop/contracts/as/intro#view-and-change-functions

    view calls do not provide the same context as a change call

    the reason is because view calls are unsigned. no sender has signed a transaction to make the call. this is useful, not requiring a signature, because some use cases are intended for casual browsing or frequent reading of data like browsing an NFT collection or rendering a dashboard

    change calls require a signed transaction so they include context like sender and other details

    cross-contract calls require gas and this gas should be charged to some account. without a signed transaction (in a view call) then there is no one to charge for this gas

    this might make you wonder: "but don't view calls cost gas too?" ... yes, they do, and today the RPC node providers are subsidizing these calls but that may change in the future