const { ethers } = require("ethers"); const API_BASE = (process.env.SECRET_API_BASE_URL || "https://api.edut.ai").trim(); const ORIGIN = (process.env.SECRET_API_ORIGIN || "https://edut.ai").trim(); const LOCALE = (process.env.SECRET_API_LOCALE || "en-US").trim(); const CHAIN_ID = Number((process.env.SECRET_API_CHAIN_ID || "84532").trim()); const PRIVATE_KEY = (process.env.DEPLOYER_PRIVATE_KEY || "").trim(); const RPC_URL = (process.env.BASE_SEPOLIA_RPC_URL || "").trim(); const GAS_PRICE_WEI = (process.env.E2E_GAS_PRICE_WEI || "").trim(); const IDENTITY_ASSURANCE = (process.env.E2E_IDENTITY_ASSURANCE_LEVEL || "").trim(); const IDENTITY_ATTESTED_BY = (process.env.E2E_IDENTITY_ATTESTED_BY || "").trim(); const IDENTITY_ATTESTATION_ID = (process.env.E2E_IDENTITY_ATTESTATION_ID || "").trim(); function required(name, value) { if (!value) { throw new Error(`Missing required env: ${name}`); } return value; } async function postJSON(path, payload) { const res = await fetch(`${API_BASE}${path}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(payload), }); const text = await res.text(); let data; try { data = text ? JSON.parse(text) : {}; } catch (err) { throw new Error(`${path} returned non-json response: ${text}`); } if (!res.ok) { throw new Error(`${path} failed (${res.status}): ${JSON.stringify(data)}`); } return data; } async function getJSON(path) { const res = await fetch(`${API_BASE}${path}`); const text = await res.text(); let data; try { data = text ? JSON.parse(text) : {}; } catch (err) { throw new Error(`${path} returned non-json response: ${text}`); } if (!res.ok) { throw new Error(`${path} failed (${res.status}): ${JSON.stringify(data)}`); } return data; } async function main() { required("DEPLOYER_PRIVATE_KEY", PRIVATE_KEY); required("BASE_SEPOLIA_RPC_URL", RPC_URL); const provider = new ethers.providers.JsonRpcProvider(RPC_URL); const signer = new ethers.Wallet(PRIVATE_KEY, provider); const address = await signer.getAddress(); console.log("wallet:", address); console.log("chain_id:", CHAIN_ID); console.log("api_base:", API_BASE); const intent = await postJSON("/secret/wallet/intent", { address, origin: ORIGIN, locale: LOCALE, chain_id: CHAIN_ID, }); console.log("intent_id:", intent.intent_id); console.log("designation_code:", intent.designation_code); console.log("display_token:", intent.display_token); const domain = { name: intent.domain_name, version: "1", chainId: intent.chain_id, verifyingContract: intent.verifying_contract, }; const types = { DesignationIntent: [ { name: "designationCode", type: "string" }, { name: "designationToken", type: "string" }, { name: "nonce", type: "string" }, { name: "issuedAt", type: "string" }, { name: "origin", type: "string" }, ], }; const message = { designationCode: intent.designation_code, designationToken: intent.display_token, nonce: intent.nonce, issuedAt: intent.issued_at, origin: ORIGIN, }; const signature = await signer._signTypedData(domain, types, message); const verify = await postJSON("/secret/wallet/verify", { intent_id: intent.intent_id, address, chain_id: CHAIN_ID, signature, }); console.log("verify_status:", verify.status); const quote = await postJSON("/secret/id/quote", { designation_code: intent.designation_code, address, chain_id: CHAIN_ID, }); console.log("quote_id:", quote.quote_id); console.log("contract:", quote.contract_address); console.log("value:", quote.value); const txReq = quote.tx || { to: quote.contract_address, data: quote.calldata, value: quote.value, }; const value = ethers.BigNumber.from(txReq.value || "0x0"); const feeData = await provider.getFeeData(); const configuredGasPrice = GAS_PRICE_WEI ? ethers.BigNumber.from(GAS_PRICE_WEI) : null; const effectiveGasPrice = configuredGasPrice || feeData.gasPrice || feeData.maxFeePerGas || ethers.BigNumber.from("0"); const gasUnitsReserve = ethers.BigNumber.from("250000"); const estimatedGasCost = effectiveGasPrice.mul(gasUnitsReserve); const totalNeeded = value.add(estimatedGasCost); const balance = await provider.getBalance(address); if (balance.lt(totalNeeded)) { const deficit = totalNeeded.sub(balance); throw new Error( [ "insufficient funded balance for membership mint preflight", `balance_wei=${balance.toString()}`, `required_min_wei=${totalNeeded.toString()}`, `deficit_wei=${deficit.toString()}`, `mint_value_wei=${value.toString()}`, `gas_reserve_wei=${estimatedGasCost.toString()}`, ].join(" ") ); } const txRequest = { to: txReq.to, data: txReq.data, value: value, }; if (GAS_PRICE_WEI) { txRequest.gasPrice = ethers.BigNumber.from(GAS_PRICE_WEI); } const tx = await signer.sendTransaction(txRequest); console.log("submitted_tx:", tx.hash); await tx.wait(1); console.log("mined_tx:", tx.hash); const confirmPayload = { designation_code: intent.designation_code, quote_id: quote.quote_id, tx_hash: tx.hash, address, chain_id: CHAIN_ID, }; if (IDENTITY_ASSURANCE) { confirmPayload.identity_assurance_level = IDENTITY_ASSURANCE; } if (IDENTITY_ATTESTED_BY) { confirmPayload.identity_attested_by = IDENTITY_ATTESTED_BY; } if (IDENTITY_ATTESTATION_ID) { confirmPayload.identity_attestation_id = IDENTITY_ATTESTATION_ID; } const confirm = await postJSON("/secret/id/confirm", confirmPayload); console.log("confirm_status:", confirm.status); if (confirm.identity_assurance_level) { console.log("identity_assurance:", confirm.identity_assurance_level); } const status = await getJSON( `/secret/id/status?wallet=${encodeURIComponent(address)}` ); console.log("membership_status:", status.status); if (status.identity_assurance_level) { console.log("membership_assurance:", status.identity_assurance_level); } console.log( JSON.stringify( { wallet: address, designation_code: intent.designation_code, quote_id: quote.quote_id, tx_hash: tx.hash, confirm_status: confirm.status, membership_status: status.status, identity_assurance_level: status.identity_assurance_level || null, }, null, 2 ) ); } main().catch((err) => { console.error(err.message || err); process.exit(1); });