UNPKG

eas-poll-action-module

Version:

A helper library for creating and voting on polls with the EasPollActionModule Open Action.

321 lines (320 loc) 13.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getVoteForActor = exports.getVoteCountForOption = exports.getVoteCount = exports.createVoteForActorQueryVariables = exports.createVoteCountForOptionQueryVariables = exports.createVoteCountQueryVariables = exports.createVoteActionRequest = exports.createPollActionModuleInput = void 0; const client_1 = require("@lens-protocol/client"); const ethers_1 = require("ethers"); const eas_sdk_1 = require("@ethereum-attestation-service/eas-sdk"); const types_1 = require("./lib/types"); const constants_1 = require("./lib/constants"); const POLYGON_MAINNET_CHAIN_ID = 137; /** * Creates an OpenActionModuleInput for initializing a poll on the EAS Poll Action Module. * * @param poll The poll to create. */ const createPollActionModuleInput = (poll) => { if (poll.options.length < 2 || poll.options.length > 4) { throw new Error("There must be between 2 and 4 poll options"); } if (poll.endTimestamp && poll.endTimestamp < Math.floor(Date.now() / 1000)) { throw new Error("Poll end timestamp must be in the future"); } while (poll.options.length < 4) { poll.options.push(""); } const data = (0, client_1.encodeData)([ { type: "tuple", name: "poll", components: [ { type: "bytes32[4]", name: "options" }, { type: "bool", name: "followersOnly" }, { type: "uint40", name: "endTimestamp" }, { type: "bool", name: "signatureRequired" }, { type: "tuple", name: "gateParams", components: [ { type: "address", name: "tokenAddress" }, { type: "uint256", name: "minThreshold" }, ], }, ], }, ], [ [ poll.options.map(ethers_1.encodeBytes32String), poll.followersOnly ?? false, poll.endTimestamp ?? 0, poll.signatureRequired ?? false, poll.gateParams ? [poll.gateParams.tokenAddress, poll.gateParams.minThreshold] : [eas_sdk_1.ZERO_ADDRESS, 0], ], ]); return { unknownOpenAction: { address: constants_1.EAS_POLL_ACTION_MODULE_ADDRESS, data, }, }; }; exports.createPollActionModuleInput = createPollActionModuleInput; /** * Creates an ActOnOpenActionRequest for voting on a poll on the EAS Poll Action Module. * * @param vote The vote to create. * @param signer Optional Signer to use for creating a signed vote attestation. If not provided, an unsigned vote attestation will be created. */ const createVoteActionRequest = async (vote, signer) => { let data; if (signer && (0, types_1.isSignedVote)(vote)) { data = await encodeSignedVoteAttestationData(signer, vote); } else { data = await encodeVoteAttestationData(vote); } return { actOn: { unknownOpenAction: { address: constants_1.EAS_POLL_ACTION_MODULE_ADDRESS, data, }, }, for: vote.publicationId, }; }; exports.createVoteActionRequest = createVoteActionRequest; /** * Creates graphql query variables for getting the vote count of a poll on the EAS Poll Action Module. * * @param publicationId The full ID of the publication as a hex string (e.g. 0xd8-0x01). * @param testnet Whether to use the testnet schema. */ const createVoteCountQueryVariables = (publicationId, testnet = false) => { const pollId = buildPollId(publicationId); return { schemaId: testnet ? constants_1.EAS_VOTE_SCHEMA_UID_TESTNET : constants_1.EAS_VOTE_SCHEMA_UID, pollId, }; }; exports.createVoteCountQueryVariables = createVoteCountQueryVariables; /** * Creates graphql query variables for getting the vote count of a specific poll option on the EAS Poll Action Module. * * @param publicationId The full ID of the publication as a hex string (e.g. 0xd8-0x01). * @param optionIndex The index of the option to get the vote count for. * @param testnet Whether to use the testnet schema. */ const createVoteCountForOptionQueryVariables = (publicationId, optionIndex, testnet = false) => { const pollId = buildPollId(publicationId); const optionIndexAbi = `{"name":"optionIndex","type":"uint8","value":${optionIndex}}`; return { schemaId: testnet ? constants_1.EAS_VOTE_SCHEMA_UID_TESTNET : constants_1.EAS_VOTE_SCHEMA_UID, pollId, optionIndex: optionIndexAbi, }; }; exports.createVoteCountForOptionQueryVariables = createVoteCountForOptionQueryVariables; const createVoteForActorQueryVariables = (publicationId, actorProfileId, testnet = false) => { const profileId = parseInt(publicationId.split("-")[0]).toString(); const pubId = parseInt(publicationId.split("-")[1]).toString(); const data = (0, client_1.encodeData)([ { name: "publicationProfileId", type: "uint256" }, { name: "publicationId", type: "uint256" }, { name: "actorProfileId", type: "uint256" }, ], [profileId, pubId, actorProfileId]); return { schemaId: testnet ? constants_1.EAS_VOTE_SCHEMA_UID_TESTNET : constants_1.EAS_VOTE_SCHEMA_UID, data, }; }; exports.createVoteForActorQueryVariables = createVoteForActorQueryVariables; /** * Gets the vote count of a poll on the EAS Poll Action Module. * * @param variables The graphql query variables. * @param testnet Whether to use the testnet schema. * * @see createVoteCountQueryVariables */ const getVoteCount = async (variables, testnet = false) => { const response = await fetch(testnet ? constants_1.EAS_GRAPHQL_ENDPOINT_TESTNET : constants_1.EAS_GRAPHQL_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ query: constants_1.GET_VOTE_COUNT_QUERY, variables, }), }); if (!response.ok) { throw new Error(`HTTP error: ${response.statusText}`); } const { data } = await response.json(); return data.groupByAttestation[0]._count._all; }; exports.getVoteCount = getVoteCount; /** * Gets the vote count of a specific poll option on the EAS Poll Action Module. * * @param variables The graphql query variables. * @param testnet Whether to use the testnet schema. * * @see createVoteCountForOptionQueryVariables */ const getVoteCountForOption = async (variables, testnet = false) => { const response = await fetch(testnet ? constants_1.EAS_GRAPHQL_ENDPOINT_TESTNET : constants_1.EAS_GRAPHQL_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ query: constants_1.GET_VOTE_COUNT_FOR_OPTION_QUERY, variables, }), }); if (!response.ok) { throw new Error(`HTTP error: ${response.statusText}`); } const { data } = await response.json(); return data.groupByAttestation[0]._count._all; }; exports.getVoteCountForOption = getVoteCountForOption; const getVoteForActor = async (variables, testnet = false) => { const response = await fetch(testnet ? constants_1.EAS_GRAPHQL_ENDPOINT_TESTNET : constants_1.EAS_GRAPHQL_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ query: constants_1.GET_VOTE_FOR_ACTOR_QUERY, variables, }), }); if (!response.ok) { throw new Error(`HTTP error: ${response.statusText}`); } const { data } = await response.json(); const attestations = data.attestations; const moduleData = (0, client_1.decodeData)(constants_1.EAS_VOTE_ABI, attestations[0].data); const attestationData = { publicationProfileId: moduleData[0], publicationId: moduleData[1], actorProfileId: moduleData[2], actorProfileOwner: moduleData[3], transactionExecutor: moduleData[4], optionIndex: parseInt(moduleData[5]), timestamp: parseInt(moduleData[6]), }; return { attester: attestations[0].attester, id: attestations[0].id, revoked: attestations[0].revoked, data: attestationData, }; }; exports.getVoteForActor = getVoteForActor; const buildPollId = (publicationId) => { const profileId = parseInt(publicationId.split("-")[0]).toString(); const pubId = parseInt(publicationId.split("-")[1]).toString(); return (0, client_1.encodeData)([ { name: "publicationProfileId", type: "uint256" }, { name: "publicationId", type: "uint256" }, ], [profileId, pubId]); }; const encodeVoteAttestationData = async (vote) => { return (0, client_1.encodeData)([ { type: "tuple", components: [ { type: "uint256", name: "publicationProfileId" }, { type: "uint256", name: "publicationId" }, { type: "uint256", name: "actorProfileId" }, { type: "address", name: "actorProfileOwner" }, { type: "address", name: "transactionExecutor" }, { type: "uint8", name: "optionIndex" }, { type: "uint40", name: "timestamp" }, ], name: "vote", }, ], [["0", "0", "0", eas_sdk_1.ZERO_ADDRESS, eas_sdk_1.ZERO_ADDRESS, vote.optionIndex.toString(), "0"]]); }; const encodeSignedVoteAttestationData = async (signer, vote) => { if (!vote.publicationId || !vote.actorProfileId || !vote.actorProfileOwner || !vote.transactionExecutor) { throw new Error("Signed votes must have publicationId, actorProfileId, actorProfileOwner, and transactionExecutor"); } const network = await signer.provider?.getNetwork(); if (!network) { throw new Error("Signer is not connected to a network"); } const isMainnet = network.matches(POLYGON_MAINNET_CHAIN_ID); const publicationProfileId = parseInt(vote.publicationId.split("-")[0]); const publicationId = parseInt(vote.publicationId.split("-")[1]); const actorProfileId = parseInt(vote.actorProfileId); const transactionExecutor = await signer.getAddress(); const timestamp = Math.floor(Date.now() / 1000); const schemaEncoder = new eas_sdk_1.SchemaEncoder(constants_1.EAS_VOTE_SCHEMA); const encodedData = schemaEncoder.encodeData([ { name: "publicationProfileId", value: publicationProfileId, type: "uint256" }, { name: "publicationId", value: publicationId, type: "uint256" }, { name: "actorProfileId", value: actorProfileId, type: "uint256" }, { name: "actorProfileOwner", value: vote.actorProfileOwner, type: "address" }, { name: "transactionExecutor", value: transactionExecutor, type: "address" }, { name: "optionIndex", value: vote.optionIndex, type: "uint8" }, { name: "timestamp", value: timestamp, type: "uint40" }, ]); const eas = new eas_sdk_1.EAS(isMainnet ? constants_1.EAS_ADDRESS : constants_1.EAS_ADDRESS_TESTNET); eas.connect(signer); const account = await signer.getAddress(); const nonce = await eas.getNonce(account); const delegated = await eas.getDelegated(); const response = await delegated.signDelegatedAttestation({ schema: isMainnet ? constants_1.EAS_VOTE_SCHEMA_UID : constants_1.EAS_VOTE_SCHEMA_UID_TESTNET, data: encodedData, nonce: nonce, revocable: true, recipient: constants_1.EAS_POLL_ACTION_MODULE_ADDRESS, expirationTime: eas_sdk_1.NO_EXPIRATION, refUID: eas_sdk_1.ZERO_BYTES32, value: 0n, deadline: eas_sdk_1.NO_EXPIRATION, }, signer); const signature = response.signature; return (0, client_1.encodeData)([ { type: "tuple", components: [ { type: "uint256", name: "publicationProfileId" }, { type: "uint256", name: "publicationId" }, { type: "uint256", name: "actorProfileId" }, { type: "address", name: "actorProfileOwner" }, { type: "address", name: "transactionExecutor" }, { type: "uint8", name: "optionIndex" }, { type: "uint40", name: "timestamp" }, ], name: "vote", }, { type: "tuple", components: [ { type: "uint8", name: "v" }, { type: "bytes32", name: "r" }, { type: "bytes32", name: "s" }, ], name: "signature", }, { type: "uint64", name: "deadline" }, ], [ [ publicationProfileId.toString(), publicationId.toString(), actorProfileId.toString(), vote.actorProfileOwner, transactionExecutor, vote.optionIndex.toString(), timestamp.toString(), ], [signature.v.toString(), signature.r, signature.s], eas_sdk_1.NO_EXPIRATION.toString(), ]); };