vialabs-stellar-common 0.1.0

Common interfaces, types, and utilities for Stellar contracts in the VIA cross-chain messaging system
Documentation
use crate::{
  errors::Error,
  message_gateway_v4::{MessageGatewayV4Client, SendRequest},
  storage::{DataKey, GATEWAY_CONTRACT_ADDRESS},
  utils::is_zero_address,
};
use soroban_sdk::{
  contracttype, panic_with_error, symbol_short, vec, xdr::ToXdr, Address, Bytes, Env, IntoVal,
  Symbol, Vec,
};

const MESSAGE_OWNER_KEY: Symbol = symbol_short!("owner");

#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct ProcessFromGatewayRequest {
  pub tx_id: u128,
  pub source_chain_id: u64,
  pub sender: Bytes,
  pub recipient: Address,
  pub on_chain_data: Bytes,
  pub off_chain_data: Bytes,
  pub gas_fee: u64,
}

pub struct Base;

impl Base {
  pub fn get_message_owner(env: &Env) -> Address {
    env.storage().instance().get(&MESSAGE_OWNER_KEY).unwrap()
  }

  pub fn set_message_owner(env: &Env, owner: Address) {
    env.storage().instance().set(&MESSAGE_OWNER_KEY, &owner);
  }

  pub fn only_message_owner(env: &Env) {
    let owner = Self::get_message_owner(env);

    owner.require_auth();
  }

  pub fn set_message_gateway(env: &Env, contract_address: Address) {
    Self::only_message_owner(env);

    env
      .storage()
      .instance()
      .set(&GATEWAY_CONTRACT_ADDRESS, &contract_address);
  }

  pub fn get_message_gateway(env: &Env) -> Address {
    env
      .storage()
      .instance()
      .get(&GATEWAY_CONTRACT_ADDRESS)
      .unwrap_or_else(|| panic_with_error!(env, Error::MissingMessageGateway))
  }

  pub fn set_message_endpoints(env: &Env, chains: Vec<u64>, endpoints: Vec<Bytes>) {
    Self::only_message_owner(env);

    let chains_length = chains.len();

    if chains_length != endpoints.len() {
      panic_with_error!(env, Error::ChainsEndpointsMislength);
    }

    for id in 0..chains_length {
      env.storage().instance().set(
        &DataKey::ChainsEndpoints(chains.get(id).unwrap()),
        &endpoints.get(id),
      );
    }
  }

  pub fn get_endpoint_by_chain_id(env: &Env, chain_id: u64) -> Bytes {
    env
      .storage()
      .instance()
      .get(&DataKey::ChainsEndpoints(chain_id))
      .unwrap_or(Bytes::new(env))
  }

  pub fn message_send(
    env: &Env,
    destination_chain: u64,
    chain_data: Bytes,
    confirmations: u32,
  ) -> u128 {
    let gateway_address = Self::get_message_gateway(env);

    if is_zero_address(env, gateway_address.clone().to_xdr(env)) {
      panic_with_error!(env, Error::MissingMessageGateway);
    }

    let chain_endpoint = Self::get_endpoint_by_chain_id(env, destination_chain);

    if chain_endpoint.is_empty() {
      panic_with_error!(env, Error::MissingChainEndpoint);
    }

    let request = SendRequest {
      sender: env.current_contract_address().to_xdr(env),
      recipient: chain_endpoint,
      destination_chain,
      chain_data,
      confirmations,
    };

    let tx_id = MessageGatewayV4Client::new(env, &gateway_address).send(&request);

    tx_id
  }

  pub fn validate_chain_sender(env: &Env, source_chain_id: u64, sender: Bytes) {
    let chain_endpoint = Self::get_endpoint_by_chain_id(env, source_chain_id);

    if chain_endpoint.len() != sender.len() {
      panic_with_error!(env, Error::SenderLengthMismatch);
    }

    let endpoint_hash = env.crypto().keccak256(&chain_endpoint);
    let sender_hash = env.crypto().keccak256(&sender);

    if sender_hash.to_array() != endpoint_hash.to_array() {
      panic_with_error!(env, Error::InvalidChainSender);
    }
  }

  pub fn message_process(env: &Env, request: ProcessFromGatewayRequest) {
    let method = Symbol::new(env, "message_process");
    let args = vec![env, request.clone().into_val(env)];

    env.invoke_contract::<()>(&request.recipient, &method, args);
  }

  pub fn message_process_from_gateway(env: &Env, request: ProcessFromGatewayRequest) {
    Self::message_process(env, request);
  }
}

pub trait MessageClientV4Interface {
  fn only_message_owner(env: &Env);

  fn set_message_owner(env: &Env, owner: Address);

  fn get_message_owner(env: &Env) -> Address;

  fn set_message_gateway(env: &Env, contract_address: Address);

  fn get_message_gateway(env: &Env) -> Address;

  fn set_message_endpoints(env: &Env, chains: Vec<u64>, endpoints: Vec<Bytes>);

  fn get_endpoint_by_chain_id(env: &Env, chain_id: u64) -> Bytes;

  fn message_send(env: &Env, destination_chain: u64, chain_data: Bytes, confirmations: u32)
    -> u128;

  fn validate_chain_sender(env: &Env, source_chain_id: u64, sender: Bytes);

  fn message_process(env: &Env, message: ProcessFromGatewayRequest);

  fn message_process_from_gateway(env: &Env, message: ProcessFromGatewayRequest);
}