本記事は Etherscan で公開されているソースコードを元に技術的な分析を行ったものであり、公式監査結果や開発者へのヒアリング結果ではありません。また、本記事に記載する考察はソースコードから推測した内容を含み、公式見解を示すものではありません。
TL;DR
- JPYSCはFiatToken系の管理型ステーブルコイン
- Mint先ホワイトリストとSeize機能が特徴
- 脅威モデルは外部攻撃より内部統制
1. 概要
コードから読み取れる事実
- 中核コントラクトは
StablecoinV1で、ERC-20 に加えて以下の機能を統合している。 - 一時停止:
Pausable - ブラックリスト:
Blacklistable - ミント先ホワイトリスト(minter 単位):
Whitelistable - 差押え(強制移転):
Seizable - 誤送金トークン回収:
AssetRecovery - ガスレス承認:
EIP2612(permit) - ガスレス送金:
ERC3009Upgradeable(transferWithAuthorization,receiveWithAuthorization) - UUPS アップグレード:
UUPSUpgradeable -
Initializable+Ownable2StepUpgradeableを採用したプロキシ前提の設計。 - ストレージは ERC-7201 namespaced storage で管理され、機能モジュールごとに専用スロットを持つ。
-
initialize内でロール初期化時の分離制約を実施している(例: minterAdmin と whitelistManager の分離、blacklister と seizer の分離、owner と upgradeAuthority の分離)。
筆者の考察
- 「法令対応・運用統制」を重視したステーブルコイン実装で、単なる ERC-20 より管理機能が強い。
- ERC-7201 namespaced storage の採用は、アップグレード時のストレージ衝突を避けるうえで妥当。
- 役割分離(SoD: Segregation of Duties)をコードレベルで強制している点は、運用手順依存を減らす実装として評価できる。
2. 主要機能
コードから読み取れる事実
主要関数一覧
| 関数シグネチャ | 役割 | 呼び出し可能な主体 | 状態変更内容 | 発火イベント |
|---|---|---|---|---|
initialize(...) |
初期化(メタデータ、各ロール、EIP-712 設定) | 初期化時に 1 回のみ | 各モジュールのロール・メタデータを設定 |
UpgradeAuthorityChanged ほか各モジュール初期化イベント |
mint(address to, uint256 amount) |
新規発行 |
onlyMinter かつ onlyWhitelisted(to)
|
totalSupply 増加、balances[to] 増加、minter allowance 減算 |
Mint, Transfer(address(0), to, amount)
|
burn(uint256 amount) |
償却 | onlyMinter |
balances[msg.sender] 減少、totalSupply 減少 |
Burn, Transfer(msg.sender, address(0), amount)
|
transfer(address to, uint256 amount) |
通常送金 | トークン保有者 | 残高移転 | Transfer |
transferFrom(address from, address to, uint256 amount) |
承認済み送金 | allowance を持つ spender | allowance 減少(無限許可除く)、残高移転 | Transfer |
approve(address spender, uint256 amount) |
利用許可設定 | トークン保有者 | allowance 設定 | Approval |
increaseAllowance(address spender, uint256 addedValue) |
allowance 増加 | トークン保有者 | allowance 加算 | Approval |
decreaseAllowance(address spender, uint256 subtractedValue) |
allowance 減少 | トークン保有者 | allowance 減算 | Approval |
permit(address owner_, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) |
署名によるガスレス承認 | 誰でも提出可(署名検証前提) | nonce 増加、allowance 設定 | Approval |
transferWithAuthorization(...) |
署名によるガスレス送金 | 誰でも提出可(署名検証前提) | authorization 状態を使用済みに更新、残高移転 |
AuthorizationUsed, Transfer
|
receiveWithAuthorization(...) |
受取人限定のガスレス送金 | msg.sender == to |
authorization 状態を使用済みに更新、残高移転 |
AuthorizationUsed, Transfer
|
cancelAuthorization(...) |
署名認可の取消 | 誰でも提出可(署名検証前提) | authorization 状態を使用済みに更新(取消) | AuthorizationCanceled |
configureMinter(address minter, uint256 allowance_) |
minter 登録/再設定 | onlyMinterAdmin |
minter フラグ・allowance 設定 | MinterConfigured |
removeMinter(address minter) |
minter 削除 | onlyMinterAdmin |
minter フラグ解除、allowance ゼロ化 | MinterRemoved |
whitelistMintDestination(address minter, address account) |
minter ごとのミント先追加 | onlyWhitelistManager |
whitelist 更新 | MintDestinationWhitelisted |
unwhitelistMintDestination(address minter, address account) |
minter ごとのミント先削除 | onlyWhitelistManager |
whitelist 更新 | MintDestinationUnwhitelisted |
blacklist(address account) / unBlacklist(address account)
|
ブラックリスト制御 | onlyBlacklister |
blacklist 状態更新 |
Blacklisted / UnBlacklisted
|
pause() / unpause()
|
停止/再開 | onlyPauser |
pause 状態更新 | OZ Paused / Unpaused
|
seize(address from, address to, uint256 amount, bytes32 trackId) |
差押えによる強制移転 | onlySeizer |
残高移転(総供給不変) |
Seized, Transfer
|
recoverERC20(address tokenContract, address to, uint256 amount) |
誤送金 ERC20 回収 | onlyAssetRecoverer |
外部 ERC20 へsafeTransfer呼び出し |
AssetRecovered |
updatePauser(...) / updateBlacklister(...) / updateSeizer(...) / updateAssetRecoverer(...) / updateWhitelistManager(...) / updateMinterAdmin(...)
|
運用ロール更新 | 各現行ロールまたはowner(デュアルアクセス) |
ロールアドレス更新 | 各 *Changed
|
updateUpgradeAuthority(address newUpgradeAuthority) |
アップグレード権限更新 | onlyUpgradeAuthority |
upgradeAuthority 更新 | UpgradeAuthorityChanged |
upgradeToAndCall(...)(UUPS 経由) |
実装アップグレード |
_authorizeUpgrade により onlyUpgradeAuthority
|
実装アドレス更新 | UUPS 標準イベント |
主要イベント一覧
| イベント | 意味 |
|---|---|
Transfer |
トークン移転(mint/burn 含む) |
Approval |
allowance 設定 |
Mint / Burn
|
発行・償却 |
MinterConfigured / MinterRemoved / MinterAdminChanged
|
minter 関連の管理変更 |
MintDestinationWhitelisted / MintDestinationUnwhitelisted / WhitelistManagerChanged
|
ミント先ホワイトリスト管理 |
Blacklisted / UnBlacklisted / BlacklisterChanged
|
ブラックリスト管理 |
PauserChanged |
pauser 更新 |
Seized / SeizerChanged
|
差押え実行・seizer 更新 |
AssetRecovered / AssetRecovererChanged
|
誤送金回収関連 |
AuthorizationUsed / AuthorizationCanceled
|
ERC-3009 認可の利用・取消 |
UpgradeAuthorityChanged |
アップグレード権限更新 |
筆者の考察
-
mintを「minter 権限 + 宛先ホワイトリスト + ブラックリスト + pause」で多層防御している点は、発行統制として強い。 -
configureMinterが allowance 再設定時に「現在 allowance が 0 であること」を要求する設計は、mempool 上の競合(再設定前の駆け込み mint)を意識した実装になっている。 -
receiveWithAuthorizationを実装しているため、ERC-3009 の典型的な先回りリスクを受取人固定フローで回避できる。
3. 処理フロー
コードから読み取れる事実
Mint フロー
-
minterAdminがconfigureMinterで minter と allowance を設定。 -
whitelistManagerがwhitelistMintDestination(minter, to)を設定。 - minter が
mint(to, amount)実行。 -
whenNotPaused、notBlacklisted、onlyWhitelisted(to)、allowance 超過チェックを通過した場合のみ発行。 -
MintとTransfer(address(0), to, amount)を発火。
Burn フロー
- minter が
burn(amount)実行。 -
whenNotPaused、onlyMinter、notBlacklisted(msg.sender)を通過。 - 残高を減算し、
totalSupplyを減算。 -
BurnとTransfer(msg.sender, address(0), amount)を発火。
Transfer フロー
- 標準送金:
transfer/transferFrom。 - ガスレス送金:
transferWithAuthorization/receiveWithAuthorization。 - いずれも残高移転は内部
_transferに集約される。 - 通常送金と
transferWithAuthorizationはwhenNotPaused+ blacklist チェックの対象。 -
receiveWithAuthorizationはmsg.sender == to制約を満たす必要がある。
管理者操作
- Pause/Unpause、Blacklist/UnBlacklist、Seize、Minter 設定、Whitelist 設定、各ロール更新、UpgradeAuthority 更新が分離ロールで実行される。
- ロール更新の多くは「現行ロールまたは Owner」が実行可能(デュアルアクセス)。
- アップグレードは Owner ではなく
upgradeAuthorityが実行主体。
状態遷移
-
paused状態: 多くのトークン操作を停止。 - blacklist 状態: 対象アカウントを各操作から排除。
- minter 状態:
configureMinter/removeMinterで有効/無効。 - authorization 状態(ERC-3009): nonce 単位で未使用 → 使用済み/取消済みに遷移。
権限の流れ
ミント時のシーケンス
筆者の考察
- フロー全体は「発行前提条件を多段化」しており、単一権限の誤操作が直ちに大きな被害へ繋がりにくい。
- 一方で、運用者はロール間の連携(minterAdmin と whitelistManager、blacklister と seizer、owner と upgradeAuthority)を手順化しないと実務で詰まりやすい。
4. セキュリティ診断
コードから読み取れる事実
観点別評価
| 観点 | 事実ベース評価 |
|---|---|
| アクセス制御 |
onlyMinter / onlyMinterAdmin / onlyWhitelistManager / onlyBlacklister / onlySeizer / onlyPauser / onlyAssetRecoverer / onlyUpgradeAuthority を実装。 |
| Mint/Burn 権限 |
mint と burn は onlyMinter。mint は宛先ホワイトリストと allowance 制約あり。 |
| Pause 機能 |
transfer / approve / transferFrom / allowance 増減 / permit / transferWithAuthorization / receiveWithAuthorization / mint / burn は whenNotPaused。seize と cancelAuthorization は pause 非依存。 |
| ブラックリスト機能 | 主要操作に notBlacklisted を適用。seize は from がブラックリスト済みであることを要求。 |
| Upgrade 権限 | UUPS の _authorizeUpgrade は onlyUpgradeAuthority。owner と upgradeAuthority の同一化を防ぐチェックあり。 |
| Reentrancy | トークン本体の主要状態更新は外部呼び出しなし。recoverERC20 は外部 ERC20 呼び出しあり(safeTransfer)。 |
| Front Running |
receiveWithAuthorization で受取人固定。configureMinter の allowance 再設定制約で競合を緩和。 |
| DOS リスク | bulk blacklist/unblacklist は配列長に応じてガス増。極端に大きい配列は実行困難化の可能性。 |
| 権限集中リスク | 役割分離制約は存在。ただし個々のロール鍵が単独管理なら運用面の集中リスクは残る。 |
| 外部コントラクト依存 | OpenZeppelin(upgradeable 含む)と EIP 署名検証(ECDSA)に依存。recoverERC20 は任意 ERC20 実装の挙動に影響を受ける。 |
| その他 |
renounceOwnership を明示的に無効化し、オーナー喪失による機能停止を防止。 |
良い実装
- 役割分離を初期化時・更新時の両方でチェックしている。
- UUPS 権限を owner から分離している。
- ERC-3009 の
receiveWithAuthorizationを実装し、提出者制約を設けている。 -
mintの 0 値ミントを禁止してスプリアスイベントを防いでいる。 -
removeMinterで allowance を 0 化し、再設定時に 0 残高条件を要求している。
気になる点
-
recoverERC20は外部トークン呼び出しを行うため、対象トークンが悪意ある実装の場合の副作用に注意が必要。 -
seizeは pause 状態でも実行可能であり、設計意図が強制執行優先である反面、運用ポリシー次第で誤解を招く可能性がある。 -
removeMinter後も minter 向けホワイトリストエントリは残るため、再有効化時に古い宛先が復活する。
改善案
- 運用レイヤで
upgradeAuthorityを Timelock + マルチシグに固定し、変更手順を監査ログ付きで標準化する。 -
recoverERC20実行時に許可対象トークンのポリシー(allow-list)や追加イベントメタデータを運用で補強する。 - minter 再有効化時にホワイトリスト再レビューを必須化し、必要ならコントラクト側に一括削除補助機能を追加する。
筆者の考察
- 全体として「攻撃技術」より「内部統制の破綻」を主たる脅威モデルに置いた設計に見える。
- よってセキュリティ品質は、コードの堅牢性に加えて、鍵管理・権限委任・緊急時手順の成熟度で大きく変わる。
5. 総合レビュー
コードから読み取れる事実
- 保守性: 機能はモジュール分割され、責務が比較的明確。
- 可読性: コメント・カスタムエラーが充実し、失敗理由の追跡がしやすい。
- 拡張性: UUPS + namespaced storage で将来拡張を想定。
- セキュリティ: 多層ガード(pause/blacklist/whitelist/role separation)を実装。
筆者の考察
- 保守性: 良好。業務ロジックと共通機能の分離が効いている。
- 可読性: 高め。エラー名とイベント名が運用文脈に沿っている。
- 拡張性: 高いが、アップグレード運用の品質が前提。
- セキュリティ: 設計は堅いが、実運用では「誰が鍵を持ち、どう承認するか」が最重要。
6. FiatToken 比較表(USDC/JPYC/JPYSC)
コードから読み取れる事実
- JPYSC は本記事で解析した
StablecoinV1実装に基づき、FiatToken 系で見られる管理機能(Pause、Blacklist、Minter 管理、Asset Recovery、ガスレス署名系、Upgradeable)を備える。 - さらに JPYSC には、minter 単位のミント先ホワイトリストと、ブラックリスト済み口座を前提にした差押え機能(Seize)が実装されている。
- USDC / JPYC については本リポジトリ内に当該コントラクト実装がないため、以下比較の USDC/JPYC 列は一般公開されている情報・公開コードで広く知られた構成を要約した参考情報である。
| 観点 | USDC(FiatToken 系) | JPYC(FiatToken 系) | JPYSC(本記事解析対象) |
|---|---|---|---|
| 基本モデル | 管理型 ERC-20(法令・運用対応を重視) | 管理型 ERC-20(運用統制機能を持つ) | 管理型 ERC-20(運用統制機能を持つ) |
| Pause | あり | あり | あり(pause / unpause) |
| Blacklist | あり | あり | あり(blacklist / unBlacklist) |
| Mint 管理 |
Minter と上位管理ロールで制御 |
Minter と管理ロールで制御 |
MinterAdmin + MinterAllowance
|
| Mint 先制御 | 実装差分あり(版によって異なる) | 実装差分あり(版によって異なる) | minter ごとの宛先ホワイトリストを実装 |
| Burn | あり(ミンター系権限で実施) | あり(ミンター系権限で実施) | あり(onlyMinter) |
| 署名系 | EIP-2612、ERC-3009 系を採用する版がある | EIP-2612/3009 系を採用する版がある | EIP-2612 + ERC-3009 を実装 |
| Asset Recovery | あり(誤送金回収) | あり(誤送金回収) | あり(recoverERC20) |
| 強制移転(Seize) | 実装方針は版・運用方針に依存 | 実装方針は版・運用方針に依存 | あり(seize、fromはブラックリスト済み要件) |
| Upgrade | Upgradeable(運用管理前提) | Upgradeable(運用管理前提) | UUPS + upgradeAuthority 分離 |
| 役割分離の強制 | 高い(管理型ステーブルコインの典型) | 高い(管理型ステーブルコインの典型) | 高い(owner/upgradeAuthority、blacklister/seizer 等を分離) |
筆者の考察
- JPYSC は FiatToken 系の設計思想を踏襲しつつ、国内運用を強く意識した統制機能を追加した実装に見える。
- 特に「minter ごとの宛先ホワイトリスト」と「blacklist を前提にした seize」は、発行・強制執行の運用ルールをコードに落とし込む方向性が明確。
- 比較時に重要なのは機能名の有無だけでなく、ロール分離の強制度合い、アップグレード権限のガバナンス、緊急時手順の設計である。
7. まとめ
コードから読み取れる事実
- JPYSC コントラクトは、ERC-20 をベースに法令対応・統制機能を重ねた設計。
- 発行・償却・送金・差押え・停止・ブラックリスト・アップグレードを明確なロールで分担。
- 署名ベース機能(EIP-2612 / ERC-3009)と運用統制機能が同居している。
筆者の考察
- 「実務で使うステーブルコイン」の観点で、かなり現実的な設計。
- 今後の品質差は、スマートコントラクトそのものより、ガバナンス実装(timelock/multisig)と運用監査体制で決まると考える。
8. 参考資料
- JPYSC 公式サイト
https://www.sbivc.co.jp/jpysc - Etherscan(JPYSC ソースコード)
https://etherscan.io/address/0xba161409749ae95e9bbc1cf3a2dd3fe3e0af1d5e#code - OpenZeppelin Contracts (Upgradeable)
https://docs.openzeppelin.com/contracts - EIP-2612
https://eips.ethereum.org/EIPS/eip-2612 - EIP-3009
https://eips.ethereum.org/EIPS/eip-3009