The Para Swift SDK provides support for Solana blockchain operations through the ParaSolanaSigner
class. This guide demonstrates how to perform common operations such as signing messages, signing transactions, and sending transactions on Solana.
Some Solana operations may require additional setup or have limited support in the current bridge implementation. Check the specific method documentation for availability.
Getting Started
To work with Solana, you’ll need:
- An initialized
ParaManager
instance
- A wallet with Solana capabilities
- An RPC URL for Solana network (mainnet, devnet, or testnet)
Initializing the Solana Signer
First, create an instance of ParaSolanaSigner
by providing your Para manager, an RPC URL, and optionally a wallet ID:
import ParaSwift
import SolanaSwift
// Create the Solana signer
let paraSolanaSigner = try ParaSolanaSigner(
paraManager: paraManager,
rpcUrl: "https://api.devnet.solana.com", // or mainnet: https://api.mainnet-beta.solana.com
walletId: wallet.id // Optional, can be selected later
)
Selecting a Wallet
If you didn’t specify a wallet ID when creating the signer, or if you want to switch to a different wallet, use the selectWallet
method:
// Select a wallet for signing
try await paraSolanaSigner.selectWallet(walletId: wallet.id)
Getting Public Key
// Get the public key for the current wallet
let publicKey = try await paraSolanaSigner.getPublicKey()
print("Wallet address: \(publicKey.base58EncodedString)")
Getting Balance
do {
// Get balance in lamports (1 SOL = 1,000,000,000 lamports)
let balance = try await paraSolanaSigner.getBalance()
let solBalance = Double(balance) / 1_000_000_000
print("Balance: \(solBalance) SOL")
} catch ParaSolanaSignerError.bridgeError(let message) {
print("Balance fetching not yet supported: \(message)")
}
Signing Messages
Para makes it easy to sign arbitrary data with your Solana wallet:
// Sign arbitrary bytes
let message = "Hello, Solana!"
let messageData = message.data(using: .utf8)!
let signature = try await paraSolanaSigner.signBytes(messageData)
// Alternatively, you can use the ParaManager directly
let signature2 = try await paraManager.signMessage(
walletId: wallet.id,
message: message // SDK handles Base64 encoding internally
)
Working with Transactions
Creating a Solana Transaction
Para uses the SolanaTransaction
model to represent transactions:
// Create a simple transfer transaction
let recipientAddress = "11111111111111111111111111111112" // System program for demo
let amountInLamports: UInt64 = 1_000_000 // 0.001 SOL
let transaction = SolanaTransaction(
to: recipientAddress,
lamports: amountInLamports,
feePayer: nil, // Will use current wallet as fee payer
recentBlockhash: nil // Will be fetched automatically
)
Signing a Transaction
To sign a transaction without broadcasting it to the network:
do {
let signedTransactionB64 = try await paraSolanaSigner.signTransaction(transaction)
print("Signed transaction (Base64): \(signedTransactionB64)")
} catch ParaSolanaSignerError.bridgeError(let message) {
print("Transaction signing not yet supported: \(message)")
}
Sending a Transaction
To sign and broadcast a transaction in a single step:
do {
let transactionSignature = try await paraSolanaSigner.sendTransaction(transaction)
print("Transaction signature: \(transactionSignature)")
} catch ParaSolanaSignerError.bridgeError(let message) {
print("Transaction sending not yet supported: \(message)")
}
Error Handling
The ParaSolanaSigner
provides specific error types for better error handling:
do {
try await paraSolanaSigner.signTransaction(transaction)
} catch ParaSolanaSignerError.missingWalletId {
print("No wallet selected. Call selectWallet() first.")
} catch ParaSolanaSignerError.invalidWalletType {
print("Selected wallet is not a Solana wallet.")
} catch ParaSolanaSignerError.invalidWalletAddress {
print("Wallet address is invalid.")
} catch ParaSolanaSignerError.signingFailed(let underlyingError) {
print("Signing failed: \(underlyingError?.localizedDescription ?? "Unknown error")")
} catch ParaSolanaSignerError.networkError(let underlyingError) {
print("Network error: \(underlyingError?.localizedDescription ?? "Unknown error")")
} catch ParaSolanaSignerError.bridgeError(let message) {
print("Bridge operation failed: \(message)")
} catch {
print("Unexpected error: \(error)")
}
Working with Different Networks
Para supports Solana mainnet, devnet, and testnet. Simply update the RPC URL to target different networks:
// Solana Mainnet
let mainnetSigner = try ParaSolanaSigner(
paraManager: paraManager,
rpcUrl: "https://api.mainnet-beta.solana.com",
walletId: wallet.id
)
// Solana Devnet
let devnetSigner = try ParaSolanaSigner(
paraManager: paraManager,
rpcUrl: "https://api.devnet.solana.com",
walletId: wallet.id
)
// Solana Testnet
let testnetSigner = try ParaSolanaSigner(
paraManager: paraManager,
rpcUrl: "https://api.testnet.solana.com",
walletId: wallet.id
)
Complete Example
Here’s a complete example showing how to use the Solana signer:
import SwiftUI
import ParaSwift
import SolanaSwift
struct SolanaWalletView: View {
@EnvironmentObject var paraManager: ParaManager
@State private var balance: UInt64 = 0
@State private var publicKey: String = ""
@State private var isLoading = false
@State private var errorMessage: String?
var wallet: Wallet // Assume this is passed in
var body: some View {
VStack(spacing: 20) {
Text("Solana Wallet")
.font(.title)
if !publicKey.isEmpty {
Text("Address: \(publicKey)")
.font(.caption)
.foregroundColor(.secondary)
}
Text("Balance: \(Double(balance) / 1_000_000_000, specifier: "%.4f") SOL")
.font(.headline)
Button("Refresh Balance") {
refreshBalance()
}
.disabled(isLoading)
Button("Sign Message") {
signTestMessage()
}
.disabled(isLoading)
if let errorMessage = errorMessage {
Text(errorMessage)
.foregroundColor(.red)
.font(.caption)
}
}
.padding()
.onAppear {
refreshBalance()
}
}
private func refreshBalance() {
isLoading = true
errorMessage = nil
Task {
do {
let signer = try ParaSolanaSigner(
paraManager: paraManager,
rpcUrl: "https://api.devnet.solana.com",
walletId: wallet.id
)
let pubKey = try await signer.getPublicKey()
publicKey = pubKey.base58EncodedString
do {
balance = try await signer.getBalance()
} catch ParaSolanaSignerError.bridgeError {
// Balance fetching not yet supported
print("Balance fetching not yet supported in bridge")
}
} catch {
errorMessage = error.localizedDescription
}
isLoading = false
}
}
private func signTestMessage() {
isLoading = true
errorMessage = nil
Task {
do {
let signer = try ParaSolanaSigner(
paraManager: paraManager,
rpcUrl: "https://api.devnet.solana.com",
walletId: wallet.id
)
let message = "Hello from Para + Solana!"
let messageData = message.data(using: .utf8)!
let signature = try await signer.signBytes(messageData)
print("Message signed successfully. Signature length: \(signature.count) bytes")
} catch {
errorMessage = error.localizedDescription
}
isLoading = false
}
}
}
Example Integration
Notes and Limitations
- Some bridge operations (like
getBalance
, signTransaction
, sendTransaction
) may not be fully implemented yet
- The SDK will throw
ParaSolanaSignerError.bridgeError
for unsupported operations
- Message signing with
signBytes
is fully supported
- Always handle errors appropriately and provide fallbacks for unsupported operations