3. Core Concepts
Understanding the fundamental concepts of the Amadeus Protocol SDK.
Keypairs
Amadeus Protocol uses BLS12-381 cryptography for all cryptographic operations. Each account has a public/private keypair.
Generating Keypairs
import { generateKeypair } from '@amadeus-protocol/sdk'
// Generate a new keypair
const keypair = generateKeypair()
console.log('Public Key:', keypair.publicKey) // Base58 encoded
console.log('Private Key:', keypair.privateKey) // Base58 encoded seed (64 bytes)
Keypair Structure
- Public Key: 48-byte BLS12-381 public key, Base58-encoded
- Private Key: 64-byte seed, Base58-encoded (also called "Seed64")
Deriving Public Keys
import { derivePublicKeyFromSeedBase58 } from '@amadeus-protocol/sdk'
// Derive public key from existing seed
const publicKey = derivePublicKeyFromSeedBase58('5Kd3N...')
console.log('Public Key:', publicKey)
Addresses
In Amadeus Protocol, public keys serve as addresses. They are Base58-encoded 48-byte BLS12-381 public keys.
Address Format
// Address is the public key
const address = keypair.publicKey
// Example: "5Kd3NvsngHeMoo884xkJ6Cyb5StvnRN6f9tYiqwqJzLpQq"
Address Characteristics
- Length: Typically 87-88 characters (Base58 encoded)
- Format: Base58-encoded public key
- Uniqueness: Each keypair has a unique address
- Public: Safe to share publicly (it's your public key)
Address Validation
import { fromBase58 } from '@amadeus-protocol/sdk'
function isValidAddress(address: string): boolean {
try {
const bytes = fromBase58(address)
return bytes.length === 48 // BLS12-381 public key is 48 bytes
} catch {
return false
}
}
Transactions
Transactions are the primary way to interact with the Amadeus blockchain. They must be:
- Built with proper structure
- Signed with the sender's private key
- Serialized (packed) into binary format
- Submitted to a node
Transaction Structure
interface UnsignedTransaction {
signer: Uint8Array // Sender's public key (48 bytes)
nonce: bigint // Transaction nonce
action: TransactionAction // Transaction action
}
interface TransactionAction {
op: 'call' // Operation type
contract: string // Contract name or address
function: string // Function to call
args: SerializableValue[] // Function arguments
}
Transaction Lifecycle
import { TransactionBuilder } from '@amadeus-protocol/sdk'
const builder = new TransactionBuilder(privateKey)
// 1. Build unsigned transaction
const unsignedTx = builder.buildTransfer({
recipient: address,
amount: 10.5,
symbol: 'AMA'
})
// 2. Sign the transaction
const { txHash, txPacked } = builder.sign(unsignedTx)
// 3. Submit to blockchain
const result = await sdk.transaction.submit(txPacked)
Transaction Nonces
Nonces are automatically generated using timestamps:
// Nonce is generated as: BigInt(Date.now()) * 1_000_000n
// This ensures uniqueness for transactions
For high-frequency transactions, ensure sufficient time between transactions to avoid nonce collisions.
Amounts
AMA tokens use 9 decimal places. Always use atomic units for transactions.
Converting Amounts
import { toAtomicAma, fromAtomicAma } from '@amadeus-protocol/sdk'
// Convert to atomic units (for transactions)
const atomic = toAtomicAma(1.5) // Returns 1500000000
// Convert from atomic units (for display)
const human = fromAtomicAma(1500000000) // Returns 1.5
Amount Precision
Always use toAtomicAma when building transactions:
// ✅ Good
const amount = toAtomicAma(1.5)
// ❌ Bad - may lose precision
const amount = 1.5 * 1000000000
Serialization
The SDK uses VecPack canonical serialization for deterministic encoding of transaction data.
Encoding
import { encode } from '@amadeus-protocol/sdk'
const data = {
foo: 'bar',
count: 42,
items: [1, 2, 3]
}
const encoded = encode(data) // Returns Uint8Array
Decoding
import { decode } from '@amadeus-protocol/sdk'
const decoded = decode(encoded) // Returns DecodedValue
Supported Types
nullbooleannumber/bigintstringUint8Array- Arrays
- Objects / Maps
Signing
Transactions are signed using BLS12-381 signatures over the transaction hash.
Signing Process
- Build transaction structure
- Serialize transaction (canonical encoding)
- Hash the serialized transaction (SHA-256)
- Sign the hash with private key (BLS12-381)
- Pack transaction with signature
// The SDK handles all of this automatically
const { txHash, txPacked } = builder.sign(unsignedTx)
Encoding Formats
Base58
Used for addresses, keys, and transaction hashes:
import { toBase58, fromBase58 } from '@amadeus-protocol/sdk'
// Encode bytes to Base58
const encoded = toBase58(new Uint8Array([1, 2, 3]))
// Decode Base58 to bytes
const decoded = fromBase58('5Kd3N...')
Base64
Used for encrypted data and binary payloads:
import { uint8ArrayToBase64, base64ToUint8Array } from '@amadeus-protocol/sdk'
// Encode to Base64
const base64 = uint8ArrayToBase64(bytes)
// Decode from Base64
const bytes = base64ToUint8Array(base64)
Encryption
The SDK provides password-based encryption for securing sensitive data.
Encrypting Data
import { encryptWithPassword } from '@amadeus-protocol/sdk'
// Encrypt private key before storage
const encrypted = await encryptWithPassword(privateKey, userPassword)
// Returns: { encryptedData, iv, salt } (all Base64 encoded)
Decrypting Data
import { decryptWithPassword } from '@amadeus-protocol/sdk'
// Decrypt when needed
const decrypted = await decryptWithPassword(encrypted, userPassword)
Security Features
- AES-GCM encryption with 256-bit keys
- PBKDF2 key derivation with 100,000 iterations
- Unique salt and IV for each encryption
- Authenticated encryption (prevents tampering)
API Client
The SDK provides a unified API client for interacting with Amadeus nodes.
Initialization
import { AmadeusSDK } from '@amadeus-protocol/sdk'
const sdk = new AmadeusSDK({
baseUrl: 'https://nodes.amadeus.bot/api',
timeout: 30000
})
API Modules
sdk.chain- Chain queries (tip, stats, entries)sdk.wallet- Wallet operations (balances, transactions)sdk.transaction- Transaction submissionsdk.contract- Contract interactionssdk.epoch- Epoch and validator datasdk.peer- Network peer informationsdk.proof- Validator proofs
Error Handling
All SDK errors are instances of AmadeusSDKError:
import { AmadeusSDKError } from '@amadeus-protocol/sdk'
try {
const balance = await sdk.wallet.getBalance(address, 'AMA')
} catch (error) {
if (error instanceof AmadeusSDKError) {
console.error('SDK Error:', error.message)
console.error('Status:', error.status)
console.error('Response:', error.response)
}
}
Next Steps
Now that you understand the core concepts: