If you want to build a liquid staking product on @Dusk_Foundation , the first thing you learn is that the obvious approach doesn't work.
A user staking from a wallet calls stake. A contract cannot. stake_from_contract refuses to be invoked directly — it validates that it was reached as part of a fund transfer. So the pattern is: move funds into your contract, then perform a contract-to-contract transfer into the stake contract, naming the function you want as part of the transfer itself.
Money and instruction travel together, or nothing happens.
That's a deliberate design choice and I've come to like it. It removes an entire bug class where a contract claims to stake value it never actually moved. The transfer is the authorisation.
The second half is the part builders underestimate. Your contract must implement callbacks — one to receive unstaked funds, one to receive rewards. Dusk doesn't push value at you and hope. It hands it back through a function you were required to write. Forget one, and you've built a pool that can take deposits and cannot return them.
Two constraints worth knowing before you start: the 1,000 $DUSK minimum applies to contracts exactly as it does to people, and stake becomes active after a maturity period.
Small honesty note: on maturity the docs give me two different framings in two places — one page says 4,320 blocks, roughly 12 hours; another describes activation at an epoch boundary. Both may be describing the same thing from different angles. If you're building around it, confirm on testnet rather than trusting either page.
Right now the ecosystem page lists exactly one staking pool built this way.
Builders — does forcing value and instruction into one atomic move make your life safer, or just slower?

#dusk