Skip to content

Add headless connectToContract and RPC-visibility polling for OZ smart accounts#151

Merged
christian-rogobete merged 4 commits into
masterfrom
sa-improvements
Jun 28, 2026
Merged

Add headless connectToContract and RPC-visibility polling for OZ smart accounts#151
christian-rogobete merged 4 commits into
masterfrom
sa-improvements

Conversation

@christian-rogobete

Copy link
Copy Markdown
Member

Summary

Two improvements to the OpenZeppelin smart-account kit:

  • A headless connectToContract that connects a smart account by its contract address alone, with no passkey credential.
  • Wallet creation and contract connection now poll the Soroban RPC for visibility instead of waiting a fixed delay.

Headless connectToContract

connectToContract(contractId) connects to an existing OZ smart account by its contract address, performing no WebAuthn ceremony and persisting no session.

The motivating use case is the agent-signer flow: an autonomous agent is granted a scoped, spend-capped Ed25519 signing authority on a smart account (an external-signer context rule) but does not own the account's passkey. Headless connect lets such an agent — or any backend service that signs through the multi-signer / external-signer path — connect by contract address alone and act as the delegated signer. A headless connection holds no passkey credential, so the single-passkey operations (submit, transfer, contractCall, …) reject it; calls must use the multi-signer path with an explicit signer.

New public surface:

  • OZWalletOperations.connectToContractOZConnectToContractResult
  • OZSmartAccountEventHeadlessConnected — emitted on a headless connect, carrying only the contract address
  • OZSmartAccountKit.isHeadless and OZConnectedState.isHeadless

RPC-visibility polling

Wallet creation (funding-account visibility) and contract connection (contract-instance visibility) previously waited a fixed delay before continuing, which could be too short on a slow RPC and needlessly long otherwise. Both now poll the Soroban RPC until the entry is visible, up to a bounded timeout, improving reliability.

Breaking change

OZConnectedState.credentialId is now nullable (StringString?). A null value indicates a headless connection. Code that reads credentialId as a non-null String must handle null; use OZConnectedState.isHeadless (or OZSmartAccountKit.isHeadless) to distinguish a headless connection from a passkey one.

…d delay

The smart-account auto-fund path funded a throwaway account via Friendbot, then
waited a fixed 5 seconds before simulating that account's balance on the Soroban
RPC. When testnet propagation exceeds that window the simulation fails with
"account entry is missing". Replace the fixed delay with a bounded poll of
SorobanServer.getAccount (1.5s interval, 45s timeout, cooperatively cancellable)
that proceeds as soon as the account is visible and otherwise fails with a clear
timeout error.
…xed delay

After the deploy transaction confirms, the deployed smart-account contract
instance may not yet be visible to the Soroban RPC simulation endpoint, so the
auto-fund flow waited a fixed 5 seconds before simulating against it. When
testnet propagation exceeds that window, funding fails with "account entry is
missing". Replace both fixed waits (the createWallet and deployPendingCredential
auto-fund paths) with a bounded poll of the contract's instance ledger entry
(1.5s interval, 45s timeout, cooperatively cancellable, clear timeout error).

The shared visibility constants are renamed from friendbot- to rpc-prefixed
since both the account and contract waits use them, and both visibility-wait
timeouts now raise the dedicated SmartAccountTransactionTimeout category rather
than the submission-failed category.
Connects the kit to a deployed smart account by contract address alone — no
passkey credential, no WebAuthn ceremony, no session — for autonomous signers
and backends operating through the multi-signer / external-signer pipeline.
The single-passkey submit() path guards on a sentinel credential and fails
fast if misrouted; a dedicated OZSmartAccountEventHeadlessConnected event is
emitted.
- Represent a headless connection with a null credentialId and a new
  isHeadless discriminator; OZConnectedState.credentialId is now
  nullable and isConnected is gated on the bound contract address.
- Narrow the RPC visibility-poll catches to Exception so programmer
  errors propagate, and correct the timeout-cause documentation.
- Cover headless connect with tests (cancellation, best-effort session
  clear, event equality, and the real kit's connection-state API), and
  make the test fixture's disconnect emit for any contract-bound state.
- Add a CHANGELOG entry for the new API and the nullability change.
@codecov

codecov Bot commented Jun 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.91525% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.10%. Comparing base (3a6ed43) to head (f0ced95).

Files with missing lines Patch % Lines
...src/smartaccount/oz/oz_transaction_operations.dart 85.71% 2 Missing ⚠️
lib/src/smartaccount/oz/oz_wallet_operations.dart 96.66% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #151      +/-   ##
==========================================
+ Coverage   90.02%   90.10%   +0.07%     
==========================================
  Files         665      665              
  Lines       33825    33880      +55     
==========================================
+ Hits        30452    30528      +76     
+ Misses       3373     3352      -21     
Files with missing lines Coverage Δ
...b/src/smartaccount/oz/oz_smart_account_events.dart 99.21% <100.00%> (+0.05%) ⬆️
lib/src/smartaccount/oz/oz_smart_account_kit.dart 100.00% <100.00%> (ø)
...ib/src/smartaccount/oz/oz_smart_account_types.dart 94.66% <100.00%> (+0.07%) ⬆️
lib/src/smartaccount/oz/oz_wallet_operations.dart 81.18% <96.66%> (+3.88%) ⬆️
...src/smartaccount/oz/oz_transaction_operations.dart 62.06% <85.71%> (+2.54%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@christian-rogobete christian-rogobete merged commit 10dbfb5 into master Jun 28, 2026
5 checks passed
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.

1 participant