Complete markdown files cleanup and organization
- Organized 252 files across project - Root directory: 187 → 2 files (98.9% reduction) - Moved configuration guides to docs/04-configuration/ - Moved troubleshooting guides to docs/09-troubleshooting/ - Moved quick start guides to docs/01-getting-started/ - Moved reports to reports/ directory - Archived temporary files - Generated comprehensive reports and documentation - Created maintenance scripts and guides All files organized according to established standards.
This commit is contained in:
358
examples/metamask-price-feed.html
Normal file
358
examples/metamask-price-feed.html
Normal file
@@ -0,0 +1,358 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ETH/USD Price Feed - ChainID 138</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
padding: 40px;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #666;
|
||||
margin-bottom: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.price-display {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border-radius: 15px;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.price-label {
|
||||
font-size: 14px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.price-info {
|
||||
background: #f8f9fa;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
button {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
padding: 15px 30px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.status.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.status.info {
|
||||
background: #e7f3ff;
|
||||
color: #0066cc;
|
||||
border: 1px solid #b3d9ff;
|
||||
}
|
||||
|
||||
.status.success {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.status.error {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.oracle-address {
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
word-break: break-all;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>ETH/USD Price Feed</h1>
|
||||
<p class="subtitle">ChainID 138 - Oracle Contract</p>
|
||||
|
||||
<div class="price-display">
|
||||
<div class="price-label">Current Price</div>
|
||||
<div class="price-value" id="priceValue">$0.00</div>
|
||||
<div class="price-label" id="priceStatus">Not loaded</div>
|
||||
</div>
|
||||
|
||||
<div class="price-info" id="priceInfo" style="display: none;">
|
||||
<div class="info-row">
|
||||
<span class="info-label">Round ID</span>
|
||||
<span class="info-value" id="roundId">-</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Last Updated</span>
|
||||
<span class="info-value" id="updatedAt">-</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Started At</span>
|
||||
<span class="info-value" id="startedAt">-</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status" id="status"></div>
|
||||
|
||||
<button id="connectBtn">Connect MetaMask</button>
|
||||
<button id="fetchPriceBtn" disabled>Fetch Price</button>
|
||||
<button id="autoRefreshBtn" disabled>Auto Refresh (60s)</button>
|
||||
|
||||
<div class="oracle-address">
|
||||
Oracle: 0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.ethers.io/lib/ethers-5.7.2.umd.min.js"></script>
|
||||
<script>
|
||||
const ORACLE_ADDRESS = '0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6';
|
||||
const RPC_URL = 'https://rpc-core.d-bis.org';
|
||||
const CHAIN_ID = 138;
|
||||
|
||||
const ORACLE_ABI = [
|
||||
"function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)",
|
||||
"function decimals() external view returns (uint8)",
|
||||
"function description() external view returns (string memory)"
|
||||
];
|
||||
|
||||
let provider = null;
|
||||
let signer = null;
|
||||
let oracle = null;
|
||||
let autoRefreshInterval = null;
|
||||
|
||||
const connectBtn = document.getElementById('connectBtn');
|
||||
const fetchPriceBtn = document.getElementById('fetchPriceBtn');
|
||||
const autoRefreshBtn = document.getElementById('autoRefreshBtn');
|
||||
const priceValue = document.getElementById('priceValue');
|
||||
const priceStatus = document.getElementById('priceStatus');
|
||||
const priceInfo = document.getElementById('priceInfo');
|
||||
const status = document.getElementById('status');
|
||||
|
||||
function showStatus(type, message) {
|
||||
status.className = `status ${type} show`;
|
||||
status.textContent = message;
|
||||
setTimeout(() => {
|
||||
status.classList.remove('show');
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
async function connectWallet() {
|
||||
if (typeof window.ethereum === 'undefined') {
|
||||
showStatus('error', 'MetaMask is not installed. Please install MetaMask to continue.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
showStatus('info', 'Connecting to MetaMask...');
|
||||
|
||||
// Request account access
|
||||
await window.ethereum.request({ method: 'eth_requestAccounts' });
|
||||
|
||||
// Check if on correct network
|
||||
const chainId = await window.ethereum.request({ method: 'eth_chainId' });
|
||||
const chainIdDecimal = parseInt(chainId, 16);
|
||||
|
||||
if (chainIdDecimal !== CHAIN_ID) {
|
||||
showStatus('info', 'Switching to ChainID 138...');
|
||||
try {
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [{ chainId: '0x8a' }] // 138 in hex
|
||||
});
|
||||
} catch (switchError) {
|
||||
if (switchError.code === 4902) {
|
||||
// Network doesn't exist, add it
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [{
|
||||
chainId: '0x8a',
|
||||
chainName: 'SMOM-DBIS-138',
|
||||
nativeCurrency: {
|
||||
name: 'Ether',
|
||||
symbol: 'ETH',
|
||||
decimals: 18
|
||||
},
|
||||
rpcUrls: [RPC_URL],
|
||||
blockExplorerUrls: ['https://explorer.d-bis.org']
|
||||
}]
|
||||
});
|
||||
} else {
|
||||
throw switchError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create provider and signer
|
||||
provider = new ethers.providers.Web3Provider(window.ethereum);
|
||||
signer = provider.getSigner();
|
||||
oracle = new ethers.Contract(ORACLE_ADDRESS, ORACLE_ABI, provider);
|
||||
|
||||
connectBtn.disabled = true;
|
||||
fetchPriceBtn.disabled = false;
|
||||
autoRefreshBtn.disabled = false;
|
||||
|
||||
showStatus('success', 'Connected to MetaMask!');
|
||||
await fetchPrice();
|
||||
} catch (error) {
|
||||
console.error('Error connecting:', error);
|
||||
showStatus('error', `Connection failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchPrice() {
|
||||
if (!oracle) {
|
||||
showStatus('error', 'Please connect MetaMask first');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
showStatus('info', 'Fetching price...');
|
||||
priceStatus.textContent = 'Loading...';
|
||||
|
||||
const result = await oracle.latestRoundData();
|
||||
const price = result.answer.toNumber() / 1e8; // Convert from 8 decimals
|
||||
const roundId = result.roundId.toString();
|
||||
const startedAt = new Date(result.startedAt.toNumber() * 1000).toLocaleString();
|
||||
const updatedAt = new Date(result.updatedAt.toNumber() * 1000).toLocaleString();
|
||||
|
||||
priceValue.textContent = `$${price.toFixed(2)}`;
|
||||
priceStatus.textContent = `Updated ${updatedAt}`;
|
||||
|
||||
document.getElementById('roundId').textContent = roundId;
|
||||
document.getElementById('updatedAt').textContent = updatedAt;
|
||||
document.getElementById('startedAt').textContent = startedAt;
|
||||
priceInfo.style.display = 'block';
|
||||
|
||||
showStatus('success', 'Price fetched successfully!');
|
||||
} catch (error) {
|
||||
console.error('Error fetching price:', error);
|
||||
showStatus('error', `Failed to fetch price: ${error.message}`);
|
||||
priceStatus.textContent = 'Error loading price';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAutoRefresh() {
|
||||
if (autoRefreshInterval) {
|
||||
clearInterval(autoRefreshInterval);
|
||||
autoRefreshInterval = null;
|
||||
autoRefreshBtn.textContent = 'Auto Refresh (60s)';
|
||||
showStatus('info', 'Auto refresh stopped');
|
||||
} else {
|
||||
autoRefreshInterval = setInterval(fetchPrice, 60000); // 60 seconds
|
||||
autoRefreshBtn.textContent = 'Stop Auto Refresh';
|
||||
showStatus('success', 'Auto refresh enabled (60s interval)');
|
||||
fetchPrice();
|
||||
}
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
connectBtn.addEventListener('click', connectWallet);
|
||||
fetchPriceBtn.addEventListener('click', fetchPrice);
|
||||
autoRefreshBtn.addEventListener('click', toggleAutoRefresh);
|
||||
|
||||
// Listen for network changes
|
||||
if (window.ethereum) {
|
||||
window.ethereum.on('chainChanged', (chainId) => {
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
window.ethereum.on('accountsChanged', (accounts) => {
|
||||
if (accounts.length === 0) {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user