Solidity多重角色:让你的合约权限像俄罗斯套娃一样层层嵌套

​今天咱们来扒一扒Solidity里那个让人又爱又恨的家伙——多重角色管理!在区块链上写合约,权限这事儿可不是小打小闹,搞不好一个函数谁都能调,资金就哗哗流走,项目直接黄摊子!多重角色就是给合约装上层层关卡,像俄罗斯套娃一样,外层管理员管内层,内层又分铸币、暂停、销毁这些小角色,权限细到毛孔里!

​ 今天咱们来扒一扒Solidity里那个让人又爱又恨的家伙——多重角色管理!在区块链上写合约,权限这事儿可不是小打小闹,搞不好一个函数谁都能调,资金就哗哗流走,项目直接黄摊子!多重角色就是给合约装上层层关卡,像俄罗斯套娃一样,外层管理员管内层,内层又分铸币、暂停、销毁这些小角色,权限细到毛孔里!

多重角色管理的底子

权限管理在Solidity里,核心就是控制谁能调用哪些函数。EVM没内置权限,得靠代码实现,通常用修饰符(modifier)或条件检查。OpenZeppelin的AccessControl是主力,它基于keccak256哈希生成角色ID,存储在映射里。角色ID是bytes32类型,比如keccak256("MINTER_ROLE"),每个角色对应一个权限集合。管理员(DEFAULT_ADMIN_ROLE)能分配和撤销角色,子角色只能执行特定操作。

多重角色意味着一个合约继承多个权限机制,比如Ownable的单一owner,再加AccessControl的多角色,最后嵌套Pausable的暂停权限。继承顺序影响函数解析,同名函数得用override明确指定。存储变量得注意布局,避免冲突——Solidity按声明顺序分配槽位,父合约变量在前。

拿个简单例子,合约继承ERC20(代币)、Ownable(单一权限)和AccessControl(多角色),mintMINTER_ROLEpausePAUSER_ROLE。构造函数授予msg.sender管理员角色,_setupRole设置权限。调用grantRole时,检查getRoleAdmin(role),确保上级角色授权。

Gas上,角色检查每调用加~2k Gas,事件记录再加~2k,但安全性值回票价。EVM里,映射存储用SLOAD读角色,SSTORE更新权限,成本固定。

开发环境先整起来

工具得齐全,用Hardhat建环境,写合约和测试。

跑命令初始化:

mkdir role-control-demo
cd role-control-demo
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox @openzeppelin/contracts @openzeppelin/contracts-upgradeable
npm install ethers
npx hardhat init

选TypeScript,装依赖:

npm install --save-dev ts-node typescript @types/node @types/mocha

目录长这样:

role-control-demo/
├── contracts/
│   ├── SimpleRole.sol
│   ├── MultiRole.sol
│   ├── NestedRole.sol
│   ├── MultiSigRole.sol
│   ├── UpgradableRole.sol
├── scripts/
│   ├── deploy.ts
├── test/
│   ├── RoleControl.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

环境就位,接下来直接上代码!

单角色权限:Ownable的入门级锁

权限管理从最简单的Ownable开始,这货就是给合约装个单一锁,只有owner能开。

代码实操

contracts/SimpleRole.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SimpleRoleToken is ERC20, Ownable {
    constructor() ERC20("SimpleRoleToken", "SRT") Ownable() {
        _mint(msg.sender, 1000000 * 10**decimals());
    }

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }

    function transferOwnership(address newOwner) public override onlyOwner {
        super.transferOwnership(newOwner);
    }
}

代码解析

  • 核心逻辑
    • 继承ERC20:代币转账功能。
    • 继承OwnableonlyOwner修饰符锁定mint
    • mintowner铸币给任意地址。
    • transferOwnership:覆盖,移交权限。
    • 构造函数:msg.senderowner,初始铸100万代币。
  • 安全点
    • onlyOwner确保只有owner能铸币。
    • Ownable自带renounceOwnership,可放弃权限。
    • 覆盖transferOwnership允许移交。
  • 潜在问题
    • 单人控制风险,owner私钥丢了就完蛋。
    • 没事件记录权限变更,链上查起来麻烦。
  • Gas成本
    • 部署:~500k Gas。
    • mint:~30k Gas(SSTORE余额)。
    • transferOwnership:~20k Gas。

测试

test/RoleControl.test.ts

import { ethers } from "hardhat";
import { expect } from "chai";
import { SimpleRoleToken } from "../typechain-types";

describe("SimpleRole", function () {
  let token: SimpleRoleToken;
  let owner: any, user1: any;

  beforeEach(async function () {
    [owner, user1] = await ethers.getSigners();
    const TokenFactory = await ethers.getContractFactory("SimpleRoleToken");
    token = await TokenFactory.deploy();
    await token.deployed();
  });

  it("lets owner mint tokens", async function () {
    await token.mint(user1.address, ethers.utils.parseEther("500"));
    expect(await token.balanceOf(user1.address)).to.equal(ethers.utils.parseEther("500"));
  });

  it("blocks non-owner from minting", async function () {
    await expect(token.connect(user1).mint(user1.address, ethers.utils.parseEther("500")))
      .to.be.revertedWith("Ownable: caller is not the owner");
  });

  it("lets owner transfer ownership", async function () {
    await token.transferOwnership(user1.address);
    expect(await token.owner()).to.equal(user1.address);
    await token.connect(user1).mint(user1.address, ethers.utils.parseEther("500"));
    expect(await token.balanceOf(user1.address)).to.equal(ethers.utils.parseEther("500"));
  });

  it("blocks non-owner from transferring ownership", async function () {
    await expect(token.connect(user1).transferOwnership(user1.address))
      .to.be.revertedWith("Ownable: caller is not the owner");
  });
});

跑测试:

npx hardhat test
  • 测试解析
    • owner铸币成功,余额更新。
    • owner调用minttransferOwnership直接报错。
    • 权限移交后,新owner能铸币。
  • 基础Ownable简单,但权限太集中,得加多角色。

多角色权限:AccessControl的分权时代

单一owner不保险,搞AccessControl分角色,铸币、暂停、销毁各管各的。

代码实操

contracts/RoleAccess.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract RoleAccessToken is ERC20, AccessControl, ReentrancyGuard {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    bool public paused;

    event Minted(address indexed to, uint256 amount);
    event Burned(address indexed from, uint256 amount);
    event Paused(address indexed account);
    event Unpaused(address indexed account);

    constructor() ERC20("RoleAccessToken", "RAT") {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(MINTER_ROLE, msg.sender);
        _setupRole(BURNER_ROLE, msg.sender);
        _setupRole(PAUSER_ROLE, msg.sender);
        _mint(msg.sender, 1000000 * 10**decimals());
    }

    function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) nonReentrant {
        _mint(to, amount);
        emit Minted(to, amount);
    }

    function burn(uint256 amount) public onlyRole(BURNER_ROLE) nonReentrant {
        _burn(msg.sender, amount);
        emit Burned(msg.sender, amount);
    }

    function transfer(address to, uint256 amount) public override returns (bool) {
        require(!paused, "Transfers paused");
        return super.transfer(to, amount);
    }

    function pause() public onlyRole(PAUSER_ROLE) {
        paused = true;
        emit Paused(msg.sender);
    }

    function unpause() public onlyRole(PAUSER_ROLE) {
        paused = false;
        emit Unpaused(msg.sender);
    }
}

代码解析

  • 核心逻辑
    • 继承ERC20AccessControlReentrancyGuard
    • 定义三个角色:MINTER_ROLE铸币、BURNER_ROLE销毁、PAUSER_ROLE暂停。
    • mint/burn:角色控制,防重入。
    • transfer:检查暂停状态。
    • pause/unpause:角色控制,触发事件。
    • 构造函数:msg.sender拿所有角色。
  • 安全点
    • 角色分离,权限细化。
    • nonReentrant防外部调用漏洞。
    • 事件记录操作,链上可查。
  • 潜在问题
    • 角色分配手动,管理员得靠谱。
    • 暂停可能影响用户体验。
  • Gas成本
    • 部署:~600k Gas。
    • mint:~35k Gas(含事件)。
    • pause:~5k Gas。

测试

test/RoleControl.test.ts(add):

import { RoleAccessToken } from "../typechain-types";

describe("RoleAccess", function () {
  let token: RoleAccessToken;
  let owner: any, minter: any, burner: any, pauser: any, user1: any;

  beforeEach(async function () {
    [owner, minter, burner, pauser, user1] = await ethers.getSigners();
    const TokenFactory = await ethers.getContractFactory("RoleAccessToken");
    token = await TokenFactory.deploy();
    await token.deployed();
    await token.grantRole(await token.MINTER_ROLE(), minter.address);
    await token.grantRole(await token.BURNER_ROLE(), burner.address);
    await token.grantRole(await token.PAUSER_ROLE(), pauser.address);
    await token.transfer(user1.address, ethers.utils.parseEther("1000"));
  });

  it("lets minter mint tokens", async function () {
    await expect(token.connect(minter).mint(user1.address, ethers.utils.parseEther("500")))
      .to.emit(token, "Minted")
      .withArgs(user1.address, ethers.utils.parseEther("500"));
    expect(await token.balanceOf(user1.address)).to.equal(ethers.utils.parseEther("1500"));
  });

  it("lets burner burn tokens", async function () {
    await expect(token.connect(burner).burn(ethers.utils.parseEther("500")))
      .to.emit(token, "Burned")
      .withArgs(burner.address, ethers.utils.parseEther("500"));
    expect(await token.balanceOf(burner.address)).to.equal(0);
  });

  it("lets pauser pause transfers", async function () {
    await expect(token.connect(pauser).pause())
      .to.emit(token, "Paused")
      .withArgs(pauser.address);
    await expect(token.connect(user1).transfer(owner.address, ethers.utils.parseEther("100")))
      .to.be.revertedWith("Transfers paused");
  });

  it("blocks unauthorized calls", async function () {
    await expect(token.connect(user1).mint(user1.address, ethers.utils.parseEther("500")))
      .to.be.revertedWith("AccessControl: account is missing role");
    await expect(token.connect(user1).burn(ethers.utils.parseEther("500")))
      .to.be.revertedWith("AccessControl: account is missing role");
    await expect(token.connect(user1).pause())
      .to.be.revertedWith("AccessControl: account is missing role");
  });
});
  • 测试解析
    • 每个角色操作成功,事件触发。
    • 非角色用户调用失败,权限锁死。
  • 优势:多角色分权,功能隔离。

嵌套角色:权限像套娃一样层层管

多角色还不够,搞嵌套权限,超级管理员管普通管理员,普通管理员管具体角色。

代码实操

contracts/NestedRoleAccess.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

contract NestedRoleAccessToken is ERC20, AccessControl {
    bytes32 public constant SUPER_ADMIN_ROLE = keccak256("SUPER_ADMIN_ROLE");
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    event RoleGranted(address indexed account, bytes32 indexed role, address indexed granter);
    event RoleRevoked(address indexed account, bytes32 indexed role, address indexed revoker);
    event Minted(address indexed to, uint256 amount);
    event Burned(address indexed from, uint256 amount);

    constructor() ERC20("NestedRoleAccessToken", "NRAT") {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(SUPER_ADMIN_ROLE, msg.sender);
        _setRoleAdmin(ADMIN_ROLE, SUPER_ADMIN_ROLE);
        _setRoleAdmin(MINTER_ROLE, ADMIN_ROLE);
        _setRoleAdmin(BURNER_ROLE, ADMIN_ROLE);
        _mint(msg.sender, 1000000 * 10**decimals());
    }

    function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
        _mint(to, amount);
        emit Minted(to, amount);
    }

    function burn(uint256 amount) public onlyRole(BURNER_ROLE) {
        _burn(msg.sender, amount);
        emit Burned(msg.sender, amount);
    }

    function grantRole(bytes32 role, address account) public override {
        require(getRoleAdmin(role) == SUPER_ADMIN_ROLE || getRoleAdmin(role) == ADMIN_ROLE, "Invalid admin role");
        _grantRole(role, account);
        emit RoleGranted(account, role, msg.sender);
    }

    function revokeRole(bytes32 role, address account) public override {
        require(getRoleAdmin(role) == SUPER_ADMIN_ROLE || getRoleAdmin(role) == ADMIN_ROLE, "Invalid admin role");
        _revokeRole(role, account);
        emit RoleRevoked(account, role, msg.sender);
    }
}

代码解析

  • 核心逻辑
    • 定义SUPER_ADMIN_ROLEADMIN_ROLEMINTER_ROLEBURNER_ROLE
    • _setRoleAdmin设置层级:SUPER_ADMIN_ROLEADMIN_ROLEADMIN_ROLEMINTER_ROLEBURNER_ROLE
    • grantRole/revokeRole:覆盖,检查上级角色。
    • mint/burn:底层角色控制。
    • 构造函数:msg.sender拿超级管理员。
  • 安全点
    • 嵌套权限,超级管理员控制管理员,层层把关。
    • 事件记录角色变更。
  • 潜在问题
    • 超级管理员权限最大,需多签保护。
  • Gas成本
    • 部署:~650k Gas。
    • 角色分配:~20k Gas。
    • mint:~35k Gas。

测试

test/RoleControl.test.ts(add):

import { NestedRoleAccessToken } from "../typechain-types";

describe("NestedRoleAccess", function () {
  let token: NestedRoleAccessToken;
  let owner: any, superAdmin: any, admin: any, minter: any, burner: any;

  beforeEach(async function () {
    [owner, superAdmin, admin, minter, burner] = await ethers.getSigners();
    const TokenFactory = await ethers.getContractFactory("NestedRoleAccessToken");
    token = await TokenFactory.deploy();
    await token.deployed();
    await token.grantRole(await token.SUPER_ADMIN_ROLE(), superAdmin.address);
  });

  it("lets super admin grant admin role", async function () {
    await expect(token.connect(superAdmin).grantRole(await token.ADMIN_ROLE(), admin.address))
      .to.emit(token, "RoleGranted")
      .withArgs(admin.address, await token.ADMIN_ROLE(), superAdmin.address);
    await expect(token.connect(admin).grantRole(await token.MINTER_ROLE(), minter.address))
      .to.emit(token, "RoleGranted")
      .withArgs(minter.address, await token.MINTER_ROLE(), admin.address);
    await token.connect(minter).mint(minter.address, ethers.utils.parseEther("500"));
    expect(await token.balanceOf(minter.address)).to.equal(ethers.utils.parseEther("500"));
  });

  it("lets admin revoke roles", async function () {
    await token.connect(superAdmin).grantRole(await token.ADMIN_ROLE(), admin.address);
    await token.connect(admin).grantRole(await token.BURNER_ROLE(), burner.address);
    await token.transfer(burner.address, ethers.utils.parseEther("1000"));
    await token.connect(admin).revokeRole(await token.BURNER_ROLE(), burner.address);
    await expect(token.connect(burner).burn(ethers.utils.parseEther("500")))
      .to.be.revertedWith("AccessControl: account is missing role");
  });

  it("blocks unauthorized role grants", async function () {
    await expect(token.connect(minter).grantRole(await token.ADMIN_ROLE(), admin.address))
      .to.be.revertedWith("AccessControl: account is missing role");
    await token.connect(superAdmin).grantRole(await token.ADMIN_ROLE(), admin.address);
    await expect(token.connect(admin).grantRole(await token.SUPER_ADMIN_ROLE(), superAdmin.address))
      .to.be.revertedWith("Invalid admin role");
  });
});
  • 测试解析
    • SUPER_ADMIN_ROLE分配ADMIN_ROLEADMIN_ROLE分配MINTER_ROLE
    • ADMIN_ROLE撤销角色,权限移除。
    • 非上级角色无法分配。
  • 优势:嵌套权限像树状结构,管理清晰。

多签角色分配

关键角色分配用多签,防止单人失误。

代码实操

contracts/MultiSigRoleAccess.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract MultiSigRoleAccessToken is ERC20, AccessControl, ReentrancyGuard {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    address[] public signers;
    uint256 public required;
    uint256 public transactionCount;
    mapping(uint256 => Transaction) public transactions;
    mapping(uint256 => mapping(address => bool)) public confirmations;

    struct Transaction {
        bytes32 role;
        address account;
        bool executed;
        uint256 confirmationCount;
    }

    event SubmitRoleGrant(uint256 indexed txId, bytes32 indexed role, address indexed account);
    event ConfirmRoleGrant(uint256 indexed txId, address indexed signer);
    event ExecuteRoleGrant(uint256 indexed txId, bytes32 indexed role, address indexed account);
    event RevokeConfirmation(uint256 indexed txId, address indexed signer);

    modifier onlySigner() {
        bool isSigner = false;
        for (uint256 i = 0; i < signers.length; i++) {
            if (signers[i] == msg.sender) {
                isSigner = true;
                break;
            }
        }
        require(isSigner, "Not signer");
        _;
    }

    constructor(address[] memory _signers, uint256 _required) ERC20("MultiSigRoleAccessToken", "MSRAT") {
        require(_signers.length > 0, "Signers required");
        require(_required > 0 && _required <= _signers.length, "Invalid required");
        signers = _signers;
        required = _required;
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _mint(msg.sender, 1000000 * 10**decimals());
    }

    function submitRoleGrant(bytes32 role, address account) public onlySigner {
        uint256 txId = transactionCount++;
        transactions[txId] = Transaction({
            role: role,
            account: account,
            executed: false,
            confirmationCount: 0
        });
        emit SubmitRoleGrant(txId, role, account);
    }

    function confirmRoleGrant(uint256 txId) public onlySigner nonReentrant {
        Transaction storage transaction = transactions[txId];
        require(!transaction.executed, "Transaction executed");
        require(!confirmations[txId][msg.sender], "Already confirmed");
        confirmations[txId][msg.sender] = true;
        transaction.confirmationCount++;
        emit ConfirmRoleGrant(txId, msg.sender);
        if (transaction.confirmationCount >= required) {
            executeRoleGrant(txId);
        }
    }

    function executeRoleGrant(uint256 txId) internal {
        Transaction storage transaction = transactions[txId];
        require(!transaction.executed, "Transaction executed");
        require(transaction.confirmationCount >= required, "Insufficient confirmations");
        transaction.executed = true;
        _grantRole(transaction.role, transaction.account);
        emit ExecuteRoleGrant(txId, transaction.role, transaction.account);
    }

    function revokeConfirmation(uint256 txId) public onlySigner {
        Transaction storage transaction = transactions[txId];
        require(!transaction.executed, "Transaction executed");
        require(confirmations[txId][msg.sender], "Not confirmed");
        confirmations[txId][msg.sender] = false;
        transaction.confirmationCount--;
        emit RevokeConfirmation(txId, msg.sender);
    }
}

代码解析

  • 核心逻辑
    • signersrequired定义多签成员和确认数。
    • submitRoleGrant:提交角色分配提案。
    • confirmRoleGrant:确认提案,够票后执行。
    • executeRoleGrant:调用_grantRole分配角色。
    • revokeConfirmation:撤销确认。
  • 安全点
    • 多签防单人滥分配。
    • nonReentrant保护确认。
    • 事件记录提案和执行。
  • 潜在问题
    • 多签成员需可信。
    • Gas成本随确认数增加。
  • Gas成本
    • 部署:~700k Gas。
    • 提交/确认:~10k Gas/次。
    • 执行:~25k Gas。

测试

test/RoleControl.test.ts(add):

import { MultiSigRoleAccessToken } from "../typechain-types";

describe("MultiSigRoleAccess", function () {
  let token: MultiSigRoleAccessToken;
  let owner: any, signer1: any, signer2: any, signer3: any, user1: any;

  beforeEach(async function () {
    [owner, signer1, signer2, signer3, user1] = await ethers.getSigners();
    const TokenFactory = await ethers.getContractFactory("MultiSigRoleAccessToken");
    token = await TokenFactory.deploy([signer1.address, signer2.address, signer3.address], 2);
    await token.deployed();
  });

  it("executes role grant with multi-sig", async function () {
    const MINTER_ROLE = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("MINTER_ROLE"));
    await token.connect(signer1).submitRoleGrant(MINTER_ROLE, user1.address);
    await token.connect(signer2).confirmRoleGrant(0);
    await expect(token.connect(signer3).confirmRoleGrant(0))
      .to.emit(token, "ExecuteRoleGrant")
      .withArgs(0, MINTER_ROLE, user1.address);
    await token.connect(user1).mint(user1.address, ethers.utils.parseEther("500"));
    expect(await token.balanceOf(user1.address)).to.equal(ethers.utils.parseEther("500"));
  });

  it("blocks role grant without enough signatures", async function () {
    const MINTER_ROLE = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("MINTER_ROLE"));
    await token.connect(signer1).submitRoleGrant(MINTER_ROLE, user1.address);
    await token.connect(signer2).confirmRoleGrant(0);
    expect(await token.balanceOf(user1.address)).to.equal(0);
  });

  it("allows revoking confirmation", async function () {
    const MINTER_ROLE = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("MINTER_ROLE"));
    await token.connect(signer1).submitRoleGrant(MINTER_ROLE, user1.address);
    await token.connect(signer2).confirmRoleGrant(0);
    await token.connect(signer2).revokeConfirmation(0);
    await token.connect(signer3).confirmRoleGrant(0);
    expect(await token.balanceOf(user1.address)).to.equal(0);
  });
});
  • 测试解析
    • 2/3确认后角色分配成功,铸币可用。
    • 单确认不触发分配。
    • 撤销确认阻止分配。
  • 优势:多签确保角色分配安全。

部署脚本

scripts/deploy.ts

import { ethers } from "hardhat";

async function main() {
  const [owner, signer1, signer2, signer3] = await ethers.getSigners();

  const SimpleRoleFactory = await ethers.getContractFactory("SimpleRoleToken");
  const simpleRole = await SimpleRoleFactory.deploy();
  await simpleRole.deployed();
  console.log(`SimpleRoleToken deployed to: ${simpleRole.address}`);

  const RoleAccessFactory = await ethers.getContractFactory("RoleAccessToken");
  const roleAccess = await RoleAccessFactory.deploy();
  await roleAccess.deployed();
  console.log(`RoleAccessToken deployed to: ${roleAccess.address}`);

  const NestedRoleAccessFactory = await ethers.getContractFactory("NestedRoleAccessToken");
  const nestedRoleAccess = await NestedRoleAccessFactory.deploy();
  await nestedRoleAccess.deployed();
  console.log(`NestedRoleAccessToken deployed to: ${nestedRoleAccess.address}`);

  const MultiSigRoleAccessFactory = await ethers.getContractFactory("MultiSigRoleAccessToken");
  const multiSigRoleAccess = await MultiSigRoleAccessFactory.deploy([signer1.address, signer2.address, signer3.address], 2);
  await multiSigRoleAccess.deployed();
  console.log(`MultiSigRoleAccessToken deployed to: ${multiSigRoleAccess.address}`);

  const UpgradableAccessFactory = await ethers.getContractFactory("UpgradableAccessToken");
  const upgradableAccess = await UpgradableAccessFactory.deploy();
  await upgradableAccess.deployed();
  const initData = UpgradableAccessFactory.interface.encodeFunctionData("initialize");
  const ProxyFactory = await ethers.getContractFactory("UUPSProxy");
  const proxy = await ProxyFactory.deploy(upgradableAccess.address, initData);
  await proxy.deployed();
  console.log(`UpgradableAccessToken deployed to: ${upgradableAccess.address}, Proxy: ${proxy.address}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

跑部署:

npx hardhat run scripts/deploy.ts --network hardhat

权限审计

  • 权限细化
    • 敏感函数加onlyRoleonlySigner
    • 角色分配用上级角色控制。
  • 防滥用
    • 多签机制防单人失误。
    • 定期撤销不活跃角色。
  • 重入保护
    • 外部调用函数加nonReentrant
    • 状态更新优先。
  • 事件记录
    • 权限变更、铸币、销毁触发事件。
  • Gas控制
    • 多签增加Gas,评估成本。
  • 测试覆盖
    • 测试角色分配、权限调用、撤销。
    • 模拟非授权和升级场景。
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
天涯学馆
天涯学馆
0x9d6d...50d5
资深大厂程序员,12年开发经验,致力于探索前沿技术!