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!
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