Solidityで複数ファイルに分けたコントラクトをimportするとき、
DeclarationError: Identifier already declared
と言われてcompileに失敗することがある。
一方で、失敗しないときもある。その違いを探す。
1. 失敗しないとき
ContractA.sol
pragma solidity ^0.4.23;
import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";
contract ContractA is Ownable {
constructor () public {
owner = msg.sender;
}
}
ContractB.sol
pragma solidity ^0.4.23;
import "./ContractA.sol";
import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";
contract ContractB is Ownable {
ContractA contractA;
constructor (ContractA _aAddress) public {
owner = msg.sender;
contractA = _aAddress;
}
}
2. 失敗するとき
ContractA.sol
pragma solidity ^0.4.23;
import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";
contract ContractA is Ownable {
constructor () public {
owner = msg.sender;
}
}
ContractB.sol
pragma solidity ^0.4.23;
import "./ContractA.sol";
import "../node_modules/openzeppelin-zos/contracts/ownership/Ownable.sol";
contract ContractB is Ownable {
ContractA contractA;
constructor (ContractA _aAddress) public {
owner = msg.sender;
contractA = _aAddress;
}
}
エラー内容
DeclarationError: Identifier already declared.
import "../node_modules/openzeppelin-zos/contracts/ownership/Ownable.sol";
^------------------------------------------------------------------------^
3. 原因
間違い探しみたいになってしまったが、1では全く同じOwnableをimportしている。
つまり、同じコントラクトを複数回importしてもエラーにはならない。
一方2では、ContractAでzeppelin-solidityのOwnableを、ContractBでzeppelin-zosのOwnableをimportしている。
これは同じ名前の別のコントラクトを実装していることになりDeclarationError: Identifier already declared.
が引き起こされる。
このように自分でimportを書いている場合、このエラーに気づかないことはあまりないかもしれないが、ライブラリの内部的にimportが行われている場合には、気づくのが難しかったりする。特に、細かいversionの違いが厄介なことがある。
4. 対処法
- 同じパッケージを使うように注意する
-
import {必要なもの} from ファイル名
の構文を使って、なるべく不要なものはimportしないようにする。
例えば、2を以下のように変更するとエラーにならない。
ContractA.sol
pragma solidity ^0.4.23;
import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";
contract ContractA is Ownable {
constructor () public {
owner = msg.sender;
}
}
ContractB.sol
pragma solidity ^0.4.23;
import {ContractA} from "./ContractA.sol";
import "../node_modules/openzeppelin-zos/contracts/ownership/Ownable.sol";
contract ContractB is Ownable {
ContractA contractA;
constructor (ContractA _aAddress) public {
owner = msg.sender;
contractA = _aAddress;
}
}