債権・アセットファイナンス
条件の異なる権利を区分し、返済・分配と記録を対応づける。
← 実装パターン一覧設備向けの債権を条件別に発行し、返済と照合する
同じ設備事業への債権でも、満期や支払条件が異なるものを区別して管理する構成です。BondTokenのclassIdを契約条件の種類、nonceIdを発行回や満期の区分に対応づけ、投資家ごとの保有口数を記録します。
契約条件、発行回、投資家の保有口数、支払予定と入金実績を対応づけた管理画面を構成できます。
システム構成と役割
契約・投資家管理
契約原本、参加資格、支払予定を管理します。
Application API / DB債権の発行・保有
classIdとnonceIdの組ごとに発行数・保有数を管理します。
BondTokenHelper支払い・請求
銀行送金なら入金照合、対応トークンなら契約に応じた請求処理を接続します。
Bank integration / DebtManagerHelper運用・報告
基準日時点の保有数と支払い結果を保存し、報告に利用します。
Snapshots / Application ledger
データモデル例
業務DBに保存する情報と、チェーンやAPIで管理する情報を対応づけます。フィールド名とサンプル値は、この構成で使うアプリケーション側の設計例です。
| 項目・値の例 | 管理する場所 | 役割・対応関係 |
|---|---|---|
agreementId / termsVersionFIN-001 / v1 | 業務DB | 元本、期日、対象設備、契約の版を識別します。 |
classId20 | チェーン+DB対応表 | 権利の種類や契約条件の区分。この分類方法は事業側で定義します。 |
nonceId202709 | チェーン+DB対応表 | 同じclass内の発行回。署名の再利用を防ぐnonceとは別の識別子です。 |
investor / unitsinvestorWallet / 10 | チェーン | 口数は整数として管理。1口の金額・通貨は契約側で定めます。 |
paymentId / recordDatePAY-2027-09 / timestamp | 業務DB | 支払いと基準日を識別し、利用したsnapshotIdや対象ブロックを保存します。 |
bankReference / transactionHashbank-ref / 0x… | 業務DB | 銀行送金とチェーン上の取引は別々の証跡として記録します。 |
処理の流れ
契約条件をclass・nonceへ対応づける
契約FIN-001をclassId 20、発行回をnonceId 202709へ紐づけます。classとnonceを作成し、公開するメタデータだけをチェーンに設定します。
createClass(classId, data) → createNonce(classId, nonceId, data)参加資格と入金を確認して発行する
本人確認や契約、払込条件は業務側で確認します。必要なホワイトリストを設定したうえで、投資家へ10口を発行します。トークンの発行だけで銀行入金が確認されるわけではありません。
issue(investor, classId, nonceId, 10n)基準日時点の保有数を確定する
支払対象の口数は、支払時点の現在残高ではなく、契約で定めた基準日の記録に対応づけます。BondTokenのsnapshotは呼出時に作成するため、後から任意の日付の残高を作る用途ではありません。
snapshot() → balanceOfAt(holder, classId, nonceId, snapshotId)支払い経路を接続する
銀行送金の場合は業務台帳で支払額と入金を照合します。DebtManagerを使う場合は対応するBondToken、元本・利払い用トークン、権限、支払日程を設定し、資金を用意してpayCouponを実行します。
DebtManager: createTranche → addCouponSchedule → payCoupon → claimCoupon償還と記録を整合させる
元本返済の確認と口数の消込を対応づけます。BondTokenのredeemは口数をburnする操作です。資金払い戻しを連動させる場合は、DebtManagerなどの支払い処理を含めた構成を使用します。
BondToken.redeem / DebtManager.redeemAtMaturity
コードを利用する前の準備
- 初期化済みBondToken、作成済みclassId・nonceId、MINTER_ROLEを持つSignerが必要です。
- 口数はbigintで渡します。小数桁を使うERC-20の金額と、債権の口数を区別します。
- 参加資格・契約・払込を確認し、必要なホワイトリストへの登録を済ませてから呼び出します。
npm install --save-exact @hazbase/kit@0.9.0 ethers@6.16.0以下はアプリケーションから呼び出すTypeScriptの関数です。接続先・権限・対象データを引数として渡します。UI、DBへの保存、処理の再開は、上記の構成に沿って業務側へ組み込めます。
契約・発行回を指定して投資家へ割り当てる
classId: 20n、nonceId: 202709n、units: 10nを渡す例です。agreementIdは自社の契約IDで、戻り値を業務台帳に保存するために使用します。
import { BondTokenHelper } from '@hazbase/kit';
type Signer = Parameters<typeof BondTokenHelper.deploy>[1];
export async function allocateBond(input: {
agreementId: string;
tokenAddress: string;
classId: bigint;
nonceId: bigint;
investor: string;
units: bigint;
issuer: Signer;
}) {
// The class and nonce must already exist; issuer needs MINTER_ROLE.
if (input.units <= 0n) throw new Error('Units must be positive.');
const bond = BondTokenHelper.attach(input.tokenAddress, input.issuer);
const receipt = await bond.issue(
input.investor, input.classId, input.nonceId, input.units,
);
const balance = await bond.balanceOf(
input.investor, input.classId, input.nonceId,
);
return {
agreementId: input.agreementId,
classId: input.classId.toString(),
nonceId: input.nonceId.toString(),
investor: input.investor,
issuedUnits: input.units.toString(),
balance: balance.toString(),
transactionHash: receipt.hash,
};
}
実行結果と、アプリケーションへの反映
対象class・nonceの保有口数が増え、取引ハッシュを取得できます。この操作には金銭の移動は含まれません。払込確認と発行結果は同じ契約IDで照合します。
実装で押さえておきたい点
利払いと残高の基準日
DebtManagerのpayCouponは実行時のsnapshotを利用します。過去日の保有者へ支払う要件では、そのまま現在の保有者へ分配せず、基準日の記録に合う処理を設計します。
通貨・桁数・端数
元本の通貨、利払いトークンのdecimals、1口あたりの金額を区別します。端数の繰越や残額の扱いを業務台帳とコントラクトの計算方法に合わせます。
支払い前の権限と資金
DebtManagerにはBondToken側の必要な権限と、支払トークンの残高・allowanceが必要です。支払不能や期限超過を「支払済み」と表示せず、状態を分けて扱います。
用途に合わせた拡張
Splitterと保有者分配の違い
Splitterは設定した宛先と比率に送金する機能です。トークン保有者を自動で列挙して配当する機能ではありません。固定の事業者間配分と、基準日残高に基づく分配で構成を分けます。
Fund Opsとの組み合わせ
アセットファイナンスを中心としたRWAへの投資を扱うファンド事業者にはFund Opsがあります。独自の契約・投資家管理と組み合わせる場合も、対象データと責任範囲を対応づけられます。