Vyper provides a built-in reentrancy guard via the `@nonreentrant("key")` decorator, which uses a storage-backed lock so a locked function cannot be re-entered while it's still executing, even across different functions sharing the same key.
@external
@nonreentrant("withdraw_lock")
def withdraw():
amount: uint256 = self.balances[msg.sender]
self.balances[msg.sender] = 0
send(msg.sender, amount)Still follow checks-effects-interactions as a first line of defence — zero out the balance before sending — and use the guard as a second, belt-and-braces layer.