First off, a point of clarification:
I have the ABI and deployment address of my logic contract saved in my proxy contract so I use a simple call function.
If you're using .call
instead of .delegateCall
, then it's not really what we'd normally call a "proxy" contract pattern.
If you use delegateCall
, then msg.sender is unchanged (it will still be the EOA making the call)--your proxy executes the functions of the logic contract, but the storage used is that of the proxy contract itself.
If you use call
, then you will be using the storage of the logic contract--and that means your "logic" contract is a "real" contract, not just a "logic" contract. (True "logic" contracts, in the contexts of a discussion on proxy contracts, don't have their storage utilized in any meaningful way, except to lock their initialize functions, generally.) In that case, msg.sender
becomes the contract that made the .call()
.
So:
- If you're using
.call
and not .delegateCall
, and so not using a "real proxy" (i.e., your storage will not transfer between upgrades), then you can just check if msg.sender
is your proxy address. tx.origin
will be the EOA.
- If you're actually doing a real proxy contract, using
delegateCall
, then you wouldn't check who is calling me
but who am I
, so in other words, something like:
contract logicContract {
address immutable myProxy;
constructor(address _myProxy) {
myProxy = _myProxy;
}
protectedFunction() {
require(address(this) == myProxy);
}
}
Again, this reason is that address(this)
will not be the logic contract's address when it is called from a proxy via delegateCall
, but will be the proxy doing the call. delegateCall
means "Use this contract's code, but run it yourself as if it were your own code".
Note: Yes, this prevents anyone else from deploying a proxy that points to your logic contract... But why would you do this? Someone would also copy your logic contract, deploy their own version but remove that check, and then point their proxy to it.
Either way, no matter what, they don't copy the state of your proxy contract... I am not sure why this makes sense, other than minor obfuscation and adding cost to someone else who wants to deploy their own proxy...?
msg.sender
is the immediate address that called the Logic Contract (Proxy in your case). Then there's atx.origin
which points to the original address to initiate the transaction.