Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Examples: add token-based #365

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions examples/token-based/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "token-based",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@ethersproject/wallet": "^5.7.0",
"@vocdoni/sdk": "^0.7.5"
},
"scripts": {
"start": "ts-node src/index.ts"
},
"devDependencies": {
"@types/node": "^20.11.25",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
}
}
17 changes: 17 additions & 0 deletions examples/token-based/readme.md
NateWilliams2 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Vocdoni SDK Token-Based Census example
==============================

This example shows how to use the SDK to create a token-based census in a Typescript project.

To execute this example, use the following steps:

~~~bash
git clone [email protected]:vocdoni/vocdoni-sdk.git
cd vocdoni-sdk/examples/token-based
yarn
yarn start
~~~

## Tutorial

This example was created to serve as a tutorial for implementing token-based censuses for voting with Vocdoni. The complete tutorial is available [here](https://developer.vocdoni.io/sdk/integration-details/census-types/on-chain#tutorial)
16 changes: 16 additions & 0 deletions examples/token-based/src/account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Account, VocdoniSDKClient } from '@vocdoni/sdk';

export const createAccount = (client: VocdoniSDKClient) => {
return client
.createAccount({
account: new Account({
languages: ['en'],
name: {
default: 'Account name',
},
description: 'Description of the account',
logo: 'https://logo.io',
}),
})
.then(() => client.fetchAccountInfo().then(info => console.log(info)));
};
55 changes: 55 additions & 0 deletions examples/token-based/src/census3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { VocdoniCensus3Client } from '@vocdoni/sdk';

// Check if target chain is supported
export const supportsChain = (census3Client: VocdoniCensus3Client, chainID: number) => {
return census3Client.getSupportedChains().then(chains => {
for (let chain of chains) {
// Check for Sepolia chainID
if (chain.chainID == chainID) {
return true;
}
}
return false;
});
};

// Check if token is already supported
export const supportsToken = (census3Client: VocdoniCensus3Client, tokenAddress: string) => {
return census3Client.getSupportedTokens().then(tokens => {
for (let token of tokens) {
if (token.ID.toLowerCase() === tokenAddress.toLowerCase()) {
return true;
}
}
return false;
});
};

export async function checkTokenReady (
census3Client: VocdoniCensus3Client,
tokenAddress,
tokenType: string,
chainID: number
) {
// See if our token's chain is supported
const supportsMyChain = await supportsChain(census3Client, chainID);
if (!supportsMyChain) {
console.error('Census service does not support chain %d', chainID);
return false;
}

// See if our token is already supported
const supportsMyToken = await supportsToken(census3Client, tokenAddress);
if (!supportsMyToken) {
console.log('Census service does not support token ' + tokenAddress + '. Registering token now.');
await census3Client.createToken(tokenAddress, tokenType, chainID);
}

// Check the token status. If syncing, return and try again later.
let status = (await census3Client.getToken(tokenAddress, chainID)).status;
if (!status.synced) {
console.log('Token %s is syncing. Progress %d%. Try again later.', tokenAddress, status.progress);
return false;
}
return true;
}
16 changes: 16 additions & 0 deletions examples/token-based/src/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { EnvOptions, VocdoniSDKClient, VocdoniCensus3Client } from '@vocdoni/sdk';
import { Wallet } from '@ethersproject/wallet';

export const getDefaultClient = () => {
const wallet = Wallet.createRandom();
const client = new VocdoniSDKClient({
env: EnvOptions.STG,
wallet: wallet,
});

return client;
};

export const getDefaultCensus3Client = () => {
return new VocdoniCensus3Client({ env: EnvOptions.STG });
};
45 changes: 45 additions & 0 deletions examples/token-based/src/election.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Election, ElectionStatus, TokenCensus, UnpublishedElection, VocdoniSDKClient } from '@vocdoni/sdk';

export const createElection = (census: TokenCensus): UnpublishedElection => {
const election: UnpublishedElection = Election.from({
title: 'Election title',
description: 'Election description',
header: 'https://source.unsplash.com/random',
endDate: new Date().getTime() + 100000,
census,
});

election.addQuestion('This is a title', 'This is a description', [
{
title: 'Option 1',
value: 0,
},
{
title: 'Option 2',
value: 1,
},
]);

return election;
};

const waitForElectionReady = (client: VocdoniSDKClient, electionId: string): Promise<string> => {
return new Promise(f => setTimeout(f, 5000))
.then(() => client.fetchElection(electionId))
.then(election => {
if (election.status !== ElectionStatus.ONGOING) {
return waitForElectionReady(client, electionId);
}
return Promise.resolve(electionId);
});
};

export const publishElection = (client: VocdoniSDKClient, election: UnpublishedElection): Promise<string> => {
return client.createElection(election).then(electionId => {
client.setElectionId(electionId);
console.log('Election created!', electionId);
console.log('View this election at ' + client.explorerUrl + '/processes/show/#/' + electionId);
console.log('Waiting for election to be published...');
return waitForElectionReady(client, electionId);
});
};
43 changes: 43 additions & 0 deletions examples/token-based/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { createAccount } from './account';
import { checkTokenReady } from './census3';
import { getDefaultClient, getDefaultCensus3Client } from './client';
import { createElection, publishElection } from './election';
import { castVotes, countVotes } from './vote';

// This is a hard-coded example of token you might use.
const myToken = {
address: '0x...',
chainID: 11155111,
type: 'erc20',
};

// Hard-coded list of private keys of token holders
const voters = ['...'];

async function main () {
const client = getDefaultClient();
const census3Client = getDefaultCensus3Client();

console.log('Checking if token is registered to the census3 service...');
const tokenIsReady = await checkTokenReady(census3Client, myToken.address, myToken.type, myToken.chainID);
if (!tokenIsReady) {
return;
}

console.log('Creating token-based census...');
const census = await census3Client.createTokenCensus(myToken.address, myToken.chainID);

console.log('Creating account...');
await createAccount(client);

console.log('Creating election...');
const election = createElection(census);
const electionId = await publishElection(client, election);

console.log('Voting...');
await castVotes(electionId, voters);

console.log('Getting results...');
await countVotes(client);
}
main();
30 changes: 30 additions & 0 deletions examples/token-based/src/vote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Wallet } from '@ethersproject/wallet';
import { EnvOptions, VocdoniSDKClient, Vote } from '@vocdoni/sdk';

export const castVotes = (electionId: string, voters: string[]) => {
var votePromises = [];
for (const voter of voters) {
const wallet = new Wallet(voter);
const client = new VocdoniSDKClient({ env: EnvOptions.STG, wallet: wallet, electionId: electionId });
// Create a vote for option 0 or 1
const vote = new Vote([Math.round(Math.random())]);
votePromises.push(
client.submitVote(vote).then(voteId => {
console.log('Vote sent! Vote id: ', voteId);
console.log('Verify vote at ' + client.explorerUrl + '/verify/#/' + voteId);
})
);
}
return Promise.all(votePromises);
};

export const countVotes = (client: VocdoniSDKClient) => {
return client.fetchElection().then(election => {
console.log('Election results: ');
election.questions.forEach(question => {
question.choices.forEach(choice => {
console.log(choice.title.default + ': ' + choice.results);
});
});
});
};
6 changes: 6 additions & 0 deletions examples/token-based/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"include": ["src"],
"compilerOptions": {
"rootDir": "./src",
}
}
Loading