Errors
All custom errors that VaultRegistrar may revert with, plus the relevant inherited errors from OpenZeppelin. Source: bc-vault-registrar/contracts/Errors.sol and the OZ base contracts.
Quick reference table
| Selector | Error | Where it's thrown | Recovery |
|---|---|---|---|
0x0819bdcd | SignatureExpired() | registerVault | Re-sign with a fresh deadline |
0xac94b822 | InvalidInvestorSignature() | registerVault | Confirm operator, token, nonce, chainId, verifyingContract all match |
0xfed39497 | InvestorNotFound(address wallet) | registerVault | Drip from faucet (auto-registers) or have admin add to registry |
0x38bfcc16 | VaultAlreadyRegistered(address vault) | registerVault | Idempotent — treat as already-done |
0x8df63830 | VaultBelongsToDifferentInvestor(address vault, string vaultInvestorId) | registerVault | Use a different vault address |
0xe6c4247b | InvalidAddress() | registerVault, addOperator, removeOperator, initialize | Pass non-zero addresses |
0xd6234725 | NotImplemented() | unregisterVault | The function is intentionally not available; redesign off-ramp |
0x308e16dd | NotAnOperator(address account) | declared on invalidateOperatorPermission; not enforced today | Have admin grant OPERATOR_ROLE |
Selector verification. The selectors above are the keccak-256 first-4-byte hashes of the canonical error signatures (e.g.
keccak256("SignatureExpired()")[:4] == 0x0819bdcd). Always verify against the deployed bytecode if you're catching them in client code; the canonical signatures are inbc-vault-registrar/contracts/Errors.sol.
SignatureExpired()
error SignatureExpired();
Thrown by registerVault when block.timestamp > deadline. The contract checks the deadline before doing any signature recovery, so this is the cheapest possible failure mode.
Recovery. The investor must produce a new signature with a deadline in the future. Do not retry the same signature — it will keep failing.
InvalidInvestorSignature()
error InvalidInvestorSignature();
Thrown by registerVault when SignatureChecker.isValidSignatureNow(investor, digest, signature) returns false.
Common causes (in rough order of frequency)
- The investor signed against the wrong
operatorfield. Submitting through the gateway? The signature must encode the gateway's operator wallet, not your factory. - The investor signed against the wrong
tokenfield. Always readregistrar.token()first; never hard-code. - The investor signed against a stale
nonce. Always readoperatorNonce(investor, operator)immediately before signing. - The investor signed against the wrong
chainIdorverifyingContractin the EIP-712 domain. Both are part of the digest. - The investor signed with a different wallet than the one passed as
investorWalletAddress. - The signature was produced over the legacy
personal_signenvelope instead ofsignTypedData_v4.
Recovery. Reproduce the digest off-chain and compare to what your wallet signed. See EIP-712 worked example.
InvestorNotFound(address wallet)
error InvestorNotFound(address wallet);
Thrown by registerVault when registry.getInvestor(investorWalletAddress) returns an empty string. The DS Registry has no entry for this wallet.
Recovery. Two options:
- Drip DST to the wallet via the faucet —
Faucet.requestTokens()auto-registers new wallets in the registry on the way. - If you control the registry admin, call
registry.addWallet(wallet, investorId)directly.
VaultAlreadyRegistered(address vault)
error VaultAlreadyRegistered(address vault);
Thrown by registerVault when the vault address is already bound to the same investor in the registry.
Recovery. Treat as success. The post-condition you wanted (isRegistered(vault, investor) == true) already holds. The contract is conservative about idempotency: it does not silently no-op, it explicitly tells you the registration already happened.
In practice you'll handle this by checking isRegistered before calling registerVault, or by catching this specific error and proceeding.
VaultBelongsToDifferentInvestor(address vault, string vaultInvestorId)
error VaultBelongsToDifferentInvestor(address vault, string vaultInvestorId);
Thrown by registerVault when the vault address resolves to a non-empty investor ID in the registry, but that ID is not the same as the investor's. Hard stop on cross-investor vault reuse.
Recovery. Use a different vault address. Cross-investor custody sharing is not allowed in this design.
InvalidAddress()
error InvalidAddress();
Thrown wherever the contract guards against the zero address — including registerVault (vault or investor zero), addOperator / removeOperator (target zero), and initialize (token zero).
Recovery. Pass a non-zero address.
NotImplemented()
error NotImplemented();
Thrown by unregisterVault. This is the only thing that function does today — there is no admin-only escape hatch.
Recovery. Redesign your off-ramp. The DS Registry binding survives the lifecycle. The only revocation primitive available to operators or investors is invalidateOperatorPermission(operator), which blocks future registrations but does not unbind anything.
NotAnOperator(address account)
error NotAnOperator(address account);
Declared in Errors.sol with the intent of guarding invalidateOperatorPermission(operator) against arbitrary target addresses, but the live implementation does not enforce it. Calling invalidateOperatorPermission(anyAddress) will succeed and increment the per-pair nonce regardless of whether the target holds OPERATOR_ROLE.
Treat this error as "ABI-declared, runtime-inert" until a future version wires the check.
Inherited errors (OpenZeppelin)
EnforcedPause()
Thrown by registerVault (because of the whenNotPaused modifier) when the contract is paused. Wait for an admin to call unpause().
ExpectedPause()
Thrown by unpause() when the contract is not currently paused. Operationally rare.
AccessControlUnauthorizedAccount(address account, bytes32 neededRole)
Thrown by addOperator, removeOperator, pause, unpause, _authorizeUpgrade when the caller does not hold DEFAULT_ADMIN_ROLE.
ECDSAInvalidSignature*
A family of OpenZeppelin ECDSA errors that may surface from inside SignatureChecker if the signature blob is malformed (wrong length, invalid v, invalid s). In practice you'll see InvalidInvestorSignature() first because the wrapper catches recovery failures, but a malformed-blob revert can still bubble.
Initializable: contract is already initialized
Thrown if anything tries to call initialize() a second time. UUPS-only concern; users do not call initialize directly.