Proxy - storage and contextThe storage variables of upgradeable contracts can't be deleted/ommited (immutable), however, new storage variables might be added.Important note: delegate contracts (module contracts) must have the same storage layout as the proxy contract. Changing this/ommiting this could cause a catastrophic fault/change in the state variables of the original proxy contract.As for the context of a transaction, the following rules remain true:msg.sender is always the external contract/EOA that called the proxy, which in terms delegatecalled the logic/delegate contract. Therefore, once the transaction reaches the delegate, msg.sender would NOT be the proxy contract.msg.value is also preserved, therefore any ether/bytes that is sent ends up at the delegate contractstorage variables of the proxy are altered in the delegate contractHow is the proxy pattern implemented in SolidityIn Solidity (and other programming languages for that matter), modularity is a common practice that allows the seperation of business logic. Since smart contracts are immutable, we have to get creative in order to make a contract extendable.In essence, the implementation of some business logic is done by a delegate contract. The address of this delegate contract is usually stored in a modifiable state variable inside the original proxy smart contract.Ultimatelly, we want to recieve some data in the proxy contract and without modifying it - forward it to one of the proxy contracts that will then perform the necessary operations on it.Pre-requisitesIn order to best understand how proxies delegate information to a different contract, we need to understand how Solidity deals with "call data" to an external contract.In Solidity, we are able to perform an external call to a contract with the following snippet:(bool success,) = someContractAddress.call{value:123}(abi.encodeWithSignature("test(uint256)", 123))With the above snippet of code, we are doing the following:Externally calling someContractAddress, with a specified value of 123 (wei) and the encoded function specifier, pointing to test(uint256) and a value of 123Delegatecall has almost the exact same syntax, with the important difference of the preservation rules (mentioned above in this notebook).(bool success,) = someContractAddress.delegatecall{value:123}(abi.encodeWithSignature("test(uint256)", 123))Now, given all of this, it is not trivial to come to the conclusion that if we can somehow change the "someContractAddress" and the encoded call data that is being sent, we can achieve modularity and extend our proxy contract.ImplementationIt is important to note that delegatecall only returns a boolean of whether or not the function was succesfull or not. In order to overcome this, we can use some simple inline assembly in order to recieve further data from using a delegate contract.To start things off, we look at Solidity's fallback function. The way that the EVM handles things in Solidity is that when msg.data of a transaction contains a function specifier that is not contained in a contract, it is forwarded to the fallback function of the called contract.fallback() external { assembly { let ptr: = mload(0x40) calldatacopy(ptr, 0, calldatasize()) let result: = delegatecall( gas(), sload(implementation.slot), ptr, calldatasize(), 0, 0 ) let size: = returndatasize() returndatacopy(ptr, 0, size) switch result case 0 { revert(ptr, size) } default { return (ptr, size) } }}We use the above shown inline assembly in order to be able to recieve any kind of return output from the function we are delegatecalling to. Inline assembly is necessary here, as we do not know what kind of data will be returned in advance.As explained in , we are able to bypass the need to use assembly if the delegate contract uses an event to broadcast the result. Since events cannot be listened to by smart contract themselves, the result would have to be directly interpreted by some frontend/something external of the Ethereum blockchain.