Fix CaseClauseError in OasisSapphire.get_meta_nonce/3 on RPC errors#8
Fix CaseClauseError in OasisSapphire.get_meta_nonce/3 on RPC errors#8dominicletz wants to merge 1 commit into
Conversation
When the Sapphire RPC node rejects a simulated call with a transient error
such as 'invalid signed simulate call query: base block not found',
oasis_call/2 returns {:error, reason} but get_meta_nonce/3 only matched on
integer nonces and :revert, raising CaseClauseError and crashing callers
like UserMonitor.
Handle {:error, reason} explicitly: log a warning and return the error tuple
so callers can retry on the next block tick. Propagate the same error from
send_transaction/5 meta-transaction sends when nonce lookup fails.
Adds unit tests for meta_nonce_from_call/2 and the send_transaction/5
error path.
There was a problem hiding this comment.
Code Review
This pull request refactors the meta-transaction flow in OasisSapphire to use a with statement for validating the nonce, extracts the nonce parsing logic into meta_nonce_from_call/2 to handle transient RPC errors, and adds unit tests. The feedback suggests adding a catch-all clause in meta_nonce_from_call/2 to prevent potential CaseClauseError crashes from unexpected results, and pattern matching on {:error, reason} directly instead of using elem/2.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| case result do | ||
| nonce when is_integer(nonce) -> | ||
| nonce | ||
|
|
||
| :revert -> | ||
| Logger.warning("Identity contract at #{DiodeClient.Base16.encode(id)} reverted") | ||
| Logger.warning("Identity contract at #{DiodeClient.Base16.encode(identity)} reverted") | ||
| 0 | ||
|
|
||
| {:error, _} = error -> | ||
| Logger.warning( | ||
| "get_meta_nonce: nonce lookup for #{DiodeClient.Base16.encode(identity)} failed: #{inspect(elem(error, 1))}" | ||
| ) | ||
|
|
||
| error | ||
| end |
There was a problem hiding this comment.
To fully prevent CaseClauseError crashes (which this PR aims to address), it is safer to add a catch-all clause to handle any unexpected results (such as nil, which can be returned by decode_result/2). Additionally, we can match {:error, reason} directly to avoid using elem(error, 1).
case result do
nonce when is_integer(nonce) ->
nonce
:revert ->
Logger.warning("Identity contract at #{DiodeClient.Base16.encode(identity)} reverted")
0
{:error, reason} = error ->
Logger.warning(
"get_meta_nonce: nonce lookup for #{DiodeClient.Base16.encode(identity)} failed: #{inspect(reason)}"
)
error
other ->
Logger.warning(
"get_meta_nonce: unexpected result for #{DiodeClient.Base16.encode(identity)}: #{inspect(other)}"
)
{:error, {:unexpected_result, other}}
end
What
Fixes the root cause of
CaseClauseErrorcrashes inDiodeClient.Shell.OasisSapphire.get_meta_nonce/3when the Sapphire RPC node returns a transient error such asinvalid signed simulate call query: base block not found.Related downstream fix: https://github.com/diodechain/ddrive/pulls (ddrive
Model.App.update_nonce/2workaround can be removed once this ships andmix.lockis bumped).Root cause
oasis_call/2can return{:error, reason}when the RPC rejects a simulated call, butget_meta_nonce/3only matched on integer nonces and:revert. The missing clause raisedCaseClauseError, which propagated through callers likeUserMonitor.update_user/2.Change
get_meta_nonce/3: handle{:error, reason}viameta_nonce_from_call/2— log a warning and return{:error, reason}instead of crashing.send_transaction/5(meta-transaction path): usewithso a failed nonce lookup returns{:error, reason}instead of passing a bad value intocreate_meta_transaction/6.Tests
test/oasis_sapphire_test.exscovers:meta_nonce_from_call/2returns integer nonces unchanged:revertreturns0with a warning{:error, reason}with a warning (no raise)send_transaction/5propagates{:error, reason}when nonce lookup fails