diff --git a/campaign/src/multi_asset_release.rs b/campaign/src/multi_asset_release.rs index e7db271..c064faa 100644 --- a/campaign/src/multi_asset_release.rs +++ b/campaign/src/multi_asset_release.rs @@ -184,8 +184,10 @@ pub fn release_milestone_multi_asset( // Update per-asset accounting let new_asset_raised = asset_raised .checked_sub(clamped_release) - .unwrap_or(0) - .max(0); + .unwrap_or_else(|| panic_with_error!(env, Error::LedgerUnderflow)); + if new_asset_raised < 0 { + panic_with_error!(env, Error::LedgerUnderflow); + } storage_set_asset_raised(env, &token_address, new_asset_raised); total_released = total_released @@ -196,8 +198,10 @@ pub fn release_milestone_multi_asset( // ── 8. Update global total-raised bookkeeping ──────────────────────────── let new_total_raised = total_raised .checked_sub(total_released) - .unwrap_or(0) - .max(0); + .unwrap_or_else(|| panic_with_error!(env, Error::LedgerUnderflow)); + if new_total_raised < 0 { + panic_with_error!(env, Error::LedgerUnderflow); + } storage_set_total_raised(env, new_total_raised); storage_increment_release_count(env); @@ -258,4 +262,67 @@ mod tests { let result = compute_asset_release(-100, 1000, 1000); assert_eq!(result, None); } + + #[test] + #[should_panic(expected = "HostError")] + fn test_release_underflow_panics() { + use crate::types::{CampaignData, CampaignStatus, MilestoneData, MilestoneStatus, StellarAsset}; + use crate::storage::{set_campaign, set_milestone, storage_set_asset_raised, storage_set_total_raised}; + use soroban_sdk::{testutils::Address as _, Address, Env, String, Vec}; + use soroban_sdk::token::StellarAssetClient; + + let env = Env::default(); + env.mock_all_auths(); + + let creator = Address::generate(&env); + let recipient = Address::generate(&env); + + let token_admin = Address::generate(&env); + let token_issuer = env.register_stellar_asset_contract(token_admin.clone()); + let token_client = StellarAssetClient::new(&env, &token_issuer); + token_client.mint(&env.current_contract_address(), &5000); + + let mut assets = Vec::new(&env); + assets.push_back(StellarAsset { + asset_code: String::from_str(&env, "USDC"), + issuer: Some(token_issuer.clone()), + }); + + let campaign = CampaignData { + creator, + goal_amount: 3000, + raised_amount: 3000, + end_time: env.ledger().timestamp() + 86400, + status: CampaignStatus::Active, + accepted_assets: assets, + milestone_count: 1, + min_donation_amount: 0, + created_at_ledger: 0, + created_at_time: 0, + concluded_at_ledger: None, + }; + set_campaign(&env, &campaign); + + let milestone = MilestoneData { + index: 0, + target_amount: 3000, // milestone_release = 3000 + released_amount: 0, + description_hash: soroban_sdk::BytesN::from_array(&env, &[0; 32]), + status: MilestoneStatus::Unlocked, + released_at: None, + released_at_ledger: None, + release_tx: None, + released_to: None, + }; + set_milestone(&env, 0, &milestone); + + // Force underflow condition: total_raised = 1000, asset_raised = 500 + // asset_release = 500 * 3000 / 1000 = 1500 + // clamped_release = min(1500, 5000) = 1500 + // new_asset_raised = 500 - 1500 => underflow! + storage_set_total_raised(&env, 1000); + storage_set_asset_raised(&env, &token_issuer, 500); + + release_milestone_multi_asset(&env, 0, recipient); + } } diff --git a/campaign/src/test/scratch_test.rs b/campaign/src/test/scratch_test.rs new file mode 100644 index 0000000..576cfec --- /dev/null +++ b/campaign/src/test/scratch_test.rs @@ -0,0 +1,9 @@ +use soroban_sdk::{Env, Address}; +use crate::test::release_milestone_tests::*; + +#[test] +fn scratch() { + let env = Env::default(); + env.mock_all_auths(); + // ... +} diff --git a/campaign/src/test_token_scratch.rs b/campaign/src/test_token_scratch.rs new file mode 100644 index 0000000..4c990f7 --- /dev/null +++ b/campaign/src/test_token_scratch.rs @@ -0,0 +1,16 @@ +#[cfg(test)] +mod test { + use soroban_sdk::{Env, Address, token::StellarAssetClient, token::Client}; + #[test] + fn test_mint() { + let env = Env::default(); + env.mock_all_auths(); + let admin = Address::generate(&env); + let token = env.register_stellar_asset_contract_v2(admin).address(); + let admin_client = StellarAssetClient::new(&env, &token); + let user = Address::generate(&env); + admin_client.mint(&user, &1000); + let client = Client::new(&env, &token); + assert_eq!(client.balance(&user), 1000); + } +} diff --git a/campaign/src/types.rs b/campaign/src/types.rs index e3bcfef..c347b58 100644 --- a/campaign/src/types.rs +++ b/campaign/src/types.rs @@ -108,6 +108,8 @@ pub enum Error { // ── Upgrade / freeze ─────────────────────────────────────────────────── 8x /// Contract is frozen; all mutating operations are blocked. ContractFrozen = 80, + /// A ledger decrement operation underflowed, indicating an invariant violation. + LedgerUnderflow = 81, } diff --git a/campaign/test_snapshots/multi_asset_release/tests/test_release_underflow_panics.1.json b/campaign/test_snapshots/multi_asset_release/tests/test_release_underflow_panics.1.json new file mode 100644 index 0000000..d725b01 --- /dev/null +++ b/campaign/test_snapshots/multi_asset_release/tests/test_release_underflow_panics.1.json @@ -0,0 +1,213 @@ +{ + "generators": { + "address": 4, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF", + { + "function": { + "contract_fn": { + "contract_address": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN", + "function_name": "set_admin", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 26, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "account": { + "account_id": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF", + "balance": "0", + "seq_num": "0", + "num_sub_entries": 0, + "inflation_dest": null, + "flags": 0, + "home_domain": "", + "thresholds": "01010101", + "signers": [], + "ext": "v0" + } + }, + "ext": "v0" + }, + "live_until": null + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + "live_until": 6311999 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": "stellar_asset", + "storage": [ + { + "key": { + "symbol": "METADATA" + }, + "val": { + "map": [ + { + "key": { + "symbol": "decimal" + }, + "val": { + "u32": 7 + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF" + } + }, + { + "key": { + "symbol": "symbol" + }, + "val": { + "string": "aaa" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "vec": [ + { + "symbol": "AssetInfo" + } + ] + }, + "val": { + "vec": [ + { + "symbol": "AlphaNum4" + }, + { + "map": [ + { + "key": { + "symbol": "asset_code" + }, + "val": { + "string": "aaa\\0" + } + }, + { + "key": { + "symbol": "issuer" + }, + "val": { + "bytes": "0000000000000000000000000000000000000000000000000000000000000004" + } + } + ] + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + "live_until": 120960 + } + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "set_admin" + }, + { + "address": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/campaign/test_snapshots/test/release_milestone_tests/test_multi_asset_release_underflow_panics.1.json b/campaign/test_snapshots/test/release_milestone_tests/test_multi_asset_release_underflow_panics.1.json new file mode 100644 index 0000000..da9f409 --- /dev/null +++ b/campaign/test_snapshots/test/release_milestone_tests/test_multi_asset_release_underflow_panics.1.json @@ -0,0 +1,822 @@ +{ + "generators": { + "address": 5, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [] + ], + "ledger": { + "protocol_version": 26, + "sequence_number": 0, + "timestamp": 31536000, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "account": { + "account_id": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF", + "balance": "0", + "seq_num": "0", + "num_sub_entries": 0, + "inflation_dest": null, + "flags": 0, + "home_domain": "", + "thresholds": "01010101", + "signers": [], + "ext": "v0" + } + }, + "ext": "v0" + }, + "live_until": null + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + "live_until": 6311999 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "AssetRaised" + }, + { + "address": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN" + } + ] + }, + "durability": "persistent", + "val": { + "i128": "1000" + } + } + }, + "ext": "v0" + }, + "live_until": 1036800 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "CampaignData" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "accepted_assets" + }, + "val": { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "asset_code" + }, + "val": { + "string": "XLM" + } + }, + { + "key": { + "symbol": "issuer" + }, + "val": { + "address": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN" + } + } + ] + } + ] + } + }, + { + "key": { + "symbol": "concluded_at_ledger" + }, + "val": "void" + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "created_at_time" + }, + "val": { + "u64": "31536000" + } + }, + { + "key": { + "symbol": "creator" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "end_time" + }, + "val": { + "u64": "31622400" + } + }, + { + "key": { + "symbol": "goal_amount" + }, + "val": { + "i128": "3000" + } + }, + { + "key": { + "symbol": "milestone_count" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "min_donation_amount" + }, + "val": { + "i128": "0" + } + }, + { + "key": { + "symbol": "raised_amount" + }, + "val": { + "i128": "3000" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Active" + } + ] + } + } + ] + } + } + }, + "ext": "v0" + }, + "live_until": 1036800 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "MilestoneData" + }, + { + "u32": 0 + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "description_hash" + }, + "val": { + "bytes": "0000000000000000000000000000000000000000000000000000000000000000" + } + }, + { + "key": { + "symbol": "index" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "release_tx" + }, + "val": "void" + }, + { + "key": { + "symbol": "released_amount" + }, + "val": { + "i128": "3000" + } + }, + { + "key": { + "symbol": "released_at" + }, + "val": "void" + }, + { + "key": { + "symbol": "released_at_ledger" + }, + "val": "void" + }, + { + "key": { + "symbol": "released_to" + }, + "val": "void" + }, + { + "key": { + "symbol": "status" + }, + "val": { + "vec": [ + { + "symbol": "Released" + } + ] + } + }, + { + "key": { + "symbol": "target_amount" + }, + "val": { + "i128": "3000" + } + } + ] + } + } + }, + "ext": "v0" + }, + "live_until": 1036800 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "ReentrancyLock" + } + ] + }, + "durability": "temporary", + "val": { + "bool": true + } + } + }, + "ext": "v0" + }, + "live_until": 15 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "TotalRaised" + } + ] + }, + "durability": "persistent", + "val": { + "i128": "1000" + } + } + }, + "ext": "v0" + }, + "live_until": 1036800 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + "live_until": 4095 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": "4837995959683129791" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + "live_until": 6311999 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + "live_until": 6311999 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + "live_until": 6311999 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "10009997000" + } + }, + { + "key": { + "symbol": "authorized" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "clawback" + }, + "val": { + "bool": false + } + } + ] + } + } + }, + "ext": "v0" + }, + "live_until": 518400 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": "3000" + } + }, + { + "key": { + "symbol": "authorized" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "clawback" + }, + "val": { + "bool": false + } + } + ] + } + } + }, + "ext": "v0" + }, + "live_until": 518400 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": "stellar_asset", + "storage": [ + { + "key": { + "symbol": "METADATA" + }, + "val": { + "map": [ + { + "key": { + "symbol": "decimal" + }, + "val": { + "u32": 7 + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF" + } + }, + { + "key": { + "symbol": "symbol" + }, + "val": { + "string": "aaa" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "vec": [ + { + "symbol": "AssetInfo" + } + ] + }, + "val": { + "vec": [ + { + "symbol": "AlphaNum4" + }, + { + "map": [ + { + "key": { + "symbol": "asset_code" + }, + "val": { + "string": "aaa\\0" + } + }, + { + "key": { + "symbol": "issuer" + }, + "val": { + "bytes": "0000000000000000000000000000000000000000000000000000000000000004" + } + } + ] + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + "live_until": 120960 + }, + { + "entry": { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + "live_until": 4095 + } + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "set_admin" + }, + { + "address": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "mint" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF" + } + ], + "data": { + "i128": "10000000" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "mint" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF" + } + ], + "data": { + "i128": "10000000000" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CCABDO7UZXYE4W6GVSEGSNNZTKSLFQGKXXQTH6OX7M7GKZ4Z6CUJNGZN", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "transfer" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJXFF" + } + ], + "data": { + "i128": "3000" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "milestone_released" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ], + "data": { + "vec": [ + { + "u32": 0 + }, + { + "i128": "3000" + }, + { + "string": "XLM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "u64": "31536000" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file