feat(relayer): migrate to SPL-token (фантики)
- ops: readGlobal читает vaultBalance (getTokenAccountBalance), createBet/closeBet передают bettorToken/vaultToken/tokenProgram, ensureVaultFunded наливает токен (mintTo, allowOwnerOffCurve) - pda: vaultPda([global,vault]), config:.env.new SMART_TOKEN_MINT, суммы в токен-единицах - check.ts: 14 проверок сигнатур создания create/close под новым токеновым IDL - @solana/spl-token dependency; .gitignore + node_modules/dist
This commit is contained in:
+36
-11
@@ -1,4 +1,5 @@
|
||||
import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
|
||||
import { getMint } from "@solana/spl-token";
|
||||
import { Program } from "@anchor-lang/core";
|
||||
import { loadConfig, loadKeypair, SIDE_UP } from "./config";
|
||||
import { getPrice } from "./priceSource";
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
closeBet,
|
||||
readGlobal,
|
||||
ensureVaultFunded,
|
||||
ensureBettorTokenAccount,
|
||||
} from "./ops";
|
||||
import { betPda } from "./pda";
|
||||
import { makeConnection, makeProgram } from "./program";
|
||||
@@ -21,6 +23,8 @@ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
const STATUS_OPEN = 0;
|
||||
async function fundBettor(connection: any, bettorPubkey: PublicKey): Promise<void> {
|
||||
// SOL всё ещё нужен беттору: рент PDA-аккаунта ставки + комиссия за tx.
|
||||
// Саму ставку (фантики) он закрывает из своего токен-аккаунта (ATA).
|
||||
const bal = await connection.getBalance(bettorPubkey);
|
||||
if (bal < 2 * LAMPORTS_PER_SOL_NUM) {
|
||||
await connection.requestAirdrop(bettorPubkey, 5 * LAMPORTS_PER_SOL_NUM);
|
||||
@@ -29,13 +33,23 @@ async function fundBettor(connection: any, bettorPubkey: PublicKey): Promise<voi
|
||||
}
|
||||
}
|
||||
|
||||
/** Человекочитаемо-формат: токен-юниты -> "N.NNNNNN" по decimals. */
|
||||
function humanize(units: string | number | bigint, decimals: number): string {
|
||||
const n = BigInt(units);
|
||||
const sign = n < 0n ? "-" : "";
|
||||
const abs = n < 0n ? -n : n;
|
||||
const pow = 10n ** BigInt(decimals);
|
||||
return `${sign}${abs / pow}.${(abs % pow).toString().padStart(decimals, "0")}`;
|
||||
}
|
||||
|
||||
/** Закрывает ставку, повторяя при «equal price» (exit==entry) пока не зафиналится. */
|
||||
async function settleBet(
|
||||
program: Program<SmartUpdown>,
|
||||
id: any,
|
||||
entryPrice: number,
|
||||
relayer: any,
|
||||
bettor: PublicKey
|
||||
bettor: PublicKey,
|
||||
mint: PublicKey
|
||||
): Promise<void> {
|
||||
for (let attempt = 1; ; attempt++) {
|
||||
const exitPrice = await getPrice();
|
||||
@@ -44,7 +58,7 @@ async function settleBet(
|
||||
await sleep(2000);
|
||||
continue;
|
||||
}
|
||||
const sig = await closeBet(program, id, exitPrice, relayer, bettor);
|
||||
const sig = await closeBet(program, id, exitPrice, relayer, bettor, mint);
|
||||
console.log(`close_bet #${id} exit=${exitPrice} (attempt ${attempt}) tx=${sig}`);
|
||||
|
||||
const status = await getStatus(program, id);
|
||||
@@ -70,6 +84,15 @@ async function main(): Promise<void> {
|
||||
|
||||
console.log(`RPC: ${cfg.rpcUrl}`);
|
||||
console.log(`Admin/relayer: ${admin.publicKey.toBase58()}`);
|
||||
console.log(`Token mint: ${cfg.tokenMint.toBase58()}`);
|
||||
|
||||
// Decimals фантиков (см. state.rs = 6). Только для человекочитаемых логов.
|
||||
let decimals = 6;
|
||||
try {
|
||||
decimals = (await getMint(connection, cfg.tokenMint)).decimals;
|
||||
} catch {
|
||||
// mint ещё не создан (плейсхолдер) — дефолт из ТЗ контракта
|
||||
}
|
||||
|
||||
const global = await readGlobal(program, program.programId);
|
||||
if (!global) {
|
||||
@@ -79,21 +102,21 @@ async function main(): Promise<void> {
|
||||
);
|
||||
}
|
||||
await initialize(program, {
|
||||
minAmount: cfg.minAmountLamports,
|
||||
maxAmount: cfg.maxAmountLamports,
|
||||
minAmount: cfg.minAmount,
|
||||
maxAmount: cfg.maxAmount,
|
||||
multiplierBps: cfg.multiplierBps,
|
||||
expirySeconds: cfg.expirySeconds,
|
||||
}, admin);
|
||||
console.log("Контракт инициализирован.");
|
||||
} else {
|
||||
console.log(
|
||||
`Global: counter=${global.counter} vault=${(global.lamports / LAMPORTS_PER_SOL_NUM).toFixed(2)} SOL ` +
|
||||
`Global: counter=${global.counter} vault=${humanize(global.vaultBalance, decimals)} fant ` +
|
||||
`expiry=${global.expirySeconds}s paused=${global.paused}`
|
||||
);
|
||||
}
|
||||
|
||||
if (cfg.allowAirdrop) {
|
||||
await ensureVaultFunded(connection, program, 10 * LAMPORTS_PER_SOL_NUM);
|
||||
await ensureVaultFunded(connection, program, admin, cfg);
|
||||
}
|
||||
|
||||
const loops = process.argv.includes("--once") ? 1 : Infinity;
|
||||
@@ -101,24 +124,26 @@ async function main(): Promise<void> {
|
||||
|
||||
while (n < loops) {
|
||||
n++;
|
||||
const req = nextBetRequest(cfg.minAmountLamports, cfg.maxAmountLamports);
|
||||
const req = nextBetRequest(cfg.minAmount, cfg.maxAmount);
|
||||
await fundBettor(connection, req.bettor.publicKey);
|
||||
// Беттору нужен токен-аккаунт (ATA) — без него create_bet не пройдёт.
|
||||
await ensureBettorTokenAccount(connection, admin, cfg.tokenMint, req.bettor.publicKey);
|
||||
|
||||
const entryPrice = await getPrice();
|
||||
await createBet(program, req.bettor, {
|
||||
await createBet(program, cfg.tokenMint, req.bettor, {
|
||||
side: req.side,
|
||||
amount: req.amountLamports,
|
||||
amount: req.amount,
|
||||
entryPrice,
|
||||
});
|
||||
|
||||
console.log(
|
||||
`ставка ${req.side === SIDE_UP ? "UP" : "DOWN"} на ${(req.amountLamports / LAMPORTS_PER_SOL_NUM).toFixed(3)} SOL, жду ${cfg.expirySeconds}s...`
|
||||
`ставка ${req.side === SIDE_UP ? "UP" : "DOWN"} на ${humanize(req.amount, decimals)} fant, жду ${cfg.expirySeconds}s...`
|
||||
);
|
||||
await sleep((cfg.expirySeconds + 1) * 1000);
|
||||
|
||||
const g0 = await readGlobal(program, program.programId);
|
||||
const id = g0!.counter.subn(1);
|
||||
await settleBet(program, id, entryPrice, admin, req.bettor.publicKey);
|
||||
await settleBet(program, id, entryPrice, admin, req.bettor.publicKey, cfg.tokenMint);
|
||||
|
||||
const outcome = await program.account.bet.fetchNullable(betPda(program.programId, id));
|
||||
console.log(
|
||||
|
||||
Reference in New Issue
Block a user