Skip to content

Commit

Permalink
update reference implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
kaihiroi committed Mar 29, 2024
1 parent 47c435f commit 060aa57
Show file tree
Hide file tree
Showing 14 changed files with 499 additions and 430 deletions.
6 changes: 3 additions & 3 deletions ERCS/erc-7546.md
Expand Up @@ -61,7 +61,7 @@ This contract SHOULD store the _Dictionary Contract_ address in the storage slot

Changes to the Dictionary address SHOULD emit events. When such an event is emitted, it MUST use the signature:
```solidity
event DictionaryUpgraded(address indexed dictionary);
event DictionaryUpgraded(address dictionary);
```

#### Functions
Expand All @@ -76,15 +76,15 @@ The Dictionary MUST maintain a mapping of function selectors to _Function Contra
Changes to this mapping SHOULD be communicated through an event (or log).

```solidity
event ImplementationUpgraded(bytes4 indexed functionSelector, address indexed implementation);
event ImplementationUpgraded(bytes4 functionSelector, address implementation);
```

#### Functions
##### `getImplementation`
This contract MUST implement this function to return _Function Implementation Contract_ address.

```solidity
function getImplementation(bytes4 functionSelector) external view returns (address);
function getImplementation(bytes4 functionSelector) external view returns(address implementation);
```

##### `setImplementation`
Expand Down
63 changes: 63 additions & 0 deletions assets/erc-7546/src/Dictionary.sol
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.22;

/// @dev OZ Library version has been tested with version 5.0.0.
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
@title Dictionary Contract
@dev This is the reference implementation for ERC-7546 Dictionary Contract.
*/
contract Dictionary is Ownable, IERC165 {
/**********************
Storage & Event
**********************/
mapping(bytes4 functionSelector => address implementation) implementations; // MUST
event ImplementationUpgraded(bytes4 functionSelector, address implementation); // SHOULD
error InvalidImplementation(address implementation); // OPTIONAL
bytes4[] functionSelectorList; // RECOMMENDED

constructor(address owner) Ownable(owner) {}


/***************
Functions
***************/
function getImplementation(bytes4 functionSelector) external view returns(address implementation) {
implementation = implementations[functionSelector];
}

function setImplementation(bytes4 functionSelector, address implementation) external onlyOwner {
/// @notice Mismatch Function Selector in Security Considerations section
if (implementation.code.length == 0) {
revert InvalidImplementation(implementation);
}

// In the case of a new functionSelector, add to the functionSelectorList.
bool _hasSetFunctionSelector;
bytes4[] memory _functionSelectorList = functionSelectorList;
for (uint i; i < _functionSelectorList.length; ++i) {
if (functionSelector == _functionSelectorList[i]) {
_hasSetFunctionSelector = true;
}
}
if (!_hasSetFunctionSelector) functionSelectorList.push(functionSelector);

// Add the pair of functionSelector and implementation address to the mapping.
implementations[functionSelector] = implementation;

// Notify the change of the mapping.
emit ImplementationUpgraded(functionSelector, implementation);
}

/// @dev The interfaceId equals to the function selector
function supportsInterface(bytes4 interfaceId) public view returns (bool) {
return implementations[interfaceId] != address(0);
}

function supportsInterfaces() external view returns (bytes4[] memory) {
return functionSelectorList;
}

}
66 changes: 66 additions & 0 deletions assets/erc-7546/src/Proxy.sol
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.22;

/// @dev OZ Library version has been tested with version 5.0.0.
import {Proxy as OZProxy} from "@openzeppelin/contracts/proxy/Proxy.sol";
import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

import {Dictionary} from "./Dictionary.sol";

/**
@title Proxy Contract
@dev This is the reference implementation for ERC-7546 Proxy Contract.
@dev In this reference implementation, the transfer functionality inherits from the OpenZeppelin contracts.
*/
contract Proxy is OZProxy {
/*************
Storage
*************/
/**
* @dev The only storage slot explicitly used within the Proxy Contract is for holding the Dictionary Contract address.
* @dev This slot is the keccak-256 hash of "erc7546.proxy.dictionary" subtracted by 1.
*/
bytes32 internal constant DICTIONARY_SLOT = 0x267691be3525af8a813d30db0c9e2bad08f63baecf6dceb85e2cf3676cff56f4;

constructor(address dictionary, bytes memory _data) payable {
_upgradeDictionaryToAndCall(dictionary, _data);
}


/***********
Event
***********/
event DictionaryUpgraded(address dictionary);


/************************
Internal Functions
************************/
/**
* @dev In this reference implementation, we use the OZ library to extract storage slot values as addresses.
*/
function _getDictionary() internal view returns (address) {
return StorageSlot.getAddressSlot(DICTIONARY_SLOT).value;
}

/**
* @dev Override an internal function that returns the destination address to utilize the forwarding functionality of the OZ contract's Proxy.
*/
function _implementation() internal view override returns (address) {
return Dictionary(_getDictionary()).getImplementation(msg.sig);
}

function _upgradeDictionaryToAndCall(address newDictionary, bytes memory data) internal {
/// @dev In this reference implementation, we've omitted it for simplicity,
/// but it's recommended to implement a check to ensure the Dictionary Contract is indeed a contract.
StorageSlot.getAddressSlot(DICTIONARY_SLOT).value = newDictionary;
emit DictionaryUpgraded(newDictionary);

/// @dev Similarly, it is recommended to verify that it is non-payable before proceeding with the initialization process,
/// though this has been omitted in this case.
if (data.length > 0) {
Address.functionDelegateCall(Dictionary(newDictionary).getImplementation(bytes4(data)), data);
}
}
}
81 changes: 0 additions & 81 deletions assets/erc-7546/src/dictionary/Dictionary.sol

This file was deleted.

24 changes: 0 additions & 24 deletions assets/erc-7546/src/dictionary/IDictionary.sol

This file was deleted.

28 changes: 0 additions & 28 deletions assets/erc-7546/src/proxy/ERC7546Proxy.sol

This file was deleted.

78 changes: 0 additions & 78 deletions assets/erc-7546/src/proxy/ERC7546Utils.sol

This file was deleted.

0 comments on commit 060aa57

Please sign in to comment.