{"record":{"id":"0b83f6be410297fd","repo":"linera-io/linera-protocol","slug":"it-is-illegal-to-call-function-instantiate-from-an","errorCode":null,"errorMessage":"It is illegal to call function instantiate from an operation","messagePattern":"It is illegal to call function instantiate from an operation","errorType":"validation","errorClass":"EvmExecutionError","httpStatus":null,"severity":"error","filePath":"linera-execution/src/evm/inputs.rs","lineNumber":181,"sourceCode":"\n/// This is the selector of `instantiate` that should be called\n/// only when creating a new instance of a shared contract\npub(crate) const INSTANTIATE_SELECTOR: &[u8] = &[156, 163, 60, 158];\n\npub(crate) fn forbid_execute_operation_origin(vec: &[u8]) -> Result<(), EvmExecutionError> {\n    ensure!(\n        vec != EXECUTE_MESSAGE_SELECTOR,\n        EvmExecutionError::IllegalOperationCall(\"function execute_message\".to_string(),)\n    );\n    ensure!(\n        vec != PROCESS_STREAMS_SELECTOR,\n        EvmExecutionError::IllegalOperationCall(\"function process_streams\".to_string(),)\n    );\n    ensure!(\n        vec != SUMMARIZE_EVENTS_SELECTOR,\n        EvmExecutionError::IllegalOperationCall(\"function summarize_events\".to_string(),)\n    );\n    ensure!(\n        vec != INSTANTIATE_SELECTOR,\n        EvmExecutionError::IllegalOperationCall(\"function instantiate\".to_string(),)\n    );\n    Ok(())\n}\n\npub(crate) fn ensure_message_length(\n    actual_length: usize,\n    min_length: usize,\n) -> Result<(), EvmExecutionError> {\n    ensure!(\n        actual_length >= min_length,\n        EvmExecutionError::OperationIsTooShort\n    );\n    Ok(())\n}\n\npub(crate) fn ensure_selector_presence(","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-execution/src/evm/inputs.rs#L163-L199","documentation":"Linera's EVM integration reserves a set of base-contract entry points (execute_message, process_streams, summarize_events, instantiate) for system-initiated calls only. Whenever an EVM application processes a user operation or an instantiation transaction, forbid_execute_operation_origin compares the first 4 bytes of the input against those reserved ABI selectors. An operation whose calldata starts with the instantiate(bytes) selector ([156,163,60,158]) is rejected with IllegalOperationCall, because instantiate is only reachable through the shared-contract creation flow.","triggerScenarios":"Submitting a user operation (or init transact payload) whose first 4 bytes equal a reserved selector: execute_message(bytes)=[173,125,234,205], process_streams(...), summarize_events(...), or instantiate(bytes)=[156,163,60,158]. Typically happens when the operation payload is built with alloy's instantiateCall abi_encode, or when a ported contract exposes a function whose signature collides with instantiate(bytes).","commonSituations":"Porting an Ethereum contract that happens to declare instantiate(bytes); forwarding raw user intent into the application without a dispatcher function; hand-crafting operation payloads instead of using ABI-generated wrappers; renaming entry points during an SDK upgrade and reintroducing a collision.","solutions":["Rename the contract function or change its parameter list so its 4-byte selector no longer collides with instantiate(bytes), execute_message(bytes), process_streams(...), or summarize_events(...)","Route user operations through a non-reserved dispatcher entry point (e.g. perform_operation(bytes)) and submit that as the operation","If you actually want shared-contract instantiation, use the application-creation/instantiate flow instead of a plain operation","Print the first 4 bytes of your operation payload and compare them against the reserved selectors declared in linera-execution/src/evm/inputs.rs:150-166"],"exampleFix":"// before: operation payload collides with the reserved instantiate(bytes) selector\nlet op = instantiateCall { value: args }.abi_encode(); // starts with [156,163,60,158]\nclient.submit_operation(app_id, op).await?;\n\n// after: dispatch through your own entry point\nlet op = performOperationCall { args: args }.abi_encode(); // distinct selector\nclient.submit_operation(app_id, op).await?;","handlingStrategy":"validation","validationCode":"// Before submitting an EVM operation, reject reserved selectors (Rust client)\nconst RESERVED: [&[u8]; 4] = &[\n    &[173, 125, 234, 205], // execute_message(bytes)\n    &[156, 163, 60, 158],  // instantiate(bytes)\n    // fill process_streams / summarize_events selectors from the SDK constants\n];\nfn is_reserved_selector(op: &[u8]) -> bool {\n    op.len() >= 4 && RESERVED.iter().any(|s| *s == &op[..4])\n}\nfn submit(app_id: ApplicationId, op: Vec<u8>) -> Result<(), String> {\n    if is_reserved_selector(&op) { return Err(\"operation uses a reserved selector\".into()); }\n    do_submit(app_id, op)\n}","typeGuard":"fn is_illegal_operation_call(err: &ExecutionError) -> bool {\n    matches!(\n        err,\n        ExecutionError::EvmError(EvmExecutionError::IllegalOperationCall(_))\n    )\n}","tryCatchPattern":"match client.submit_operation(app_id, op).await {\n    Ok(out) => out,\n    Err(ref e) if is_illegal_operation_call(e) => {\n        // recover: rewrite the operation to use a non-reserved entry point\n        retry_with_dispatcher_entrypoint(op)\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Generate operation payloads only from your own contract ABI, never from the base-contract ABI","Keep a unit test that asserts every operation your client sends has a selector not in the reserved list","Document which entry points your application exposes for operations versus system calls"],"tags":["evm","solidity","abi-selector","entry-point","linera"],"backgroundTag":"reserved-function-call","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}