発行枚数を増発できない設計(コードレベル)
「絶対に増発できない」はコード+権限放棄+アップグレード不可の3点を同時に満たさない限り成立しません。どれか1つでも抜けると“後から増やせる余地”が残ります。
以下は、実務レベルで“増発不可能”を成立させる設計です。
■ 1.設計の絶対条件(これが揃って初めて成立)
固定供給(immutable)
初期発行時に全量mint
以降はmint関数そのものが存在しないOwner権限の完全破棄
owner = address(0)
もしくはそもそもOwnerを持たない設計Upgrade不可
Proxy禁止(UUPS / Transparent など全NG)
コントラクトは完全固定
■ 2.Solidity実装(最小かつ安全な形)
OpenZeppelinベースでもいいですが、あえて“余計な機能を削る”のがポイント
</> solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract FixedSupplyToken {
string public name = "Fixed Token";
string public symbol = "FIX";
uint8 public decimals = 18;
uint256 public immutable totalSupply;
mapping(address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 value);
constructor(uint256 _initialSupply) {
totalSupply = _initialSupply;
balanceOf[msg.sender] = _initialSupply;
emit Transfer(address(0), msg.sender, _initialSupply);
}
function transfer(address to, uint256 value) external returns (bool) {
require(balanceOf[msg.sender] >= value, "balance too low");
unchecked {
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
}
emit Transfer(msg.sender, to, value);
return true;
}}
■ 3.この設計の重要ポイント
mint関数が存在しない
→ 呼び出し不可能ではなく「物理的に存在しない」totalSupplyがimmutable
→ デプロイ後に変更不可owner / admin が存在しない
→ 操作主体そのものが無い
■ 4.よくある“偽の固定供給”に注意
〇 パターン1:mint関数あり + 未使用
</> solidity
function mint(...) external onlyOwner {}
👉「使ってない」ではなく「使える時点でアウト」
〇 パターン2:Upgradeable
Proxy経由でロジック差し替え
👉 後からmint追加可能
〇 パターン3:外部コントラクト経由
ブリッジやラッパーで実質増発
👉 見かけの供給だけ固定
〇 パターン4:Owner renounceの罠
</> solidity
renounceOwnership()
👉 一見安全だが…
その前に仕込まれている可能性、別の権限(role)が残っている
■ 5.完全固定にするための追加強化
コード検証(Etherscan等)
ソース完全公開
バイトコード一致外部依存ゼロ
Oracleなし
外部コールなしブリッジ禁止
👉 他チェーンで“増える”余地を排除
■ 6.「絶対」を崩す唯一の例外
実は1つだけ例外があります。
👉 チェーン自体が巻き戻る場合
ハードフォーク
コンセンサス変更
例:Ethereum のDAO事件
👉これはアプリ側では防げない
■ 7.本質まとめ
本当に増発できない設計とは:
▶ mintが存在しない
▶ 権限が存在しない
▶ 変更手段が存在しない
👉つまり
「未来の自由度を完全に捨てる設計」
■ 8.実務的な補足(重要)
この設計は“最も安全”ですが同時に:
▶ 柔軟性ゼロ
▶ バグ修正不可
▶ 経済設計の調整不可
👉だから多くのプロジェクトは採用しない
👉そして“増発可能”に逃げる
