rkalis / truffle-plugin-verify

✅ Verify your smart contracts on Etherscan from the Truffle CLI
https://kalis.me/verify-truffle-smart-contracts-etherscan/
MIT License
467 stars 130 forks source link

Fails to verify Argent contracts #46

Closed elenadimitrova closed 3 years ago

elenadimitrova commented 3 years ago

I have added the plugin to my truffle setup here https://github.com/argentlabs/argent-contracts/pull/146/commits/54d619e5585814ec9bcabb0f5a5eb20b375e5200 Running verification for two different contracts produces the same issue. Example below is the output for GuardianStorage validation

$ npx truffle run verify GuardianStorage --network ropsten --debug
DEBUG logging is turned ON
Running truffle-plugin-verify v0.5.0
Verifying GuardianStorage
Reading artifact file at /argent-contracts/build/contracts/GuardianStorage.json
Retrieving constructor parameters from https://api-ropsten.etherscan.io/api?apiKey=xxx&module=account&action=txlist&address=0x2a7c09350fc7c2D84577001A89FA9b37BB5F36bF&page=1&sort=asc&offset=1
Constructor parameters retrieved: 0x
Sending verify request with POST arguments:
{
  "apikey": "xxx",
  "module": "contract",
  "action": "verifysourcecode",
  "contractaddress": "0x2a7c09350fc7c2D84577001A89FA9b37BB5F36bF",
  "sourceCode": "{\"language\":\"Solidity\",\"sources\":{\"/Users/Elena/Source/argent-contracts/contracts/infrastructure/storage/IGuardianStorage.sol\":{\"content\":\"// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>\\n\\n// This program is free software: you can redistribute it and/or modify\\n// it under the terms of the GNU General Public License as published by\\n// the Free Software Foundation, either version 3 of the License, or\\n// (at your option) any later version.\\n\\n// This program is distributed in the hope that it will be useful,\\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\\n// GNU General Public License for more details.\\n\\n// You should have received a copy of the GNU General Public License\\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\\n\\n// SPDX-License-Identifier: GPL-3.0-only\\npragma solidity >=0.5.4 <0.7.0;\\n\\ninterface IGuardianStorage {\\n\\n    /**\\n     * @notice Lets an authorised module add a guardian to a wallet.\\n     * @param _wallet The target wallet.\\n     * @param _guardian The guardian to add.\\n     */\\n    function addGuardian(address _wallet, address _guardian) external;\\n\\n    /**\\n     * @notice Lets an authorised module revoke a guardian from a wallet.\\n     * @param _wallet The target wallet.\\n     * @param _guardian The guardian to revoke.\\n     */\\n    function revokeGuardian(address _wallet, address _guardian) external;\\n\\n    /**\\n     * @notice Checks if an account is a guardian for a wallet.\\n     * @param _wallet The target wallet.\\n     * @param _guardian The account.\\n     * @return true if the account is a guardian for a wallet.\\n     */\\n    function isGuardian(address _wallet, address _guardian) external view returns (bool);\\n\\n    function isLocked(address _wallet) external view returns (bool);\\n\\n    function getLock(address _wallet) external view returns (uint256);\\n\\n    function getLocker(address _wallet) external view returns (address);\\n\\n    function setLock(address _wallet, uint256 _releaseAfter) external;\\n\\n    function getGuardians(address _wallet) external view returns (address[] memory);\\n\\n    function guardianCount(address _wallet) external view returns (uint256);\\n}\"},\"/Users/Elena/Source/argent-contracts/contracts/infrastructure/storage/Storage.sol\":{\"content\":\"// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>\\n\\n// This program is free software: you can redistribute it and/or modify\\n// it under the terms of the GNU General Public License as published by\\n// the Free Software Foundation, either version 3 of the License, or\\n// (at your option) any later version.\\n\\n// This program is distributed in the hope that it will be useful,\\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\\n// GNU General Public License for more details.\\n\\n// You should have received a copy of the GNU General Public License\\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\\n\\n// SPDX-License-Identifier: GPL-3.0-only\\npragma solidity >=0.5.4 <0.7.0;\\n\\nimport \\\"../../wallet/IWallet.sol\\\";\\n\\n/**\\n * @title Storage\\n * @notice Base contract for the storage of a wallet.\\n * @author Julien Niset - <julien@argent.xyz>\\n */\\ncontract Storage {\\n\\n    /**\\n     * @notice Throws if the caller is not an authorised module.\\n     */\\n    modifier onlyModule(address _wallet) {\\n        require(IWallet(_wallet).authorised(msg.sender), \\\"TS: must be an authorized module to call this method\\\");\\n        _;\\n    }\\n}\"},\"/Users/Elena/Source/argent-contracts/contracts/infrastructure_0.5/storage/GuardianStorage.sol\":{\"content\":\"// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>\\n\\n// This program is free software: you can redistribute it and/or modify\\n// it under the terms of the GNU General Public License as published by\\n// the Free Software Foundation, either version 3 of the License, or\\n// (at your option) any later version.\\n\\n// This program is distributed in the hope that it will be useful,\\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\\n// GNU General Public License for more details.\\n\\n// You should have received a copy of the GNU General Public License\\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\\n\\npragma solidity ^0.5.4;\\n\\nimport \\\"../../infrastructure/storage/Storage.sol\\\";\\nimport \\\"../../infrastructure/storage/IGuardianStorage.sol\\\";\\n\\n/**\\n * @title GuardianStorage\\n * @notice Contract storing the state of wallets related to guardians and lock.\\n * The contract only defines basic setters and getters with no logic. Only modules authorised\\n * for a wallet can modify its state.\\n * @author Julien Niset - <julien@argent.im>\\n * @author Olivier Van Den Biggelaar - <olivier@argent.im>\\n */\\ncontract GuardianStorage is IGuardianStorage, Storage {\\n\\n    struct GuardianStorageConfig {\\n        // the list of guardians\\n        address[] guardians;\\n        // the info about guardians\\n        mapping (address => GuardianInfo) info;\\n        // the lock's release timestamp\\n        uint256 lock;\\n        // the module that set the last lock\\n        address locker;\\n    }\\n\\n    struct GuardianInfo {\\n        bool exists;\\n        uint128 index;\\n    }\\n\\n    // wallet specific storage\\n    mapping (address => GuardianStorageConfig) internal configs;\\n\\n    // *************** External Functions ********************* //\\n\\n    /**\\n     * @notice Lets an authorised module add a guardian to a wallet.\\n     * @param _wallet The target wallet.\\n     * @param _guardian The guardian to add.\\n     */\\n    function addGuardian(address _wallet, address _guardian) external onlyModule(_wallet) {\\n        GuardianStorageConfig storage config = configs[_wallet];\\n        config.info[_guardian].exists = true;\\n        config.info[_guardian].index = uint128(config.guardians.push(_guardian) - 1);\\n    }\\n\\n    /**\\n     * @notice Lets an authorised module revoke a guardian from a wallet.\\n     * @param _wallet The target wallet.\\n     * @param _guardian The guardian to revoke.\\n     */\\n    function revokeGuardian(address _wallet, address _guardian) external onlyModule(_wallet) {\\n        GuardianStorageConfig storage config = configs[_wallet];\\n        address lastGuardian = config.guardians[config.guardians.length - 1];\\n        if (_guardian != lastGuardian) {\\n            uint128 targetIndex = config.info[_guardian].index;\\n            config.guardians[targetIndex] = lastGuardian;\\n            config.info[lastGuardian].index = targetIndex;\\n        }\\n        config.guardians.length--;\\n        delete config.info[_guardian];\\n    }\\n\\n    /**\\n     * @notice Returns the number of guardians for a wallet.\\n     * @param _wallet The target wallet.\\n     * @return the number of guardians.\\n     */\\n    function guardianCount(address _wallet) external view returns (uint256) {\\n        return configs[_wallet].guardians.length;\\n    }\\n\\n    /**\\n     * @notice Gets the list of guaridans for a wallet.\\n     * @param _wallet The target wallet.\\n     * @return the list of guardians.\\n     */\\n    function getGuardians(address _wallet) external view returns (address[] memory) {\\n        GuardianStorageConfig storage config = configs[_wallet];\\n        address[] memory guardians = new address[](config.guardians.length);\\n        for (uint256 i = 0; i < config.guardians.length; i++) {\\n            guardians[i] = config.guardians[i];\\n        }\\n        return guardians;\\n    }\\n\\n    /**\\n     * @notice Checks if an account is a guardian for a wallet.\\n     * @param _wallet The target wallet.\\n     * @param _guardian The account.\\n     * @return true if the account is a guardian for a wallet.\\n     */\\n    function isGuardian(address _wallet, address _guardian) external view returns (bool) {\\n        return configs[_wallet].info[_guardian].exists;\\n    }\\n\\n    /**\\n     * @notice Lets an authorised module set the lock for a wallet.\\n     * @param _wallet The target wallet.\\n     * @param _releaseAfter The epoch time at which the lock should automatically release.\\n     */\\n    function setLock(address _wallet, uint256 _releaseAfter) external onlyModule(_wallet) {\\n        configs[_wallet].lock = _releaseAfter;\\n        if (_releaseAfter != 0 && msg.sender != configs[_wallet].locker) {\\n            configs[_wallet].locker = msg.sender;\\n        }\\n    }\\n\\n    /**\\n     * @notice Checks if the lock is set for a wallet.\\n     * @param _wallet The target wallet.\\n     * @return true if the lock is set for the wallet.\\n     */\\n    function isLocked(address _wallet) external view returns (bool) {\\n        return configs[_wallet].lock > block.timestamp;\\n    }\\n\\n    /**\\n     * @notice Gets the time at which the lock of a wallet will release.\\n     * @param _wallet The target wallet.\\n     * @return the time at which the lock of a wallet will release, or zero if there is no lock set.\\n     */\\n    function getLock(address _wallet) external view returns (uint256) {\\n        return configs[_wallet].lock;\\n    }\\n\\n    /**\\n     * @notice Gets the address of the last module that modified the lock for a wallet.\\n     * @param _wallet The target wallet.\\n     * @return the address of the last module that modified the lock for a wallet.\\n     */\\n    function getLocker(address _wallet) external view returns (address) {\\n        return configs[_wallet].locker;\\n    }\\n}\"},\"/Users/Elena/Source/argent-contracts/contracts/wallet/IWallet.sol\":{\"content\":\"// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>\\n\\n// This program is free software: you can redistribute it and/or modify\\n// it under the terms of the GNU General Public License as published by\\n// the Free Software Foundation, either version 3 of the License, or\\n// (at your option) any later version.\\n\\n// This program is distributed in the hope that it will be useful,\\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\\n// GNU General Public License for more details.\\n\\n// You should have received a copy of the GNU General Public License\\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\\n\\n// SPDX-License-Identifier: GPL-3.0-only\\npragma solidity >=0.5.4 <0.7.0;\\n\\n/**\\n * @title IWallet\\n * @notice Interface for the BaseWallet\\n */\\ninterface IWallet {\\n    /**\\n     * @notice Returns the wallet owner.\\n     * @return The wallet owner address.\\n     */\\n    function owner() external view returns (address);\\n\\n    /**\\n     * @notice Returns the number of authorised modules.\\n     * @return The number of authorised modules.\\n     */\\n    function modules() external view returns (uint);\\n\\n    /**\\n     * @notice Sets a new owner for the wallet.\\n     * @param _newOwner The new owner.\\n     */\\n    function setOwner(address _newOwner) external;\\n\\n    /**\\n     * @notice Checks if a module is authorised on the wallet.\\n     * @param _module The module address to check.\\n     * @return `true` if the module is authorised, otherwise `false`.\\n     */\\n    function authorised(address _module) external view returns (bool);\\n\\n    /**\\n     * @notice Returns the module responsible for a static call redirection.\\n     * @param _sig The signature of the static call.\\n     * @return the module doing the redirection\\n     */\\n    function enabled(bytes4 _sig) external view returns (address);\\n\\n    /**\\n     * @notice Enables/Disables a module.\\n     * @param _module The target module.\\n     * @param _value Set to `true` to authorise the module.\\n     */\\n    function authoriseModule(address _module, bool _value) external;\\n\\n    /**\\n    * @notice Enables a static method by specifying the target module to which the call must be delegated.\\n    * @param _module The target module.\\n    * @param _method The static method signature.\\n    */\\n    function enableStaticCall(address _module, bytes4 _method) external;\\n}\"}},\"settings\":{\"remappings\":[],\"optimizer\":{\"enabled\":true,\"runs\":999},\"evmVersion\":\"byzantium\",\"libraries\":{\"\":{}}}}",
  "codeformat": "solidity-standard-json-input",
  "contractname": "/Users/Elena/Source/argent-contracts/contracts/infrastructure_0.5/storage/GuardianStorage.sol:GuardianStorage",
  "compilerversion": "v0.5.4+commit.9549d8ff.Linux.g++",
  "constructorArguements": ""
}
Checking status of verification request cnvw4fqmrlyscrwxrgpebdngpehylcdib61vydtusrgn71nfqh
Fail - Unable to verify
Failed to verify 1 contract(s): GuardianStorage

When checking on the EtherScan API the result for the above verification request I get:

{"status":"0","message":"NOTOK","result":"Fail - Unable to verify"}

I haven't been able to get anything more descriptive than that error and I have been able to verify that contract (on a different network) manually. Any help is appreciated.

rkalis commented 3 years ago

Hey @elenadimitrova, I somehow missed this report. Sorry about that.

I took a look at your repository, but it's a bit more complicated than most Truffle projects, so could you provide me with some instructions on what to run to compile + deploy your contracts to a testnet, then I'll try to look into this.

elenadimitrova commented 3 years ago

@rkalis to reproduce, please use the migrate-to-truffle branch here https://github.com/argentlabs/argent-contracts/tree/experiment/migrate-to-truffle Run npm run cc to compile everything. Since we are not using truffle migrations we need to pass the contract addresses explicitly:

npx truffle run verify ApprovedTransfer@0x2d5186B00eE04d7ca6DBf3E3E07ce6003DFFEF2B --network test --debug

This currently produces the following error

Fail - Unable to verify
Failed to verify 1 contract(s): ApprovedTransfer@0x2d5186B00eE04d7ca6DBf3E3E07ce6003DFFEF2B
rkalis commented 3 years ago

Hey @elenadimitrova, I managed to verify the contract manually, and I think I tracked down the issue in the plugin. Do you have another deployed contract name + address that I can use to test it further, since I verified ApprovedTransfer@0x2d5186B00eE04d7ca6DBf3E3E07ce6003DFFEF2B manually through the form.

elenadimitrova commented 3 years ago

Thanks for your help @rkalis , please use the following:

WalletFactory@0x31301b8ab26814a40a24128ee1d984fbd66c6992
CompoundRegistry@0xbfad9acd0d1529cec09d15896d873e46928f19df
BaseWallet@0xa1832b5d79bdbba645fd6969275ee1c6cf503e99
MakerV2Manager@0x9efcd441bd4d1d25f066347dfa1f33214a741080

I can provide more if needed.

rkalis commented 3 years ago

Alright, looks like I've been able to fix the bug that caused this, I've been able to verify WalletFactory, BaseWallet and MakerV2Manager using the plugin locally. I haven't published the new version though.

I wasn't able to verify CompoundRegistry, but it's because of some different reason that I haven't been able to figure out. It is the only one of the 4 that you listed that uses 0.5.4 rather than 0.6.12. Is there anything special about your deployment settings for the 0.5 contracts as opposed to the 0.6 contracts?

elenadimitrova commented 3 years ago

There are nine different groups of contracts compiled differently. The contracts group CompoundRegistry lives in has the following compilation settings configured https://github.com/argentlabs/argent-contracts/blob/cf7e41fa6e9e9f2bd4ff6be464173030884f4098/truffle-config-infrastructure-0.5.js#L7

Although I am not too worried about verifying the 0.5 contracts as those are legacy and verified and will likely never be redeployed and verified again. Still here are some other 0.5 based examples you can try with to be sure it's a problem with that solc version:

ModuleRegistry@0xfe0a3ea221de42612bd6782bbba0c71feb20dc29
ArgentENSResolver@0x728ce7265872f12139cac55906cd7cbbcbec5f3a
rkalis commented 3 years ago

I tried those contracts and they also both failed, so it does appear to be related to that somehow. If you're not too concerned with verifying the 0.5 contracts, I'll publish the bug-fix to a new version of the plugin, so you can try it out with the 0.6 contracts.

rkalis commented 3 years ago

I just published truffle-plugin-verify@0.5.2, which includes the fix to the first issue. Let me know if you still encounter issues with different contracts when using this version.

elenadimitrova commented 3 years ago

Thanks @rkalis that solves this issue 🎉 . Closing now.

rkalis commented 3 years ago

So you mean to call the verification as a JS function?

I've never done it before, but I suppose you could require the plugin and then call the exported function, but you'd need to pass in the contract parameter into the function somehow.

const verifyContracts = require('truffle-plugin-verify');

// Get the current truffle config from truffle
const config = ...;

// Add truffle-plugin-verify specific config parameters
config._ = ['verify', 'ContractName1', 'ContractName2', ...];
config.network_id = 3; // ropsten

// Verify contracts
verifyContracts(config);

Note that the function can make process.exit() calls if something goes wrong, and it logs results to stdout/stderr (and doesn't return anything). It should be possible to update the plugin so that it has more first-class support for this kind of workflow, but currently it's kind of convoluted.