Network SAC Contracts
This program monitors the chain for every time a Stellar Asset contract is wrapped, enabling Soroban usage of that token.
It also pushes to a table the total amount of deployed SACs by ledger, allowing to potentially see the trend in the growth of wrapped assets.
This table is public and can be queried as follows through the Mercury endpoint:
query SACTrend {
allZephyr263707E6Cf91Cece33596A294D312A5Ds {
edges {
node {
sequence
number
}
}
}
}
query AllSACs {
allZephyrB8978D969636A31Fe5E95Efe542A1536S {
edges {
node {
contract
asset
}
}
}
}
#![allow(unused)] fn main() { use rs_zephyr_sdk::{ stellar_xdr::next::{ ContractIdPreimage, FeeBumpTransactionInnerTx, HostFunction, LedgerKey, Limits, OperationBody, ScAddress, TransactionEnvelope, TransactionExt, TransactionResultMeta, TransactionResultResult, TransactionV1Envelope, WriteXdr, }, EnvClient, }; struct CreatedSAC { contract_id: [u8; 32], asset: Vec<u8>, } #[no_mangle] pub extern "C" fn on_close() { let mut env = EnvClient::default(); let reader = env.reader(); let envelopes = reader.envelopes(); let processing = reader.tx_processing(); let current_ledger = reader.ledger_sequence(); let mut created = Vec::new(); for (idx, envelope) in envelopes.iter().enumerate() { match envelope { TransactionEnvelope::Tx(tx) => write_from_v1(idx, tx, &processing, &mut created), // v0 txs cannot inlcude soroban data TransactionEnvelope::TxV0(_) => (), TransactionEnvelope::TxFeeBump(tx) => match &tx.tx.inner_tx { FeeBumpTransactionInnerTx::Tx(tx) => { write_from_v1(idx, &tx, &processing, &mut created) } }, } } // add all created SACs to the database. for sac in &created { env.db_write( "sacs", &["contract", "asset"], &[&sac.contract_id, &sac.asset], ) .unwrap(); } // if no SAC was deployed add new record into historical trend let num_created = created.len() as i64; if num_created > 0 { let previous_sacs = env.db_read("sac_count", &["number"]); if let Ok(rows) = previous_sacs { if let Some(last) = rows.rows.last() { let mut byte_array: [u8; 8] = [0; 8]; let int = &last.row[0].0; byte_array.copy_from_slice(&int[..int.len()]); let tot_sacs = i64::from_be_bytes(byte_array) + num_created; env.db_write( "sac_count", &["sequence", "number"], &[¤t_ledger.to_be_bytes(), &tot_sacs.to_be_bytes()], ) .unwrap() } else { env.db_write( "sac_count", &["sequence", "number"], &[¤t_ledger.to_be_bytes(), &num_created.to_be_bytes()], ) .unwrap() } } } } fn write_from_v1( idx: usize, tx: &TransactionV1Envelope, processing: &Vec<TransactionResultMeta>, created: &mut Vec<CreatedSAC>, ) { match &tx.tx.operations.get(0).unwrap().body { // we search for create SAC operations OperationBody::InvokeHostFunction(op) => { if let HostFunction::CreateContract(create_contract) = &op.host_function { if let ContractIdPreimage::Asset(asset) = &create_contract.contract_id_preimage { let matching_processing = processing.get(idx).unwrap(); // we make sure that the tx was successful if let TransactionResultResult::TxSuccess(_) = matching_processing.result.result.result { if let TransactionExt::V1(soroban) = &tx.tx.ext { if let LedgerKey::ContractData(data) = &soroban.resources.footprint.read_write[0] { if let ScAddress::Contract(contract) = &data.contract { created.push(CreatedSAC { contract_id: contract.0, asset: asset.to_xdr(Limits::none()).unwrap(), }); } } } } } } } _ => (), } } }