Repository navigation
Expand file tree
/
Copy pathdeploy-v4.html
More file actions
150 lines (140 loc) · 8.36 KB
/
Copy pathdeploy-v4.html
File metadata and controls
150 lines (140 loc) · 8.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex">
<title>Deploy TBlock hook V4</title>
<style>
:root{--fond:#04090c;--carte:#0b1c22;--ligne:#16323c;--texte:#f2fbff;--doux:#9cc3cf;--accent:#35d3f0;--ok:#3ddc97;--ko:#ff6b6b}
body{margin:0;background:var(--fond);color:var(--texte);font:15px/1.5 system-ui,-apple-system,Segoe UI,sans-serif}
main{max-width:640px;margin:0 auto;padding:24px 16px}
h1{font-size:20px;margin:0 0 6px}
h2{font-size:15px;margin:0 0 6px}
.carte{background:var(--carte);border:1px solid var(--ligne);border-radius:14px;padding:16px;margin:14px 0}
.ligne{display:flex;gap:10px;align-items:baseline;margin:6px 0}
.ligne b{min-width:18px}
code{font-size:12.5px;word-break:break-all;color:var(--doux)}
button{font:inherit;font-weight:700;padding:12px 18px;border-radius:12px;border:0;cursor:pointer;background:var(--accent);color:#012}
button[disabled]{opacity:.45;cursor:not-allowed}
.ok{color:var(--ok)} .ko{color:var(--ko)} .doux{color:var(--doux)}
ul{margin:6px 0 0;padding-left:18px} li{margin:4px 0}
</style>
</head>
<body>
<main>
<h1>Deploy the TBlock hook V4</h1>
<p class="doux">One transaction, signed by <b>your</b> wallet. This page never sees a key. The contract has no owner and
lands at the same address whoever sends it — the address depends only on its code.</p>
<div class="carte">
<h2>What the V4 changes</h2>
<ul class="doux">
<li><b>A market cannot be traded while it is empty.</b> Until now, between the life fee and the creator's first
liquidity, anyone could open the pool and push its price to the floor with a 1 wei swap — the launch then
failed and the ≈ $1 was lost. The V4 refuses swaps until liquidity has been minted.</li>
<li><b>No label, no market.</b> Bringing a block to life now requires the metadata engraved by this app's Create.
A block born on another launchpad is refused — that is the trade-off, said plainly.</li>
</ul>
</div>
<div class="carte">
<div class="ligne"><b id="c1">…</b><span>Base mainnet reachable, hook address still empty</span></div>
<div class="ligne"><b id="c2">…</b><span>Dry run on Base returns the expected hook address</span></div>
<div class="ligne"><b id="c3">…</b><span>Wallet connected on Base</span></div>
<p class="doux" style="margin:10px 0 0">Expected hook: <code id="attendu"></code></p>
<p class="doux">Fee wallet baked in: <code id="fee"></code> · life fee <code id="vie"></code> wei · label <code id="marq"></code></p>
</div>
<div class="carte">
<button id="connecter" type="button">Connect wallet</button>
<button id="deployer" type="button" disabled>Deploy — sign in your wallet</button>
<p id="etat" class="doux" style="margin:12px 0 0"></p>
<p id="relecture" class="doux" style="margin:8px 0 0"></p>
</div>
</main>
<script type="module">
/* ⛔ PAGE DE DEPLOIEMENT DU HOOK V4 (2026-09-20), copie de celle du V3 qui a servi. `cast` sur la machine de Phil n a
* pas le compte importe ; cette page laisse SON wallet signer.
* Le contrat part par le deployeur CREATE2 standard : l adresse ne depend QUE du code (aucun proprietaire), donc
* n importe quel wallet peut l envoyer. La page VERIFIE avant d autoriser la signature :
* 1. aucun code a l adresse attendue ; 2. un eth_call de la transaction rend exactement cette adresse ;
* 3. wallet sur Base.
* ⛔⛔ ET APRES : elle relit le contrat deploye, y compris `porteLeLabel(<un vrai block ne par notre app>)`. C est la
* seule verification qui ne peut PAS se faire avant — forge ne sait pas executer un B20 (code 0xEF, execute
* nativement par le noeud Base : l EVM locale rend OpcodeNotFound). Si elle rend `false`, le verrou du label
* refuserait nos propres createurs, et il ne faut surtout pas brancher l app dessus. */
import { envoyerDepuisWallet } from './envoi.js';
const $ = (s) => document.querySelector(s);
const RPC = 'https://mainnet.base.org';
const rpc = async (method, params) => {
const r = await fetch(RPC, { method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }) }).then((x) => x.json());
if (r.error) throw new Error(r.error.message || 'rpc error');
return r.result;
};
const marque = (id, ok, texte) => { const e = $(id); e.textContent = ok ? '✓' : '✗'; e.className = ok ? 'ok' : 'ko'; if (texte) e.title = texte; };
let tx = null, compte = null, pret = { vide: false, simu: false };
async function verifier() {
tx = await fetch('./deploy-v4.json', { cache: 'no-store' }).then((r) => r.json());
$('#attendu').textContent = tx.hook;
$('#fee').textContent = tx.feeWallet;
$('#vie').textContent = tx.fraisVieWei;
$('#marq').textContent = tx.marqueur;
const code = await rpc('eth_getCode', [tx.hook, 'latest']);
pret.vide = code === '0x';
marque('#c1', pret.vide);
if (!pret.vide) {
$('#etat').innerHTML = '<span class="ok">The hook V4 is already deployed at this address. Nothing to do.</span>';
void relire();
return;
}
const r = await rpc('eth_call', [{ to: tx.deployeur, data: tx.data, gas: '0x4c4b40' }, 'latest']);
pret.simu = ('0x' + String(r).slice(-40)).toLowerCase() === tx.hook.toLowerCase();
marque('#c2', pret.simu);
majBouton();
}
function majBouton() { $('#deployer').disabled = !(pret.vide && pret.simu && compte); }
/** ⛔ Les trois lectures qui comptent, faites SUR LA CHAINE une fois le contrat vivant. */
async function relire() {
const appel = async (data) => rpc('eth_call', [{ to: tx.hook, data }, 'latest']);
const lignes = [];
try {
/* ⛔ Les trois selecteurs sont MESURES avec `cast sig` et ecrits dans deploy-v4.json, jamais devines ici. */
const fee = '0x' + String(await appel(tx.selecteurs.feeWallet)).slice(-40);
lignes.push((fee.toLowerCase() === tx.feeWallet.toLowerCase() ? '✓' : '✗') + ' fee wallet: ' + fee);
const taille = BigInt(await appel(tx.selecteurs.tailleMarqueur));
lignes.push((taille === BigInt(tx.marqueur.length) ? '✓' : '✗') + ' label length: ' + taille);
const arg = tx.temoinLabel.replace(/^0x/, '').toLowerCase().padStart(64, '0');
const porte = BigInt(await appel(tx.selecteurs.porteLeLabel + arg)) === 1n;
lignes.push((porte ? '✓' : '✗') + ' a block born through this app passes the label check ('
+ tx.temoinLabel.slice(0, 10) + '…)' + (porte ? '' : ' — DO NOT switch the app to this hook'));
} catch (e) {
lignes.push('✗ could not read the deployed hook: ' + (e.message || e));
}
$('#relecture').innerHTML = lignes.map((l) => '<div>' + l.replace(/&/g, '&').replace(/</g, '<') + '</div>').join('');
}
$('#connecter').addEventListener('click', async () => {
if (!window.ethereum) { $('#etat').textContent = 'No wallet found in this browser.'; return; }
try {
const comptes = await window.ethereum.request({ method: 'eth_requestAccounts' });
compte = comptes && comptes[0];
try { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x2105' }] }); } catch (_) {}
const chaine = await window.ethereum.request({ method: 'eth_chainId' });
marque('#c3', !!compte && Number(chaine) === 8453);
if (Number(chaine) !== 8453) compte = null;
} catch (e) { $('#etat').textContent = 'Connection refused: ' + (e.message || e); }
majBouton();
});
$('#deployer').addEventListener('click', async () => {
$('#deployer').disabled = true;
$('#etat').textContent = 'Opening your wallet — check it shows Base and a contract call, then sign.';
const r = await envoyerDepuisWallet({ eth: window.ethereum, rpc, chaineAttendue: 8453, compte, to: tx.deployeur, data: tx.data, value: '0x0' });
if (r.etat !== 'CONFIRME') { $('#etat').innerHTML = '<span class="ko">Not done: ' + (r.pourquoi || r.etat) + '</span>'; majBouton(); return; }
const code = await rpc('eth_getCode', [tx.hook, 'latest']);
$('#etat').innerHTML = code !== '0x'
? '<span class="ok">Deployed. Hook V4 is live at ' + tx.hook + '.</span> Transaction: <code>' + r.hash + '</code>'
: '<span class="ko">Transaction confirmed but no code at the address yet — send me the hash: </span><code>' + r.hash + '</code>';
void relire();
});
verifier().catch((e) => { $('#etat').textContent = 'Could not read Base right now: ' + (e.message || e); });
</script>
</body>
</html>