Skip to main content

outlayer_cli/commands/
checks.rs

1use anyhow::{Context, Result};
2
3use crate::api::ApiClient;
4use crate::config::NetworkConfig;
5
6/// Resolve wallet API key from --api-key flag or OUTLAYER_WALLET_KEY env var
7pub fn resolve_wallet_key(api_key: Option<&str>) -> Result<String> {
8    if let Some(key) = api_key {
9        return Ok(key.to_string());
10    }
11    std::env::var("OUTLAYER_WALLET_KEY")
12        .context("Wallet API key required. Use --api-key or set OUTLAYER_WALLET_KEY env var")
13}
14
15/// `outlayer checks create <token> <amount>`
16pub async fn create(
17    network: &NetworkConfig,
18    api_key: Option<&str>,
19    token: &str,
20    amount: &str,
21    memo: Option<&str>,
22    expires_in: Option<u64>,
23) -> Result<()> {
24    let key = resolve_wallet_key(api_key)?;
25    let api = ApiClient::new(network);
26
27    eprintln!("Creating payment check...");
28
29    let resp = api
30        .create_payment_check(&key, token, amount, memo, expires_in)
31        .await?;
32
33    println!("check_id:  {}", resp.check_id);
34    println!("check_key: {}", resp.check_key);
35    println!("token:     {}", resp.token);
36    println!("amount:    {}", resp.amount);
37    if let Some(memo) = &resp.memo {
38        println!("memo:      {}", memo);
39    }
40    println!("created:   {}", resp.created_at);
41    if let Some(expires) = &resp.expires_at {
42        println!("expires:   {}", expires);
43    }
44
45    eprintln!("\nSave the check_key — it is shown only once. Send it to the recipient.");
46
47    Ok(())
48}
49
50/// `outlayer checks batch-create --file checks.json`
51pub async fn batch_create(
52    network: &NetworkConfig,
53    api_key: Option<&str>,
54    file: &str,
55) -> Result<()> {
56    let key = resolve_wallet_key(api_key)?;
57    let api = ApiClient::new(network);
58
59    let data = std::fs::read_to_string(file)
60        .with_context(|| format!("Failed to read file: {}", file))?;
61    let checks: Vec<serde_json::Value> =
62        serde_json::from_str(&data).context("Invalid JSON in checks file. Expected array of {token, amount, memo?, expires_in?}")?;
63
64    if checks.is_empty() {
65        anyhow::bail!("Empty checks array");
66    }
67    if checks.len() > 10 {
68        anyhow::bail!("Maximum 10 checks per batch");
69    }
70
71    eprintln!("Creating {} payment checks...", checks.len());
72
73    let resp = api.batch_create_payment_checks(&key, &checks).await?;
74
75    for (i, check) in resp.checks.iter().enumerate() {
76        println!("--- Check {} ---", i + 1);
77        println!("check_id:  {}", check.check_id);
78        println!("check_key: {}", check.check_key);
79        println!("amount:    {}", check.amount);
80        if let Some(memo) = &check.memo {
81            println!("memo:      {}", memo);
82        }
83    }
84
85    eprintln!("\nSave all check_keys — they are shown only once.");
86
87    Ok(())
88}
89
90/// `outlayer checks claim <check_key>`
91pub async fn claim(
92    network: &NetworkConfig,
93    api_key: Option<&str>,
94    check_key: &str,
95    amount: Option<&str>,
96) -> Result<()> {
97    let key = resolve_wallet_key(api_key)?;
98    let api = ApiClient::new(network);
99
100    if let Some(amt) = amount {
101        eprintln!("Claiming {} from payment check...", amt);
102    } else {
103        eprintln!("Claiming payment check (full)...");
104    }
105
106    let resp = api.claim_payment_check(&key, check_key, amount).await?;
107
108    println!("token:     {}", resp.token);
109    println!("claimed:   {}", resp.amount_claimed);
110    println!("remaining: {}", resp.remaining);
111    if let Some(memo) = &resp.memo {
112        println!("memo:      {}", memo);
113    }
114    println!("time:      {}", resp.claimed_at);
115
116    eprintln!("\nFunds landed in your intents balance.");
117
118    Ok(())
119}
120
121/// `outlayer checks reclaim <check_id>`
122pub async fn reclaim(
123    network: &NetworkConfig,
124    api_key: Option<&str>,
125    check_id: &str,
126    amount: Option<&str>,
127) -> Result<()> {
128    let key = resolve_wallet_key(api_key)?;
129    let api = ApiClient::new(network);
130
131    if let Some(amt) = amount {
132        eprintln!("Reclaiming {} from check {}...", amt, check_id);
133    } else {
134        eprintln!("Reclaiming check {} (full remaining)...", check_id);
135    }
136
137    let resp = api.reclaim_payment_check(&key, check_id, amount).await?;
138
139    println!("token:     {}", resp.token);
140    println!("reclaimed: {}", resp.amount_reclaimed);
141    println!("remaining: {}", resp.remaining);
142    println!("time:      {}", resp.reclaimed_at);
143
144    eprintln!("\nFunds returned to your intents balance.");
145
146    Ok(())
147}
148
149/// `outlayer checks status <check_id>`
150pub async fn status(
151    network: &NetworkConfig,
152    api_key: Option<&str>,
153    check_id: &str,
154) -> Result<()> {
155    let key = resolve_wallet_key(api_key)?;
156    let api = ApiClient::new(network);
157
158    let resp = api.get_payment_check_status(&key, check_id).await?;
159
160    println!("check_id:  {}", resp.check_id);
161    println!("status:    {}", resp.status);
162    println!("token:     {}", resp.token);
163    println!("amount:    {}", resp.amount);
164    println!("claimed:   {}", resp.claimed_amount);
165    println!("reclaimed: {}", resp.reclaimed_amount);
166    if let Some(memo) = &resp.memo {
167        println!("memo:      {}", memo);
168    }
169    println!("created:   {}", resp.created_at);
170    if let Some(expires) = &resp.expires_at {
171        println!("expires:   {}", expires);
172    }
173    if let Some(claimed_at) = &resp.claimed_at {
174        println!("claimed_at: {}", claimed_at);
175    }
176    if let Some(claimed_by) = &resp.claimed_by {
177        println!("claimed_by: {}", claimed_by);
178    }
179
180    Ok(())
181}
182
183/// `outlayer checks list`
184pub async fn list(
185    network: &NetworkConfig,
186    api_key: Option<&str>,
187    status_filter: Option<&str>,
188    limit: i64,
189) -> Result<()> {
190    let key = resolve_wallet_key(api_key)?;
191    let api = ApiClient::new(network);
192
193    let resp = api
194        .list_payment_checks(&key, status_filter, limit)
195        .await?;
196
197    if resp.checks.is_empty() {
198        eprintln!("No payment checks found.");
199        return Ok(());
200    }
201
202    println!(
203        "{:<20} {:<18} {:>12} {:>12} {:<10}",
204        "CHECK_ID", "TOKEN", "AMOUNT", "CLAIMED", "STATUS"
205    );
206
207    for check in &resp.checks {
208        let token_short = if check.token.len() > 16 {
209            format!("{}...", &check.token[..14])
210        } else {
211            check.token.clone()
212        };
213        println!(
214            "{:<20} {:<18} {:>12} {:>12} {:<10}",
215            check.check_id, token_short, check.amount, check.claimed_amount, check.status
216        );
217    }
218
219    Ok(())
220}
221
222/// `outlayer checks sign-message <message> <recipient>`
223pub async fn sign_message(
224    network: &NetworkConfig,
225    api_key: Option<&str>,
226    message: &str,
227    recipient: &str,
228    nonce: Option<&str>,
229) -> Result<()> {
230    let key = resolve_wallet_key(api_key)?;
231    let api = ApiClient::new(network);
232
233    eprintln!("Signing message for {}...", recipient);
234
235    let resp = api.sign_message(&key, message, recipient, nonce).await?;
236
237    println!("account_id: {}", resp.account_id);
238    println!("public_key: {}", resp.public_key);
239    println!("signature:  {}", resp.signature);
240    println!("nonce:      {}", resp.nonce);
241
242    Ok(())
243}
244
245/// `outlayer checks peek <check_key>`
246pub async fn peek(
247    network: &NetworkConfig,
248    api_key: Option<&str>,
249    check_key: &str,
250) -> Result<()> {
251    let key = resolve_wallet_key(api_key)?;
252    let api = ApiClient::new(network);
253
254    let resp = api.peek_payment_check(&key, check_key).await?;
255
256    println!("token:   {}", resp.token);
257    println!("balance: {}", resp.balance);
258    println!("status:  {}", resp.status);
259    if let Some(memo) = &resp.memo {
260        println!("memo:    {}", memo);
261    }
262    if let Some(expires) = &resp.expires_at {
263        println!("expires: {}", expires);
264    }
265
266    Ok(())
267}