Преглед изворни кода

chore(dev-hub) Entropy improvements

Aditya Arora пре 2 месеци
родитељ
комит
c6d926d6bf

+ 10 - 5
apps/developer-hub/content/docs/entropy/best-practices.mdx

@@ -3,12 +3,14 @@ title: Best Practices
 description: Best practices for using Pyth Entropy in your applications
 ---
 
+import { DynamicCodeBlock } from "fumadocs-ui/components/dynamic-codeblock";
+
 ## Generating random values within a specific range
 
 You can map the random number provided by Entropy into a smaller range using the solidity [modulo operator](https://docs.soliditylang.org/en/latest/types.html#modulo).
 Here is a simple example of how to map a random number provided by Entropy into a range between `minRange` and `maxRange` (inclusive).
 
-```solidity
+<DynamicCodeBlock lang="solidity" code={`\
 // Maps a random number into a range between minRange and maxRange (inclusive)
 function mapRandomNumber(
     bytes32 randomNumber,
@@ -21,8 +23,9 @@ function mapRandomNumber(
     uint256 randomUint = uint256(randomNumber);
 
     return minRange + int256(randomUint % range);
+
 }
-```
+`} />
 
 Notice that using the modulo operator can distort the distribution of random numbers if it's not a power of 2. This is
 negligible for small and medium ranges, but it can be noticeable for large ranges.
@@ -35,7 +38,9 @@ If you need to generate multiple random values in a single transaction, you can
 
 In the following example, `mapRandomNumber` is used to generate 6 random attributes for a character.
 
-```solidity
+<DynamicCodeBlock
+  lang="solidity"
+  code={`\
 function generateAttributes(bytes32 randomNumber) internal {
   int256 strength = mapRandomNumber(
     keccak256(abi.encodePacked(randomNumber, "strength")),
@@ -68,5 +73,5 @@ function generateAttributes(bytes32 randomNumber) internal {
     100
   );
 }
-
-```
+`}
+/>

+ 316 - 59
apps/developer-hub/content/docs/entropy/create-your-first-entropy-app.mdx

@@ -1,9 +1,9 @@
 ---
 title: Create your first Entropy app on EVM
-description: Build a coin flip application using Pyth Entropy
+description: Build a coin flip example using Pyth Entropy
 ---
 
-# Create your first Entropy app on EVM
+import { DynamicCodeBlock } from "fumadocs-ui/components/dynamic-codeblock";
 
 In this tutorial we will implement and deploy a coin flip contract which will use entropy to generate a random output.
 
@@ -11,98 +11,355 @@ In this tutorial we will implement and deploy a coin flip contract which will us
 
 Before we start, please make sure you have the following tools installed:
 
-- [Foundry](https://book.getfoundry.sh/getting-started/installation)
-- [Node.js](https://nodejs.org/en/download) (version >= v18.0.0)
+- [Foundry](https://book.getfoundry.sh/getting-started/installation).
+- [Node](https://nodejs.org/en/download). Run `node -v{:js}` to confirm. You should get an output with version >= `v18.0.0`.
 
 ## Getting Started
 
-Create a directory named `coin-flip` and initialize a new Foundry project:
+Create a directory named `coin-flip{:bash}` in your filesystem.
+We will use this directory as the working directory for the rest of the tutorial.
+Let's initialize a new project in `coin-flip{:bash}` by running `forge init contracts{:bash}`.
 
-```bash
+This will create a new directory in `coin-flip{:bash}` named `contracts/src`, which will contain the smart contract code.
+
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
 mkdir coin-flip
 cd coin-flip
 forge init contracts
-```
+`}
+/>
 
-Install the Pyth Entropy SDK:
+Now we will install the Pyth Entropy SDK in the `contracts` directory.
 
-```bash
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
 cd contracts
 npm init -y
 npm install @pythnetwork/entropy-sdk-solidity
-```
+`}
+/>
 
-Add a `remappings.txt` file to the `contracts` directory:
+Add a `remappings.txt` file to `contracts` directory with the following content to tell Foundry where to find the Pyth Entropy SDK.
 
-```text
+<DynamicCodeBlock
+  lang="text"
+  code={`\
 @pythnetwork/entropy-sdk-solidity/=node_modules/@pythnetwork/entropy-sdk-solidity
-```
+`}
+/>
 
-## Smart Contract Implementation
+## Implementation
 
-Replace the content of `contracts/src/Counter.sol` with the following coin flip contract:
+Create a new file `CoinFlip.sol{:solidity}` in `contracts/src` directory and add the following code into it to start.
 
-```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
+<DynamicCodeBlock lang="solidity" code={`\
+// contracts/src/CoinFlip.sol
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.13;
 
-import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
+import "@pythnetwork/entropy-sdk-solidity/IEntropyV2.sol";
 import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";
 
 contract CoinFlip is IEntropyConsumer {
-    IEntropy entropy;
+event FlipRequested(uint64 sequenceNumber);
+event FlipResult(uint64 sequenceNumber, bool isHeads);
 
-    struct FlipRequest {
-        address player;
-        bool isHeads;
-    }
+IEntropyV2 entropy;
 
-    mapping(uint64 => FlipRequest) public requests;
-    mapping(address => uint256) public results; // 0 = not played, 1 = won, 2 = lost
+constructor(address \_entropy) {
+entropy = IEntropyV2(\_entropy);
+}
 
-    constructor(address entropyAddress) {
-        entropy = IEntropy(entropyAddress);
-    }
+// This method is required by the IEntropyConsumer interface
+function getEntropy() internal view override returns (address) {
+return address(entropy);
+}
+}
+
+`} />
+
+The code implements a`CoinFlip` contract which inherits the `IEntropyConsumer` interface.
+We have also defined some events, properties and a constructor to instantiate the contract.
+One of the properties is of type `IEntropyV2` which is an interface imported from the Entropy SDK.
+
+### Request a coin flip
+
+Copy the following code into `CoinFlip.sol{:solidity}`.
+
+<DynamicCodeBlock lang="solidity" code={`\
+contract CoinFlip {
+  // ... prior code omitted
+
+function request() external payable {
+// get the required fee
+uint128 requestFee = entropy.getFeeV2();
+// check if the user has sent enough fees
+if (msg.value < requestFee) revert("not enough fees");
+
+    // pay the fees and request a random number from entropy
+    uint64 sequenceNumber = entropy.requestV2{ value: requestFee }();
+
+    // emit event
+    emit FlipRequested(sequenceNumber);
+
+}
+}
+
+`} />
+
+Users will invoke the `request` method to initiate a coin flip, paying a fee in the process.
+The method first retrieves the fee required to request a random number from Entropy.
+It then includes the fee in the `requestV2{:bash}` method call to Entropy.
+Finally, the method emits a `FlipRequested{:bash}` event with a `sequenceNumber`. This event is also defined in the code snippet above.
+
+### Handle the callback
+
+Copy the following code into `CoinFlip.sol{:solidity}`.
+
+<DynamicCodeBlock lang="solidity" code={`\
+contract CoinFlip {
+  // ... prior code omitted
+
+function entropyCallback(
+uint64 sequenceNumber,
+// If your app uses multiple providers, you can use this argument
+// to distinguish which one is calling the app back. This app only
+// uses one provider so this argument is not used.
+address \_providerAddress,
+bytes32 randomNumber
+) internal override {
+bool isHeads = uint256(randomNumber) % 2 == 0;
+
+    emit FlipResult(sequenceNumber, isHeads);
+
+}
+}
+
+`} />
+
+Implement `entropyCallback` method which is required by the `IEntropyConsumer` Interface. Entropy calls back this method to fulfill a request. Entropy will call back this
+method with the `sequenceNumber` of the request, the `_providerAddress` from which the random number was requested and the generated `randomNumber`.
+Finally, the method emits a `FlipResult` event with the result of the flip.
+
+Yay! you have successfully implemented a coin flip contract.
+
+## Deploy
+
+First, create a new wallet
+
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
+cast wallet new
+`}
+/>
+
+This command will generate a new Ethereum keypair, producing output similar to the following. Note that the address and private key will be different hexadecimal values.
+
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
+Successfully created new keypair.
+Address:     0xB806824fdA4b2b6631e9B87a86d42C9dfd04D129
+Private key: 0x0d510c72fd2279155c717eb433ae598a83cfb34b09c2ada86bc424b481082023
+  `}
+/>
+
+We will export the values from the command above as environment variables to simplify the commands below. We will also export the RPC URL of the network. Run the following commands in your shell substituting the address and private key in the indicated places:
+
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
+export ADDRESS=<address from above>
+export PRIVATE_KEY=<your private key from above>
+export RPC_URL="https://sepolia.optimism.io"
+`}
+/>
+
+Next, use the [Superchain Faucet](https://app.optimism.io/faucet?utm_source=docs) to claim some test Sepolia ETH. Paste the address from the command above into the faucet to get your ETH. You can verify that the ETH has arrived in your wallet by running the command
 
-    function flipCoin(bool isHeads) external payable {
-        uint256 fee = entropy.getFee(entropy.getDefaultProvider());
-        require(msg.value >= fee, "Insufficient fee");
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
+cast balance $ADDRESS -r $RPC_URL -e
+`}
+/>
 
-        bytes32 userRandomNumber = keccak256(abi.encode(block.timestamp, msg.sender));
-        uint64 sequenceNumber = entropy.requestWithCallback{value: fee}(
-            entropy.getDefaultProvider(),
-            userRandomNumber
-        );
+The final step before deploying is to get the arguments for the contract's constructor: the [Entropy contract address](https://docs.pyth.network/entropy/contract-addresses) for Optimism Sepolia and [the Provider address](https://docs.pyth.network/entropy/contract-addresses). We will also export these values as environment variables for convenience:
 
-        requests[sequenceNumber] = FlipRequest(msg.sender, isHeads);
+```bash copy
+export ENTROPY_ADDRESS=0x4821932D0CDd71225A6d914706A621e0389D7061
+```
+
+Finally, let's deploy the contracts. Run the following command:
+
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
+forge create src/CoinFlip.sol:CoinFlip \
+--private-key $PRIVATE_KEY \
+--rpc-url $RPC_URL \
+--constructor-args $ENTROPY_ADDRESS
+  `}
+/>
+
+You should see an output similar to:
+
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
+[⠢] Compiling...
+[⠔] Compiling 28 files with 0.8.23
+[⠑] Solc 0.8.23 finished in 3.40s
+Compiler run successful!
+Deployer: 0xfa57d0f2CBDA2729273F2a431E4FeDAc656d0402
+Deployed to: 0x8676ba0Dd492AB9813BC21D5Dce318427d1d73ae
+Transaction hash: 0x2178aa6d402c94166a93e81822248d00dd003827675ebd49b3c542970f5a0189
+`}
+/>
+
+Let's export the coin flip contract address as environment variable for later use:
+
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
+export COINFLIP_ADDRESS=<Deployed to address from above>
+`}
+/>
+
+Congratulations you have successfully implemented and deployed a CoinFlip contract.
+
+## Interact from Javascript
+
+Next, let’s interact with the CoinFlip contract from Javascript. Create a new directory inside `coin-flip` named `app`. Run `cd app` to make it your terminal’s working directory — the following commands will need to be run from here.
+
+Run the following to initialise a new project and install required libraries.
+
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
+npm init -y
+npm install web3 @pythnetwork/entropy-sdk-solidity
+`}
+/>
+
+Create a `script.js` file in `app` and add the following code to the script.
+
+<DynamicCodeBlock lang="javascript" code={`\
+const { Web3 } = require("web3");
+const CoinFlipAbi = require("../contracts/out/CoinFlip.sol/CoinFlip.json");
+const EntropyAbi = require("@pythnetwork/entropy-sdk-solidity/abis/IEntropyV2.json");
+
+async function main() {
+const web3 = new Web3(process.env["RPC_URL"]);
+const { address } = web3.eth.accounts.wallet.add(
+process.env["PRIVATE_KEY"]
+)[0];
+
+web3.eth.defaultBlock = "finalized";
+
+const coinFlipContract = new web3.eth.Contract(
+CoinFlipAbi.abi,
+process.env["COINFLIP_ADDRESS"]
+);
+
+const entropyContract = new web3.eth.Contract(
+EntropyAbi,
+process.env["ENTROPY_ADDRESS"]
+);
+}
+
+main();
+`} />
+
+The code above imports the required libraries and defines a `main` method. In `main` we initialize web3 contracts that help us interact with the coin flip and entropy contracts. At the end, the script calls the main method.
+
+Next, add the following code to the main method to request a flip from the CoinFlip contract.
+
+<DynamicCodeBlock lang="javascript" code={`\
+async main() {
+  // ... prior code omitted
+
+// Request a random number
+
+const fee = await entropyContract.methods.getFeeV2().call()
+console.log(\`fee: \${fee}\`);
+
+const requestReceipt = await coinFlipContract.methods
+.request()
+.send({
+value: fee,
+from: address,
+});
+
+console.log(\`request tx: \${requestReceipt.transactionHash}\`);
+// Read the sequence number for the request from the transaction events.
+const sequenceNumber =
+requestReceipt.events.FlipRequested.returnValues.sequenceNumber;
+console.log(\`sequence: \${sequenceNumber}\`);
+}
+`} />
+
+The code snippet above generates a random number. The code calls the Entropy contract to get the fee required for requesting a random number. Then it calls the request method of the CoinFlip contract with the `userRandomNumber` as an argument and the required fee. Finally, the code reads the sequenceNumber from the `FlipRequested` event emitted by the CoinFlip contract.
+
+Finally, add the following code snippet to get the flip result.
+
+<DynamicCodeBlock lang="javascript" code={`\
+async main() {
+  // ... prior code omitted
+
+let fromBlock = requestReceipt.blockNumber;
+const intervalId = setInterval(async () => {
+const currentBlock = await web3.eth.getBlockNumber();
+
+    if(fromBlock > currentBlock) {
+      return;
     }
 
-    function entropyCallback(
-        uint64 sequenceNumber,
-        address,
-        bytes32 randomNumber
-    ) internal override {
-        FlipRequest memory request = requests[sequenceNumber];
-        bool coinIsHeads = uint256(randomNumber) % 2 == 0;
-
-        if (coinIsHeads == request.isHeads) {
-            results[request.player] = 1; // won
-        } else {
-            results[request.player] = 2; // lost
-        }
+    // Get 'FlipResult' events emitted by the CoinFlip contract for given block range.
+    const events = await coinFlipContract.getPastEvents("FlipResult", {
+      fromBlock: fromBlock,
+      toBlock: currentBlock,
+    });
+    fromBlock = currentBlock + 1n;
+
+    // Find the event with the same sequence number as the request.
+    const event = events.find(event => event.returnValues.sequenceNumber === sequenceNumber);
+
+    // If the event is found, log the result and stop polling.
+    if(event !== undefined) {
+      console.log(\`result: \${event.returnValues.isHeads ? 'Heads' : 'Tails'}\`);
+      clearInterval(intervalId);
     }
+
+}, 1000);
 }
-```
+`} />
+
+The code above polls for new `FlipResult` events emitted by the CoinFlip contract. It checks if the event has the same `sequenceNumber` as the request. If it does, it logs the result and stops polling.
 
-## Deployment
+That's it, Let's run the script with the command `node script.js` . You should get an output similar to:
 
-Create a deployment script and deploy to Optimism Sepolia testnet. Check the [contract addresses](contract-addresses) page for the Entropy contract address on your chosen network.
+<DynamicCodeBlock
+  lang="bash"
+  code={`\
+fee         : 101
+request tx  : 0xde0dce36a3c149b189aba8b29cad98375a62a811e65efdae28b28524da59cfb6
+sequence    : 42
+result      : Tails
+`}
+/>
+
+Note that: the script can fail due to transient RPC issues. You can run the script again to get the expected result.
 
 ## Next Steps
 
-- Add more complex game logic
-- Implement proper error handling
-- Add events for better tracking
-- Consider gas optimization techniques
+Congratulations! You've built your first app using Entropy. In this tutorial, we created a Solidity contract that generates a random flip using Entropy. We deployed the contract and interacted with it from Javascript.
+
+You can learn more about Entropy from the following links:
 
-For a complete example with deployment scripts and frontend integration, see the [Coin Flip example](https://github.com/pyth-network/pyth-examples/tree/main/entropy/coin_flip) in the Pyth examples repository.
+- [Protocol Design](https://docs.pyth.network/entropy/protocol-design)
+- [Best Practices](https://docs.pyth.network/entropy/best-practices)

+ 37 - 5
apps/developer-hub/content/docs/entropy/index.mdx

@@ -5,20 +5,26 @@ icon: DiceSix
 full: true
 ---
 
+import {
+  RocketLaunch,
+  FileText,
+  DiceSix,
+} from "@phosphor-icons/react/dist/ssr";
+
 **Pyth Entropy** is an on-chain random number generator (RNG) designed for developers who need fair, unbiased, and cryptographically secure randomness.
 Whether you're building a blockchain game, NFT mint, lottery, or simulation, Entropy delivers randomness that is:
 
-- **Trustless & verifiable** - built on commit-reveal(TODO: link to commit-reveal).
-- **Low-latency** - randomness available within a few blocks(TODO: link to latency).
-- **Easy to integrate** - Permissionless Integration, Visual Tx Explorer(TODO: link to explorer).
-- **Cost-efficient** - designed for scalable production use(TODO: link to fees).
+- **Trustless & verifiable** - built on [commit-reveal protocol](./entropy/protocol-design).
+- **Low-latency** - randomness available within a [few blocks](./entropy/contract-addresses).
+- **Easy to integrate** - Permissionless Integration, including [Visual Tx Explorer](https://entropy-explorer.pyth.network/).
+- **Cost-efficient** - designed for scalable production use [fees](./entropy/contract-addresses).
 - **Native gas fees** - pay with chain native token.
 
 ## What's New in Entropy v2
 
 Entropy v2 introduces several improvements and new features to make random number generation more flexible and efficient.
 See [What's New in Entropy v2](whats-new-entropyv2) for more details.
-(TODO: This can be displayed in a banner above) (TODO: Add aan infographic here)
+(TODO: Add an infographic here)
 
 ## Getting Started
 
@@ -31,3 +37,29 @@ Please see [How to Generate Random Numbers Using Pyth Entropy](generate-random-n
 - [Contract Addresses/Supported Networks](contract-addresses)
 - [Error Codes](error-codes)
 - [Entropy Debugger](https://entropy-debugger.pyth.network/) - Interactive tool for diagnosing callback issues
+
+## Start Building
+
+<Cards className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
+  <Card
+    href="/entropy/create-your-first-entropy-app"
+    title="Build Your First App"
+    icon={<RocketLaunch size={16} fill="unset" weight="duotone" />}
+  >
+    Step-by-step tutorial to deploy a coin flip using Entropy v2.
+  </Card>
+  <Card
+    href="/entropy/generate-random-numbers-evm"
+    title="Generate Random Numbers"
+    icon={<DiceSix size={16} fill="unset" />}
+  >
+    How-to guide for reading fees and requesting randomness on EVM.
+  </Card>
+  <Card
+    href="/entropy/contract-addresses"
+    title="Contracts & Providers"
+    icon={<FileText size={16} fill="unset" />}
+  >
+    Find Entropy addresses, reveal delays, gas limits, and fees.
+  </Card>
+</Cards>

+ 3 - 2
apps/developer-hub/content/docs/entropy/meta.json

@@ -7,6 +7,8 @@
     "---Introduction---",
     "index",
     "whats-new-entropyv2",
+    "---Tutorials---",
+    "create-your-first-entropy-app",
     "---How-To Guides---",
     "generate-random-numbers-evm",
     "set-custom-gas-limits",
@@ -21,7 +23,6 @@
     "[Fortuna API Reference](https://fortuna.dourolabs.app/docs/)",
     "---Understanding Entropy---",
     "protocol-design",
-    "fees",
-    "create-your-first-entropy-app"
+    "fees"
   ]
 }

+ 2 - 1
apps/developer-hub/source.config.ts

@@ -1,3 +1,4 @@
+import { rehypeCode } from "fumadocs-core/mdx-plugins";
 import { defineConfig, defineDocs } from "fumadocs-mdx/config";
 import rehypeKatex from "rehype-katex";
 import remarkMath from "remark-math";
@@ -27,6 +28,6 @@ export const docs = defineDocs({
 export default defineConfig({
   mdxOptions: {
     remarkPlugins: [remarkMath],
-    rehypePlugins: (v) => [rehypeKatex, ...v],
+    rehypePlugins: (v) => [rehypeKatex, rehypeCode, ...v],
   },
 });

+ 13 - 0
apps/developer-hub/src/components/Root/theme.css

@@ -334,3 +334,16 @@
   );
   --color-fd-ring: light-dark(var(--color-violet-600), var(--color-violet-400));
 }
+
+/* Global typography: tighten line spacing in docs content */
+:where(.prose) {
+  line-height: 1.5;
+}
+
+:where(.prose) :where(p, li) {
+  line-height: 1.5;
+}
+
+:where(.prose) :where(h1, h2, h3, h4, h5, h6) {
+  line-height: 1.2;
+}