From 678766aaeb2420f2f7618fe2514e795caac6cbda Mon Sep 17 00:00:00 2001 From: tactfulcipher101 Date: Mon, 9 Feb 2026 07:39:38 +0100 Subject: [PATCH] Update security considerations in proxy_delegate.md --- docs/proxy_delegate.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/proxy_delegate.md b/docs/proxy_delegate.md index 2f42609..c57ffbb 100644 --- a/docs/proxy_delegate.md +++ b/docs/proxy_delegate.md @@ -67,6 +67,17 @@ The address variables in line 3 and 4 store the address of the delegate and the The actual forwarding functionality is implemented in the function starting from line 11. The function does not have a name and is therefore the fallback function, which is being called for every unknown function identifier. Therefore, every function call to the proxy (besides the ones to `upgradeDelegate(..)`) will trigger the fallback function and execute the following inline assembly code: Line 13 loads the first variable in storage, in this case the address of the delegate, and stores it in the memory variable `_target`. Line 14 copies the function signature and any parameters into memory. In line 15 the `delegatecall` to the `_target` address is made, including the function data that has been stored in memory. A boolean containing the execution outcome is returned and stored in the `result` variable. Line 16 copies the actual return value into memory. The switch in line 17 checks whether the execution outcome was negative, in which case any state changes are reverted, or positive, in which case the result is returned to the caller of the proxy. + +## Security considerations + +While the Proxy Delegate pattern makes contract upgrades possible, it also introduces important security trade-offs that developers should understand before using it. +Because delegatecall executes the delegate’s code using the proxy’s storage, both contracts must share the same storage layout. If an upgraded delegate changes the order or type of existing state variables, the proxy may read or write incorrect values. This can lead to unexpected behavior and, in the worst case, permanent loss of funds. For this reason, storage layouts should be treated as append only across upgrades. +Another key consideration is upgrade authority. The address that is allowed to update the delegate effectively controls the contract’s logic. If this authority is compromised or misused, malicious code can be introduced without changing the proxy’s address. To reduce this risk, projects often rely on multisignature wallets, timelocks, or governance processes to manage upgrades. +Delegatecall also increases the overall attack surface. Since the delegate runs in the context of the proxy, it has access to the proxy’s storage and balance. A flawed, buggy or even malicious implementation can unintentionally overwrite critical state variables or introduce dangerous behavior such as disabling core functionality which now affects the whole architecture. +Initialization is another common source of errors in upgradeable systems. Incorrect or missing initialization logic can leave a proxy in an unusable state. Similarly, upgrading to a delegate that is incompatible with the existing storage or expected interfaces may permanently break the contract. +Finally, upgradeable contracts introduce a trust assumption. Unlike immutable contracts, users must trust that future upgrades will not introduce unwanted behavior. This trade-off should be communicated clearly, and where possible, mitigated through transparent upgrade processes or limited upgrade periods. + + ## Consequences There are several implications that should be considered when using the Proxy Delegate pattern for achieving upgradeability. With its implementation, complexity is increased drastically and especially developers new to smart contract development with Solidity, might find it difficult to understand the concepts of delegatecalls and inline assembly. This increases the chance of introducing bugs or other unintended behavior. Another point are the limitations on storage changes: fields cannot be deleted nor rearranged. While this is not an insurmountable problem, it is important to be aware of, in order to not accidentally break contract storage. An important negative consequence from a social perspecive is the potential loss in trust from users. With upgradeable contracts, immutability as one of the key benefits of blockchains, can be avoided. Users have to trust the responsible entities to not introduce any unwanted functionality with one of their upgrades. A solution to this caveat could be strategies that only allow for partial upgrades. Core features could be non-upgradeable, while other, less essential, features are implemented with the option for upgrades. If this approach is not applicable, a trust loss could also be mitigated by introducing a test period, during which upgrades can be carried out. After the expiration of the test period, the contract cannot be changed any longer.