import * as anchor from "@coral-xyz/anchor"; import { Transaction } from "@solana/web3.js"; import { expect } from "chai"; import fetch from "node-fetch"; import { getTestContext, setTestContext } from "../setup"; import { getAssociatedTokenAddress, TOKEN_PROGRAM_ID, getAccount, } from "@solana/spl-token"; describe("Register Local Admins (API)", () => { it("Register Local Admins via Backend API", async () => { const ctx = getTestContext(); const { connection, program, axumBaseUrl, globalAdmin, registryPda, localAdminGermanyPda, localAdminFrancePda, localAdminGermany, localAdminFrance, euaMint, usdcMint, db, } = ctx; // ============================================================ // STEP 1: Check DB State Before // ============================================================ console.log("Checking database state before registration..."); const localAdminCountBefore = await db.getLocalAdminCount(); expect(localAdminCountBefore).to.equal(0); console.log("✓ No local admins in database before registration"); // ============================================================ // STEP 2: Calculate vault addresses for Germany Local Admin // ============================================================ const localAdminGermanyEuaVault = await getAssociatedTokenAddress( euaMint, localAdminGermanyPda, true ); const localAdminGermanyUsdcVault = await getAssociatedTokenAddress( usdcMint, localAdminGermanyPda, true ); // ============================================================ // STEP 3: Register Germany Local Admin // ============================================================ console.log("Registering Germany local admin..."); const germanyRequest = { registry: registryPda.toBase58(), local_admin: localAdminGermanyPda.toBase58(), admin_pubkey: localAdminGermany.publicKey.toBase58(), global_admin: globalAdmin.publicKey.toBase58(), state_name: "Germany", eua_vault: localAdminGermanyEuaVault.toBase58(), usdc_vault: localAdminGermanyUsdcVault.toBase58(), eua_mint: euaMint.toBase58(), usdc_mint: usdcMint.toBase58(), }; const createGermanyTxResponse = await fetch( `${axumBaseUrl}/api/registry/register-local-admin`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(germanyRequest), } ); if (!createGermanyTxResponse.ok) { const error = await createGermanyTxResponse.text(); throw new Error(`Failed to create Germany transaction: ${error}`); } const { transaction_base64: germanyTxBase64 } = await createGermanyTxResponse.json(); const germanyTxBuffer = Buffer.from(germanyTxBase64, "base64"); const germanyTransaction = Transaction.from(germanyTxBuffer); germanyTransaction.sign(globalAdmin); const signedGermanyTxBase64 = germanyTransaction.serialize().toString("base64"); const submitGermanyTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedGermanyTxBase64 }), }); if (!submitGermanyTxResponse.ok) { const error = await submitGermanyTxResponse.text(); throw new Error(`Failed to submit Germany transaction: ${error}`); } const { signature: germanySignature } = await submitGermanyTxResponse.json(); console.log(`✓ Germany transaction signature: ${germanySignature}`); // ============================================================ // STEP 4: Calculate vault addresses for France Local Admin // ============================================================ const localAdminFranceEuaVault = await getAssociatedTokenAddress( euaMint, localAdminFrancePda, true ); const localAdminFranceUsdcVault = await getAssociatedTokenAddress( usdcMint, localAdminFrancePda, true ); // ============================================================ // STEP 5: Register France Local Admin // ============================================================ console.log("Registering France local admin..."); const franceRequest = { registry: registryPda.toBase58(), local_admin: localAdminFrancePda.toBase58(), admin_pubkey: localAdminFrance.publicKey.toBase58(), global_admin: globalAdmin.publicKey.toBase58(), state_name: "France", eua_vault: localAdminFranceEuaVault.toBase58(), usdc_vault: localAdminFranceUsdcVault.toBase58(), eua_mint: euaMint.toBase58(), usdc_mint: usdcMint.toBase58(), }; const createFranceTxResponse = await fetch( `${axumBaseUrl}/api/registry/register-local-admin`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(franceRequest), } ); if (!createFranceTxResponse.ok) { const error = await createFranceTxResponse.text(); throw new Error(`Failed to create France transaction: ${error}`); } const { transaction_base64: franceTxBase64 } = await createFranceTxResponse.json(); const franceTxBuffer = Buffer.from(franceTxBase64, "base64"); const franceTransaction = Transaction.from(franceTxBuffer); franceTransaction.sign(globalAdmin); const signedFranceTxBase64 = franceTransaction.serialize().toString("base64"); const submitFranceTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedFranceTxBase64 }), }); if (!submitFranceTxResponse.ok) { const error = await submitFranceTxResponse.text(); throw new Error(`Failed to submit France transaction: ${error}`); } const { signature: franceSignature } = await submitFranceTxResponse.json(); console.log(`✓ France transaction signature: ${franceSignature}`); // ============================================================ // STEP 6: Allocate 3000 EUA to Germany Local Admin // ============================================================ console.log("Allocating 3000 EUA tokens to Germany Local Admin..."); const germanyEuaAmount = 3000; const germanyEuaRequest = { registry: registryPda.toBase58(), local_admin: localAdminGermanyPda.toBase58(), eua_mint: euaMint.toBase58(), eua_vault: localAdminGermanyEuaVault.toBase58(), global_admin: globalAdmin.publicKey.toBase58(), token_program: TOKEN_PROGRAM_ID.toBase58(), amount: germanyEuaAmount, }; const createGermanyEuaTxResponse = await fetch( `${axumBaseUrl}/api/registry/mint-eua-to-local-admin`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(germanyEuaRequest), } ); if (!createGermanyEuaTxResponse.ok) { const error = await createGermanyEuaTxResponse.text(); throw new Error(`Failed to create Germany EUA mint transaction: ${error}`); } const { transaction_base64: germanyEuaTxBase64 } = await createGermanyEuaTxResponse.json(); const germanyEuaTxBuffer = Buffer.from(germanyEuaTxBase64, "base64"); const germanyEuaTransaction = Transaction.from(germanyEuaTxBuffer); germanyEuaTransaction.sign(globalAdmin); const signedGermanyEuaTxBase64 = germanyEuaTransaction.serialize().toString("base64"); const submitGermanyEuaTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedGermanyEuaTxBase64 }), }); if (!submitGermanyEuaTxResponse.ok) { const error = await submitGermanyEuaTxResponse.text(); throw new Error(`Failed to submit Germany EUA mint transaction: ${error}`); } const { signature: germanyEuaSignature } = await submitGermanyEuaTxResponse.json(); console.log(`✓ Germany EUA mint transaction signature: ${germanyEuaSignature}`); // ============================================================ // STEP 7: Allocate 3000 EUA to France Local Admin // ============================================================ console.log("Allocating 3000 EUA tokens to France Local Admin..."); const franceEuaAmount = 3000; const franceEuaRequest = { registry: registryPda.toBase58(), local_admin: localAdminFrancePda.toBase58(), eua_mint: euaMint.toBase58(), eua_vault: localAdminFranceEuaVault.toBase58(), global_admin: globalAdmin.publicKey.toBase58(), token_program: TOKEN_PROGRAM_ID.toBase58(), amount: franceEuaAmount, }; const createFranceEuaTxResponse = await fetch( `${axumBaseUrl}/api/registry/mint-eua-to-local-admin`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(franceEuaRequest), } ); if (!createFranceEuaTxResponse.ok) { const error = await createFranceEuaTxResponse.text(); throw new Error(`Failed to create France EUA mint transaction: ${error}`); } const { transaction_base64: franceEuaTxBase64 } = await createFranceEuaTxResponse.json(); const franceEuaTxBuffer = Buffer.from(franceEuaTxBase64, "base64"); const franceEuaTransaction = Transaction.from(franceEuaTxBuffer); franceEuaTransaction.sign(globalAdmin); const signedFranceEuaTxBase64 = franceEuaTransaction.serialize().toString("base64"); const submitFranceEuaTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedFranceEuaTxBase64 }), }); if (!submitFranceEuaTxResponse.ok) { const error = await submitFranceEuaTxResponse.text(); throw new Error(`Failed to submit France EUA mint transaction: ${error}`); } const { signature: franceEuaSignature } = await submitFranceEuaTxResponse.json(); console.log(`✓ France EUA mint transaction signature: ${franceEuaSignature}`); // Store local admin vaults in context setTestContext({ localAdminGermanyEuaVault, localAdminGermanyUsdcVault, localAdminFranceEuaVault, localAdminFranceUsdcVault, }); // ============================================================ // STEP 8: Verify On-Chain State - Registry // ============================================================ console.log("Verifying registry state..."); const registry = await program.account.registry.fetch(registryPda); expect(registry.localAdminCount).to.equal(2); console.log(`✓ Registry local admin count: ${registry.localAdminCount}`); // ============================================================ // STEP 9: Verify On-Chain State - Germany Local Admin // ============================================================ console.log("Verifying Germany local admin state..."); const germanyLocalAdmin = await program.account.localAdmin.fetch( localAdminGermanyPda ); expect(germanyLocalAdmin.stateName).to.equal("Germany"); expect(germanyLocalAdmin.admin.equals(localAdminGermany.publicKey)).to.be.true; expect(germanyLocalAdmin.isActive).to.be.true; expect(germanyLocalAdmin.companiesManaged).to.equal(0); console.log(`✓ Germany local admin verified: ${germanyLocalAdmin.stateName}`); // ============================================================ // STEP 10: Verify On-Chain State - France Local Admin // ============================================================ console.log("Verifying France local admin state..."); const franceLocalAdmin = await program.account.localAdmin.fetch( localAdminFrancePda ); expect(franceLocalAdmin.stateName).to.equal("France"); expect(franceLocalAdmin.admin.equals(localAdminFrance.publicKey)).to.be.true; expect(franceLocalAdmin.isActive).to.be.true; expect(franceLocalAdmin.companiesManaged).to.equal(0); console.log(`✓ France local admin verified: ${franceLocalAdmin.stateName}`); // ============================================================ // STEP 11: Verify EUA Token Balances // ============================================================ console.log("Verifying local admin EUA token balances..."); const germanyEuaAccount = await getAccount(connection, localAdminGermanyEuaVault); expect(germanyEuaAccount.amount.toString()).to.equal(germanyEuaAmount.toString()); console.log(`✓ Germany Local Admin EUA vault balance: ${germanyEuaAccount.amount} EUA`); const franceEuaAccount = await getAccount(connection, localAdminFranceEuaVault); expect(franceEuaAccount.amount.toString()).to.equal(franceEuaAmount.toString()); console.log(`✓ France Local Admin EUA vault balance: ${franceEuaAccount.amount} EUA`); // ============================================================ // STEP 12: Verify DB State After (Wait for Event Handler) // ============================================================ console.log("Verifying database state after registration..."); // Wait for Germany local admin to appear in DB const germanyInDb = await db.waitForLocalAdmin(localAdminGermanyPda.toBase58(), 5000); expect(germanyInDb).to.be.true; // Wait for France local admin to appear in DB const franceInDb = await db.waitForLocalAdmin(localAdminFrancePda.toBase58(), 5000); expect(franceInDb).to.be.true; // Get local admin count const localAdminCountAfter = await db.getLocalAdminCount(); expect(localAdminCountAfter).to.equal(2); console.log(`✓ Database has ${localAdminCountAfter} local admins`); // Verify Germany local admin in DB const germanyDbRecord = await db.getLocalAdmin(localAdminGermanyPda.toBase58()); expect(germanyDbRecord).to.not.be.undefined; expect(germanyDbRecord.state_name).to.equal("Germany"); expect(germanyDbRecord.admin_pubkey).to.equal(localAdminGermany.publicKey.toBase58()); expect(germanyDbRecord.is_active).to.be.true; expect(germanyDbRecord.companies_managed).to.equal(0); console.log(`✓ Germany local admin verified in database: ${germanyDbRecord.state_name}`); // Verify France local admin in DB const franceDbRecord = await db.getLocalAdmin(localAdminFrancePda.toBase58()); expect(franceDbRecord).to.not.be.undefined; expect(franceDbRecord.state_name).to.equal("France"); expect(franceDbRecord.admin_pubkey).to.equal(localAdminFrance.publicKey.toBase58()); expect(franceDbRecord.is_active).to.be.true; expect(franceDbRecord.companies_managed).to.equal(0); console.log(`✓ France local admin verified in database: ${franceDbRecord.state_name}`); console.log("\n=== Summary ==="); console.log(`On-Chain: ${registry.localAdminCount} local admins`); console.log(`Database: ${localAdminCountAfter} local admins`); console.log("✓ Local admins registered and funded successfully via API"); console.log("✓ Database state matches on-chain state"); }); });