zephyr_sdk/
utils.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! Utilities for working with common data patterns.
//! 
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};

/// Returns an allocated String object starting from a Soroban SDK Address object.
pub fn address_to_alloc_string(env: &EnvClient, address: soroban_sdk::Address) -> String {
    soroban_string_to_alloc_string(env, address.to_string())
}

/// Builds an address from a slice.
pub fn address_from_str(env: &EnvClient, address: &str) -> Address {
    Address::from_string(&soroban_sdk::String::from_str(
        env.soroban(),
        address,
    ))
}

/// Returns an allocated String object starting from a Soroban SDK String object.
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()
}

/// Extract the instance storage map from an ScVal.
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
}

/// Convert Int128Parts into a native i128.
pub fn parts_to_i128(parts: &Int128Parts) -> i128 {
    ((parts.hi as i128) << 64) | (parts.lo as i128)
}

/// Converts a vector into an array.
/// Panics if the provided array size != vector's length.
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)?,
    )))
}

/// Hash a stellar transaction.
pub fn sha256(payload: &[u8]) -> [u8; 32] {
    Sha256::digest(payload).into()
}

/// Hash a stellar transaction.
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())
}

/// Sign any payload.
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())
}

/// Sign a stellar transaction.
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()
}

/// Builds an [`HashIdPreimage::SorobanAuthorization`] from the given nonce, signature, and invocation.
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,
    })
}

/// Pushes a key to the read-only footprint
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();
}

/// Pushes a key to the read-write footprint
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();
}

/// Helper to add both contract code and instance to the footprint.
/// Useful especially for smart accounts.
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()))
    }));
}