use crate::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct EvmTransaction {
pub expiration_time_seconds: u64,
pub gas_limit: String,
pub value: String,
pub to: String,
pub from: String,
pub data: String,
}
#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct EVMFunctionResultV0 {
pub txs: Vec<EvmTransaction>,
pub signatures: Vec<Vec<u8>>,
pub call_ids: Vec<Vec<u8>>,
pub checksums: Vec<Vec<u8>>,
}
#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct EVMFunctionResultV1 {
pub function_id: String,
pub signer: String,
pub txs: Vec<EvmTransaction>,
pub signatures: Vec<String>,
pub resolved_ids: Vec<String>,
pub checksums: Vec<String>,
pub error_codes: Vec<u8>,
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
#[serde(tag = "version")]
pub enum EVMFunctionResult {
V0(EVMFunctionResultV0),
V1(EVMFunctionResultV1),
}
impl Default for EVMFunctionResult {
fn default() -> Self {
Self::V1(EVMFunctionResultV1::default())
}
}
#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct SOLFunctionResultV0 {
pub serialized_tx: Vec<u8>,
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
#[serde(tag = "version")]
pub enum SOLFunctionResult {
V0(SOLFunctionResultV0),
}
impl Default for SOLFunctionResult {
fn default() -> Self {
Self::V0(SOLFunctionResultV0::default())
}
}
#[derive(Default, PartialEq, Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "chain")]
pub enum ChainResultInfo {
#[default]
None,
Solana(SOLFunctionResult),
Evm(EVMFunctionResult),
}
#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)]
pub struct FunctionResultV0 {
pub quote: Vec<u8>,
pub fn_key: Vec<u8>,
pub signer: Vec<u8>,
pub fn_request_key: Vec<u8>,
pub fn_request_hash: Vec<u8>,
pub chain_result_info: ChainResultInfo,
#[serde(default)]
pub error_code: u8,
}
#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)]
pub struct FunctionResultV1 {
pub quote: Vec<u8>,
pub chain_result_info: ChainResultInfo,
pub signature: Vec<u8>,
#[serde(default)]
pub error_code: u8,
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
#[serde(tag = "version")]
pub enum FunctionResult {
V0(FunctionResultV0),
V1(FunctionResultV1),
}
impl Default for FunctionResult {
fn default() -> Self {
Self::V1(FunctionResultV1::default())
}
}
pub static FUNCTION_RESULT_PREFIX: &str = "FN_OUT: ";
impl FunctionResult {
pub fn emit(&self) {
println!(
"{}{}",
FUNCTION_RESULT_PREFIX,
hex::encode(serde_json::to_string(&self).unwrap())
);
}
pub fn decode(s: &str) -> std::result::Result<Self, SbError> {
let stripped = s.strip_prefix(FUNCTION_RESULT_PREFIX);
if stripped.is_none() {
return Err("String does not start with 'FN_OUT: '".into());
}
let stripped = stripped.unwrap();
let decoded = hex::decode(stripped)?;
match serde_json::from_slice::<FunctionResult>(&decoded) {
Ok(deserialized) => return Ok(deserialized),
Err(e) => log::info!("Failed to decode FunctionResult: {:?}", e),
};
match serde_json::from_slice::<LegacyFunctionResult>(&decoded) {
Ok(deserialized) => return Ok(deserialized.into()),
Err(e) => log::info!("Failed to decode LegacyFunctionResult: {:?}", e),
};
Err(SbError::CustomMessage(format!(
"Failed to decode FunctionResult string {:?}",
String::from_utf8(decoded).unwrap_or_default()
)))
}
}
impl From<LegacyFunctionResult> for FunctionResult {
fn from(item: LegacyFunctionResult) -> FunctionResult {
FunctionResult::V0(FunctionResultV0 {
quote: item.quote,
fn_key: item.fn_key,
signer: item.signer,
fn_request_key: item.fn_request_key,
fn_request_hash: item.fn_request_hash,
chain_result_info: item.chain_result_info.into(),
error_code: item.error_code,
})
}
}
#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)]
pub struct LegacyFunctionResult {
pub version: u32,
pub quote: Vec<u8>,
pub fn_key: Vec<u8>,
pub signer: Vec<u8>,
pub fn_request_key: Vec<u8>,
pub fn_request_hash: Vec<u8>,
pub chain_result_info: LegacyChainResultInfo,
#[serde(default)]
pub error_code: u8,
}
#[derive(Default, PartialEq, Clone, Debug, Serialize, Deserialize)]
pub enum LegacyChainResultInfo {
#[default]
None,
Solana(SOLFunctionResult),
Evm(EVMFunctionResult),
}
impl From<LegacyChainResultInfo> for ChainResultInfo {
fn from(item: LegacyChainResultInfo) -> ChainResultInfo {
match item {
LegacyChainResultInfo::Solana(sol) => ChainResultInfo::Solana(sol),
LegacyChainResultInfo::Evm(evm) => ChainResultInfo::Evm(evm),
_ => ChainResultInfo::None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
pub const EMPTY_ENCODED_FN_RESULT: &str = "7b2276657273696f6e223a302c2271756f7465223a5b5d2c22666e5f6b6579223a5b5d2c227369676e6572223a5b5d2c22666e5f726571756573745f6b6579223a5b5d2c22666e5f726571756573745f68617368223a5b5d2c22636861696e5f726573756c745f696e666f223a224e6f6e65222c226572726f725f636f6465223a307d";
pub const SOL_ENCODED_FN_RESULT: &str = "7b2276657273696f6e223a302c2271756f7465223a5b5d2c22666e5f6b6579223a5b5d2c227369676e6572223a5b5d2c22666e5f726571756573745f6b6579223a5b5d2c22666e5f726571756573745f68617368223a5b5d2c22636861696e5f726573756c745f696e666f223a7b22536f6c616e61223a7b2273657269616c697a65645f7478223a5b312c322c335d7d7d2c226572726f725f636f6465223a307d";
#[test]
fn test_legacy_decode() {
let _ = simple_logger::init_with_level(log::Level::Debug);
let decoded =
FunctionResult::decode(&format!("FN_OUT: {}", EMPTY_ENCODED_FN_RESULT)).unwrap();
assert_eq!(decoded, FunctionResult::default());
}
#[test]
fn test_decode() {
let _ = simple_logger::init_with_level(log::Level::Debug);
let fr = FunctionResult::default();
let encoded = format!(
"FN_OUT: {}",
hex::encode(serde_json::to_string(&fr).unwrap())
);
println!("Encoded: {:?}", encoded);
let decoded = FunctionResult::decode(&encoded).unwrap();
println!("Decoded: {:?}", decoded);
assert_eq!(decoded, FunctionResult::default());
}
#[test]
fn test_evm_v0_decode() {
let _ = simple_logger::init_with_level(log::Level::Debug);
let evm_result = EVMFunctionResultV0::default();
let fr = FunctionResult::V0(FunctionResultV0 {
quote: vec![],
fn_key: vec![],
signer: vec![],
fn_request_key: vec![],
fn_request_hash: vec![],
chain_result_info: ChainResultInfo::Evm(EVMFunctionResult::V0(evm_result)),
error_code: 0,
});
let encoded = format!(
"FN_OUT: {}",
hex::encode(serde_json::to_string(&fr).unwrap())
);
println!("Encoded: {:?}", encoded);
let decoded = FunctionResult::decode(&encoded).unwrap();
println!("Decoded: {:?}", decoded);
match decoded {
FunctionResult::V0(FunctionResultV0 {
chain_result_info:
ChainResultInfo::Evm(EVMFunctionResult::V0(decoded_evm_v0_result)),
..
}) => {
assert_eq!(decoded_evm_v0_result, EVMFunctionResultV0::default());
}
_ => panic!("Expected EVMFunctionResultV0"),
}
}
}