0

I'm working on a Upgradble ( Proxy - Logic ) pattern smart contract.

I have the ABI and deployment address of my logic contract saved in my proxy contract so I use a simple call function.

The proxy contract is pausable. so if it's paused, it doesn't call the Logic contract.

Since the logic contract functions must be either external or public, I need to find the address of the caller contract and check if it's my own deployed proxy contract or not.

"msg.sender" is the Address of the user who initiated the call to the proxy contract in the first place.

now

Question:

How can I find (in run time) the Address of the contract that is calling the logic contract functions?

1
  • 1
    I think you've got the things other way around. msg.sender is the immediate address that called the Logic Contract (Proxy in your case). Then there's a tx.origin which points to the original address to initiate the transaction. Feb 1, 2022 at 9:23

1 Answer 1

1

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:

  1. 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.
  2. 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...?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.