Skip to content

Commit

Permalink
add Versioned contract
Browse files Browse the repository at this point in the history
  • Loading branch information
kovalgek committed Apr 21, 2024
1 parent 4ac5b44 commit 1cf4329
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 38 deletions.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion contracts/token/ERC20Bridged.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ contract ERC20Bridged is IERC20Bridged, ERC20Core, ERC20Metadata {
/// @notice Sets the name and the symbol of the tokens if they both are empty
/// @param name_ The name of the token
/// @param symbol_ The symbol of the token
function initializeERC20Metadata(string memory name_, string memory symbol_) public {
function _initializeERC20Metadata(string memory name_, string memory symbol_) internal {
_setERC20MetadataName(name_);
_setERC20MetadataSymbol(symbol_);
}
Expand Down
26 changes: 22 additions & 4 deletions contracts/token/ERC20BridgedPermit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ pragma solidity 0.8.10;

import {ERC20Bridged} from "./ERC20Bridged.sol";
import {PermitExtension} from "./PermitExtension.sol";
import {Versioned} from "../utils/Versioned.sol";

/// @author kovalgek
contract ERC20BridgedPermit is ERC20Bridged, PermitExtension {
contract ERC20BridgedPermit is ERC20Bridged, PermitExtension, Versioned {

/// @param name_ The name of the token
/// @param symbol_ The symbol of the token
Expand All @@ -26,13 +27,30 @@ contract ERC20BridgedPermit is ERC20Bridged, PermitExtension {
{
}

/// @notice Sets the name, the symbol and the version of the tokens if they are empty
/// @notice Initializes the contract from scratch.
/// @param name_ The name of the token
/// @param symbol_ The symbol of the token
/// @param version_ The version of the token
function initialize(string memory name_, string memory symbol_, string memory version_) external {
initializeERC20Metadata(name_, symbol_);
initializeEIP5267Metadata(name_, version_);
_initialize_v2(name_, symbol_, version_);
}

/// @notice A function to finalize upgrade to v2 (from v1).
function finalizeUpgrade_v2(string memory name_, string memory version_) external {
// name and symbol from ERCMetadata already set up in storage, then it is defenetly was v1
if (bytes(name()).length > 0 && bytes(symbol()).length > 0) {
_updateContractVersion(2);
_initializeEIP5267Metadata(name_, version_);
} else {
// metadata is epmty, incorrect function to call
// error?
}
}

function _initialize_v2(string memory name_, string memory symbol_, string memory version_) internal {
_updateContractVersion(2);
_initializeERC20Metadata(name_, symbol_);
_initializeEIP5267Metadata(name_, version_);
}

/// @inheritdoc PermitExtension
Expand Down
9 changes: 0 additions & 9 deletions contracts/token/ERC20Metadata.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,11 @@ contract ERC20Metadata is IERC20Metadata {

/// @dev Sets the name of the token. Might be called only when the name is empty
function _setERC20MetadataName(string memory name_) internal {
if (bytes(name()).length > 0) {
revert ErrorNameAlreadySet();
}
_loadDynamicMetadata().name = name_;
}

/// @dev Sets the symbol of the token. Might be called only when the symbol is empty
function _setERC20MetadataSymbol(string memory symbol_) internal {
if (bytes(symbol()).length > 0) {
revert ErrorSymbolAlreadySet();
}
_loadDynamicMetadata().symbol = symbol_;
}

Expand All @@ -85,7 +79,4 @@ contract ERC20Metadata is IERC20Metadata {
r.slot := slot
}
}

error ErrorNameAlreadySet();
error ErrorSymbolAlreadySet();
}
20 changes: 10 additions & 10 deletions contracts/token/ERC20RebasableBridged.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
import {IERC20Wrapper} from "./interfaces/IERC20Wrapper.sol";
import {ITokenRateOracle} from "../optimism/TokenRateOracle.sol";
import {ERC20Metadata} from "./ERC20Metadata.sol";
import {UnstructuredRefStorage} from "./UnstructuredRefStorage.sol";
import {UnstructuredStorage} from "./UnstructuredStorage.sol";
import {UnstructuredRefStorage} from "../lib/UnstructuredRefStorage.sol";
import {UnstructuredStorage} from "../lib/UnstructuredStorage.sol";

/// @author kovalgek
/// @notice Extends the ERC20 functionality that allows the bridge to mint/burn shares
Expand Down Expand Up @@ -207,14 +207,6 @@ contract ERC20RebasableBridged is IERC20, IERC20Wrapper, IERC20BridgedShares, ER
return tokensAmount;
}

/// @notice Sets the name and the symbol of the tokens if they both are empty
/// @param name_ The name of the token
/// @param symbol_ The symbol of the token
function initializeERC20Metadata(string memory name_, string memory symbol_) public {
_setERC20MetadataName(name_);
_setERC20MetadataSymbol(symbol_);
}

function _getTokenAllowance() internal pure returns (mapping(address => mapping(address => uint256)) storage) {
return TOKEN_ALLOWANCE_POSITION.storageMapAddressMapAddressUint256();
}
Expand Down Expand Up @@ -371,6 +363,14 @@ contract ERC20RebasableBridged is IERC20, IERC20Wrapper, IERC20BridgedShares, ER
emit TransferShares(_from, _to, _sharesAmount);
}

/// @notice Sets the name and the symbol of the tokens if they both are empty
/// @param name_ The name of the token
/// @param symbol_ The symbol of the token
function _initializeERC20Metadata(string memory name_, string memory symbol_) internal {
_setERC20MetadataName(name_);
_setERC20MetadataSymbol(symbol_);
}

/// @dev validates that account_ is not zero address
modifier onlyNonZeroAccount(address account_) {
if (account_ == address(0)) {
Expand Down
8 changes: 5 additions & 3 deletions contracts/token/ERC20RebasableBridgedPermit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ pragma solidity 0.8.10;

import {ERC20RebasableBridged} from "./ERC20RebasableBridged.sol";
import {PermitExtension} from "./PermitExtension.sol";
import {Versioned} from "../utils/Versioned.sol";

/// @author kovalgek
contract ERC20RebasableBridgedPermit is ERC20RebasableBridged, PermitExtension {
contract ERC20RebasableBridgedPermit is ERC20RebasableBridged, PermitExtension, Versioned {

/// @param name_ The name of the token
/// @param symbol_ The symbol of the token
Expand Down Expand Up @@ -35,8 +36,9 @@ contract ERC20RebasableBridgedPermit is ERC20RebasableBridged, PermitExtension {
/// @param symbol_ The symbol of the token
/// @param version_ The version of the token
function initialize(string memory name_, string memory symbol_, string memory version_) external {
initializeERC20Metadata(name_, symbol_);
initializeEIP5267Metadata(name_, version_);
_initializeContractVersionTo(1);
_initializeERC20Metadata(name_, symbol_);
_initializeEIP5267Metadata(name_, version_);
}

/// @inheritdoc PermitExtension
Expand Down
14 changes: 3 additions & 11 deletions contracts/token/PermitExtension.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

pragma solidity 0.8.10;

import {UnstructuredRefStorage} from "./UnstructuredRefStorage.sol";
import {UnstructuredRefStorage} from "../lib//UnstructuredRefStorage.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
import {IERC2612} from "@openzeppelin/contracts/interfaces/draft-IERC2612.sol";
import {SignatureChecker} from "../lib/SignatureChecker.sol";
Expand Down Expand Up @@ -33,13 +33,13 @@ abstract contract PermitExtension is IERC2612, EIP712 {
/// @param name_ The name of the token
/// @param version_ The current major version of the signing domain (aka token version)
constructor(string memory name_, string memory version_) EIP712(name_, version_) {
initializeEIP5267Metadata(name_, version_);
_initializeEIP5267Metadata(name_, version_);
}

/// @notice Sets the name and the version of the tokens if they both are empty
/// @param name_ The name of the token
/// @param version_ The version of the token
function initializeEIP5267Metadata(string memory name_, string memory version_) public {
function _initializeEIP5267Metadata(string memory name_, string memory version_) internal {
_setEIP5267MetadataName(name_);
_setEIP5267MetadataVersion(version_);
}
Expand Down Expand Up @@ -142,22 +142,14 @@ abstract contract PermitExtension is IERC2612, EIP712 {

/// @dev Sets the name of the token. Might be called only when the name is empty
function _setEIP5267MetadataName(string memory name_) internal {
if (bytes(_loadEIP5267Metadata().name).length > 0) {
revert ErrorEIP5267NameAlreadySet();
}
_loadEIP5267Metadata().name = name_;
}

/// @dev Sets the version of the token. Might be called only when the version is empty
function _setEIP5267MetadataVersion(string memory version_) internal {
if (bytes(_loadEIP5267Metadata().version).length > 0) {
revert ErrorEIP5267VersionAlreadySet();
}
_loadEIP5267Metadata().version = version_;
}

error ErrorInvalidSignature();
error ErrorDeadlineExpired();
error ErrorEIP5267NameAlreadySet();
error ErrorEIP5267VersionAlreadySet();
}
60 changes: 60 additions & 0 deletions contracts/utils/Versioned.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: 2022 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.10;

import {UnstructuredStorage} from "../lib//UnstructuredStorage.sol";

contract Versioned {
using UnstructuredStorage for bytes32;

event ContractVersionSet(uint256 version);

error NonZeroContractVersionOnInit();
error InvalidContractVersionIncrement();
error UnexpectedContractVersion(uint256 expected, uint256 received);

/// @dev Storage slot: uint256 version
/// Version of the initialized contract storage.
/// The version stored in CONTRACT_VERSION_POSITION equals to:
/// - 0 right after the deployment, before an initializer is invoked (and only at that moment);
/// - N after calling initialize(), where N is the initially deployed contract version;
/// - N after upgrading contract by calling finalizeUpgrade_vN().
bytes32 internal constant CONTRACT_VERSION_POSITION = keccak256("lido.Versioned.contractVersion");

uint256 internal constant PETRIFIED_VERSION_MARK = type(uint256).max;

constructor() {
// lock version in the implementation's storage to prevent initialization
CONTRACT_VERSION_POSITION.setStorageUint256(PETRIFIED_VERSION_MARK);
}

/// @notice Returns the current contract version.
function getContractVersion() public view returns (uint256) {
return CONTRACT_VERSION_POSITION.getStorageUint256();
}

function _checkContractVersion(uint256 version) internal view {
uint256 expectedVersion = getContractVersion();
if (version != expectedVersion) {
revert UnexpectedContractVersion(expectedVersion, version);
}
}

/// @dev Sets the contract version to N. Should be called from the initialize() function.
function _initializeContractVersionTo(uint256 version) internal {
if (getContractVersion() != 0) revert NonZeroContractVersionOnInit();
_setContractVersion(version);
}

/// @dev Updates the contract version. Should be called from a finalizeUpgrade_vN() function.
function _updateContractVersion(uint256 newVersion) internal {
if (newVersion != getContractVersion() + 1) revert InvalidContractVersionIncrement();
_setContractVersion(newVersion);
}

function _setContractVersion(uint256 version) private {
CONTRACT_VERSION_POSITION.setStorageUint256(version);
emit ContractVersionSet(version);
}
}

0 comments on commit 1cf4329

Please sign in to comment.