Solana API

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

import {
  SolanaApp,
  DEFAULT_SOL_PATH,
} from '@openloop/sdk-core'

Initialization

const sol = new SolanaApp(transport)

Methods

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

getAddress

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
}>

Parameters

Name Type Description
path string BIP44 path (e.g. "44'/501'/0'/0'")

Default Path

DEFAULT_SOL_PATH = "44'/501'/0'/0'"

Note: Solana uses Ed25519, so every element of the path is hardened.

Example

const { address, publicKey } = await sol.getAddress(DEFAULT_SOL_PATH)
// address: "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"

signTransaction

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)

Parameters

Name Type Description
path string BIP44 path
txBytes Uint8Array Serialized transaction (raw binary)

Returns

A 64-byte Ed25519 signature as a hex string.

Example

const signatureHex = await sol.signTransaction(
  DEFAULT_SOL_PATH,
  transactionBytes
)

Note: Large transactions are automatically split and sent using the Ledger P2 chunking protocol.


signOffchainMessage

Sign an off-chain message, such as Sign-In With Solana.

async signOffchainMessage(
  path: string,
  messageBytes: Uint8Array
): Promise<string>  // 64-byte Ed25519 signature (hex)

Parameters

Name Type Description
path string BIP44 path
messageBytes Uint8Array Raw bytes of the message

Example

const message = new TextEncoder().encode('Hello, Solana!')
const signatureHex = await sol.signOffchainMessage(DEFAULT_SOL_PATH, message)

@solana/web3.js Integration

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)

Next Steps