function withdrawERC20(
address token,
address to,
uint256 amount,
uint256 salt,
uint256 deadline,
bytes calldata ownerSig,
bytes calldata backendSig
) external {
if (block.timestamp > deadline) revert WithdrawExpired();
// Compliance restriction on this vault's owner
if (!depositLimitRegistry.canWithdraw(vaultOwner)) revert WithdrawalRestricted();
// EIP-712 digest of WithdrawERC20(token, to, amount, salt, deadline)
// under this vault's domain (see the EIP-712 page)
bytes32 digest = ...;
if (usedDigests[digest]) revert WithdrawAlreadyUsed();
usedDigests[digest] = true;
if (ECDSA.recover(digest, ownerSig) != vaultOwner) revert InvalidOwnerSignature();
if (!factory.isBackend(ECDSA.recover(digest, backendSig))) revert InvalidBackendSignature();
if (amount > ledgerERC20[token]) revert InsufficientLedger();
ledgerERC20[token] -= amount;
IERC20(token).safeTransfer(to, amount);
}