在以太坊智能合约开发中,继承是Solidity提供的一种强大机制,用于代码复用、模块化和功能扩展。通过继承,开发者可以创建可重用的基合约,并在派生合约中扩展或修改功能,从而提高开发效率并减少重复代码。继承简介什么是继承?Solidity的继承允许一个合约(派生合约)从另一个合约(基合约)
在以太坊智能合约开发中,继承是 Solidity 提供的一种强大机制,用于代码复用、模块化和功能扩展。通过继承,开发者可以创建可重用的基合约,并在派生合约中扩展或修改功能,从而提高开发效率并减少重复代码。
Solidity 的继承允许一个合约(派生合约)从另一个合约(基合约)继承状态变量、函数和修饰器。派生合约可以:
virtual
和 override
)。Solidity 使用 is
关键字实现继承,派生合约继承基合约的所有 public
和 internal
成员。
示例:简单的单继承
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Base {
uint256 public data;
function setData(uint256 value) public {
data = value;
}
function getData() public view returns (uint256) {
return data;
}
}
contract Derived is Base {
function incrementData() public {
data += 1;
}
}
说明:
Derived
继承 Base
,可直接访问 data
和 setData
。incrementData
是 Derived
的新功能,扩展了 Base
。public
和 internal
成员自动继承,private
成员不可继承。派生合约需要正确初始化基合约的构造函数。
示例:构造函数继承
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Base {
uint256 public data;
constructor(uint256 initialValue) {
data = initialValue;
}
}
contract Derived is Base {
constructor(uint256 initialValue) Base(initialValue) {
}
function incrementData() public {
data += 1;
}
}
说明:
Derived
的构造函数通过 Base(initialValue)
调用基合约的构造函数。基合约的函数可标记为 virtual
,允许派生合约通过 override
重写。
示例:函数重写
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Base {
function getValue() public pure virtual returns (string memory) {
return "Base Value";
}
}
contract Derived is Base {
function getValue() public pure override returns (string memory) {
return "Derived Value";
}
}
说明:
virtual
表示函数可被重写。override
表示派生合约覆盖了基合约函数。Solidity 支持多继承,派生合约可以继承多个基合约,但需注意菱形继承问题。
示例:多继承
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Base1 {
function getName() public pure virtual returns (string memory) {
return "Base1";
}
}
contract Base2 {
function getVersion() public pure virtual returns (uint256) {
return 2;
}
}
contract Derived is Base1, Base2 {
function getName() public pure override returns (string memory) {
return "Derived";
}
}
说明:
Derived
继承 Base1
和 Base2
,并重写 Base1
的 getName
。菱形继承问题: 当多个基合约继承自同一父合约,可能导致状态变量或函数冲突。Solidity 使用线性化(C3 Linearization)解析继承顺序。
示例:菱形继承
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Root {
uint256 public data = 1;
}
contract Base1 is Root {
function getData() public view virtual returns (uint256) {
return data;
}
}
contract Base2 is Root {
function getData() public view virtual returns (uint256) {
return data + 1;
}
}
contract Derived is Base1, Base2 {
function getData() public view override(Base1, Base2) returns (uint256) {
return data + 2;
}
}
说明:
Base1
和 Base2
都继承 Root
,共享 data
。Derived
通过 override(Base1, Base2)
明确重写 getData
。Derived -> Base1 -> Base2 -> Root
。抽象合约定义未实现(或部分实现)的函数,派生合约必须实现。
示例:抽象合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
abstract contract AbstractBase {
uint256 public data;
function setData(uint256 value) public {
data = value;
}
function processData() public virtual returns (uint256);
}
contract Derived is AbstractBase {
function processData() public pure override returns (uint256) {
return 42;
}
}
说明:
AbstractBase
使用 abstract
关键字,包含未实现的 processData
。Derived
必须实现 processData
,否则无法编译。接口定义函数签名但不实现,强制派生合约实现所有函数。
示例:接口
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IBase {
function getValue() external view returns (uint256);
}
contract Derived is IBase {
uint256 public data = 100;
function getValue() external view override returns (uint256) {
return data;
}
}
说明:
interface
关键字,所有函数默认为 external
和 virtual
。Derived
实现 IBase
的 getValue
。以下是一个结合继承、访问控制和安全数学运算的 NFT 合约,基于 ERC721 标准。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SecureNFT is ERC721, Ownable, Pausable, ReentrancyGuard {
uint256 public constant MAX_SUPPLY = 1000;
uint256 public totalSupply;
uint256 public mintPrice = 0.1 ether;
event Minted(address indexed to, uint256 tokenId);
constructor() ERC721("SecureNFT", "SNFT") Ownable(msg.sender) {}
function mint(address to) public payable nonReentrant whenNotPaused {
require(totalSupply < MAX_SUPPLY, "Max supply reached");
require(msg.value >= mintPrice, "Insufficient payment");
uint256 tokenId = totalSupply;
totalSupply += 1; // 内置溢出检查
_safeMint(to, tokenId);
emit Minted(to, tokenId);
}
function setMintPrice(uint256 newPrice) public onlyOwner {
mintPrice = newPrice;
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
// 重写 ERC721 的 _update 以添加额外检查
function _update(address to, uint256 tokenId, address auth)
internal
override
virtual
returns (address)
{
require(!paused(), "Contract is paused");
return super._update(to, tokenId, auth);
}
}
说明:
ERC721
(NFT 标准)、Ownable
(所有者管理)、Pausable
(暂停功能)、ReentrancyGuard
(防止重入)。mint
使用安全数学运算(totalSupply += 1
)和 nonReentrant
。_update
添加暂停检查。onlyOwner
限制关键操作(如 setMintPrice
、暂停)。测试用例:
const { expect } = require("chai");
describe("SecureNFT", function () {
let SecureNFT, contract, owner, user;
beforeEach(async function () {
SecureNFT = await ethers.getContractFactory("SecureNFT");
[owner, user] = await ethers.getSigners();
contract = await SecureNFT.deploy();
await contract.deployed();
});
it("should mint NFT correctly", async function () {
await expect(contract.connect(user).mint(user.address, { value: ethers.parseEther("0.1") }))
.to.emit(contract, "Minted")
.withArgs(user.address, 0);
expect(await contract.ownerOf(0)).to.equal(user.address);
expect(await contract.totalSupply()).to.equal(1);
});
it("should revert mint when paused", async function () {
await contract.connect(owner).pause();
await expect(contract.connect(user).mint(user.address, { value: ethers.parseEther("0.1") }))
.to.be.revertedWith("Contract is paused");
});
it("should revert mint with insufficient payment", async function () {
await expect(contract.connect(user).mint(user.address, { value: ethers.parseEther("0.05") }))
.to.be.revertedWith("Insufficient payment");
});
it("should allow owner to set mint price", async function () {
await contract.connect(owner).setMintPrice(ethers.parseEther("0.2"));
expect(await contract.mintPrice()).to.equal(ethers.parseEther("0.2"));
});
it("should revert if non-owner sets mint price", async function () {
await expect(contract.connect(user).setMintPrice(ethers.parseEther("0.2")))
.to.be.revertedWith("Ownable: caller is not the owner");
});
});
运行测试:
npx hardhat test
说明:
ERC721
函数(如 _safeMint
)正确工作。如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!