SHA256
Переписать shine_payments и обновить тестовый UI с известным багом state
This commit is contained in:
@@ -66,7 +66,7 @@
|
||||
<button id="updateCoefBtn">Обновить</button>
|
||||
</div>
|
||||
<div class="formula">Лимит покупки Q1 = max(limit_usd_cents - q1_sum_total_usd_cents, 0)</div>
|
||||
<div class="formula">Шаг выплаты Q1 = ticket + dao(1x) + reward; Q2 = ticket + dao(2x) + reward</div>
|
||||
<div class="formula">Шаг выплаты Q1 = ticket + dao(1x) + reward; Q2 = ticket + dao(2x) + reward; Q3 = ticket + dao(3x) + reward</div>
|
||||
<div id="updateResult" class="muted"></div>
|
||||
</div>
|
||||
|
||||
@@ -100,6 +100,12 @@
|
||||
<div class="row"><button id="loadQ2Btn">Показать очередь 2</button></div>
|
||||
<div id="queue2Table" class="muted"></div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<h3>Очередь 3 (все билеты)</h3>
|
||||
<div class="row"><button id="loadQ3Btn">Показать очередь 3</button></div>
|
||||
<div id="queue3Table" class="muted"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/@solana/web3.js@1.95.3/lib/index.iife.min.js"></script>
|
||||
@@ -109,13 +115,16 @@
|
||||
const RPC_URL = "https://api.devnet.solana.com";
|
||||
const connection = new solanaWeb3.Connection(RPC_URL, "confirmed");
|
||||
const SEEDS = {
|
||||
config: "shine_payments_v3_config",
|
||||
coef: "shine_payments_v3_coef_limit",
|
||||
queues: "shine_payments_v3_queues",
|
||||
inflow: "shine_payments_v3_inflow_vault",
|
||||
ticketQ1: "shine_payments_v3_q1_ticket",
|
||||
ticketQ2: "shine_payments_v3_q2_ticket",
|
||||
config: "shine_payments_config",
|
||||
coef: "shine_payments_coef_limit",
|
||||
queues: "shine_payments_queues",
|
||||
inflow: "shine_payments_inflow_vault",
|
||||
ticketQ1: "shine_payments_q1_ticket",
|
||||
ticketQ2: "shine_payments_q2_ticket",
|
||||
ticketQ3: "shine_payments_q3_ticket",
|
||||
};
|
||||
const IX = { init: 1, updateCoefLimit: 2 };
|
||||
const USERS_IX = { initUsersEconomyConfig: 1, updateUsersEconomyConfig: 2 };
|
||||
const USERS_SEEDS = {
|
||||
economyConfig: "shine_users_v1_economy_config",
|
||||
};
|
||||
@@ -162,10 +171,8 @@
|
||||
if (!Number.isFinite(v) || v <= 0) throw new Error("Некорректная сумма USD");
|
||||
return BigInt(Math.round(v * 100));
|
||||
}
|
||||
async function ixDiscriminator(name) {
|
||||
const msg = utf8("global:" + name);
|
||||
const hash = await crypto.subtle.digest("SHA-256", msg);
|
||||
return new Uint8Array(hash).slice(0, 8);
|
||||
function ixData(tag, ...parts) {
|
||||
return concat(new Uint8Array([tag]), ...parts);
|
||||
}
|
||||
function isUnauthorizedDao(msg) {
|
||||
const s = String(msg || "").toLowerCase();
|
||||
@@ -202,7 +209,11 @@
|
||||
const q2Paid = readU64(data, o); o += 8;
|
||||
const q2SumTotal = readU64(data, o); o += 8;
|
||||
const q2SumPaid = readU64(data, o); o += 8;
|
||||
return { version, q1Total, q1Paid, q1SumTotal, q1SumPaid, q2Total, q2Paid, q2SumTotal, q2SumPaid };
|
||||
const q3Total = readU64(data, o); o += 8;
|
||||
const q3Paid = readU64(data, o); o += 8;
|
||||
const q3SumTotal = readU64(data, o); o += 8;
|
||||
const q3SumPaid = readU64(data, o); o += 8;
|
||||
return { version, q1Total, q1Paid, q1SumTotal, q1SumPaid, q2Total, q2Paid, q2SumTotal, q2SumPaid, q3Total, q3Paid, q3SumTotal, q3SumPaid };
|
||||
}
|
||||
function parseTicket(data) {
|
||||
let o = 0;
|
||||
@@ -263,7 +274,7 @@
|
||||
return { usersEconomyConfigPda };
|
||||
}
|
||||
function ticketPda(queueId, index) {
|
||||
const seed = queueId === 1 ? SEEDS.ticketQ1 : SEEDS.ticketQ2;
|
||||
const seed = queueId === 1 ? SEEDS.ticketQ1 : (queueId === 2 ? SEEDS.ticketQ2 : SEEDS.ticketQ3);
|
||||
const [pda] = solanaWeb3.PublicKey.findProgramAddressSync([utf8(seed), u64ToBytes(index)], PROGRAM_ID);
|
||||
return pda;
|
||||
}
|
||||
@@ -320,6 +331,7 @@
|
||||
<div>Доступно в inflow (сверх ренты): <b>${lamportsToSolStr(core.inflowAvailable)} SOL</b></div>
|
||||
<div>Q1: total=${core.queues.q1Total}, paid=${core.queues.q1Paid}, sum_total=${centsToUsdStr(core.queues.q1SumTotal)} USD, sum_paid=${centsToUsdStr(core.queues.q1SumPaid)} USD</div>
|
||||
<div>Q2: total=${core.queues.q2Total}, paid=${core.queues.q2Paid}, sum_total=${centsToUsdStr(core.queues.q2SumTotal)} USD, sum_paid=${centsToUsdStr(core.queues.q2SumPaid)} USD</div>
|
||||
<div>Q3: total=${core.queues.q3Total}, paid=${core.queues.q3Paid}, sum_total=${centsToUsdStr(core.queues.q3SumTotal)} USD, sum_paid=${centsToUsdStr(core.queues.q3SumPaid)} USD</div>
|
||||
`;
|
||||
} catch (e) {
|
||||
el.innerHTML = `<span class="err">${String(e.message || e)}</span>`;
|
||||
@@ -362,7 +374,6 @@
|
||||
else if (!provider.isConnected) await provider.connect();
|
||||
|
||||
const pdas = derivePdas();
|
||||
const disc = await ixDiscriminator("init");
|
||||
const keys = [
|
||||
{ pubkey: walletPubkey, isSigner: true, isWritable: true },
|
||||
{ pubkey: pdas.configPda, isSigner: false, isWritable: true },
|
||||
@@ -371,7 +382,7 @@
|
||||
{ pubkey: pdas.inflowPda, isSigner: false, isWritable: true },
|
||||
{ pubkey: solanaWeb3.SystemProgram.programId, isSigner: false, isWritable: false },
|
||||
];
|
||||
const ix = new solanaWeb3.TransactionInstruction({ programId: PROGRAM_ID, keys, data: disc });
|
||||
const ix = new solanaWeb3.TransactionInstruction({ programId: PROGRAM_ID, keys, data: ixData(IX.init) });
|
||||
const sig = await sendInstruction(ix);
|
||||
out.innerHTML = `<span class="ok">Init выполнен. Tx: <code>${sig}</code></span>`;
|
||||
await refreshAll();
|
||||
@@ -397,8 +408,7 @@
|
||||
const rewardLamports = solToLamports(document.getElementById("rewardInput").value.trim());
|
||||
if (rewardLamports > MAX_REWARD_LAMPORTS) throw new Error("Награда не должна быть больше 0.01 SOL");
|
||||
|
||||
const disc = await ixDiscriminator("update_coef_limit");
|
||||
const data = concat(disc, u64ToBytes(coefPpm), u64ToBytes(limitUsdCents), u64ToBytes(rewardLamports));
|
||||
const data = ixData(IX.updateCoefLimit, u64ToBytes(coefPpm), u64ToBytes(limitUsdCents), u64ToBytes(rewardLamports));
|
||||
const keys = [
|
||||
{ pubkey: walletPubkey, isSigner: true, isWritable: true },
|
||||
{ pubkey: core.pdas.configPda, isSigner: false, isWritable: true },
|
||||
@@ -427,13 +437,12 @@
|
||||
if (!walletPubkey) await connectWallet();
|
||||
else if (!provider.isConnected) await provider.connect();
|
||||
const usersPdas = deriveUsersPdas();
|
||||
const disc = await ixDiscriminator("init_users_economy_config");
|
||||
const keys = [
|
||||
{ pubkey: walletPubkey, isSigner: true, isWritable: true },
|
||||
{ pubkey: usersPdas.usersEconomyConfigPda, isSigner: false, isWritable: true },
|
||||
{ pubkey: solanaWeb3.SystemProgram.programId, isSigner: false, isWritable: false },
|
||||
];
|
||||
const ix = new solanaWeb3.TransactionInstruction({ programId: USERS_PROGRAM_ID, keys, data: disc });
|
||||
const ix = new solanaWeb3.TransactionInstruction({ programId: USERS_PROGRAM_ID, keys, data: ixData(USERS_IX.initUsersEconomyConfig) });
|
||||
const sig = await sendInstruction(ix);
|
||||
out.innerHTML = `<span class="ok">Users Economy init выполнен. Tx: <code>${sig}</code></span>`;
|
||||
await refreshUsersEconomy();
|
||||
@@ -455,9 +464,8 @@
|
||||
const startBonusLimit = BigInt(document.getElementById("usersBonusInput").value.trim());
|
||||
if (startBonusLimit < 0n) throw new Error("Стартовый бонус не может быть отрицательным");
|
||||
|
||||
const disc = await ixDiscriminator("update_users_economy_config");
|
||||
const data = concat(
|
||||
disc,
|
||||
const data = ixData(
|
||||
USERS_IX.updateUsersEconomyConfig,
|
||||
u64ToBytes(registrationFeeLamports),
|
||||
u64ToBytes(lamportsPerLimitStep),
|
||||
u64ToBytes(startBonusLimit)
|
||||
@@ -483,17 +491,17 @@
|
||||
|
||||
function currentDebtBeforeTicket(ticket, queues) {
|
||||
if (ticket.isPaid) return 0n;
|
||||
const paidSum = ticket.queueId === 1 ? queues.q1SumPaid : queues.q2SumPaid;
|
||||
const paidSum = ticket.queueId === 1 ? queues.q1SumPaid : (ticket.queueId === 2 ? queues.q2SumPaid : queues.q3SumPaid);
|
||||
return ticket.debtBefore > paidSum ? (ticket.debtBefore - paidSum) : 0n;
|
||||
}
|
||||
|
||||
async function showQueue(queueId) {
|
||||
const out = document.getElementById(queueId === 1 ? "queue1Table" : "queue2Table");
|
||||
const out = document.getElementById(queueId === 1 ? "queue1Table" : (queueId === 2 ? "queue2Table" : "queue3Table"));
|
||||
out.textContent = "Загрузка...";
|
||||
try {
|
||||
const core = await loadCore();
|
||||
if (core.notInited) throw new Error("Сначала выполните init");
|
||||
const total = queueId === 1 ? core.queues.q1Total : core.queues.q2Total;
|
||||
const total = queueId === 1 ? core.queues.q1Total : (queueId === 2 ? core.queues.q2Total : core.queues.q3Total);
|
||||
if (total === 0n) {
|
||||
out.innerHTML = `<span class="muted">Очередь ${queueId} пока пустая.</span>`;
|
||||
return;
|
||||
@@ -555,6 +563,7 @@
|
||||
document.getElementById("usersUpdateBtn").addEventListener("click", updateUsersEconomy);
|
||||
document.getElementById("loadQ1Btn").addEventListener("click", () => showQueue(1));
|
||||
document.getElementById("loadQ2Btn").addEventListener("click", () => showQueue(2));
|
||||
document.getElementById("loadQ3Btn").addEventListener("click", () => showQueue(3));
|
||||
refreshAll();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user