Skip to content

Commit

Permalink
Add attestation/sbts/gov-id endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
calebtuttle committed Feb 7, 2024
1 parent 76c6e93 commit 0db4237
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/routes/sbt-attestation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import express from "express";
import {
sybilResistanceGovIdSBT,
getAttestorAddress,
} from "../services/sbt-attestation.js";

const router = express.Router();

router.get("/sbts/gov-id/", sybilResistanceGovIdSBT);
router.get("/attestor-address", getAttestorAddress);

export default router;
2 changes: 2 additions & 0 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import residence from "./routes/residence.js";
import sybilResistance from "./routes/sybil-resistance.js";
import snapshotStrategies from "./routes/snapshot-strategies.js";
import metrics from "./routes/metrics.js";
import sbtAttestation from "./routes/sbt-attestation.js";

// ----------------------------
// Setup express app
Expand All @@ -25,6 +26,7 @@ app.use("/residence", residence);
app.use("/sybil-resistance", sybilResistance);
app.use("/snapshot-strategies", snapshotStrategies);
app.use("/metrics", metrics);
app.use("/attestation", sbtAttestation);

app.get("/", (req, res) => {
console.log(`${new Date().toISOString()} GET /`);
Expand Down
112 changes: 112 additions & 0 deletions src/services/sbt-attestation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { ethers } from "ethers";
import { providers } from "../init.js";
import { logWithTimestamp, assertValidAddress } from "../utils/utils.js";
import { blocklistGetAddress } from "../utils/dynamodb.js";
import { sybilResistanceAddrsByNetwork } from "../constants/contractAddresses.js";
import {
hubV3Address,
govIdIssuerAddress,
v3KYCSybilResistanceCircuitId,
} from "../constants/misc.js";
import AntiSybilStoreABI from "../constants/AntiSybilStoreABI.js";
import HubV3ABI from "../constants/HubV3ABI.js";

function sign(actionId, address) {
const attestor = new ethers.utils.SigningKey(process.env.ATTESTOR_PRIVATE_KEY);

const digest = ethers.utils.solidityKeccak256(
["uint256", "address"],
[parseInt(actionId), address]
);
const personalSignPreimage = ethers.utils.solidityKeccak256(
["string", "bytes32"],
["\x19Ethereum Signed Message:\n32", digest]
);
return ethers.utils.joinSignature(attestor.signDigest(personalSignPreimage));
}

export async function sybilResistanceGovIdSBT(req, res) {
try {
const address = req.query.user;
const actionId = req.query["action-id"];
if (!address) {
return res.status(400).json({ error: "address query parameter is required" });
}
if (!actionId) {
return res.status(400).json({ error: "action-id query parameter is required" });
}
if (!assertValidAddress(address)) {
return res.status(400).json({ error: "Invalid user address" });
}
if (!parseInt(actionId)) {
return res.status(400).json({ error: "Invalid action-id" });
}

// Check blocklist first
const blockListResult = await blocklistGetAddress(address);
if (blockListResult.Item) {
return res.status(200).json({ isUnique: false });
}

const provider = providers.optimism;

// Check v1/v2 contract
const v1ContractAddr = sybilResistanceAddrsByNetwork.optimism;
const v1Contract = new ethers.Contract(
v1ContractAddr,
AntiSybilStoreABI,
provider
);
const isUnique = await v1Contract.isUniqueForAction(address, actionId);

if (isUnique) {
const signature = sign(actionId, address);
return res.status(200).json({ isUnique, signature });
}

// Check v3 contract
try {
const hubV3Contract = new ethers.Contract(hubV3Address, HubV3ABI, provider);

const sbt = await hubV3Contract.getSBT(address, v3KYCSybilResistanceCircuitId);

const publicValues = sbt[1];
const actionIdInSBT = publicValues[2].toString();
const issuerAddress = publicValues[4].toHexString();

const actionIdIsValid = actionId == actionIdInSBT;
const issuerIsValid = govIdIssuerAddress == issuerAddress;

const isUnique = issuerIsValid && actionIdIsValid;

if (isUnique) {
const signature = sign(actionId, address);
return res.status(200).json({ isUnique, signature });
}

return res.status(200).json({ isUnique });
} catch (err) {
if ((err.errorArgs?.[0] ?? "").includes("SBT is expired")) {
return res.status(200).json({ isUnique: false });
}

throw err;
}
} catch (err) {
console.log(err);
logWithTimestamp(
"sybilResistanceGovId: Encountered error while calling smart contract. Exiting"
);
return res.status(500).json({ error: "An unexpected error occured" });
}
}

export async function getAttestorAddress(req, res) {
try {
const attestor = new ethers.Wallet(process.env.ATTESTOR_PRIVATE_KEY);
return res.status(200).json({ address: attestor.address });
} catch (err) {
console.log(err);
return res.status(500).json({ error: "An unexpected error occured" });
}
}

0 comments on commit 0db4237

Please sign in to comment.