Skip to content

Commit

Permalink
docs: add docs to contract interfaces (#175)
Browse files Browse the repository at this point in the history
Co-authored-by: Milap Sheth <milap@interoplabs.io>
  • Loading branch information
TanvirDeol and milapsheth authored Jan 28, 2025
1 parent 4da911b commit 2f17e32
Show file tree
Hide file tree
Showing 16 changed files with 323 additions and 48 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 47 additions & 6 deletions contracts/stellar-axelar-gas-service/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,23 @@ pub trait AxelarGasServiceInterface: OperatableInterface {
/// Pay for gas using a token for sending a message on a destination chain.
///
/// This function is called on the source chain before calling the gateway to send a message.
///
/// `sender` refers to the address that sent the cross-chain message via the `axelar_gateway`.
/// The `spender` pays the gas but might differ from the `sender`,
/// e.g. the `sender` is a contract, but the `spender` can be the user signing the transaction.
///
/// # Arguments
/// * `sender` - The address initiating the gas payment. It's the address that sent the cross-chain message via the `axelar_gateway`.
/// * `destination_chain` - The destination chain for the message.
/// * `destination_address` - The destination contract address for the message.
/// * `payload` - The payload data associated with the message.
/// * `spender` - The address of the spender paying for the gas. Might differ from the `sender`. Excess gas will be refunded to this address.
/// * `token` - The token used to pay for the gas, including the address and amount.
/// * `metadata` - Additional metadata associated with the gas payment.
///
/// # Errors
/// - [`ContractError::InvalidAmount`]: If the token amount is zero or negative.
///
/// # Authorization
/// - The `spender` address must authorize the token transfer to the gas service.
fn pay_gas(
env: Env,
sender: Address,
Expand All @@ -26,9 +39,20 @@ pub trait AxelarGasServiceInterface: OperatableInterface {

/// Adds additional gas payment after initiating a cross-chain message.
///
/// `sender` refers to the address that sent the cross-chain message via the `axelar_gateway`.
/// The `spender` pays the gas but might differ from the `sender`,
/// e.g. the `sender` is a contract, but the `spender` can be the user signing the transaction.
///
/// # Arguments
/// * `sender` - The address that sent the cross-chain message.
/// * `message_id` - The identifier of the message for which gas is being added.
/// * `spender` - The address of the spender paying for the gas.
/// * `token` - The token used to pay for the gas, including the address and amount.
///
/// # Errors
/// - [`ContractError::InvalidAmount`]: If the token amount is zero or negative.
///
/// # Authorization
/// - The `spender` address must authorize.
fn add_gas(
env: Env,
sender: Address,
Expand All @@ -37,13 +61,30 @@ pub trait AxelarGasServiceInterface: OperatableInterface {
token: Token,
) -> Result<(), ContractError>;

/// Collects gas fees and transfers them to a specified receiver.
///
/// Allows the `gas_collector` to collect accumulated fees from the contract.
///
/// Only callable by the `operator`.
/// # Arguments
/// * `receiver` - The address that will receive the collected feeds.
/// * `token` - The token address and amount for the fee collection.
///
/// # Errors
/// - [`ContractError::InvalidAmount`]: If the token amount is zero or negative.
/// - [`ContractError::InsufficientBalance`]: If the contract's token balance is insufficient to cover the transfer.
///
/// # Authorization
/// - [`Self::operator`] must authorize.
fn collect_fees(env: Env, receiver: Address, token: Token) -> Result<(), ContractError>;

/// Refunds gas payment to the receiver in relation to a specific cross-chain message.
/// Refunds gas payment to the specified receiver in relation to a specific cross-chain message.
///
/// # Arguments
/// * `message_id` - The identifier of the cross-chain message for which the gas fees are being refunded.
/// * `receiver` - The address of the receiver to whom the gas fees will be refunded.
/// * `token` - The token used for the refund, including the address and amount.
///
/// Only callable by the `operator`.
/// # Authorization
/// - [`Self::operator`] must authorize.
fn refund(env: Env, message_id: String, receiver: Address, token: Token);
}
33 changes: 30 additions & 3 deletions contracts/stellar-axelar-gateway/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,40 @@ pub trait AxelarGatewayInterface:
/// Returns the minimum delay between rotations.
fn minimum_rotation_delay(env: &Env) -> u64;

/// Approves a collection of messages.
/// Approves a collection of messages with the provided proof.
///
/// This function allows the approval of multiple messages using a cryptographic proof.
/// It ensures that the messages are not empty and prevents replay attacks by checking
/// if the messages have already been approved or executed.
///
/// # Arguments
/// * `messages` - A vector of messages to be approved.
/// * `proof` - The cryptographic proof used to validate the approval.
///
/// # Errors
/// - [`ContractError::EmptyMessages`]: If the provided messages vector is empty.
/// - Any error from `auth::validate_proof` due to an invalid proof.
fn approve_messages(
env: &Env,
messages: Vec<Message>,
proof: Proof,
) -> Result<(), ContractError>;

/// Rotates the signers.
/// Rotates to `signers` if the `proof` is valid.
///
/// If `bypass_rotation_delay` is set to true, the `operator` must authorize the rotation.
///
/// # Arguments
/// * `signers` - The new set of weighted signers to be rotated in.
/// * `proof` - The cryptographic proof used to validate the rotation.
/// * `bypass_rotation_delay` - A boolean indicating whether to bypass the rotation delay.
///
/// # Errors
/// - [`ContractError::NotLatestSigners`]: If the provided signers are not the latest and `bypass_rotation_delay` is false.
/// - Any error from `auth::validate_proof` due to invalid proof.
///
/// # Authorization
/// - The `operator` must authorize if `bypass_rotation_delay` is true.
fn rotate_signers(
env: &Env,
signers: WeightedSigners,
Expand All @@ -42,7 +68,8 @@ pub trait AxelarGatewayInterface:
/// Returns the signers hash by epoch.
fn signers_hash_by_epoch(env: &Env, epoch: u64) -> Result<BytesN<32>, ContractError>;

/// Validate the `proof` for `data_hash` created by the signers. Returns a boolean indicating if the proof was created by the latest signers.
/// Validate the `proof` for `data_hash` created by the signers.
/// Returns a boolean indicating if the proof was created by the latest signers.
fn validate_proof(
env: &Env,
data_hash: BytesN<32>,
Expand Down
2 changes: 2 additions & 0 deletions contracts/stellar-axelar-operators/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ publish = true
crate-type = ["cdylib", "rlib"]

[features]
library = [] # Only export the contract interface
testutils = ["stellar-axelar-std/testutils"]

[dependencies]
cfg-if = { workspace = true }
soroban-sdk = { workspace = true }
stellar-axelar-std = { workspace = true }

Expand Down
20 changes: 8 additions & 12 deletions contracts/stellar-axelar-operators/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use stellar_axelar_std::{ensure, interfaces, Ownable, Upgradable};

use crate::error::ContractError;
use crate::event::{OperatorAddedEvent, OperatorRemovedEvent};
use crate::interface::AxelarOperatorsInterface;
use crate::storage_types::DataKey;

#[contract]
Expand All @@ -16,18 +17,17 @@ impl AxelarOperators {
pub fn __constructor(env: Env, owner: Address) {
interfaces::set_owner(&env, &owner);
}
}

/// Return true if the account is an operator.
pub fn is_operator(env: Env, account: Address) -> bool {
#[contractimpl]
impl AxelarOperatorsInterface for AxelarOperators {
fn is_operator(env: Env, account: Address) -> bool {
let key = DataKey::Operators(account);

env.storage().instance().has(&key)
}

/// Add an address as an operator.
///
/// Only callable by the contract owner.
pub fn add_operator(env: Env, account: Address) -> Result<(), ContractError> {
fn add_operator(env: Env, account: Address) -> Result<(), ContractError> {
Self::owner(&env).require_auth();

let key = DataKey::Operators(account.clone());
Expand All @@ -46,10 +46,7 @@ impl AxelarOperators {
Ok(())
}

/// Remove an address as an operator.
///
/// Only callable by the contract owner.
pub fn remove_operator(env: Env, account: Address) -> Result<(), ContractError> {
fn remove_operator(env: Env, account: Address) -> Result<(), ContractError> {
Self::owner(&env).require_auth();

let key = DataKey::Operators(account.clone());
Expand All @@ -66,8 +63,7 @@ impl AxelarOperators {
Ok(())
}

/// Execute a function on a contract as an operator.
pub fn execute(
fn execute(
env: Env,
operator: Address,
contract: Address,
Expand Down
65 changes: 65 additions & 0 deletions contracts/stellar-axelar-operators/src/interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use soroban_sdk::{contractclient, Address, Env, Symbol, Val, Vec};
use stellar_axelar_std::interfaces::OwnableInterface;

use crate::error::ContractError;

#[allow(dead_code)]
#[contractclient(name = "AxelarOperatorsClient")]
pub trait AxelarOperatorsInterface: OwnableInterface {
/// Return whether specified account is an operator.
fn is_operator(env: Env, account: Address) -> bool;

/// Add an address as an operator.
///
/// The operator is authorized to execute any third party contract via this contract.
/// An app can give a privileged role to this contract, which can then allow multiple operators
/// to call it, e.g. `refund` on the gas service.
///
/// # Arguments
/// * `account` - The address to be added as an operator.
///
/// # Errors
/// - [`ContractError::OperatorAlreadyAdded`]: If the specified account is already an operator.
///
/// # Authorization
/// - [`Self::owner`] must authorize.
fn add_operator(env: Env, account: Address) -> Result<(), ContractError>;

/// Remove an address as an operator.
///
/// The address is no longer authorized to execute apps via this contract.
///
/// # Arguments
/// * `account` - The address to be removed as an operator.
///
/// # Errors
/// - [`ContractError::NotAnOperator`]: If the specified account is not an operator.
///
/// # Authorization
/// - [`Self::owner`] must authorize.
fn remove_operator(env: Env, account: Address) -> Result<(), ContractError>;

/// Execute a function on any contract as the operators contract.
///
/// # Arguments
/// * `operator` - The address of the operator executing the function.
/// * `contract` - The address of the contract on which the function will be executed.
/// * `func` - The symbol representing the function to be executed.
/// * `args` - The arguments to be passed to the function.
///
/// # Returns
/// - `Ok(Val)`: Returns the result of the function execution.
///
/// # Errors
/// - [`ContractError::NotAnOperator`]: If the specified operator is not authorized.
///
/// # Authorization
/// - An `operator` must authorize.
fn execute(
env: Env,
operator: Address,
contract: Address,
func: Symbol,
args: Vec<Val>,
) -> Result<Val, ContractError>;
}
18 changes: 13 additions & 5 deletions contracts/stellar-axelar-operators/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@
#[cfg(any(test, feature = "testutils"))]
extern crate std;

pub mod event;
mod storage_types;

mod contract;
pub mod error;

pub use contract::{AxelarOperators, AxelarOperatorsClient};
mod interface;

#[cfg(test)]
mod tests;

cfg_if::cfg_if! {
if #[cfg(all(feature = "library", not(feature = "testutils")))] {
pub use interface::{AxelarOperatorsClient, AxelarOperatorsInterface};
} else {
pub mod event;
mod storage_types;
mod contract;

pub use contract::{AxelarOperators, AxelarOperatorsClient};
}
}
10 changes: 7 additions & 3 deletions contracts/stellar-example/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use stellar_interchain_token_service::executable::CustomInterchainTokenExecutabl
use stellar_interchain_token_service::InterchainTokenServiceClient;

use crate::event::{ExecutedEvent, TokenReceivedEvent, TokenSentEvent};
use crate::interface::ExampleInterface;
use crate::storage_types::DataKey;

#[contract]
Expand Down Expand Up @@ -122,15 +123,18 @@ impl Example {
.instance()
.set(&DataKey::InterchainTokenService, &interchain_token_service);
}
}

pub fn gas_service(env: &Env) -> Address {
#[contractimpl]
impl ExampleInterface for Example {
fn gas_service(env: &Env) -> Address {
env.storage()
.instance()
.get(&DataKey::GasService)
.expect("gas service not found")
}

pub fn send(
fn send(
env: &Env,
caller: Address,
destination_chain: String,
Expand Down Expand Up @@ -161,7 +165,7 @@ impl Example {
);
}

pub fn send_token(
fn send_token(
env: &Env,
caller: Address,
token_id: BytesN<32>,
Expand Down
Loading

0 comments on commit 2f17e32

Please sign in to comment.