Complete API reference for the SwiftRemit secure migration system.
Export complete contract state for migration.
pub fn export_migration_state(
env: Env,
caller: Address,
) -> Result<MigrationSnapshot, ContractError>Creates a cryptographically verified snapshot of all contract data including instance storage, persistent storage, and a SHA-256 verification hash.
env: Env- Soroban environmentcaller: Address- Admin address (must be authorized)
Ok(MigrationSnapshot)- Complete state snapshot with verification hashErr(ContractError)- Error if caller is not admin or export fails
| Error | Code | Condition |
|---|---|---|
Unauthorized |
14 | Caller is not admin |
NotInitialized |
2 | Contract not initialized |
- Requires admin authorization
- Caller must be registered admin
let snapshot = contract.export_migration_state(&admin)?;
println!("Exported {} remittances", snapshot.persistent_data.remittances.len());
println!("Verification hash: {:?}", snapshot.verification_hash);Import contract state from migration snapshot.
pub fn import_migration_state(
env: Env,
caller: Address,
snapshot: MigrationSnapshot,
) -> Result<(), ContractError>Restores complete contract state from a verified snapshot. Verifies cryptographic hash before importing to ensure data integrity.
env: Env- Soroban environmentcaller: Address- Admin address (must be authorized)snapshot: MigrationSnapshot- Complete migration snapshot to import
Ok(())- Import successfulErr(ContractError)- Error if verification fails or contract already initialized
| Error | Code | Condition |
|---|---|---|
AlreadyInitialized |
1 | Contract already has data |
InvalidMigrationHash |
20 | Hash verification failed |
Unauthorized |
14 | Caller is not admin |
- Requires caller authorization
- Contract must not be initialized
- Verifies SHA-256 hash before import
- Atomic operation (all or nothing)
- Replay protection through initialization check
// Deploy new contract
let new_contract = deploy_contract();
// Import state
let snapshot = get_snapshot_from_old_contract();
new_contract.import_migration_state(&admin, snapshot)?;
// Verify import
assert_eq!(new_contract.get_platform_fee_bps(), 250);Verify migration snapshot integrity without importing.
pub fn verify_migration_snapshot(
env: Env,
snapshot: MigrationSnapshot,
) -> MigrationVerificationValidates that a snapshot's cryptographic hash matches its contents. Useful for pre-import validation and auditing.
env: Env- Soroban environmentsnapshot: MigrationSnapshot- Snapshot to verify
MigrationVerification with:
valid: bool- Whether hash matchesexpected_hash: BytesN<32>- Hash from snapshotactual_hash: BytesN<32>- Computed hashtimestamp: u64- Verification time
- No authorization required (read-only)
let snapshot = contract.export_migration_state(&admin)?;
let verification = contract.verify_migration_snapshot(snapshot.clone())?;
if !verification.valid {
panic!("Snapshot integrity check failed!");
}
println!("✓ Snapshot verified");Export state in batches for large datasets.
pub fn export_migration_batch(
env: Env,
caller: Address,
batch_number: u32,
batch_size: u32,
) -> Result<MigrationBatch, ContractError>Exports a subset of remittances in a batch with its own verification hash. Useful for contracts with many remittances to avoid resource limits.
env: Env- Soroban environmentcaller: Address- Admin address (must be authorized)batch_number: u32- Which batch to export (0-indexed)batch_size: u32- Number of items per batch (1-100)
Ok(MigrationBatch)- Batch with remittances and verification hashErr(ContractError)- Error if parameters invalid or caller not admin
| Error | Code | Condition |
|---|---|---|
InvalidAmount |
3 | Batch size is 0 or > 100 |
Unauthorized |
14 | Caller is not admin |
- Requires admin authorization
// Export in batches of 50
let batch_size = 50;
let total_remittances = contract.get_remittance_counter()?;
let total_batches = (total_remittances + batch_size - 1) / batch_size;
for batch_num in 0..total_batches {
let batch = contract.export_migration_batch(&admin, batch_num, batch_size)?;
println!("Batch {}/{}: {} remittances",
batch_num + 1, total_batches, batch.remittances.len());
}Import state from batch.
pub fn import_migration_batch(
env: Env,
caller: Address,
batch: MigrationBatch,
) -> Result<(), ContractError>Imports a single batch of remittances with hash verification. Batches should be imported in order (0, 1, 2, ...) for consistency.
env: Env- Soroban environmentcaller: Address- Admin address (must be authorized)batch: MigrationBatch- Batch to import with verification hash
Ok(())- Import successfulErr(ContractError)- Error if hash verification fails or caller not admin
| Error | Code | Condition |
|---|---|---|
InvalidMigrationHash |
20 | Batch hash verification failed |
Unauthorized |
14 | Caller is not admin |
- Requires admin authorization
- Verifies batch hash before import
- Detects tampering or corruption
// Import batches in order
let batches = export_all_batches(&old_contract, &admin)?;
for batch in batches {
new_contract.import_migration_batch(&admin, batch)?;
println!("Imported batch {}/{}", batch.batch_number + 1, batch.total_batches);
}Complete state snapshot with cryptographic verification.
#[contracttype]
pub struct MigrationSnapshot {
pub version: u32,
pub timestamp: u64,
pub ledger_sequence: u32,
pub instance_data: InstanceData,
pub persistent_data: PersistentData,
pub verification_hash: BytesN<32>,
}version: u32- Schema version for forward compatibility (currently 1)timestamp: u64- Unix timestamp when snapshot was createdledger_sequence: u32- Ledger sequence number when snapshot was createdinstance_data: InstanceData- Contract-level configurationpersistent_data: PersistentData- Per-entity dataverification_hash: BytesN<32>- SHA-256 hash of all data
Contract-level configuration data.
#[contracttype]
pub struct InstanceData {
pub admin: Address,
pub usdc_token: Address,
pub platform_fee_bps: u32,
pub remittance_counter: u64,
pub accumulated_fees: i128,
pub paused: bool,
pub admin_count: u32,
}admin: Address- Legacy admin addressusdc_token: Address- USDC token contract addressplatform_fee_bps: u32- Platform fee in basis points (0-10000)remittance_counter: u64- Global remittance counteraccumulated_fees: i128- Total accumulated platform feespaused: bool- Contract pause statusadmin_count: u32- Number of registered admins
Per-entity persistent storage data.
#[contracttype]
pub struct PersistentData {
pub remittances: Vec<Remittance>,
pub agents: Vec<Address>,
pub admin_roles: Vec<Address>,
pub settlement_hashes: Vec<u64>,
pub whitelisted_tokens: Vec<Address>,
}remittances: Vec<Remittance>- All remittances indexed by IDagents: Vec<Address>- Registered agent addressesadmin_roles: Vec<Address>- Admin role addressessettlement_hashes: Vec<u64>- Remittance IDs that have been settledwhitelisted_tokens: Vec<Address>- Whitelisted token addresses
Batch of remittances for incremental migration.
#[contracttype]
pub struct MigrationBatch {
pub batch_number: u32,
pub total_batches: u32,
pub remittances: Vec<Remittance>,
pub batch_hash: BytesN<32>,
}batch_number: u32- Batch number (0-indexed)total_batches: u32- Total number of batchesremittances: Vec<Remittance>- Remittances in this batchbatch_hash: BytesN<32>- SHA-256 hash of this batch
Result of snapshot verification.
#[contracttype]
pub struct MigrationVerification {
pub valid: bool,
pub expected_hash: BytesN<32>,
pub actual_hash: BytesN<32>,
pub timestamp: u64,
}valid: bool- Whether verification passedexpected_hash: BytesN<32>- Hash from snapshotactual_hash: BytesN<32>- Computed hashtimestamp: u64- Verification timestamp
Maximum number of items per batch.
pub const MAX_MIGRATION_BATCH_SIZE: u32 = 100;Prevents excessive resource consumption in a single transaction.
import { Contract } from '@stellar/stellar-sdk';
// Export from old contract
const oldContract = new Contract(oldContractId);
const snapshot = await oldContract.export_migration_state({ caller: admin });
// Verify snapshot
const verification = await oldContract.verify_migration_snapshot({
snapshot: snapshot
});
if (!verification.valid) {
throw new Error('Snapshot verification failed');
}
// Deploy new contract
const newContract = await deployNewContract();
// Import state
await newContract.import_migration_state({
caller: admin,
snapshot: snapshot
});
console.log('Migration complete!');from stellar_sdk import SorobanServer, Contract
# Export from old contract
old_contract = Contract(old_contract_id)
snapshot = old_contract.export_migration_state(caller=admin)
# Verify snapshot
verification = old_contract.verify_migration_snapshot(snapshot=snapshot)
assert verification['valid'], "Snapshot verification failed"
# Deploy new contract
new_contract = deploy_new_contract()
# Import state
new_contract.import_migration_state(caller=admin, snapshot=snapshot)
print("Migration complete!")use soroban_sdk::{contract, contractimpl, Address, Env};
#[contract]
pub struct MigrationHelper;
#[contractimpl]
impl MigrationHelper {
pub fn migrate(
env: Env,
old_contract: Address,
new_contract: Address,
admin: Address,
) -> Result<(), Error> {
let old = SwiftRemitContractClient::new(&env, &old_contract);
let new = SwiftRemitContractClient::new(&env, &new_contract);
// Export
let snapshot = old.export_migration_state(&admin)?;
// Verify
let verification = old.verify_migration_snapshot(snapshot.clone())?;
if !verification.valid {
return Err(Error::InvalidSnapshot);
}
// Import
new.import_migration_state(&admin, snapshot)?;
Ok(())
}
}| Code | Error | Description | Recovery |
|---|---|---|---|
| 1 | AlreadyInitialized | Contract already has data | Deploy fresh contract |
| 2 | NotInitialized | Contract not initialized | Initialize contract first |
| 3 | InvalidAmount | Batch size invalid | Use 1-100 batch size |
| 14 | Unauthorized | Caller is not admin | Use admin address |
| 20 | InvalidMigrationHash | Hash verification failed | Re-export snapshot |
| 21 | MigrationInProgress | Migration already active | Wait for completion |
| 22 | InvalidMigrationBatch | Batch invalid | Check batch number/order |
let snapshot = old_contract.export_migration_state(&admin)?;
let verification = old_contract.verify_migration_snapshot(snapshot.clone())?;
if !verification.valid {
return Err("Snapshot verification failed");
}
new_contract.import_migration_state(&admin, snapshot)?;if remittance_count > 100 {
// Use batch migration
let batch_size = 50;
for batch_num in 0..total_batches {
let batch = old_contract.export_migration_batch(&admin, batch_num, batch_size)?;
new_contract.import_migration_batch(&admin, batch)?;
}
} else {
// Use full migration
let snapshot = old_contract.export_migration_state(&admin)?;
new_contract.import_migration_state(&admin, snapshot)?;
}// Pause old contract
old_contract.pause(&admin)?;
// Perform migration
let snapshot = old_contract.export_migration_state(&admin)?;
new_contract.import_migration_state(&admin, snapshot)?;
// Verify new contract
verify_migration_success(&old_contract, &new_contract)?;// Test migration on testnet
let test_snapshot = test_old_contract.export_migration_state(&admin)?;
test_new_contract.import_migration_state(&admin, test_snapshot)?;
// Verify test migration
assert_migration_success(&test_old_contract, &test_new_contract)?;
// If successful, proceed to mainnet
let mainnet_snapshot = mainnet_old_contract.export_migration_state(&admin)?;
mainnet_new_contract.import_migration_state(&admin, mainnet_snapshot)?;| Operation | Estimated Gas | Notes |
|---|---|---|
| export_migration_state (100 items) | ~500,000 | Scales with data |
| import_migration_state (100 items) | ~800,000 | Includes verification |
| verify_migration_snapshot | ~100,000 | Read-only |
| export_migration_batch (50 items) | ~250,000 | Per batch |
| import_migration_batch (50 items) | ~400,000 | Per batch |
- Use batch size of 50-100 for optimal gas efficiency
- Export batches in parallel (read-only operations)
- Import batches sequentially (write operations)
- Verify once before importing all batches
For issues and questions:
- Documentation: MIGRATION.md
- API Reference: This document
- GitHub Issues: Create an issue