211 lines
6.5 KiB
JavaScript
211 lines
6.5 KiB
JavaScript
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);
|
|
});
|