XrpApp クラスは、XRP Ledger
のアドレス取得とトランザクション署名を提供します。Ed25519(デフォルト)と
secp256k1 の両方の楕円曲線をサポートしています。
XrpApp が提供するのは、この 2
つのメソッドだけです。トランザクションの組み立て(Payment や TrustSet
などの種類ごとのシリアライズ)は含まれません。組み立てには xrpl.js
などを使い、できあがった署名前のトランザクションをデバイスに渡します。
| 項目 | Ethereum | XRP |
|---|---|---|
| アドレス形式 | 0x... (hex) |
r... (Base58Check) |
| デフォルト曲線 | secp256k1 | Ed25519 |
| Ed25519 公開鍵 | — | 0xED プレフィックス付き |
| 署名フォーマット | v/r/s | raw bytes (Ed25519: 64B, secp256k1: DER) |
| BIP44 パス | 44'/60'/... |
44'/144'/...(Ed25519: 全ハードニング) |
import {
XrpApp,
DEFAULT_XRP_PATH,
type XrpSignatureResult,
type XrpCurve,
} from '@openloop/sdk-core'const xrp = new XrpApp(transport)どちらのメソッドも、実行中は transport.lock()
でデバイスを占有し、終わると(失敗したときも)transport.unlock()
で解放します。
| メソッド | 説明 |
|---|---|
getAddress(path, curve?) |
XRP アドレスと公開鍵を取得 |
signTransaction(path, txBlob, curve?) |
シリアライズ済みトランザクションに署名 |
デバイスから XRP Ledger アドレスを取得します。
async getAddress(
path: string,
curve?: XrpCurve
): Promise<{
publicKey: string // 公開鍵 (hex, Ed25519: 33B with 0xED prefix, secp256k1: 33B compressed)
address: string // XRP アドレス ("r...")
}>| 名前 | 型 | デフォルト | 説明 |
|---|---|---|---|
path |
string |
— | BIP44 パス |
curve |
XrpCurve |
'ed25519' |
'ed25519' or 'secp256k1' |
XrpCurve 型type XrpCurve = 'ed25519' | 'secp256k1'DEFAULT_XRP_PATH = "44'/144'/0'/0'/0'"(Ed25519:
全要素ハードニング)
// Ed25519(デフォルト)
const { address, publicKey } = await xrp.getAddress(DEFAULT_XRP_PATH)
// address: "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"
// publicKey: "ed01a2b3c4..." (0xED prefix)
// secp256k1
const { address: addr2, publicKey: pub2 } = await xrp.getAddress(
"44'/144'/0'/0/0", // secp256k1 は非ハードニングパスも可
'secp256k1'
)| 曲線 | P2 値 | 説明 |
|---|---|---|
'ed25519' |
0x80 |
Ed25519(デフォルト、推奨) |
'secp256k1' |
0x40 |
secp256k1(Ethereum 互換) |
APDU は CLA 0xE0 / INS 0x02 /
P1 0x00 で、データ部は BIP32 パスです。応答は
公開鍵の長さ(1B) + 公開鍵 + アドレスの長さ(1B) + アドレス(ASCII)
の形で、SDK がこれを分解して返します。
シリアライズ済みの XRP トランザクションに署名します。
async signTransaction(
path: string,
txBlob: string,
curve?: XrpCurve
): Promise<XrpSignatureResult>| 名前 | 型 | 説明 |
|---|---|---|
path |
string |
BIP44 パス |
txBlob |
string |
シリアライズ済みトランザクション (hex, 0x
あり/なし) |
curve |
XrpCurve |
曲線(省略時は 'ed25519')。Ledger 互換仕様に従い P2 に
curveMask(secp256k1=0x40 /
ed25519=0x80)として送出。getAddress
で取得した曲線と一致させる |
txBlob に渡すものencode() が返す形)。TxnSignature
は含めません。SigningPubKey を含めてください。
getAddress が返した publicKey を、そのまま
SigningPubKey に設定します。デバイスは
SigningPubKey の先頭バイト(0xED =
Ed25519、0x02 / 0x03 =
secp256k1)から曲線を判定します。STX\0 =
53545800)は付けません。
前置きはデバイスが付けます。Account は、path と curve
から導かれるこのデバイスのアドレスでなければなりません。別のアドレスの
blob
はデバイスが拒否し、ApduError(0x6985)で返ります。path
と curve は、getAddress
に渡したものと同じにしてください。blob は 255 バイトを超えても渡せます。SDK が自動で複数の APDU
に分割します(先頭は P1 0x80、途中は
P1 0x81、最後は P1 0x01。1 つに収まるときは
P1 0x00)。
XrpSignatureResult| フィールド | 型 | 説明 |
|---|---|---|
signature |
string |
署名の hex(Ed25519: 64 bytes, secp256k1: DER エンコード可変長) |
// Ed25519 (省略時のデフォルト)
const { signature } = await xrp.signTransaction(
DEFAULT_XRP_PATH,
'120000228...' // 署名前のトランザクション blob(SigningPubKey を含む)
)
// secp256k1(getAddress で 'secp256k1' を取った場合は同じ値を渡す)
const { signature: sig2 } = await xrp.signTransaction(
"44'/144'/0'/0/0",
'120000228...',
'secp256k1'
)署名の拒否や使用中は
ApduError(0x6985)、通信の失敗は
TransportError で届きます。詳細はエラー処理を参照してください。署名を伴う呼び出しなので、timeout
や disconnected
のあとで自動的に送り直さないでください。
XrpApp は WalletConnect では使えません(XRP は
WalletConnect 非対応です)。
デバイスが返した署名は、@openloop/sdk-core/verify の
verifyXrpTransaction で、デバイスの外から検証できます。
import { verifyXrpTransaction } from '@openloop/sdk-core/verify'
const { publicKey } = await xrp.getAddress(DEFAULT_XRP_PATH)
const result = await xrp.signTransaction(DEFAULT_XRP_PATH, txBlob)
// txBlob は signTransaction に渡したものと同じ(前置きなし)
const check = verifyXrpTransaction(txBlob, 'ed25519', result, publicKey)
console.log(check.ok) // true なら署名は publicKey のものverifyXrpTransaction(txBlobHex, curve, result, pubkeyHex)
は { recovered: string; expected?: string; ok: boolean }
を返します。署名済みのトランザクション全体から検証するときは
verifyXrpSignedTx(signedTxHex)
を使います(SigningPubKey と TxnSignature
を取り出すので、公開鍵の指定は要りません)。
import { Client, encode } from 'xrpl'
import { XrpApp, DEFAULT_XRP_PATH } from '@openloop/sdk-core'
import { WebHidTransport } from '@openloop/transport-webhid'
const transport = await WebHidTransport.connect()
const xrp = new XrpApp(transport)
// アドレス取得
const { address, publicKey } = await xrp.getAddress(DEFAULT_XRP_PATH)
// XRP Ledger に接続
const client = new Client('wss://xrplcluster.com/')
await client.connect()
// トランザクション作成
const prepared = await client.autofill({
TransactionType: 'Payment',
Account: address,
Amount: '1000000', // 1 XRP = 1,000,000 drops
Destination: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe',
})
// SigningPubKey を入れてシリアライズし、デバイスで署名
const unsigned = { ...prepared, SigningPubKey: publicKey.toUpperCase() }
const txBlob = encode(unsigned)
const { signature } = await xrp.signTransaction(DEFAULT_XRP_PATH, txBlob)
// 署名をトランザクションに追加
const signedTx = {
...unsigned,
TxnSignature: signature.toUpperCase(),
}
// ブロードキャスト
const result = await client.submitAndWait(encode(signedTx))
console.log('TX Result:', result.result.meta)
await client.disconnect()