use std::collections::HashSet;
use tari_ootle_common_types::SubstateRequirement;
use tari_ootle_transaction::{TransactionBuilder, UnsignedTransaction, args};
use tari_template_lib_types::{
Amount,
UtxoAddress,
constants::{
TARI_TOKEN,
XTR_FAUCET_CLAIM_RESOURCE_ADDRESS,
XTR_FAUCET_COMPONENT_ADDRESS,
XTR_FAUCET_VAULT_ADDRESS,
},
stealth::StealthTransferStatement,
};
use crate::{
Address,
ToAccountAddress,
builtin_templates::UnsignedTransactionBuilder,
provider::{Provider, ProviderError, WantInput},
};
pub type IFaucet<'a, P> = FaucetInvokeBuilder<'a, P>;
pub struct FaucetInvokeBuilder<'a, P> {
builder: TransactionBuilder,
provider: &'a P,
want_list: HashSet<WantInput>,
account_workspace_name: Option<String>,
}
impl<'a, P: Provider> UnsignedTransactionBuilder for FaucetInvokeBuilder<'a, P> {
fn default_signer_address(&self) -> &Address {
self.provider.default_signer_address()
}
fn add_input<S: Into<SubstateRequirement>>(mut self, substate_id: S) -> Self {
self.builder = self.builder.add_input(substate_id);
self
}
async fn prepare(self) -> Result<UnsignedTransaction, ProviderError> {
let Self {
builder,
provider,
want_list,
..
} = self;
let unsigned_tx = builder.build_unsigned();
let unsigned_tx = provider.resolve_input_want_list(unsigned_tx, &want_list).await?;
Ok(unsigned_tx)
}
}
impl<'a, P: Provider> FaucetInvokeBuilder<'a, P> {
pub fn new(provider: &'a P) -> Self {
let network = provider.network();
Self {
builder: TransactionBuilder::new(network).with_auto_fill_inputs(),
provider,
want_list: HashSet::new(),
account_workspace_name: None,
}
}
pub fn pay_fee<A: Into<Amount>>(mut self, amount: A) -> Self {
if let Some(name) = self.account_workspace_name.take() {
self.builder = self.builder.pay_fee_from_component(name, amount);
} else {
let component_addr = self.default_signer_address().to_account_address();
self.want_list.insert(WantInput::VaultForResource {
component_address: component_addr,
resource_address: TARI_TOKEN,
required: true,
});
self.builder = self.builder.pay_fee_from_component(component_addr, amount);
}
self
}
fn next_workspace_name(&mut self) -> String {
format!("__AccountInvokeBuilder_{}", self.builder.next_workspace_id())
}
pub fn take_faucet_funds_stealth(
mut self,
transfer: StealthTransferStatement,
pay_revealed_amount_as_fees: bool,
) -> Self {
let amount = transfer.inputs_statement.revealed_amount;
if !amount.is_positive() {
panic!("Transfer amount must be positive");
}
let has_revealed_output = transfer.outputs_statement.revealed_output_amount.is_positive();
let workspace_names = has_revealed_output.then(|| {
(
(!pay_revealed_amount_as_fees).then(|| self.next_workspace_name()),
self.next_workspace_name(),
)
});
let recipient_account_pk = *self.default_signer_address().account_public_key();
for input in &transfer.inputs_statement.inputs {
self.want_list.insert(WantInput::SpecificSubstate {
substate_id: UtxoAddress::new(TARI_TOKEN, input.commitment.into()).into(),
required: true,
});
}
self.builder = self.builder.with_fee_instructions_builder(|builder| {
builder
.add_input(XTR_FAUCET_VAULT_ADDRESS)
.add_input(XTR_FAUCET_CLAIM_RESOURCE_ADDRESS)
.call_method(XTR_FAUCET_COMPONENT_ADDRESS, "take_confidential", args![transfer])
.then(|builder| {
if let Some((account_ws_name, bucket_name)) = &workspace_names {
if let Some(account_ws_name) = account_ws_name {
builder
.put_last_instruction_output_on_workspace(bucket_name)
.create_account_with_bucket(recipient_account_pk, bucket_name)
.put_last_instruction_output_on_workspace(account_ws_name)
} else {
builder
.put_last_instruction_output_on_workspace(bucket_name)
.pay_fee_from_bucket(bucket_name)
}
} else {
builder
}
})
});
self.account_workspace_name = workspace_names.and_then(|(account_ws_name, _)| account_ws_name);
self
}
pub fn take_faucet_funds(mut self) -> Self {
let bucket_name = self.next_workspace_name();
let recipient_account_pk = *self.default_signer_address().account_public_key();
let recipient_account_addr = self.default_signer_address().to_account_address();
self.want_list.insert(WantInput::VaultForResource {
component_address: recipient_account_addr,
resource_address: TARI_TOKEN,
required: false,
});
self.want_list.insert(WantInput::SpecificSubstate {
substate_id: recipient_account_addr.into(),
required: false,
});
let account_name = self.next_workspace_name();
self.builder = self.builder.with_fee_instructions_builder(|builder| {
builder
.call_method(XTR_FAUCET_COMPONENT_ADDRESS, "take", args![])
.add_input(XTR_FAUCET_VAULT_ADDRESS)
.add_input(XTR_FAUCET_CLAIM_RESOURCE_ADDRESS)
.put_last_instruction_output_on_workspace(&bucket_name)
.create_account_with_bucket(recipient_account_pk, &bucket_name)
.put_last_instruction_output_on_workspace(&account_name)
});
self.account_workspace_name = Some(account_name);
self
}
}