// ==UserScript==
// @name Stake.com Visual Balance Modifier (Working In-Game) (ONLY STAKE ORIGINALS)
// @namespace http://tampermonkey.net/
// @version 8.0
// @description Adds a persistent fake BTC balance and an integrated settings UI in the website footer to visually simulate gameplay on Stake.com.
// @author XaRTeCK (Using Gemini 2.5 Pro NGL :3)
// @match *://stake.com/*
// @match *://rgs.twist-rgs.com/*
// @connect stake.com
// @connect rgs.twist-rgs.com
// @license CC-BY-NC-ND-4.0
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
const BALANCE_STORAGE_KEY = 'stake_fake_btc_balance_v4';
const WELCOME_MSG_KEY = 'stake_welcome_msg_shown_v5';
const DEFAULT_BTC_VALUE = 0.0047; // Approximately $500 USD if 1 BTC = $105,000
let currentFakeBet = {
amount: 0,
currency: null
};
function getFakeBtcValue() {
const savedBalance = localStorage.getItem(BALANCE_STORAGE_KEY);
return savedBalance ? parseFloat(savedBalance) : DEFAULT_BTC_VALUE;
}
function setFakeBtcValue(amount) {
const numericAmount = parseFloat(amount);
if (!isNaN(numericAmount) && numericAmount >= 0) {
localStorage.setItem(BALANCE_STORAGE_KEY, numericAmount.toString());
// This forces the balance display component to re-render with the new value
const balanceToggle = document.querySelector('[data-testid="balance-toggle"] button');
if (balanceToggle) {
balanceToggle.click();
balanceToggle.click();
}
}
}
function showWelcomePopup() {
if (localStorage.getItem(WELCOME_MSG_KEY)) return;
const popupHTML = `
<div id="visual-script-welcome" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: #2f3c4c; color: #fff; padding: 25px; border-radius: 10px; box-shadow: 0 5px 20px rgba(0,0,0,0.5); z-index: 9999; max-width: 400px; text-align: center; font-family: 'Inter', sans-serif;">
<h2 style="margin: 0 0 15px 0; font-size: 22px;">Gameplay Modifier Active</h2>
<p style="margin: 0 0 20px 0; font-size: 16px; line-height: 1.5; color: #b0bdce;">
Your fake balance is <strong>${getFakeBtcValue()} BTC</strong>.
To change this amount, <strong>scroll to the very bottom of the Stake.com page.</strong>
</p>
<button id="visual-script-close" style="background-color: #008756; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 16px; font-weight: bold;">Got it</button>
</div>
<div id="visual-script-overlay" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 9998;"></div>
`;
document.body.insertAdjacentHTML('beforeend', popupHTML);
document.getElementById('visual-script-close').addEventListener('click', () => {
document.getElementById('visual-script-welcome')?.remove();
document.getElementById('visual-script-overlay')?.remove();
localStorage.setItem(WELCOME_MSG_KEY, 'true');
});
}
function injectBalanceSettings(footerElement) {
if (document.getElementById('fake-balance-settings')) return;
const settingsHTML = `
<div id="fake-balance-settings" class="p-4 mt-6 border-t-2 border-t-grey-500 text-grey-200">
<div class="flex flex-col gap-2 max-w-sm mx-auto">
<label for="fake-balance-input-btc" class="ds-body-md-strong text-white text-center">Visual Balance Modifier</label>
<p class="ds-body-sm text-center">This only changes the balance you see on your screen.</p>
<input type="number" step="0.001" id="fake-balance-input-btc" value="${getFakeBtcValue()}"
style="background-color: #1f2a38; border: 1px solid #3c4a5c; color: white; border-radius: 5px; padding: 8px; width: 100%; text-align: center;"
>
<span id="fake-balance-saved" style="color: #6CDE07; font-size: 12px; height: 16px; transition: opacity 0.3s ease-out; opacity: 0; text-align: center;">Saved!</span>
</div>
</div>
`;
footerElement.insertAdjacentHTML('afterbegin', settingsHTML);
const input = document.getElementById('fake-balance-input-btc');
const savedMessage = document.getElementById('fake-balance-saved');
let timeoutId;
input.addEventListener('input', (event) => {
setFakeBtcValue(event.target.value);
savedMessage.style.opacity = '1';
clearTimeout(timeoutId);
timeoutId = setTimeout(() => { savedMessage.style.opacity = '0'; }, 1500);
});
}
const originalFetch = window.fetch;
window.fetch = async function(url, options) {
const FAKE_BTC_BALANCE = getFakeBtcValue();
const FAKE_PROVIDER_BALANCE = FAKE_BTC_BALANCE * 100_000_000;
const requestUrl = new URL(url.toString(), window.location.origin);
const host = requestUrl.hostname;
const path = requestUrl.pathname;
if (host.includes('rgs.twist-rgs.com') && path.includes('/wallet/authenticate')) {
const response = await originalFetch(url, options);
const data = await response.clone().json();
if (data.balance) data.balance.amount = FAKE_PROVIDER_BALANCE;
return new Response(JSON.stringify(data), { status: 200, headers: response.headers });
}
if (host.includes('stake.com') && path.includes('/_api/graphql') && options?.body) {
let requestBody;
try { requestBody = JSON.parse(options.body); } catch (e) { return originalFetch(url, options); }
let modifiedOptions = options;
if (requestBody.operationName === 'UserBalances') {
const response = await originalFetch(url, options);
const data = await response.clone().json();
const btcBalance = data?.data?.user?.balances.find(b => b.available.currency === 'btc');
if (btcBalance) {
btcBalance.available.amount = FAKE_BTC_BALANCE;
}
return new Response(JSON.stringify(data), { status: response.status, headers: response.headers });
}
if (requestBody.query?.includes('mutation') && requestBody.variables?.amount > 0) {
currentFakeBet = { amount: requestBody.variables.amount, currency: requestBody.variables.currency };
const modifiedBody = JSON.parse(JSON.stringify(requestBody));
modifiedBody.variables.amount = 0;
modifiedOptions = { ...options, body: JSON.stringify(modifiedBody) };
}
const response = await originalFetch(url, modifiedOptions);
const responseClone = response.clone();
try {
const data = await response.json();
if (data.data) {
const gameDataKey = Object.keys(data.data).find(key => data.data[key] && typeof data.data[key] === 'object' && 'amount' in data.data[key]);
if (gameDataKey && currentFakeBet.amount > 0) {
const gameData = data.data[gameDataKey];
gameData.amount = currentFakeBet.amount;
if (gameData.payoutMultiplier > 0) gameData.payout = gameData.payoutMultiplier * currentFakeBet.amount;
if (!gameData.active) currentFakeBet = { amount: 0, currency: null };
return new Response(JSON.stringify(data), { status: 200, headers: response.headers });
}
}
return responseClone;
} catch (e) { return responseClone; }
}
if (host.includes('stake.com') && path.startsWith('/_api/casino/')) {
let modifiedOptions = options;
if (/\/(bet|roll|bonus)$/.test(path) && options?.body) {
try {
const originalRequestBody = JSON.parse(options.body);
const modifiedBody = { ...originalRequestBody };
let totalAmount = 0;
if (path.includes('/roulette/bet')) {
['colors', 'parities', 'dozens', 'numbers', 'columns', 'halves'].forEach(key => {
if (Array.isArray(modifiedBody[key])) {
modifiedBody[key].forEach(bet => { totalAmount += bet.amount; bet.amount = 0; });
}
});
} else if (originalRequestBody.amount > 0) {
totalAmount = originalRequestBody.amount;
modifiedBody.amount = 0;
}
if (totalAmount > 0) {
currentFakeBet = { amount: totalAmount, currency: originalRequestBody.currency };
modifiedOptions = { ...options, body: JSON.stringify(modifiedBody) };
}
} catch (e) {}
}
const response = await originalFetch(url, modifiedOptions);
const responseClone = response.clone();
try {
const data = await response.json();
const gameDataKey = Object.keys(data).find(key => data[key] && typeof data[key] === 'object' && 'amount' in data[key]);
if (gameDataKey && currentFakeBet.amount > 0 && data[gameDataKey].currency === currentFakeBet.currency) {
const gameData = data[gameDataKey];
gameData.amount = currentFakeBet.amount;
if (gameData.payoutMultiplier > 0) gameData.payout = gameData.payoutMultiplier * currentFakeBet.amount;
if (gameData.state?.rounds) gameData.state.rounds.forEach(r => { if ('amount' in r) r.amount = currentFakeBet.amount; });
if (!gameData.active) currentFakeBet = { amount: 0, currency: null };
return new Response(JSON.stringify(data), { status: 200, headers: response.headers });
}
return responseClone;
} catch (e) { return responseClone; }
}
return originalFetch(url, options);
};
window.addEventListener('DOMContentLoaded', () => {
showWelcomePopup();
const footerObserver = new MutationObserver((mutations, observer) => {
const footer = document.querySelector('footer[data-testid="footer"]');
if (footer) {
injectBalanceSettings(footer);
observer.disconnect(); // Stop observing once the footer is found and injected
}
});
footerObserver.observe(document.body, {
childList: true,
subtree: true
});
});
})();