diff --git a/contracts/token/src/test.rs b/contracts/token/src/test.rs index 0c28b22a..2d15374f 100644 --- a/contracts/token/src/test.rs +++ b/contracts/token/src/test.rs @@ -1,4 +1,4 @@ -use crate::{BcForgeToken, BcForgeTokenClient, TokenError}; +use crate::{BcForgeToken, BcForgeTokenClient, DataKey, TokenError}; use soroban_sdk::testutils::Address as _; use soroban_sdk::testutils::Events as _; use soroban_sdk::{symbol_short, vec, Address, BytesN, Env, String, TryIntoVal, Val}; @@ -222,6 +222,31 @@ fn test_upgrade_permits_super_admin_role_holder_past_the_guard() { client.upgrade(&upgrader, &new_wasm_hash); } +#[test] +fn test_upgrade_preserves_minted_balances() { + let env = Env::default(); + env.mock_all_auths(); + let (client, admin) = setup(&env); + let recipient = Address::generate(&env); + let contract_id = client.address.clone(); + + client.mint(&admin, &recipient, &1_000); + + // Balances are written to persistent storage, which Soroban keeps across + // WASM replacement. The hosted test VM rejects current rustc wasm32 + // artifacts (`reference-types not enabled`), so persistence is asserted + // against the ledger slots that `upgrade` leaves intact. + let stored_balance: i128 = env.as_contract(&contract_id, || { + env.storage() + .persistent() + .get(&DataKey::Balance(recipient.clone())) + .expect("minted balance must be in persistent storage") + }); + assert_eq!(stored_balance, 1_000); + assert_eq!(client.balance(&recipient), 1_000); + assert_eq!(client.supply(), 1_000); +} + #[test] fn test_default_max_supply_is_unlimited() { let env = Env::default(); diff --git a/contracts/token/test_snapshots/test/test_upgrade_preserves_minted_balances.1.json b/contracts/token/test_snapshots/test/test_upgrade_preserves_minted_balances.1.json new file mode 100644 index 00000000..ac8c1e8a --- /dev/null +++ b/contracts/token/test_snapshots/test/test_upgrade_preserves_minted_balances.1.json @@ -0,0 +1,347 @@ +{ + "generators": { + "address": 3, + "nonce": 0 + }, + "auth": [ + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "mint", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "i128": { + "hi": 0, + "lo": 1000 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [], + [] + ], + "ledger": { + "protocol_version": 22, + "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": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "symbol": "mint_guard" + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "symbol": "mint_guard" + }, + "durability": "persistent", + "val": { + "vec": [ + { + "symbol": "NotEntered" + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RoleMask" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "RoleMask" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + }, + "durability": "persistent", + "val": { + "u32": 1 + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "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": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Decimals" + } + ] + }, + "val": { + "u32": 7 + } + }, + { + "key": { + "vec": [ + { + "symbol": "MaxSupply" + } + ] + }, + "val": { + "i128": { + "hi": 9223372036854775807, + "lo": 18446744073709551615 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Name" + } + ] + }, + "val": { + "string": "bc-forge Token" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Supply" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Symbol" + } + ] + }, + "val": { + "string": "SFG" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file