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:
@@ -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
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user