Status: live. Every partner-managed vault is deployed by the
platform’s back-office wallet. Partners do not call
deployVault themselves — the on-chain bootstrap is handled
end-to-end by the backend in response to KYC approval
(single_wallet, or multi_wallet with requirePerWalletKyc=true)
or first-request auto-onboarding (multi_wallet with
requirePerWalletKyc=false). Poll GET /api/me/vault or
subscribe to the user_activity WebSocket channel until the
vault address resolves.
What triggers it
There are two trigger paths, picked automatically based on the
partner record:
- KYC approval — single_wallet partners, and multi_wallet
partners with
requirePerWalletKyc=true. The auto-deploy job is
enqueued the moment the EOA’s kyc_status flips to APPROVED
(tier ≥ 1). See
Partner kinds for the
per-flag enforcement matrix.
- First request auto-onboarding — multi_wallet partners with
requirePerWalletKyc=false (KYB-onboarded prime brokerage
model). The first authenticated request from the partner key
that names a previously-unseen sub-account in X-User-Wallet
creates the core.users row and enqueues an auto-deploy job
right then — without retail KYC for the sub-account.
Either way, the on-chain VaultFactory.deployVault call comes from
the back-office submitter, gas is paid by the platform, and the
result is identical: a fully-initialised VaultImplementation
clone with deposit-limit caps registered against your EOA.
Sequence
Typical end-to-end timing on testnet: under 15 seconds from
trigger to deployed=true. Mainnet target is similar; spikes
during high block latency are visible on the
status page.
Reading deployment status
GET /api/me/vault
X-Api-Key: ps_live_<keyId>_<secret> # needs `portfolio:read`
{
"walletAddress": "0x...",
"vaultAddress": "0x...",
"deployed": true,
"deployStatus": "confirmed",
"deployTxHash": "0x...",
"deployRequestedAt": "2026-04-25T12:34:06.247Z",
"deployConfirmedAt": "2026-04-25T12:34:15.256Z"
}
| Field | Meaning |
|---|
vaultAddress | The deterministic vault contract address — populated as soon as the deploy job is enqueued, even if not yet mined. |
deployed | true once VaultFactory.vaultOf(eoa) returns a non-zero address on chain. The off-chain mirror is consistent with the chain within ~1 block. |
deployStatus | See deployStatus values below — seven states tracking the auto-deploy job from enqueue to terminal. |
deployTxHash | Set once the back-office wallet broadcasts the tx. Useful for blockscout cross-reference. |
deployStatus values
| Value | Meaning | Action |
|---|
not_started | No deploy job enqueued yet for this wallet — typically a brand-new EOA before KYC tier 1 clears (single_wallet) or before the first authenticated request lands (multi_wallet with requirePerWalletKyc=false). | Wait for the trigger to fire; if it’s been more than 15 seconds, see What if auto-deploy doesn’t fire below. |
pending | Job enqueued; awaiting wallet-pool slot. | Poll again in a few seconds. |
building | Match-submitter is constructing the on-chain tx. | Poll again in a few seconds. |
submitted | Tx broadcast; awaiting on-chain finality. deployTxHash is now set. | Poll until confirmed, or subscribe to vault.deploy_confirmed on /ws/user. |
confirmed | Tx mined; vaultOf(eoa) returns the vault address. | Proceed to deposit + trade. deployed: true. |
skipped_existing | Wallet already had an on-chain vault before the auto-deploy system existed (legacy users). | Treat as confirmed — deployed: true. |
failed_permanent | Tx reverted; the job will not retry. | Contact support. The most common cause is a misconfigured back-office wallet pool. |
The same status flips on the user_activity WebSocket channel as
vault.deploy_confirmed for partners that prefer push semantics — see
WebSocket subscriptions.
What stays the same
- The on-chain artefact is identical to a manually-deployed vault — same
VaultImplementation clone at the same deterministic address, same
ABI, same vaultOf(eoa) lookup.
- Vault ownership stays with your associated EOA. The back-office
wallet pays gas to deploy but never holds keys to the vault.
- Emergency withdraw, dual-signature withdrawal, splits, merges, and
redeems — all unchanged. Auto-deploy only saves the on-chain bootstrap,
not custody.
Order.maker is your vault address; Order.signer is your EOA — the
EIP-712 domain and signature flow are identical to manual deploy.
What stays your responsibility
approve of USDC to the vault. ERC-20 transferability is owner-only
by design — only the EOA that holds USDC can authorise the vault to
pull it. The platform cannot bypass that.
depositERC20 from your EOA. Auto-deploy stops at the vault
contract; the actual USDC transfer is a separate user-side tx (or via
your wallet provider’s permit flow if it supports EIP-2612).
- Custody of the EOA private key — never reaches our infrastructure.
- Gas for trading-side txes (split / merge / convert / withdraw /
cancel on-chain) is paid by your EOA. Auto-deploy only covers the
deploy bootstrap.
Limits initialisation
Per-EOA deposit caps in DepositLimitRegistry (daily / weekly / monthly)
are initialised by the platform alongside the deploy. By the time
/api/me/vault reports deployed=true, the user is also cleared to
deposit up to their tier-1 caps. See Deposit limits
for the cap shape and how usage windows roll.
Verifying
// Off-chain: poll until the vault deploys
const r = await fetch(`${BASE}/api/me/vault`, {
headers: { 'X-Api-Key': process.env.PS_API_KEY! },
});
const v = await r.json();
if (v.deployed) {
console.log('vault ready:', v.vaultAddress, 'tx:', v.deployTxHash);
}
// Or push: subscribe via WS
ws.send(JSON.stringify({
type: 'subscribe',
channel: 'user_activity',
}));
// → { type: 'event', topic: 'vault.deploy_confirmed', vaultAddress: '0x...', txHash: '0x...' }
What if auto-deploy doesn’t fire
For a partner-issued API key, auto-deploy is the only path —
operations issues orders:write / vault:write keys only after
the prerequisite KYC (or partner-level KYB) lands, so by the time
your code runs the trigger is already in flight.
If GET /api/me/vault keeps returning deployStatus:'not_started'
past the typical 15-second window, the most common causes are:
- KYC not actually approved for the EOA (single_wallet path) — call
your integration manager.
- multi_wallet
requirePerWalletKyc=true and the sub-account hasn’t
been KYC’d. Either complete KYC for that sub-account or ask
operations to flip the partner to requirePerWalletKyc=false if
the prime-brokerage compliance model fits.
failed_permanent on deployStatus — the on-chain tx reverted
and the job will not retry. Contact support; usually means the
back-office wallet pool is degraded.
VaultFactory.deployVault(eoa) is callable on-chain by any address
(the function only stores vaultOwner = eoa and is idempotent), so
in a true emergency a partner can deploy themselves and pay gas —
but operations should be the first call. The contract reference at
Vault contracts → VaultFactory
documents the function signature.