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 gateway_client = MessageGatewayV4Client::new(env, &gateway_address);
gateway_client.process_fee(&env.current_contract_address());
let request = SendRequest {
recipient: chain_endpoint,
destination_chain,
chain_data,
confirmations,
};
let tx_id = gateway_client.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 set_message_gateway(env: &Env, contract_address: 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 get_message_gateway(env: &Env) -> Address;
fn get_message_owner(env: &Env) -> Address;
fn validate_chain_sender(env: &Env, source_chain_id: u64, sender: Bytes);
fn message_send(env: &Env, destination_chain: u64, chain_data: Bytes, confirmations: u32)
-> u128;
fn message_process(env: &Env, message: ProcessFromGatewayRequest);
fn message_process_from_gateway(env: &Env, message: ProcessFromGatewayRequest);
}