Skip to content

Fix CaseClauseError in OasisSapphire.get_meta_nonce/3 on RPC errors#8

Open
dominicletz wants to merge 1 commit into
masterfrom
cursor/fix-oasis-get-meta-nonce-error-30de
Open

Fix CaseClauseError in OasisSapphire.get_meta_nonce/3 on RPC errors#8
dominicletz wants to merge 1 commit into
masterfrom
cursor/fix-oasis-get-meta-nonce-error-30de

Conversation

@dominicletz

Copy link
Copy Markdown
Member

What

Fixes the root cause of CaseClauseError crashes in DiodeClient.Shell.OasisSapphire.get_meta_nonce/3 when the Sapphire RPC node returns a transient error such as invalid signed simulate call query: base block not found.

Related downstream fix: https://github.com/diodechain/ddrive/pulls (ddrive Model.App.update_nonce/2 workaround can be removed once this ships and mix.lock is bumped).

Root cause

oasis_call/2 can return {:error, reason} when the RPC rejects a simulated call, but get_meta_nonce/3 only matched on integer nonces and :revert. The missing clause raised CaseClauseError, which propagated through callers like UserMonitor.update_user/2.

Change

  • get_meta_nonce/3: handle {:error, reason} via meta_nonce_from_call/2 — log a warning and return {:error, reason} instead of crashing.
  • send_transaction/5 (meta-transaction path): use with so a failed nonce lookup returns {:error, reason} instead of passing a bad value into create_meta_transaction/6.

Tests

test/oasis_sapphire_test.exs covers:

  1. meta_nonce_from_call/2 returns integer nonces unchanged
  2. :revert returns 0 with a warning
  3. The exact RPC error from production reports returns {:error, reason} with a warning (no raise)
  4. send_transaction/5 propagates {:error, reason} when nonce lookup fails
$ mix test test/oasis_sapphire_test.exs
4 tests, 0 failures

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +89 to 103
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants