import * as anchor from "@coral-xyz/anchor"; import { Transaction } from "@solana/web3.js"; import { expect } from "chai"; import fetch from "node-fetch"; import { getTestContext } from "../setup"; describe("Start Auction (API)", () => { it("Local Admin Germany Starts Published Auction", async () => { const ctx = getTestContext(); const { connection, program, axumBaseUrl, localAdminGermany, localAdminGermanyPda, auctionPda, auctionId, } = ctx; console.log("Starting auction..."); console.log(` Auction PDA: ${auctionPda.toBase58()}`); console.log(` Auction ID: ${auctionId}`); console.log(` Local Admin: ${localAdminGermanyPda.toBase58()}`); // ============================================================ // STEP 1: Verify Auction State Before (should be Published) // ============================================================ console.log("Verifying auction state before start..."); const auctionBefore = await program.account.auction.fetch(auctionPda); expect(auctionBefore.status).to.deep.equal({ published: {} }); console.log("✓ Auction status: Published"); // Wait for start_time to be reached (auction was published with start_time = current + 5 seconds) const startTime = auctionBefore.startTime.toNumber(); const currentTime = Math.floor(Date.now() / 1000); if (currentTime < startTime) { const waitTime = (startTime - currentTime + 10) * 1000; // +10 second buffer console.log(`Waiting ${waitTime}ms for auction start time...`); await new Promise(resolve => setTimeout(resolve, waitTime)); } // ============================================================ // STEP 2: Create API Request Payload // ============================================================ const request = { auction_pda: auctionPda.toBase58(), local_admin_pubkey: localAdminGermany.publicKey.toBase58(), }; console.log("Calling API to create start auction transaction..."); // ============================================================ // STEP 3: Call Axum API to Get Unsigned Transaction // ============================================================ const createTxResponse = await fetch( `${axumBaseUrl}/api/primary-market/start-auction`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request), } ); if (!createTxResponse.ok) { const error = await createTxResponse.text(); throw new Error(`Failed to create transaction: ${error}`); } const { transaction_base64, message } = await createTxResponse.json(); console.log(`API Response: ${message}`); // ============================================================ // STEP 4: Deserialize and Sign Transaction // ============================================================ const txBuffer = Buffer.from(transaction_base64, "base64"); const transaction = Transaction.from(txBuffer); console.log("Transaction signer (expected):"); console.log(" Local Admin Germany:", localAdminGermany.publicKey.toBase58()); // Sign with localAdminGermany (the only required signer) transaction.sign(localAdminGermany); console.log("Transaction signed locally"); // ============================================================ // STEP 5: Submit Signed Transaction to Backend // ============================================================ const signedTxBase64 = transaction.serialize().toString("base64"); const submitTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedTxBase64 }), }); if (!submitTxResponse.ok) { const error = await submitTxResponse.text(); throw new Error(`Failed to submit transaction: ${error}`); } const { signature } = await submitTxResponse.json(); console.log(`✓ Transaction signature: ${signature}`); // ============================================================ // STEP 6: Verify On-Chain State - AUCTION STARTED // ============================================================ console.log("Verifying auction state after start..."); const auctionAfter = await program.account.auction.fetch(auctionPda); expect(auctionAfter.status).to.deep.equal({ active: {} }); expect(auctionAfter.localAdmin.equals(localAdminGermanyPda)).to.be.true; expect(auctionAfter.auctionId.toNumber()).to.equal(auctionId); expect(auctionAfter.euaVolumeOffered.toNumber()).to.equal(1000); expect(auctionAfter.totalBids).to.equal(0); console.log("✓ Auction account state verified"); console.log(` - Auction ID: ${auctionAfter.auctionId.toNumber()}`); console.log(` - Status: Active`); console.log(` - EUA Volume Offered: ${auctionAfter.euaVolumeOffered.toNumber()}`); console.log(` - Total Bids: ${auctionAfter.totalBids}`); // ============================================================ // STEP 7: Verify Local Admin State (unchanged) // ============================================================ const localAdmin = await program.account.localAdmin.fetch(localAdminGermanyPda); expect(localAdmin.activeAuction).to.not.be.null; expect(localAdmin.activeAuction.equals(auctionPda)).to.be.true; console.log(`✓ Local Admin state verified:`); console.log(` - Active Auction: ${localAdmin.activeAuction.toBase58()}`); console.log(` - Total Auctions: ${localAdmin.totalAuctions.toNumber()}`); // ============================================================ // STEP 8: Verify Events/Logs // ============================================================ const txDetails = await connection.getTransaction(signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0, }); if (txDetails?.meta?.logMessages) { const logs = txDetails.meta.logMessages; const hasStartLog = logs.some((log) => log.includes("Auction started") || log.includes("AuctionStarted") ); if (hasStartLog) { console.log("✓ Auction started event found in logs"); } } console.log("✓ Auction started successfully via API"); console.log("\n=== Summary ==="); console.log(`Auction PDA: ${auctionPda.toBase58()}`); console.log(`Auction ID: ${auctionId}`); console.log(`Status: Published → Active`); console.log(`EUA Volume: ${auctionAfter.euaVolumeOffered.toNumber()}`); console.log(`Total Bids: ${auctionAfter.totalBids}`); }); // Optional: Test error cases // it("Should Reject Starting Auction Before Start Time", async () => { // // This would require publishing a new auction with a far future start time // // and trying to start it immediately // }); // it("Should Reject Starting Auction That Is Not Published", async () => { // // This would require having an auction in a different status // }); // it("Should Reject Starting Auction with Wrong Local Admin", async () => { // // Try to start with localAdminFrance instead of localAdminGermany // }); });