Skip to main content

cast/cmd/tip20/
create.rs

1use crate::{
2    cmd::{
3        erc20::build_provider_with_signer,
4        send::{cast_send, cast_send_with_access_key},
5    },
6    tx::{SendTxOpts, TxParams},
7};
8use alloy_ens::NameOrAddress;
9use alloy_primitives::B256;
10use alloy_sol_types::sol;
11use foundry_cli::utils::{LoadConfig, get_chain};
12use foundry_common::provider::ProviderBuilder;
13use tempo_alloy::TempoNetwork;
14use tempo_contracts::precompiles::{TIP20_FACTORY_ADDRESS, is_iso4217_currency};
15
16sol! {
17    #[sol(rpc)]
18    interface ITIP20Factory {
19        function createToken(
20            string memory name,
21            string memory symbol,
22            string memory currency,
23            address quoteToken,
24            address admin,
25            bytes32 salt
26        ) external returns (address token);
27    }
28}
29
30/// Returns a warning message for non-ISO 4217 currency codes used in TIP-20 token creation.
31pub(crate) fn iso4217_warning_message(currency: &str) -> String {
32    let hyperlink = |url: &str| format!("\x1b]8;;{url}\x1b\\{url}\x1b]8;;\x1b\\");
33    let tip20_docs = hyperlink("https://docs.tempo.xyz/protocol/tip20/overview");
34    let iso_docs = hyperlink("https://www.iso.org/iso-4217-currency-codes.html");
35
36    format!(
37        "\"{currency}\" is not a recognized ISO 4217 currency code.\n\
38         \n\
39         If the token you are trying to deploy is a fiat-backed stablecoin, Tempo strongly\n\
40         recommends that the currency code field be the ISO-4217 currency code of the fiat\n\
41         currency your token tracks (e.g. \"USD\", \"EUR\", \"GBP\").\n\
42         \n\
43         The currency field is IMMUTABLE after token creation and affects fee payment\n\
44         eligibility, DEX routing, and quote token pairing. Only \"USD\"-denominated tokens\n\
45         can be used to pay transaction fees on Tempo.\n\
46         \n\
47         Learn more:\n  \
48         - Tempo TIP-20 docs: {tip20_docs}\n  \
49         - ISO 4217 standard: {iso_docs}"
50    )
51}
52
53#[allow(clippy::too_many_arguments)]
54pub(super) async fn run(
55    name: String,
56    symbol: String,
57    currency: String,
58    quote_token: NameOrAddress,
59    admin: NameOrAddress,
60    salt: B256,
61    force: bool,
62    send_tx: SendTxOpts,
63    tx_opts: TxParams,
64) -> eyre::Result<()> {
65    let (signer, tempo_access_key) = if send_tx.eth.wallet.from.is_some() {
66        send_tx.eth.wallet.maybe_signer().await?
67    } else {
68        (None, None)
69    };
70
71    let config = send_tx.eth.rpc.load_config()?;
72
73    if !is_iso4217_currency(&currency) && !force {
74        sh_warn!("{}", super::iso4217_warning_message(&currency))?;
75        let response: String = foundry_common::prompt!("\nContinue anyway? [y/N] ")?;
76        if !matches!(response.trim(), "y" | "Y") {
77            sh_println!("Aborted.")?;
78            return Ok(());
79        }
80    }
81
82    let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout);
83    let provider = ProviderBuilder::<TempoNetwork>::from_config(&config)?.build()?;
84    let quote_token_addr = quote_token.resolve(&provider).await?;
85    let admin_addr = admin.resolve(&provider).await?;
86
87    let mut tx = ITIP20Factory::new(TIP20_FACTORY_ADDRESS, &provider)
88        .createToken(name, symbol, currency, quote_token_addr, admin_addr, salt)
89        .into_transaction_request();
90
91    tx_opts.apply::<TempoNetwork>(&mut tx, get_chain(config.chain, &provider).await?.is_legacy());
92
93    if let Some(ref access_key) = tempo_access_key {
94        let signer = signer.as_ref().ok_or_else(|| eyre::eyre!("access key requires a signer"))?;
95        cast_send_with_access_key(
96            &provider,
97            tx,
98            signer,
99            access_key,
100            send_tx.cast_async,
101            send_tx.confirmations,
102            timeout,
103        )
104        .await?;
105    } else {
106        let signer = signer.unwrap_or(send_tx.eth.wallet.signer().await?);
107        let provider = build_provider_with_signer::<TempoNetwork>(&send_tx, signer)?;
108        cast_send(provider, tx, send_tx.cast_async, send_tx.sync, send_tx.confirmations, timeout)
109            .await?;
110    }
111
112    Ok(())
113}