import * as anchor from "@coral-xyz/anchor"; import { Transaction } from "@solana/web3.js"; import { getAssociatedTokenAddress } from "@solana/spl-token"; import { expect } from "chai"; import fetch from "node-fetch"; import { getTestContext, setTestContext } from "../setup"; describe("Register Companies (API)", () => { it("Register Companies with USDC Vaults via Backend API", async () => { const ctx = getTestContext(); const { connection, program, axumBaseUrl, registryPda, localAdminGermanyPda, localAdminFrancePda, company1Pda, company2Pda, localAdminGermany, localAdminFrance, company1Admin, company2Admin, euaMint, ghgMint, usdcMint, db, } = ctx; // ============================================================ // STEP 1: Check DB State Before // ============================================================ console.log("Checking database state before registration..."); const companyCountBefore = await db.getCompanyCount(); expect(companyCountBefore).to.equal(0); console.log("✓ No companies in database before registration"); // Check local admin companies_managed count const germanyAdminBefore = await db.getLocalAdmin(localAdminGermanyPda.toBase58()); const franceAdminBefore = await db.getLocalAdmin(localAdminFrancePda.toBase58()); expect(germanyAdminBefore.companies_managed).to.equal(0); expect(franceAdminBefore.companies_managed).to.equal(0); // ============================================================ // STEP 2: Calculate Token Vault Addresses for Company 1 (BMW) // ============================================================ console.log("Calculating vault addresses for BMW..."); const company1EuaVault = await getAssociatedTokenAddress( euaMint, company1Pda, true ); const company1GhgVault = await getAssociatedTokenAddress( ghgMint, company1Pda, true ); const company1UsdcVault = await getAssociatedTokenAddress( usdcMint, company1Pda, true ); console.log(`✓ BMW EUA Vault: ${company1EuaVault.toBase58()}`); console.log(`✓ BMW GHG Vault: ${company1GhgVault.toBase58()}`); console.log(`✓ BMW USDC Vault: ${company1UsdcVault.toBase58()}`); // ============================================================ // STEP 3: Register Company 1 (BMW) via API // ============================================================ console.log("Registering BMW Manufacturing GmbH..."); const bmwRequest = { registry: registryPda.toBase58(), local_admin: localAdminGermanyPda.toBase58(), company: company1Pda.toBase58(), eua_vault: company1EuaVault.toBase58(), ghg_vault: company1GhgVault.toBase58(), usdc_vault: company1UsdcVault.toBase58(), eua_mint: euaMint.toBase58(), ghg_mint: ghgMint.toBase58(), usdc_mint: usdcMint.toBase58(), company_admin: company1Admin.publicKey.toBase58(), payer: localAdminGermany.publicKey.toBase58(), company_name: "BMW Manufacturing GmbH", }; const createBmwTxResponse = await fetch( `${axumBaseUrl}/api/registry/register-company`, { 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 transaction: ${error}`); } const { transaction_base64: bmwTxBase64 } = await createBmwTxResponse.json(); const bmwTxBuffer = Buffer.from(bmwTxBase64, "base64"); const bmwTransaction = Transaction.from(bmwTxBuffer); bmwTransaction.sign(localAdminGermany); 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 transaction: ${error}`); } const { signature: bmwSignature } = await submitBmwTxResponse.json(); console.log(`✓ BMW transaction signature: ${bmwSignature}`); // ============================================================ // STEP 4: Calculate Token Vault Addresses for Company 2 (Total) // ============================================================ console.log("Calculating vault addresses for Total Energies..."); const company2EuaVault = await getAssociatedTokenAddress( euaMint, company2Pda, true ); const company2GhgVault = await getAssociatedTokenAddress( ghgMint, company2Pda, true ); const company2UsdcVault = await getAssociatedTokenAddress( usdcMint, company2Pda, true ); console.log(`✓ Total EUA Vault: ${company2EuaVault.toBase58()}`); console.log(`✓ Total GHG Vault: ${company2GhgVault.toBase58()}`); console.log(`✓ Total USDC Vault: ${company2UsdcVault.toBase58()}`); // ============================================================ // STEP 5: Register Company 2 (Total Energies) via API // ============================================================ console.log("Registering Total Energies SA..."); const totalRequest = { registry: registryPda.toBase58(), local_admin: localAdminFrancePda.toBase58(), company: company2Pda.toBase58(), eua_vault: company2EuaVault.toBase58(), ghg_vault: company2GhgVault.toBase58(), usdc_vault: company2UsdcVault.toBase58(), eua_mint: euaMint.toBase58(), ghg_mint: ghgMint.toBase58(), usdc_mint: usdcMint.toBase58(), company_admin: company2Admin.publicKey.toBase58(), payer: localAdminFrance.publicKey.toBase58(), company_name: "Total Energies SA", }; const createTotalTxResponse = await fetch( `${axumBaseUrl}/api/registry/register-company`, { 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 transaction: ${error}`); } const { transaction_base64: totalTxBase64 } = await createTotalTxResponse.json(); const totalTxBuffer = Buffer.from(totalTxBase64, "base64"); const totalTransaction = Transaction.from(totalTxBuffer); totalTransaction.sign(localAdminFrance); 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 transaction: ${error}`); } const { signature: totalSignature } = await submitTotalTxResponse.json(); console.log(`✓ Total transaction signature: ${totalSignature}`); // ============================================================ // STEP 6: Store Vault Addresses in Test Context // ============================================================ setTestContext({ company1EuaVault, company1GhgVault, company1UsdcVault, company2EuaVault, company2GhgVault, company2UsdcVault, }); console.log("✓ Vault addresses stored in test context"); // ============================================================ // STEP 7: Verify On-Chain State - Registry // ============================================================ console.log("Verifying registry state..."); const registry = await program.account.registry.fetch(registryPda); expect(registry.companyCount).to.equal(2); console.log(`✓ Registry company count: ${registry.companyCount}`); // ============================================================ // STEP 8: Verify On-Chain State - Company 1 (BMW) // ============================================================ console.log("Verifying BMW company state..."); const company1 = await program.account.company.fetch(company1Pda); expect(company1.name).to.equal("BMW Manufacturing GmbH"); expect(company1.companyAdmin.equals(company1Admin.publicKey)).to.be.true; expect(company1.localAdmin.equals(localAdminGermanyPda)).to.be.true; expect(company1.isActive).to.be.true; expect(company1.euaVault.equals(company1EuaVault)).to.be.true; expect(company1.ghgVault.equals(company1GhgVault)).to.be.true; expect(company1.usdcVault.equals(company1UsdcVault)).to.be.true; console.log(`✓ BMW company verified: ${company1.name}`); // ============================================================ // STEP 9: Verify On-Chain State - Company 2 (Total) // ============================================================ console.log("Verifying Total Energies company state..."); const company2 = await program.account.company.fetch(company2Pda); expect(company2.name).to.equal("Total Energies SA"); expect(company2.companyAdmin.equals(company2Admin.publicKey)).to.be.true; expect(company2.localAdmin.equals(localAdminFrancePda)).to.be.true; expect(company2.isActive).to.be.true; expect(company2.euaVault.equals(company2EuaVault)).to.be.true; expect(company2.ghgVault.equals(company2GhgVault)).to.be.true; expect(company2.usdcVault.equals(company2UsdcVault)).to.be.true; console.log(`✓ Total company verified: ${company2.name}`); // ============================================================ // STEP 10: Verify Local Admin State Updates // ============================================================ console.log("Verifying local admin state updates..."); const germanyLocalAdmin = await program.account.localAdmin.fetch( localAdminGermanyPda ); expect(germanyLocalAdmin.companiesManaged).to.equal(1); console.log( `✓ Germany local admin companies managed: ${germanyLocalAdmin.companiesManaged}` ); const franceLocalAdmin = await program.account.localAdmin.fetch( localAdminFrancePda ); expect(franceLocalAdmin.companiesManaged).to.equal(1); console.log( `✓ France local admin companies managed: ${franceLocalAdmin.companiesManaged}` ); // ============================================================ // STEP 11: Verify Token Vaults Created // ============================================================ console.log("Verifying token vault accounts created..."); const bmwEuaVaultInfo = await connection.getAccountInfo(company1EuaVault); const bmwGhgVaultInfo = await connection.getAccountInfo(company1GhgVault); const bmwUsdcVaultInfo = await connection.getAccountInfo(company1UsdcVault); expect(bmwEuaVaultInfo).to.not.be.null; expect(bmwGhgVaultInfo).to.not.be.null; expect(bmwUsdcVaultInfo).to.not.be.null; console.log("✓ BMW token vaults created"); const totalEuaVaultInfo = await connection.getAccountInfo(company2EuaVault); const totalGhgVaultInfo = await connection.getAccountInfo(company2GhgVault); const totalUsdcVaultInfo = await connection.getAccountInfo(company2UsdcVault); expect(totalEuaVaultInfo).to.not.be.null; expect(totalGhgVaultInfo).to.not.be.null; expect(totalUsdcVaultInfo).to.not.be.null; console.log("✓ Total token vaults created"); // ============================================================ // STEP 12: Verify DB State After (Wait for Event Handler) // ============================================================ console.log("Verifying database state after registration..."); // Wait for companies to appear in DB const bmwInDb = await db.waitForCompany(company1Pda.toBase58(), 5000); expect(bmwInDb).to.be.true; const totalInDb = await db.waitForCompany(company2Pda.toBase58(), 5000); expect(totalInDb).to.be.true; // Get company count const companyCountAfter = await db.getCompanyCount(); expect(companyCountAfter).to.equal(2); console.log(`✓ Database has ${companyCountAfter} companies`); // Verify BMW in DB const bmwDbRecord = await db.getCompany(company1Pda.toBase58()); expect(bmwDbRecord).to.not.be.undefined; expect(bmwDbRecord.company_name).to.equal("BMW Manufacturing GmbH"); expect(bmwDbRecord.company_admin_pubkey).to.equal(company1Admin.publicKey.toBase58()); expect(bmwDbRecord.local_admin_pubkey).to.equal(localAdminGermanyPda.toBase58()); expect(bmwDbRecord.is_active).to.be.true; expect(bmwDbRecord.eua_vault).to.equal(company1EuaVault.toBase58()); expect(bmwDbRecord.ghg_vault).to.equal(company1GhgVault.toBase58()); expect(bmwDbRecord.usdc_vault).to.equal(company1UsdcVault.toBase58()); expect(bmwDbRecord.locked_eua_amount).to.equal("0"); expect(bmwDbRecord.locked_usdc_amount).to.equal("0"); expect(bmwDbRecord.eua_balance).to.equal("0"); expect(bmwDbRecord.ghg_balance).to.equal("0"); expect(bmwDbRecord.usdc_balance).to.equal("0"); console.log(`✓ BMW verified in database: ${bmwDbRecord.company_name}`); // Verify Total in DB const totalDbRecord = await db.getCompany(company2Pda.toBase58()); expect(totalDbRecord).to.not.be.undefined; expect(totalDbRecord.company_name).to.equal("Total Energies SA"); expect(totalDbRecord.company_admin_pubkey).to.equal(company2Admin.publicKey.toBase58()); expect(totalDbRecord.local_admin_pubkey).to.equal(localAdminFrancePda.toBase58()); expect(totalDbRecord.is_active).to.be.true; expect(totalDbRecord.eua_vault).to.equal(company2EuaVault.toBase58()); expect(totalDbRecord.ghg_vault).to.equal(company2GhgVault.toBase58()); expect(totalDbRecord.usdc_vault).to.equal(company2UsdcVault.toBase58()); expect(totalDbRecord.locked_eua_amount).to.equal("0"); expect(totalDbRecord.locked_usdc_amount).to.equal("0"); expect(totalDbRecord.eua_balance).to.equal("0"); expect(totalDbRecord.ghg_balance).to.equal("0"); expect(totalDbRecord.usdc_balance).to.equal("0"); console.log(`✓ Total verified in database: ${totalDbRecord.company_name}`); // Verify local admin companies_managed updated in DB const germanyAdminAfter = await db.getLocalAdmin(localAdminGermanyPda.toBase58()); const franceAdminAfter = await db.getLocalAdmin(localAdminFrancePda.toBase58()); // Note: companies_managed might not be updated yet depending on event handler implementation // If your event handler updates this, uncomment: // expect(germanyAdminAfter.companies_managed).to.equal(1); // expect(franceAdminAfter.companies_managed).to.equal(1); // Verify companies by local admin const germanyCompanies = await db.getCompaniesByLocalAdmin(localAdminGermanyPda.toBase58()); expect(germanyCompanies.length).to.equal(1); expect(germanyCompanies[0].company_name).to.equal("BMW Manufacturing GmbH"); const franceCompanies = await db.getCompaniesByLocalAdmin(localAdminFrancePda.toBase58()); expect(franceCompanies.length).to.equal(1); expect(franceCompanies[0].company_name).to.equal("Total Energies SA"); console.log("\n=== Summary ==="); console.log(`On-Chain: ${registry.companyCount} companies`); console.log(`Database: ${companyCountAfter} companies`); console.log("✓ Companies registered with USDC vaults successfully via API"); console.log("✓ Database state matches on-chain state"); }); });