1
0
forked from GRIN/grim

Build 75: no pairing by default; price back over the mixnet

- Pairing defaults to Off on first run: no conversion is shown anywhere and no
  price request leaves the device until the user opts into a pairing. Off is a
  picker option and the choice persists.
- The rate fetch goes back over the Nym mixnet (reverting Build 71's clearnet
  route) — it's opt-in and infrequent, so it stays private and doesn't get in
  the way of slatepacks. CoinGecko's free-tier rate limit (shared exit IP) rules
  out live/1s polling, so it keeps the slow cache.
- Small note on the Pairing page that rates fetch over the mixnet.
This commit is contained in:
2ro
2026-06-14 18:08:23 -04:00
parent e5d1f0c6db
commit fb4f27a88f
3 changed files with 26 additions and 15 deletions
+9
View File
@@ -2226,6 +2226,15 @@ impl GoblinWalletView {
}
}
});
ui.add_space(10.0);
ui.label(
RichText::new(
"Rates fetch over the Nym mixnet, only while a pairing is on — \
off means no rate request leaves your device.",
)
.font(FontId::new(12.0, fonts::regular()))
.color(t.text_dim),
);
ui.add_space(16.0);
});
}
+12 -13
View File
@@ -12,15 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! GRIN price preview, cached per currency. The rate is non-sensitive metadata
//! (no payment info), so — like the update check, and like upstream Grim — it is
//! fetched direct over HTTPS rather than waiting on the mixnet, keeping the
//! preview prompt. Payments, relays and identity stay mixnet-only.
//! GRIN price preview, cached per currency. Off by default (no pairing → no
//! fetch); only once the user opts into a pairing is the rate fetched — and it
//! goes over the Nym mixnet like everything else, never the clear net. The rate
//! barely moves and CoinGecko's free tier rate-limits frequent polling (the
//! shared exit IP all the more), so it refreshes on a slow cache, not live.
use lazy_static::lazy_static;
use parking_lot::RwLock;
use std::collections::{HashMap, HashSet};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::time::{SystemTime, UNIX_EPOCH};
use crate::nym;
/// Cache refresh interval (seconds).
const REFRESH_SECS: i64 = 300;
@@ -91,20 +94,16 @@ fn trigger_refresh(vs: String) {
});
}
/// Fetch the GRIN/`vs` rate from CoinGecko, direct over HTTPS.
/// Fetch the GRIN/`vs` rate from CoinGecko over the Nym mixnet.
async fn fetch_rate(vs: &str) -> Option<f64> {
let url = format!(
"https://api.coingecko.com/api/v3/simple/price?ids=grin&vs_currencies={}",
vs
);
// CoinGecko rejects requests without a User-Agent (403). A static,
// non-identifying UA.
let client = reqwest::Client::builder()
.user_agent("goblin-wallet")
.timeout(Duration::from_secs(20))
.build()
.ok()?;
let body = client.get(&url).send().await.ok()?.text().await.ok()?;
// non-identifying UA is fine over the mixnet.
let headers = vec![("User-Agent".to_string(), "goblin-wallet".to_string())];
let body = nym::http_request("GET", url, None, headers).await?;
let parsed: Option<f64> = serde_json::from_str::<serde_json::Value>(&body)
.ok()
.and_then(|doc| doc.get("grin")?.get(vs)?.as_f64());
+5 -2
View File
@@ -458,9 +458,12 @@ impl AppConfig {
return p;
}
}
// No pairing chosen yet → off by default: no conversion is shown anywhere
// and no price is fetched until the user opts into a pairing. (A legacy
// `fiat_preview = true` still defaults to USD for existing users.)
match r_config.fiat_preview {
Some(false) => Pairing::Off,
_ => Pairing::Usd,
Some(true) => Pairing::Usd,
_ => Pairing::Off,
}
}