diff --git a/docs/dapp/opl/README.mdx b/docs/dapp/opl/README.mdx index d4463d5757..3329dc90e6 100644 --- a/docs/dapp/opl/README.mdx +++ b/docs/dapp/opl/README.mdx @@ -10,7 +10,7 @@ import {findSidebarItem} from '@site/src/sidebarUtils'; The Oasis Privacy Layer (OPL) is a powerful solution that enables developers to integrate privacy features into their decentralized applications (dApps) -across multiple EVM-compatible networks. +across multiple EVM-compatible networks. - **Privacy-First**: OPL leverages the [Sapphire]'s privacy features to ensure that contract data and computation remains confidential. @@ -65,7 +65,6 @@ enables seamless state transitions across multiple chains. ## Examples { -+ await hre.run('compile'); -+ const ethers = hre.ethers; -+ const BallotBoxV1 = await ethers.getContractFactory('BallotBoxV1'); -+ const signer = ethers.provider.getSigner(); -+ const signerAddr = await signer.getAddress(); -+ -+ // Start by predicting the address of the DAO contract. -+ const hostConfig = hre.config.networks[args.hostNetwork]; -+ if (!('url' in hostConfig)) throw new Error(`${args.hostNetwork} not configured`); -+ const provider = new ethers.providers.JsonRpcProvider(hostConfig.url); -+ let nonce = await provider.getTransactionCount(signerAddr); -+ if (args.hostNetwork === 'local') nonce++; -+ const daoAddr = ethers.utils.getContractAddress({ from: signerAddr, nonce }); -+ -+ const ballotBox = await BallotBoxV1.deploy(daoAddr); -+ await ballotBox.deployed(); -+ console.log('expected DAO', daoAddr); -+ console.log('BallotBox', ballotBox.address); -+ return ballotBox.address; -+ }); -+ -+task('deploy-dao') -+ .addParam('ballotBoxAddr') -+ .setAction(async (args, hre) => { -+ await hre.run('compile'); -+ const DAOV1 = await hre.ethers.getContractFactory('DAOV1'); -+ const dao = await DAOV1.deploy(args.ballotBoxAddr); -+ await dao.deployed(); -+ console.log('DAO', dao.address); -+ return dao; -+ }); -+ -+task('deploy-local').setAction(async (_args, hre) => { -+ await hre.run('compile'); -+ const ballotBox = await hre.run('deploy-ballot-box', { hostNetwork: 'local' }); -+ await hre.run('deploy-dao', { ballotBoxAddr: ballotBox }); -+ }); - - const config: HardhatUserConfig = { - solidity: "0.8.18", -+ networks: { -+ hardhat: { -+ chainId: 1337, // @see https://hardhat.org/metamask-issue.html -+ }, -+ local: { -+ url: 'http://127.0.0.1:8545', -+ }, -+ 'bsc-testnet': { -+ url: 'https://data-seed-prebsc-1-s1.binance.org:8545', -+ chainId: 97, -+ accounts, -+ }, -+ 'sapphire-testnet': { -+ url: 'https://testnet.sapphire.oasis.io', -+ chainId: 0x5aff, -+ accounts, -+ }, -+ } - }; - - export default config; -``` - -#### Localhost - -We can start local Hardhat node again: - -```shell -npx hardhat node -``` - -Our deploy should succeed locally. - -```shell -npx hardhat deploy-local --network localhost -Nothing to compile -No need to generate any newer typings. -Nothing to compile -No need to generate any newer typings. -expected DAO 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 -BallotBox 0x0165878A594ca255338adfa4d48449f69242Eb8F -Nothing to compile -No need to generate any newer typings. -DAO 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 -``` - -We will use these addresses in our frontend application. - -#### Testnet - -We can likewise deploy to [Testnet](../../sapphire/guide.mdx#testnet-and-mainnet) -with Sapphire. - -In this case, we should prepare a wallet with Testnet tokens on both BNB Smart -Chain [Faucet](https://testnet.bnbchain.org/faucet-smart) and Sapphire [faucet](https://faucet.testnet.oasis.io). - -We will use a common private key for both the host and enclave smart contracts. - -```shell -export PRIVATE_KEY= -``` - -Deploy the enclave smart contract using Testnet parameters. - -```shell -npx hardhat deploy-ballot-box --network sapphire-testnet --host-network bsc-testnet -Nothing to compile -No need to generate any newer typings. -expected DAO 0xFBcb580DD6D64fbF7caF57FB0439502412324179 -BallotBox 0xFb40591a8df155da291A4B52E4Df9901a95b7C06 -``` - -Next, use the obtained `BallotBox` address below to deploy the host smart -contract: - -```shell -npx hardhat deploy-dao --network bsc-testnet --ballot-box-addr {BALLOT_BOX_ADDR} -Nothing to compile -No need to generate any newer typings. -DAO 0xFBcb580DD6D64fbF7caF57FB0439502412324179 -``` - -:::info Example - -You can try out and download a complete backend example with both host and -enclave smart contracts from the -[Oasis Playground repository][backend-example]. - -::: - -[backend-example]: https://github.com/oasisprotocol/demo-opl-secret-ballot/tree/main/backend diff --git a/docs/dapp/opl/secret-ballot-example/enclave.md b/docs/dapp/opl/secret-ballot-example/enclave.md deleted file mode 100644 index d77638eb72..0000000000 --- a/docs/dapp/opl/secret-ballot-example/enclave.md +++ /dev/null @@ -1,184 +0,0 @@ -# Ballot Contract - -Next, we will write a smart contract that holds private data. This smart -contract will run inside a trusted execution environment (TEE) on the Oasis -Sapphire ParaTime, which why we refer to these as an *enclave* smart contract. - -Create a new Solidity contract named `BallotBoxV1.sol`. - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {Enclave, Result, autoswitch} from "@oasisprotocol/sapphire-contracts/contracts/OPL.sol"; - -error NotActive(); - -type ProposalId is bytes32; - -struct ProposalParams { - string ipfsHash; - uint16 numChoices; - bool publishVotes; -} - -contract BallotBoxV1 is Enclave { - error NotPublishingVotes(); - error AlreadyVoted(); - error UnknownChoice(); - - struct Ballot { - bool active; - ProposalParams params; - /// voter -> choice id - mapping(address => Choice) votes; - /// choice id -> vote - uint256[32] voteCounts; - } - - struct Choice { - bool exists; - uint8 choice; - } - - event BallotClosed(ProposalId indexed id, uint256 topChoice); - - mapping(ProposalId => Ballot) private _ballots; - - constructor(address dao) Enclave(dao, autoswitch("bsc")) { - registerEndpoint("createBallot", _oplCreateBallot); - } - - function castVote( - ProposalId proposalId, - uint256 choiceIdBig - ) external payable { - Ballot storage ballot = _ballots[proposalId]; - if (!ballot.active) revert NotActive(); - uint8 choiceId = uint8(choiceIdBig & 0xff); - if (choiceId >= ballot.params.numChoices) revert UnknownChoice(); - Choice memory existingVote = ballot.votes[_msgSender()]; - // 1 click 1 vote - for (uint256 i; i < ballot.params.numChoices; ++i) { - // read-modify-write all counts to make it harder to determine which one is chosen. - ballot.voteCounts[i] ^= 1 << 255; // flip the top bit to constify gas usage a bit - // Arithmetic is not guaranteed to be constant time, so this might still leak the choice to a highly motivated observer. - ballot.voteCounts[i] += i == choiceId ? 1 : 0; - ballot.voteCounts[i] -= existingVote.exists && existingVote.choice == i - ? 1 - : 0; - } - ballot.votes[_msgSender()].exists = true; - ballot.votes[_msgSender()].choice = choiceId; - } - - function closeBallot(ProposalId proposalId) external payable { - Ballot storage ballot = _ballots[proposalId]; - if (!ballot.active) revert NotActive(); - _closeBallot(proposalId, ballot); - } - - function getVoteOf(ProposalId proposalId, address voter) external view returns (Choice memory) { - Ballot storage ballot = _ballots[proposalId]; - if (voter == msg.sender) return ballot.votes[msg.sender]; - if (!ballot.params.publishVotes) revert NotPublishingVotes(); - return ballot.votes[voter]; - } - - function ballotIsActive(ProposalId id) external view returns (bool) { - return _ballots[id].active; - } - - function _oplCreateBallot(bytes calldata args) internal returns (Result) { - (ProposalId id, ProposalParams memory params) = abi.decode( - args, - (ProposalId, ProposalParams) - ); - Ballot storage ballot = _ballots[id]; - ballot.params = params; - ballot.active = true; - for (uint256 i; i < params.numChoices; ++i) ballot.voteCounts[i] = 1 << 255; // gas usage side-channel resistance. - return Result.Success; - } - - function _closeBallot(ProposalId _proposalId, Ballot storage _ballot) internal { - uint256 topChoice; - uint256 topChoiceCount; - for (uint8 i; i < _ballot.params.numChoices; ++i) { - uint256 choiceVoteCount = _ballot.voteCounts[i] & (type(uint256).max >> 1); - if (choiceVoteCount > topChoiceCount) { - topChoice = i; - topChoiceCount = choiceVoteCount; - } - } - postMessage("ballotClosed", abi.encode(_proposalId, topChoice)); - emit BallotClosed(_proposalId, topChoice); - delete _ballots[_proposalId]; - } -} -``` - -#### Autoswitch - -In this tutorial, the *enclave* smart contract will talk to the *host* smart -contract deployed on the Binance Smart Chain (`bsc`). - -:::tip - -Autoswitch will automatically pick the Testnet host network if the enclave network is also Testnet. - -::: - -Autoswitch supports the following networks: -- `ethereum` -- `goerli` -- `optimism` -- `bsc` -- `bsc-testnet` -- `polygon` -- `fantom` -- `fantom-testnet` -- `moonriver` -- `arbitrum-one` -- `arbitrum-nova` -- `sapphire` -- `sapphire-testnet` -- `polygon-mumbai` -- `avalanche` -- `avalanche-fuji` -- `arbitrum-testnet` - -```solidity - constructor(address dao) Enclave(dao, autoswitch("bsc")) { - registerEndpoint("createBallot", _oplCreateBallot); - } -``` - -#### Event - -Closing a ballot has an effect on the host chain network (`postMessage()`): - -```solidity - function _closeBallot(ProposalId _proposalId, Ballot storage _ballot) internal { - uint256 topChoice; - uint256 topChoiceCount; - for (uint8 i; i < _ballot.params.numChoices; ++i) { - uint256 choiceVoteCount = _ballot.voteCounts[i] & (type(uint256).max >> 1); - if (choiceVoteCount > topChoiceCount) { - topChoice = i; - topChoiceCount = choiceVoteCount; - } - } - postMessage("ballotClosed", abi.encode(_proposalId, topChoice)); - emit BallotClosed(_proposalId, topChoice); - delete _ballots[_proposalId]; - } -``` - -#### Private - -The private variable `_ballots` is encrypted on Sapphire. - -```solidity - mapping(ProposalId => Ballot) private _ballots; -``` diff --git a/docs/dapp/opl/secret-ballot-example/frontend.md b/docs/dapp/opl/secret-ballot-example/frontend.md deleted file mode 100644 index c697c5f25a..0000000000 --- a/docs/dapp/opl/secret-ballot-example/frontend.md +++ /dev/null @@ -1,110 +0,0 @@ -# Frontend Application - -We will need a [Pinata](https://www.pinata.cloud) development API -[key](https://docs.pinata.cloud/docs/getting-started#2-generate-your-api-keys) -and JWT with the `pinFileToIPFS` permission. Let's obtain that first. - -### VueJS - -We will take a shortcut and bypass developing a VueJS app. Instead, we will -simply apply a sparse checkout of the complete frontend repo. Inside your -`opl-secret-ballot` directory run: - -```shell -git init . -git remote add -f demo-opl-secret-ballot https://github.com/oasisprotocol/demo-opl-secret-ballot -git checkout demo-opl-secret-ballot/main frontend -``` - -Next, update the `@oasislabs/secret-ballot-backend` package name in -`frontend/package.json` to match your `backend/package.json` project name. - -We recommend using [pnpm](https://pnpm.io) to install dependencies, but `yarn` -and `npm` will work with some modifications around workspaces. - -```shell npm2yarn -npm install -``` - -Compile and Hot-Reload for Development -```shell npm2yarn -npm run dev -``` - -Build assets for deployment -```shell npm2yarn -npm run build -``` - -We can now reference the deployed contracts in our frontend Vue app. - -Modify the `.env.development` file with the appropriate addresses: -```yaml -VITE_BALLOT_BOX_V1_ADDR=0xFb40591a8df155da291A4B52E4Df9901a95b7C06 -``` -and -```yaml -VITE_DAO_V1_ADDR=0xFBcb580DD6D64fbF7caF57FB0439502412324179 -``` - -### Pinata - -Additionally, we will need a [Pinata](https://www.pinata.cloud) JWT -[key](https://docs.pinata.cloud/reference/datatestauthentication) to access the -pinning service with which we store our ballots as JSON. - -```yaml -VITE_PINATA_JWT= -``` - -### Start - -Start Vue app -```shell npm2yarn -npm run dev -``` - -### MetaMask - -You can use one of the deployed test accounts and associated private key with -[MetaMask](https://metamask.io). - -If you have not added a local network to MetaMask already, you can use this -configuration. - -#### Localhost - -* RPC HTTP endpoint: `http://127.0.0.1:8545/` -* Chain ID: - * Decimal: 1337 - -## Example - -You should be able to navigate to -[http://localhost:5173](http://localhost:5173) and create a new poll. - -![Create a poll](../../images/opl/create-poll.png) - -Confirm and sign a transaction to create a new poll (issues a request against -the Host contract). - -![Confirm new poll](../../images/opl/confirm-new-poll.png) - -Voting on a ballot issues a request to the *enclave* contract. - -![Vote on ballot](../../images/opl/vote-on-ballot.png) - -You should be able to see results from past polls. - -![See past proposals](../../images/opl/past-dao-proposals.png) - -If you were able to get to this point, congrats! You have created an OPL dApp! - -:::info Example - -You can try out and download a frontend of the secret ballot Dapp from the -[Oasis Playground repository][frontend-example]. - -::: - -[frontend-example]: https://github.com/oasisprotocol/demo-opl-secret-ballot/tree/main/frontend diff --git a/docs/dapp/opl/secret-ballot-example/host.md b/docs/dapp/opl/secret-ballot-example/host.md deleted file mode 100644 index 76623b8d26..0000000000 --- a/docs/dapp/opl/secret-ballot-example/host.md +++ /dev/null @@ -1,292 +0,0 @@ -# DAO Contract - -Let's start with a smart contract that describes a basic DAO, `DAOV1.sol`, with a -mapping of proposals before we consider the OPL differences. - -## Base Contract - -Inside your `contracts/` directory, create a `DAOV1.sol` file. You may have -already deployed a similar contract to your home network such as BNB or -Polygon. - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; - -type ProposalId is bytes32; - -struct ProposalParams { - string ipfsHash; - uint16 numChoices; - bool publishVotes; -} - -contract DAOV1 { - using EnumerableSet for EnumerableSet.Bytes32Set; - - error AlreadyExists(); - error NoChoices(); - error TooManyChoices(); - - event ProposalClosed(ProposalId id, uint256 topChoice); - - struct Proposal { - bool active; - uint16 topChoice; - ProposalParams params; - } - - struct ProposalWithId { - ProposalId id; - Proposal proposal; - } - - mapping(ProposalId => Proposal) public proposals; - EnumerableSet.Bytes32Set private activeProposals; - ProposalId[] private pastProposals; - - constructor() {} - - function createProposal(ProposalParams calldata _params) external payable returns (ProposalId) { - bytes32 proposalHash = keccak256(abi.encode(msg.sender, _params)); - ProposalId proposalId = ProposalId.wrap(proposalHash); - if (_params.numChoices == 0) revert NoChoices(); - if (_params.numChoices > type(uint16).max) revert TooManyChoices(); - if (proposals[proposalId].active) revert AlreadyExists(); - Proposal storage proposal = proposals[proposalId]; - proposal.params = _params; - proposal.active = true; - activeProposals.add(proposalHash); - return proposalId; - } - - function getActiveProposals( - uint256 _offset, - uint256 _count - ) external view returns (ProposalWithId[] memory _proposals) { - if (_offset + _count > activeProposals.length()) { - _count = activeProposals.length() - _offset; - } - _proposals = new ProposalWithId[](_count); - for (uint256 i; i < _count; ++i) { - ProposalId id = ProposalId.wrap(activeProposals.at(_offset + i)); - _proposals[i] = ProposalWithId({id: id, proposal: proposals[id]}); - } - } - - function getPastProposals( - uint256 _offset, - uint256 _count - ) external view returns (ProposalWithId[] memory _proposals) { - if (_offset + _count > pastProposals.length) { - _count = pastProposals.length - _offset; - } - _proposals = new ProposalWithId[](_count); - for (uint256 i; i < _count; ++i) { - ProposalId id = pastProposals[_offset + i]; - _proposals[i] = ProposalWithId({id: id, proposal: proposals[id]}); - } - } -} -``` - -Instead of storing complete ballot proposals on the network, we will use -[IPFS](https://ipfs.tech). Our smart contract will refer to a pinned -[IPFS file] by its [hash](https://docs.ipfs.tech/concepts/hashing/). - -```solidity -struct ProposalParams { - string ipfsHash; - uint16 numChoices; - bool publishVotes; -} -``` - -Our very simple DAO contract creates proposals and manages them, allowing -both active and past proposals to be queried externally through methods -`getActiveProposals` and `getPastProposals`. This would be sufficient on a -single chain, and it is possible to develop confidential applications without -bridges, relying solely on [Sapphire](../../sapphire/README.mdx). However, we will proceed -to demonstrate the cross-chain capabilities of OPL. - -## What is different with OPL? - -OPL leverages [Celer](https://celer.network)'s inter-chain [messaging] -capabilities in order to connect smart contracts deployed on a home network -such as Polygon, or BNB, with the privacy preserving smart contracts deployed -on Sapphire. - -You will not need to learn how Celer Inter-chain Message (IM) works in order to -use OPL, but if you would like to learn more, you can see that OPL realizes -the Celer IM interface through the abstraction of an [Endpoint]: - -```solidity -interface ICelerMessageBus { - function feeBase() external view returns (uint256); - - function feePerByte() external view returns (uint256); - - function sendMessage( - address _host, - uint256 _hostChainId, - bytes calldata _message - ) external payable; -} -``` - -which allows us to use this bridge [function]: - -```solidity -function sendMessage( - address _receiver, - uint64 _dstChainId, - bytes memory _message, - uint256 _fee -) internal -``` - -In production, you can see the deployed [cBridge contract] and -[MessageBus contract]. - -### How does OPL do this? - -We can [register] functions with endpoints in order to simplify our code. -Endpoints are effectively callbacks which listen to messages from the enclaved -smart contract. - -```solidity -function registerEndpoint( - bytes memory _method, - function(bytes calldata) returns (Result) _cb -) internal { - // This is a waste of an SLOAD, but the alternative before immutable arrays - // (https://github.com/ethereum/solidity/issues/12587) land is terribly verbose. - // This can be fixed once gas usage becomes a problem. - endpoints[bytes4(keccak256(_method))] = _cb; -} -``` - -Under the hood, a `postMessage` function [sends] the message using the Celer -Message Bus. If you would prefer using a different bridging partner, this -pattern will provide you a place to start that integration. - -```solidity - ICelerMessageBus(messageBus).sendMessage{value: fee}( - remote, - remoteChainId, - envelope - ); -``` - -### Endpoints? Why not Solidity events? - -Events in Solidity are non-confidential and do not allow cross-chain -communication. For this reason, OPL uses *endpoints* for passing messages -cross-chain. For example, this function below will listen to such a message and - close the proposal. - -```solidity - function _oplBallotClosed(bytes calldata _args) internal returns (Result) { - (ProposalId proposalId, uint16 topChoice) = abi.decode(_args, (ProposalId, uint16)); - proposals[proposalId].topChoice = topChoice; - proposals[proposalId].active = false; - activeProposals.remove(ProposalId.unwrap(proposalId)); - pastProposals.push(proposalId); - emit ProposalClosed(proposalId, topChoice); - return Result.Success; - } -``` - -### Let's see the code - -Let's see OPL at work. We can add our own implementation of event handling to -process cross-chain messages. Let's make the following changes to `DAOV1.sol`. - -```diff -diff --git a/backend/contracts/DAOV1.sol b/backend/contracts/DAOV1.sol -index 21ea93e..827d80a 100644 ---- a/backend/contracts/DAOV1.sol -+++ b/backend/contracts/DAOV1.sol -@@ -1,6 +1,7 @@ - // SPDX-License-Identifier: MIT - pragma solidity ^0.8.0; - -+import {Host, Result} from "@oasisprotocol/sapphire-contracts/contracts/OPL.sol"; - import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; - - type ProposalId is bytes32; -@@ -11,7 +12,7 @@ struct ProposalParams { - bool publishVotes; - } - --contract DAOV1 { -+contract DAOV1 is Host { - using EnumerableSet for EnumerableSet.Bytes32Set; - - error AlreadyExists(); -@@ -35,7 +36,9 @@ contract DAOV1 { - EnumerableSet.Bytes32Set private activeProposals; - ProposalId[] private pastProposals; - -- constructor() {} -+ constructor(address _ballotBox) Host(_ballotBox) { -+ registerEndpoint("ballotClosed", _oplBallotClosed); -+ } - - function createProposal(ProposalParams calldata _params) external payable returns (ProposalId) { - bytes32 proposalHash = keccak256(abi.encode(msg.sender, _params)); -@@ -47,6 +50,7 @@ contract DAOV1 { - proposal.params = _params; - proposal.active = true; - activeProposals.add(proposalHash); -+ postMessage("createBallot", abi.encode(proposalId, _params)); - return proposalId; - } - -@@ -77,4 +81,14 @@ contract DAOV1 { - _proposals[i] = ProposalWithId({id: id, proposal: proposals[id]}); - } - } -+ -+ function _oplBallotClosed(bytes calldata _args) internal returns (Result) { -+ (ProposalId proposalId, uint16 topChoice) = abi.decode(_args, (ProposalId, uint16)); -+ proposals[proposalId].topChoice = topChoice; -+ proposals[proposalId].active = false; -+ activeProposals.remove(ProposalId.unwrap(proposalId)); -+ pastProposals.push(proposalId); -+ emit ProposalClosed(proposalId, topChoice); -+ return Result.Success; -+ } - } -``` - -#### Host - -A _host_ contract in our terminology is just a smart contract that extends the -`Host` contract [provided] and deployed on a home network such as BNB or -Polygon with a reference to the Sapphire network where our _enclave_ -(privacy-preserving) smart contract will reside. - -#### Constructor - -We provide the address of the confidential (also known as *enclave*) smart -contract deployed on the Oasis Sapphire as a constructor parameter to the -*host* smart contract. - -```solidity - constructor(address _ballotBox) Host(_ballotBox) { - registerEndpoint("ballotClosed", _oplBallotClosed); - } -``` - -[messaging]: https://im-docs.celer.network/developer/celer-im-overview -[IPFS file]: https://docs.ipfs.tech/concepts/lifecycle/#_1-content-addressable-representation -[function]: https://im-docs.celer.network/developer/development-guide/contract-framework#send-message -[cBridge contract]: https://explorer.sapphire.oasis.io/address/0x9B36f165baB9ebe611d491180418d8De4b8f3a1f/transactions -[MessageBus contract]: https://explorer.sapphire.oasis.io/address/0x9Bb46D5100d2Db4608112026951c9C965b233f4D/transactions -[register]: https://github.com/oasisprotocol/sapphire-paratime/blob/9a74e57e72b06ba86ec8454062b8c0a5281edb97/contracts/contracts/opl/Endpoint.sol#L76-L84 -[sends]: https://github.com/oasisprotocol/sapphire-paratime/blob/9a74e57e72b06ba86ec8454062b8c0a5281edb97/contracts/contracts/opl/Endpoint.sol#L91-L119 -[provided]: https://github.com/oasisprotocol/sapphire-paratime/blob/main/contracts/contracts/opl/Host.sol -[Endpoint]: https://github.com/oasisprotocol/sapphire-paratime/blob/9a74e57e72b06ba86ec8454062b8c0a5281edb97/contracts/contracts/opl/Endpoint.sol#L35-L45 \ No newline at end of file diff --git a/docs/dapp/opl/secret-ballot-example/introduction.md b/docs/dapp/opl/secret-ballot-example/introduction.md deleted file mode 100644 index 6c1f005b4b..0000000000 --- a/docs/dapp/opl/secret-ballot-example/introduction.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -description: How to build your first dApp on OPL ---- - -# Overview - -On-chain voting is the basis for any decentralized autonomous organization -(DAO) that would like to foster bottom-up decision making. -In this tutorial, you will create a [secret ballot](https://en.wikipedia.org/wiki/Secret_ballot) -dApp that can only be built with the Oasis Privacy Layer. - -Why is this important? [Privacy](https://en.wikipedia.org/wiki/Secret_ballot) -protects the voter (DAO token holder) from intimidation and bullying when -exercising their right of participation, such as on a protocol. -Vote organizers can encourage participation with ballots not only by protecting -the identity of voters, but also by sealing the results of an ongoing -vote, giving the same weight to the first and last votes. - -## Getting Started - -If you have made a dApp before, then you likely know most if not all of -the tools covered here! But even if you haven't used all of these tools listed, -you should still keep going! We will do this together. - -By the end of this tutorial, we will have: - -- written smart contracts using the OPL [library](https://github.com/oasisprotocol/sapphire-paratime/blob/main/contracts/contracts/OPL.sol) -- used [Hardhat](https://hardhat.org/docs) development environment for OPL -- used [Hardhat Deploy](https://github.com/wighawag/hardhat-deploy) to deploy -smarts contracts to a testnet. -- used [Pinata](https://www.pinata.cloud) -to store simple JSON data. Not everything has to go on a blockchain. -- used [Celer](https://im-docs.celer.network/developer/celer-im-overview) to -pass messages cross multiple chains -- built a simple [Vue.JS](https://vuejs.org/guide/introduction.html) -app to interact with our dApp through [MetaMask](https://docs.metamask.io/wallet). diff --git a/docs/dapp/opl/secret-ballot-example/setup.md b/docs/dapp/opl/secret-ballot-example/setup.md deleted file mode 100644 index 4d75d88c99..0000000000 --- a/docs/dapp/opl/secret-ballot-example/setup.md +++ /dev/null @@ -1,64 +0,0 @@ -# Setup - -Let's get started and make our new project. You will need [Node.js](https://nodejs.org/en/download) -version [18](https://nodejs.org/en/blog/announcements/v18-release-announce). - -We will be using a monorepo for both the frontend and backend of our dApp. - -```shell -mkdir opl-secret-ballot; -mkdir opl-secret-ballot/backend; -mkdir opl-secret-ballot/frontend; -``` - -Our smart contracts will live inside a Hardhat project under -`opl-secret-ballot/backend`, and our VueJS app will be in the -`opl-secret-ballot/frontend`. - -## Workspace - -We suggest using [`pnpm`](https://pnpm.io/motivation), and creating a workspace -file `opl-secret-ballot/pnpm-workspace.yaml` with the following content: - -```yaml -packages: [frontend, backend] -``` - -## Hardhat - -Let's create a new Hardhat project. - -:::info - -Currently we are compatible with Hardhat up to `2.19`. You may need to -specify the version of Hardhat to install. - -::: - -```shell -cd opl-secret-ballot/backend && npx hardhat init -``` - -When initializing the Hardhat application, we would like to use the `backend` -directory as the project root. - -```shell -Hardhat project root: ยท /Users/oasis/opl-secret-ballot/backend -``` - -We would like to set `@oasisprotocol/secret-ballot-backend` as the package name -inside `package.json` at `version` of `1.0.0`. - -Finally, we need to install the following dependencies: - -- `@oasisprotocol/sapphire-contracts` contains the OPL Solidity smart contracts. -- `@oasisprotocol/sapphire-hardhat` integrates Sapphire using the Hardhat -config file. -- `@openzeppelin/contracts` contains standardized DAO contracts which we will -use to build the secret ballot application. - -```shell npm2yarn -npm install -D @openzeppelin/contracts @oasisprotocol/sapphire-contracts @oasisprotocol/sapphire-hardhat -``` - -You should be able to start your localhost Hardhat node. diff --git a/sidebarDapp.ts b/sidebarDapp.ts index e1ac747b41..8fe94ae281 100644 --- a/sidebarDapp.ts +++ b/sidebarDapp.ts @@ -54,21 +54,6 @@ export const sidebarDapp: SidebarsConfig = { }, items: [ 'dapp/opl/opl-sdk/ping-example', - { - type: 'category', - label: 'Secret Ballot Example', - link: { - type: 'doc', - id: 'dapp/opl/secret-ballot-example/README', - }, - items: [ - 'dapp/opl/secret-ballot-example/setup', - 'dapp/opl/secret-ballot-example/host', - 'dapp/opl/secret-ballot-example/enclave', - 'dapp/opl/secret-ballot-example/build', - 'dapp/opl/secret-ballot-example/frontend', - ], - } ], }, {