Skip to main content
Glama
api.ts10.7 kB
import { CoinInfo, ContractAddress } from "@midnight-ntwrk/compact-runtime"; import { DeployedDaoVotingContract, DaoVotingProviders, DaoVotingPrivateState, DaoVotingState, VoteType, ElectionStatus } from "./common-types.js"; // Re-export VoteType for external use export { VoteType }; import { assertIsContractAddress } from "@midnight-ntwrk/midnight-js-utils"; import { DaoVoting, witnesses } from "./contract/index.js"; import { findDeployedContract, deployContract, FinalizedCallTxData } from "@midnight-ntwrk/midnight-js-contracts"; import { BalancedTransaction, createBalancedTx, FinalizedTxData, MidnightProvider, UnbalancedTransaction, WalletProvider } from "@midnight-ntwrk/midnight-js-types"; import { Wallet } from "@midnight-ntwrk/wallet-api"; import { Transaction, TransactionId } from "@midnight-ntwrk/zswap"; import { Resource } from "@midnight-ntwrk/wallet"; import { getLedgerNetworkId, getZswapNetworkId } from "@midnight-ntwrk/midnight-js-network-id"; import { Transaction as ZswapTransaction } from '@midnight-ntwrk/zswap'; import { levelPrivateStateProvider } from "@midnight-ntwrk/midnight-js-level-private-state-provider"; import { indexerPublicDataProvider } from "@midnight-ntwrk/midnight-js-indexer-public-data-provider"; import { NodeZkConfigProvider } from "@midnight-ntwrk/midnight-js-node-zk-config-provider"; import { httpClientProofProvider } from "@midnight-ntwrk/midnight-js-http-client-proof-provider"; import { config } from "../../config.js"; import path from "path"; import { firstValueFrom } from 'rxjs'; import { createLogger } from "../../logger/index.js"; const logger = createLogger('dao-voting-api'); const currentDir = path.resolve(process.cwd()); export const contractConfig = { privateStateStoreName: 'dao-voting-private-state', zkConfigPath: path.resolve(currentDir, 'src/integrations/dao/contract/managed/dao-voting'), }; /** * Helper function to pad string to specified length * pad(n, s): UTF-8 bytes of s followed by 0x00 up to length n */ export const pad = (s: string, n: number): Uint8Array => { const bytes = new TextEncoder().encode(s); if (bytes.length > n) throw new Error('String too long for pad length'); const out = new Uint8Array(n); out.set(bytes); return out; }; export const createWalletAndMidnightProvider = async (wallet: Wallet): Promise<WalletProvider & MidnightProvider> => { // Get the current wallet state using firstValueFrom const state = await firstValueFrom(wallet.state()); return { coinPublicKey: state.coinPublicKey, encryptionPublicKey: state.encryptionPublicKey, balanceTx(tx: UnbalancedTransaction, newCoins: CoinInfo[]): Promise<BalancedTransaction> { return wallet .balanceTransaction( ZswapTransaction.deserialize(tx.serialize(getLedgerNetworkId()), getZswapNetworkId()), newCoins, ) .then((tx) => wallet.proveTransaction(tx)) .then((zswapTx) => { const ledgerTx = Transaction.deserialize(zswapTx.serialize(getZswapNetworkId()), getLedgerNetworkId()); return createBalancedTx(ledgerTx as any); }); }, submitTx(tx: BalancedTransaction): Promise<TransactionId> { return wallet.submitTransaction(tx); }, }; }; export const configureProviders = async (wallet: Wallet & Resource): Promise<DaoVotingProviders> => { const walletAndMidnightProvider = await createWalletAndMidnightProvider(wallet); return { privateStateProvider: levelPrivateStateProvider({ privateStateStoreName: contractConfig.privateStateStoreName, }), publicDataProvider: indexerPublicDataProvider(config.indexer, config.indexerWS), zkConfigProvider: new NodeZkConfigProvider<'open_election' | 'close_election' | 'cast_vote' | 'fund_treasury' | 'payout_approved_proposal' | 'cancel_payout'>( contractConfig.zkConfigPath, ), proofProvider: httpClientProofProvider(config.proofServer), walletProvider: walletAndMidnightProvider, midnightProvider: walletAndMidnightProvider, }; }; export const daoVotingContractInstance = new (DaoVoting as any).Contract(witnesses); export const getDaoVotingLedgerState = async ( providers: DaoVotingProviders, contractAddress: ContractAddress, ): Promise<DaoVotingState | null> => { assertIsContractAddress(contractAddress); logger.info('Checking DAO voting contract ledger state...'); const state = await providers.publicDataProvider .queryContractState(contractAddress) .then((contractState: any) => (contractState != null ? (DaoVoting as any).ledger(contractState.data) : null)); logger.info(`Ledger state: ${state ? 'DAO voting available' : 'No state'}`); return state; }; export const joinDaoVotingContract = async ( providers: DaoVotingProviders, contractAddress: string, ): Promise<DeployedDaoVotingContract> => { const daoVotingContract = await findDeployedContract(providers, { contractAddress, contract: daoVotingContractInstance, privateStateId: 'daoVotingPrivateState', initialPrivateState: {}, }); logger.info(`Joined DAO voting contract at address: ${daoVotingContract.deployTxData.public.contractAddress}`); return daoVotingContract as any; }; export const deployDaoVotingContract = async ( providers: DaoVotingProviders, privateState: DaoVotingPrivateState, fundingTokenAddress: string, daoVoteTokenAddress: string, ): Promise<DeployedDaoVotingContract> => { logger.info('Deploying DAO voting contract...'); const fundingTokenAddressBytes = new Uint8Array(Buffer.from(fundingTokenAddress.replace('0x', ''), 'hex')); const daoVoteTokenAddressBytes = new Uint8Array(Buffer.from(daoVoteTokenAddress.replace('0x', ''), 'hex')); const daoVotingContract = await deployContract(providers, { contract: daoVotingContractInstance, privateStateId: 'daoVotingPrivateState', initialPrivateState: privateState, args: [ { bytes: fundingTokenAddressBytes }, { bytes: daoVoteTokenAddressBytes }, ], } as any); logger.info(`Deployed DAO voting contract at address: ${daoVotingContract.deployTxData.public.contractAddress}`); return daoVotingContract as any; }; export const openElection = async ( daoVotingContract: DeployedDaoVotingContract, electionId: string, ): Promise<FinalizedTxData> => { logger.info(`Opening election with ID: ${electionId}`); const electionIdBytes = pad(electionId, 32); const finalizedTxData = await daoVotingContract.callTx.open_election(electionIdBytes); logger.info(`Transaction ${finalizedTxData.public.txId} added in block ${finalizedTxData.public.blockHeight}`); return finalizedTxData.public; }; export const closeElection = async ( daoVotingContract: DeployedDaoVotingContract, ): Promise<FinalizedTxData> => { logger.info('Closing election...'); const finalizedTxData = await daoVotingContract.callTx.close_election(); logger.info(`Transaction ${finalizedTxData.public.txId} added in block ${finalizedTxData.public.blockHeight}`); return finalizedTxData.public; }; export const castVote = async ( daoVotingContract: DeployedDaoVotingContract, voteType: VoteType, voteCoin: any, // CoinInfo type from the contract ): Promise<FinalizedTxData> => { const voteTypeNames = ['YES', 'NO', 'ABSENT']; logger.info(`Casting ${voteTypeNames[voteType]} vote...`); const finalizedTxData = await daoVotingContract.callTx.cast_vote(BigInt(voteType), voteCoin); logger.info(`Transaction ${finalizedTxData.public.txId} added in block ${finalizedTxData.public.blockHeight}`); return finalizedTxData.public; }; export const fundTreasury = async ( daoVotingContract: DeployedDaoVotingContract, fundCoin: any, // CoinInfo type from the contract ): Promise<FinalizedTxData> => { logger.info('Funding treasury...'); const finalizedTxData = await daoVotingContract.callTx.fund_treasury(fundCoin); logger.info(`Transaction ${finalizedTxData.public.txId} added in block ${finalizedTxData.public.blockHeight}`); return finalizedTxData.public; }; export const payoutApprovedProposal = async ( daoVotingContract: DeployedDaoVotingContract, ): Promise<FinalizedTxData> => { logger.info('Paying out approved proposal to contract owner...'); const finalizedTxData = await daoVotingContract.callTx.payout_approved_proposal(); logger.info(`Transaction ${finalizedTxData.public.txId} added in block ${finalizedTxData.public.blockHeight}`); return finalizedTxData.public; }; export const cancelPayout = async ( daoVotingContract: DeployedDaoVotingContract, ): Promise<FinalizedTxData> => { logger.info('Cancelling payout...'); const finalizedTxData = await daoVotingContract.callTx.cancel_payout(); logger.info(`Transaction ${finalizedTxData.public.txId} added in block ${finalizedTxData.public.blockHeight}`); return finalizedTxData.public; }; export const getElectionStatus = async ( providers: DaoVotingProviders, contractAddress: ContractAddress, ): Promise<ElectionStatus | null> => { const state = await getDaoVotingLedgerState(providers, contractAddress); if (state === null) { return null; } return { isOpen: state.election_open, electionId: Buffer.from(state.election_id).toString('hex'), yesVotes: state.yes_votes, noVotes: state.no_votes, absentVotes: state.absent_votes, totalVotes: state.total_votes, }; }; export const displayDaoVotingState = async ( providers: DaoVotingProviders, daoVotingContract: DeployedDaoVotingContract, ): Promise<{ state: DaoVotingState | null; contractAddress: string }> => { const contractAddress = daoVotingContract.deployTxData.public.contractAddress; const state = await getDaoVotingLedgerState(providers, contractAddress); if (state === null) { logger.info(`There is no DAO voting contract deployed at ${contractAddress}.`); } else { logger.info(`DAO Voting State:`); logger.info(` Election Open: ${state.election_open}`); logger.info(` Election ID: ${Buffer.from(state.election_id).toString('hex')}`); logger.info(` Yes Votes: ${state.yes_votes}`); logger.info(` No Votes: ${state.no_votes}`); logger.info(` Absent Votes: ${state.absent_votes}`); logger.info(` Total Votes: ${state.total_votes}`); logger.info(` Treasury Value: ${state.treasury.value}`); logger.info(` Treasury Coin Color: ${Buffer.from(state.treasury.color).toString('hex')}`); logger.info(` Treasury Coin nonce: ${Buffer.from(state.treasury.nonce).toString('hex')}`); logger.info(` Treasury Coin mt_index: ${state.treasury.mt_index}`); logger.info(` DAO Vote Coin Color: ${Buffer.from(state.dao_vote_coin_color).toString('hex')}`); } return { contractAddress, state }; };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/evilpixi/pixi-midnight-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server