import * as anchor from "@coral-xyz/anchor"; import { Transaction } from "@solana/web3.js"; import { TOKEN_PROGRAM_ID, getAccount } from "@solana/spl-token"; import { expect } from "chai"; import fetch from "node-fetch"; import { getTestContext } from "../setup"; describe("Allocate EUA Tokens (API)", () => { it("Allocate EUA Tokens to Companies via Backend API", async () => { const ctx = getTestContext(); const { connection, program, axumBaseUrl, registryPda, company1Pda, company2Pda, globalAdmin, euaMint, company1EuaVault, company2EuaVault, db, } = ctx; // ============================================================ // STEP 1: Check DB State Before // ============================================================ console.log("Checking database state before EUA allocation..."); // Check initial balances const bmwBefore = await db.getCompany(company1Pda.toBase58()); const totalBefore = await db.getCompany(company2Pda.toBase58()); expect(bmwBefore.eua_balance).to.equal("0"); expect(totalBefore.eua_balance).to.equal("0"); // Check minting logs (should be empty) const bmwMintLogsBefore = await db.getEuaMintingLogs(company1Pda.toBase58()); const totalMintLogsBefore = await db.getEuaMintingLogs(company2Pda.toBase58()); const bmwMintedBefore = await db.getTotalEuaMinted(company1Pda.toBase58()); const totalMintedBefore = await db.getTotalEuaMinted(company2Pda.toBase58()); console.log(`✓ BMW initial EUA balance: ${bmwBefore.eua_balance}`); console.log(`✓ Total initial EUA balance: ${totalBefore.eua_balance}`); console.log(`✓ BMW minting logs: ${bmwMintLogsBefore.length}`); console.log(`✓ Total minting logs: ${totalMintLogsBefore.length}`); // ============================================================ // STEP 2: Allocate 1000 EUA to Company 1 (BMW) // ============================================================ console.log("Allocating 1000 EUA tokens to BMW..."); const bmwEuaAmount = 1000; const bmwRequest = { registry: registryPda.toBase58(), company: company1Pda.toBase58(), eua_mint: euaMint.toBase58(), eua_vault: company1EuaVault.toBase58(), global_admin: globalAdmin.publicKey.toBase58(), token_program: TOKEN_PROGRAM_ID.toBase58(), amount: bmwEuaAmount, }; const createBmwTxResponse = await fetch( `${axumBaseUrl}/api/registry/mint-eua`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(bmwRequest), } ); if (!createBmwTxResponse.ok) { const error = await createBmwTxResponse.text(); throw new Error(`Failed to create BMW EUA mint transaction: ${error}`); } const { transaction_base64: bmwTxBase64 } = await createBmwTxResponse.json(); const bmwTxBuffer = Buffer.from(bmwTxBase64, "base64"); const bmwTransaction = Transaction.from(bmwTxBuffer); bmwTransaction.sign(globalAdmin); const signedBmwTxBase64 = bmwTransaction.serialize().toString("base64"); const submitBmwTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedBmwTxBase64 }), }); if (!submitBmwTxResponse.ok) { const error = await submitBmwTxResponse.text(); throw new Error(`Failed to submit BMW EUA mint transaction: ${error}`); } const { signature: bmwSignature } = await submitBmwTxResponse.json(); console.log(`✓ BMW EUA mint transaction signature: ${bmwSignature}`); // ============================================================ // STEP 3: Allocate 500 EUA to Company 2 (Total Energies) // ============================================================ console.log("Allocating 500 EUA tokens to Total Energies..."); const totalEuaAmount = 500; const totalRequest = { registry: registryPda.toBase58(), company: company2Pda.toBase58(), eua_mint: euaMint.toBase58(), eua_vault: company2EuaVault.toBase58(), global_admin: globalAdmin.publicKey.toBase58(), token_program: TOKEN_PROGRAM_ID.toBase58(), amount: totalEuaAmount, }; const createTotalTxResponse = await fetch( `${axumBaseUrl}/api/registry/mint-eua`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(totalRequest), } ); if (!createTotalTxResponse.ok) { const error = await createTotalTxResponse.text(); throw new Error(`Failed to create Total EUA mint transaction: ${error}`); } const { transaction_base64: totalTxBase64 } = await createTotalTxResponse.json(); const totalTxBuffer = Buffer.from(totalTxBase64, "base64"); const totalTransaction = Transaction.from(totalTxBuffer); totalTransaction.sign(globalAdmin); const signedTotalTxBase64 = totalTransaction.serialize().toString("base64"); const submitTotalTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedTotalTxBase64 }), }); if (!submitTotalTxResponse.ok) { const error = await submitTotalTxResponse.text(); throw new Error(`Failed to submit Total EUA mint transaction: ${error}`); } const { signature: totalSignature } = await submitTotalTxResponse.json(); console.log(`✓ Total EUA mint transaction signature: ${totalSignature}`); // ============================================================ // STEP 4: Verify EUA Token Balances On-Chain // ============================================================ console.log("Verifying EUA token balances on-chain..."); const bmwEuaAccount = await getAccount(connection, company1EuaVault); expect(bmwEuaAccount.amount.toString()).to.equal(bmwEuaAmount.toString()); console.log(`✓ BMW EUA vault balance: ${bmwEuaAccount.amount} EUA`); const totalEuaAccount = await getAccount(connection, company2EuaVault); expect(totalEuaAccount.amount.toString()).to.equal(totalEuaAmount.toString()); console.log(`✓ Total EUA vault balance: ${totalEuaAccount.amount} EUA`); // ============================================================ // STEP 5: Verify Token Account Ownership // ============================================================ console.log("Verifying token account ownership..."); expect(bmwEuaAccount.owner.equals(company1Pda)).to.be.true; console.log(`✓ BMW EUA vault owned by: ${bmwEuaAccount.owner.toBase58()}`); expect(totalEuaAccount.owner.equals(company2Pda)).to.be.true; console.log( `✓ Total EUA vault owned by: ${totalEuaAccount.owner.toBase58()}` ); // ============================================================ // STEP 6: Verify Token Mint // ============================================================ console.log("Verifying token mint addresses..."); expect(bmwEuaAccount.mint.equals(euaMint)).to.be.true; expect(totalEuaAccount.mint.equals(euaMint)).to.be.true; console.log(`✓ Both vaults hold tokens from EUA mint: ${euaMint.toBase58()}`); // ============================================================ // STEP 7: Verify Company Account State (Unchanged) // ============================================================ console.log("Verifying company account states..."); const company1 = await program.account.company.fetch(company1Pda); expect(company1.euaVault.equals(company1EuaVault)).to.be.true; expect(company1.isActive).to.be.true; console.log(`✓ BMW company account verified: ${company1.name}`); const company2 = await program.account.company.fetch(company2Pda); expect(company2.euaVault.equals(company2EuaVault)).to.be.true; expect(company2.isActive).to.be.true; console.log(`✓ Total company account verified: ${company2.name}`); // ============================================================ // STEP 8: Verify DB State After (Wait for Event Handler) // ============================================================ console.log("Verifying database state after EUA allocation..."); // Wait for EUA minting to be logged const bmwMintingLogged = await db.waitForEuaMintingLog( company1Pda.toBase58(), BigInt(bmwEuaAmount), 5000 ); expect(bmwMintingLogged).to.be.true; const totalMintingLogged = await db.waitForEuaMintingLog( company2Pda.toBase58(), BigInt(totalEuaAmount), 5000 ); expect(totalMintingLogged).to.be.true; // Verify BMW EUA balance in DB const bmwAfter = await db.getCompany(company1Pda.toBase58()); expect(bmwAfter.eua_balance).to.equal(bmwEuaAmount.toString()); console.log(`✓ BMW EUA balance in DB: ${bmwAfter.eua_balance}`); // Verify Total EUA balance in DB const totalAfter = await db.getCompany(company2Pda.toBase58()); expect(totalAfter.eua_balance).to.equal(totalEuaAmount.toString()); console.log(`✓ Total EUA balance in DB: ${totalAfter.eua_balance}`); // Verify minting logs const bmwMintLogsAfter = await db.getEuaMintingLogs(company1Pda.toBase58()); expect(bmwMintLogsAfter.length).to.be.greaterThan(0); const bmwLatestLog = bmwMintLogsAfter[0]; expect(bmwLatestLog.company_pubkey).to.equal(company1Pda.toBase58()); expect(bmwLatestLog.company_name).to.equal("BMW Manufacturing GmbH"); expect(bmwLatestLog.amount).to.equal(bmwEuaAmount.toString()); expect(bmwLatestLog.minted_by).to.equal(globalAdmin.publicKey.toBase58()); expect(bmwLatestLog.eua_mint).to.equal(euaMint.toBase58()); expect(bmwLatestLog.eua_vault).to.equal(company1EuaVault.toBase58()); console.log(`✓ BMW minting log verified (${bmwMintLogsAfter.length} total logs)`); const totalMintLogsAfter = await db.getEuaMintingLogs(company2Pda.toBase58()); expect(totalMintLogsAfter.length).to.be.greaterThan(0); const totalLatestLog = totalMintLogsAfter[0]; expect(totalLatestLog.company_pubkey).to.equal(company2Pda.toBase58()); expect(totalLatestLog.company_name).to.equal("Total Energies SA"); expect(totalLatestLog.amount).to.equal(totalEuaAmount.toString()); expect(totalLatestLog.minted_by).to.equal(globalAdmin.publicKey.toBase58()); expect(totalLatestLog.eua_mint).to.equal(euaMint.toBase58()); expect(totalLatestLog.eua_vault).to.equal(company2EuaVault.toBase58()); console.log(`✓ Total minting log verified (${totalMintLogsAfter.length} total logs)`); // Verify total minted amounts const bmwTotalMinted = await db.getTotalEuaMinted(company1Pda.toBase58()); const totalTotalMinted = await db.getTotalEuaMinted(company2Pda.toBase58()); expect(bmwTotalMinted).to.equal(BigInt(bmwEuaAmount)); expect(totalTotalMinted).to.equal(BigInt(totalEuaAmount)); console.log(`✓ BMW total minted: ${bmwTotalMinted} EUA`); console.log(`✓ Total total minted: ${totalTotalMinted} EUA`); // Verify global total const globalTotalMinted = await db.getTotalEuaMinted(); expect(globalTotalMinted).to.equal(BigInt(bmwEuaAmount + totalEuaAmount)); console.log(`✓ Global total minted: ${globalTotalMinted} EUA`); // ============================================================ // STEP 9: Summary // ============================================================ console.log("\n=== EUA Allocation Summary ==="); console.log(`BMW Manufacturing GmbH:`); console.log(` - On-Chain: ${bmwEuaAccount.amount} EUA`); console.log(` - Database: ${bmwAfter.eua_balance} EUA`); console.log(` - Minting Logs: ${bmwMintLogsAfter.length}`); console.log(`Total Energies SA:`); console.log(` - On-Chain: ${totalEuaAccount.amount} EUA`); console.log(` - Database: ${totalAfter.eua_balance} EUA`); console.log(` - Minting Logs: ${totalMintLogsAfter.length}`); console.log(`Full Amount of EUA Allocated:`); console.log(` - On-Chain: ${Number(bmwEuaAccount.amount) + Number(totalEuaAccount.amount)} EUA`); console.log(` - Database: ${globalTotalMinted} EUA`); console.log("✓ EUA tokens allocated successfully via API"); console.log("✓ Database state matches on-chain state"); }); });