多重继承!在区块链上写智能合约,代码复用和模块化是王道,而多重继承就像搭积木,能让你把各种功能组合得飞起!想让合约同时有代币、权限控制、暂停功能?多重继承直接搞定!多重继承的核心概念先搞清楚几个关键点:多重继承:一个合约从多个父合约继承功能,复用代码和逻辑。Solidity继承机制:使
多重继承!在区块链上写智能合约,代码复用和模块化是王道,而多重继承就像搭积木,能让你把各种功能组合得飞起!想让合约同时有代币、权限控制、暂停功能?多重继承直接搞定!
先搞清楚几个关键点:
is关键字继承多个合约。override)和多态。ERC20)、权限(Ownable)、暂停(Pausable)。override和virtual,规范继承。delegatecall可能影响继承行为。咱们用Solidity 0.8.20,结合OpenZeppelin和Hardhat,从基础继承到复杂多重继承,逐一分析实现和注意事项。
用Hardhat搭建开发环境,写和测试合约。
mkdir multiple-inheritance-demo
cd multiple-inheritance-demo
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox @openzeppelin/contracts @openzeppelin/contracts-upgradeable
npm install ethers
初始化Hardhat:
npx hardhat init
选择TypeScript项目,安装依赖:
npm install --save-dev ts-node typescript @types/node @types/mocha
目录结构:
multiple-inheritance-demo/
├── contracts/
│ ├── BasicInheritance.sol
│ ├── MultiInheritance.sol
│ ├── DiamondInheritance.sol
│ ├── ComplexInheritance.sol
│ ├── UpgradableInheritance.sol
├── scripts/
│ ├── deploy.ts
├── test/
│ ├── Inheritance.test.ts
├── hardhat.config.ts
├── tsconfig.json
├── package.json
tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"outDir": "./dist",
"rootDir": "./"
},
"include": ["hardhat.config.ts", "scripts", "test"]
}
hardhat.config.ts:
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
const config: HardhatUserConfig = {
solidity: {
version: "0.8.20",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
networks: {
hardhat: {
chainId: 1337,
},
},
};
export default config;
跑本地节点:
npx hardhat node
先从简单的单继承开始,理解基本逻辑。
contracts/BasicInheritance.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract BasicToken is ERC20 {
constructor() ERC20("BasicToken", "BTK") {
_mint(msg.sender, 1000000 * 10**decimals());
}
}
ERC20,获得标准代币功能(如transfer、balanceOf)。ERC20提供标准化的代币逻辑,减少错误。balances)由ERC20管理。test/Inheritance.test.ts:
import { ethers } from "hardhat";
import { expect } from "chai";
import { BasicToken } from "../typechain-types";
describe("BasicInheritance", function () {
let token: BasicToken;
let owner: any, user1: any;
beforeEach(async function () {
[owner, user1] = await ethers.getSigners();
const TokenFactory = await ethers.getContractFactory("BasicToken");
token = await TokenFactory.deploy();
await token.deployed();
});
it("should mint tokens to owner", async function () {
expect(await token.balanceOf(owner.address)).to.equal(ethers.utils.parseEther("1000000"));
});
it("should transfer tokens", async function () {
await token.transfer(user1.address, ethers.utils.parseEther("1000"));
expect(await token.balanceOf(user1.address)).to.equal(ethers.utils.parseEther("1000"));
expect(await token.balanceOf(owner.address)).to.equal(ethers.utils.parseEther("999000"));
});
});
跑测试:
npx hardhat test
transfer正确更新余额。结合ERC20、Ownable和Pausable,实现代币、权限和暂停功能。
contracts/MultiInheritance.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
contract MultiToken is ERC20, Ownable, Pausable {
constructor() ERC20("MultiToken", "MTK") Ownable() {
_mint(msg.sender, 1000000 * 10**decimals());
}
function transfer(address to, uint256 amount) public virtual override whenNotPaused {
super.transfer(to, amount);
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
}
ERC20:代币功能。Ownable:onlyOwner修饰符,限制敏感操作。Pausable:暂停/恢复功能。transfer:覆盖ERC20的transfer,加上whenNotPaused。onlyOwner限制pause/unpause。whenNotPaused保护transfer。pause/transfer增加test/Inheritance.test.ts(add):
import { MultiToken } from "../typechain-types";
describe("MultiInheritance", function () {
let token: MultiToken;
let owner: any, user1: any;
beforeEach(async function () {
[owner, user1] = await ethers.getSigners();
const TokenFactory = await ethers.getContractFactory("MultiToken");
token = await TokenFactory.deploy();
await token.deployed();
await token.transfer(user1.address, ethers.utils.parseEther("1000"));
});
it("should pause and block transfers", async function () {
await token.pause();
await expect(token.connect(user1).transfer(owner.address, ethers.utils.parseEther("100")))
.to.be.revertedWith("Pausable: paused");
});
it("should unpause and allow transfers", async function () {
await token.pause();
await token.unpause();
await token.connect(user1).transfer(owner.address, ethers.utils.parseEther("100"));
expect(await token.balanceOf(owner.address)).to.equal(ethers.utils.parseEther("999100"));
});
it("should restrict pause to owner", async function () {
await expect(token.connect(user1).pause()).to.be.revertedWith("Ownable: caller is not the owner");
});
});
transfer失败。transfer成功。owner无法暂停,验证权限。多个父合约有同名函数,需处理“钻石问题”。
contracts/DiamondInheritance.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract ParentA {
function foo() public pure virtual returns (string memory) {
return "ParentA";
}
}
contract ParentB {
function foo() public pure virtual returns (string memory) {
return "ParentB";
}
}
contract DiamondChild is ParentA, ParentB {
function foo() public pure override(ParentA, ParentB) returns (string memory) {
return string(abi.encodePacked(ParentA.foo(), " + ", ParentB.foo()));
}
}
ParentA和ParentB有同名函数foo。DiamondChild继承两者,覆盖foo,组合两父类输出。virtual和override规范覆盖。override(ParentA, ParentB)明确解析冲突。foo调用~25k Gas。test/Inheritance.test.ts(add):
import { DiamondChild } from "../typechain-types";
describe("DiamondInheritance", function () {
let token: DiamondChild;
beforeEach(async function () {
const TokenFactory = await ethers.getContractFactory("DiamondChild");
token = await TokenFactory.deploy();
await token.deployed();
});
it("should resolve diamond inheritance", async function () {
expect(await token.foo()).to.equal("ParentA + ParentB");
});
});
foo正确组合两父类输出。override确保无歧义。结合ERC20、Ownable、Pausable、AccessControl,实现复杂功能。
contracts/ComplexInheritance.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
contract ComplexToken is ERC20, Ownable, Pausable, AccessControl {
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
event Pause(address indexed account);
event Unpause(address indexed account);
constructor() ERC20("ComplexToken", "CTK") Ownable() {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(PAUSER_ROLE, msg.sender);
_setupRole(MINTER_ROLE, msg.sender);
_mint(msg.sender, 1000000 * 10**decimals());
}
function transfer(address to, uint256 amount) public virtual override whenNotPaused {
super.transfer(to, amount);
}
function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
_mint(to, amount);
}
function pause() public onlyRole(PAUSER_ROLE) {
_pause();
emit Pause(msg.sender);
}
function unpause() public onlyRole(PAUSER_ROLE) {
_unpause();
emit Unpause(msg.sender);
}
}
ERC20:代币功能。Ownable:onlyOwner权限。Pausable:暂停功能。AccessControl:角色管理(如PAUSER_ROLE、MINTER_ROLE)。transfer:覆盖,添加whenNotPaused。mint/pause/unpause:角色控制。PAUSER_ROLE和MINTER_ROLE独立。test/Inheritance.test.ts(add):
import { ComplexToken } from "../typechain-types";
describe("ComplexInheritance", function () {
let token: ComplexToken;
let owner: any, pauser: any, minter: any, user1: any;
beforeEach(async function () {
[owner, pauser, minter, user1] = await ethers.getSigners();
const TokenFactory = await ethers.getContractFactory("ComplexToken");
token = await TokenFactory.deploy();
await token.deployed();
await token.grantRole(await token.PAUSER_ROLE(), pauser.address);
await token.grantRole(await token.MINTER_ROLE(), minter.address);
await token.transfer(user1.address, ethers.utils.parseEther("1000"));
});
it("should allow pauser to pause", async function () {
await expect(token.connect(pauser).pause())
.to.emit(token, "Pause")
.withArgs(pauser.address);
await expect(token.connect(user1).transfer(owner.address, ethers.utils.parseEther("100")))
.to.be.revertedWith("Pausable: paused");
});
it("should allow minter to mint", async function () {
await token.connect(minter).mint(user1.address, ethers.utils.parseEther("1000"));
expect(await token.balanceOf(user1.address)).to.equal(ethers.utils.parseEther("2000"));
});
it("should restrict unauthorized actions", async function () {
await expect(token.connect(user1).pause()).to.be.revertedWith("AccessControl: account is missing role");
await expect(token.connect(user1).mint(user1.address, ethers.utils.parseEther("1000")))
.to.be.revertedWith("AccessControl: account is missing role");
});
});
PAUSER_ROLE可暂停,触发事件。MINTER_ROLE可铸造代币。结合代理模式支持升级。
contracts/UpgradableInheritance.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract UpgradableToken is ERC20Upgradeable, OwnableUpgradeable, PausableUpgradeable, UUPSUpgradeable {
function initialize() public initializer {
__ERC20_init("UpgradableToken", "UTK");
__Ownable_init();
__Pausable_init();
__UUPSUpgradeable_init();
_mint(msg.sender, 1000000 * 10**decimals());
}
function transfer(address to, uint256 amount) public virtual override whenNotPaused {
super.transfer(to, amount);
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}
contracts/UpgradableTokenV2.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract UpgradableTokenV2 is ERC20Upgradeable, OwnableUpgradeable, PausableUpgradeable, UUPSUpgradeable {
uint256 public transferFee = 1 * 10**16; // 1% fee
function initialize() public initializer {
__ERC20_init("UpgradableTokenV2", "UTKV2");
__Ownable_init();
__Pausable_init();
__UUPSUpgradeable_init();
_mint(msg.sender, 1000000 * 10**decimals());
}
function transfer(address to, uint256 amount) public virtual override whenNotPaused {
uint256 fee = (amount * transferFee) / 1e18;
super.transfer(to, amount - fee);
super.transfer(owner(), fee);
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}
contracts/UUPSProxy.sol:
// SPDX-License-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract UUPSProxy is ERC1967Proxy {
constructor(address logic, bytes memory data) ERC1967Proxy(logic, data) {}
}
ERC20Upgradeable、OwnableUpgradeable、PausableUpgradeable、UUPSUpgradeable。initialize:初始化代币、权限、暂停和代理。transfer:用whenNotPaused保护。UUPSUpgradeable确保升级安全。test/Inheritance.test.ts(add):
import { UUPSProxy, UpgradableToken, UpgradableTokenV2 } from "../typechain-types";
describe("UpgradableInheritance", function () {
let proxy: UUPSProxy;
let token: UpgradableToken;
let tokenV2: UpgradableTokenV2;
let owner: any, user1: any;
beforeEach(async function () {
[owner, user1] = await ethers.getSigners();
const TokenFactory = await ethers.getContractFactory("UpgradableToken");
token = await TokenFactory.deploy();
await token.deployed();
const ProxyFactory = await ethers.getContractFactory("UUPSProxy");
const initData = TokenFactory.interface.encodeFunctionData("initialize");
proxy = await ProxyFactory.deploy(token.address, initData);
await proxy.deployed();
const TokenV2Factory = await ethers.getContractFactory("UpgradableTokenV2");
tokenV2 = await TokenV2Factory.deploy();
await tokenV2.deployed();
await (await ethers.getContractFactory("UpgradableToken")).attach(proxy.address)
.transfer(user1.address, ethers.utils.parseEther("1000"));
});
it("should pause and block transfers", async function () {
const proxyAsToken = await ethers.getContractFactory("UpgradableToken").then(f => f.attach(proxy.address));
await proxyAsToken.pause();
await expect(proxyAsToken.connect(user1).transfer(owner.address, ethers.utils.parseEther("100")))
.to.be.revertedWith("Pausable: paused");
});
it("should upgrade and apply fee", async function () {
const proxyAsToken = await ethers.getContractFactory("UpgradableToken").then(f => f.attach(proxy.address));
await proxyAsToken.upgradeTo(tokenV2.address);
const proxyAsTokenV2 = await ethers.getContractFactory("UpgradableTokenV2").then(f => f.attach(proxy.address));
await proxyAsTokenV2.connect(user1).transfer(owner.address, ethers.utils.parseEther("100"));
expect(await proxyAsTokenV2.balanceOf(owner.address)).to.equal(ethers.utils.parseEther("999099")); // 1% fee
});
});
scripts/deploy.ts:
import { ethers } from "hardhat";
async function main() {
const [owner] = await ethers.getSigners();
const BasicTokenFactory = await ethers.getContractFactory("BasicToken");
const basicToken = await BasicTokenFactory.deploy();
await basicToken.deployed();
console.log(`BasicToken deployed to: ${basicToken.address}`);
const MultiTokenFactory = await ethers.getContractFactory("MultiToken");
const multiToken = await MultiTokenFactory.deploy();
await multiToken.deployed();
console.log(`MultiToken deployed to: ${multiToken.address}`);
const DiamondChildFactory = await ethers.getContractFactory("DiamondChild");
const diamondChild = await DiamondChildFactory.deploy();
await diamondChild.deployed();
console.log(`DiamondChild deployed to: ${diamondChild.address}`);
const ComplexTokenFactory = await ethers.getContractFactory("ComplexToken");
const complexToken = await ComplexTokenFactory.deploy();
await complexToken.deployed();
console.log(`ComplexToken deployed to: ${complexToken.address}`);
const UpgradableTokenFactory = await ethers.getContractFactory("UpgradableToken");
const upgradableToken = await UpgradableTokenFactory.deploy();
await upgradableToken.deployed();
const initData = UpgradableTokenFactory.interface.encodeFunctionData("initialize");
const ProxyFactory = await ethers.getContractFactory("UUPSProxy");
const proxy = await ProxyFactory.deploy(upgradableToken.address, initData);
await proxy.deployed();
console.log(`UpgradableToken deployed to: ${upgradableToken.address}, Proxy: ${proxy.address}`);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
跑部署:
npx hardhat run scripts/deploy.ts --network hardhat
virtual和override明确覆盖。ParentA.foo())避免歧义。pause、mint)加权限修饰符。AccessControl支持多角色管理。Pause)记录关键操作。如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!