Solidity代币燃烧:让你的代币安全烧得飞起不翻车

Solidity里一个超硬核的主题——安全的代币燃烧!在区块链上,代币燃烧就是把代币永久销毁,减少总供给,可能是为了控通胀、提价值,或者搞个活动吸引眼球。但这事可不是随便写两行代码就完,烧不好分分钟出漏洞,代币飞了,项目也凉凉!代币燃烧的核心概念先搞清楚几个关键点:代币燃烧:将代币从流通中永

Solidity里一个超硬核的主题——安全的代币燃烧!在区块链上,代币燃烧就是把代币永久销毁,减少总供给,可能是为了控通胀、提价值,或者搞个活动吸引眼球。但这事可不是随便写两行代码就完,烧不好分分钟出漏洞,代币飞了,项目也凉凉!

代币燃烧的核心概念

先搞清楚几个关键点:

  • 代币燃烧:将代币从流通中永久移除,通常减少totalSupply并转移到“黑洞地址”(如0x0)或直接销毁余额。
  • 目的
    • 减少总供给,增加稀缺性。
    • 激励用户(如销毁换NFT)。
    • 调整经济模型(如通缩机制)。
  • 安全风险
    • 权限控制:谁能烧?没限制可能被恶意用户滥用。
    • 重入攻击:燃烧涉及转账,可能被攻击者利用。
    • 数据一致性totalSupply和用户余额必须同步。
    • 事件缺失:无事件记录,链上追踪困难。
    • Gas限制:批量燃烧可能耗尽Gas。
  • 工具
    • Solidity 0.8.x:自带溢出/下溢检查,安全可靠。
    • OpenZeppelin:提供安全的ERC20和访问控制库。
    • Hardhat:测试和调试燃烧逻辑。
  • EVM特性
    • 燃烧到0x0地址只是转移,实际销毁需更新totalSupply
    • delegatecall或外部调用需防重入。
  • 事件:用事件记录燃烧操作,便于链上追踪。

咱们用Solidity 0.8.20,结合OpenZeppelin和Hardhat,从基础燃烧到多签和批量机制,逐步实现安全的代币燃烧。

环境准备

用Hardhat搭建开发环境,写和测试合约。

mkdir token-burn-demo
cd token-burn-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

目录结构:

token-burn-demo/
├── contracts/
│   ├── BasicBurn.sol
│   ├── ReentrancyProtectedBurn.sol
│   ├── MultiSigBurn.sol
│   ├── BatchBurn.sol
│   ├── BurnWithReward.sol
├── scripts/
│   ├── deploy.ts
├── test/
│   ├── TokenBurn.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

基础代币燃烧

先从简单的ERC20燃烧开始,弄清楚基本逻辑。

合约代码

contracts/BasicBurn.sol

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

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

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

    function burn(uint256 amount) public {
        _burn(msg.sender, amount);
    }
}

解析

  • 逻辑
    • 继承OpenZeppelin的ERC20Ownable
    • 构造函数:铸造100万代币给部署者。
    • burn:调用ERC20._burn,减少调用者余额和totalSupply
  • 安全特性
    • _burn检查余额,防止下溢(Solidity 0.8.x)。
    • 无需黑洞地址,直接更新状态。
  • 问题
    • 任何人都能调用burn,可能误操作。
    • 无事件记录,链上追踪困难。
  • Gas:单次burn约30k Gas(1次SSTORE)。

测试

test/TokenBurn.test.ts

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

describe("BasicBurn", function () {
  let token: BasicBurnToken;
  let owner: any, user1: any;

  beforeEach(async function () {
    [owner, user1] = await ethers.getSigners();
    const TokenFactory = await ethers.getContractFactory("BasicBurnToken");
    token = await TokenFactory.deploy();
    await token.deployed();
    await token.transfer(user1.address, ethers.utils.parseEther("1000"));
  });

  it("should burn tokens correctly", async function () {
    const initialBalance = await token.balanceOf(user1.address);
    const burnAmount = ethers.utils.parseEther("500");
    await token.connect(user1).burn(burnAmount);
    expect(await token.balanceOf(user1.address)).to.equal(initialBalance.sub(burnAmount));
    expect(await token.totalSupply()).to.equal(ethers.utils.parseEther("999500"));
  });

  it("should revert if insufficient balance", async function () {
    await expect(token.connect(user1).burn(ethers.utils.parseEther("2000"))).to.be.revertedWith("ERC20: burn amount exceeds balance");
  });
});

跑测试:

npx hardhat test
  • 解析
    • 用户燃烧500代币,余额和totalSupply正确减少。
    • 超额燃烧被阻止,验证_burn的安全检查。
  • 问题:无权限控制,需改进。

防止重入攻击

燃烧可能涉及外部调用(如通知),需防重入。

问题代码

contracts/ReentrancyProtectedBurn.sol

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

contract VulnerableBurn {
    mapping(address => uint256) public balances;
    uint256 public totalSupply;

    constructor() {
        balances[msg.sender] = 1000000 * 10**18;
        totalSupply = 1000000 * 10**18;
    }

    function burn(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        (bool success, ) = msg.sender.call{value: 0}(abi.encodeWithSignature("notifyBurn(uint256)", amount));
        require(success, "Notification failed");
        balances[msg.sender] -= amount;
        totalSupply -= amount;
    }
}

解析

  • 漏洞
    • burn先调用外部notifyBurn,后更新状态。
    • 攻击者可通过notifyBurn再次调用burn,重复燃烧未更新的余额。
  • 攻击场景
    • 攻击者合约调用burn,触发notifyBurn
    • notifyBurn再次调用burn,余额未更新,重复扣除。

修复代码

contracts/ReentrancyProtectedBurn.sol(update):

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

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

contract SecureBurnToken is ERC20, ReentrancyGuard, Ownable {
    event Burn(address indexed burner, uint256 amount);

    constructor() ERC20("SecureBurnToken", "SBT") Ownable() {
        _mint(msg.sender, 1000000 * 10**decimals());
    }

    function burn(uint256 amount) public nonReentrant {
        _burn(msg.sender, amount);
        emit Burn(msg.sender, amount);
        (bool success, ) = msg.sender.call{value: 0}(abi.encodeWithSignature("notifyBurn(uint256)", amount));
        require(success, "Notification failed");
    }
}

解析

  • 修复
    • ReentrancyGuardnonReentrant修饰符,防止重复进入。
    • 先调用_burn,更新状态,后发送通知。
    • 添加Burn事件,记录燃烧操作。
  • 安全特性
    • nonReentrant锁住函数,防止重入。
    • 状态更新在前,外部调用在后。
    • 事件便于链上追踪。
  • GasnonReentrant增加~2k Gas,安全性提升。

测试

test/TokenBurn.test.ts(add):

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

describe("ReentrancyProtectedBurn", function () {
  let token: SecureBurnToken;
  let owner: any, attacker: any;

  beforeEach(async function () {
    [owner, attacker] = await ethers.getSigners();
    const TokenFactory = await ethers.getContractFactory("SecureBurnToken");
    token = await TokenFactory.deploy();
    await token.deployed();
    await token.transfer(attacker.address, ethers.utils.parseEther("1000"));

    const AttackerFactory = await ethers.getContractFactory("BurnAttacker");
    const attackerContract = await AttackerFactory.deploy(token.address);
    await attackerContract.deployed();
    await token.transfer(attackerContract.address, ethers.utils.parseEther("1000"));
  });

  it("should prevent reentrancy attack", async function () {
    const AttackerFactory = await ethers.getContractFactory("BurnAttacker");
    const attackerContract = await AttackerFactory.deploy(token.address);
    await expect(attackerContract.attack(ethers.utils.parseEther("500"))).to.be.revertedWith("ReentrancyGuard: reentrant call");
    expect(await token.balanceOf(attackerContract.address)).to.equal(ethers.utils.parseEther("1000"));
  });

  it("should emit Burn event", async function () {
    await expect(token.connect(attacker).burn(ethers.utils.parseEther("500")))
      .to.emit(token, "Burn")
      .withArgs(attacker.address, ethers.utils.parseEther("500"));
    expect(await token.totalSupply()).to.equal(ethers.utils.parseEther("999500"));
  });
});

contracts/BurnAttacker.sol

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

contract BurnAttacker {
    SecureBurnToken public token;

    constructor(address _token) {
        token = SecureBurnToken(_token);
    }

    function attack(uint256 amount) public {
        token.burn(amount);
    }

    function notifyBurn(uint256 amount) external {
        if (address(token).balance >= amount) {
            token.burn(amount);
        }
    }
}
  • 测试解析
    • 攻击者合约尝试重入,触发nonReentrant保护,失败。
    • 正常燃烧触发Burn事件,totalSupply正确减少。

多签控制燃烧

为燃烧加多签机制,需多人同意。

合约代码

contracts/MultiSigBurn.sol

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

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

contract MultiSigBurnToken is ERC20, Ownable {
    address[] public burners;
    uint256 public required;
    uint256 public transactionCount;
    mapping(uint256 => Transaction) public transactions;
    mapping(uint256 => mapping(address => bool)) public confirmations;

    struct Transaction {
        address burner;
        uint256 amount;
        bool executed;
        uint256 confirmationCount;
    }

    event SubmitBurn(uint256 indexed txId, address indexed burner, uint256 amount);
    event ConfirmBurn(uint256 indexed txId, address indexed burner);
    event ExecuteBurn(uint256 indexed txId, address indexed burner, uint256 amount);
    event RevokeConfirmation(uint256 indexed txId, address indexed burner);
    event Burn(address indexed burner, uint256 amount);

    modifier onlyBurner() {
        bool isBurner = false;
        for (uint256 i = 0; i < burners.length; i++) {
            if (burners[i] == msg.sender) {
                isBurner = true;
                break;
            }
        }
        require(isBurner, "Not burner");
        _;
    }

    constructor(address[] memory _burners, uint256 _required) ERC20("MultiSigBurnToken", "MSBT") Ownable() {
        require(_burners.length > 0, "Burners required");
        require(_required > 0 && _required <= _burners.length, "Invalid required");
        burners = _burners;
        required = _required;
        _mint(msg.sender, 1000000 * 10**decimals());
    }

    function submitBurn(address burner, uint256 amount) public onlyBurner {
        require(balanceOf(burner) >= amount, "Insufficient balance");
        uint256 txId = transactionCount++;
        transactions[txId] = Transaction({
            burner: burner,
            amount: amount,
            executed: false,
            confirmationCount: 0
        });
        emit SubmitBurn(txId, burner, amount);
    }

    function confirmBurn(uint256 txId) public onlyBurner {
        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 ConfirmBurn(txId, msg.sender);
        if (transaction.confirmationCount >= required) {
            executeBurn(txId);
        }
    }

    function executeBurn(uint256 txId) internal {
        Transaction storage transaction = transactions[txId];
        require(!transaction.executed, "Transaction executed");
        require(transaction.confirmationCount >= required, "Insufficient confirmations");
        transaction.executed = true;
        _burn(transaction.burner, transaction.amount);
        emit ExecuteBurn(txId, transaction.burner, transaction.amount);
        emit Burn(transaction.burner, transaction.amount);
    }

    function revokeConfirmation(uint256 txId) public onlyBurner {
        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);
    }
}

解析

  • 逻辑
    • burnersrequired控制多签。
    • submitBurn:提交燃烧提案。
    • confirmBurn:确认提案,达标后执行。
    • executeBurn:调用_burn销毁代币,触发Burn事件。
    • revokeConfirmation:撤销确认。
  • 安全特性
    • 多签防止单人误操作。
    • 检查余额和执行状态。
    • 事件记录提案和燃烧。
  • Gas:多签增加Gas(~50k/提案),但安全性高。
  • 适用场景:团队控制燃烧,防止滥用。

测试

test/TokenBurn.test.ts(add):

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

describe("MultiSigBurn", function () {
  let token: MultiSigBurnToken;
  let owner: any, burner1: any, burner2: any, burner3: any;

  beforeEach(async function () {
    [owner, burner1, burner2, burner3] = await ethers.getSigners();
    const TokenFactory = await ethers.getContractFactory("MultiSigBurnToken");
    token = await TokenFactory.deploy([burner1.address, burner2.address, burner3.address], 2);
    await token.deployed();
    await token.transfer(burner1.address, ethers.utils.parseEther("1000"));
  });

  it("should execute burn with multi-sig", async function () {
    await token.connect(burner1).submitBurn(burner1.address, ethers.utils.parseEther("500"));
    await token.connect(burner2).confirmBurn(0);
    await expect(token.connect(burner3).confirmBurn(0))
      .to.emit(token, "Burn")
      .withArgs(burner1.address, ethers.utils.parseEther("500"));
    expect(await token.balanceOf(burner1.address)).to.equal(ethers.utils.parseEther("500"));
    expect(await token.totalSupply()).to.equal(ethers.utils.parseEther("999500"));
  });

  it("should not execute without enough confirmations", async function () {
    await token.connect(burner1).submitBurn(burner1.address, ethers.utils.parseEther("500"));
    await token.connect(burner2).confirmBurn(0);
    expect(await token.balanceOf(burner1.address)).to.equal(ethers.utils.parseEther("1000"));
  });

  it("should allow revoking confirmation", async function () {
    await token.connect(burner1).submitBurn(burner1.address, ethers.utils.parseEther("500"));
    await token.connect(burner2).confirmBurn(0);
    await token.connect(burner2).revokeConfirmation(0);
    await token.connect(burner3).confirmBurn(0);
    expect(await token.balanceOf(burner1.address)).to.equal(ethers.utils.parseEther("1000"));
  });
});
  • 测试解析
    • 2/3确认后执行燃烧,totalSupply减少。
    • 单人确认不触发燃烧。
    • 撤销确认阻止燃烧。

批量燃烧

支持一次烧多个账户的代币,适合大规模操作。

合约代码

contracts/BatchBurn.sol

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

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

contract BatchBurnToken is ERC20, Ownable {
    event Burn(address indexed burner, uint256 amount);

    constructor() ERC20("BatchBurnToken", "BBT") Ownable() {
        _mint(msg.sender, 1000000 * 10**decimals());
    }

    function batchBurn(address[] memory accounts, uint256[] memory amounts) public onlyOwner {
        require(accounts.length == amounts.length, "Invalid input");
        for (uint256 i = 0; i < accounts.length; i++) {
            _burn(accounts[i], amounts[i]);
            emit Burn(accounts[i], amounts[i]);
        }
    }
}

解析

  • 逻辑
    • batchBurn:批量销毁多个账户的代币。
    • 限制onlyOwner,防止滥用。
    • 检查输入长度一致。
    • 触发Burn事件。
  • 安全特性
    • onlyOwner控制权限。
    • _burn验证余额。
    • 事件记录每笔燃烧。
  • 问题:大数组可能耗尽Gas,需限制批量大小。
  • Gas:每账户~30k Gas,需评估数组长度。

测试

test/TokenBurn.test.ts(add):

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

describe("BatchBurn", function () {
  let token: BatchBurnToken;
  let owner: any, user1: any, user2: any;

  beforeEach(async function () {
    [owner, user1, user2] = await ethers.getSigners();
    const TokenFactory = await ethers.getContractFactory("BatchBurnToken");
    token = await TokenFactory.deploy();
    await token.deployed();
    await token.transfer(user1.address, ethers.utils.parseEther("1000"));
    await token.transfer(user2.address, ethers.utils.parseEther("1000"));
  });

  it("should batch burn tokens", async function () {
    await expect(token.batchBurn(
      [user1.address, user2.address],
      [ethers.utils.parseEther("500"), ethers.utils.parseEther("300")]
    ))
      .to.emit(token, "Burn")
      .withArgs(user1.address, ethers.utils.parseEther("500"))
      .to.emit(token, "Burn")
      .withArgs(user2.address, ethers.utils.parseEther("300"));
    expect(await token.balanceOf(user1.address)).to.equal(ethers.utils.parseEther("500"));
    expect(await token.balanceOf(user2.address)).to.equal(ethers.utils.parseEther("700"));
    expect(await token.totalSupply()).to.equal(ethers.utils.parseEther("999200"));
  });

  it("should revert if input arrays mismatch", async function () {
    await expect(token.batchBurn(
      [user1.address],
      [ethers.utils.parseEther("500"), ethers.utils.parseEther("300")]
    )).to.be.revertedWith("Invalid input");
  });
});
  • 测试解析
    • 批量燃烧两个账户的代币,触发事件,totalSupply减少。
    • 输入数组长度不一致时失败。

燃烧换奖励

燃烧代币换取奖励(如NFT或ETH),增加激励。

合约代码

contracts/BurnWithReward.sol

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

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

contract RewardNFT is ERC721, Ownable {
    uint256 public tokenId;

    constructor() ERC721("RewardNFT", "RNFT") Ownable() {}

    function mint(address to) public onlyOwner returns (uint256) {
        tokenId++;
        _mint(to, tokenId);
        return tokenId;
    }
}

contract BurnWithRewardToken is ERC20, Ownable, ReentrancyGuard {
    RewardNFT public nft;
    uint256 public burnThreshold = 100 * 10**18; // 100 tokens for 1 NFT
    uint256 public ethReward = 0.1 ether; // 0.1 ETH per 100 tokens

    event Burn(address indexed burner, uint256 amount);
    event Reward(address indexed user, uint256 tokenId);
    event EthReward(address indexed user, uint256 amount);

    constructor(address _nft) ERC20("BurnWithRewardToken", "BWRT") Ownable() {
        nft = RewardNFT(_nft);
        _mint(msg.sender, 1000000 * 10**decimals());
    }

    function burnForReward(uint256 amount) public nonReentrant payable {
        require(amount >= burnThreshold, "Amount too low");
        require(address(this).balance >= ethReward, "Insufficient ETH");
        _burn(msg.sender, amount);
        emit Burn(msg.sender, amount);

        uint256 nftCount = amount / burnThreshold;
        for (uint256 i = 0; i < nftCount; i++) {
            uint256 newTokenId = nft.mint(msg.sender);
            emit Reward(msg.sender, newTokenId);
        }

        uint256 ethAmount = (amount / burnThreshold) * ethReward;
        (bool success, ) = msg.sender.call{value: ethAmount}("");
        require(success, "ETH transfer failed");
        emit EthReward(msg.sender, ethAmount);
    }

    function depositEth() public payable onlyOwner {}
}

解析

  • 逻辑
    • burnForReward:燃烧代币换NFT和ETH。
    • burnThreshold:100代币换1个NFT和0.1 ETH。
    • nonReentrant防止重入。
    • depositEth:为合约充值ETH。
    • 触发BurnRewardEthReward事件。
  • 安全特性
    • nonReentrant保护外部调用。
    • 检查burnThreshold和ETH余额。
    • 事件记录所有操作。
  • Gas:每次NFT铸造和ETH转账增加Gas(~50k/NFT)。
  • 适用场景:激励用户参与,增加项目活跃度。

测试

test/TokenBurn.test.ts(add):

import { BurnWithRewardToken, RewardNFT } from "../typechain-types";

describe("BurnWithReward", function () {
  let token: BurnWithRewardToken;
  let nft: RewardNFT;
  let owner: any, user1: any;

  beforeEach(async function () {
    [owner, user1] = await ethers.getSigners();
    const NftFactory = await ethers.getContractFactory("RewardNFT");
    nft = await NftFactory.deploy();
    await nft.deployed();

    const TokenFactory = await ethers.getContractFactory("BurnWithRewardToken");
    token = await TokenFactory.deploy(nft.address);
    await token.deployed();

    await token.depositEth({ value: ethers.utils.parseEther("1") });
    await token.transfer(user1.address, ethers.utils.parseEther("1000"));
  });

  it("should burn tokens and reward NFT and ETH", async function () {
    const initialBalance = await ethers.provider.getBalance(user1.address);
    await expect(token.connect(user1).burnForReward(ethers.utils.parseEther("200")))
      .to.emit(token, "Burn")
      .withArgs(user1.address, ethers.utils.parseEther("200"))
      .to.emit(token, "Reward")
      .withArgs(user1.address, 1)
      .to.emit(token, "EthReward")
      .withArgs(user1.address, ethers.utils.parseEther("0.2"));
    expect(await token.balanceOf(user1.address)).to.equal(ethers.utils.parseEther("800"));
    expect(await token.totalSupply()).to.equal(ethers.utils.parseEther("999800"));
    expect(await nft.ownerOf(1)).to.equal(user1.address);
    expect(await ethers.provider.getBalance(user1.address)).to.be.gt(initialBalance);
  });

  it("should revert if amount too low", async function () {
    await expect(token.connect(user1).burnForReward(ethers.utils.parseEther("50"))).to.be.revertedWith("Amount too low");
  });
});
  • 测试解析
    • 燃烧200代币,获得2个NFT和0.2 ETH。
    • 低于burnThreshold失败。
    • 事件和状态正确更新。

部署脚本

scripts/deploy.ts

import { ethers } from "hardhat";

async function main() {
  const [owner, burner1, burner2, burner3] = await ethers.getSigners();

  const BasicBurnFactory = await ethers.getContractFactory("BasicBurnToken");
  const basicBurn = await BasicBurnFactory.deploy();
  await basicBurn.deployed();
  console.log(`BasicBurnToken deployed to: ${basicBurn.address}`);

  const SecureBurnFactory = await ethers.getContractFactory("SecureBurnToken");
  const secureBurn = await SecureBurnFactory.deploy();
  await secureBurn.deployed();
  console.log(`SecureBurnToken deployed to: ${secureBurn.address}`);

  const MultiSigBurnFactory = await ethers.getContractFactory("MultiSigBurnToken");
  const multiSigBurn = await MultiSigBurnFactory.deploy([burner1.address, burner2.address, burner3.address], 2);
  await multiSigBurn.deployed();
  console.log(`MultiSigBurnToken deployed to: ${multiSigBurn.address}`);

  const BatchBurnFactory = await ethers.getContractFactory("BatchBurnToken");
  const batchBurn = await BatchBurnFactory.deploy();
  await batchBurn.deployed();
  console.log(`BatchBurnToken deployed to: ${batchBurn.address}`);

  const RewardNftFactory = await ethers.getContractFactory("RewardNFT");
  const rewardNft = await RewardNftFactory.deploy();
  await rewardNft.deployed();
  console.log(`RewardNFT deployed to: ${rewardNft.address}`);

  const BurnWithRewardFactory = await ethers.getContractFactory("BurnWithRewardToken");
  const burnWithReward = await BurnWithRewardFactory.deploy(rewardNft.address);
  await burnWithReward.deployed();
  console.log(`BurnWithRewardToken deployed to: ${burnWithReward.address}`);
}

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

跑部署:

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

跑代码,体验Solidity代币燃烧!

点赞 0
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

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