Skip to main content

VaultRegistrar Integration Guide

AI assistant skill pack for integrating DS Tokens into a DeFi protocol using Securitize's VaultRegistrar on the DS Protocol testnet. Use this context for all questions, code generation, and debugging in your project.

What VaultRegistrar does

VaultRegistrar is an on-chain registry that authorises DeFi operator contracts to manage DS Token vaults on behalf of investors. DS Token is a permissioned ERC-20 — transfers only succeed if both sender and receiver are registered in the DS Protocol. VaultRegistrar bridges that permission model with DeFi.

The 7-step integration lifecycle

  1. Get DS Tokens — Use the testnet faucet at https://labs.dev.securitize.io/faucet.
  2. Register as Operator — Call addOperator(yourContract) on VaultRegistrar. Your protocol contract (the one that calls registerVault()) must hold OPERATOR_ROLE. Do this once at https://labs.dev.securitize.io/register-as-operator.
  3. Deploy a vault — Deploy an ERC-4626-compatible vault that accepts DS Token.
  4. Collect investor signature — Investor signs EIP-712 typed data (see below).
  5. Call registerVault() — Your contract submits the signature on-chain.
  6. Transfer tokens — DS Tokens move into the vault.
  7. Protocol interaction — Your protocol logic executes with registered tokens.

The operator concept

The operator field in the EIP-712 message must match the exact msg.sender of registerVault() on-chain. This is your protocol contract — not the deployer EOA, not a backend wallet. The contract calls registerVault(), so the contract address is the one that needs OPERATOR_ROLE.

IVaultRegistrar interface

interface IVaultRegistrar {
function registerVault(
address vault,
address investor,
uint256 deadline,
bytes calldata signature
) external;

function addOperator(address operator) external;
function removeOperator(address operator) external;
function isOperator(address operator) external view returns (bool);
function invalidateNonce() external;
function operatorNonce(address investor, address operator)
external view returns (uint256);
}

EIP-712 typed data

Investors sign this structure. Your contract must verify the same structure when calling registerVault().

Domain:

  • name: "VaultRegistrar"
  • version: "1"
  • chainId: the target chain ID
  • verifyingContract: the VaultRegistrar proxy address

Type VaultPermission:

  • investor address — the investor's wallet
  • operator address — your protocol contract (must hold OPERATOR_ROLE)
  • token address — DS Token contract address
  • nonce uint256 — read from operatorNonce(investor, operator) before signing
  • deadline uint256 — Unix timestamp, e.g. block.timestamp + 1 hour

Collecting the signature (wagmi / viem)

import { useSignTypedData } from 'wagmi'

const { signTypedData } = useSignTypedData()

signTypedData({
domain: {
name: 'VaultRegistrar',
version: '1',
chainId,
verifyingContract: VAULT_REGISTRAR_ADDRESS,
},
types: {
VaultPermission: [
{ name: 'investor', type: 'address' },
{ name: 'operator', type: 'address' },
{ name: 'token', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
},
primaryType: 'VaultPermission',
message: {
investor: investorAddress,
operator: YOUR_PROTOCOL_CONTRACT,
token: DS_TOKEN_ADDRESS,
nonce: await publicClient.readContract({
address: VAULT_REGISTRAR_ADDRESS,
abi: vaultRegistrarAbi,
functionName: 'operatorNonce',
args: [investorAddress, YOUR_PROTOCOL_CONTRACT],
}),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
},
})

Calling registerVault() from your contract

function deposit(
address investor,
address vault,
uint256 amount,
uint256 deadline,
bytes calldata signature
) external {
// 1. Register the vault using the investor's EIP-712 signature
IVaultRegistrar(VAULT_REGISTRAR).registerVault(
vault,
investor,
deadline,
signature
);

// 2. Transfer DS Tokens into the vault
IERC20(DS_TOKEN).transferFrom(investor, vault, amount);

// 3. Your protocol logic
}

msg.sender of this function must equal the operator in the signature and must hold OPERATOR_ROLE on the VaultRegistrar.

Common errors

ErrorCause
InvalidSignatureoperator in signature ≠ msg.sender, or wrong nonce / deadline
SignatureExpiredblock.timestamp > deadline — use a future deadline
InvalidNonceInvestor called invalidateNonce() — fetch a fresh nonce
OperatorNotRegisteredmsg.sender does not hold OPERATOR_ROLE
VaultAlreadyRegisteredVault already registered for this investor + operator pair

Sandbox tools

Use the live sandbox at https://labs.dev.securitize.io to test your integration:

  • Faucet /faucet — Get testnet DS Tokens
  • Register as Operator /register-as-operator — Add your contract to VaultRegistrar
  • Mock Protocol Example /mock-protocol — Run a full sign → approve → deposit flow in the browser
  • Protocol Tester /protocol-tester — Upload your ABI and call any write function with EIP-712 signing support
  • Docs /docs — Full integration documentation

See also