Squid for Apps
initWaaPSquid returns one container of chain facades backed by
Squid dWallets. The facades you select share a single wallet iframe
and a single authenticated session, so a user logs in once and can sign on every
chain you asked for.
Quick start
import { initWaaPSquid, WAAP_EVENTS } from '@human.tech/waap-sdk'
// `chains` is required and must name at least one facade.
const waap = initWaaPSquid({
chains: ['evm', 'sui']
})
// 1. Authenticate. Safe to call on startup: it checks the existing session
// first and only opens the login screen when there isn't one.
await waap.session.login()
// 2. Create or restore the Squid dWallets. This step is explicit.
await waap.squid.onboard()
// 3. Sign.
const [address] = (await waap.evm.request({
method: 'eth_requestAccounts'
})) as string[]
// Release the facade when the owning UI unmounts.
waap.destroy()Onboarding is explicit and must happen after login. Connecting or signing
through a Squid facade before onboard() will fail. Squid never provisions
dWallets implicitly. This is deliberate: creating a dWallet is an on-chain
operation, not a side effect of rendering a Connect button.
Selecting chains
chains is required and must name at least one facade. Only the chains you
name are present on the returned object, and the type reflects that. Reading an
unnamed facade is a compile error, not a runtime undefined.
const waap = initWaaPSquid({
chains: ['evm', 'solana']
})
waap.evm // available
waap.solana // available
waap.sui // compile error — not selectedNaming the facades is deliberate rather than defaulting to all three: the old
default loaded the Sui and Solana surfaces for callers that only ever used EVM.
chains scopes which SDK surfaces load, not the dual-curve provisioning
transaction, which always covers both curves.
The facade
| Member | Purpose |
|---|---|
evm / sui / solana | Chain facades, present according to chains. ethereum is an alias for evm. |
squid.onboard() | Create or restore the Squid dWallets for both curves. |
squid.getStatus() | Read the current lifecycle without triggering onboarding. |
squid.onboardWithStatus() | Onboard and return the immediately observable status. |
session | Authentication, status, and lifecycle events. |
getAccountStatus() | Combined local status for the initializer. |
preload() | Mount the iframe and finish its handshake without opening wallet UI. |
destroy() | Release every facade and the shared iframe. |
waap.onboard() is a shorthand for waap.squid.onboard().
waap.ethereum is an alias for waap.evm and is not going away. It is what
the published integrations use. Prefer evm: it matches the chains value
that produced the facade, and the facade serves every EVM chain rather than
Ethereum specifically.
waap.auth is different. A deprecated alias for the session surface
that exists only when all three chains are selected. Use waap.session.
Every facade shares one authentication lifecycle, so waap.evm.login(),
waap.sui.login(), waap.solana.login(), and waap.session.login() are the
same call. session.login() is the chain-neutral spelling and the one to reach
for, but a facade call is not broken.
squid.getStatus() reports absent, provisioning, awaiting-network,
active, or failed.
Tracking the lifecycle
Onboarding is asynchronous and involves an on-chain operation, so surface its progress rather than blocking your UI on it.
import { WAAP_EVENTS } from '@human.tech/waap-sdk'
waap.session.on(WAAP_EVENTS.squidPending, () => showSpinner())
waap.session.on(WAAP_EVENTS.squidReady, () => enableSigning())
waap.session.on(WAAP_EVENTS.squidFailed, ({ message }) => showError(message))| Constant | Event name |
|---|---|
WAAP_EVENTS.squidPending | waap_squid_pending |
WAAP_EVENTS.squidReady | waap_squid_ready |
WAAP_EVENTS.squidFailed | waap_squid_failed |
getSquidStatus() returns the same state synchronously:
const { state, operationId, transactionDigest, message } = waap.getSquidStatus()operationId and transactionDigest identify the provisioning operation and
are safe to log or display.
In React
import { initWaaPSquid, useWaapAuth } from '@human.tech/waap-sdk'
const waap = initWaaPSquid({
chains: ['evm', 'sui', 'solana']
})
function SignButton() {
const auth = useWaapAuth(waap.session)
return (
<button disabled={!auth.squidReady} onClick={send}>
{auth.squidReady ? 'Send' : 'Preparing wallet…'}
</button>
)
}useWaapAuth(waap.session) exposes squidStatus plus the derived
isConnected, isPending, squidReady, error, address, suiAddress, and
solanaAddress fields. squidReady is true once the lifecycle reaches
active.
Startup performance
Initialize the facade once for the lifetime of the embedding UI. Do not create
and destroy it per Connect click. Squid coalesces the selected facades’ setup into
one iframe request, so preload() warms all of them together:
import { initWaaPSquid, preloadWaaPOnIdle } from '@human.tech/waap-sdk'
const waap = initWaaPSquid({
chains: ['evm', 'sui', 'solana'],
deferIframe: true
})
const cancelPreload = preloadWaaPOnIdle(waap)
// UI teardown
cancelPreload()
waap.destroy()preload() never opens wallet UI and never creates a Squid account. It only
completes the non-interactive handshake. Call it after your page’s LCP.
What stays the same
Squid changes where key material lives, not how requests are reviewed. Every
Squid transaction goes through the same authorization gate as standard mode: exact chain
bytes are verified before any decision, the Policy Engine authorizes against the
user’s limits, and a single-use approval names one transaction.
Privileges work in Squid mode via withPT: true, exactly
as they do in standard mode. On eth_sendTransaction / eth_signTransaction for EVM,
signTransaction / signAndExecuteTransaction for Sui, and signTransaction /
signAndSendTransaction for Solana. It is ignored on message signing.
Related
- Squid Mode overview: what Squid is and when to choose it
- Squid for Agents: the headless
waap-clipath - Quick Start: the standard-mode integration
- Privileges: scoped, time-bounded autonomy