Skip to content

No revert reason available when a privacy transaction fails at the paymaster (third-party anonymizer) #240

Description

@RaYYeR220

What I'm trying to do

I'm building Cordon (registered in #237, repo https://github.com/RaYYeR220/cordon) — a credential and policy gate for shielded value. It's an anonymizer contract: the pool calls privacy_invoke on it, it checks a credential and a published policy against the value passing through, and it panics if the payment breaks the rule so the whole transaction reverts.

The contracts are on mainnet and the wiring is readable:

PolicyGate          0x061c734fe518f4c1a0e46d3d2a35b4ff1ab0df17dec510cff401d25e67dfc6b2
IssuerRegistry      0x001cc4f14b4af4f7b1d7a6b973fbe968513abf1c94a3e9602c7fdd14e3d3aae7
RevocationRegistry  0x035cc9e0dd4767aa259d6d7a6c6c10cb58fc97acdc0b45b7541807329a655c6d
PolicyRegistry      0x01b0cf177a70f390af44dc706e7867fa5d0be8920c14d23d4230955a027ab9ee

What happens

Composing a payment in my app and signing it in Ready X, the wallet prepares fine and then fails at submission. The wallet's own console shows it getting all the way to submitting:

[PrivacyService] preparing privacy transaction   latestBlock: 14050731, lastPrivacyTxBlock: 13980425
[PrivacyService] submitting privacy transaction  provingBlockId: 14050721

and then PaymasterV2Error: Paymaster error 156: An error occurred (TRANSACTION_EXECUTION_ERROR).

The action array is the documented three-action shape — withdraw to the gate, transfer with "OPEN", then invoke — and the wallet substitutes ${poolAddress} and ${openNoteIds[0]} itself.

The actual problem: there is no revert reason anywhere

Nothing reaches a block, so there's no receipt to read. I scanned 40 blocks of starknet_getBlockWithReceipts looking for a reverted transaction mentioning the gate or a CORDON_ panic code and found none. The paymaster reports TRANSACTION_EXECUTION_ERROR without the reason, so I can't tell whether my contract panicked and which check did it, or whether the transaction never got that far.

That's what I'm stuck on. Not "it doesn't work" — I can't see why it doesn't work.

What I've ruled out

The gate's state is clean, all read off mainnet just now:

  • accounted_balance(STRK) = 0, and its STRK balance is 0
  • allowance(gate → pool) = 0, so it isn't tripping its own stale-allowance guard
  • the pool is not paused
  • is_open_note_depositor_blocked(gate) = 0
  • privacy_invoke is present on the deployed class with the expected signature, and privacy_pool() reads back as the real pool

Third-party anonymizers clearly do work: the pool emitted 30 ExternalContractInvoked events across 6 distinct anonymizer contracts in the last 100k blocks, so this isn't a blanket restriction.

I also tried submitting through executeWithProof after strk20PrepareInvoke, to bind the resolved note id into what the subject signs. Ready rejects that payload outright with INVALID_REQUEST_PAYLOAD (114) before signing anything — same family as #190. Falling back to strk20InvokeTransaction with the same actions gets me to the paymaster error above.

What would help

  1. Is there a way to get the revert reason out of a paymaster-simulated privacy transaction? Even a way to reproduce the simulation locally would do. Right now a failing transaction is completely opaque to the person who wrote the contract it failed in.
  2. Is there any restriction on which anonymizer contracts the wallet's paymaster route will execute — an allowlist, a size or calldata limit, a gas ceiling on the invoke phase? If so it isn't documented anywhere I could find, and it would explain this exactly.
  3. Failing both, the mainnet proving service URL (SDK route on mainnet: ContractDiscoveryProvider not exported, and a proving-service request #121, mainnet proving service url #124, Mainnet proving service URL for the SDK route (Mirage) #135, Privacy SDK route — proving service access for mainnet (and Sepolia) #147, Mainnet proving service access for Private Poker (RFP-03) #158, Mainnet proving for dapps that call their own contract: the Wallet API route cannot express compute_and_invoke #204) would let me run the SDK route and read the failure myself instead of asking.

Happy to hand over anything useful — the action array, the calldata, a transaction I can get to land. The contracts, the SDK and a rehearsal script that reads all of this off mainnet are in the repo if it's easier to reproduce than describe.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions