Skip to content

About

Official TypeScript/JavaScript SDK for Amadeus Protocol

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Repository files navigation

@amadeus-protocol/sdk

Official TypeScript/JavaScript SDK for Amadeus Protocol - Core utilities for serialization, cryptography, transaction building, and API client.

npm version License: MIT TypeScript Node.js Bundle Size

Installation

npm install @amadeus-protocol/sdk
# or
yarn add @amadeus-protocol/sdk
# or
pnpm add @amadeus-protocol/sdk
# or
bun add @amadeus-protocol/sdk

ESM-only: this package is published as pure ESM ("type": "module"). Use Node.js 20+ with "type": "module" in your package.json, or any modern bundler (Vite, webpack, esbuild, Metro). For CommonJS consumers, run via tsx. See Troubleshooting for the 1.0.x ERR_MODULE_NOT_FOUND issue and upgrade path.

What's New in 1.7.0

  • Paged get_prefix — since node 78b5e9c the endpoint answers {rows, cursor} pages. decodeContractState() reads both the paged and the old shape, and contract.getPrefixEntries() (so every staking.* read) follows the cursor. Making your own HTTP calls? Use readAllPrefixPages() with prefixPagePath()
  • Concurrent requests on one AmadeusClient no longer abort each other

What's New in 1.6.0

  • Dual-derivation import — resolveMnemonicImport() derives both accounts a phrase can open, checks each one for history with addressHasActivity(), and tells you which to open or that the user must choose. See Importing a phrase into a wallet
  • resolveCandidateForAddress() — find the derivation of a phrase that opens a known address
  • derivationForSeed() — find which derivation produced a stored seed
  • validateMnemonic() and the derive functions now accept phrases with extra whitespace or line breaks
  • SDK_VERSION now reports the real package version (it was stuck at 1.2.0)

What's New in 1.5.0

  • Two derivations per phrase — since 1.4.0 the same recovery phrase can open two different addresses, depending on which SDK version derived it. 1.5.0 adds mnemonicToCandidateKeypairs() to derive both, so wallets can recover either. See Mnemonics and account derivation
  • secretKeyToSeedBase58() — import a raw 32-byte BLS key (e.g. a hub export)

See the CHANGELOG for full release history.

Breaking in 1.4.0: mnemonicToSeedBase58 returns a different account

mnemonicToSeedBase58() kept its name but switched from the raw BIP39 seed (legacy) to the EIP-2333 path m/12381'/36969'/0'/0'/0' (eip2333), the standard shared with the hub, Prime Hub and cli-wallet. Restoring a phrase with 1.4.0+ opens a different address than 1.3.0 and earlier did. Seeds already stored in a vault are unaffected. See the CHANGELOG.

What's New in 1.2.0

  • Staking (LockupVault) — read an account's staking position in one call: sdk.staking.getPosition(publicKey) returns every vault plus the rolled-up totals (total staked, accrued yield, stake-weighted APY, maturity/unlock state, stake per validator)
  • contract.getPrefixEntries() — prefix reads that return raw [key, value] byte pairs. /api/contract/get_prefix answers with a VecPack binary body, which getPrefix() mangles by parsing it as text
  • client.postBinary() — POST returning raw bytes, for the binary endpoints
  • decodeContractState() fix — a prefix matching nothing comes back as an empty VecPack list, not an empty map. It used to throw; it now decodes to []. This is the common case for any account that has never staked
  • ChainStats — added the fields the node actually returns: total_locked, total_supply, supply_computed_at_height, and validators (with per-validator commission_bps and stake)

See the CHANGELOG for full release history.

What's New in 1.1.0

  • contract.view() — read-only contract execution
  • chain.getByFilter() / chain.getKpi() — filtered tx queries + protocol KPIs
  • proof.getContractStateProof() — merkle proofs for contract state
  • submitAndWait(txPacked, { finalized: true }) — wait for finality instead of confirmation
  • NFT contract — NFT_ABI, buildNftTransfer/Mint/CreateCollection, TransactionBuilder.nftTransfer/nftMint/nftCreateCollection
  • ESM fix — published dist/*.js now resolves correctly under raw node (the 1.0.x ERR_MODULE_NOT_FOUND bug)

See the CHANGELOG for full release history.

Features

  • Canonical Serialization (VecPack): Deterministic encoding/decoding for cryptographic operations
  • Cryptographic Operations: BLS12-381 key generation, signing, and verification
  • Password-Based Encryption: Secure AES-GCM encryption with PBKDF2 key derivation for wallet data
  • Transaction Building: Create and sign Amadeus protocol transactions
  • Token Conversions: Convert between atomic units and human-readable amounts
  • Encoding Utilities: Base58 and Base64 encoding/decoding for addresses, keys, and binary data
  • Staking: Read LockupVault positions — totals, per-vault detail, and stake-weighted APY
  • API Client: Full-featured HTTP client for interacting with Amadeus nodes
  • Type Safety: Complete TypeScript definitions for all APIs
  • Zero Dependencies: Uses native fetch (no axios or other HTTP libraries)

Quick Start

Using the SDK

import { AmadeusSDK } from '@amadeus-protocol/sdk'

// Initialize SDK (uses default node URL if not specified)
const sdk = new AmadeusSDK({
	baseUrl: 'https://mainnet-rpc.ama.one/api'
})

// Query chain
const tip = await sdk.chain.getTip()
console.log('Current height:', tip.entry.header.height)

// Query wallet balance
const balance = await sdk.wallet.getBalance('5Kd3N...', 'AMA')
console.log('Balance:', balance.balance.float)

// Submit transaction
const result = await sdk.transaction.submit(txPacked)

Usage

API Client

import { AmadeusSDK } from '@amadeus-protocol/sdk'

const sdk = new AmadeusSDK({
	baseUrl: 'https://mainnet-rpc.ama.one/api',
	timeout: 30000 // Optional: custom timeout
})

// Chain API
const stats = await sdk.chain.getStats()
const tip = await sdk.chain.getTip()
const entry = await sdk.chain.getByHash('5Kd3N...')
const { txs, cursor } = await sdk.chain.getByFilter({ contract: 'Coin', function: 'transfer' })
const { kpi } = await sdk.chain.getKpi()

// Wallet API
const balance = await sdk.wallet.getBalance('5Kd3N...', 'AMA')
const allBalances = await sdk.wallet.getAllBalances('5Kd3N...')

// Transaction API
const result = await sdk.transaction.submit(txPacked)
const confirmed = await sdk.transaction.submitAndWait(txPacked)
const finalized = await sdk.transaction.submitAndWait(txPacked, { finalized: true })
const tx = await sdk.transaction.get('5Kd3N...')

// Contract API
const contractData = await sdk.contract.get(key)
const richlist = await sdk.contract.getRichlist()
const { success, result } = await sdk.contract.view({
	contract: 'LockupPrime',
	function: 'view_balance',
	args: ['my_vault']
})

// Epoch API
const scores = await sdk.epoch.getScore()
const emission = await sdk.epoch.getEmissionAddress('5Kd3N...')

// Peer API
const nodes = await sdk.peer.getNodes()
const trainers = await sdk.peer.getTrainers()

// Proof API
const validatorProof = await sdk.proof.getValidators(entryHash)
const stateProof = await sdk.proof.getContractStateProof(stateKey)

Key Generation

import { generateKeypair, derivePublicKeyFromSeedBase58 } from '@amadeus-protocol/sdk'

// Generate a new keypair
const keypair = generateKeypair()
console.log(keypair.publicKey) // Base58 public key
console.log(keypair.privateKey) // Base58 private key (seed)

// Derive public key from existing seed
const publicKey = derivePublicKeyFromSeedBase58(keypair.privateKey)

Transaction Building

Using TransactionBuilder Class (Recommended)

import { TransactionBuilder, fromBase58, toAtomicAma } from '@amadeus-protocol/sdk'

// Instance-based usage (convenient for multiple transactions)
const builder = new TransactionBuilder('5Kd3N...') // Base58 encoded seed

// Option 1: Build and sign in one step (convenience)
const { txHash, txPacked } = builder.transfer({
	recipient: '5Kd3N...', // Base58 encoded recipient address
	amount: 10.5, // Amount in human-readable format
	symbol: 'AMA' // Token symbol
})

// Option 2: Build unsigned, then sign (more control)
const unsignedTx = builder.buildTransfer({
	recipient: '5Kd3N...',
	amount: 10.5,
	symbol: 'AMA'
})
// Can inspect or modify unsignedTx before signing
const { txHash, txPacked } = builder.sign(unsignedTx)

// Option 3: Build custom transaction and sign
const unsignedTx = builder.build('Coin', 'transfer', [
	fromBase58('5Kd3N...'), // Recipient bytes
	toAtomicAma(10.5).toString(), // Amount in atomic units
	'AMA'
])
const { txHash, txPacked } = builder.sign(unsignedTx)

// Option 4: Build and sign custom transaction (convenience)
const { txHash, txPacked } = builder.buildAndSign('Coin', 'transfer', [
	fromBase58('5Kd3N...'),
	toAtomicAma(10.5).toString(),
	'AMA'
])

ABI-Driven (Lockup, LockupPrime, etc.)

The recommended pattern for built-in contracts. Pass any ABI to builder.contract(abi) and get fully-typed function calls:

import {
	TransactionBuilder,
	LOCKUP_PRIME_ABI,
	LOCKUP_ABI,
	toAtomicAma
} from '@amadeus-protocol/sdk'

const builder = new TransactionBuilder('5Kd3N...')

// LockupPrime — auto-typed methods derived from the ABI
builder.contract(LOCKUP_PRIME_ABI).lock({ amount: toAtomicAma(100).toString(), tier: '30d' })
builder.contract(LOCKUP_PRIME_ABI).unlock({ vaultIndex: '3' })
builder.contract(LOCKUP_PRIME_ABI).daily_checkin({ vaultIndex: '7' })

// Lockup
builder.contract(LOCKUP_ABI).unlock({ vaultIndex: '5' })

NFT (transfer, mint, create_collection)

The Nft built-in contract has dedicated builder methods. NFT amounts are integer counts, not AMA atomic units.

const builder = new TransactionBuilder(privateKey)

// Create a collection (caller becomes owner)
builder.nftCreateCollection({ collection: 'AGENTIC', soulbound: false })

// Mint tokens (collection owner only)
builder.nftMint({ recipient: '5Kd3N...', amount: 10, collection: 'AGENTIC', token: '1' })

// Transfer
builder.nftTransfer({ recipient: '5Kd3N...', amount: 1, collection: 'AGENTIC', token: '1' })

Static variants: TransactionBuilder.buildSignedNftTransfer/Mint/CreateCollection(input) — each takes the same params plus senderPrivkey.

Signing Transactions

The SDK supports two patterns. Pick whichever fits your workflow.

Pattern 1 — Auto-signed (high-level, recommended)

The TransactionBuilder instance methods build and sign in one call. Best for app code where you have the private key in hand.

import { TransactionBuilder, LOCKUP_PRIME_ABI, toAtomicAma } from '@amadeus-protocol/sdk'

const builder = new TransactionBuilder('5Kd3N...') // Base58 seed

// Coin transfer
const a = builder.transfer({ recipient: '5Kd3N...', amount: 10.5, symbol: 'AMA' })

// ABI-driven (any contract)
const b = builder.contract(LOCKUP_PRIME_ABI).lock({
	amount: toAtomicAma(100).toString(),
	tier: '30d'
})

// NFT
const c = builder.nftTransfer({
	recipient: '5Kd3N...',
	amount: 1,
	collection: 'AGENTIC',
	token: '1'
})

// All return { txHash, txPacked } ready for sdk.transaction.submit(txPacked)

Pattern 2 — Manual: build a ContractCall, sign separately

Build a ContractCall with a standalone helper or createContract(ABI), then sign it independently with TransactionBuilder.signCall(privkey, call). Useful when:

  • You want to inspect or log the call before signing
  • The signing key lives somewhere else (HSM, separate process, separate machine)
  • You want to batch-build and sign at the end
import {
	TransactionBuilder,
	createContract,
	LOCKUP_PRIME_ABI,
	buildCoinTransfer,
	buildNftTransfer,
	toAtomicAma
} from '@amadeus-protocol/sdk'

// Build a ContractCall — three ways:

// A. Standalone helper (Coin)
const callA = buildCoinTransfer({ recipient: '5Kd3N...', amount: 10.5, symbol: 'AMA' })

// B. Standalone helper (NFT)
const callB = buildNftTransfer({
	recipient: '5Kd3N...',
	amount: 1,
	collection: 'AGENTIC',
	token: '1'
})

// C. ABI-driven, any contract
const lockupPrime = createContract(LOCKUP_PRIME_ABI)
const callC = lockupPrime.lock({ amount: toAtomicAma(100).toString(), tier: '30d' })

// Inspect if you want
console.log('Will call:', callC.contract, callC.method, callC.args)

// Sign with any private key (no builder instance needed)
const { txHash, txPacked } = TransactionBuilder.signCall('5Kd3N...', callC)

Build-unsigned-then-sign (debugging)

If you need to inspect the full unsigned transaction (nonce, signer, action) before signing:

const builder = new TransactionBuilder('5Kd3N...')

const unsigned = builder.buildTransfer({ recipient: '5Kd3N...', amount: 10.5, symbol: 'AMA' })
console.log('Nonce:', unsigned.tx.nonce)
console.log('Action:', unsigned.tx.action)

const { txHash, txPacked } = builder.sign(unsigned)

Using Static Methods

import {
	TransactionBuilder,
	fromBase58,
	toAtomicAma,
	getPublicKey,
	deriveSkAndSeed64FromBase58Seed
} from '@amadeus-protocol/sdk'

// Option 1: Build and sign transfer in one step (convenience)
const { txHash, txPacked } = TransactionBuilder.buildSignedTransfer({
	senderPrivkey: '5Kd3N...', // Base58 encoded seed
	recipient: '5Kd3N...', // Base58 encoded recipient address
	amount: 10.5, // Amount in human-readable format
	symbol: 'AMA' // Token symbol
})

// Option 2: Build unsigned transfer, then sign
const { seed64 } = deriveSkAndSeed64FromBase58Seed('5Kd3N...')
const signerPubKey = getPublicKey(seed64)

const unsignedTx = TransactionBuilder.buildTransfer(
	{ recipient: '5Kd3N...', amount: 10.5, symbol: 'AMA' },
	signerPubKey
)
const { txHash, txPacked } = TransactionBuilder.sign(unsignedTx, '5Kd3N...')

// Option 3: Build custom unsigned transaction, then sign
const unsignedTx = TransactionBuilder.build(
	signerPubKey,
	'Coin', // Contract name
	'transfer', // Method name
	[fromBase58('5Kd3N...'), toAtomicAma(10.5).toString(), 'AMA']
)
const { txHash, txPacked } = TransactionBuilder.sign(unsignedTx, '5Kd3N...')

// Option 4: Build and sign custom transaction (convenience)
const { seed64, sk } = deriveSkAndSeed64FromBase58Seed('5Kd3N...')
const signerPubKey = getPublicKey(seed64)

const { txHash, txPacked } = TransactionBuilder.buildAndSign(signerPubKey, sk, 'Coin', 'transfer', [
	fromBase58('5Kd3N...'),
	toAtomicAma(10.5).toString(),
	'AMA'
])

Serialization

import { encode, decode } from '@amadeus-protocol/sdk'

// Encode data to canonical format
const data = {
	foo: 'bar',
	count: 42,
	items: [1, 2, 3]
}
const encoded = encode(data)

// Decode data from canonical format
const decoded = decode(encoded)

Token Conversions

import { toAtomicAma, fromAtomicAma } from '@amadeus-protocol/sdk'

// Convert to atomic units
const atomic = toAtomicAma(1.5) // Returns 1500000000

// Convert from atomic units
const ama = fromAtomicAma(1500000000) // Returns 1.5

Mnemonics and account derivation

import { generateMnemonic, validateMnemonic, mnemonicToSeedBase58 } from '@amadeus-protocol/sdk'

// Generate a 12-word mnemonic
const mnemonic = generateMnemonic()

// Validate a mnemonic
if (validateMnemonic(mnemonic)) {
	// Derive a Base58 seed (compatible with TransactionBuilder)
	const seedBase58 = mnemonicToSeedBase58(mnemonic)
}

One phrase can open two different accounts, depending on how it was derived:

Derivation How the seed is derived Produced by
eip2333 EIP-2333 BLS12-381 along m/12381'/36969'/0'/0'/0' mnemonicToSeedBase58 in SDK 1.4.0+, hub, Prime Hub, cli-wallet
legacy The raw 64-byte BIP39 seed, used directly as the Amadeus seed mnemonicToSeedBase58 in SDK 1.3.0 and earlier, and wallets built on it

mnemonicToSeedBase58 always returns the eip2333 seed. To recover a wallet that may have been created with the legacy derivation, derive both candidates:

import { mnemonicToCandidateKeypairs, validateMnemonic } from '@amadeus-protocol/sdk'

// Candidates do not check the checksum, so validate first: a typo still derives two
// plausible-looking but wrong accounts.
if (!validateMnemonic(mnemonic)) throw new Error('Invalid recovery phrase')

const [standard, legacy] = mnemonicToCandidateKeypairs(mnemonic)
// standard.derivation === 'eip2333', legacy.derivation === 'legacy'
// Each has { derivation, seed, publicKey }. To pick one on import, see resolveMnemonicImport below.

When you already know the address (for example, copied from the wallet), resolve the matching candidate directly. It trims the address and throws on an invalid phrase:

import { TransactionBuilder, resolveCandidateForAddress } from '@amadeus-protocol/sdk'

const candidate = resolveCandidateForAddress(mnemonic, walletAddress)
if (!candidate) throw new Error('This phrase does not open that address')

// candidate.seed signs as walletAddress, whichever derivation created it
const builder = new TransactionBuilder(candidate.seed)

Importing a phrase into a wallet

resolveMnemonicImport probes both candidates and opens one only when one has history and the other is confirmed empty, or both are confirmed empty (standard wins). Otherwise (both have history, or any probe is unknown) it reports that the user must choose. Always show the other address too. addressHasActivity is the default probe: it checks current balances (including PRIME) and transactions sent or received, and answers 'unknown' rather than 'inactive' when a request fails or times out (8 s by default). Pass the same API base URL the rest of your app uses; any path prefix is kept.

import { addressHasActivity, resolveMnemonicImport, validateMnemonic } from '@amadeus-protocol/sdk'

if (!validateMnemonic(mnemonic)) throw new Error('Invalid recovery phrase')

const apiBaseUrl = 'https://mainnet-rpc.ama.one/api'
const result = await resolveMnemonicImport(mnemonic, (address) =>
	addressHasActivity(apiBaseUrl, address)
)

if (result.needsChoice) {
	// reason is 'both-active' or 'check-failed': let the user pick from result.candidates
} else {
	// reason is 'single-active' or 'none-active': open result.chosen, still showing the other
}

To label an account that was saved with its phrase but without a recorded derivation, use derivationForSeed(mnemonic, seed). It returns 'eip2333', 'legacy' or null (also for a malformed phrase, so it never throws).

Encoding Utilities

import { toBase58, fromBase58, uint8ArrayToBase64, base64ToUint8Array } from '@amadeus-protocol/sdk'

// Base58 encoding
const encoded = toBase58(new Uint8Array([1, 2, 3]))
const decoded = fromBase58('5Kd3N...')

// Base64 encoding
const base64 = uint8ArrayToBase64(new Uint8Array([1, 2, 3]))
const bytes = base64ToUint8Array(base64)

Password-Based Encryption

import { encryptWithPassword, decryptWithPassword } from '@amadeus-protocol/sdk'

// Encrypt sensitive data (e.g., private keys)
const encrypted = await encryptWithPassword('sensitive wallet data', 'my-password')
// Returns: { encryptedData, iv, salt } (all Base64 encoded)

// Decrypt the data
const decrypted = await decryptWithPassword(encrypted, 'my-password')
// Returns: 'sensitive wallet data'

API Reference

Constants

  • AMADEUS_PUBLIC_KEY_BYTE_LENGTH: Byte length of public key (48)
  • AMADEUS_SEED_BYTE_LENGTH: Byte length of seed (64)
  • AMA_TOKEN_DECIMALS: Number of decimal places (9)
  • AMA_TOKEN_DECIMALS_MULTIPLIER: Multiplier for conversions (10^9)
  • AMA_TRANSFER_FEE: Network transfer fee (0.02)
  • EXPLORER_URL: Default explorer URL
  • NODE_API_URL: Default node API URL

Serialization

  • encode(term: SerializableValue): Uint8Array - Encode value to canonical format
  • decode(bytes: Uint8Array | number[]): DecodedValue - Decode from canonical format

Crypto

  • generateKeypair(): KeyPair - Generate a new keypair
  • generatePrivateKey(): Uint8Array - Generate a random 64-byte seed
  • getPublicKey(seed64: Uint8Array): Uint8Array - Derive public key from seed
  • derivePublicKeyFromSeedBase58(base58Seed: string): string - Derive public key from Base58 seed
  • deriveSkAndSeed64FromBase58Seed(base58Seed64: string) - Derive secret key and seed

Mnemonics (BIP39)

  • generateMnemonic(): string - Generate a 12-word BIP39 mnemonic
  • validateMnemonic(mnemonic: string): boolean - Validate a BIP39 mnemonic
  • normalizeMnemonic(mnemonic: string): string - Trim, lowercase and collapse whitespace; the form to store and compare phrases in
  • mnemonicToSeedBase58(mnemonic: string): string - Derive the standard (eip2333) Base58 seed from a mnemonic. Returned the legacy seed before 1.4.0
  • mnemonicToCandidateKeypairs(mnemonic: string): MnemonicCandidate[] - Derive both candidates, eip2333 first and legacy second. Does not validate the mnemonic
  • resolveCandidateForAddress(mnemonic: string, address: string): MnemonicCandidate | null - Validate the mnemonic and return the candidate that opens address, or null
  • derivationForSeed(mnemonic: string, seed: string): HdDerivation | null - Which derivation of the mnemonic produced seed, or null
  • resolveMnemonicImport(mnemonic: string, probeActivity: (address: string) => Promise<AddressActivity>): Promise<ResolvedMnemonicImport> - Derive both candidates, probe each for history and pick the account to open, or report that the user must choose
  • addressHasActivity(apiBaseUrl: string, address: string, timeoutMs?: number): Promise<AddressActivity> - 'active', 'inactive' or 'unknown' from balances and transaction history. Never throws
  • secretKeyToSeedBase58(secretKey: Uint8Array): string - Wrap a raw 32-byte BLS secret key into a 64-byte Base58 seed
  • encodeVaultSecret, decodeVaultSecret, detectInputType - Vault helpers

Encoding

  • toBase58(buf: Uint8Array): string - Encode bytes to Base58
  • fromBase58(str: string): Uint8Array - Decode Base58 to bytes
  • uint8ArrayToBase64(bytes: Uint8Array): string - Convert bytes to Base64
  • base64ToUint8Array(base64: string): Uint8Array - Convert Base64 to bytes
  • arrayBufferToBase64(buffer: ArrayBuffer): string - Convert ArrayBuffer to Base64
  • base64ToArrayBuffer(base64: string): ArrayBuffer - Convert Base64 to ArrayBuffer
  • uint8ArrayToArrayBuffer(bytes: Uint8Array): ArrayBuffer - Convert Uint8Array to ArrayBuffer
  • arrayBufferToUint8Array(buffer: ArrayBuffer): Uint8Array - Convert ArrayBuffer to Uint8Array

Conversion

  • toAtomicAma(ama: number): number - Convert AMA to atomic units
  • fromAtomicAma(atomicAma: number | string): number - Convert atomic units to AMA

Encryption

  • encryptWithPassword(plaintext: string, password: string): Promise<EncryptedPayload> - Encrypt data with password (AES-GCM + PBKDF2)
  • decryptWithPassword(payload: EncryptedPayload, password: string): Promise<string> - Decrypt data with password
  • generateSalt(): Uint8Array - Generate random salt (16 bytes)
  • generateIV(): Uint8Array - Generate random IV (12 bytes)
  • deriveKey(password: string, salt: Uint8Array | ArrayBuffer): Promise<CryptoKey> - Derive AES-GCM key from password

Transaction Building

  • TransactionBuilder - Class for building and signing transactions
    • Constructor: new TransactionBuilder(privateKey?: string) - Create a new builder instance
    • ABI-driven (recommended):
      • contract(abi) - Returns a typed, signer-bound contract interface; each ABI function becomes a method that builds and signs in one step
    • Generic instance methods:
      • build(contract, method, args, signerPk?): UnsignedTransactionWithHash
      • sign(unsignedTx, signerSk?): BuildTransactionResult
      • buildAndSign(contract, method, args, signerPk?, signerSk?): BuildTransactionResult
      • buildFromCall(call): UnsignedTransactionWithHash
      • buildAndSignCall(call): BuildTransactionResult
    • Coin transfer:
      • buildTransfer(input, signerPk?): UnsignedTransactionWithHash
      • transfer(input): BuildTransactionResult
    • NFT (Nft contract):
      • nftTransfer({ recipient, amount, collection, token }): BuildTransactionResult
      • nftMint({ recipient, amount, collection, token }): BuildTransactionResult
      • nftCreateCollection({ collection, soulbound? }): BuildTransactionResult
    • Lockup / LockupPrime convenience methods:
      • lockupUnlock({ vaultIndex }): BuildTransactionResult
      • lockupPrimeLock({ amount, tier }): BuildTransactionResult
      • lockupPrimeUnlock({ vaultIndex }): BuildTransactionResult
      • lockupPrimeDailyCheckin({ vaultIndex }): BuildTransactionResult
    • Static methods: signCall, buildFromCall, buildAndSignCall, buildSignedTransfer, buildSignedNft{Transfer,Mint,CreateCollection}, buildSignedLockup{Unlock}, buildSignedLockupPrime{Lock,Unlock,DailyCheckin}

Contract ABIs

as const ABI definitions for built-in contracts. Pass any ABI to createContract(abi) or builder.contract(abi) for fully-typed function calls.

  • LOCKUP_ABI - Lockup (vesting) — unlock(vaultIndex)
  • LOCKUP_PRIME_ABI - LockupPrime — lock(amount, tier), unlock(vaultIndex), daily_checkin(vaultIndex)
  • NFT_ABI - Nft — transfer, mint, create_collection

Standalone builders that return a ContractCall:

  • buildCoinTransfer({ recipient, amount, symbol })
  • buildNftTransfer({ recipient, amount, collection, token })
  • buildNftMint({ recipient, amount, collection, token })
  • buildNftCreateCollection({ collection, soulbound? })
  • createContract(abi).fn(params) - generic ABI-driven builder
  • buildContractCall(abi, fn, params) - lower-level ABI-driven builder

Staking (LockupVault)

Amadeus staking is the LockupVault contract (bic:lockup_vault:): AMA locked for a tier duration, backing a validator and earning a locked-in APY. Not to be confused with Lockup (vesting) or LockupPrime (points).

const position = await sdk.staking.getPosition('5Kd3N...')

position.totalStaked // AMA staked = principal + yield compounded into the vaults
position.totalAccrued // yield compounded so far
position.weightedApyPercent // stake-weighted average APY
position.vaultCount // how many vaults
position.nextMatureEpoch // when the soonest lock expires
position.vaults // per-vault detail (tier, amount, APY, validator, maturity)
  • sdk.staking.getPosition(pk, opts?) — vaults plus rolled-up totals
  • sdk.staking.getVaults(pk, opts?) — just the vaults
  • sdk.staking.getAllVaults(opts?) — every vault on the chain (explorer-style views)
  • sdk.staking.getValidatorCommissions(opts?) — commission per validator, epoch-resolved
  • sdk.staking.getCurrentEpoch() — epoch from the chain tip

Pass { currentEpoch } across several calls so they resolve against one epoch and skip the extra round trip. The building blocks are exported too, for callers that own their own HTTP layer: buildOwnerVaultsKeyPrefix, readAllPrefixPages, prefixPagePath, decodeContractStatePage, decodeContractState, parseLockupVaultEntries, summarizeLockupVaults, epochFromHeight, flatToAma.

get_prefix answers in pages of { rows, cursor } (at most 1,000 rows each); fetch the next page with ?cursor=<cursor> until the cursor is null. readAllPrefixPages(fetchPage) does that loop, and throws on a shape it does not recognise instead of reading it as empty.

Amounts are atomic bigint (*Flat) so nothing is lost to float rounding — real positions exceed 2^53 atomic units. Use flatToAma() or the pre-computed number fields.

Full API Reference

For complete documentation including request/response types, error handling, and end-to-end examples, see docs.ama.one/sdk.

Examples

See the examples/ directory for comprehensive usage examples:

  • Basic Usage - SDK initialization, key generation, queries
  • Transaction Flow - Complete transaction building and submission flow
  • API Usage - All API endpoints demonstrated
# Run examples
npm run example:basic
npm run example:tx
npm run example:api

Testing

The SDK includes comprehensive test coverage:

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

TypeScript Support

This package is written in TypeScript and includes full type definitions. All exports are typed and documented.

Error Handling

All errors are thrown as AmadeusSDKError instances with descriptive messages:

import { AmadeusSDK, AmadeusSDKError } from '@amadeus-protocol/sdk'

try {
	const balance = await sdk.wallet.getBalance('invalid')
} catch (error) {
	if (error instanceof AmadeusSDKError) {
		console.error('SDK Error:', error.message)
		console.error('Status:', error.status)
		console.error('Response:', error.response)
	}
}

License

MIT

Contributing

Contributions are welcome! Please ensure all code follows the existing style and includes appropriate tests.

About

Official TypeScript/JavaScript SDK for Amadeus Protocol

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages