@xyo-network/dapp-kit-wallet
v2.3.0
Published
Portable account-bound sign-only wallet capabilities for dApp clients
Readme
dapp-kit-wallet
Neutral, account-bound XL1 sign-only capabilities. This sibling package does not depend on dapp-kit core, browser/Node hosts, React, CLI implementations, or Crypto Cards. Its SDK adapter receives an already selected account/HD wallet; it does not derive a path, open a key store, export a key, publish bodies, or broadcast.
const provider = createSdkWalletProvider({ id: 'selected-sdk-account', account })
const session = await provider.activate({
account: account.address,
chain: { chainId, genesisBlockHash },
})
const signer = createWalletSessionSigner(session, { nextRequestId })
// Supply signer to an independently owned reader/broadcaster and durable writer.
// Await the writer's durable completion/recovery handoff before releasing ownership.
await provider.close()Import these functions from @xyo-network/dapp-kit-wallet. The caller supplies
the verified chain/genesis context. A signature binds its transaction chain ID;
it does not prove the host's genesis assertion. The session refuses mismatched
account/chain requests and validates the signed envelope, cryptographic
signature, payload bodies and all transaction fields except the SDK's specified
signing fields (addresses, previous_hashes, $signatures, _hash,
_dataHash).
Metadata, subscriptions and refresh never activate the adapter. createWalletProvider
accepts either static permitted accounts or an injected stateSource, plus an activation factory. Adapter sessions
must recheck permission/lock/account binding immediately before every dispatch.
createWalletProviderDescriptor registers this capability through actor-kit's
existing provider system; it does not add a resolver or actor lifecycle.
For an SDK gateway that accepts signerFactory, use
createWalletSessionSignerFactory(session, { nextRequestId }). It resolves the
SDK's XyoSignerMoniker using its existing provider factory and locator. It
delegates only XL1 transactions to the bound session; generic sign() fails
with unsupported before dispatch and no JWT signer is exposed. The gateway
receives no raw SDK account. The factory borrows the session: stop/cancellation
detaches its own pending waits and blocks subsequent signing but does not close
the wallet session. Drain the client, then the gateway, then the wallet owner.
Any late verified result remains recoverable from the original wallet session.
Dynamic SDK-backed hosts can return createSdkWalletAdapterSession(account)
from their activation factory after opening their own vault and selecting the
bound account. This helper exposes only the SDK transaction signer, binding
check and local cleanup. It uses the same real SDK signing and hash metadata as
the static provider. Creating, observing or refreshing a provider never opens
that vault. The host retains ownership of the supplied account.
Request IDs bind one canonical unsigned request within an account/chain/genesis scope. Reusing a signed request returns copied retained bytes without signing. Conflicting reuse fails, and duplicate pending calls are rejected. Pending or unknown signing blocks new signing across every chain session for that account within this provider, because SDK signature continuity is account-scoped. External providers/tabs/processes still require owner-level coordination.
Every error after entering a signer is uncertain, including RPC rejection codes such as 4001. Invalid signatures are uncertain too. Pre-dispatch failures are reported as not attempted. Errors expose a fixed safe vocabulary and never carry adapter error text, causes, key objects or transaction payloads.
Close prevents new dispatch and releases adapter resources; abort detaches the
pending request. Neither revokes wallet grants nor proves remote cancellation. A valid late response is
retained under its original request for signingOutcome() recovery even after
the waiting caller has closed. A host must bound adapter teardown and reconcile
its own durable signing intent.
Retention is in memory, bounded to 1024 requests per account by default
(maxRequestsPerScope can set a different bound). Reaching the bound fails
closed; uncertainty is never evicted. This is not a crash-durable journal or a
wallet-side deduplication promise. Production clients must keep their existing
durable signing intent and exact-byte submission journal. This package does not
qualify dapp-kit-browser's separate writable-host durability gate.
The current concrete adapter is the supplied SDK account adapter. Browser vault, Chrome extension and Aries requester-scoped implementations are separate work; their optional revoke, unlock, derivation and remote-outcome capabilities are not implied by this adapter.
Dynamic adapters supply WalletStateSource: an initial WalletObservedState,
an explicit asynchronous refresh(), and an optional native subscribe() that
returns its cleanup function. The adapter host owns native events or polling;
the neutral provider creates no timers or transport. Omit source subscriptions
when unsupported; snapshot().observation.sourceEvents reports that absence.
provider.subscribe() observes local snapshot updates, including refresh and
close, and provider.refresh() only reads current public evidence. Static SDK
providers return their immutable metadata when refreshed.
Snapshots distinguish availability, connection, lock, access/grants, inventory,
network selection and signing support. Inventory is ready or stale with
public accounts, or unknown/unavailable without accounts. The compatibility
accounts view is undefined for missing inventory, so it must not be treated
as a known empty wallet. Only ready inventory and known authority admit
activation/signing. Explicit connection, unlock and key management remain with
their qualified platform host. All optional management, revocation and remote
outcome operations remain explicitly unsupported by this capability.
By default, unknown or locked state blocks activation. An interactive wallet
that atomically enforces the bound account, grants and unlock at signing may
declare signing.lockHandling: 'wallet-managed' together with interaction
required or optional. Its snapshot retains the actual unknown/locked state;
the exception permits a deliberate request to the wallet, not unattended
consent or automatic reveal. Optional interaction means the wallet may prompt
or use its existing grant. Unknown/none interaction and unknown grants,
connection, inventory or signing support do not qualify for the exception.
The source owns monotonically ordered revision and authorityRevision values
within one provider incarnation. Advance authority revision for opaque grant or
backing-session replacement and mutable active-account changes, even if public
inventory is unchanged. A source revision cannot name conflicting observations.
The provider's snapshot revision advances on presentation changes; its
authorityGeneration advances on security-relevant changes. Renaming a public
account or refreshing unchanged state preserves the session. Account-set,
access, lock, connection, network or signing-support changes invalidate existing
sessions and pending activations, without changing their account/genesis binding.
assertBinding is still mandatory immediately before every actual dispatch.
Superseded refreshes, refreshes overtaken by source events, older events and all callbacks after close cannot restore authority. A failed refresh publishes only a safe issue code, preserves available metadata as stale, and blocks signing. Malformed or conflicting observation requires a strictly newer valid source revision before recovery; replaying the last accepted revision cannot clear it. Later valid state can permit a fresh activation but never revives an old session. Provider close unsubscribes once and rejects waiting refresh calls; late remote work may still finish. Invalidation preserves account-wide uncertain outcomes and any valid late signature under its original binding.
Read-only recovery code can import assertWalletRequest,
assertWalletHydratedBodies, validateWalletSignedTransaction and
walletCanonicalJson from the package root. They validate existing SDK wire
evidence without opening a provider, accessing an account or signing. Signed
validation snapshots both inputs before asynchronous verification and checks
the original account, chain, request commitments, actual signatures and carried
body references. It returns SDK signed transaction evidence; the caller still
owns durable retention and previous-hash continuity. Genesis remains an
independent host check.
Direct malformed request/body assertions expose fixed wallet errors. Failure to validate a signed result preserves an unknown outcome; a validation failure does not prove an earlier wallet attempt was unattempted. Never use these helpers to clear a retained intent, infer global latestness or establish broadcast, inclusion or finality. Exact recovery must use the original retained request and binding.
