Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Natspec comments + readme update #13

Merged
merged 2 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
703 changes: 568 additions & 135 deletions README.md

Large diffs are not rendered by default.

Binary file removed docs/img/proxy-diagram.png
Binary file not shown.
Binary file removed docs/img/router-diagram.png
Binary file not shown.
Binary file removed docs/img/update-diagram.png
Binary file not shown.
20 changes: 15 additions & 5 deletions src/core/Router.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)

pragma solidity ^0.8.0;

import "../interface/IRouter.sol";

/// @title ERC-7504 Dynamic Contracts: Router.
/// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
/// @notice Routes an incoming call to an appropriate implementation address.

abstract contract Router is IRouter {

/**
* @notice delegateCalls the appropriate implementation address for the given incoming function call.
* @dev The implementation address to delegateCall MUST be retrieved from calling `getImplementationForFunction` with the
* incoming call's function selector.
*/
fallback() external payable virtual {
/// @dev delegate calls the appropriate implementation smart contract for a given function.
address implementation = getImplementationForFunction(msg.sig);
require(implementation != address(0), "Router: function does not exist.");
_delegate(implementation);
Expand Down Expand Up @@ -40,6 +46,10 @@ abstract contract Router is IRouter {
}
}

/// @dev Returns the implementation contract address for a given function signature.
function getImplementationForFunction(bytes4 _functionSelector) public view virtual returns (address);
/**
* @notice Returns the implementation address to delegateCall for the given function selector.
* @param _functionSelector The function selector to get the implementation address for.
* @return implementation The implementation address to delegateCall for the given function selector.
*/
function getImplementationForFunction(bytes4 _functionSelector) public view virtual returns (address implementation);
}
8 changes: 6 additions & 2 deletions src/core/RouterPayable.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)

pragma solidity ^0.8.0;

import "./Router.sol";

/// @title IRouterPayable.
/// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
/// @notice A Router with `receive` as a fixed function.

abstract contract RouterPayable is Router {

/// @notice Lets a contract receive native tokens.
receive() external payable virtual {}
}
2 changes: 1 addition & 1 deletion src/example/RouterImmutable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ contract RouterImmutable is BaseRouterWithDefaults {
Overrides
//////////////////////////////////////////////////////////////*/

/// @dev Returns whether a function can be disabled in an extension in the given execution context.
/// @dev Returns whether all relevant permission and other checks are met before any upgrade.
function isAuthorizedCallToUpgrade() internal view virtual override returns (bool) {
return false;
}
Expand Down
12 changes: 6 additions & 6 deletions src/example/RouterRegistryConstrained.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

pragma solidity ^0.8.0;

import "../presets/BaseRouterWithDefaults.sol";
import "../presets/BaseRouter.sol";

/**
* This smart contract is an EXAMPLE, and is not meant for use in production.
Expand All @@ -26,18 +26,18 @@ contract ExtensionRegistry {
/**
* This smart contract is an EXAMPLE, and is not meant for use in production.
*/
contract RouterRegistryConstrained is BaseRouterWithDefaults {
contract RouterRegistryConstrained is BaseRouter {

address public admin;
ExtensionRegistry public registry;

// @dev Cannot initialize with extensions before registry is set, so we pass empty array to base constructor.
constructor(address _registry) BaseRouterWithDefaults(new Extension[](0)) {
/// @dev Cannot initialize with extensions before registry is set, so we pass empty array to base constructor.
constructor(address _registry) {
admin = msg.sender;
registry = ExtensionRegistry(_registry);
}

// @dev Sets the admin address.
/// @dev Sets the admin address.
function setAdmin(address _admin) external {
require(msg.sender == admin, "RouterUpgradeable: Only admin can set a new admin");
admin = _admin;
Expand All @@ -47,7 +47,7 @@ contract RouterRegistryConstrained is BaseRouterWithDefaults {
Overrides
//////////////////////////////////////////////////////////////*/

/// @dev Returns whether a function can be disabled in an extension in the given execution context.
/// @dev Returns whether all relevant permission and other checks are met before any upgrade.
function isAuthorizedCallToUpgrade() internal view virtual override returns (bool) {
return msg.sender == admin;
}
Expand Down
11 changes: 7 additions & 4 deletions src/interface/IExtension.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)

pragma solidity ^0.8.0;


/// @title IExtension
/// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
/// @notice Provides an `Extension` abstraction for a router's implementation contracts.

interface IExtension {
/*///////////////////////////////////////////////////////////////
Structs
//////////////////////////////////////////////////////////////*/

/**
* @notice A extension's metadata.
* @notice An interface to describe an extension's metadata.
*
* @param name The unique name of the extension.
* @param metadataURI The URI where the metadata for the extension lives.
Expand All @@ -22,7 +25,7 @@ interface IExtension {
}

/**
* @notice An interface to describe a extension's function.
* @notice An interface to describe an extension's function.
*
* @param functionSelector The 4 byte selector of the function.
* @param functionSignature Function signature as a string. E.g. "transfer(address,address,uint256)"
Expand Down
54 changes: 34 additions & 20 deletions src/interface/IExtensionManager.sol
Original file line number Diff line number Diff line change
@@ -1,56 +1,70 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IExtension.sol";

/// @title IExtensionManager
/// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
/// @notice Defined storage and API for managing a router's extensions.

interface IExtensionManager is IExtension {

/*///////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/

/// @dev Emitted when a extension is added.
event ExtensionAdded(string indexed name, address indexed implementation, Extension extension);

/// @dev Emitted when a extension is replaced.
event ExtensionReplaced(string indexed name, address indexed implementation, Extension extension);

/// @dev Emitted when a extension is removed.
event ExtensionRemoved(string indexed name, Extension extension);

/// @dev Emitted when a function is enabled i.e. made callable.
event FunctionEnabled(string indexed name, bytes4 indexed functionSelector, ExtensionFunction extFunction, ExtensionMetadata extMetadata);

/// @dev Emitted when a function is disabled i.e. made un-callable.
event FunctionDisabled(string indexed name, bytes4 indexed functionSelector, ExtensionMetadata extMetadata);

/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/

/**
* @notice Add a new extension to the router.
* @param extension The extension to add.
*/
function addExtension(Extension memory extension) external;

/**
* @notice Fully replace an existing extension of the router.
* @dev The extension with name `extension.name` is the extension being replaced.
* @param extension The extension to replace or overwrite.
*/
function replaceExtension(Extension memory extension) external;

/**
* @notice Remove an existing extension from the router.
* @param extensionName The name of the extension to remove.
*/
function removeExtension(string memory extensionName) external;

/**
* @notice Enables a single function in an existing extension.
* @dev Makes the given function callable on the router.
*
* @param extensionName The name of the extension to which `extFunction` belongs.
* @param extFunction The function to enable.
*/
function enableFunctionInExtension(string memory extensionName, ExtensionFunction memory extFunction) external;

/**
* @notice Disables a single function in an Extension.
*
* @param extensionName The name of the extension to which the function of `functionSelector` belongs.
* @param functionSelector The function to disable.
*/
function disableFunctionInExtension(string memory extensionName, bytes4 functionSelector) external;

/*///////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/

/// @dev Emitted when a extension is added.
event ExtensionAdded(string indexed name, address indexed implementation, Extension extension);

/// @dev Emitted when a extension is added.
event ExtensionReplaced(string indexed name, address indexed implementation, Extension extension);

/// @dev Emitted when a extension is added.
event ExtensionRemoved(string indexed name, Extension extension);

/// @dev Emitted when a function is updated.
event FunctionAdded(string indexed name, bytes4 indexed functionSelector, ExtensionFunction extFunction, ExtensionMetadata extMetadata);

/// @dev Emitted when a function is removed.
event FunctionRemoved(string indexed name, bytes4 indexed functionSelector, ExtensionMetadata extMetadata);
}
24 changes: 16 additions & 8 deletions src/interface/IRouter.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
* @title ERC-7504 Dynamic Contracts.
* @dev Fallback function delegateCalls `getImplementationForFunction(msg.sig)` for a given incoming call.
* NOTE: The ERC-165 identifier for this interface is 0xce0b6013.
*/
/// @title ERC-7504 Dynamic Contracts: IRouter.
/// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
/// @notice Routes an incoming call to an appropriate implementation address.
/// @dev Fallback function delegateCalls `getImplementationForFunction(msg.sig)` for a given incoming call.
/// NOTE: The ERC-165 identifier for this interface is 0xce0b6013.

interface IRouter {

/**
Expand All @@ -16,6 +16,14 @@ interface IRouter {
*/
fallback() external payable;

/// @dev Returns the implementation address to delegateCall for the given function selector.
function getImplementationForFunction(bytes4 _functionSelector) external view returns (address);
/*///////////////////////////////////////////////////////////////
View Functions
//////////////////////////////////////////////////////////////*/

/**
* @notice Returns the implementation address to delegateCall for the given function selector.
* @param _functionSelector The function selector to get the implementation address for.
* @return implementation The implementation address to delegateCall for the given function selector.
*/
function getImplementationForFunction(bytes4 _functionSelector) external view returns (address implementation);
}
8 changes: 5 additions & 3 deletions src/interface/IRouterPayable.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)

pragma solidity ^0.8.0;

import "./IRouter.sol";

/// @dev See {IRouter}.
/// @title IRouterPayable.
/// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
/// @notice A Router with `receive` as a fixed function.

interface IRouterPayable is IRouter {
/// @notice Lets a contract receive native tokens.
receive() external payable;
}
14 changes: 8 additions & 6 deletions src/interface/IRouterState.sol
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IExtension.sol";

/**
* @title ERC-7504 Dynamic Contracts.
* NOTE: The ERC-165 identifier for this interface is 0x4a00cc48.
*/
/// @title ERC-7504 Dynamic Contracts: IRouterState.
/// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
/// @notice Defines an API to expose a router's extensions.

interface IRouterState is IExtension {

/*///////////////////////////////////////////////////////////////
View Functions
//////////////////////////////////////////////////////////////*/

/// @dev Returns all extensions of the Router.
/**
* @notice Returns all extensions of the Router.
* @return allExtensions An array of all extensions.
*/
function getAllExtensions() external view returns (Extension[] memory allExtensions);
}
21 changes: 16 additions & 5 deletions src/interface/IRouterStateGetters.sol
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)

pragma solidity ^0.8.0;

import "./IExtension.sol";

/// @title IRouterStateGetters.
/// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
/// @notice Helper view functions to inspect a router's state.

interface IRouterStateGetters is IExtension {

/*///////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/

/// @dev Returns the extension metadata for a given function.
function getMetadataForFunction(bytes4 functionSelector) external view returns (ExtensionMetadata memory);
/**
* @notice Returns the extension metadata for a given function.
* @param functionSelector The function selector to get the extension metadata for.
* @return metadata The extension metadata for a given function.
*/
function getMetadataForFunction(bytes4 functionSelector) external view returns (ExtensionMetadata memory metadata);

/// @dev Returns the extension metadata and functions for a given extension.
/**
* @notice Returns the extension metadata and functions for a given extension.
* @param extensionName The name of the extension to get the metadata and functions for.
* @return extension The extension metadata and functions for a given extension.
*/
function getExtension(string memory extensionName) external view returns (Extension memory);
}
9 changes: 6 additions & 3 deletions src/lib/ExtensionManagerStorage.sol
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
// SPDX-License-Identifier: MIT
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)

pragma solidity ^0.8.0;

import "./StringSet.sol";
import "../interface/IExtension.sol";

/// @title IExtensionManagerStorage
/// @author thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
/// @notice Defined storage for managing a router's extensions.

library ExtensionManagerStorage {

/// @custom:storage-location erc7201:extension.manager.storage
bytes32 public constant EXTENSION_MANAGER_STORAGE_POSITION = keccak256(abi.encode(uint256(keccak256("extension.manager.storage")) - 1));

struct Data {
/// @dev Set of names of all extensions stored.
/// @dev Set of names of all extensions of the router.
StringSet.Set extensionNames;
/// @dev Mapping from extension name => `Extension` i.e. extension metadata and functions.
mapping(string => IExtension.Extension) extensions;
/// @dev Mapping from function selector => metadata of the extension the function belongs to.
mapping(bytes4 => IExtension.ExtensionMetadata) extensionMetadata;
}

/// @dev Returns access to the extension manager's storage.
function data() internal pure returns (Data storage data_) {
bytes32 position = EXTENSION_MANAGER_STORAGE_POSITION;
assembly {
Expand Down
4 changes: 1 addition & 3 deletions src/lib/StringSet.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// SPDX-License-Identifier: Apache 2.0
// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library StringSet {
Expand Down
Loading