use ed25519_dalek::{ed25519::signature::{Keypair, SignerMut}, SigningKey, VerifyingKey};
use sha2::{Sha256, Digest};
use soroban_sdk::{xdr::{self, DecoratedSignature, Hash, HashIdPreimage, HashIdPreimageSorobanAuthorization, Int128Parts, LedgerFootprint, LedgerKey, Limits, ScMapEntry, ScString, ScSymbol, ScVal, ScVec, Signature, SignatureHint, SorobanAuthorizedInvocation, Transaction, TransactionEnvelope, TransactionSignaturePayload, TransactionSignaturePayloadTaggedTransaction, TransactionV1Envelope, VecM, WriteXdr}, Address};
use crate::{EnvClient, SdkError};
pub fn address_to_alloc_string(env: &EnvClient, address: soroban_sdk::Address) -> String {
soroban_string_to_alloc_string(env, address.to_string())
}
pub fn address_from_str(env: &EnvClient, address: &str) -> Address {
Address::from_string(&soroban_sdk::String::from_str(
env.soroban(),
address,
))
}
pub fn soroban_string_to_alloc_string(env: &EnvClient, string: soroban_sdk::String) -> String {
let soroban_string = env.to_scval(string);
let ScVal::String(ScString(string)) = soroban_string else {
panic!()
};
string.try_into().unwrap()
}
pub fn instance_entries(val: &ScVal) -> Option<Vec<ScMapEntry>> {
if let ScVal::ContractInstance(instance) = val {
if let Some(map) = &instance.storage {
return Some(map.to_vec());
}
}
None
}
pub fn parts_to_i128(parts: &Int128Parts) -> i128 {
((parts.hi as i128) << 64) | (parts.lo as i128)
}
pub fn to_array<T, const N: usize>(v: Vec<T>) -> [T; N] {
v.try_into().unwrap_or_else(|v: Vec<T>| {
panic!("Expected a Vec of length {} but it was {}", N, v.len())
})
}
#[allow(missing_docs)]
pub fn to_datakey_u32(int: u32) -> ScVal {
ScVal::U32(int)
}
#[allow(missing_docs)]
pub fn to_datakey_symbol(variant_str: &str) -> ScVal {
let tot_s_val = ScVal::Symbol(ScSymbol(variant_str.to_string().try_into().unwrap()));
ScVal::Vec(Some(ScVec(VecM::try_from(vec![tot_s_val]).unwrap())))
}
#[allow(missing_docs)]
pub fn to_scval_symbol(from: &str) -> Result<ScVal, SdkError> {
Ok(ScVal::Symbol(ScSymbol(
from.try_into().map_err(|_| SdkError::Conversion)?,
)))
}
pub fn sha256(payload: &[u8]) -> [u8; 32] {
Sha256::digest(payload).into()
}
pub fn hash_transaction(tx: &Transaction, network_passphrase: &str) -> Result<[u8; 32], xdr::Error> {
let signature_payload = TransactionSignaturePayload {
network_id: Hash(Sha256::digest(network_passphrase).into()),
tagged_transaction: TransactionSignaturePayloadTaggedTransaction::Tx(tx.clone()),
};
Ok(Sha256::digest(signature_payload.to_xdr(Limits::none())?).into())
}
pub fn ed25519_sign(secret_key: &str, payload: &[u8]) -> (VerifyingKey, [u8; 64]) {
let mut signing = SigningKey::from_bytes(
&stellar_strkey::ed25519::PrivateKey::from_string(secret_key)
.unwrap()
.0,
);
(signing.verifying_key(), signing.sign(payload).to_bytes().try_into().unwrap())
}
pub fn sign_transaction(tx: Transaction, network_passphrase: &str, secret_key: &str) -> String {
let tx_hash = hash_transaction(&tx, network_passphrase).unwrap();
let (verifying, tx_signature) = ed25519_sign(secret_key, &tx_hash);
let decorated_signature = DecoratedSignature {
hint: SignatureHint(verifying.to_bytes()[28..].try_into().unwrap()),
signature: Signature(tx_signature.try_into().unwrap()),
};
let envelope = TransactionEnvelope::Tx(TransactionV1Envelope {
tx: tx.clone(),
signatures: [decorated_signature].try_into().unwrap(),
});
envelope.to_xdr_base64(Limits::none()).unwrap()
}
pub fn build_authorization_preimage(nonce: i64, signature_expiration_ledger: u32, invocation: SorobanAuthorizedInvocation) -> HashIdPreimage {
HashIdPreimage::SorobanAuthorization(HashIdPreimageSorobanAuthorization {
network_id: xdr::Hash(Sha256::digest("Test SDF Network ; September 2015").into()),
nonce,
signature_expiration_ledger,
invocation,
})
}
pub fn footprint_read_push(footprint: &mut LedgerFootprint, key: LedgerKey) {
let mut read = footprint.read_only.to_vec();
read.push(key);
footprint.read_only = read.try_into().unwrap();
}
pub fn footprint_read_write_push(footprint: &mut LedgerFootprint, key: LedgerKey) {
let mut read = footprint.read_write.to_vec();
read.push(key);
footprint.read_write = read.try_into().unwrap();
}
pub fn add_contract_to_footprint(footprint: &mut LedgerFootprint, contract_id: &str, wasm_hash: &[u8]) {
footprint_read_push(footprint, LedgerKey::ContractData(xdr::LedgerKeyContractData {
contract: xdr::ScAddress::Contract(xdr::Hash(stellar_strkey::Contract::from_string(contract_id).unwrap().0)),
key: xdr::ScVal::LedgerKeyContractInstance,
durability: xdr::ContractDataDurability::Persistent }));
footprint_read_push(footprint, xdr::LedgerKey::ContractCode(xdr::LedgerKeyContractCode {
hash: xdr::Hash(to_array(wasm_hash.to_vec()))
}));
}