はじめに

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

今回は、ブロックチェーン上のデータをデータベースのように整理・管理できる仕組みを定義し、効率的で拡張性の高いオンチェーンデータ管理を実現する仕組みを提案しているERC7813についてまとめていきます!

以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

概要

ERC7813は、ブロックチェーン上のデータを柔軟に保存・管理するための「オンチェーンストレージパターン」を提案しています。 データを「テーブル」と呼ばれる構造体で整理し、各テーブルは「キー(key)」と「値(value)」のスキーマ(構造の定義)を持つ「レコード」の集合として構成されます。 これは、一般的なデータベースにおけるテーブルの仕組みと似ています。

この仕組みでは、以下のような要素が定義されています。

  • 統一されたコントラクトインターフェース データへのアクセスを行うために、統一的なスマートコントラクトのインターフェースが提供されます。 これにより、異なる開発者やサービスが同じ形式でデータを操作できるようになります。

  • 効率的なバイナリエンコーディング形式 静的データ型(サイズが固定のデータ)と動的データ型(サイズが可変のデータ)の両方を対象に、コンパクトな形式でデータをエンコード(符号化)します。 これにより、保存やイベント発火にかかるガスコスト(取引手数料)を抑えることができます。

  • 標準化されたイベントによる状態管理 スマートコントラクトの状態が変更されると、標準化された形式のイベントが発行されます。 このイベントを通じて、オフチェーン(ブロックチェーンの外)で動作するインデクサー(データの追跡・整理を行う仕組み)が自動的に状態を同期し、スキーマに基づいたデータの複製を行うことができます。

  • 動的なテーブル登録機能 新しいテーブルを、実行時に動的に登録することが可能です。 これは「スキーマメタデータ」を保存する特別なテーブルを通じて行われ、既存のコントラクトや統合システムを壊すことなく、システム全体を拡張できます。

この仕組みにより、ブロックチェーン上のデータ構造をより柔軟に管理し、開発者が安全かつ効率的にデータの拡張や更新を行うことができるようになります。

動機

これまでのスマートコントラクト開発では、オンチェーンのデータ管理に統一された基準が存在しませんでした。 その結果、以下のような課題が発生していました。

  • データの構造が固定的で、柔軟な拡張や変更が難しい
  • コントラクトのロジックとオフチェーンサービスが密結合しており、更新時に互換性が崩れる
  • データ構造を変更すると、既存のシステムや連携部分に不具合が起きやすい

ERC7813が導入するストレージパターンは、こうした問題を解決するために設計されています。

メリット

項目 説明
自動インデックス化 ステート変更時に発行される標準イベントを通じて、オフチェーンのサービスが自動的にデータを追跡・同期できます。これにより、スキーマ情報に基づいたAPIが容易に構築できます。
カスタム読み取り関数の削除 特定のデータ取得関数をコントラクトごとに実装する必要がなくなり、統一されたインターフェースを通してデータを参照できます。これにより、コントラクトと外部サービス間の結合度を下げ、開発効率が向上します。
簡単なアップグレード 「非構造化ストレージ(unstructured storage)」を採用することで、ストレージレイアウトを固定せずにロジックのみを更新可能です。従来の固定構造に伴うリスクを避けられます。
柔軟なデータ拡張 新しいテーブルを実行時に追加登録できるため、既存のシステムやデータ利用者との互換性を保ちながら機能を拡張できます。
ガスコストの削減 効率的なデータパッキング(データの圧縮)により、ストレージ操作やイベント発火時のコストを軽減できます。

仕様

定義

Store

javascript
// Store: A smart contract that implements the interface proposed by this ERC
// and organizes data in Tables. It emits events for each data operation.

この「Store」は、ERCで定義されたインターフェースを実装するスマートコントラクトです。 データを「テーブル」単位で整理し、各データ操作ごとにイベントを発行します。 Storeはデータの読み書き・削除といった操作を一元的に扱う中心的な存在です。 オンチェーン上に存在し、操作に応じてイベントを発行することで、オフチェーンのシステム(インデクサーなど)がそのステートを追跡・同期できるようになります。

Table

javascript
// Table: A storage structure that holds Records sharing the same Schema.

「Table」は、同一のスキーマ(データ構造)を持つ複数のレコードを格納するためのストレージ構造です。 テーブルには「オンチェーンテーブル」と「オフチェーンテーブル」の2種類があります。 オンチェーンテーブルはブロックチェーン上にステートを保持し、イベントも発行します。 オフチェーンテーブルはデータ自体はオンチェーンに保存せず、イベントのみを発行します。

種類 説明
オンチェーンテーブル 状態をブロックチェーン上に保存し、インデクサーのためにイベントを発行します。
オフチェーンテーブル 状態をオンチェーンに保持せず、イベントのみを発行します。

Record

javascript
// Record: A piece of data stored in a Table, addressed by one or more keys.

「Record」は、テーブルに格納される1つのデータ単位です。 レコードは1つ以上のキーによって一意に特定されます。 各レコードは、キー(識別子)と値(データ本体)から構成されます。

ResourceId

javascript
type ResourceId is bytes32;

「ResourceId」は各テーブルを一意に識別するための32バイト値です。 この値は最初の2バイトがテーブルの種類を示し、残りの30バイトがユニークな識別子として使われます。 「0x7462("tb")」はオンチェーンテーブル、「0x6f74("ot")」はオフチェーンテーブルを意味します。

バイト範囲 内容
0-1 テーブルの種類を示す識別子
2-31 テーブル固有のユニークID

Schema

javascript
type Schema is bytes32;

「Schema」はテーブル内のレコード構造(キーと値の型)を定義するデータ型です。 テーブルには「キーのスキーマ」と「値のスキーマ」の2種類があります。 キーのスキーマは固定長のデータ型のみで構成され、値のスキーマは固定長・可変長の両方を含むことができます。

バイト範囲 内容 制約
0-1 静的フィールドの総バイト長 -
2 静的フィールド数 (28 - 動的フィールド数) 以下
3 動的フィールド数 キースキーマは0、値スキーマは最大5
4-31 各フィールドの型を1バイトで指定 動的型は静的型の後に並ぶ必要あり

SchemaType

javascript
enum SchemaType {
  // uint系: 0x00〜0x1F
  // int系: 0x20〜0x3F
  // bytes系: 0x40〜0x5F
  // bool: 0x60
  // address: 0x61
  // 可変長配列やbytes/string型も含む
}

「SchemaType」は、スキーマで定義される各フィールドの型を1バイトで表現する値です。 データ型を小さな整数値にマッピングすることで、効率的なバイナリ表現を可能にします。

値の範囲 データ型
0x00〜0x1F uint8〜uint256
0x20〜0x3F int8〜int256
0x40〜0x5F bytes1〜bytes32
0x60 bool
0x61 address
0x62〜0x81 uint8[]〜uint256[]
0xC4 bytes
0xC5 string

FieldLayout

javascript
type FieldLayout is bytes32;

「FieldLayout」は、各静的フィールドのバイト長など、スキーマの具体的な構成情報をエンコードします。 オンチェーン上での処理を最適化するために、各フィールドの長さをあらかじめ計算し保持します。

バイト範囲 内容 制約
0-1 静的フィールド全体の長さ -
2 静的フィールド数 (28 - 動的フィールド数) 以下
3 動的フィールド数 キースキーマは0、値スキーマは最大5
4-31 各静的フィールドの長さ -

EncodedLengths

javascript
type EncodedLengths is bytes32;

「EncodedLengths」は、各動的フィールドの長さをまとめて符号化した値です。 動的データのデコードに必要な情報を保持します。 これにより、各動的フィールドの長さを個別に保存する必要がなくなり、効率が向上します。

バイト範囲 型 内容
0x00〜0x06 uint56 動的データ全体の長さ
0x07〜0x0B uint40 1つ目の動的フィールドの長さ
0x0C〜0x10 uint40 2つ目の動的フィールドの長さ
... ... 最大5つまで対応

データエンコーディング

レコードデータは、Storeメソッドやイベントで返却・発行される時に以下のルールで符号化されます。

  • 静的フィールドはパディング(余分なゼロ埋め)を行わずに連結されます。
  • 動的フィールドは長さを含めず、すべてを1つのEncodedLengthsにまとめます。
  • 最大フィールド数は28個で、そのうち動的フィールドは最大5個です。

エンコーディング例

javascript
(uint256 id, address owner, string description, uint8[] scores)

擬似コード例は以下になります。

javascript
bytes memory staticData = abi.encodePacked(id, owner);
bytes memory packedScores = packElementsWithoutPadding(scores);
bytes memory dynamicData = abi.encodePacked(description, packedScores);

EncodedLengths encodedLengths = dynamicData.length;
encodedLengths |= (description.length << (56));
encodedLengths |= (encodedData.length << (56 + 8 * 5));

// Full record
(staticData, encodedLengths, dynamicData)

Store インターフェース

javascript
interface IStore {
  function getRecord(ResourceId tableId, bytes32[] calldata keyTuple)
    external view returns (bytes memory staticData, EncodedLengths encodedLengths, bytes memory dynamicData);

  function getField(ResourceId tableId, bytes32[] calldata keyTuple, uint8 fieldIndex)
    external view returns (bytes memory data);

  function getFieldLength(ResourceId tableId, bytes32[] memory keyTuple, uint8 fieldIndex)
    external view returns (uint256);
}

getRecord

指定されたテーブルIDとキーに対応するレコード全体を取得する関数。 静的データ・動的データ・動的データの長さ情報をすべて返します。

引数

  • tableId
    • 対象テーブルのResourceId。
  • keyTuple
    • レコードを特定するキー配列。

戻り値

  • staticData
    • 静的データのバイナリ。
  • encodedLengths
    • 動的データの長さ情報。
  • dynamicData
    • 動的データ本体。

getField

指定されたフィールドのデータを単独で取得する関数。 個別のフィールドデータをエンコード済みで返します。

引数

  • tableId
    • テーブルのResourceId。
  • keyTuple
    • 対象レコードのキー。
  • fieldIndex
    • 取得したいフィールドの番号。

戻り値

  • data
    • エンコード済みフィールドデータ。

getFieldLength

指定されたフィールドのバイト長を取得する関数。 動的・静的フィールドに関わらず、対象データの長さを返します。

引数

  • tableId
    • テーブルの識別子。
  • keyTuple
    • 対象レコードのキー。
  • fieldIndex
    • 対象フィールドの番号。

戻り値

  • uint256
    • バイト長。

ストア操作とイベント

Store_SetRecord

javascript
event Store_SetRecord(
  ResourceId indexed tableId,
  bytes32[] keyTuple,
  bytes staticData,
  EncodedLengths encodedLengths,
  bytes dynamicData
);

レコード全体が設定(上書き)されたときに発行されるイベント。 レコードが新規または既存に関係なく、完全に書き換えられた際に発火します。

パラメータ

  • tableId
    • 対象テーブルのID。
  • keyTuple
    • レコードのキー配列。
  • staticData
    • 静的データの符号化結果。
  • encodedLengths
    • 動的データの長さ情報。
  • dynamicData
    • 動的データの符号化結果。

Store_SpliceStaticData

javascript
event Store_SpliceStaticData(
  ResourceId indexed tableId,
  bytes32[] keyTuple,
  uint48 start,
  bytes data
);

静的データの一部が上書きされたときに発行されるイベント。 静的データの特定位置を上書きする操作が行われた場合に発行されます。

パラメータ

  • tableId
    • 対象テーブルのID。
  • keyTuple
    • レコードキー。
  • start
    • 上書きを開始するバイト位置。
  • data
    • 上書き内容。

Store_SpliceDynamicData

javascript
event Store_SpliceDynamicData(
  ResourceId indexed tableId,
  bytes32[] keyTuple,
  uint8 dynamicFieldIndex,
  uint48 start,
  uint40 deleteCount,
  EncodedLengths encodedLengths,
  bytes data
);

動的データの一部が変更されたときに発行されるイベント。 動的データの追加・削除・置換が行われたときに発行されます。

パラメータ

  • tableId
    • 対象テーブルのID。
  • keyTuple
    • レコードのキー。
  • dynamicFieldIndex
    • 動的フィールドの番号。
  • start
    • 操作の開始位置。
  • deleteCount
    • 削除するバイト数。
  • encodedLengths
    • 新しい動的フィールドの長さ情報。
  • data
    • 挿入するデータ。

Store_DeleteRecord

javascript
event Store_DeleteRecord(ResourceId indexed tableId, bytes32[] keyTuple);

レコードが削除されたときに発行されるイベント。 指定されたキーのレコードがテーブルから削除された時に発行されます。

パラメータ

  • tableId
    • テーブルのID。
  • keyTuple
    • 削除対象のキー。

特殊テーブル:Tables テーブル

このテーブルは、すべてのテーブルの情報を管理する特別なオンチェーンテーブルです。 新しいテーブルを動的に登録するために使用されます。

キー 説明
tableId 対象テーブルのResourceId。
値 説明
fieldLayout 各静的データ型のバイト長を符号化したFieldLayout。
keySchema キーのデータ型を示すスキーマ。
valueSchema 値のデータ型を示すスキーマ。
abiEncodedKeyNames キー名をABI形式でエンコードしたバイト列。
abiEncodedFieldNames フィールド名をABI形式でエンコードしたバイト列。

このテーブルに保存されたレコードは不変です。 登録時に一度だけStore_SetRecordイベントが発行され、その後は変更されません。

登録時に使用されるtableIdの例は以下です。

javascript
ResourceId tableId = ResourceId.wrap(
  0x746273746f72650000000000000000005461626c657300000000000000000000
);

この仕組みにより、オフチェーンのインデクサーは全テーブルの構造を解釈でき、 構造化されたデータベースのようにブロックチェーンデータを扱うことが可能になります。

補足

Spliceイベント

Store には、レコード全体を上書きするStore_SetRecordイベントがありますが、これだけでは効率面に課題があります。 このため、ERC7813では「部分的な更新」を可能にする2種類のスプライスイベント — Store_SpliceStaticDataとStore_SpliceDynamicData — を導入しています。

部分更新が必要な理由

もし一部のデータしか変更していないにもかかわらずStore_SetRecordイベントを発行すると、全レコードを読み出して再エンコードし、再度発行する必要があります。 これはガスコスト(手数料)と処理量の両面で非効率です。 特に大きな可変長データ(例:長文の文字列や配列)を持つレコードでは、このコストが急増します。

スプライスイベントを使えば、変更箇所だけを最小限のデータで更新できます。 これにより、以下のような効果が得られます。

項目 効果
更新コストの削減 レコード全体を再構築せず、変更箇所だけを処理するためガスコストが低下します。
スケーラビリティの向上 大きなデータ構造を扱う場合でもコストが増えにくく、スケールしやすい設計になります。
オフチェーン同期の効率化 インデクサーが差分だけを処理すればよいため、状態同期が高速になります。

つまり、スプライスイベントは、オンチェーンストレージの「効率的な差分更新」を可能にするための仕組みです。

動的型配列の禁止

ERCでは、string[]やbytes[]のような「動的型の配列」をスキーマで定義することを明示的に禁止しています。 これは意図的な制約であり、システムの単純化と効率性を維持するために重要です。

制約の意図

動的型の配列は「ネストされた可変長データ構造」を生むため、ストレージの実装やエンコーディング処理が複雑化します。 これにより、ストレージ操作のガスコストが増加し、データ復元(デコード)も重くなります。

この問題を避けるため、仕様では「フラットなデータ構造」を推奨しています。 つまり、すべてのレコードは一次元的なデータ構造で表現し、複雑な配列構造は別のテーブルを用いて管理します。

代替モデルの例

もしstring[]のような可変配列を保存したい場合は、以下のようなテーブル構成を使用します。

カラム名 型 説明
index uint256 配列要素のインデックス
data bytes 実際の要素データ

このように「1要素=1レコード」として保存すれば、配列の操作をテーブル操作として安全に実現できます。 これにより、設計が単純化され、ガス効率も大幅に向上します。

FieldLayoutによる最適化(FieldLayout Optimization)

FieldLayoutは、テーブル内の各静的フィールド(固定長データ)のバイト長を事前に計算して保持する構造です。 これをスキーマ(Tablesテーブル)内に含めることで、実行時の効率を大幅に向上させることができます。

最適化の仕組み

通常、スマートコントラクトは各フィールドの長さやオフセットを動的に計算します。 しかし、この処理は計算量が多く、特に複雑なスキーマではガス消費が増えます。

FieldLayoutを導入すると、各フィールドのバイト長をあらかじめ記録しておけるため、実行時に再計算する必要がありません。 これにより、ストレージ操作のコストが減り、データの読み書きが効率化されます。

主な利点

項目 説明
実行コストの削減 長さやオフセットを都度計算する必要がなくなるため、ガス使用量を削減できます。
読み取り処理の高速化 オフチェーン側もTablesテーブルからFieldLayout情報を取得し、データを素早くデコードできます。
実装の一貫性 すべてのテーブルで統一的な方法でスキーマ情報を扱えるようになります。

つまり、FieldLayoutは「スキーマ情報のキャッシュ」として働き、オンチェーン・オフチェーン両方で処理の最適化を実現する仕組みです。

特別なTablesテーブル

最後に、ERC7813では「Tablesテーブル」という特別なオンチェーンテーブルが定義されています。 これはすべてのテーブルのスキーマ情報を登録・管理するメタデータテーブルとして機能します。

意義と役割

このテーブルを設けることで、オフチェーンのインデクサーがテーブル構造(スキーマ)を自動的に把握できるようになります。 スキーマを知らない状態で単なるバイナリデータを処理するよりも、構造を理解した形でデータを扱えるようになるため、 SQLのようなクエリ機能や高度なデータ分析が可能になります。

主なメリット

項目 説明
スキーマ認識型インデクサーの実現 各テーブルのスキーマ情報を基に、高度なデータクエリをサポートするAPIを構築できます。
実装の簡素化 既存の Store イベントを再利用してテーブル登録を行うため、新しいイベントを追加する必要がありません。
一貫性と拡張性 すべてのテーブルが同一の Store インターフェース上で管理されるため、システム全体の整合性が保たれます。

イメージ図

text
      Tables テーブル                              
  ┌─────────────────────┐
  │ tableId             │→ 各テーブルの識別子
  │ fieldLayout         │→ 静的フィールドの長さ情報
  │ keySchema           │→ キーの型情報
  │ valueSchema         │→ 値の型情報
  │ abiEncodedKeyNames  │→ キー名(文字列配列)
  │ abiEncodedFieldNames│→ フィールド名(文字列配列)
  └─────────────────────┘
             ↓ 登録イベント(Store_SetRecord)
   他のすべてのテーブルがここで登録される

このように、Tablesテーブルはブロックチェーン上での「スキーマ管理の中枢」として機能し、オフチェーンサービスがデータ構造を正確に理解できるようにします。

参考実装

Storeイベントインデックス

以下の例は、単純なインメモリインデクサーが Store イベントを利用して Store 状態をオフチェーンで複製する方法を示しています。 このインデクサーは生のエンコード済みデータに対して動作します。 この例ではTypeScriptを使用していますが、他の言語でも容易に再現できます。

ts
type Hex = `0x${string}`;

type Record = {
  staticData: Hex;
  encodedLengths: Hex;
  dynamicData: Hex;
};

const store = new Map<string, Record>();

// Create a key string from a table ID and key tuple to use in our store Map above
function storeKey(tableId: Hex, keyTuple: Hex[]): string {
  return `${tableId}:${keyTuple.join(",")}`;
}

// Like `Array.splice`, but for strings of bytes
function bytesSplice(
  data: Hex,
  start: number,
  deleteCount = 0,
  newData: Hex = "0x"
): Hex {
  const dataNibbles = data.replace(/^0x/, "").split("");
  const newDataNibbles = newData.replace(/^0x/, "").split("");
  return `0x${dataNibbles
    .splice(start, deleteCount * 2)
    .concat(newDataNibbles)
    .join("")}`;
}

function bytesLength(data: Hex): number {
  return data.replace(/^0x/, "").length / 2;
}

function processStoreEvent(log: StoreEvent) {
  if (log.eventName === "Store_SetRecord") {
    const key = storeKey(log.args.tableId, log.args.keyTuple);

    // Overwrite all of the Record's fields
    store.set(key, {
      staticData: log.args.staticData,
      encodedLengths: log.args.encodedLengths,
      dynamicData: log.args.dynamicData,
    });
  } else if (log.eventName === "Store_SpliceStaticData") {
    const key = storeKey(log.args.tableId, log.args.keyTuple);
    const record = store.get(key) ?? {
      staticData: "0x",
      encodedLengths: "0x",
      dynamicData: "0x",
    };

    // Splice the static field data of the Record
    store.set(key, {
      staticData: bytesSplice(
        record.staticData,
        log.args.start,
        bytesLength(log.args.data),
        log.args.data
      ),
      encodedLengths: record.encodedLengths,
      dynamicData: record.dynamicData,
    });
  } else if (log.eventName === "Store_SpliceDynamicData") {
    const key = storeKey(log.args.tableId, log.args.keyTuple);
    const record = store.get(key) ?? {
      staticData: "0x",
      encodedLengths: "0x",
      dynamicData: "0x",
    };

    // Splice the dynamic field data of the Record
    store.set(key, {
      staticData: record.staticData,
      encodedLengths: log.args.encodedLengths,
      dynamicData: bytesSplice(
        record.dynamicData,
        log.args.start,
        log.args.deleteCount,
        log.args.data
      ),
    });
  } else if (log.eventName === "Store_DeleteRecord") {
    const key = storeKey(log.args.tableId, log.args.keyTuple);

    // Delete the whole Record
    store.delete(key);
  }
}

セキュリティ

アクセス制御

ERC7813が定義しているのは、Store からデータを読み取るための関数のみです。 具体的には次の3つです。

関数名 役割
getRecord 指定されたテーブルIDとキーに基づき、レコード全体を取得する。
getField 指定されたフィールドのみを取得する。
getFieldLength 指定されたフィールドの長さを取得する。

これらは読み取り専用の関数です。 一方で、「データを書き込む」、「変更する」、「削除する」といった操作に関する関数は、この標準では定義されていません。 そのため、Store の実装を行う開発者は、それぞれのユースケースに合わせて適切なアクセス制御(Access Control)を設ける必要があります。

アクセス制御が必要な理由

ブロックチェーン上では、すべてのコントラクトやアカウントが公開されており、関数が「公開状態(public/external)」であれば、誰でも呼び出すことができます。 そのため、もしデータ更新のメソッドを無制限に呼び出せるようにしてしまうと、悪意のある第三者がストレージの内容を自由に書き換えるリスクがあります。

実装上の対策例
対策 説明
ロールベースアクセス制御 OwnableやAccessControlを利用して、管理者(owner)または特定のロールを持つアドレスのみが書き込みできるようにする。
ホワイトリスト方式 許可された特定のコントラクトまたはアカウントのみが Store への書き込み操作を行えるよう制限する。
認可付き関数呼び出し トランザクション発行時に署名検証を行い、署名者が正当な権限を持つか確認する。

開発者は、自身のアプリケーションがどのようなデータを管理するのか、誰がどのようにそのデータを変更できるのかを明確に定義し、それに基づいたアクセス制御を設計する必要があります。

オンチェーンデータの公開性

もう1つ重要なセキュリティの特徴は、Store に保存されたデータは**完全に公開されているという点です。

通常のスマートコントラクトでは、storage変数は「内部状態」として扱われ、他のコントラクトから直接読み取ることはできません。 もし他のコントラクトに読み取らせたい場合は、開発者が明示的に「読み取り関数」を用意する必要があります。

しかし、ERC7813で定義された Store はそれとは異なります。 Store 内にあるデータは、以下の読み取り関数を通じて、あらゆるコントラクトからアクセス可能です。

関数名 概要
getRecord テーブルIDとキーを指定すると、レコード全体を取得できます。
getField 特定のフィールドを単独で取得できます。
getFieldLength フィールドのバイト長を取得できます。

この仕組みにより、オンチェーン上の他のスマートコントラクトも、Store に保存されたデータを直接読み取ることができます。 これは透明性と相互運用性を高める利点がありますが、一方で「秘匿すべき情報を保存してはいけない」という明確な制約も生まれます。

公開データであることの影響
  • 他コントラクトからの読み取り 他のスマートコントラクトがgetRecordなどを呼び出すことで、データを参照できます。 これにより、複数のアプリケーション間でデータを共有することが容易になります。
  • オフチェーンからのアクセス 誰でもブロックチェーンをスキャンすることで、Store 内のすべてのデータを復元できます。 そのため、個人情報や機密データを格納してはいけません。
  • 暗号化が必要なケース どうしても機密データを扱いたい場合は、あらかじめ暗号化した状態で保存し、復号はオフチェーンで行うように設計することが推奨されます。
注意点のまとめ
注意点 説明
すべてのデータは公開状態 Store内に保存されたデータは、オンチェーン・オフチェーン問わず誰でも閲覧可能です。
センシティブ情報の格納禁止 個人情報、秘密鍵、パスワード、内部ロジックに関するデータなどを直接保存してはいけません。
暗号化保存を検討 機密性を求める場合は、暗号化後のデータのみを保存する方式を取る必要があります。

引用

alvarius (@alvrs), dk1a (@dk1a), frolic (@frolic), ludens (@ludns), vdrg (@vdrg), yonada yonada@proton.me, "ERC-7813: Store, Table-Based Introspectable Storage [DRAFT]," Ethereum Improvement Proposals, no. 7813, November 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7813.

最後に

今回は「ブロックチェーン上のデータをデータベースのように整理・管理できる仕組みを定義し、効率的で拡張性の高いオンチェーンデータ管理を実現する仕組みを提案しているERC7813」についてまとめてきました! いかがだったでしょうか?

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!

他の媒体でも情報発信しているのでぜひ他も見ていってください!