# [ERC8056] ERC20トークンの実残高を変えずに表示数量だけを調整する仕組みを理解しよう！

> ERC8056は、ERC20トークンに表示用の倍率を追加する拡張です。提案名では、この仕組みを Scaled UI Amount Extension と呼んでいます。

- URL: https://cardene.dev/articles/a7eb4e6d40793a5ce75e
- 公開日: 2026-06-12
- 更新日: 2026-06-12
- トピック: Blockchain
- タグ: ethereum, smartcontract, rwa, erc

## はじめに

初めまして。
『DApps開発入門』という本や色々記事を書いているかるでねです。

https://amzn.asia/d/gxvJ0Pw

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください！

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://twitter.com/cardene777

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

今回は「**ERC20トークンの実残高を変えずに表示数量だけを調整する表示数量スケーリング拡張を定義するERC8056**」についてまとめていきます。

**ERC8056**は、ERC20トークンの `balanceOf` や `transfer` で扱う数量をそのまま保ちながら、ウォレットや取引所でユーザーに見せる数量だけを倍率で変えられるようにする提案です。
株式分割のように、保有者全員の見た目の数量を同じ比率で変えたい場面で、全保有者への追加mintや個別送金を避けられます。

:::message
以下にまとめられているものを解説しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-8056

## 概要

**ERC8056**は、ERC20トークンに表示用の倍率を追加する拡張です。
提案名では、この仕組みを `Scaled UI Amount Extension` と呼んでいます。
トークンの実残高、総供給量、送金数量、DeFiコントラクト内の会計は `raw amount` として扱い、ウォレットや取引所の画面では `raw amount` に `uiMultiplier` を掛けた数量を表示します。

**ERC20**は、トークンの残高取得、送金、承認を共通化するための標準です。
**ERC8056**はこの標準を置き換えず、表示用の関数とイベントを追加します。

:::message
**ERC20**については以下の記事を参考にしてください。
:::

[\[ERC20\] ERC20を1からわかりやすく解説](/articles/aeefe2d89dd5332c9f43)

**ERC8056**の中心は、実残高と表示数量を分けることです。

![ERC8056 の全体像](../../images/erc8056/overview.png)

表示倍率が `2.0` の時、実残高が `100` のアカウントはウォレット上で `200` と表示できます。
一方で、送金や残高会計は `100` のまま扱います。
そのため、既存のERC20連携先は従来どおりの数量で処理できます。

## 動機

現実資産をトークン化する時、株式分割のようなイベントをオンチェーンで表現したくなる場面があります。
例えば、ある株式が2対1で分割されると、株主が持つ株数は見た目として2倍になります。
しかし、経済的な持分が突然2倍になるわけではありません。

ERC20だけで同じ見せ方を作るには、全保有者へ追加mintしたり、保有者ごとに残高を調整したりする必要があります。
保有者数が多いほどガスコストと運用負荷が大きくなり、取引所、レンディング、AMMを含む内部会計とも衝突しやすくなります。

**ERC8056**は、この問題を「実残高を変える」のではなく「表示倍率を変える」ことで解決します。
発行体は `uiMultiplier` を更新し、インテグレーターは表示時だけその倍率を使います。
これにより、トークンの経済的な会計を保ちながら、ユーザーに見える数量だけを分割後の数量へ合わせられます。

#### 既存方式との違い

株式分割の表現には、リベース型トークン、ラッパートークン、交換レート型トークン、オフチェーン表示だけで処理する方法があります。
ただし、それぞれ課題があります。

| 方式 | 課題 |
|---|---|
| リベース型トークン | 残高そのものが変わるため、DeFiコントラクト側の前提と衝突しやすい |
| ラッパートークン | 分割のたびに別トークンや変換処理が増え、運用が複雑になる |
| 交換レート型トークン | 表示側で追加計算が必要になり、ユーザーが直感的に理解しにくい |
| オフチェーン表示 | 標準化されていないため、ウォレットや取引所ごとに扱いが分かれる |

**ERC8056**は、オンチェーンで倍率を公開し、表示側が同じ規則で数量を変換できるようにします。
倍率は表示用の層に閉じるため、ERC20の実残高を前提にした既存システムを壊しません。

## 仕様

**ERC8056**に準拠するコントラクトは、中心となる `IScaledUIAmount` と、予約中の表示倍率を公開する `IScaledUIAmountNewUIMultiplier` を実装します。
変換補助と表示用残高取得は任意拡張として定義されます。

### IScaledUIAmount

`IScaledUIAmount` は、現在有効な表示倍率を返す中心インターフェースです。

```js
interface IScaledUIAmount {
    event UIMultiplierUpdated(
        uint256 oldMultiplier,
        uint256 newMultiplier,
        uint256 effectiveAtTimestamp
    );

    event TransferWithUIAmount(
        address indexed from,
        address indexed to,
        uint256 amount,
        uint256 uiAmount
    );

    function uiMultiplier() external view returns (uint256);
}
```

`uiMultiplier` は18桁の小数精度で表現されます。
`1e18` が `1.0` を表し、`2e18` が `2.0` を表します。

#### `uiMultiplier`

```js
function uiMultiplier() external view returns (uint256);
```

現在有効な表示倍率を返します。
実装が将来の倍率を予約している場合でも、反映時刻に到達するまでは古い倍率を返し、反映時刻以降は新しい倍率を返します。

- **戻り値**

| 型 | 説明 |
|---|---|
| `uint256` | 18桁精度の表示倍率。`1e18` が `1.0` |

表示数量は、実残高と倍率から計算します。

```js
uiAmount = (rawAmount * uiMultiplier) / 1e18
```

この式は、実残高を表示用数量に変換するだけです。
ERC20の `balanceOf`、`transfer`、`transferFrom` で使う数量は変わりません。

#### `UIMultiplierUpdated`

```js
event UIMultiplierUpdated(
    uint256 oldMultiplier,
    uint256 newMultiplier,
    uint256 effectiveAtTimestamp
);
```

表示倍率が変更された時に発行されるイベントです。
ウォレット、取引所、インデクサーはこのイベントを見て、表示用キャッシュやユーザー通知を更新できます。

| フィールド | 説明 |
|---|---|
| `oldMultiplier` | 変更前の表示倍率 |
| `newMultiplier` | 変更後に使う表示倍率 |
| `effectiveAtTimestamp` | 新しい倍率が有効になるUnixタイムスタンプ |

#### `TransferWithUIAmount`

```js
event TransferWithUIAmount(
    address indexed from,
    address indexed to,
    uint256 amount,
    uint256 uiAmount
);
```

ERC20送金と同時に、表示用数量も通知したい実装で使うイベントです。
このイベントは任意で発行され、インターフェースIDには影響しません。

| フィールド | 説明 |
|---|---|
| `from` | 送信元アドレス |
| `to` | 送信先アドレス |
| `amount` | ERC20送金で使われた実数量 |
| `uiAmount` | 送金時点の表示倍率を使った表示用数量 |

このイベントがあっても、ERC20の `Transfer` イベントの意味は変わりません。
送金数量の正本は `amount` であり、`uiAmount` は画面表示や履歴表示の補助です。

### IScaledUIAmountNewUIMultiplier

`IScaledUIAmountNewUIMultiplier` は、予約中の倍率と反映時刻を公開する必須拡張です。

```js
interface IScaledUIAmountNewUIMultiplier {
    function newUIMultiplier() external view returns (uint256);
    function effectiveAt() external view returns (uint256);
}
```

表示倍率が突然変わると、ウォレット、取引所、インデクサーが更新を追いにくくなります。
そこで、準拠コントラクトは予約中の倍率と反映時刻を外部から読めるようにします。

表示倍率の予約と反映は以下です。

![表示倍率の予約と反映](../../images/erc8056/multiplier-activation-flow.png)

`effectiveAt` に到達するまでは、`uiMultiplier` は現在の倍率を返します。
到達後は、予約されていた `newUIMultiplier` が有効な表示倍率として使われます。
この間も、ERC20の実残高は変更されません。

#### `newUIMultiplier`

```js
function newUIMultiplier() external view returns (uint256);
```

反映待ちの新しい表示倍率を返します。
すでに反映済みの状態では、現在有効な倍率と同じ値を返す実装になります。

- **戻り値**

| 型 | 説明 |
|---|---|
| `uint256` | 18桁精度の予約済み表示倍率 |

#### `effectiveAt`

```js
function effectiveAt() external view returns (uint256);
```

`newUIMultiplier` が有効になるUnixタイムスタンプを返します。
ウォレットや取引所はこの値を使い、ユーザーに「いつ表示数量が変わるか」を事前に示せます。

- **戻り値**

| 型 | 説明 |
|---|---|
| `uint256` | 新しい倍率が有効になるUnixタイムスタンプ |

### IScaledUIAmountConversion

`IScaledUIAmountConversion` は、実数量と表示用数量をオンチェーンで相互変換する任意拡張です。

```js
interface IScaledUIAmountConversion {
    function toUIAmount(uint256 rawAmount) external view returns (uint256);
    function fromUIAmount(uint256 uiAmount) external view returns (uint256);
}
```

この拡張を実装すると、ウォレットや他のコントラクトは変換式を自前で持たず、トークンコントラクトに変換結果を問い合わせられます。

#### `toUIAmount`

```js
function toUIAmount(uint256 rawAmount) external view returns (uint256);
```

実数量を表示用数量へ変換します。

- **引数**

| 名前 | 型 | 説明 |
|---|---|---|
| `rawAmount` | `uint256` | ERC20の実数量 |

- **戻り値**

| 型 | 説明 |
|---|---|
| `uint256` | 表示倍率を反映した表示用数量 |

#### `fromUIAmount`

```js
function fromUIAmount(uint256 uiAmount) external view returns (uint256);
```

表示用数量を実数量へ戻します。
ユーザーが画面上で `200` と入力した時、実際に `transfer` へ渡す数量を計算する用途で使えます。

- **引数**

| 名前 | 型 | 説明 |
|---|---|---|
| `uiAmount` | `uint256` | ユーザーに見せる表示用数量 |

- **戻り値**

| 型 | 説明 |
|---|---|
| `uint256` | ERC20の `transfer` へ渡す実数量 |

### IScaledUIAmountBalances

`IScaledUIAmountBalances` は、表示倍率を反映した残高と総供給量を返す任意拡張です。

```js
interface IScaledUIAmountBalances {
    function balanceOfUI(address account) external view returns (uint256);
    function totalSupplyUI() external view returns (uint256);
}
```

#### `balanceOfUI`

```js
function balanceOfUI(address account) external view returns (uint256);
```

指定したアカウントの表示用残高を返します。
内部的には `balanceOf(account)` に表示倍率を掛けた値になります。

- **引数**

| 名前 | 型 | 説明 |
|---|---|---|
| `account` | `address` | 表示用残高を確認するアドレス |

- **戻り値**

| 型 | 説明 |
|---|---|
| `uint256` | 表示倍率を反映した残高 |

#### `totalSupplyUI`

```js
function totalSupplyUI() external view returns (uint256);
```

表示倍率を反映した総供給量を返します。
実際のERC20総供給量は `totalSupply()` の戻り値であり、`totalSupplyUI()` は表示用の値です。

- **戻り値**

| 型 | 説明 |
|---|---|
| `uint256` | 表示倍率を反映した総供給量 |

### インターフェース検出

**ERC8056**に準拠するコントラクトは、**ERC165**によるインターフェース検出を実装します。
`IScaledUIAmount` に対応している場合、`supportsInterface` で対象のインターフェースIDに `true` を返します。

:::message
**ERC165**については以下の記事を参考にしてください。
:::

[\[ERC165\] コントラクトが特定のインターフェースをサポートしているか確認する仕組みを理解しよう！](/articles/541184977a5cba51b759)

提案で示されているインターフェースIDは以下です。

| インターフェース | ID |
|---|---|
| `IScaledUIAmount` | `0xa60bf13d` |
| `IScaledUIAmountNewUIMultiplier` | `0xTBD` |
| `IScaledUIAmountConversion` | `0xTBD` |
| `IScaledUIAmountBalances` | `0xd890fd71` |

`0xTBD` は未確定の値として示されています。
実装や連携側は、提案の更新でIDが確定したかを確認する必要があります。

### 実装要件

準拠コントラクトが満たす要件は以下です。

| 要件 | 内容 |
|---|---|
| ERC165対応 | `supportsInterface` で対応インターフェースを検出できる |
| 倍率の精度 | `uiMultiplier` は18桁精度で表す |
| ERC20互換 | `balanceOf`、`transfer`、`transferFrom` は実数量で動き続ける |
| イベント発行 | 表示倍率が変わる時に `UIMultiplierUpdated` イベントを発行する |

倍率の更新方法は、提案が固定しません。
倍率を変更する主体は、発行体の管理者、ロールベースの権限、ガバナンス、オラクル連携から実装側で決めます。
ただし、誰でも倍率を変えられる設計にすると、ユーザーの表示残高を自由に操作できてしまいます。

## 補足

#### 設計判断

**ERC8056**は、ERC20の中心関数を変更せず、表示専用の関数を分けて追加します。
`balanceOf` や `transfer` の意味を変えると、既存のウォレット、取引所、DeFiコントラクトが同じトークンを違う数量として扱う恐れがあります。
そのため、ERC20の実数量を正本に残し、表示用の倍率だけを別インターフェースとして公開します。

倍率は18桁精度で表現します。
これはEthereumでよく使われる小数表現に合わせ、`1e18` を `1.0` として扱うためです。
また、変換補助や表示用残高取得を任意拡張に分けることで、最小限の実装は `uiMultiplier` と予約中倍率の公開に集中できます。

#### テストケース

提案で想定されている確認観点は、初期倍率と株式分割の2つです。
初期状態では `uiMultiplier` が `1e18` であり、`balanceOf` と `balanceOfUI` が同じ数量を返すことを確認します。

株式分割の確認では、2対1の分割を表すために倍率を `2e18` に設定します。
この時、表示用残高は実残高の2倍になり、`toUIAmount` と `fromUIAmount` が対応する数量を相互に変換できる必要があります。
一方で、ERC20の実残高、送金数量、総供給量は倍率変更だけでは変わりません。

#### 表示と会計の境界

インテグレーターが最も注意すべき点は、表示用数量と実数量を混ぜないことです。
ウォレット、ポートフォリオ、価格表示では `uiAmount` を見せられます。
一方で、送金、残高会計、DeFiコントラクトとの連携では `raw amount` を使います。

![扱う値を分ける](../../images/erc8056/display-vs-accounting.png)

ユーザーが画面で `200` と見ていても、`transfer` に渡す実数量は `100` かもしれません。
そのため、送金画面では表示数量と実数量の対応を明確にし、内部処理ではERC20の実数量を一貫して使う必要があります。

#### ウォレットの扱い

ウォレットは、まず `supportsInterface` で `IScaledUIAmount` 対応を確認します。
対応していないトークンは通常のERC20として表示します。

対応しているトークンでは、`balanceOf` と `uiMultiplier` から表示用残高を計算できます。
`IScaledUIAmountBalances` に対応している場合は、`balanceOfUI` を使って表示用残高を直接取得できます。

ウォレットの処理は以下です。

1. `supportsInterface(0xa60bf13d)` で対応を確認する
    未対応なら通常のERC20残高を表示します。
2. `balanceOf(account)` で実残高を取得する
    送金や内部処理では、この値を正本として扱います。
3. `uiMultiplier()` または `balanceOfUI(account)` で表示用残高を得る
    画面には表示用数量を出し、必要なら実数量も併記します。
4. `newUIMultiplier()` と `effectiveAt()` を確認する
    表示倍率の変更予定があれば、反映時刻をユーザーへ示します。

#### 取引所の扱い

取引所は、入出金や内部台帳を実数量で記録します。
表示用数量を台帳の数量として保存すると、倍率更新のたびに過去の入出金履歴と現在残高の整合が崩れます。

画面表示では、内部台帳の実数量に現在の `uiMultiplier` を掛けます。
ユーザーが表示用数量で注文や出金を入力する場合は、`fromUIAmount` と同じ計算で実数量へ戻してから処理します。

#### DeFiコントラクトの扱い

DeFiコントラクトは、ERC20の `balanceOf`、`transfer`、`transferFrom` をそのまま使います。
利息計算、担保率、LP残高、プール内残高は実数量で扱う必要があります。

表示倍率をDeFiコントラクト内の会計へ混ぜると、同じトークンでもインテグレーターごとに計算結果が変わります。
**ERC8056**は表示のための拡張であり、オンチェーン会計を倍率で書き換える仕組みではありません。

## 互換性

**ERC8056**はERC20と互換性があります。
既存のERC20関数は実数量で動き続け、表示倍率は追加関数として提供されます。

この互換性により、**ERC8056**未対応のウォレットやDeFiコントラクトでも、通常のERC20としてトークンを扱えます。
ただし、未対応の画面では分割後の表示数量が反映されません。
そのため、発行体や取引所は表示倍率の存在をユーザーに伝え、対応済みUIでは表示用数量と実数量の違いを明確にする必要があります。

## 参考実装

提案の参考実装では、ERC20、ERC165、所有者権限、4つのインターフェースを組み合わせた `ScaledUIToken` コントラクトが示されています。
中心となる処理は、現在時刻と `effectiveAt` を比較して、現在の倍率か予約中の倍率を返す部分です。

```js
uint256 private constant MULTIPLIER_DECIMALS = 1e18;
uint256 private _uiMultiplier = MULTIPLIER_DECIMALS;
uint256 private _newUIMultiplier = MULTIPLIER_DECIMALS;
uint256 private _effectiveAt = 0;

function uiMultiplier() public view returns (uint256) {
    if (block.timestamp >= _effectiveAt) {
        return _newUIMultiplier;
    }

    return _uiMultiplier;
}
```

この実装では、反映時刻に到達した後だけ予約中の倍率を返します。
`_uiMultiplier` と `_newUIMultiplier` を分けることで、インテグレーターは現在の倍率と反映予定の倍率を同時に確認できます。

表示用数量への変換は、倍率を掛けて18桁精度で割り戻します。

```js
function toUIAmount(uint256 rawAmount) public view returns (uint256) {
    return (rawAmount * uiMultiplier()) / MULTIPLIER_DECIMALS;
}

function fromUIAmount(uint256 uiAmount) public view returns (uint256) {
    return (uiAmount * MULTIPLIER_DECIMALS) / uiMultiplier();
}
```

`toUIAmount` は実数量から表示用数量を計算します。
`fromUIAmount` は表示用数量から実数量を計算します。
どちらもERC20の実残高を書き換えません。

倍率を更新する関数は、発行体やガバナンスの権限を持つアドレスに制限する必要があります。
参考実装では、将来時刻を指定してから `UIMultiplierUpdated` イベントを発行する流れになっています。

## セキュリティ

#### 倍率変更権限

表示倍率を不正に変更できると、ユーザーが自分の保有量を誤認します。
例えば、悪意ある管理者が倍率を `10.0` に変更すると、実残高は変わっていないのに画面上の数量だけが10倍に見えます。

実装では、倍率変更関数を所有者、ロール、ガバナンスのような信頼できる主体に限定する必要があります。
また、反映時刻を未来に置き、ウォレットや取引所が事前に通知できる余地を持たせる設計が重要です。

#### オーバーフロー

表示用数量は `rawAmount * uiMultiplier` で計算します。
`rawAmount` や倍率が大きい場合、乗算でオーバーフローするリスクがあります。

Solidity 0.8以降のオーバーフロー検出を使うか、乗算と除算の順序を安全に扱うライブラリを使う必要があります。
特に取引所やインデクサーがオフチェーンで計算する場合も、任意精度整数を使い、JavaScriptの通常の `number` で処理しないことが重要です。

#### ユーザーの誤認

表示用数量と実数量が異なるため、ユーザーは「ウォレットに見える数量」と「ERC20送金で動く数量」を混同しやすくなります。
ウォレットや取引所は、表示用数量を出す時に、必要に応じて実数量や倍率も確認できるようにします。

特に入出金画面、約定履歴、税務・会計向けCSVでは、どの数量が実数量で、どの数量が表示用数量なのかを明確に分ける必要があります。

#### オラクル依存

倍率更新を自動化する実装では、外部データやオラクルに依存します。
オラクルが停止したり、誤った倍率を返したりすると、ユーザーに表示される数量が誤ります。

自動更新を採用する場合でも、倍率の上限、下限、反映待ち時間、管理者による停止手段を用意することで、誤更新の影響を抑えられます。

## 最後に

今回は「**ERC20トークンの実残高を変えずに表示数量だけを調整する表示数量スケーリング拡張を定義するERC8056**」についてまとめてきました。

**ERC8056**は、現実資産トークンで必要になりやすい株式分割のような表示変更を、ERC20の実残高を変えずに扱うための提案です。
表示用倍率を標準化することで、ウォレット、取引所、DeFiコントラクトが「表示」と「会計」を分けて扱いやすくなります。
一方で、倍率変更権限やユーザーへの表示方法を誤ると、保有量の誤認につながるため、インテグレーター側の実装ルールも重要です。

他でも色々記事を書いているのでぜひよろしければ読んでいってください！

https://amzn.asia/d/gxvJ0Pw

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://twitter.com/cardene777

https://cardene.substack.com/
