SHA256
Синхронизировать изменения проекта
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Менеджерские билеты — Shine Payments Devnet</title>
|
||||
<title>Менеджерские билеты — Shine Payments Mainnet</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
@@ -33,13 +33,25 @@
|
||||
.ok { color: var(--ok); }
|
||||
.warn { color: var(--warn); }
|
||||
.err { color: var(--err); white-space: pre-wrap; }
|
||||
.err-box {
|
||||
margin-top: 10px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #4c2730;
|
||||
border-radius: 8px;
|
||||
background: #23151a;
|
||||
color: var(--err);
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.45;
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
code { background: var(--code); padding: 2px 4px; border-radius: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="topbar"><a class="back" href="./index.html">← На главную</a></div>
|
||||
<h1>Менеджер: создание билетов (Devnet)</h1>
|
||||
<h1>Менеджер: создание билетов (Mainnet)</h1>
|
||||
<div class="muted">Программа: <code id="programId"></code></div>
|
||||
|
||||
<div class="panel">
|
||||
@@ -78,7 +90,7 @@
|
||||
<script src="https://unpkg.com/@solana/web3.js@1.95.3/lib/index.iife.min.js"></script>
|
||||
<script>
|
||||
const PROGRAM_ID = new solanaWeb3.PublicKey("SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v");
|
||||
const RPC_URL = "https://api.mainnet-beta.solana.com";
|
||||
const RPC_URL = "https://solana-rpc.publicnode.com";
|
||||
const connection = new solanaWeb3.Connection(RPC_URL, "confirmed");
|
||||
const SEEDS = {
|
||||
managerAllowance: "shine_p_manager_allow",
|
||||
@@ -130,6 +142,55 @@
|
||||
return s.includes("managerlimitexceeded") || s.includes("invalidmanagerwallet");
|
||||
}
|
||||
|
||||
function safeJson(value) {
|
||||
try {
|
||||
return JSON.stringify(value, null, 2);
|
||||
} catch {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
function formatSolanaErrorDetails(error) {
|
||||
const parts = [];
|
||||
const msg = String(error?.message || error || "").trim();
|
||||
if (msg) parts.push(msg);
|
||||
|
||||
const logs = error?.logs || error?.transactionLogs || error?.simulationLogs || error?.data?.logs;
|
||||
if (Array.isArray(logs) && logs.length) {
|
||||
parts.push(`Logs:\n${logs.join("\n")}`);
|
||||
}
|
||||
|
||||
const errObj = error?.value?.err || error?.err || error?.data?.err;
|
||||
if (errObj) {
|
||||
parts.push(`Err:\n${safeJson(errObj)}`);
|
||||
}
|
||||
|
||||
if (error?.signature) {
|
||||
parts.push(`Signature: ${error.signature}`);
|
||||
}
|
||||
|
||||
return parts.length ? parts.join("\n\n") : "unknown";
|
||||
}
|
||||
|
||||
async function attachSolanaLogs(error) {
|
||||
if (!error) return error;
|
||||
|
||||
if (typeof error.getLogs === "function") {
|
||||
try {
|
||||
const logs = await error.getLogs(connection);
|
||||
if (Array.isArray(logs) && logs.length) {
|
||||
error.logs = logs;
|
||||
error.transactionLogs = logs;
|
||||
error.simulationLogs = logs;
|
||||
}
|
||||
} catch {
|
||||
// Игнорируем: часть RPC не умеет отдавать логи для expired/confirm ошибок.
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
function parseManagerAllowance(data) {
|
||||
let o = 0;
|
||||
const version = data[o++];
|
||||
@@ -183,8 +244,29 @@
|
||||
tx.recentBlockhash = bh.blockhash;
|
||||
const signed = await provider.signTransaction(tx);
|
||||
const sig = await connection.sendRawTransaction(signed.serialize(), { skipPreflight: false });
|
||||
await connection.confirmTransaction({ signature: sig, blockhash: bh.blockhash, lastValidBlockHeight: bh.lastValidBlockHeight }, "confirmed");
|
||||
return sig;
|
||||
try {
|
||||
await connection.confirmTransaction(
|
||||
{ signature: sig, blockhash: bh.blockhash, lastValidBlockHeight: bh.lastValidBlockHeight },
|
||||
"confirmed",
|
||||
);
|
||||
return { signature: sig, confirmed: true };
|
||||
} catch (confirmError) {
|
||||
confirmError.signature = sig;
|
||||
const txInfo = await connection.getTransaction(sig, {
|
||||
commitment: "confirmed",
|
||||
maxSupportedTransactionVersion: 0,
|
||||
});
|
||||
if (txInfo?.meta?.logMessages?.length) {
|
||||
confirmError.logs = txInfo.meta.logMessages;
|
||||
confirmError.transactionLogs = txInfo.meta.logMessages;
|
||||
}
|
||||
await attachSolanaLogs(confirmError);
|
||||
const postCheck = await Promise.resolve(typeof ix._postCheck === "function" ? ix._postCheck(sig) : null);
|
||||
if (postCheck?.success) {
|
||||
return { signature: sig, confirmed: false, postCheck };
|
||||
}
|
||||
throw confirmError;
|
||||
}
|
||||
}
|
||||
|
||||
function deriveManagerAllowancePda(managerWallet) {
|
||||
@@ -272,8 +354,17 @@
|
||||
{ pubkey: solanaWeb3.SystemProgram.programId, isSigner: false, isWritable: false },
|
||||
];
|
||||
const ix = new solanaWeb3.TransactionInstruction({ programId: PROGRAM_ID, keys, data });
|
||||
const sig = await sendInstruction(ix);
|
||||
out.innerHTML = `<span class="ok">Билет создан. Tx: <code>${sig}</code></span>`;
|
||||
ix._postCheck = async () => {
|
||||
const ai = await connection.getAccountInfo(ticketPda, "confirmed");
|
||||
if (!ai) return { success: false };
|
||||
return { success: true, ticketPda: ticketPda.toBase58() };
|
||||
};
|
||||
const result = await sendInstruction(ix);
|
||||
if (result.confirmed) {
|
||||
out.innerHTML = `<span class="ok">Билет создан. Tx: <code>${result.signature}</code></span>`;
|
||||
} else {
|
||||
out.innerHTML = `<span class="warn">Транзакция подтверждалась с задержкой, но билет создан on-chain. Tx: <code>${result.signature}</code></span>`;
|
||||
}
|
||||
await refresh();
|
||||
} catch (e) {
|
||||
const raw = String(e.message || e);
|
||||
@@ -281,7 +372,7 @@
|
||||
out.innerHTML = `<span class="warn">Операция отклонена: лимит менеджера недостаточен или кошелек не имеет прав менеджера.</span>`;
|
||||
return;
|
||||
}
|
||||
out.innerHTML = `<span class="err">${raw}</span>`;
|
||||
out.innerHTML = `<span class="err">Ошибка создания билета:</span><div class="err-box">${formatSolanaErrorDetails(e)}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user