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