Contract is not upgrade safe, Defining structs is not yet supported

I am working on Integration component(which will shows current market rate of our deployed token) .whenever i deployed that code it shows the code is not Upgrade safe
i am using Buidler plugin for deployment.can anyone help me to sort out why this error occuring.herewith i am attaching some screenshot and codes.

code:
pragma solidity >=0.4.24;

// SPDX-License-Identifier: MIT

import "./Select.sol";

import "../math/SafeMath.sol";

import "../ownership/Ownable.sol";

interface IOracle {

    function getData() external returns (uint256, bool);

}

/**

 * @title Median Oracle

 *

 * @notice Provides a value onchain that's aggregated from a whitelisted set of

 *         providers.

 */

contract EnbOracle is OwnableUpgradeSafe, IOracle {

    using SafeMath for uint256;

    struct Report {

        uint256 timestamp;

        uint256 payload;

    }

    // Addresses of providers authorized to push reports.

    address[] public providers;

    // Reports indexed by provider address. Report[0].timestamp > 0

    // indicates provider existence.

    mapping (address => Report[2]) public providerReports;

    event ProviderAdded(address provider);

    event ProviderRemoved(address provider);

    event ReportTimestampOutOfRange(address provider);

    event ProviderReportPushed(address indexed provider, uint256 payload, uint256 timestamp);

    // The number of seconds after which the report is deemed expired.

    uint256 public reportExpirationTimeSec;

    // The number of seconds since reporting that has to pass before a report

    // is usable.

    uint256 public reportDelaySec;

    // The minimum number of providers with valid reports to consider the

    // aggregate report valid.

    uint256 public minimumProviders;

    // Timestamp of 1 is used to mark uninitialized and invalidated data.

    // This is needed so that timestamp of 1 is always considered expired.

    uint256 private constant MAX_REPORT_EXPIRATION_TIME = 520 weeks;

    /**

    * @param reportExpirationTimeSec_ The number of seconds after which the

    *                                 report is deemed expired.

    * @param reportDelaySec_ The number of seconds since reporting that has to

    *                        pass before a report is usable

    * @param minimumProviders_ The minimum number of providers with valid

    *                          reports to consider the aggregate report valid.

    */

    function initialize(uint256 reportExpirationTimeSec_,

                uint256 reportDelaySec_,

                uint256 minimumProviders_)

        public

    {

        minimumProviders = 1;

        require(reportExpirationTimeSec_ <= MAX_REPORT_EXPIRATION_TIME);

        require(minimumProviders_ > 0);

        reportExpirationTimeSec = reportExpirationTimeSec_;

        reportDelaySec = reportDelaySec_;

        minimumProviders = minimumProviders_;

    }

     /**

     * @notice Sets the report expiration period.

     * @param reportExpirationTimeSec_ The number of seconds after which the

     *        report is deemed expired.

     */

    function setReportExpirationTimeSec(uint256 reportExpirationTimeSec_)

        external

        onlyOwner

    {

        require(reportExpirationTimeSec_ <= MAX_REPORT_EXPIRATION_TIME);

        reportExpirationTimeSec = reportExpirationTimeSec_;

    }

    /**

    * @notice Sets the time period since reporting that has to pass before a

    *         report is usable.

    * @param reportDelaySec_ The new delay period in seconds.

    */

    function setReportDelaySec(uint256 reportDelaySec_)

        external

        onlyOwner

    {

        reportDelaySec = reportDelaySec_;

    }

    /**

    * @notice Sets the minimum number of providers with valid reports to

    *         consider the aggregate report valid.

    * @param minimumProviders_ The new minimum number of providers.

    */

    function setMinimumProviders(uint256 minimumProviders_)

        external

        onlyOwner

    {

        require(minimumProviders_ > 0);

        minimumProviders = minimumProviders_;

    }

    /**

     * @notice Pushes a report for the calling provider.

     * @param payload is expected to be 18 decimal fixed point number.

     */

    function pushReport(uint256 payload) external

    {

        address providerAddress = msg.sender;

        Report[2] storage reports = providerReports[providerAddress];

        uint256[2] memory timestamps = [reports[0].timestamp, reports[1].timestamp];

        require(timestamps[0] > 0);

        uint8 index_recent = timestamps[0] >= timestamps[1] ? 0 : 1;

        uint8 index_past = 1 - index_recent;

        // Check that the push is not too soon after the last one.

        require(timestamps[index_recent].add(reportDelaySec) <= block.timestamp);

        reports[index_past].timestamp = block.timestamp;

        reports[index_past].payload = payload;

        emit ProviderReportPushed(providerAddress, payload, block.timestamp);

    }

    /**

    * @notice Invalidates the reports of the calling provider.

    */

    function purgeReports() external

    {

        address providerAddress = msg.sender;

        require (providerReports[providerAddress][0].timestamp > 0);

        providerReports[providerAddress][0].timestamp=1;

        providerReports[providerAddress][1].timestamp=1;

    }

    /**

    * @notice Computes median of provider reports whose timestamps are in the

    *         valid timestamp range.

    * @return AggregatedValue: Median of providers reported values.

    *         valid: Boolean indicating an aggregated value was computed successfully.

    */

    function getData()

        external override

        returns (uint256, bool)

    {

        uint256 reportsCount = providers.length;

        uint256[] memory validReports = new uint256[](reportsCount);

        uint256 size = 0;

        uint256 minValidTimestamp =  block.timestamp.sub(reportExpirationTimeSec);

        uint256 maxValidTimestamp =  block.timestamp.sub(reportDelaySec);

        for (uint256 i = 0; i < reportsCount; i++) {

            address providerAddress = providers[i];

            Report[2] memory reports = providerReports[providerAddress];

            uint8 index_recent = reports[0].timestamp >= reports[1].timestamp ? 0 : 1;

            uint8 index_past = 1 - index_recent;

            uint256 reportTimestampRecent = reports[index_recent].timestamp;

            if (reportTimestampRecent > maxValidTimestamp) {

                // Recent report is too recent.

                uint256 reportTimestampPast = providerReports[providerAddress][index_past].timestamp;

                if (reportTimestampPast < minValidTimestamp) {

                    // Past report is too old.

                    emit ReportTimestampOutOfRange(providerAddress);

                } else if (reportTimestampPast > maxValidTimestamp) {

                    // Past report is too recent.

                    emit ReportTimestampOutOfRange(providerAddress);

                } else {

                    // Using past report.

                    validReports[size++] = providerReports[providerAddress][index_past].payload;

                }

            } else {

                // Recent report is not too recent.

                if (reportTimestampRecent < minValidTimestamp) {

                    // Recent report is too old.

                    emit ReportTimestampOutOfRange(providerAddress);

                } else {

                    // Using recent report.

                    validReports[size++] = providerReports[providerAddress][index_recent].payload;

                }

            }

        }

        if (size < minimumProviders) {

            return (0, false);

        }

        return (Select.computeMedian(validReports, size), true);

    }

    /**

     * @notice Authorizes a provider.

     * @param provider Address of the provider.

     */

    function addProvider(address provider)

        external

        onlyOwner

    {

        require(providerReports[provider][0].timestamp == 0);

        providers.push(provider);

        providerReports[provider][0].timestamp = 1;

        emit ProviderAdded(provider);

    }

    /**

     * @notice Revokes provider authorization.

     * @param provider Address of the provider.

     */

    function removeProvider(address provider)

        external

        onlyOwner

    {

        delete providerReports[provider];

        for (uint256 i = 0; i < providers.length; i++) {

            if (providers[i] == provider) {

                if (i + 1  != providers.length) {

                    providers[i] = providers[providers.length-1];

                }

               // providers.length--;

                emit ProviderRemoved(provider);

                break;

            }

        }

    }

    /**

     * @return The number of authorized providers.

     */

    function providersSize()

        external

        view

        returns (uint256)

    {

        return providers.length;

    }

}


pragma solidity >=0.6.8;

// SPDX-License-Identifier: MIT

import "../math/SafeMath.sol";

library Select {

    using SafeMath for uint256;

    /**

     * @dev Sorts the input array up to the denoted size, and returns the median.

     * @param array Input array to compute its median.

     * @param size Number of elements in array to compute the median for.

     * @return Median of array.

     */

    function computeMedian(uint256[] memory array, uint256 size)

        internal

        pure

        returns (uint256)

    {

        require(size > 0 && array.length >= size);

        for (uint256 i = 1; i < size; i++) {

            for (uint256 j = i; j > 0 && array[j-1]  > array[j]; j--) {

                uint256 tmp = array[j];

                array[j] = array[j-1];

                array[j-1] = tmp;

            }

        }

        if (size % 2 == 1) {

            return array[size / 2];

        } else {

            return array[size / 2].add(array[size / 2 - 1]) / 2;

        }

    }

}
1 Like

Hey, the OpenZeppelin team just released a new version for upgrading contract, you can have a check at here: First release of OpenZeppelin Contracts Upgradeable.
Please format your code by ```

1 Like

Can you show us the full error message you are seeing?

1 Like

Hi @frangio thanks for responding my query.This was the full error message i got.

(node:13388) UnhandledPromiseRejectionWarning: Error: Contract EnbOracle is not upgrade safe

contracts\interface\EnbOracle.sol:21: Defining structs like Report is not yet supported
If you have manually checked for storage layout compatibility, you can skip this check with the unsafeAllowCustomTypes flag
https://zpl.in/upgrades/error-007
at Object.assertUpgradeSafe (D:\EnbOracle\node_modules@openzeppelin\upgrades-core\src\validate.ts:177:11)
at Proxy.deployProxy (D:\EnbOracle\node_modules@openzeppelin\buidler-upgrades\src\deploy-proxy.ts:35:5)
at main (D:\EnbOracle\scripts\deploy.js:6:15)
(node:13388) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of
an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:13388) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that
are not handled will terminate the Node.js process with a non-zero exit code.

1 Like

Hi @Skyge thanks , i will check it out the First release of OpenZeppelin Contracts Upgradeable

1 Like

You have to use the flag mentioned in the error message because we don't yet support checking structs automatically.

For example:

3 Likes

Hi @frangio i add { unsafeAllowCustomTypes: true } that too … but i got same error.

1 Like

Ok, I see. The issue is that if you’re providing the options argument you should also provide the args argument (in this case an empty array). It should be:

upgrades.deployProxy(EnbOracle, [], { unsafeAllowCustomTypes: true });

We could infer this automatically, I will take a look at that during the week.

2 Likes

After your suggestion I added empty array too but it shows this kind of error and warning.

1 Like

Hi @Prabhakaran,

You will continue to get the warning about Potentially unsafe deployment of EnbOracle to remind you to manually check the storage layout for incompatibilities during upgrades.

Thanks for the response @abcoathup .
“how to manually check the storage incompatibility” ,can you please suggest on how to do this.

1 Like

Hi @Prabhakaran,

Write higher level tests that set state prior to upgrade and then check state after the upgrade to ensure that state is maintained. Every state variable should be checked.

See examples:

1 Like

Okay thanks @abcoathup I will check it out …

1 Like

Hi @Prabhakaran,

We have found an error in Upgrades Plugins for Truffle and Hardhat. Users of the flag unsafeAllowCustomTypes should update their dependencies to the latest version.

See post for more details: Problem in Upgrades Plugins for users of unsafeAllowCustomTypes.

2 Likes

@Prabhakaran The latest release of the Upgrades plugins supports structs. Storage is now automatically checked for compatibility. Take a look at the announcement!

1 Like