Skip to content

Commit

Permalink
⚡️ Adding msg.sender separation for factory calls
Browse files Browse the repository at this point in the history
  • Loading branch information
zeroknots committed Apr 18, 2024
1 parent 6f5e84a commit a72c4c0
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 6 deletions.
4 changes: 3 additions & 1 deletion src/IRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,9 @@ interface IRegistry is IERC7484 {
/**
* In order to make the integration into existing business logics possible,
* the Registry is able to utilize external factories that can be utilized to deploy the modules.
* @dev Registry can use other factories to deploy the module
* @dev Registry can use other factories to deploy the module.
* @dev Note that this function will call the external factory via the FactoryTrampolin contract.
* Factory MUST not assume that msg.sender == registry
* @dev This function is used to deploy and register a module using a factory contract.
* Since one of the parameters of this function is a unique resolverUID and any
* registered module address can only be registered once,
Expand Down
40 changes: 35 additions & 5 deletions src/core/ModuleManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@ import { ResolverRecord, ModuleRecord, ResolverUID } from "../DataTypes.sol";
import { ResolverManager } from "./ResolverManager.sol";
import { IRegistry } from "../IRegistry.sol";

/**
* In order to separate msg.sender context from registry,
* interactions with external Factories are done with this Trampolin contract.
*/
contract FactoryTrampolin {
error FactoryCallFailed(address factory);

/**
* @param factory the address of the factory to call
* @param callOnFactory the call data to send to the factory
* @return moduleAddress the moduleAddress that was returned by the
*/
function deployViaFactory(address factory, bytes memory callOnFactory) external payable returns (address moduleAddress) {
// call external factory to deploy module
bool success;
/* solhint-disable no-inline-assembly */
assembly ("memory-safe") {
success := call(gas(), factory, 0, add(callOnFactory, 0x20), mload(callOnFactory), 0, 32)
moduleAddress := mload(0)
}
if (!success) {
revert FactoryCallFailed(factory);
}
}
}

/**
* In order for Attesters to be able to make statements about a Module, the Module first needs to be registered on the Registry.
* This can be done as part of or after Module deployment. On registration, every module is tied to a
Expand All @@ -34,6 +60,12 @@ abstract contract ModuleManager is IRegistry, ResolverManager {

mapping(address moduleAddress => ModuleRecord moduleRecord) internal $moduleAddrToRecords;

FactoryTrampolin private FACTORY_TRAMPOLIN;

constructor() {
FACTORY_TRAMPOLIN = new FactoryTrampolin();
}

/**
* @inheritdoc IRegistry
*/
Expand Down Expand Up @@ -105,11 +137,9 @@ abstract contract ModuleManager is IRegistry, ResolverManager {
// prevent someone from calling a registry function pretending its a factory
if (factory == address(this)) revert FactoryCallFailed(factory);

// call external factory to deploy module
(bool ok, bytes memory returnData) = factory.call{ value: msg.value }(callOnFactory);
if (!ok) revert FactoryCallFailed(factory);

moduleAddress = abi.decode(returnData, (address));
// Call the factory via the trampolin contract. This will make sure that there is msg.sender separation
// Making "raw" calls to user supplied addresses could create security issues.
moduleAddress = FACTORY_TRAMPOLIN.deployViaFactory({ factory: factory, callOnFactory: callOnFactory });

ModuleRecord memory record = _storeModuleRecord({
moduleAddress: moduleAddress,
Expand Down

0 comments on commit a72c4c0

Please sign in to comment.