aex402-sdk-crystal
AeX402 Crystal SDK
Crystal SDK for the AeX402 Hybrid AMM on Solana. Supports stable pools (AeX402 curve), volatile pools (constant product), and virtual pool graduation system.
Installation
Add to your shard.yml:
dependencies:
aex402:
github: aldrin-labs/aex402-crystal
version: ~> 0.1.0
Then run:
shards install
Usage
Basic Example
require "aex402"
# Parse pool account data from Solana
pool = AeX402::Accounts.parse_pool(account_data)
if pool
puts "Pool balances: #{pool.bal0} / #{pool.bal1}"
puts "Amp: #{pool.amp}"
puts "Fee: #{pool.fee_bps} bps"
end
# Simulate a swap
expected_out = AeX402::Math.simulate_swap(
bal_in: pool.bal0,
bal_out: pool.bal1,
amount_in: 1_000_000_u64,
amp: pool.amp,
fee_bps: pool.fee_bps
)
puts "Expected output: #{expected_out}"
# Build swap instruction
ix = AeX402::Instructions.swap_t0_t1(
pool: pool_address,
vault0: pool.vault0.to_s,
vault1: pool.vault1.to_s,
user_token0: user_token0_address,
user_token1: user_token1_address,
user: user_address,
args: AeX402::SwapSimpleArgs.new(
amount_in: 1_000_000_u64,
min_out: (expected_out.not_nil! * 99 // 100).to_u64
)
)
Account Parsing
# Parse different account types
pool = AeX402::Accounts.parse_pool(data)
npool = AeX402::Accounts.parse_npool(data)
farm = AeX402::Accounts.parse_farm(data)
user_farm = AeX402::Accounts.parse_user_farm(data)
lottery = AeX402::Accounts.parse_lottery(data)
# Get account type from data
account_type = AeX402::Accounts.get_account_type(data)
case account_type
when :pool
# Handle pool
when :farm
# Handle farm
end
Math Operations
# Calculate invariant D
d = AeX402::Math.calc_d(balance0, balance1, amp)
# Calculate output amount
y = AeX402::Math.calc_y(new_balance_in, d, amp)
# Simulate swap with fees
output = AeX402::Math.simulate_swap(
bal_in: 1_000_000_u64,
bal_out: 1_000_000_u64,
amount_in: 100_000_u64,
amp: 100_u64,
fee_bps: 30_u64
)
# Calculate LP tokens for deposit
lp_tokens = AeX402::Math.calc_lp_tokens(
amt0: deposit0,
amt1: deposit1,
bal0: current_bal0,
bal1: current_bal1,
lp_supply: current_supply,
amp: amp
)
# Get current amp during ramping
current_amp = AeX402::Math.get_current_amp(
amp: pool.amp,
target_amp: pool.target_amp,
ramp_start: pool.ramp_start,
ramp_end: pool.ramp_stop,
now: Time.utc.to_unix
)
# Calculate price impact
impact = AeX402::Math.calc_price_impact(bal_in, bal_out, amount_in, amp, fee_bps)
# Calculate virtual price
virtual_price = AeX402::Math.calc_virtual_price(bal0, bal1, lp_supply, amp)
Instruction Building
# Create pool
ix = AeX402::Instructions.create_pool(
pool: pool_address,
mint0: mint0_address,
mint1: mint1_address,
authority: authority_address,
args: AeX402::CreatePoolArgs.new(amp: 100_u64, bump: bump)
)
# Add liquidity
ix = AeX402::Instructions.add_liquidity(
pool: pool_address,
vault0: vault0,
vault1: vault1,
lp_mint: lp_mint,
user_token0: user_t0,
user_token1: user_t1,
user_lp: user_lp,
user: user_address,
args: AeX402::AddLiqArgs.new(
amount0: 1_000_000_u64,
amount1: 1_000_000_u64,
min_lp: 900_000_u64
)
)
# Remove liquidity
ix = AeX402::Instructions.remove_liquidity(...)
# Stake LP tokens
ix = AeX402::Instructions.stake_lp(
user_position: position,
farm: farm_address,
user_lp: user_lp,
lp_vault: lp_vault,
user: user_address,
args: AeX402::StakeArgs.new(amount: 1_000_000_u64)
)
PDA Derivation
# Derive pool PDA
result = AeX402::PDA.derive_pool_pda(mint0, mint1)
if result
pool_address = result.address
bump = result.bump
end
# Derive farm PDA
result = AeX402::PDA.derive_farm_pda(pool_address)
# Derive user farm position
result = AeX402::PDA.derive_user_farm_pda(farm_address, user_address)
# Derive lottery PDAs
lottery = AeX402::PDA.derive_lottery_pda(pool_address)
entry = AeX402::PDA.derive_lottery_entry_pda(lottery_address, user_address)
Virtual Pool System
# Parse global header
header = AeX402::VPool::Accounts.parse_global_header(data)
# Parse a virtual pool slot
slot = AeX402::VPool::Accounts.parse_vpool_slot(data, slot_index)
# Calculate current price on bonding curve
price = AeX402::VPool::Math.calc_price(
tokens_sold: slot.tokens_sold,
base_price: slot.base_price,
slope: slot.slope
)
# Simulate buy
simulation = AeX402::VPool::Math.calc_buy(
sol_in: BigInt.new("1000000000"), # 1 SOL
tokens_sold: slot.tokens_sold,
base_price: slot.base_price,
slope: slot.slope
)
if simulation
puts "Tokens out: #{simulation.tokens_out}"
puts "New price: #{simulation.new_price}"
puts "Price impact: #{simulation.price_impact}%"
end
# Check graduation status
can_graduate = AeX402::VPool.can_graduate?(slot)
progress = AeX402::VPool.graduation_progress(slot)
# Get pool statistics
stats = AeX402::VPool.stats(slot, Time.utc.to_unix)
Module Structure
AeX402::Constants- Program ID, discriminators, pool constantsAeX402::Errors- Error codes and messagesAeX402::Types- Data structures (Pool, NPool, Farm, etc.)AeX402::Accounts- Account parsing functionsAeX402::Instructions- Instruction buildersAeX402::Math- StableSwap math (Newton's method)AeX402::PDA- PDA derivation utilitiesAeX402::VPool- Virtual pool graduation systemAeX402::VPool::ConstantsAeX402::VPool::TypesAeX402::VPool::AccountsAeX402::VPool::InstructionsAeX402::VPool::Math
Constants
# Program ID
AeX402::PROGRAM_ID # => "3AMM53MsJZy2Jvf7PeHHga3bsGjWV4TSaYz29WUtcdje"
# Pool constraints
AeX402::MIN_AMP # => 1
AeX402::MAX_AMP # => 100_000
AeX402::DEFAULT_FEE_BPS # => 30 (0.3%)
AeX402::NEWTON_ITERATIONS # => 255
AeX402::MAX_TOKENS # => 8
# Account sizes
AeX402::POOL_SIZE # => 1024 bytes
AeX402::NPOOL_SIZE # => 2048 bytes
Error Handling
begin
# ... operation
rescue ex : AeX402::AeX402Error
puts "Error code: #{ex.code}"
puts "Message: #{ex.message}"
rescue ex : AeX402::ParseError
puts "Failed to parse account: #{ex.message}"
rescue ex : AeX402::MathError
puts "Math computation failed: #{ex.message}"
end
Development
# Run tests
crystal spec
# Format code
crystal tool format
# Build documentation
crystal docs
License
MIT
Repository
aex402-sdk-crystal
Owner
Statistic
- 0
- 0
- 0
- 0
- 1
- about 3 hours ago
- December 29, 2025
License
Links
Synced at
Mon, 29 Dec 2025 21:58:59 GMT
Languages