External Vault Onboarding Runbook (setup-external-vaults.sh)
Why This Exists
Core deployment (DeployQuantillon) intentionally does not bootstrap external adapters.
After core deploy:
defaultStakingVaultId = 0- redemption priority is empty
- no adapter is registered via
setStakingVault
Use this runbook to onboard staking vault adapters post-deploy.
Script
./scripts/deployment/setup-external-vaults.sh
The script performs, for each --vault entry:
- Grant
VAULT_FACTORY_ROLEtoQuantillonVaultonstQEUROFactory. - Register the vault token via
selfRegisterStQEURO(factory, vaultId, vaultName). - Configure adapter binding via
setStakingVault(vaultId, adapter, true). - Grant
VAULT_MANAGER_ROLEtoQuantillonVaulton the adapter (adapter.grantRole(VAULT_MANAGER_ROLE, vault)).
Adapter yield is distributed via
QuantillonVault.harvestAndDistributeVaultYield— grantYIELD_DISTRIBUTOR_ROLEto the yield keeper separately (yield distribution no longer routes through YieldShift).
Required — do not skip. The vault calls
adapter.depositUnderlying/withdrawUnderlying/harvestYieldToVault, all gated byVAULT_MANAGER_ROLEon the adapter. The adapter constructor grants that role only to its admin, so without step 4 every external-vault deploy / redeem-sourcing / harvest reverts. Step 4's signer must hold the adapter'sDEFAULT_ADMIN_ROLE.
Recommended — seed each new stQEURO series. To avoid the well-known first-depositor donation/rounding edge case, the operator should make the first stake into each newly registered stQEURO vault (a small deposit) so it is never bootstrapped by an arbitrary first external user.
Then it sets:
setDefaultStakingVaultId(...)setRedemptionPriority(...)
Prerequisites
- Foundry installed (
castavailable inPATH) - Core deployment completed (
QuantillonVault,stQEUROFactory, and optionallyYieldShiftaddresses) - Deployer/admin key with required governance/role permissions
- RPC URL for target chain
Parameters
--rpc-url <url>: chain RPC endpoint--private-key <hex>: signer private key--quantillon-vault <address>: deployedQuantillonVault--factory <address>: deployedstQEUROFactory--vault <vaultId:vaultName:adapterAddress>: repeatable vault definitionvaultId: positive integervaultName: uppercase/digits token label (factory validation applies)adapterAddress: external strategy adapter for that vault id
--default-vault-id <vaultId>: optional, defaults to first--vaultitem
Ready-To-Run Examples
Localhost (31337)
./scripts/deployment/setup-external-vaults.sh \
--rpc-url http://localhost:8545 \
--private-key "$PRIVATE_KEY" \
--quantillon-vault 0xQuantillonVault \
--factory 0xStQEUROFactory \
--vault 1:AAVE1:0xMockAaveAdapter \
--vault 2:MORPHO1:0xMorphoAdapter \
--default-vault-id 2
Base Sepolia (84532)
./scripts/deployment/setup-external-vaults.sh \
--rpc-url https://sepolia.base.org \
--private-key "$PRIVATE_KEY" \
--quantillon-vault 0xQuantillonVault \
--factory 0xStQEUROFactory \
--vault 1:AAVE1:0xAdapterA \
--vault 2:MORPHO1:0xAdapterB
Verification Commands
Export addresses once:
export RPC_URL=http://localhost:8545
export QUANTILLON_VAULT=0xQuantillonVault
export FACTORY=0xStQEUROFactory
export YIELD_SHIFT=0xYieldShift
Check default vault:
cast call "$QUANTILLON_VAULT" "defaultStakingVaultId()(uint256)" --rpc-url "$RPC_URL"
Check per-vault token + adapter exposure:
cast call "$FACTORY" "getStQEUROByVaultId(uint256)(address)" 1 --rpc-url "$RPC_URL"
cast call "$QUANTILLON_VAULT" "getVaultExposure(uint256)(address,bool,uint256,uint256)" 1 --rpc-url "$RPC_URL"
Check routing events:
cast logs --rpc-url "$RPC_URL" --address "$QUANTILLON_VAULT" "StakingVaultConfigured(uint256,address,bool)"
cast logs --rpc-url "$RPC_URL" --address "$QUANTILLON_VAULT" "DefaultStakingVaultUpdated(uint256)"
cast logs --rpc-url "$RPC_URL" --address "$QUANTILLON_VAULT" "RedemptionPriorityUpdated(uint256[])"
If --yield-shift was used, verify source authorization/binding:
cast call "$YIELD_SHIFT" "authorizedYieldSources(address)(bool)" 0xAdapterA --rpc-url "$RPC_URL"
cast call "$YIELD_SHIFT" "sourceToVaultId(address)(uint256)" 0xAdapterA --rpc-url "$RPC_URL"
cast call "$YIELD_SHIFT" "enforceSourceVaultBinding()(bool)" --rpc-url "$RPC_URL"