diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index d4a0d541f..8763fd7e6 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -4,9 +4,10 @@ use std::num::ParseIntError; use clap::{command, Parser}; use soroban_env_host::xdr::{ - self, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, Memo, MuxedAccount, - Operation, OperationBody, Preconditions, ScMetaEntry, ScMetaV0, SequenceNumber, Transaction, - TransactionExt, TransactionResult, TransactionResultResult, Uint256, VecM, + self, ContractCodeEntryExt, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, + LedgerEntryData, Limits, Memo, MuxedAccount, Operation, OperationBody, Preconditions, ReadXdr, + ScMetaEntry, ScMetaV0, SequenceNumber, Transaction, TransactionExt, TransactionResult, + TransactionResultResult, Uint256, VecM, }; use super::restore; @@ -127,8 +128,28 @@ impl NetworkRunnable for Cmd { let code_key = xdr::LedgerKey::ContractCode(xdr::LedgerKeyContractCode { hash: hash.clone() }); let contract_data = client.get_ledger_entries(&[code_key]).await?; - if !contract_data.entries.unwrap_or_default().is_empty() { - return Ok(hash); + // Skip install if the contract is already installed, and the contract has an extension version that isn't V0. + // In protocol 21 extension V1 was added that stores additional information about a contract making execution + // of the contract cheaper. So if folks want to reinstall we should let them which is why the install will still + // go ahead if the contract has a V0 extension. + if let Some(entries) = contract_data.entries { + if let Some(entry_result) = entries.first() { + let entry: LedgerEntryData = + LedgerEntryData::from_xdr_base64(&entry_result.xdr, Limits::none())?; + + match &entry { + LedgerEntryData::ContractCode(code) => { + // Skip reupload if this isn't V0 because V1 extension already + // exists. + if code.ext.ne(&ContractCodeEntryExt::V0) { + return Ok(hash); + } + } + _ => { + tracing::warn!("Entry retrieved should be of type ContractCode"); + } + } + } } let txn = client