From 1ab335b74b9bb7656cecf2de0d1955e1c76c74e0 Mon Sep 17 00:00:00 2001 From: emmdim Date: Fri, 22 Sep 2023 18:21:44 +0200 Subject: [PATCH] Implements getProposals Signed-off-by: emmdim --- packages/contracts-ethers/package.json | 2 +- packages/contracts/package.json | 1 + packages/contracts/utils/helpers.ts | 1 - packages/js-client/package.json | 10 +- packages/js-client/src/client.ts | 25 +- packages/js-client/src/internal/constants.ts | 8 +- packages/js-client/src/internal/core.ts | 8 +- packages/js-client/src/internal/interfaces.ts | 40 ++- .../src/internal/modules/encoding.ts | 14 +- .../src/internal/modules/estimation.ts | 26 +- .../js-client/src/internal/modules/methods.ts | 212 ++++++------- packages/js-client/src/internal/utils.ts | 297 ++++++++++++------ packages/js-client/src/types.ts | 101 ++++-- .../test/helpers/deploy-contracts.ts | 190 +++++------ .../test/integration/encoding.test.ts | 6 +- .../test/integration/methods.test.ts | 138 ++++---- 16 files changed, 638 insertions(+), 441 deletions(-) diff --git a/packages/contracts-ethers/package.json b/packages/contracts-ethers/package.json index 3743c07d..83f47414 100644 --- a/packages/contracts-ethers/package.json +++ b/packages/contracts-ethers/package.json @@ -1,7 +1,7 @@ { "name": "@vocdoni/offchain-voting-ethers", "author": "Vocdoni Association", - "version": "0.0.2", + "version": "0.0.3", "description": "Plugin contract definitions for ethers.js", "main": "dist/bundle-cjs.js", "module": "dist/bundle-esm.js", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 83729236..dd9ff4b9 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -69,6 +69,7 @@ "lint:ts": "eslint --ignore-path ./.eslintignore --ext .js,.ts .", "postinstall": "DOTENV_CONFIG_PATH=../../.env.example yarn typechain", "test": "hardhat test", + "my": "hardhat run --network goerli deploy/02_setup/11_setup_conclude.ts", "typechain": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat typechain", "clean": "rimraf ./artifacts ./cache ./coverage ./types ./coverage.json && yarn typechain" } diff --git a/packages/contracts/utils/helpers.ts b/packages/contracts/utils/helpers.ts index 467230e0..24cc149d 100644 --- a/packages/contracts/utils/helpers.ts +++ b/packages/contracts/utils/helpers.ts @@ -35,7 +35,6 @@ export const ERRORS = { export function getPluginRepoFactoryAddress(networkName: string) { let pluginRepoFactoryAddr: string; - if ( networkName === 'localhost' || networkName === 'hardhat' || diff --git a/packages/js-client/package.json b/packages/js-client/package.json index aa919ee1..80235ce7 100644 --- a/packages/js-client/package.json +++ b/packages/js-client/package.json @@ -1,7 +1,7 @@ { "name": "@vocdoni/offchain-voting", "author": "Vocdoni Association", - "version": "0.0.8", + "version": "0.0.19", "license": "AGPL-3.0-or-later", "main": "dist/index.js", "module": "dist/offchain-voting.esm.js", @@ -26,9 +26,7 @@ "clean": "rm -Rf .turbo dist", "examples": "node ./scripts/generate-examples-md.js ./examples ./examples.md" }, - "peerDependencies": { - "@vocdoni/offchain-voting-ethers": ">=0.0.1" - }, + "peerDependencies": {}, "husky": { "hooks": { "pre-commit": "tsdx lint --fix" @@ -74,7 +72,9 @@ "@ethersproject/wallet": "^5.7.0", "graphql": "^16.6.0", "graphql-request": "4.3.0", - "@vocdoni/offchain-voting-ethers": "./vocdoni-offchain-voting-ethers-v0.0.2.tgz" + "@vocdoni/offchain-voting-ethers": "./vocdoni-offchain-voting-ethers-v0.0.3.tgz", + "@vocdoni/sdk": "0.3.0", + "axios": "0.27.2" }, "jest": { "testEnvironment": "./test-environment.js", diff --git a/packages/js-client/src/client.ts b/packages/js-client/src/client.ts index 736eae94..75d9dda8 100644 --- a/packages/js-client/src/client.ts +++ b/packages/js-client/src/client.ts @@ -5,17 +5,16 @@ import { IOffchainVotingClientEncoding, IOffchainVotingClientEstimation, IOffchainVotingClientMethods, - SimpleStoragClientEstimation, + OffchainVotingClientEstimation, OffchainVotingClientDecoding, OffchainVotingClientEncoding, OffchainVotingClientMethods, } from './internal'; import { OffchainVotingClientCore } from './internal/core'; -import { - PluginInstallItem, -} from '@aragon/sdk-client-common'; -import { Networkish } from '@ethersproject/providers'; import { OffchainVotingPluginInstall } from './types'; +import { PluginInstallItem } from '@aragon/sdk-client-common'; +import { Networkish } from '@ethersproject/providers'; +import { EnvOptions } from '@vocdoni/sdk'; export class OffchainVotingClient extends OffchainVotingClientCore @@ -26,12 +25,16 @@ export class OffchainVotingClient public encoding: IOffchainVotingClientEncoding; public decoding: IOffchainVotingClientDecoding; - constructor(pluginContext: OffchainVotingContext) { - super(pluginContext); - this.methods = new OffchainVotingClientMethods(pluginContext); - this.estimation = new SimpleStoragClientEstimation(pluginContext); - this.encoding = new OffchainVotingClientEncoding(pluginContext); - this.decoding = new OffchainVotingClientDecoding(pluginContext); + constructor(pluginContext: OffchainVotingContext, vocdoniEnv: EnvOptions) { + if (!vocdoniEnv) throw 'Invalid Vocdoni environment'; + super(pluginContext, vocdoniEnv); + this.methods = new OffchainVotingClientMethods(pluginContext, vocdoniEnv); + this.estimation = new OffchainVotingClientEstimation( + pluginContext, + vocdoniEnv + ); + this.encoding = new OffchainVotingClientEncoding(pluginContext, vocdoniEnv); + this.decoding = new OffchainVotingClientDecoding(pluginContext, vocdoniEnv); } static encoding = { diff --git a/packages/js-client/src/internal/constants.ts b/packages/js-client/src/internal/constants.ts index d76f010c..ec342368 100644 --- a/packages/js-client/src/internal/constants.ts +++ b/packages/js-client/src/internal/constants.ts @@ -13,16 +13,16 @@ export const DEFAULT_ADDRESSES: { repoAddress: '', }, goerli: { - setupAddress: '', - repoAddress: '', + setupAddress: '0xAe0c94AB8289C6fb0a45CA7733a84f549808F75b', + repoAddress: '0x2a5Cc5974D3ab30d4B0a6e6a605e06956c975171', }, matic: { setupAddress: '', repoAddress: '', }, maticmum: { - setupAddress: '', - repoAddress: '0xaca70d8c462940b839de386bcdd4cacf745632ca', + setupAddress: '0x5A6E29875cCa6eb7a9c39938720e6096468a8917', + repoAddress: '0x5BD8F8Dc73476d24F37c4d885c4528d5abB8cBe6', }, }; diff --git a/packages/js-client/src/internal/core.ts b/packages/js-client/src/internal/core.ts index 768faf37..3af72156 100644 --- a/packages/js-client/src/internal/core.ts +++ b/packages/js-client/src/internal/core.ts @@ -1,14 +1,16 @@ import { OffchainVotingContext } from '../context'; import { ClientCore } from '@aragon/sdk-client-common'; +import { EnvOptions, VocdoniSDKClient } from '@vocdoni/sdk'; export class OffchainVotingClientCore extends ClientCore { public offchainVotingContext: string; public offchainVotingRepoAddress: string; - - constructor(pluginContext: OffchainVotingContext) { + protected vocdoniSDK: VocdoniSDKClient; + + constructor(pluginContext: OffchainVotingContext, vocdoniEnv: EnvOptions) { super(pluginContext); + this.vocdoniSDK = new VocdoniSDKClient({ env: vocdoniEnv }); this.offchainVotingContext = pluginContext.offchainVotingBackendUrl; this.offchainVotingRepoAddress = pluginContext.offchainVotingRepoAddress; } } - diff --git a/packages/js-client/src/internal/interfaces.ts b/packages/js-client/src/internal/interfaces.ts index 4876f01f..a67413bb 100644 --- a/packages/js-client/src/internal/interfaces.ts +++ b/packages/js-client/src/internal/interfaces.ts @@ -1,7 +1,18 @@ -import { PrepareInstallationParams } from '../types'; +import { + CreateGasslessProposalParams, + GaslessVotingProposal, + PrepareInstallationParams, + GaslessPluginVotingSettings, +} from '../types'; +import { IDAO } from '@aragon/osx-ethers'; import { VotingSettings, MintTokenParams, + ProposalCreationStepValue, + Erc20TokenDetails, + Erc721TokenDetails, + Erc20WrapperTokenDetails, + TokenVotingMember, } from '@aragon/sdk-client'; import { GasFeeEstimation, @@ -24,8 +35,33 @@ export interface IOffchainVotingClientMethods { // repo if its not specified in the state of the client prepareInstallation( params: PrepareInstallationParams - ): AsyncGenerator + ): AsyncGenerator; // Add any methods that you need + createProposal( + params: CreateGasslessProposalParams + ): AsyncGenerator; + // + getProposal( + dao: IDAO, + pluginAddress: string, + proposalId: number + ): Promise; + // + getProposals( + dao: IDAO, + pluginAddress: string + ): Promise; + // + getVotingSettings( + pluginAddress: string, + blockNumber?: number + ): Promise; + getToken( + pluginAddress: string + ): Promise< + Erc20TokenDetails | Erc721TokenDetails | Erc20WrapperTokenDetails | null + >; + getMembers(pluginAddress: string): Promise; } export interface IOffchainVotingClientEstimation { prepareInstallation( diff --git a/packages/js-client/src/internal/modules/encoding.ts b/packages/js-client/src/internal/modules/encoding.ts index 0ac8f4d4..7fb92fb1 100644 --- a/packages/js-client/src/internal/modules/encoding.ts +++ b/packages/js-client/src/internal/modules/encoding.ts @@ -1,7 +1,11 @@ import metadata from '../../../../contracts/src/build-metadata.json'; -import { DEFAULT_ADDRESSES } from '../constants'; -import { ITokenVotingClientEncoding } from '../interfaces'; import { OffchainVotingPluginInstall } from '../../types'; +import { DEFAULT_ADDRESSES } from '../constants'; +import { OffchainVotingClientCore } from '../core'; +import { + IOffchainVotingClientEncoding, + ITokenVotingClientEncoding, +} from '../interfaces'; import { mintTokenParamsToContract, initParamsToContract } from '../utils'; import { IERC20MintableUpgradeable__factory } from '@aragon/osx-ethers'; import { @@ -34,8 +38,8 @@ const prepareInstallationDataTypes = getNamedTypesFromMetadata( * Encoding module the SDK TokenVoting Client */ export class OffchainVotingClientEncoding - extends ClientCore - implements ITokenVotingClientEncoding + extends OffchainVotingClientCore + implements IOffchainVotingClientEncoding { /** * Computes the parameters to be given when creating the DAO, @@ -56,6 +60,8 @@ export class OffchainVotingClientEncoding } const args = initParamsToContract(params); const hexBytes = defaultAbiCoder.encode(prepareInstallationDataTypes, args); + console.log(`network ${networkName}`); + console.log(`repoaddress ${DEFAULT_ADDRESSES[networkName].repoAddress}`); return { id: DEFAULT_ADDRESSES[networkName].repoAddress, data: hexToBytes(hexBytes), diff --git a/packages/js-client/src/internal/modules/estimation.ts b/packages/js-client/src/internal/modules/estimation.ts index edf8b425..e45772f0 100644 --- a/packages/js-client/src/internal/modules/estimation.ts +++ b/packages/js-client/src/internal/modules/estimation.ts @@ -1,17 +1,19 @@ -import * as BUILD_METADATA from "../../../../contracts/src/build-metadata.json"; -import { PrepareInstallationParams } from "../../types"; -import { OffchainVotingClientCore } from "../core"; -import { IOffchainVotingClientEstimation } from "../interfaces"; -import { PluginRepo__factory } from "@aragon/osx-ethers"; +import * as BUILD_METADATA from '../../../../contracts/src/build-metadata.json'; +import { PrepareInstallationParams } from '../../types'; +import { OffchainVotingClientCore } from '../core'; +import { IOffchainVotingClientEstimation } from '../interfaces'; +import { PluginRepo__factory } from '@aragon/osx-ethers'; import { GasFeeEstimation, prepareGenericInstallationEstimation, -} from "@aragon/sdk-client-common"; +} from '@aragon/sdk-client-common'; -export class SimpleStoragClientEstimation extends OffchainVotingClientCore - implements IOffchainVotingClientEstimation { +export class OffchainVotingClientEstimation + extends OffchainVotingClientCore + implements IOffchainVotingClientEstimation +{ public async prepareInstallation( - params: PrepareInstallationParams, + params: PrepareInstallationParams ): Promise { let version = params.versionTag; // if not specified use the lates version @@ -21,13 +23,13 @@ export class SimpleStoragClientEstimation extends OffchainVotingClientCore // connect to the plugin repo const pluginRepo = PluginRepo__factory.connect( this.offchainVotingRepoAddress, - signer, + signer ); // get latest release const currentRelease = await pluginRepo.latestRelease(); // get latest version - const latestVersion = await pluginRepo["getLatestVersion(uint8)"]( - currentRelease, + const latestVersion = await pluginRepo['getLatestVersion(uint8)']( + currentRelease ); version = latestVersion.tag; } diff --git a/packages/js-client/src/internal/modules/methods.ts b/packages/js-client/src/internal/modules/methods.ts index 129791a7..0e7caeed 100644 --- a/packages/js-client/src/internal/modules/methods.ts +++ b/packages/js-client/src/internal/modules/methods.ts @@ -1,9 +1,10 @@ import { CreateGasslessProposalParams, - GasslessVotingProposal, + GaslessVotingProposal, PrepareInstallationParams, - VocdoniVotingSettings, - vocdoniProposalParams, + GaslessPluginVotingSettings, + GaslessProposalParametersContractStruct, + GaslessVotingMember, } from '../../types'; import { INSTALLATION_ABI } from '../constants'; import { OffchainVotingClientCore } from '../core'; @@ -11,15 +12,17 @@ import { IOffchainVotingClientMethods } from '../interfaces'; import { initParamsToContract, toGaslessVotingProposal, + toNewProposal, votingSettingsfromContract, } from '../utils'; -import { GovernanceWrappedERC20__factory } from '@aragon/osx-ethers'; +import { GovernanceWrappedERC20__factory, IDAO } from '@aragon/osx-ethers'; import { Erc20TokenDetails, Erc20WrapperTokenDetails, Erc721TokenDetails, ProposalCreationStepValue, ProposalCreationSteps, + TokenVotingMember, } from '@aragon/sdk-client'; import { findLog, @@ -31,16 +34,22 @@ import { } from '@aragon/sdk-client-common'; import { InvalidAddressError, - InvalidProposalIdError, ProposalCreationError, SizeMismatchError, UnsupportedNetworkError, boolArrayToBitmap, encodeProposalId, - isProposalId, + hexToBytes, } from '@aragon/sdk-common'; import { isAddress } from '@ethersproject/address'; +import { BigNumber } from '@ethersproject/bignumber'; import { VocdoniVoting__factory } from '@vocdoni/offchain-voting-ethers'; +import { + EnvOptions, + ErrElectionNotFound, + VocdoniCensus3Client, +} from '@vocdoni/sdk'; +import axios from 'axios'; export class OffchainVotingClientMethods extends OffchainVotingClientCore @@ -77,7 +86,7 @@ export class OffchainVotingClientMethods * @return {*} {AsyncGenerator} * @memberof TokenVotingClient */ - public async *createProposalOffchain( + public async *createProposal( params: CreateGasslessProposalParams ): AsyncGenerator { const signer = this.web3.getConnectedSigner(); @@ -94,22 +103,22 @@ export class OffchainVotingClientMethods throw new SizeMismatchError(); } const allowFailureMap = boolArrayToBitmap(params.failSafeActions); - const startTimestamp = params.startDate - ? new Date(params.startDate).getTime() - : 0; - const endTimestamp = params.endDate - ? new Date(params.endDate).getTime() - : 0; - const votingParams: vocdoniProposalParams = { - censusBlock: params.censusBlock, - securityBlock: params.securityBlock, - startDate: Math.round(startTimestamp / 1000), - endDate: Math.round(endTimestamp / 1000), - expirationDate: params.expirationDate, + // const startTimestamp = params.startDate + // ? new Date(params.startDate.toNumber()).getTime() + // : 0; + // const endTimestamp = params.endDate + // ? new Date(params.endDate.toNumber()).getTime() + // : 0; + const votingParams: GaslessProposalParametersContractStruct = { + censusBlock: [] as string[], + startDate: BigNumber.from(params.startDate), + endDate: BigNumber.from(params.endDate), + expirationDate: BigNumber.from(0), + securityBlock: BigNumber.from(0), }; const tx = await gaslessVotingContract.createProposal( // toUtf8Bytes(params.metadataUri), - params.vochainProposalId, + hexToBytes(params.vochainProposalId), allowFailureMap, votingParams, params.actions || [] @@ -265,23 +274,38 @@ export class OffchainVotingClientMethods * @memberof TokenVotingClient */ public async getProposal( + dao: IDAO, pluginAddress: string, proposalId: number - // ): Promise { - ): Promise { + ): Promise { const signer = this.web3.getConnectedSigner(); const gaslessVotingContract = VocdoniVoting__factory.connect( pluginAddress, signer ); + let pluginSettings = votingSettingsfromContract( + await gaslessVotingContract.getPluginSettings() + ); let proposal = await gaslessVotingContract.getProposal(proposalId); if (!proposal) { return null; } + let parsedSCProposal = toGaslessVotingProposal(proposal); + if (!parsedSCProposal.vochainProposalId) + Promise.reject(new ErrElectionNotFound()); + const vochainProposal = await this.vocdoniSDK.fetchElection( + parsedSCProposal.vochainProposalId + ); // TODO - return toGaslessVotingProposal(proposal); + return toNewProposal( + proposalId, + dao, + pluginSettings, + vochainProposal, + parsedSCProposal + ); } /** @@ -291,90 +315,20 @@ export class OffchainVotingClientMethods * @return {*} {Promise} * @memberof TokenVotingClient */ - // public async getProposals({ - // daoAddressOrEns, - // limit = 10, - // status, - // skip = 0, - // direction = SortDirection.ASC, - // sortBy = ProposalSortBy.CREATED_AT, - // }: ProposalQueryParams): Promise { - // let where = {}; - // let address = daoAddressOrEns; - // if (address) { - // if (!isAddress(address)) { - // await this.web3.ensureOnline(); - // const provider = this.web3.getProvider(); - // if (!provider) { - // throw new NoProviderError(); - // } - // try { - // const resolvedAddress = await provider.resolveName(address); - // if (!resolvedAddress) { - // throw new InvalidAddressOrEnsError(); - // } - // address = resolvedAddress; - // } catch (e) { - // throw new InvalidAddressOrEnsError(e); - // } - // } - // where = { dao: address.toLowerCase() }; - // } - // if (status) { - // where = { ...where, ...computeProposalStatusFilter(status) }; - // } - // const query = QueryTokenVotingProposals; - // const params = { - // where, - // limit, - // skip, - // direction, - // sortBy, - // }; - // const name = "TokenVoting proposals"; - // type T = { tokenVotingProposals: SubgraphTokenVotingProposalListItem[] }; - // const { tokenVotingProposals } = await this.graphql.request({ - // query, - // params, - // name, - // }); - // return Promise.all( - // tokenVotingProposals.map( - // async ( - // proposal: SubgraphTokenVotingProposalListItem, - // ): Promise => { - // // format in the metadata field - // if (!proposal.metadata) { - // return toTokenVotingProposalListItem( - // proposal, - // EMPTY_PROPOSAL_METADATA_LINK, - // ); - // } - // try { - // const metadataCid = resolveIpfsCid(proposal.metadata); - // // Avoid blocking Promise.all if this individual fetch takes too long - // const stringMetadata = await promiseWithTimeout( - // this.ipfs.fetchString(metadataCid), - // MULTI_FETCH_TIMEOUT, - // ); - // const metadata = JSON.parse(stringMetadata) as ProposalMetadata; - // return toTokenVotingProposalListItem(proposal, metadata); - // } catch (err) { - // if (err instanceof InvalidCidError) { - // return toTokenVotingProposalListItem( - // proposal, - // UNSUPPORTED_PROPOSAL_METADATA_LINK, - // ); - // } - // return toTokenVotingProposalListItem( - // proposal, - // UNAVAILABLE_PROPOSAL_METADATA, - // ); - // } - // }, - // ), - // ); - // } + public async getProposals( + dao: IDAO, + pluginAddress: string + ): Promise { + let id = 0; + let proposal = null; + let proposals: GaslessVotingProposal[] = []; + do { + let proposal = await this.getProposal(dao, pluginAddress, id); + if (proposal) proposals.push(proposal); + id += 1; + } while (proposal != null); + return proposals; + } /** * Returns the settings of a plugin given the address of the plugin instance @@ -387,7 +341,7 @@ export class OffchainVotingClientMethods public async getVotingSettings( pluginAddress: string, blockNumber?: number - ): Promise { + ): Promise { if (!isAddress(pluginAddress)) { throw new InvalidAddressError(); } @@ -443,4 +397,46 @@ export class OffchainVotingClientMethods type: TokenType.ERC20, }; } + + /** + * Returns the details of the token used in a specific plugin instance + * + * @param {string} pluginAddress + * @return {*} {Promise} + * @memberof TokenVotingClient + */ + public async getMembers(pluginAddress: string): Promise { + if (!isAddress(pluginAddress)) { + throw new InvalidAddressError(); + } + const signer = this.web3.getConnectedSigner(); + + const gaslessVotingContract = VocdoniVoting__factory.connect( + pluginAddress, + signer + ); + if (!gaslessVotingContract) { + return Promise.reject(); + } + const pluginSettings = await gaslessVotingContract.getPluginSettings(); + const census3client = new VocdoniCensus3Client({ env: EnvOptions.STG }); + return axios + .get( + census3client.url + + `/debug/token/${pluginSettings.daoTokenAddress}/holders` + ) + .then((response) => + response.data.map( + (val: GaslessVotingMember) => + ({ + address: val.address, + balance: val.balance, + delegatee: null, + delegators: [], + votingPower: val.balance, + } as TokenVotingMember) + ) + ) + .catch(() => []); + } } diff --git a/packages/js-client/src/internal/utils.ts b/packages/js-client/src/internal/utils.ts index da278b22..8b7ee2e3 100644 --- a/packages/js-client/src/internal/utils.ts +++ b/packages/js-client/src/internal/utils.ts @@ -1,21 +1,31 @@ // add internal utils import { ContractMintTokenParams, - GasslessVotingProposal, + GaslessVotingProposal, OffchainVotingPluginInstall, - VocdoniVotingSettings, + GaslessPluginVotingSettings, VoteOption, ProposalFromSC, + GaslessProposalParametersStruct, + GaslessProposalParametersContractStruct, + InvalidResults, + GaslessVotingProposalFromSC, } from '../types'; -import { MintTokenParams, SubgraphAction } from '@aragon/sdk-client'; -import { DaoAction } from '@aragon/sdk-client-common'; -import { hexToBytes } from '@aragon/sdk-common'; +import { IDAO } from '@aragon/osx-ethers'; +import { MintTokenParams, VoteValues } from '@aragon/sdk-client'; +import { + DaoAction, + EMPTY_PROPOSAL_METADATA_LINK, + ProposalStatus, +} from '@aragon/sdk-client-common'; +import { hexToBytes, encodeRatio, decodeRatio } from '@aragon/sdk-common'; import { Result } from '@ethersproject/abi'; import { BigNumber } from '@ethersproject/bignumber'; import { AddressZero } from '@ethersproject/constants'; import { Contract } from '@ethersproject/contracts'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; import { VocdoniVoting } from '@vocdoni/offchain-voting-ethers'; +import { ElectionStatus, PublishedElection } from '@vocdoni/sdk'; // export function votingModeFromContracts(votingMode: number): VotingMode { // switch (votingMode) { @@ -43,33 +53,57 @@ export function mintTokenParamsFromContract(result: Result): MintTokenParams { }; } -export function votingSettingsToContract( - params: VocdoniVotingSettings -): [boolean, number, number, number, BigNumber, string, BigNumber, string] { +export function createProposalVotingSettingsToContract( + params: GaslessPluginVotingSettings +): [ + boolean, + number, + number, + number, + BigNumber, + BigNumber, + string, + BigNumber, + string +] { return [ true, params.minTallyApprovals, - params.minParticipation, - params.supportThreshold, - params.minDuration, - '', - params.minProposerVotingPower, + encodeRatio(params.minParticipation, 6), + encodeRatio(params.supportThreshold, 6), + BigNumber.from(params.minDuration), + BigNumber.from(0), + '0x0000000000000000000000000000000000000000', + BigNumber.from(params.minProposerVotingPower ?? 0), params.censusStrategy, ]; } export function votingSettingsfromContract( settings: VocdoniVoting.PluginSettingsStructOutput -): VocdoniVotingSettings { +): GaslessPluginVotingSettings { return { onlyCommitteeProposalCreation: settings[0], minTallyApprovals: settings[1], - minParticipation: settings[2], - supportThreshold: settings[3], + minParticipation: decodeRatio(settings[2], 6), + supportThreshold: decodeRatio(settings[3], 6), minDuration: settings[4], - daoTokenAddress: settings[5], - minProposerVotingPower: settings[6], - censusStrategy: settings[7], + expirationTime: settings[5], + daoTokenAddress: settings[6], + minProposerVotingPower: settings[7].toBigInt(), + censusStrategy: settings[8], + }; +} + +export function proposalParamsfromContract( + params: GaslessProposalParametersContractStruct +): GaslessProposalParametersStruct { + return { + censusBlock: params.censusBlock, + securityBlock: 0, + startDate: params.startDate.toNumber(), + endDate: params.endDate.toNumber(), + expirationDate: params.expirationDate.toNumber(), }; } @@ -91,7 +125,7 @@ export function initParamsToContract(params: OffchainVotingPluginInstall) { } return [ params.committee, - votingSettingsToContract(params.votingSettings), + createProposalVotingSettingsToContract(params.votingSettings), token, balances, ]; @@ -162,26 +196,19 @@ export async function voteWithSigners( export function toGaslessVotingProposal( proposal: ProposalFromSC -): GasslessVotingProposal { - proposal.parameters.startDate; - const startDate = new Date(proposal.parameters.startDate.toString()); - const endDate = new Date(proposal.parameters.endDate.toString()); - const expirationDate = new Date( - proposal.parameters.expirationDate.toString() - ); +): GaslessVotingProposalFromSC { + // const startDate = new Date(proposal.parameters.startDate.toString()); + // const endDate = new Date(proposal.parameters.endDate.toString()); + // const expirationDate = new Date( + // proposal.parameters.expirationDate.toString() + // ); return { executed: proposal.executed, // TODO FIX // approvers: proposal.approvals, approvers: [], vochainProposalId: proposal.vochainProposalId, - parameters: { - censusBlock: proposal.parameters.censusBlock.toNumber(), - securityBlock: 0, - startDate, - endDate, - expirationDate, - }, + parameters: proposalParamsfromContract(proposal.parameters), allowFailureMap: proposal.allowFailureMap.toNumber(), tally: proposal.tally.map((int) => { return int.map((x) => x.toNumber()); @@ -195,68 +222,140 @@ export function toGaslessVotingProposal( }), }; } -// let usedVotingWeight: bigint = BigInt(0); -// for (const voter of proposal.voters) { -// usedVotingWeight += BigInt(voter.votingPower); -// } -// const token = parseToken(proposal.plugin.token); -// return { -// id: getCompactProposalId(proposal.id), -// dao: { -// address: proposal.dao.id, -// name: proposal.dao.subdomain, -// }, -// creatorAddress: proposal.creator, -// metadata: { -// title: metadata.title, -// summary: metadata.summary, -// description: metadata.description, -// resources: metadata.resources, -// media: metadata.media, -// }, -// startDate, -// endDate, -// creationDate, -// creationBlockNumber: parseInt(proposal.creationBlockNumber), -// executionDate, -// executionBlockNumber: parseInt(proposal.executionBlockNumber) || null, -// executionTxHash: proposal.executionTxHash || null, -// actions: proposal.actions.map( -// (action: SubgraphAction): DaoAction => { -// return { -// data: hexToBytes(action.data), -// to: action.to, -// value: BigInt(action.value), -// }; -// }, -// ), -// status: computeProposalStatus(proposal), -// result: { -// yes: proposal.yes ? BigInt(proposal.yes) : BigInt(0), -// no: proposal.no ? BigInt(proposal.no) : BigInt(0), -// abstain: proposal.abstain ? BigInt(proposal.abstain) : BigInt(0), -// }, -// settings: { -// supportThreshold: decodeRatio(BigInt(proposal.supportThreshold), 6), -// duration: parseInt(proposal.endDate) - -// parseInt(proposal.startDate), -// minParticipation: decodeRatio( -// (BigInt(proposal.minVotingPower) * BigInt(1000000)) / -// BigInt(proposal.totalVotingPower), -// 6, -// ), -// }, -// token, -// usedVotingWeight, -// totalVotingWeight: BigInt(proposal.totalVotingPower), -// votes: proposal.voters.map( -// (voter: SubgraphTokenVotingVoterListItem) => { -// return { -// voteReplaced: voter.voteReplaced, -// address: voter.voter.address, -// vote: SubgraphVoteValuesMap.get(voter.voteOption) as VoteValues, -// weight: BigInt(voter.votingPower), -// }; -// }, -// ), -// }; + +function vochainVoteResultsToProposal(results: string[][]): { + [key: string]: number; +} { + let parsedResults: { [key: string]: number } = {}; + Object.keys(VoteValues) + .filter((key) => isNaN(Number(key))) + .forEach((key, i) => { + parsedResults[key] = Number(results[i]); + }); + return parsedResults; +} + +function hasSupportThreshold( + yes: number, + no: number, + supportThreshold: number +): boolean { + return (1 - supportThreshold) * yes > supportThreshold * no; +} + +function hasMinParticipation( + yes: number, + no: number, + abstain: number, + totalVotes: number, + minParticipation: number +): boolean { + return yes + no + abstain > minParticipation * totalVotes; +} + +function isProposalApproved( + results: string[][], + totalVotes: number, + supportThreshold: number, + minParticipation: number +): boolean { + const parsedResults = vochainVoteResultsToProposal(results); + const calculatedTotal = Object.keys(VoteValues) + .filter((key) => isNaN(Number(key))) + .map((x) => parsedResults[x]) + .reduce((a, b) => a + b); + if (calculatedTotal != totalVotes) throw new InvalidResults(); + + return ( + hasSupportThreshold( + parsedResults[VoteValues.YES.toString()], + parsedResults[VoteValues.NO.toString()], + supportThreshold + ) && + hasMinParticipation( + parsedResults[VoteValues.YES.toString()], + parsedResults[VoteValues.NO.toString()], + parsedResults[VoteValues.ABSTAIN.toString()], + totalVotes, + minParticipation + ) + ); +} + +export function vochainStatusToProposalStatus( + vochainProposal: PublishedElection, + executed: boolean, + supportThreshold: number, + minParticipation: number +): ProposalStatus { + const vochainStatus = vochainProposal.status; + if ([ElectionStatus.UPCOMING, ElectionStatus.PAUSED].includes(vochainStatus)) + return ProposalStatus.PENDING; + if (ElectionStatus.ONGOING === vochainStatus) return ProposalStatus.ACTIVE; + // if ([].includes[vochainStatus]) + if (ElectionStatus.RESULTS) { + if (executed) return ProposalStatus.EXECUTED; + else if (vochainProposal.finalResults) { + return isProposalApproved( + vochainProposal.results, + vochainProposal.voteCount, + supportThreshold, + minParticipation + ) + ? ProposalStatus.SUCCEEDED + : ProposalStatus.DEFEATED; + } else { + //TODO decide how to handle this cases + return ProposalStatus.PENDING; + } + } + // TODO decide how to handle this cases + if ( + [ElectionStatus.CANCELED, ElectionStatus.PROCESS_UNKNOWN].includes( + vochainStatus + ) + ) + return ProposalStatus.PENDING; + // TODO decide which is the generic one + return ProposalStatus.PENDING; +} + +export function toNewProposal( + SCproposalID: number, + dao: IDAO, + settings: GaslessPluginVotingSettings, + vochainProposal: PublishedElection, + SCProposal: GaslessVotingProposalFromSC +): GaslessVotingProposal { + // vochainProposal. + return { + id: `0x${SCproposalID.toString()}`, // string; + dao: { + address: dao.address, //string; + name: '', //string; TODO + }, + creatorAddress: vochainProposal.organizationId, //string; + metadata: EMPTY_PROPOSAL_METADATA_LINK, //ProposalMetadata; //TODO + startDate: vochainProposal.startDate, //Date; + endDate: vochainProposal.endDate, //Date; + creationDate: vochainProposal.creationTime, //Date; + actions: SCProposal.actions, //DaoAction[]; + status: vochainStatusToProposalStatus( + vochainProposal, + SCProposal.executed, + settings.supportThreshold, + settings.minParticipation + ), //ProposalStatus; //TODO + creationBlockNumber: 0, //number; //TODO + executionDate: null, //Date | null; //TODO + executionBlockNumber: null, //number | null; //TODO + executionTxHash: null, //string | null; + executed: SCProposal.executed, //boolean; + approvers: SCProposal.approvers, //string[]; + vochainProposalId: SCProposal.vochainProposalId, //string; + parameters: SCProposal.parameters, //GaslessProposalParametersStruct; + allowFailureMap: SCProposal.allowFailureMap, //number; + tally: SCProposal.tally, //number[][]; + settings, + } as GaslessVotingProposal; +} diff --git a/packages/js-client/src/types.ts b/packages/js-client/src/types.ts index 7d0327dd..5b75b2ec 100644 --- a/packages/js-client/src/types.ts +++ b/packages/js-client/src/types.ts @@ -6,6 +6,7 @@ import { VersionTag, ContextParams, DaoAction, + ProposalBase, } from '@aragon/sdk-client-common'; import { BigNumber } from '@ethersproject/bignumber'; import { VocdoniVoting } from '@vocdoni/offchain-voting-ethers'; @@ -36,7 +37,7 @@ type ExistingTokenParams = { // export type OffchainVotingPluginInstall = TokenVotingPluginInstall; export type OffchainVotingPluginInstall = { committee: string[]; - votingSettings: VocdoniVotingSettings; + votingSettings: GaslessPluginVotingSettings; newToken?: NewTokenParams; useToken?: ExistingTokenParams; }; @@ -58,7 +59,7 @@ export type OffchainVotingOverriddenState = OverriddenState & { }; export type ContractMintTokenParams = [string, BigNumber]; export type ContractTokenVotingInitParams = [ - VocdoniVotingSettings[], + GaslessPluginVotingSettings[], [ string, // address string, // name @@ -93,10 +94,10 @@ export enum VotingMode { export type VotingSettings = { votingMode: number; - supportThreshold: BigNumber; - minParticipation: BigNumber; - minDuration: number; - minProposerVotingPower: number; + supportThreshold: number; + minParticipation: number; + minDuration: BigNumber; + minProposerVotingPower: BigNumber; }; export const RATIO_BASE = BigNumber.from(10).pow(6); // 100% => 10**6 @@ -106,45 +107,72 @@ export const ONE_HOUR = 60 * 60; export const ONE_DAY = 24 * ONE_HOUR; export const ONE_YEAR = 365 * ONE_DAY; -export type VocdoniVotingSettings = { +export type GaslessPluginVotingSettings = { minTallyApprovals: number; minDuration: BigNumber; + expirationTime: BigNumber; minParticipation: number; supportThreshold: number; - minProposerVotingPower: BigNumber; + minProposerVotingPower: bigint; censusStrategy: string; - daoTokenAddress: string; + daoTokenAddress?: string; // calculated during the DAO installation onlyCommitteeProposalCreation?: boolean; }; -export type vocdoniProposalParamsOut = { - censusBlock: number; - securityBlock: number; - startDate: Date; - endDate: Date; - expirationDate: Date; +// export type GaslessProposalParamsOut = { +// censusBlock: number; +// securityBlock: number; +// startDate: Date; +// endDate: Date; +// expirationDate: Date; +// }; + +// export type GaslessProposalParams = { +// censusBlock: string[]; // following the multichain notation https://eips.ethereum.org/EIPS/eip-3770 +// securityBlock: number; // calculated internally in the smart contract +// startDate: number; +// endDate: number; +// expirationDate: number; // calculated internally in the smart contract based on expirationTime +// }; + +export type GaslessProposalParametersStruct = { + censusBlock?: string[]; // following the multichain notation https://eips.ethereum.org/EIPS/eip-3770 + securityBlock?: number; // calculated internally in the smart contract + startDate: number; // UNIX timestamp (ms) + endDate: number; // UNIX timestamp (ms) + expirationDate?: number; // calculated internally in the smart contract based on expirationTime }; -export type vocdoniProposalParams = { - censusBlock: number; - securityBlock: number; - startDate: number; - endDate: number; - expirationDate: number; +export type GaslessProposalParametersContractStruct = { + censusBlock: string[]; + securityBlock: BigNumber; + startDate: BigNumber; + endDate: BigNumber; + expirationDate: BigNumber; }; -export type GasslessVotingProposal = { +export type GaslessVotingProposalFromSC = { executed: boolean; approvers: string[]; vochainProposalId: string; - parameters: vocdoniProposalParamsOut; + parameters: GaslessProposalParametersStruct; allowFailureMap: number; tally: number[][]; actions?: DaoAction[]; }; +export type GaslessVotingProposal = ProposalBase & { + executed: boolean; + approvers: string[]; + vochainProposalId: string; + parameters: GaslessProposalParametersStruct; + allowFailureMap: number; + tally: number[][]; + settings: GaslessPluginVotingSettings; +}; + export type CreateGasslessProposalParams = CreateProposalBaseParams & - vocdoniProposalParams & { + GaslessProposalParametersStruct & { vochainProposalId: string; }; @@ -212,3 +240,28 @@ export type ProposalFromSC = { tally: BigNumber[][]; actions: IDAO.ActionStructOutput[]; }; + +export type GaslessVotingMember = { + /** The address of the member */ + address: string; + /** The balance of the member */ + balance: bigint; +}; + +class SdkError extends Error { + public cause?: Error | string; + constructor(message: string, cause?: any) { + super(message); + if (typeof cause === 'string') { + this.cause = cause; + } else if (cause instanceof Error) { + this.cause = cause.message; + } + } +} + +export class InvalidResults extends SdkError { + constructor(message?: string, cause?: any) { + super(message ? message : 'Invalid results', cause); + } +} diff --git a/packages/js-client/test/helpers/deploy-contracts.ts b/packages/js-client/test/helpers/deploy-contracts.ts index bfb035a3..8a9b30cd 100644 --- a/packages/js-client/test/helpers/deploy-contracts.ts +++ b/packages/js-client/test/helpers/deploy-contracts.ts @@ -1,21 +1,23 @@ -import { ERC1967ABI, ERC1967Bytecode } from "../abi"; -import * as aragonContracts from "@aragon/osx-ethers"; -import ENSRegistry from "@ensdomains/ens-contracts/artifacts/contracts/registry/ENSRegistry.sol/ENSRegistry.json"; -import PublicResolver from "@ensdomains/ens-contracts/artifacts/contracts/resolvers/PublicResolver.sol/PublicResolver.json"; -import { Signer } from "@ethersproject/abstract-signer"; -import { hexlify } from "@ethersproject/bytes"; -import { AddressZero, HashZero } from "@ethersproject/constants"; -import { Contract, ContractFactory } from "@ethersproject/contracts"; -import { id, namehash } from "@ethersproject/hash"; -import { JsonRpcProvider } from "@ethersproject/providers"; -import { toUtf8Bytes } from "@ethersproject/strings"; -import { parseEther } from "@ethersproject/units"; +import { ERC1967ABI, ERC1967Bytecode } from '../abi'; +import * as aragonContracts from '@aragon/osx-ethers'; +import ENSRegistry from '@ensdomains/ens-contracts/artifacts/contracts/registry/ENSRegistry.sol/ENSRegistry.json'; +import PublicResolver from '@ensdomains/ens-contracts/artifacts/contracts/resolvers/PublicResolver.sol/PublicResolver.json'; +import { Signer } from '@ethersproject/abstract-signer'; +import { hexlify } from '@ethersproject/bytes'; +import { AddressZero, HashZero } from '@ethersproject/constants'; +import { Contract, ContractFactory } from '@ethersproject/contracts'; +import { id, namehash } from '@ethersproject/hash'; +import { JsonRpcProvider } from '@ethersproject/providers'; +import { toUtf8Bytes } from '@ethersproject/strings'; +import { parseEther } from '@ethersproject/units'; import { VocdoniVotingSetup, VocdoniVotingSetup__factory, -} from "@vocdoni/offchain-voting-ethers"; +} from '@vocdoni/offchain-voting-ethers'; -export type Deployment = OsxDeployment & VocdoniVotingDeployment & EnsDeployment; +export type Deployment = OsxDeployment & + VocdoniVotingDeployment & + EnsDeployment; export type VocdoniVotingDeployment = { vocdoniVotingRepo: aragonContracts.PluginRepo; @@ -35,19 +37,22 @@ export type EnsDeployment = { ensResolver: Contract; }; -const WALLET_ADDRESS = "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199"; +const WALLET_ADDRESS = '0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199'; export async function deploy(): Promise { - const provider = new JsonRpcProvider("http://127.0.0.1:8545"); + const provider = new JsonRpcProvider('http://127.0.0.1:8545'); const deployOwnerWallet = provider.getSigner(); const ens = await deployEnsContracts(deployOwnerWallet); const osx = await deployOsxContracts(deployOwnerWallet, ens); - const offchainVoting = await deployvocdoniVotingContracts(deployOwnerWallet, osx); + const offchainVoting = await deployvocdoniVotingContracts( + deployOwnerWallet, + osx + ); // send ETH to hardcoded wallet in tests await deployOwnerWallet.sendTransaction({ to: WALLET_ADDRESS, - value: parseEther("50.0"), + value: parseEther('50.0'), }); return { ...osx, @@ -58,38 +63,37 @@ export async function deploy(): Promise { export async function deployvocdoniVotingContracts( deployer: Signer, - osx: OsxDeployment, + osx: OsxDeployment ): Promise { // Deploying the base contracts for the Token Voting Plugin - const governanceErc20Factory = new aragonContracts - .GovernanceERC20__factory(); - const governanceWrappedErc20Factory = new aragonContracts - .GovernanceWrappedERC20__factory(); - const governanceErc20Instance = await governanceErc20Factory.connect( - deployer, - ).deploy(AddressZero, "Test Token", "TTK", { amounts: [], receivers: [] }); + const governanceErc20Factory = new aragonContracts.GovernanceERC20__factory(); + const governanceWrappedErc20Factory = + new aragonContracts.GovernanceWrappedERC20__factory(); + const governanceErc20Instance = await governanceErc20Factory + .connect(deployer) + .deploy(AddressZero, 'Test Token', 'TTK', { amounts: [], receivers: [] }); const governanceErc20WrappedInstance = await governanceWrappedErc20Factory - .connect( - deployer, - ).deploy(AddressZero, "Wrapped Test Token", "wTTK"); + .connect(deployer) + .deploy(AddressZero, 'Wrapped Test Token', 'wTTK'); const vocdoniVotingFactory = new VocdoniVotingSetup__factory(); - const vocdoniVortingPluginSetup = await vocdoniVotingFactory.connect(deployer) + const vocdoniVortingPluginSetup = await vocdoniVotingFactory + .connect(deployer) .deploy( governanceErc20Instance.address, - governanceErc20WrappedInstance.address, + governanceErc20WrappedInstance.address ); const vocdoniVotingRepoAddress = await deployPlugin( - "simple-storage", + 'vocdoni-voting', vocdoniVortingPluginSetup.address, await deployer.getAddress(), - osx.pluginRepoFactory, + osx.pluginRepoFactory ); const vocdoniVotingRepo = aragonContracts.PluginRepo__factory.connect( vocdoniVotingRepoAddress, - deployer, + deployer ); return { @@ -103,25 +107,23 @@ async function deployPlugin( setupAddress: string, maintainer: string, pluginRepoFactory: aragonContracts.PluginRepoFactory, - releaseMetadata: string = - "ipfs://QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", - buildMetadata: string = - "ipfs://QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", + releaseMetadata: string = 'ipfs://QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR', + buildMetadata: string = 'ipfs://QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR' ) { - const address = await pluginRepoFactory.callStatic - .createPluginRepoWithFirstVersion( + const address = + await pluginRepoFactory.callStatic.createPluginRepoWithFirstVersion( name, setupAddress, maintainer, hexlify(toUtf8Bytes(releaseMetadata)), - hexlify(toUtf8Bytes(buildMetadata)), + hexlify(toUtf8Bytes(buildMetadata)) ); const tx = await pluginRepoFactory.createPluginRepoWithFirstVersion( name, setupAddress, maintainer, hexlify(toUtf8Bytes(releaseMetadata)), - hexlify(toUtf8Bytes(buildMetadata)), + hexlify(toUtf8Bytes(buildMetadata)) ); await tx.wait(); return address; @@ -129,39 +131,39 @@ async function deployPlugin( export async function deployOsxContracts( signer: Signer, - ens: EnsDeployment, + ens: EnsDeployment ): Promise { try { const { ensRegistry, ensResolver } = ens; const proxyFactory = new ContractFactory( ERC1967ABI, ERC1967Bytecode, - signer, + signer ); const managingDaoFactory = new aragonContracts.DAO__factory(); const managingDao = await managingDaoFactory.connect(signer).deploy(); - const initializeManagingDaoData = managingDaoFactory.interface - .encodeFunctionData("initialize", [ - "0x", + const initializeManagingDaoData = + managingDaoFactory.interface.encodeFunctionData('initialize', [ + '0x', await signer.getAddress(), AddressZero, - "0x", + '0x', ]); const managingDaoProxy = await proxyFactory.deploy( managingDao.address, - initializeManagingDaoData, + initializeManagingDaoData ); const managingDaoInstance = aragonContracts.DAO__factory.connect( managingDaoProxy.address, - signer, + signer ); - const ensSubdomainRegistrarFactory = new aragonContracts - .ENSSubdomainRegistrar__factory(); + const ensSubdomainRegistrarFactory = + new aragonContracts.ENSSubdomainRegistrar__factory(); // DAO Registrar const daoRegistrar = await ensSubdomainRegistrarFactory @@ -173,98 +175,98 @@ export async function deployOsxContracts( const daoRegsitrarProxy = await proxyFactory.deploy( daoRegistrar.address, - "0x", + '0x' ); const pluginRegistrarProxy = await proxyFactory.deploy( pluginRegistrar.address, - "0x", + '0x' ); - const daoRegistrarInstance = aragonContracts.ENSSubdomainRegistrar__factory - .connect( + const daoRegistrarInstance = + aragonContracts.ENSSubdomainRegistrar__factory.connect( daoRegsitrarProxy.address, - signer, + signer ); - const pluginRegistrarInstance = aragonContracts - .ENSSubdomainRegistrar__factory.connect( + const pluginRegistrarInstance = + aragonContracts.ENSSubdomainRegistrar__factory.connect( pluginRegistrarProxy.address, - signer, + signer ); await registerEnsName( - "eth", - "dao", + 'eth', + 'dao', ensRegistry, daoRegistrarInstance.address, - ensResolver.address, + ensResolver.address ); await registerEnsName( - "eth", - "plugin", + 'eth', + 'plugin', ensRegistry, pluginRegistrarInstance.address, - ensResolver.address, + ensResolver.address ); await daoRegistrarInstance.initialize( managingDaoInstance.address, ensRegistry.address, - namehash("dao.eth"), + namehash('dao.eth') ); await pluginRegistrarInstance.initialize( managingDaoInstance.address, ensRegistry.address, - namehash("plugin.eth"), + namehash('plugin.eth') ); // Dao Registry const daoRegistryFactory = new aragonContracts.DAORegistry__factory(); const daoRegistry = await daoRegistryFactory.connect(signer).deploy(); const daoRegistryProxy = await proxyFactory.deploy( daoRegistry.address, - "0x", + '0x' ); const daoRegistryInstance = aragonContracts.DAORegistry__factory.connect( daoRegistryProxy.address, - signer, + signer ); await daoRegistryInstance.initialize( managingDaoInstance.address, - daoRegistrarInstance.address, + daoRegistrarInstance.address ); // Plugin Repo Registry - const pluginRepoRegistryFactory = new aragonContracts - .PluginRepoRegistry__factory(); + const pluginRepoRegistryFactory = + new aragonContracts.PluginRepoRegistry__factory(); const pluginRepoRegistry = await pluginRepoRegistryFactory .connect(signer) .deploy(); const pluginRepoRegistryProxy = await proxyFactory.deploy( pluginRepoRegistry.address, - "0x", + '0x' ); - const pluginRepoRegistryInstance = aragonContracts - .PluginRepoRegistry__factory.connect( + const pluginRepoRegistryInstance = + aragonContracts.PluginRepoRegistry__factory.connect( pluginRepoRegistryProxy.address, - signer, + signer ); await pluginRepoRegistryInstance.initialize( managingDaoInstance.address, - pluginRegistrarInstance.address, + pluginRegistrarInstance.address ); // Plugin Repo Factory - const pluginRepoFactoryFactory = new aragonContracts - .PluginRepoFactory__factory(); + const pluginRepoFactoryFactory = + new aragonContracts.PluginRepoFactory__factory(); const pluginRepoFactory = await pluginRepoFactoryFactory .connect(signer) .deploy(pluginRepoRegistryInstance.address); // Plugin Setup Prcessor - const pluginSetupProcessorFacotry = new aragonContracts - .PluginSetupProcessor__factory(); + const pluginSetupProcessorFacotry = + new aragonContracts.PluginSetupProcessor__factory(); const pluginSetupProcessor = await pluginSetupProcessorFacotry .connect(signer) .deploy(pluginRepoRegistryInstance.address); @@ -280,25 +282,25 @@ export async function deployOsxContracts( await managingDaoInstance.grant( daoRegistrarInstance.address, daoRegistryInstance.address, - id("REGISTER_ENS_SUBDOMAIN_PERMISSION"), + id('REGISTER_ENS_SUBDOMAIN_PERMISSION') ); // ENS Plugin await managingDaoInstance.grant( pluginRegistrarInstance.address, pluginRepoRegistryInstance.address, - id("REGISTER_ENS_SUBDOMAIN_PERMISSION"), + id('REGISTER_ENS_SUBDOMAIN_PERMISSION') ); // DAO Registry await managingDaoInstance.grant( daoRegistryInstance.address, daoFactory.address, - id("REGISTER_DAO_PERMISSION"), + id('REGISTER_DAO_PERMISSION') ); // Plugin Registry await managingDaoInstance.grant( pluginRepoRegistryInstance.address, pluginRepoFactory.address, - id("REGISTER_PLUGIN_REPO_PERMISSION"), + id('REGISTER_PLUGIN_REPO_PERMISSION') ); return { managingDaoAddress: managingDaoInstance.address, @@ -316,11 +318,11 @@ async function deployEnsContracts(signer: Signer) { try { const registryFactory = new ContractFactory( ENSRegistry.abi, - ENSRegistry.bytecode, + ENSRegistry.bytecode ); const publicResolverFactory = new ContractFactory( PublicResolver.abi, - PublicResolver.bytecode, + PublicResolver.bytecode ); const registry = await registryFactory.connect(signer).deploy(); @@ -332,11 +334,11 @@ async function deployEnsContracts(signer: Signer) { await publicResolver.deployed(); await registerEnsName( - "", - "eth", + '', + 'eth', registry, await signer.getAddress(), - publicResolver.address, + publicResolver.address ); return { ensRegistry: registry, ensResolver: publicResolver }; } catch (e) { @@ -349,15 +351,15 @@ async function registerEnsName( name: string, registry: Contract, owner: string, - resolver: string, + resolver: string ) { try { await registry.setSubnodeRecord( - tld !== "" ? namehash(tld) : HashZero, + tld !== '' ? namehash(tld) : HashZero, id(name), owner, resolver, - 0, + 0 ); } catch (e) { throw e; diff --git a/packages/js-client/test/integration/encoding.test.ts b/packages/js-client/test/integration/encoding.test.ts index 5e339059..9f902eef 100644 --- a/packages/js-client/test/integration/encoding.test.ts +++ b/packages/js-client/test/integration/encoding.test.ts @@ -8,7 +8,7 @@ import { } from "@aragon/sdk-client-common"; import { Server } from "ganache"; import { OffchainVotingClient } from "../../src"; -import { VocdoniVotingSettings } from "../../src/internal/types"; +import { GaslessPluginVotingSettings } from "../../src/internal/types"; import { AddressZero } from "@ethersproject/constants"; import { BigNumber } from "@ethersproject/bignumber"; import { encodeRatio } from "@aragon/sdk-common"; @@ -50,7 +50,7 @@ describe("Encoding", () => { ], }; - const vocdoniVotingSettings: VocdoniVotingSettings = { + const GaslessPluginVotingSettings: GaslessPluginVotingSettings = { onlyCommitteeProposalCreation: true, minTallyApprovals: 1, minDuration: 3600, @@ -65,7 +65,7 @@ describe("Encoding", () => { .getPluginInstallItem( { committee: [TEST_WALLET_ADDRESS], - votingSettings: vocdoniVotingSettings, + votingSettings: GaslessPluginVotingSettings, newToken: token, }, "maticmum", diff --git a/packages/js-client/test/integration/methods.test.ts b/packages/js-client/test/integration/methods.test.ts index b3344064..b6c1e868 100644 --- a/packages/js-client/test/integration/methods.test.ts +++ b/packages/js-client/test/integration/methods.test.ts @@ -1,20 +1,18 @@ import * as mockedGraphqlRequest from '../mocks/graphql-request'; import { - NumbersQueryParams, - NumbersSortBy, - MyPluginClient, - MyPluginContext, + // NumbersQueryParams, + // NumbersSortBy, OffchainVotingContext, OffchainVotingClient, } from '../../src'; -import { QueryNumber, QueryNumbers } from '../../src/internal/graphql-queries'; -import { - SubgraphNumber, - SubgraphNumberListItem, -} from '../../src/internal/types'; +// import { QueryNumber, QueryNumbers } from '../../src/internal/graphql-queries'; +// import { +// SubgraphNumber, +// SubgraphNumberListItem, +// } from '../../src/internal/types'; import { contextParamsLocalChain } from '../constants'; -import { buildMyPluginDao } from '../helpers/build-daos'; +import { buildVocdoniVotingDao } from '../helpers/build-daos'; import * as deployContracts from '../helpers/deploy-contracts'; import * as ganacheSetup from '../helpers/ganache-setup'; import { @@ -30,16 +28,16 @@ import { Server } from 'ganache'; jest.spyOn(SupportedNetworksArray, 'includes').mockReturnValue(true); jest .spyOn(ContextCore.prototype, 'network', 'get') - .mockReturnValue({ chainId: 5, name: 'goerli' }); + .mockReturnValue({ chainId: 80001, name: 'polygonMumbai' }); describe('Methods', () => { let server: Server; let deployment: deployContracts.Deployment; let dao: { dao: string; plugins: string[] }; beforeAll(async () => { - server = await ganacheSetup.start(); - deployment = await deployContracts.deploy(); - dao = await buildMyPluginDao(deployment); + // server = await ganacheSetup.start(); + // deployment = await deployContracts.deploy(); + dao = await buildVocdoniVotingDao(deployment); contextParamsLocalChain.offchainVotingRepoAddress = deployment.vocdoniVotingRepo.address; contextParamsLocalChain.ensRegistryAddress = deployment.ensRegistry.address; @@ -56,70 +54,70 @@ describe('Methods', () => { const client = new OffchainVotingClient(context); client.methods.prepareInstallation( { - + } ) - + }); - it('Should get a number', async () => { - const context = new MyPluginContext(contextParamsLocalChain); - const client = new MyPluginClient(context); - const mockedClient = mockedGraphqlRequest.getMockedInstance( - client.graphql.getClient() - ); - const subgraphResponse: SubgraphNumber = { - number: { - value: '1', - }, - }; - mockedClient.request.mockResolvedValueOnce({ - dao: subgraphResponse, - }); +// it('Should get a number', async () => { +// const context = new MyPluginContext(contextParamsLocalChain); +// const client = new MyPluginClient(context); +// const mockedClient = mockedGraphqlRequest.getMockedInstance( +// client.graphql.getClient() +// ); +// const subgraphResponse: SubgraphNumber = { +// number: { +// value: '1', +// }, +// }; +// mockedClient.request.mockResolvedValueOnce({ +// dao: subgraphResponse, +// }); - const number = await client.methods.getNumber(dao.dao); +// const number = await client.methods.getNumber(dao.dao); - expect(number.toString()).toBe('1'); +// expect(number.toString()).toBe('1'); - expect(mockedClient.request).toHaveBeenCalledWith(QueryNumber, { - id: dao.dao, - }); - }); +// expect(mockedClient.request).toHaveBeenCalledWith(QueryNumber, { +// id: dao.dao, +// }); +// }); - it('Should get a list of numbers', async () => { - const context = new MyPluginContext(contextParamsLocalChain); - const client = new MyPluginClient(context); - const mockedClient = mockedGraphqlRequest.getMockedInstance( - client.graphql.getClient() - ); - const limit = 5; - const params: NumbersQueryParams = { - limit, - sortBy: NumbersSortBy.CREATED_AT, - direction: SortDirection.ASC, - skip: 0, - }; - const subgraphResponse: SubgraphNumberListItem[] = [ - { - id: dao.dao, - subdomain: 'test', - number: { - value: '1', - }, - }, - ]; - mockedClient.request.mockResolvedValueOnce({ - daos: subgraphResponse, - }); +// it('Should get a list of numbers', async () => { +// const context = new MyPluginContext(contextParamsLocalChain); +// const client = new MyPluginClient(context); +// const mockedClient = mockedGraphqlRequest.getMockedInstance( +// client.graphql.getClient() +// ); +// const limit = 5; +// const params: NumbersQueryParams = { +// limit, +// sortBy: NumbersSortBy.CREATED_AT, +// direction: SortDirection.ASC, +// skip: 0, +// }; +// const subgraphResponse: SubgraphNumberListItem[] = [ +// { +// id: dao.dao, +// subdomain: 'test', +// number: { +// value: '1', +// }, +// }, +// ]; +// mockedClient.request.mockResolvedValueOnce({ +// daos: subgraphResponse, +// }); - const numbers = await client.methods.getNumbers(params); +// const numbers = await client.methods.getNumbers(params); - for (const [index, subgraphNumber] of subgraphResponse.entries()) { - expect(subgraphNumber.id).toBe(numbers[index].id); - expect(subgraphNumber.subdomain).toBe(numbers[index].subdomain); - expect(subgraphNumber.number.value).toBe(numbers[index].value.toString()); - } +// for (const [index, subgraphNumber] of subgraphResponse.entries()) { +// expect(subgraphNumber.id).toBe(numbers[index].id); +// expect(subgraphNumber.subdomain).toBe(numbers[index].subdomain); +// expect(subgraphNumber.number.value).toBe(numbers[index].value.toString()); +// } - expect(mockedClient.request).toHaveBeenCalledWith(QueryNumbers, params); - }); -}); +// expect(mockedClient.request).toHaveBeenCalledWith(QueryNumbers, params); +// }); +// });