【XRPL運用ベストプラクティス】トランザクション送信における責務分離の実装指針
はじめに
こんにちは!一般社団法人XRPL Japan 理事をしている増田(通称:ぽこ太郎)です。
XRPL を使った DApp の本番運用においては、ウォレットとトランザクション送信処理の 責務を整理 することが重要です。
この記事では、その設計ポイントをわかりやすく整理してお伝えいたします!
ウォレットSDKの責務は分けるべきか?
トランザクション送信において、ウォレットの SDK にどこまでの責務を持たせるべきかは、本番環境での運用設計において重要な論点です。
ここでは、 送信処理をウォレットSDKに集約した場合の影響 を整理しつつ、代表的なウォレットである GemWallet を例に考察していきます。
トランザクション送信における責務の整理
ウォレットSDKに送信処理を一任すると、UI層にロジックが集中し、 保守や拡張、SDKのバージョン対応 などが難しくなりがちです。
| No | 責務 | 役割 |
|---|---|---|
| 1 | トランザクションの作成 | トランザクションに応じたパラメータ生成 |
| 2 | 署名処理 | トランザクションへの署名 |
| 3 | トランザクションの送信 | ネットワークへブロードキャスト |
| 4 | 結果取得と処理 | 成否判定やエラー時のリカバリ |
責務の分け方と設計の考え方
ウォレット SDK の責務を適切に分離するには、トランザクション送信のライフサイクルを次の 3 つのレイヤーに分けて考えると整理しやすくなります。

1. トランザクションの作成レイヤ
このレイヤーでは、型定義やバリデーションの方針が重要です。
長期運用を見据え、拡張性や柔軟性とのバランスを意識しましょう。
実装方法
| 区分 | 方法 | メリット | 注意点 |
|---|---|---|---|
| 松 | 独自実装した型定義を使用 | 柔軟性と型安全性に優れ、長期運用に強い | 初期コストは高め |
| 竹 |
xrpl.js を使用 |
最新の仕様に追随しやすく、広く使われている | 更新や破壊的変更に注意が必要 |
| 梅 | ウォレットSDK を使用 | 実装が簡易で取り組みやすい | 柔軟性と拡張性は限定される |
💡 個人的には、保守性と拡張性のバランスが良いため 竹(xrpl.js の型定義を直接使用) を選択します。
サンプルコード
import { signTransaction, getAddress } from '@gemwallet/api'
import type { SubmittableTransaction } from 'xrpl' // ここで xrpl.js を使用
async function sendTrustSetTransaction() {
const res = await getAddress()
// xrpl.jsの型定義を使用
const transaction: SubmittableTransaction = {
TransactionType: 'TrustSet',
Account: res.result.address,
LimitAmount: {
currency: 'ABC',
issuer: 'rLQ23456789012345678901234567890123456789',
value: '100'
},
}
}
2. 署名レイヤ
このレイヤーの主な役割は、ユーザーの秘密鍵を使って安全に署名を行うことです。
SDKを使いつつ、 マルチウォレット対応を見据えた構成 がポイントです。
実装方法
| 区分 | 方法 | メリット | 注意点 |
|---|---|---|---|
| - | ウォレットSDK を使用 | 実装は非常に簡易 | マルチウォレット対応で複雑化しやすい |
💡 マルチウォレットに対応する場合は、ウォレット種別ごとにファクトリーを用いて適切な SDK 実装を生成する構成を取ると、実装の切り替えや拡張がスムーズになります。
サンプルコード
import { signTransaction, getAddress } from '@gemwallet/api' // ここで ウォレットSDK を使用
import type { SubmittableTransaction } from 'xrpl'
async function sendTrustSetTransaction() {
const res = await getAddress()
const transaction: SubmittableTransaction = {
TransactionType: 'TrustSet',
Account: res.result.address,
LimitAmount: {
currency: 'ABC',
issuer: 'rLQ23456789012345678901234567890123456789',
value: '100'
},
}
// ウォレットSDKを使用して署名
const response = await signTransaction({ transaction })
const txBlob = response?.result?.signature
console.log('txBlob: ', txBlob)
}
3. 送信・監視レイヤ
ここでは、署名済みトランザクションの送信と結果の取得・監視を行います。
実装方法
| 区分 | 方法 | メリット | 注意点 |
|---|---|---|---|
| 松 | Backend API 経由で送信 | セキュリティ・可観測性が高い | サーバーインフラの構築と保守、実装コストが大きい |
| 竹 | フロントエンドから WSS 経由で直接送信 | レイテンシが低く、UXに優れている | 接続管理やセキュリティ要件を別途設計する必要がある |
| 梅 | ウォレットSDK を使用 | 実装が簡易で取り組みやすい | 柔軟性と拡張性は限定される |
💡 私は「松」派です。
バックエンドAPI経由の送信がセキュリティと可観測性が高く、TypeScriptベースのモノリシック構成にすることで保守性も高くなります。
サンプルコード
// バックエンド(NextJS)で、トランザクションを送信
const response = await fetch(XRPL_API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
method: 'submit',
params: [
{
tx_blob: txBlob
}
]
})
})
const json = await response.json()
const result = json.result
console.log('result: ', result)
まとめ
ウォレット SDK に送信処理をすべて任せる設計は、一見シンプルですが、保守や拡張の面で課題が多くなりがちです。
そのため、本記事では処理を「リクエスト作成」「署名」「送信・監視」の3つのレイヤーに分け、それぞれの責務に応じた最適な実装方法を整理しました。
- 責務を明確に分離することで、保守性・拡張性・セキュリティ・可観測性を高められる
- 各レイヤーの実装方針は、開発体制やプロダクトのフェーズに応じて柔軟に選択するのが重要
最後に...
今後、Web3やブロックチェーンがチェーンの壁を越えてつながっていく世界の中で、XRPLがどんな可能性を広げていくのか、ぜひ一緒にワクワクしながら追いかけていきましょう!
最後まで読んでいただき、ありがとうございました!
Discussion