Skip to main content

tempo_bench/cmd/max_tps/
mpp.rs

1use super::*;
2use alloy::{
3    primitives::keccak256,
4    sol,
5    sol_types::{SolCall, SolValue},
6};
7use tempo_alloy::{primitives::transaction::Call, rpc::TempoTransactionRequest};
8
9sol! {
10    #[sol(rpc)]
11    #[allow(clippy::too_many_arguments)]
12    interface ITempoStreamChannel {
13        function open(
14            address payee,
15            address token,
16            uint128 deposit,
17            bytes32 salt,
18            address authorizedSigner
19        ) external returns (bytes32 channelId);
20
21        function close(
22            bytes32 channelId,
23            uint128 cumulativeAmount,
24            bytes calldata signature
25        ) external;
26    }
27}
28
29/// Precompute the channel ID using the same formula as the contract.
30///
31/// `channelId = keccak256(abi.encode(payer, payee, token, salt, authorizedSigner, channel, chainId))`
32pub(super) fn compute_channel_id(
33    payer: Address,
34    payee: Address,
35    token: Address,
36    salt: B256,
37    authorized_signer: Address,
38    channel_address: Address,
39    chain_id: u64,
40) -> B256 {
41    keccak256(
42        (
43            payer,
44            payee,
45            token,
46            salt,
47            authorized_signer,
48            channel_address,
49            U256::from(chain_id),
50        )
51            .abi_encode(),
52    )
53}
54
55/// Build a `TempoTransactionRequest` with two calls: channel open + close.
56///
57/// The signer is both payer and payee, so `close` succeeds immediately.
58pub(super) fn build_open_and_close(
59    channel_address: Address,
60    payer: Address,
61    token: Address,
62    salt: B256,
63    channel_id: B256,
64) -> TempoTransactionRequest {
65    let open_call = Call {
66        to: channel_address.into(),
67        input: ITempoStreamChannel::openCall {
68            payee: payer,
69            token,
70            deposit: 1,
71            salt,
72            authorizedSigner: Address::ZERO,
73        }
74        .abi_encode()
75        .into(),
76        value: U256::ZERO,
77    };
78
79    let close_call = Call {
80        to: channel_address.into(),
81        input: ITempoStreamChannel::closeCall {
82            channelId: channel_id,
83            cumulativeAmount: 0,
84            signature: Default::default(),
85        }
86        .abi_encode()
87        .into(),
88        value: U256::ZERO,
89    };
90
91    TempoTransactionRequest {
92        calls: vec![open_call, close_call],
93        ..Default::default()
94    }
95}
96
97/// Approves the channel contract to spend tokens for signers that need it.
98pub(super) async fn setup(
99    signer_providers: &[(Secp256k1Signer, DynProvider<TempoNetwork>)],
100    channel_address: Address,
101    fee_token: Address,
102    max_concurrent_requests: usize,
103    max_concurrent_transactions: usize,
104) -> eyre::Result<()> {
105    info!(%channel_address, "Checking MPP channel token approvals");
106
107    // Check allowances and collect approval futures for signers that need it
108    let approvals: Vec<_> = stream::iter(signer_providers.iter())
109        .filter_map(|(signer, provider)| {
110            let provider = provider.clone();
111            let owner = signer.address();
112            async move {
113                let token = ITIP20Instance::new(fee_token, provider);
114                let allowance = token.allowance(owner, channel_address).call().await.ok()?;
115                if allowance == U256::ZERO {
116                    Some(Box::pin(
117                        async move { token.approve(channel_address, U256::MAX).send().await },
118                    ) as BoxFuture<'static, _>)
119                } else {
120                    None
121                }
122            }
123        })
124        .collect::<Vec<_>>()
125        .await;
126
127    if approvals.is_empty() {
128        info!("All signers already have approvals");
129        return Ok(());
130    }
131
132    info!(count = approvals.len(), "Approving signers");
133
134    join_all(
135        approvals.into_iter().progress(),
136        max_concurrent_requests,
137        max_concurrent_transactions,
138    )
139    .await
140    .context("Failed to approve MPP channel contract")?;
141
142    Ok(())
143}