//! Functionality for determining who is calling a contract.
library;
use ::address::Address;
use ::contract_id::ContractId;
use ::identity::Identity;
use ::option::Option::{self, *};
use ::result::Result::{self, *};
use ::inputs::{
Input,
input_address,
input_coin_owner,
input_count,
input_message_recipient,
input_message_sender,
input_type,
};
use ::revert::revert;
use ::vec::Vec;
use ::codec::*;
use ::debug::*;
/// The error type used when an `Identity` cannot be determined.
pub enum AuthError {
/// The caller is external, but the inputs to the transaction are not all owned by the same address.
InputsNotAllOwnedBySameAddress: (),
/// The caller is internal, but the `caller_address` function was called.
CallerIsInternal: (),
}
/// Returns `true` if the caller is external (i.e. a `script`).
/// Otherwise, if the caller is a contract, returns `false`.
///
/// # Additional Information
///
/// For more information refer to the [VM Instruction Set](https://fuellabs.github.io/fuel-specs/master/vm/instruction_set#gm-get-metadata).
///
/// # Returns
///
/// * [bool] - `true` if the caller is external, `false` otherwise.
///
/// # Examples
///
/// ```sway
/// use std::auth::caller_is_external;
///
/// fn foo() {
/// if caller_is_external() {
/// log("Caller is external.")
/// } else {
/// log("Caller is a contract.")
/// }
/// }
/// ```
pub fn caller_is_external() -> bool {
asm(r1) {
gm r1 i1;
r1: bool
}
}
/// If the caller is internal, returns the contract ID of the caller.
///
/// # Additional Information
///
/// External calls result in undefined behavior.
///
/// # Returns
///
/// * [ContractId] - The contract ID of the caller.
///
/// # Examples
///
/// ```sway
/// use std::auth::{caller_is_external, caller_contract_id};
///
/// fn foo() {
/// if !caller_is_external() {
/// let caller_contract_id = caller_contract_id();
/// log(caller_contract_id);
/// }
/// }
/// ```
pub fn caller_contract_id() -> ContractId {
ContractId::from(asm(r1) {
gm r1 i2;
r1: b256
})
}
/// Get the `Identity` (i.e. `Address` or `ContractId`) from which a call was made.
/// Returns a `Ok(Identity)`, or `Err(AuthError)` if an identity cannot be determined.
///
/// # Additional Information
///
/// Returns an `AuthError::InputsNotAllOwnedBySameAddress` if the caller is external and the inputs to the transaction are not all owned by the same address.
/// Should not return an `AuthError::CallerIsInternal` under any circumstances.
///
/// # Returns
///
/// * [Result<Identity, AuthError>] - `Ok(Identity)` if the identity can be determined, `Err(AuthError)` otherwise.
///
/// # Examples
///
/// ```sway
/// fn foo() {
/// match msg_sender() {
/// Ok(Identity::Address(address)) => log(address),
/// Ok(Identity::ContractId(contract_id)) => log(contract_id),
/// Err(AuthError::InputsNotAllOwnedBySameAddress) => log("Inputs not all owned by same address."),
/// Err(AuthError::CallerIsInternal) => log("Hell froze over."),
/// }
/// }
/// ```
pub fn msg_sender() -> Result<Identity, AuthError> {
if caller_is_external() {
match caller_address() {
Err(err) => Err(err),
Ok(owner) => Ok(Identity::Address(owner)),
}
} else {
// Get caller's `ContractId`.
Ok(Identity::ContractId(caller_contract_id()))
}
}
/// Get the owner of the inputs (of type `Input::Coin` or `Input::Message`) to a
/// `TransactionScript` if they all share the same owner.
///
/// # Returns
///
/// * [Result<Address, AuthError>] - `Ok(Address)` if the single owner can be determined, `Err(AuthError)` otherwise.
///
/// # Examples
///
/// ```sway
/// use std::auth::caller_address;
///
/// fn foo() {
/// match caller_address() {
/// Ok(address) => log(address),
/// Err(AuthError::InputsNotAllOwnedBySameAddress) => log("Inputs not all owned by the same address."),
/// Err(AuthError::CallerIsInternal) => log("Hell froze over."),
/// }
/// }
/// ```
pub fn caller_address() -> Result<Address, AuthError> {
let input_count = input_count().as_u64();
let mut first_input_owner = None;
let mut iter = 0;
while iter < input_count {
let input_owner = match input_address(iter) {
Some(address) => address,
None => {
// Input is not `Input::Coin` or `Input::Message`, continue looping.
iter += 1;
continue;
}
};
// This is the first input seen that is either a coin or a message.
if first_input_owner.is_none() {
first_input_owner = Some(input_owner);
iter += 1;
continue;
}
// Compare the current input owner to the first one.
// `first_input_owner` must be `Some` at this point,
// so we can safely unwrap.
if input_owner == first_input_owner.unwrap() {
// Owner is the same, continue looping.
iter += 1;
continue;
}
// Owners are not the same, return error.
return Err(AuthError::InputsNotAllOwnedBySameAddress);
}
// `first_input_owner` must be `Some` if the caller is an address, otherwise it's a contract.
match first_input_owner {
Some(address) => Ok(address),
None => Err(AuthError::CallerIsInternal),
}
}
/// Get the owners of the inputs (of type `Input::Coin` or `Input::Message`) to a
/// `TransactionScript`.
///
/// # Additional Information
///
/// The returned `Vec` is not deduplicated, so it may contain repeated addresses.
///
/// # Returns
///
/// * [Vec<Address>] - The addresses of the owners of the inputs.
///
/// # Examples
///
/// ```sway
/// use std::auth::caller_addresses;
///
/// fn foo(some_address: Address) {
/// let addresses = caller_addresses();
///
/// assert(addresses.get(0).unwrap() == some_address);
/// }
/// ```
pub fn caller_addresses() -> Vec<Address> {
let input_count = input_count().as_u64();
let mut addresses = Vec::new();
let mut iter = 0;
while iter < input_count {
match input_address(iter) {
Some(address) => {
addresses.push(address);
},
_ => {}
}
iter += 1;
}
addresses
}
/// Get the current predicate's address when called in an internal context.
///
/// # Returns
///
/// * [Option<Address>] - The address of this predicate.
///
/// # Examples
///
/// ```sway
/// use std::auth::predicate_address;
///
/// fn main() {
/// let this_predicate = predicate_address().unwrap();
/// log(this_predicate);
/// }
/// ```
pub fn predicate_address() -> Option<Address> {
// Get the index of the current predicate:
// i3 == GM_GET_VERIFYING_PREDICATE
let predicate_index = asm(r1) {
gm r1 i3;
r1: u64
};
input_address(predicate_index)
}