Chainlink Functions source script cannot write through its ethers provider — sign locally and POST eth_sendRawTransaction with fetch
Symptom: inside a Chainlink Functions source script you follow the documented pattern of subclassing ethers.JsonRpcProvider as FunctionsJsonRpcProvider (overriding _send to route through Functions.makeHttpRequest), and read calls work. As soon as you try a state-changing call — contract.increment(), signer.sendTransaction(...) — it fails.
Cause: that custom provider only implements the request shapes the sandbox supports. The Functions runtime has no persistent connection: no filters, no polling, no receipt waiting, so ethers' write path (which internally polls for the transaction and can broadcast through methods the override does not forward) breaks. Only plain single-shot HTTP requests survive.
Workaround — do the transaction plumbing by hand, calling the JSON-RPC methods you need through fetch, and use ethers purely for ABI encoding and local signing:
async function rpcCall(method, params = []) {
const res = await fetch(RPC_URL, { method: "POST",
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }) });
return (await res.json()).result;
}
const data = contract.interface.encodeFunctionData("increment", []);
const tx = { from: signer.address, to: CONTRACT_ADDRESS, data,
nonce: await rpcCall("eth_getTransactionCount", [signer.address, "latest"]),
gasPrice: await rpcCall("eth_gasPrice"),
chainId: await rpcCall("eth_chainId") };
tx.gasLimit = await rpcCall("eth_estimateGas", [tx]);
const txHash = await rpcCall("eth_sendRawTransaction", [await signer.signTransaction(tx)]);
return Functions.encodeString(txHash);Important caveat before you ship this: every node in the DON executes your source independently, so a write inside a Functions request is attempted once per node — expect duplicate broadcasts, nonce races, and non-deterministic results that can break consensus on the returned value. Writing on chain belongs in the consumer contract's fulfillRequest callback; treat the above as a last resort, and keep the private key in encrypted secrets.
Source: adapted from a Stack Exchange answer by SYED ASAD KAZMI (question by Sokrates), https://ethereum.stackexchange.com/questions/162283/error-in-chainlink-functions-source-script-with-ethersjs-for-writing-to-smart-co — CC BY-SA 4.0