- Build WASM:
cargo build --target wasm32-unknown-unknown --release - Generated binary at:
target/wasm32-unknown-unknown/release/aura_vault.wasm - Run contract unit tests:
cargo test - All 22 tests passing
- Testnet credentials prepared
- Testnet tokens funded (XLM + test tokens)
- Contract uploaded to testnet
- Contract ID noted and verified
# Initialize vault
stellar contract invoke \
--id <CONTRACT_ID> \
--source <ADMIN_KEYPAIR> \
--network testnet \
-- initialize \
--admin <ADMIN_ADDRESS> \
--underlying_token <TOKEN_CONTRACT_ID>Verification:
- TX succeeds with no errors
- TX hash recorded
- Cannot initialize twice (verify AlreadyInitialized error)
# First deposit should get 1:1 share ratio
stellar contract invoke \
--id <CONTRACT_ID> \
--source <USER1_KEYPAIR> \
--network testnet \
-- deposit \
--caller <USER1_ADDRESS> \
--amount 1000000Verification:
- TX succeeds
- Shares returned = 1000000 (1:1 ratio)
-
balance_of(USER1)returns 1000000 -
total_assets()returns 1000000
# Second deposit: 2M tokens, should mint floor(2M * 1M / 1M) = 2M shares
stellar contract invoke \
--id <CONTRACT_ID> \
--source <USER2_KEYPAIR> \
--network testnet \
-- deposit \
--caller <USER2_ADDRESS> \
--amount 2000000Verification:
- TX succeeds
- Shares returned = 2000000
-
balance_of(USER2)returns 2000000 -
total_assets()returns 3000000 (1M + 2M) -
total_shares()returns 3000000
# Inject 300k yield without minting shares
stellar contract invoke \
--id <CONTRACT_ID> \
--source <KEEPER_KEYPAIR> \
--network testnet \
-- harvest \
--caller <KEEPER_ADDRESS> \
--yield_amount 300000Verification:
- TX succeeds
-
total_assets()returns 3300000 (3M + 300k yield) -
total_shares()still 3000000 (no new shares minted) - Exchange rate increased: 3.3M / 3M = 1.1
User has 1M shares after first deposit. Withdraw 500k shares:
- Expected redemption: floor(500k * 3.3M / 3M) = floor(550k) = 550k tokens
stellar contract invoke \
--id <CONTRACT_ID> \
--source <USER1_KEYPAIR> \
--network testnet \
-- withdraw \
--caller <USER1_ADDRESS> \
--shares 500000Verification:
- TX succeeds
- Tokens returned = 550000
-
balance_of(USER1)returns 500000 (1M - 500k) -
total_assets()returns 2750000 (3.3M - 550k) -
total_shares()returns 2500000 (3M - 500k)
User has 500k shares. Withdraw all:
- Expected redemption: floor(500k * 2.75M / 2.5M) = floor(550k) = 550k tokens
stellar contract invoke \
--id <CONTRACT_ID> \
--source <USER1_KEYPAIR> \
--network testnet \
-- withdraw \
--caller <USER1_ADDRESS> \
--shares 500000Verification:
- TX succeeds
-
balance_of(USER1)returns 0 - User receives 550k tokens
-
total_assets()returns 2200000 (2.75M - 550k)
Try to deposit before initialize:
stellar contract invoke \
--id <NEW_CONTRACT_ID> \
--source <USER_KEYPAIR> \
--network testnet \
-- deposit \
--caller <USER_ADDRESS> \
--amount 1000000Verification:
- Error code 1 returned
- No state changes
Call initialize twice:
# First call succeeds
stellar contract invoke --id <CONTRACT_ID> ... -- initialize ...
# Second call fails
stellar contract invoke --id <CONTRACT_ID> ... -- initialize ...Verification:
- Second call returns error 2
Try to withdraw more shares than owned:
# User has 100k shares
stellar contract invoke \
--id <CONTRACT_ID> \
--source <USER_KEYPAIR> \
--network testnet \
-- withdraw \
--caller <USER_ADDRESS> \
--shares 200000 # More than ownedVerification:
- Error code 3 returned
- No state changes
Deposit zero or negative:
stellar contract invoke \
--id <CONTRACT_ID> \
--source <USER_KEYPAIR> \
--network testnet \
-- deposit \
--caller <USER_ADDRESS> \
--amount 0Verification:
- Error code 5 returned
Deposit with extremely large numbers:
stellar contract invoke \
--id <CONTRACT_ID> \
--source <USER_KEYPAIR> \
--network testnet \
-- deposit \
--caller <USER_ADDRESS> \
--amount 999999999999999999 # i128::MAXVerification:
- Error code 6 returned (if it overflows)
Harvest when no deposits exist:
# On new vault with no deposits
stellar contract invoke \
--id <EMPTY_CONTRACT_ID> \
--source <KEEPER_KEYPAIR> \
--network testnet \
-- harvest \
--caller <KEEPER_ADDRESS> \
--yield_amount 1000Verification:
- Error code 8 returned
Setup: 3 users deposit over time
# User 1: Deposit 1M tokens
# Expected: 1M shares, exchange rate 1.0
# User 2: Deposit 2M tokens
# Expected: 2M shares, exchange rate 1.0
# Harvest 300k
# Expected: exchange rate becomes 1.1
# User 3: Deposit 3M tokens
# Expected: floor(3M * 3M / 3.3M) ≈ 2.727M shares
# User 1 withdraws 1M shares
# Expected: floor(1M * 3.3M / 5.727M) ≈ 576k tokensTest Code:
def test_multi_user_scenario():
vault = AuraVaultClient('CABC...')
# User 1 deposit
shares_1 = vault.deposit(user1, 1_000_000)
assert shares_1 == 1_000_000
# User 2 deposit
shares_2 = vault.deposit(user2, 2_000_000)
assert shares_2 == 2_000_000
# Harvest
vault.harvest(keeper, 300_000)
total = vault.get_total_assets()
assert total == 3_300_000
# User 3 deposit
shares_3 = vault.deposit(user3, 3_000_000)
assert shares_3 == 2_727_272 # floor(3M * 3M / 3.3M)
# User 1 withdraw
tokens = vault.withdraw(user1, 1_000_000)
assert tokens == 576923 # floor(1M * 3.3M / 5.727M)Verify floor division throughout:
def test_rounding():
vault = AuraVaultClient('CABC...')
# Deposit that results in rounding
vault.deposit(user1, 1_000_000)
vault.harvest(keeper, 1) # Add 1 wei of yield
# Deposit 333k tokens
# Shares = floor(333k * 1M / 1M) = 333k
shares = vault.deposit(user2, 333_333)
assert shares == 333_333def test_high_frequency():
vault = AuraVaultClient('CABC...')
for i in range(100):
shares = vault.deposit(users[i], 10_000 + i)
assert shares > 0
total = vault.get_total_assets()
assert total == sum(10_000 + i for i in range(100))Success Criteria:
- All 100 deposits succeed
- No timeouts
- Final state matches expectations
def test_harvest_performance():
vault = AuraVaultClient('CABC...')
vault.deposit(user1, 10_000_000)
# 1000 harvests
for i in range(1000):
vault.harvest(keeper, 1000 + i)
total = vault.get_total_assets()
assert total == 10_000_000 + sum(1000 + i for i in range(1000))Success Criteria:
- All harvests succeed
- Total assets calculated correctly
- No overflow errors
def test_auth_required():
vault = AuraVaultClient('CABC...')
# Try to deposit without auth from caller
# This should fail at Soroban level
try:
result = vault.deposit(user2, 1_000_000)
assert False, "Should have failed auth check"
except Exception as e:
assert "auth" in str(e).lower()The contract uses CEI (Checks-Effects-Interactions) pattern:
- Effects (state changes) happen before interactions (token transfers)
- Verified in contract code review
Before deploying to mainnet:
- All tests passing on testnet
- No remaining error logs
- Contract code reviewed by 2+ developers
- Security audit completed (if applicable)
- Upgrade path tested (if applicable)
- Admin key management documented
- Emergency pause procedure established
- Monitoring/alerting configured
- Liquidity provider onboarded
- User documentation finalized
- Support team trained
After any code changes:
# 1. Rebuild and test
cargo test
# 2. Deploy to testnet
stellar contract upload --wasm ...
stellar contract deploy --wasm-hash ...
# 3. Run full test suite
python test_suite.py
# 4. Manual spot checks
# - Initialize new vault
# - Deposit from 3 users
# - Harvest
# - Withdraw
# - Check balances
# 5. Verify no state corruption
python verify_invariants.pyUse this to document test runs:
Date: 2024-06-25
Tester: Your Name
Network: testnet
Contract ID: CABC...
[ ] Initialization
Status: PASS / FAIL
TX Hash: abc123...
Notes:
[ ] Deposit (1:1)
Status: PASS / FAIL
Shares: 1000000
Notes:
[ ] Deposit (pro-rata)
Status: PASS / FAIL
Shares: 2000000
Notes:
[ ] Harvest
Status: PASS / FAIL
Exchange Rate: 1.1x
Notes:
[ ] Withdraw
Status: PASS / FAIL
Tokens: 550000
Notes:
[ ] Error handling
Status: PASS / FAIL
Notes:
Overall: PASS / FAIL
Issues: None / See notes above
Sign-off: ________________