Every high-level client is bound to a signing-capable account. The constructor
you use decides which operations the account can perform.
Constructor
Source
Transfer / withdraw
Execute
account.fromMnemonic
BIP-39 mnemonic
Yes
Yes
account.fromSeed
64-byte seed
Yes
Yes
account.fromEthereumSignature
EOA personal_sign signature
Yes
Yes
account.fromWallet
Wallet signature (browser)
Yes
Yes
account.fromKeys
Raw keys
Yes
No
execute() needs a seed-backed account, so derive with fromMnemonic,
fromSeed, fromEthereumSignature, or fromWallet. fromKeys can transfer
and withdraw but cannot execute.account.fromMetaMask remains available as a deprecated alias for
account.fromWallet.
The SDK can derive an Unlink account from an EOA personal_sign signature
instead of a mnemonic. The Quickstart shows the one-shot
account.fromWallet wrapper. This section is the lower-level reference.
The message is the single source of truth. Bumping its format is a breaking change for every existing account.
appId is embedded verbatim. The SDK rejects empty strings, LF/CR, or anything over 64 bytes UTF-8 but does not canonicalise casing or whitespace. Use a stable app identifier.
chainId must be a positive integer.
The message uses the literal label Tenant: for address compatibility, even though the parameter is named appId. Do not change the message text, or existing accounts derive differently.
If you control the signing path yourself, build the message, sign it, and pass
the signature into account.fromEthereumSignature with the same appId and
chainId.
The signature is canonicalised internally and expanded with HKDF-SHA256 into the
64-byte seed consumed by account.fromSeed. appId and chainId are bound
into the HKDF salt. Any mismatch derives a fresh, empty account instead of a
silent account swap.Stability across wallets relies on RFC-6979 deterministic ECDSA, which every mainstream wallet uses today. For long-term recovery, prefer the keystore export (account.export(keys)) over re-deriving from a wallet signature each session.
userStorage stores up to two opaque base64 payloads for a generic application
userId. Engine does not interpret the payload. If mode is "encrypted",
encrypt and decrypt client-side before calling Engine.Create the client with the application user id. Dynamic, your own auth, or any
other session provider should sit behind your app’s requireUser() boundary and
only supply this generic id to Engine:
For encrypted objects, store a base64-encoded envelope. The wrapping key is derived
locally by an unlock adapter, such as a passphrase adapter today or a WebAuthn
PRF adapter when available:
Object keys and userId values may contain letters, numbers, ., _, and -,
but cannot be exactly . or ... Each payload is capped at 16,384 base64
characters.
Never derive encryption keys from Dynamic JWTs, Dynamic user ids, email,
wallet addresses, Dynamic signatures, or any other identity-provider value.
For WebAuthn PRF, derive a client-side KEK from the authenticator PRF output
and decrypt locally; Engine must never receive PRF output, KEKs, plaintext
seeds, or decrypted object contents. Use passphrase unlock as the fallback
when PRF support is unavailable.