The SolanaApp class provides Solana address retrieval,
transaction signing, and off-chain message signing. It uses an APDU
protocol compatible with the Ledger Solana app.
import {
SolanaApp,
DEFAULT_SOL_PATH,
} from '@openloop/sdk-core'const sol = new SolanaApp(transport)| Method | Description |
|---|---|
getAddress(path) |
Get the Ed25519 public key and the Base58 address |
signTransaction(path, txBytes) |
Sign a transaction |
signOffchainMessage(path, messageBytes) |
Sign an off-chain message |
Get the Solana address — the Base58 encoding of the Ed25519 public key — from the device.
async getAddress(path: string): Promise<{
publicKey: string // Ed25519 public key (hex, 32 bytes)
address: string // Base58 address
}>| Name | Type | Description |
|---|---|---|
path |
string |
BIP44 path (e.g. "44'/501'/0'/0'") |
DEFAULT_SOL_PATH = "44'/501'/0'/0'"
Note: Solana uses Ed25519, so every element of the path is hardened.
const { address, publicKey } = await sol.getAddress(DEFAULT_SOL_PATH)
// address: "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"Produce an Ed25519 signature over the binary data of a Solana transaction.
async signTransaction(
path: string,
txBytes: Uint8Array
): Promise<string> // 64-byte Ed25519 signature (hex)| Name | Type | Description |
|---|---|---|
path |
string |
BIP44 path |
txBytes |
Uint8Array |
Serialized transaction (raw binary) |
A 64-byte Ed25519 signature as a hex string.
const signatureHex = await sol.signTransaction(
DEFAULT_SOL_PATH,
transactionBytes
)Note: Large transactions are automatically split and sent using the Ledger P2 chunking protocol.
Sign an off-chain message, such as Sign-In With Solana.
async signOffchainMessage(
path: string,
messageBytes: Uint8Array
): Promise<string> // 64-byte Ed25519 signature (hex)| Name | Type | Description |
|---|---|---|
path |
string |
BIP44 path |
messageBytes |
Uint8Array |
Raw bytes of the message |
const message = new TextEncoder().encode('Hello, Solana!')
const signatureHex = await sol.signOffchainMessage(DEFAULT_SOL_PATH, message)import {
Connection,
PublicKey,
SystemProgram,
Transaction,
} from '@solana/web3.js'
import { SolanaApp, DEFAULT_SOL_PATH } from '@openloop/sdk-core'
import { WebHidTransport } from '@openloop/transport-webhid'
const transport = await WebHidTransport.connect()
const sol = new SolanaApp(transport)
// Get the address
const { address } = await sol.getAddress(DEFAULT_SOL_PATH)
const publicKey = new PublicKey(address)
// Build the transaction
const connection = new Connection('https://api.mainnet-beta.solana.com')
const recentBlockhash = (await connection.getLatestBlockhash()).blockhash
const tx = new Transaction()
tx.add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: new PublicKey('...'),
lamports: 1000000, // 0.001 SOL
})
)
tx.recentBlockhash = recentBlockhash
tx.feePayer = publicKey
// Sign on the device
const txBytes = tx.serializeMessage()
const signatureHex = await sol.signTransaction(DEFAULT_SOL_PATH, txBytes)
// Add the signature to the transaction
const signatureBytes = Uint8Array.from(
signatureHex.match(/.{2}/g)!.map(b => parseInt(b, 16))
)
tx.addSignature(publicKey, Buffer.from(signatureBytes))
// Broadcast
const txId = await connection.sendRawTransaction(tx.serialize())
console.log('TX:', txId)