permit functions use deployment-time instead of execution-time chain ID
The contracts `Base`, `MSD`, and `MSDS` each have an EIP-2612-style `permit` function that supports approvals with EIP-712 signatures. We focus this discussion on the `Base` contract, but the same applies to `MSD` and `MSDS`.\\nWhen the contract is initialized, the chain ID is queried (with the `CHAINID` opcode) and becomes part of the `DOMAIN_SEPARATOR` — a hash of several values which (presumably) don't change over the lifetime of the contract and that can therefore be computed only once, when the contract is deployed.\\n```\\nfunction \\_initialize(\\n    string memory \\_name,\\n    string memory \\_symbol,\\n    uint8 \\_decimals,\\n    IControllerInterface \\_controller,\\n    IInterestRateModelInterface \\_interestRateModel\\n) internal virtual {\\n    controller = \\_controller;\\n    interestRateModel = \\_interestRateModel;\\n    accrualBlockNumber = block.number;\\n    borrowIndex = BASE;\\n    flashloanFeeRatio = 0.0008e18;\\n    protocolFeeRatio = 0.25e18;\\n    \\_\\_Ownable\\_init();\\n    \\_\\_ERC20\\_init(\\_name, \\_symbol, \\_decimals);\\n    \\_\\_ReentrancyGuard\\_init();\\n\\n    uint256 chainId;\\n\\n    assembly {\\n        chainId := chainid()\\n    }\\n    DOMAIN\\_SEPARATOR = keccak256(\\n        abi.encode(\\n            keccak256(\\n                "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"\\n            ),\\n            keccak256(bytes(\\_name)),\\n            keccak256(bytes("1")),\\n            chainId,\\n            address(this)\\n        )\\n    );\\n}\\n```\\n\\nThe `DOMAIN_SEPARATOR` is supposed to prevent replay attacks by providing context for the signature; it is hashed into the digest to be signed.\\n```\\nbytes32 \\_digest =\\n    keccak256(\\n        abi.encodePacked(\\n            "\\x19\\x01",\\n            DOMAIN\\_SEPARATOR,\\n            keccak256(\\n                abi.encode(\\n                    PERMIT\\_TYPEHASH,\\n                    \\_owner,\\n                    \\_spender,\\n                    \\_value,\\n                    \\_currentNonce,\\n                    \\_deadline\\n                )\\n            )\\n        )\\n    );\\naddress \\_recoveredAddress = ecrecover(\\_digest, \\_v, \\_r, \\_s);\\nrequire(\\n    \\_recoveredAddress != address(0) && \\_recoveredAddress == \\_owner,\\n    "permit: INVALID\\_SIGNATURE!"\\n);\\n```\\n\\nThe chain ID is not necessarily constant, though. In the event of a chain split, only one of the resulting chains gets to keep the original chain ID and the other will have to use a new one. With the current pattern, a signature will be valid on both chains; if the `DOMAIN_SEPARATOR` is recomputed for every verification, a signature will only be valid on the chain that keeps the original ID — which is probably the intended behavior.\\nRemark\\nThe reason why the not necessarily constant chain ID is part of the supposedly constant `DOMAIN_SEPARATOR` is that EIP-712 predates the introduction of the `CHAINID` opcode. Originally, it was not possible to query the chain ID via opcode, so it had to be supplied to the constructor of a contract by the deployment script.
An obvious fix is to compute the `DOMAIN_SEPARATOR` dynamically in `permit`. However, since a chain split is a relatively unlikely event, it makes sense to compute the `DOMAIN_SEPARATOR` at deployment/initialization time and then check in `permit` whether the current chain ID equals the one that went into the `DOMAIN_SEPARATOR`. If that is true, we proceed as before. If the chain ID has changed, we could (1) just revert, or (2) recompute the `DOMAIN_SEPARATOR` with the new chain ID. Solution (1) is probably the easiest and most straightforward to implement, but it should be noted that it makes the `permit` functionality of this contract completely unusable on the new chain.
```\\nfunction \\_initialize(\\n    string memory \\_name,\\n    string memory \\_symbol,\\n    uint8 \\_decimals,\\n    IControllerInterface \\_controller,\\n    IInterestRateModelInterface \\_interestRateModel\\n) internal virtual {\\n    controller = \\_controller;\\n    interestRateModel = \\_interestRateModel;\\n    accrualBlockNumber = block.number;\\n    borrowIndex = BASE;\\n    flashloanFeeRatio = 0.0008e18;\\n    protocolFeeRatio = 0.25e18;\\n    \\_\\_Ownable\\_init();\\n    \\_\\_ERC20\\_init(\\_name, \\_symbol, \\_decimals);\\n    \\_\\_ReentrancyGuard\\_init();\\n\\n    uint256 chainId;\\n\\n    assembly {\\n        chainId := chainid()\\n    }\\n    DOMAIN\\_SEPARATOR = keccak256(\\n        abi.encode(\\n            keccak256(\\n                "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"\\n            ),\\n            keccak256(bytes(\\_name)),\\n            keccak256(bytes("1")),\\n            chainId,\\n            address(this)\\n        )\\n    );\\n}\\n```\\n