Skip to content

Commit f88fe2b

Browse files
authored
added coin_flip (#7)
1 parent 67ea4ea commit f88fe2b

File tree

12 files changed

+405
-0
lines changed

12 files changed

+405
-0
lines changed

entropy/coin_flip/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Coin Flip Example application
2+
3+
The coin flip example demonstrates how to use Pyth Entropy to flip a fair coin.
4+
5+
## Try it out
6+
7+
To try the example, first run the following commands from the root of the `pyth-crosschain` repository:
8+
9+
```shell
10+
npm install
11+
npx lerna run build
12+
```
13+
14+
These commands will build dependencies for the typescript project.
15+
16+
Next, choose a network to run the example on.
17+
The example has been deployed on the following networks:
18+
19+
```
20+
| Chain Name | Address | RPC |
21+
|------------------|--------------------------------------------|--------------------------------------------|
22+
| optimism-sepolia | 0x2eE67fF5d8548fF544f2c178a0FcAFe503A634Be | https://sepolia.optimism.io/ |
23+
| arbitrum-sepolia | 0xCd76c50c3210C5AaA9c39D53A4f95BFd8b1a3a19 | https://sepolia-rollup.arbitrum.io/rpc |
24+
```
25+
26+
You will also need the private key of a wallet with some gas tokens for your chosen network.
27+
Then, from the `coin_flip/app` directory, run the following command:
28+
29+
```
30+
npm run flip-coin -- \
31+
--private-key <hexadecimal evm private key> \
32+
--address <address> \
33+
--rpc-url <rpc url>
34+
```
35+
36+
You can populate the arguments to this command from the table above.
37+
The command should print output like this:
38+
39+
```text
40+
Running coin flip prototcol.
41+
1. Generating user's random number...
42+
number : 0x7c94c33d424e0a683deb15b55cc7d40d5cc8154478c76c971b677c35e32cb2f4
43+
2. Requesting coin flip...
44+
fee : 101 wei
45+
tx : 0x23e8e1c800d2e9c55d7e8bf1b2bd5e835979c1aa076f56ab4a74828a45684d9b
46+
sequence : 37
47+
3. Waiting for result...
48+
result : Tails
49+
```
50+
51+
## Understanding the Example
52+
53+
The example consists of a Solidity contract and a Typescript script.
54+
See the extensive code comments in the contract at `contract/src/CoinFlip.sol` to learn how the example works.
55+
The typescript script is available at `app/src/flip_coin.ts` and demonstrates how to interact with the contract.

entropy/coin_flip/app/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
lib
3+
.dccache
4+
*mnemonic*

entropy/coin_flip/app/package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "@pythnetwork/eth-coin-flip-example",
3+
"version": "0.1.0",
4+
"private": true,
5+
"dependencies": {
6+
"@pythnetwork/pyth-evm-js": "*",
7+
"@pythnetwork/pyth-sdk-solidity": "*",
8+
"@types/jest": "^27.5.2",
9+
"@types/node": "^16.11.64",
10+
"buffer": "^6.0.3",
11+
"ethers": "^5.7.2",
12+
"prettier": "^2.7.1",
13+
"typescript": "^4.8.4"
14+
},
15+
"devDependencies": {
16+
"ts-node": "^10.9.1"
17+
},
18+
"scripts": {
19+
"build": "tsc",
20+
"flip-coin": "npm run build && node lib/flip_coin.js",
21+
"format": "prettier --write src/"
22+
}
23+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
[
2+
{
3+
"type": "constructor",
4+
"inputs": [
5+
{ "name": "_entropy", "type": "address", "internalType": "address" },
6+
{
7+
"name": "_entropyProvider",
8+
"type": "address",
9+
"internalType": "address"
10+
}
11+
],
12+
"stateMutability": "nonpayable"
13+
},
14+
{ "type": "receive", "stateMutability": "payable" },
15+
{
16+
"type": "function",
17+
"name": "_entropyCallback",
18+
"inputs": [
19+
{ "name": "sequence", "type": "uint64", "internalType": "uint64" },
20+
{ "name": "provider", "type": "address", "internalType": "address" },
21+
{ "name": "randomNumber", "type": "bytes32", "internalType": "bytes32" }
22+
],
23+
"outputs": [],
24+
"stateMutability": "nonpayable"
25+
},
26+
{
27+
"type": "function",
28+
"name": "getFlipFee",
29+
"inputs": [],
30+
"outputs": [
31+
{ "name": "fee", "type": "uint256", "internalType": "uint256" }
32+
],
33+
"stateMutability": "view"
34+
},
35+
{
36+
"type": "function",
37+
"name": "requestFlip",
38+
"inputs": [
39+
{
40+
"name": "userRandomNumber",
41+
"type": "bytes32",
42+
"internalType": "bytes32"
43+
}
44+
],
45+
"outputs": [],
46+
"stateMutability": "payable"
47+
},
48+
{
49+
"type": "event",
50+
"name": "FlipRequest",
51+
"inputs": [
52+
{
53+
"name": "sequenceNumber",
54+
"type": "uint64",
55+
"indexed": false,
56+
"internalType": "uint64"
57+
}
58+
],
59+
"anonymous": false
60+
},
61+
{
62+
"type": "event",
63+
"name": "FlipResult",
64+
"inputs": [
65+
{
66+
"name": "sequenceNumber",
67+
"type": "uint64",
68+
"indexed": false,
69+
"internalType": "uint64"
70+
},
71+
{
72+
"name": "isHeads",
73+
"type": "bool",
74+
"indexed": false,
75+
"internalType": "bool"
76+
}
77+
],
78+
"anonymous": false
79+
},
80+
{ "type": "error", "name": "InsufficientFee", "inputs": [] }
81+
]
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import Web3 from "web3";
2+
import yargs from "yargs";
3+
import { hideBin } from "yargs/helpers";
4+
import HDWalletProvider from "@truffle/hdwallet-provider";
5+
import CoinFlipAbi from "./CoinFlipAbi.json";
6+
7+
const parser = yargs(hideBin(process.argv))
8+
.option("private-key", {
9+
description: "Private key (as a hexadecimal string) of the sender",
10+
type: "string",
11+
required: true,
12+
})
13+
.option("address", {
14+
description: "The address of the CoinFlip contract",
15+
type: "string",
16+
required: true,
17+
})
18+
.option("rpc-url", {
19+
description:
20+
"The URL of an ETH RPC service for reading/writing to the blockchain",
21+
type: "string",
22+
required: true,
23+
})
24+
.help()
25+
.alias("help", "h")
26+
.parserConfiguration({
27+
"parse-numbers": false,
28+
});
29+
30+
async function main() {
31+
const argv = await parser.argv;
32+
33+
const coinFlipContractAddress = argv.address;
34+
const rpc = argv.rpcUrl;
35+
const privateKey = argv.privateKey;
36+
37+
const provider = new HDWalletProvider({
38+
privateKeys: [privateKey],
39+
providerOrUrl: rpc,
40+
});
41+
42+
const web3 = new Web3(provider as any);
43+
44+
const coinFlipContract = new web3.eth.Contract(
45+
CoinFlipAbi as any,
46+
coinFlipContractAddress
47+
);
48+
49+
console.log(`Running coin flip prototcol.`);
50+
51+
console.log("1. Generating user's random number...");
52+
const randomNumber = web3.utils.randomHex(32);
53+
console.log(` number : ${randomNumber}`);
54+
55+
console.log("2. Requesting coin flip...");
56+
const flipFee = await coinFlipContract.methods.getFlipFee().call();
57+
console.log(` fee : ${flipFee} wei`);
58+
59+
const receipt = await coinFlipContract.methods
60+
.requestFlip(randomNumber)
61+
.send({ value: flipFee, from: provider.getAddress(0) });
62+
63+
console.log(` tx : ${receipt.transactionHash}`);
64+
const sequenceNumber = receipt.events.FlipRequest.returnValues.sequenceNumber;
65+
console.log(` sequence : ${sequenceNumber}`);
66+
67+
console.log("3. Waiting for result...");
68+
// Poll for new FlipResult events emitted by the CoinFlip contract. It checks if the event
69+
// has the same sequenceNumber as the request. If it does,
70+
// it logs the result and stops polling.
71+
let fromBlock = receipt.blockNumber;
72+
const intervalId = setInterval(async () => {
73+
const currentBlock = await web3.eth.getBlockNumber();
74+
75+
if (fromBlock > currentBlock) {
76+
return;
77+
}
78+
79+
// Get 'FlipResult' events emitted by the CoinFlip contract for given block range.
80+
const events = await coinFlipContract.getPastEvents("FlipResult", {
81+
fromBlock: fromBlock,
82+
toBlock: currentBlock,
83+
});
84+
fromBlock = currentBlock + 1;
85+
86+
// Find the event with the same sequence number as the request.
87+
const event = events.find(
88+
(event) => event.returnValues.sequenceNumber === sequenceNumber
89+
);
90+
91+
// If the event is found, log the result and stop polling.
92+
if (event !== undefined) {
93+
console.log(
94+
` result : ${event.returnValues.isHeads ? "Heads" : "Tails"}`
95+
);
96+
clearInterval(intervalId);
97+
}
98+
}, 1000);
99+
100+
provider.engine.stop();
101+
}
102+
103+
main();

entropy/coin_flip/app/tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"extends": "../../../../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"target": "esnext",
5+
"module": "commonjs",
6+
"declaration": true,
7+
"rootDir": "src/",
8+
"outDir": "./lib",
9+
"strict": true,
10+
"esModuleInterop": true,
11+
"resolveJsonModule": true
12+
},
13+
"include": ["src", "src/*.json"],
14+
"exclude": ["node_modules"]
15+
}

entropy/coin_flip/contract/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
lib/*
2+
!lib/README.md
3+
cache
4+
out
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[profile.default]
2+
solc = '0.8.4'
3+
src = 'src'
4+
out = 'out'
5+
libs = ['lib', '../../../entropy_sdk/solidity']
6+
7+
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Forge installs the dependencies in this folder. They are .gitignored
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ds-test/=lib/forge-std/lib/ds-test/src/
2+
forge-std/=lib/forge-std/src/
3+
entropy-sdk-solidity/=../../../entropy_sdk/solidity/

0 commit comments

Comments
 (0)