Skip to content

Commit 02ad78b

Browse files
author
Dev Kalra
authored
Update coinflip contracts to use entropy v2 (#1461)
* update coinflip contracts to use entropy v2 * correct formatting
1 parent 8d92ad9 commit 02ad78b

File tree

5 files changed

+134
-223
lines changed

5 files changed

+134
-223
lines changed

target_chains/ethereum/examples/coin_flip/README.md

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,10 @@ Next, choose a network to run the example on.
1717
The example has been deployed on the following networks:
1818

1919
```
20-
| Chain Name | Address | RPC |
21-
|-----------------|--------------------------------------------|--------------------------------------------|
22-
| optimism-goerli | 0x3bA217Cd7840Cc5B34FD5B7263Cebd8CD8665788 | https://goerli.optimism.io |
23-
| avalanche-fuji | 0xE7E52C85907d59C45b2C56EF32B78F514F8c547a | https://api.avax-test.network/ext/bc/C/rpc |
24-
| eos-evm-testnet | 0x413405Aee2db95cb028B60CBAd87FC0B932947f4 | https://api.testnet.evm.eosnetwork.com/ |
20+
| Chain Name | Address | RPC |
21+
|------------------|--------------------------------------------|--------------------------------------------|
22+
| optimism-sepolia | 0x2eE67fF5d8548fF544f2c178a0FcAFe503A634Be | https://sepolia.optimism.io/ |
23+
| arbitrum-sepolia | 0xCd76c50c3210C5AaA9c39D53A4f95BFd8b1a3a19 | https://sepolia-rollup.arbitrum.io/rpc |
2524
```
2625

2726
You will also need the private key of a wallet with some gas tokens for your chosen network.
@@ -30,7 +29,6 @@ Then, from the `coin_flip/app` directory, run the following command:
3029
```
3130
npm run flip-coin -- \
3231
--private-key <hexadecimal evm private key> \
33-
--chain-name <chain name> \
3432
--address <address> \
3533
--rpc-url <rpc url>
3634
```
@@ -41,18 +39,13 @@ The command should print output like this:
4139
```text
4240
Running coin flip prototcol.
4341
1. Generating user's random number...
44-
number : 0x79b029406af43b11937bca98c49633f9382ed7d3fc0d60e110258c5c8f0d1a05
45-
commitment: 0xd4bca63083f9fb9e83e68348cb48f45babd820fc3559c60ba9a67b0ab3845cea
42+
number : 0x7c94c33d424e0a683deb15b55cc7d40d5cc8154478c76c971b677c35e32cb2f4
4643
2. Requesting coin flip...
47-
fee : 87 wei
48-
tx : 0x3a59bb8c1aaa8c6ff97147bb3197e9b89c0d87174b0b6c32374fc62de6d8db94
49-
sequence : 50
50-
3. Retrieving provider's random number...
51-
fetch url : https://fortuna-staging.dourolabs.app/v1/chains/optimism-goerli/revelations/50
52-
number : 0x760e53a19a4677ef671fde63db59462dfb3e09e94418e9962e2fa764026b8400
53-
4. Revealing the result of the coin flip...
54-
tx : 0x0549b93b12684187f73ddcaf8351ca4049867882c1b138989e15363a4d103220
55-
result : tails
44+
fee : 101 wei
45+
tx : 0x23e8e1c800d2e9c55d7e8bf1b2bd5e835979c1aa076f56ab4a74828a45684d9b
46+
sequence : 37
47+
3. Waiting for result...
48+
result : Tails
5649
```
5750

5851
## Understanding the Example
Lines changed: 45 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,81 @@
11
[
22
{
3+
"type": "constructor",
34
"inputs": [
5+
{ "name": "_entropy", "type": "address", "internalType": "address" },
46
{
5-
"internalType": "address",
6-
"name": "_entropy",
7-
"type": "address"
8-
},
9-
{
10-
"internalType": "address",
117
"name": "_entropyProvider",
12-
"type": "address"
8+
"type": "address",
9+
"internalType": "address"
1310
}
1411
],
15-
"stateMutability": "nonpayable",
16-
"type": "constructor"
17-
},
18-
{
19-
"inputs": [],
20-
"name": "IncorrectSender",
21-
"type": "error"
12+
"stateMutability": "nonpayable"
2213
},
14+
{ "type": "receive", "stateMutability": "payable" },
2315
{
24-
"inputs": [],
25-
"name": "InsufficientFee",
26-
"type": "error"
27-
},
28-
{
29-
"anonymous": false,
16+
"type": "function",
17+
"name": "_entropyCallback",
3018
"inputs": [
31-
{
32-
"indexed": false,
33-
"internalType": "uint64",
34-
"name": "sequenceNumber",
35-
"type": "uint64"
36-
}
19+
{ "name": "sequence", "type": "uint64", "internalType": "uint64" },
20+
{ "name": "provider", "type": "address", "internalType": "address" },
21+
{ "name": "randomNumber", "type": "bytes32", "internalType": "bytes32" }
3722
],
38-
"name": "FlipRequest",
39-
"type": "event"
23+
"outputs": [],
24+
"stateMutability": "nonpayable"
4025
},
4126
{
42-
"anonymous": false,
43-
"inputs": [
44-
{
45-
"indexed": false,
46-
"internalType": "bool",
47-
"name": "isHeads",
48-
"type": "bool"
49-
}
27+
"type": "function",
28+
"name": "getFlipFee",
29+
"inputs": [],
30+
"outputs": [
31+
{ "name": "fee", "type": "uint256", "internalType": "uint256" }
5032
],
51-
"name": "FlipResult",
52-
"type": "event"
33+
"stateMutability": "view"
5334
},
5435
{
55-
"inputs": [],
56-
"name": "getFlipFee",
57-
"outputs": [
36+
"type": "function",
37+
"name": "requestFlip",
38+
"inputs": [
5839
{
59-
"internalType": "uint256",
60-
"name": "fee",
61-
"type": "uint256"
40+
"name": "userRandomNumber",
41+
"type": "bytes32",
42+
"internalType": "bytes32"
6243
}
6344
],
64-
"stateMutability": "nonpayable",
65-
"type": "function"
45+
"outputs": [],
46+
"stateMutability": "payable"
6647
},
6748
{
49+
"type": "event",
50+
"name": "FlipRequest",
6851
"inputs": [
6952
{
70-
"internalType": "bytes32",
71-
"name": "userCommitment",
72-
"type": "bytes32"
53+
"name": "sequenceNumber",
54+
"type": "uint64",
55+
"indexed": false,
56+
"internalType": "uint64"
7357
}
7458
],
75-
"name": "requestFlip",
76-
"outputs": [],
77-
"stateMutability": "payable",
78-
"type": "function"
59+
"anonymous": false
7960
},
8061
{
62+
"type": "event",
63+
"name": "FlipResult",
8164
"inputs": [
8265
{
83-
"internalType": "uint64",
8466
"name": "sequenceNumber",
85-
"type": "uint64"
86-
},
87-
{
88-
"internalType": "bytes32",
89-
"name": "userRandom",
90-
"type": "bytes32"
67+
"type": "uint64",
68+
"indexed": false,
69+
"internalType": "uint64"
9170
},
9271
{
93-
"internalType": "bytes32",
94-
"name": "providerRandom",
95-
"type": "bytes32"
72+
"name": "isHeads",
73+
"type": "bool",
74+
"indexed": false,
75+
"internalType": "bool"
9676
}
9777
],
98-
"name": "revealFlip",
99-
"outputs": [],
100-
"stateMutability": "nonpayable",
101-
"type": "function"
78+
"anonymous": false
10279
},
103-
{
104-
"stateMutability": "payable",
105-
"type": "receive"
106-
}
80+
{ "type": "error", "name": "InsufficientFee", "inputs": [] }
10781
]

target_chains/ethereum/examples/coin_flip/app/src/flip_coin.ts

Lines changed: 41 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,13 @@ import yargs from "yargs";
33
import { hideBin } from "yargs/helpers";
44
import HDWalletProvider from "@truffle/hdwallet-provider";
55
import CoinFlipAbi from "./CoinFlipAbi.json";
6-
import axios from "axios";
76

8-
const argv = yargs(hideBin(process.argv))
7+
const parser = yargs(hideBin(process.argv))
98
.option("private-key", {
109
description: "Private key (as a hexadecimal string) of the sender",
1110
type: "string",
1211
required: true,
1312
})
14-
.option("fortuna-url", {
15-
description: "URL of the fortuna server for your chosen provider",
16-
type: "string",
17-
default: "https://fortuna-staging.dourolabs.app",
18-
})
19-
.option("chain-name", {
20-
description:
21-
"The name of your blockchain (for accessing data from fortuna)",
22-
type: "string",
23-
required: true,
24-
})
2513
.option("address", {
2614
description: "The address of the CoinFlip contract",
2715
type: "string",
@@ -37,37 +25,15 @@ const argv = yargs(hideBin(process.argv))
3725
.alias("help", "h")
3826
.parserConfiguration({
3927
"parse-numbers": false,
40-
})
41-
.parseSync();
42-
43-
const fortunaUrl = argv.fortunaUrl;
44-
const chainName = argv.chainName;
45-
const coinFlipContractAddress = argv.address;
46-
const rpc = argv.rpcUrl;
47-
const privateKey = argv.privateKey;
48-
49-
async function fetchWithRetry(url: string, maxRetries: number): Promise<any> {
50-
let retryCount = 0;
51-
52-
async function doRequest() {
53-
try {
54-
const response = await axios.get(url);
55-
return response.data;
56-
} catch (error) {
57-
if (retryCount < maxRetries) {
58-
retryCount++;
59-
setTimeout(doRequest, 1000);
60-
} else {
61-
console.error("Max retry attempts reached. Exiting.");
62-
throw error;
63-
}
64-
}
65-
}
66-
67-
return await doRequest(); // Start the initial request
68-
}
28+
});
6929

7030
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+
7137
const provider = new HDWalletProvider({
7238
privateKeys: [privateKey],
7339
providerOrUrl: rpc,
@@ -84,38 +50,52 @@ async function main() {
8450

8551
console.log("1. Generating user's random number...");
8652
const randomNumber = web3.utils.randomHex(32);
87-
const commitment = web3.utils.keccak256(randomNumber);
8853
console.log(` number : ${randomNumber}`);
89-
console.log(` commitment: ${commitment}`);
9054

9155
console.log("2. Requesting coin flip...");
9256
const flipFee = await coinFlipContract.methods.getFlipFee().call();
9357
console.log(` fee : ${flipFee} wei`);
9458

9559
const receipt = await coinFlipContract.methods
96-
.requestFlip(commitment)
60+
.requestFlip(randomNumber)
9761
.send({ value: flipFee, from: provider.getAddress(0) });
9862

9963
console.log(` tx : ${receipt.transactionHash}`);
10064
const sequenceNumber = receipt.events.FlipRequest.returnValues.sequenceNumber;
10165
console.log(` sequence : ${sequenceNumber}`);
10266

103-
console.log("3. Retrieving provider's random number...");
104-
const url = `${fortunaUrl}/v1/chains/${chainName}/revelations/${sequenceNumber}`;
105-
console.log(` fetch url : ${url}`);
106-
// Note that there is a potential race condition here: the server may not have observed the request ^
107-
// before this HTTP response. Hence, we retry fetching the url a couple of times.
108-
const response = await fetchWithRetry(url, 3);
109-
const providerRandom = `0x${response.value.data}`;
110-
console.log(` number : ${providerRandom}`);
111-
112-
console.log("4. Revealing the result of the coin flip...");
113-
const receipt2 = await coinFlipContract.methods
114-
.revealFlip(sequenceNumber, randomNumber, providerRandom)
115-
.send({ from: provider.getAddress(0) });
116-
console.log(` tx : ${receipt2.transactionHash}`);
117-
const isHeads = receipt2.events.FlipResult.returnValues.isHeads;
118-
console.log(` result : ${isHeads ? "heads" : "tails"}`);
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);
11999

120100
provider.engine.stop();
121101
}

target_chains/ethereum/examples/coin_flip/contract/scripts/deploy.sh

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
#!/bin/bash -e
22

33
# URL of the ethereum RPC node to use. Choose this based on your target network
4-
RPC_URL=https://api.testnet.evm.eosnetwork.com/
4+
RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
55

66
# The address of the Pyth contract on your network. See the list of contract addresses here https://docs.pyth.network/documentation/pythnet-price-feeds/evm
7-
ENTROPY_CONTRACT_ADDRESS="0xD42c7a708E74AD19401D907a14146F006c851Ee3"
7+
ENTROPY_CONTRACT_ADDRESS="0x549Ebba8036Ab746611B4fFA1423eb0A4Df61440"
88
PROVIDER="0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344"
99

1010
# Deployments
11-
# optimism-goerli 0x3bA217Cd7840Cc5B34FD5B7263Cebd8CD8665788
12-
# avalanche-fuji 0xE7E52C85907d59C45b2C56EF32B78F514F8c547a
13-
# eos-evm-testnet 0x413405Aee2db95cb028B60CBAd87FC0B932947f4
11+
# optimism-sepolia 0x2eE67fF5d8548fF544f2c178a0FcAFe503A634Be
12+
# arbitrum-sepolia 0xCd76c50c3210C5AaA9c39D53A4f95BFd8b1a3a19
1413

1514
# Note the -l here uses a ledger wallet to deploy your contract. You may need to change this
1615
# option if you are using a different wallet.

0 commit comments

Comments
 (0)