Then when using this code to deploy it using a CREATE2 or CREATE3 deployer contract like Axelar's Constant Address Deployer, all the tokens go to the ConstAddressDeployer contract instead of the account that called deploy().
Replacing msg.sender with tx.origin in the token contract constructor solves the problem. But I've read in many places that tx.origin shouldn't be used.
The situation you've described is a common challenge when using factory or deployer contracts in combination with other contracts that rely on msg.sender for initial setup, especially when the desired behavior is to assign ownership or mint tokens to the original caller.
Here's a breakdown of the problem and potential solutions:
The Problem with msg.sender and Factory Contracts:
When a contract (like Axelar's Constant Address Deployer) deploys another contract, the msg.sender in the deployed contract's constructor will be the address of the deployer contract, not the address of the account that called the deployer contract's function.
This is why, in the scenario you described, all the tokens go to the ConstAddressDeployer contract.
tx.origin always refers to the original sender of the transaction, i.e., the externally owned account (EOA) that initiated the transaction.
While this would solve the problem in the scenario you described, using tx.origin is generally discouraged because it can introduce security vulnerabilities, especially in more complex dApps where malicious contracts can trick users into sending transactions.
a. Pass the Desired Address as a Constructor Argument:
Modify the token contract to accept an address as a constructor argument. This address will be the recipient of the minted tokens or the owner of the contract.
When the deployer contract deploys the token contract, it can pass the desired address (e.g., msg.sender of the deployer contract) as an argument to the token contract's constructor.
b. Use a Setup Function:
Instead of setting the owner or minting tokens in the constructor, have a separate public function in the token contract for this purpose. This function can only be called once and sets the owner or mints the tokens.
After deploying the token contract using the deployer contract, call this setup function directly from the desired owner account.
c. Delegate Call Initialization:
Use a delegate call from the deployer contract to the initialization function of the token contract. This way, msg.sender within the token contract will be the original caller, not the deployer contract. However, this approach is more complex and requires a deeper understanding of the Ethereum Virtual Machine (EVM) to implement safely.
The first solution (passing the desired address as a constructor argument) is the most straightforward and is commonly used in practice. It provides clarity and avoids the pitfalls associated with tx.origin and the complexities of delegate calls.