Entropy Solidity SDK & usage example (#1124)
* grr * revert * ok * ok * ok * deploy script * implement interface * doc comments * fix comment
This commit is contained in:
parent
d4fad5049c
commit
ddbbe2af14
|
@ -2,3 +2,9 @@ chains:
|
|||
optimism-goerli:
|
||||
geth_rpc_addr: https://goerli.optimism.io
|
||||
contract_addr: 0x28F16Af4D87523910b843a801454AEde5F9B0459
|
||||
avalanche-fuji:
|
||||
geth_rpc_addr: https://api.avax-test.network/ext/bc/C/rpc
|
||||
contract_addr: 0xD42c7a708E74AD19401D907a14146F006c851Ee3
|
||||
eos-evm-testnet:
|
||||
geth_rpc_addr: https://api.testnet.evm.eosnetwork.com/
|
||||
contract_addr: 0xD42c7a708E74AD19401D907a14146F006c851Ee3
|
||||
|
|
|
@ -16,7 +16,7 @@ pub mod state;
|
|||
// Server TODO list:
|
||||
// - Tests
|
||||
// - Reduce memory requirements for storing hash chains to increase scalability
|
||||
// - Name things nicely (service name, API resource names)
|
||||
// - Name things nicely (API resource names)
|
||||
// - README
|
||||
// - Choose data formats for binary data
|
||||
#[tokio::main]
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"target_chains/cosmwasm/tools",
|
||||
"target_chains/cosmwasm/deploy-scripts",
|
||||
"target_chains/ethereum/contracts",
|
||||
"target_chains/ethereum/entropy_sdk/solidity",
|
||||
"target_chains/ethereum/sdk/js",
|
||||
"target_chains/ethereum/sdk/solidity",
|
||||
"target_chains/ethereum/examples/oracle_swap/app",
|
||||
|
@ -11755,6 +11756,10 @@
|
|||
"resolved": "target_chains/cosmwasm/tools",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@pythnetwork/entropy-sdk-solidity": {
|
||||
"resolved": "target_chains/ethereum/entropy_sdk/solidity",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@pythnetwork/eth-oracle-swap-example-frontend": {
|
||||
"resolved": "target_chains/ethereum/examples/oracle_swap/app",
|
||||
"link": true
|
||||
|
@ -57381,6 +57386,7 @@
|
|||
"@openzeppelin/contracts": "^4.5.0",
|
||||
"@openzeppelin/contracts-upgradeable": "^4.5.2",
|
||||
"@openzeppelin/hardhat-upgrades": "^1.22.1",
|
||||
"@pythnetwork/entropy-sdk-solidity": "*",
|
||||
"@pythnetwork/pyth-multisig-wh-message-builder": "*",
|
||||
"@pythnetwork/pyth-sdk-solidity": "^2.2.0",
|
||||
"contract_manager": "*",
|
||||
|
@ -57988,6 +57994,54 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"target_chains/ethereum/entropy_sdk/solidity": {
|
||||
"version": "0.1.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-solidity": "^1.0.0-rc.1",
|
||||
"solc": "^0.8.15"
|
||||
}
|
||||
},
|
||||
"target_chains/ethereum/entropy_sdk/solidity/node_modules/commander": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
||||
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"target_chains/ethereum/entropy_sdk/solidity/node_modules/semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"target_chains/ethereum/entropy_sdk/solidity/node_modules/solc": {
|
||||
"version": "0.8.21",
|
||||
"resolved": "https://registry.npmjs.org/solc/-/solc-0.8.21.tgz",
|
||||
"integrity": "sha512-N55ogy2dkTRwiONbj4e6wMZqUNaLZkiRcjGyeafjLYzo/tf/IvhHY5P5wpe+H3Fubh9idu071i8eOGO31s1ylg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"command-exists": "^1.2.8",
|
||||
"commander": "^8.1.0",
|
||||
"follow-redirects": "^1.12.1",
|
||||
"js-sha3": "0.8.0",
|
||||
"memorystream": "^0.3.1",
|
||||
"semver": "^5.5.0",
|
||||
"tmp": "0.0.33"
|
||||
},
|
||||
"bin": {
|
||||
"solcjs": "solc.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"target_chains/ethereum/examples/oracle_swap/app": {
|
||||
"name": "@pythnetwork/eth-oracle-swap-example-frontend",
|
||||
"version": "0.1.0",
|
||||
|
@ -66350,6 +66404,43 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@pythnetwork/entropy-sdk-solidity": {
|
||||
"version": "file:target_chains/ethereum/entropy_sdk/solidity",
|
||||
"requires": {
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-solidity": "^1.0.0-rc.1",
|
||||
"solc": "^0.8.15"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
||||
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true
|
||||
},
|
||||
"solc": {
|
||||
"version": "0.8.21",
|
||||
"resolved": "https://registry.npmjs.org/solc/-/solc-0.8.21.tgz",
|
||||
"integrity": "sha512-N55ogy2dkTRwiONbj4e6wMZqUNaLZkiRcjGyeafjLYzo/tf/IvhHY5P5wpe+H3Fubh9idu071i8eOGO31s1ylg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"command-exists": "^1.2.8",
|
||||
"commander": "^8.1.0",
|
||||
"follow-redirects": "^1.12.1",
|
||||
"js-sha3": "0.8.0",
|
||||
"memorystream": "^0.3.1",
|
||||
"semver": "^5.5.0",
|
||||
"tmp": "0.0.33"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@pythnetwork/eth-oracle-swap-example-frontend": {
|
||||
"version": "file:target_chains/ethereum/examples/oracle_swap/app",
|
||||
"requires": {
|
||||
|
@ -67858,6 +67949,7 @@
|
|||
"@openzeppelin/hardhat-upgrades": "^1.22.1",
|
||||
"@openzeppelin/test-helpers": "^0.5.15",
|
||||
"@openzeppelin/truffle-upgrades": "^1.14.0",
|
||||
"@pythnetwork/entropy-sdk-solidity": "*",
|
||||
"@pythnetwork/pyth-multisig-wh-message-builder": "*",
|
||||
"@pythnetwork/pyth-sdk-solidity": "^2.2.0",
|
||||
"@truffle/hdwallet-provider": "^2.1.5",
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"target_chains/cosmwasm/tools",
|
||||
"target_chains/cosmwasm/deploy-scripts",
|
||||
"target_chains/ethereum/contracts",
|
||||
"target_chains/ethereum/entropy_sdk/solidity",
|
||||
"target_chains/ethereum/sdk/js",
|
||||
"target_chains/ethereum/sdk/solidity",
|
||||
"target_chains/ethereum/examples/oracle_swap/app",
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./PythRandomState.sol";
|
||||
import "./PythRandomErrors.sol";
|
||||
import "./PythRandomEvents.sol";
|
||||
import "@pythnetwork/entropy-sdk-solidity/PythRandomState.sol";
|
||||
import "@pythnetwork/entropy-sdk-solidity/PythRandomErrors.sol";
|
||||
import "@pythnetwork/entropy-sdk-solidity/PythRandomEvents.sol";
|
||||
import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
|
||||
|
||||
// PythRandom implements a secure 2-party random number generation procedure. The protocol
|
||||
// is an extension of a simple commit/reveal protocol. The original version has the following steps:
|
||||
|
@ -78,7 +79,7 @@ import "./PythRandomEvents.sol";
|
|||
// - function to check invariants??
|
||||
// - need to increment pyth fees if someone transfers funds to the contract via another method
|
||||
// - off-chain data ERC support?
|
||||
contract PythRandom is PythRandomState, PythRandomEvents {
|
||||
contract PythRandom is IEntropy, PythRandomState {
|
||||
// TODO: Use an upgradeable proxy
|
||||
constructor(uint pythFeeInWei) {
|
||||
_state.accruedPythFeesInWei = 0;
|
||||
|
@ -95,7 +96,7 @@ contract PythRandom is PythRandomState, PythRandomEvents {
|
|||
bytes32 commitment,
|
||||
bytes32 commitmentMetadata,
|
||||
uint64 chainLength
|
||||
) public {
|
||||
) public override {
|
||||
if (chainLength == 0) revert PythRandomErrors.AssertionFailure();
|
||||
|
||||
PythRandomStructs.ProviderInfo storage provider = _state.providers[
|
||||
|
@ -124,7 +125,7 @@ contract PythRandom is PythRandomState, PythRandomEvents {
|
|||
// Withdraw a portion of the accumulated fees for the provider msg.sender.
|
||||
// Calling this function will transfer `amount` wei to the caller (provided that they have accrued a sufficient
|
||||
// balance of fees in the contract).
|
||||
function withdraw(uint256 amount) public {
|
||||
function withdraw(uint256 amount) public override {
|
||||
PythRandomStructs.ProviderInfo storage providerInfo = _state.providers[
|
||||
msg.sender
|
||||
];
|
||||
|
@ -155,7 +156,7 @@ contract PythRandom is PythRandomState, PythRandomEvents {
|
|||
address provider,
|
||||
bytes32 userCommitment,
|
||||
bool useBlockHash
|
||||
) public payable returns (uint64 assignedSequenceNumber) {
|
||||
) public payable override returns (uint64 assignedSequenceNumber) {
|
||||
PythRandomStructs.ProviderInfo storage providerInfo = _state.providers[
|
||||
provider
|
||||
];
|
||||
|
@ -205,7 +206,7 @@ contract PythRandom is PythRandomState, PythRandomEvents {
|
|||
uint64 sequenceNumber,
|
||||
bytes32 userRandomness,
|
||||
bytes32 providerRevelation
|
||||
) public returns (bytes32 randomNumber) {
|
||||
) public override returns (bytes32 randomNumber) {
|
||||
// TODO: do we need to check that this request exists?
|
||||
// TODO: this method may need to be authenticated to prevent griefing
|
||||
bytes32 key = requestKey(provider, sequenceNumber);
|
||||
|
@ -257,25 +258,33 @@ contract PythRandom is PythRandomState, PythRandomEvents {
|
|||
|
||||
function getProviderInfo(
|
||||
address provider
|
||||
) public view returns (PythRandomStructs.ProviderInfo memory info) {
|
||||
)
|
||||
public
|
||||
view
|
||||
override
|
||||
returns (PythRandomStructs.ProviderInfo memory info)
|
||||
{
|
||||
info = _state.providers[provider];
|
||||
}
|
||||
|
||||
function getRequest(
|
||||
address provider,
|
||||
uint64 sequenceNumber
|
||||
) public view returns (PythRandomStructs.Request memory req) {
|
||||
) public view override returns (PythRandomStructs.Request memory req) {
|
||||
bytes32 key = requestKey(provider, sequenceNumber);
|
||||
req = _state.requests[key];
|
||||
}
|
||||
|
||||
function getFee(address provider) public view returns (uint feeAmount) {
|
||||
function getFee(
|
||||
address provider
|
||||
) public view override returns (uint feeAmount) {
|
||||
return _state.providers[provider].feeInWei + _state.pythFeeInWei;
|
||||
}
|
||||
|
||||
function getAccruedPythFees()
|
||||
public
|
||||
view
|
||||
override
|
||||
returns (uint accruedPythFeesInWei)
|
||||
{
|
||||
return _state.accruedPythFeesInWei;
|
||||
|
@ -283,7 +292,7 @@ contract PythRandom is PythRandomState, PythRandomEvents {
|
|||
|
||||
function constructUserCommitment(
|
||||
bytes32 userRandomness
|
||||
) public pure returns (bytes32 userCommitment) {
|
||||
) public pure override returns (bytes32 userCommitment) {
|
||||
userCommitment = keccak256(bytes.concat(userRandomness));
|
||||
}
|
||||
|
||||
|
@ -291,7 +300,7 @@ contract PythRandom is PythRandomState, PythRandomEvents {
|
|||
bytes32 userRandomness,
|
||||
bytes32 providerRandomness,
|
||||
bytes32 blockHash
|
||||
) public pure returns (bytes32 combinedRandomness) {
|
||||
) public pure override returns (bytes32 combinedRandomness) {
|
||||
combinedRandomness = keccak256(
|
||||
abi.encodePacked(userRandomness, providerRandomness, blockHash)
|
||||
);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"@openzeppelin/hardhat-upgrades": "^1.22.1",
|
||||
"@pythnetwork/pyth-multisig-wh-message-builder": "*",
|
||||
"@pythnetwork/pyth-sdk-solidity": "^2.2.0",
|
||||
"@pythnetwork/entropy-sdk-solidity": "*",
|
||||
"contract_manager": "*",
|
||||
"dotenv": "^10.0.0",
|
||||
"elliptic": "^6.5.2",
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./PythRandomEvents.sol";
|
||||
|
||||
interface IEntropy is PythRandomEvents {
|
||||
// Register msg.sender as a randomness provider. The arguments are the provider's configuration parameters
|
||||
// and initial commitment. Re-registering the same provider rotates the provider's commitment (and updates
|
||||
// the feeInWei).
|
||||
//
|
||||
// chainLength is the number of values in the hash chain *including* the commitment, that is, chainLength >= 1.
|
||||
function register(
|
||||
uint feeInWei,
|
||||
bytes32 commitment,
|
||||
bytes32 commitmentMetadata,
|
||||
uint64 chainLength
|
||||
) external;
|
||||
|
||||
// Withdraw a portion of the accumulated fees for the provider msg.sender.
|
||||
// Calling this function will transfer `amount` wei to the caller (provided that they have accrued a sufficient
|
||||
// balance of fees in the contract).
|
||||
function withdraw(uint256 amount) external;
|
||||
|
||||
// As a user, request a random number from `provider`. Prior to calling this method, the user should
|
||||
// generate a random number x and keep it secret. The user should then compute hash(x) and pass that
|
||||
// as the userCommitment argument. (You may call the constructUserCommitment method to compute the hash.)
|
||||
//
|
||||
// This method returns a sequence number. The user should pass this sequence number to
|
||||
// their chosen provider (the exact method for doing so will depend on the provider) to retrieve the provider's
|
||||
// number. The user should then call fulfillRequest to construct the final random number.
|
||||
//
|
||||
// This method will revert unless the caller provides a sufficient fee (at least getFee(provider)) as msg.value.
|
||||
// Note that excess value is *not* refunded to the caller.
|
||||
function request(
|
||||
address provider,
|
||||
bytes32 userCommitment,
|
||||
bool useBlockHash
|
||||
) external payable returns (uint64 assignedSequenceNumber);
|
||||
|
||||
// Fulfill a request for a random number. This method validates the provided userRandomness and provider's proof
|
||||
// against the corresponding commitments in the in-flight request. If both values are validated, this function returns
|
||||
// the corresponding random number.
|
||||
//
|
||||
// Note that this function can only be called once per in-flight request. Calling this function deletes the stored
|
||||
// request information (so that the contract doesn't use a linear amount of storage in the number of requests).
|
||||
// If you need to use the returned random number more than once, you are responsible for storing it.
|
||||
function reveal(
|
||||
address provider,
|
||||
uint64 sequenceNumber,
|
||||
bytes32 userRandomness,
|
||||
bytes32 providerRevelation
|
||||
) external returns (bytes32 randomNumber);
|
||||
|
||||
function getProviderInfo(
|
||||
address provider
|
||||
) external view returns (PythRandomStructs.ProviderInfo memory info);
|
||||
|
||||
function getRequest(
|
||||
address provider,
|
||||
uint64 sequenceNumber
|
||||
) external view returns (PythRandomStructs.Request memory req);
|
||||
|
||||
function getFee(address provider) external view returns (uint feeAmount);
|
||||
|
||||
function getAccruedPythFees()
|
||||
external
|
||||
view
|
||||
returns (uint accruedPythFeesInWei);
|
||||
|
||||
function constructUserCommitment(
|
||||
bytes32 userRandomness
|
||||
) external pure returns (bytes32 userCommitment);
|
||||
|
||||
function combineRandomValues(
|
||||
bytes32 userRandomness,
|
||||
bytes32 providerRandomness,
|
||||
bytes32 blockHash
|
||||
) external pure returns (bytes32 combinedRandomness);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "@pythnetwork/entropy-sdk-solidity",
|
||||
"version": "0.1.0",
|
||||
"description": "Generate secure random numbers with Pyth Entropy",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pyth-network/pyth-crosschain",
|
||||
"directory": "target_chains/ethereum/entropy_sdk/solidity"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "npx prettier --write ."
|
||||
},
|
||||
"keywords": [
|
||||
"pyth",
|
||||
"solidity",
|
||||
"random"
|
||||
],
|
||||
"author": "Douro Labs",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pyth-network/pyth-crosschain/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pyth-network/pyth-crosschain/tree/main/target_chains/ethereum/entropy_sdk/solidity",
|
||||
"devDependencies": {
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-solidity": "^1.0.0-rc.1",
|
||||
"solc": "^0.8.15"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
lib/*
|
||||
!lib/README.md
|
||||
cache
|
||||
out
|
|
@ -0,0 +1,7 @@
|
|||
[profile.default]
|
||||
solc = '0.8.4'
|
||||
src = 'src'
|
||||
out = 'out'
|
||||
libs = ['lib', '../../../entropy_sdk/solidity']
|
||||
|
||||
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
|
|
@ -0,0 +1 @@
|
|||
Forge installs the dependencies in this folder. They are .gitignored
|
|
@ -0,0 +1,3 @@
|
|||
ds-test/=lib/forge-std/lib/ds-test/src/
|
||||
forge-std/=lib/forge-std/src/
|
||||
entropy-sdk-solidity/=../../../entropy_sdk/solidity/
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
# URL of the ethereum RPC node to use. Choose this based on your target network
|
||||
RPC_URL=https://api.avax-test.network/ext/bc/C/rpc
|
||||
|
||||
# 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
|
||||
ENTROPY_CONTRACT_ADDRESS="0xD42c7a708E74AD19401D907a14146F006c851Ee3"
|
||||
PROVIDER="0x368397bDc956b4F23847bE244f350Bde4615F25E"
|
||||
|
||||
# Avalanche fuji address:
|
||||
# 0x544c5ab499C38dff495724451783F63a3eeA40F2
|
||||
|
||||
# Note the -l here uses a ledger wallet to deploy your contract. You may need to change this
|
||||
# option if you are using a different wallet.
|
||||
forge create src/CoinFlip.sol:CoinFlip \
|
||||
-l \
|
||||
--rpc-url $RPC_URL \
|
||||
--constructor-args \
|
||||
$ENTROPY_CONTRACT_ADDRESS \
|
||||
$PROVIDER
|
|
@ -0,0 +1,117 @@
|
|||
// SPDX-License-Identifier: Apache 2
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "entropy-sdk-solidity/IEntropy.sol";
|
||||
|
||||
library CoinFlipErrors {
|
||||
error IncorrectSender();
|
||||
|
||||
error InsufficientFee();
|
||||
}
|
||||
|
||||
/// Example contract using Pyth Entropy to allow a user to flip a secure fair coin.
|
||||
/// Users interact with the contract by sending two transactions:
|
||||
/// 1. Users request a coin flip. This operation commits the flip to use a specific (but currently unknown) random number
|
||||
/// generated by Pyth Entropy.
|
||||
/// 2. Users reveal the result of the coin flip. This operation reveals the random number from Pyth Entropy and checks
|
||||
/// its validity, then converts the random number into the result of a coin flip.
|
||||
contract CoinFlip {
|
||||
// Event emitted when a coin flip is requested. The sequence number is required to reveal
|
||||
// the result of the flip.
|
||||
event FlipRequest(uint64 sequenceNumber);
|
||||
|
||||
// Event emitted when the result of the coin flip is known.
|
||||
event FlipResult(bool isHeads);
|
||||
|
||||
// Contracts using Pyth Entropy should import the solidity SDK and then store both the Entropy contract
|
||||
// and a specific entropy provider to use for requests. Each provider commits to a sequence of random numbers.
|
||||
// Providers are then responsible for two things:
|
||||
// 1. Operating an off-chain service that reveals their random numbers once they've been committed to on-chain
|
||||
// 2. Maintaining the secrecy of the other random numbers
|
||||
// Users should choose a reliable provider who they trust to uphold these commitments.
|
||||
// (For the moment, the only available provider is 0x368397bDc956b4F23847bE244f350Bde4615F25E)
|
||||
IEntropy private entropy;
|
||||
address private entropyProvider;
|
||||
|
||||
// The contract is required to maintain a collection of in-flight requests. This mapping allows the contract
|
||||
// to match the revealed random numbers against the original requests. The key of the map can be the
|
||||
// sequence number provided by the Entropy protocol, and the value can be whatever information the protocol
|
||||
// needs to resolve in-flight requests.
|
||||
mapping(uint64 => address) private requestedFlips;
|
||||
|
||||
constructor(address _entropy, address _entropyProvider) {
|
||||
entropy = IEntropy(_entropy);
|
||||
entropyProvider = _entropyProvider;
|
||||
}
|
||||
|
||||
// Request to flip a coin. The caller should generate a random number prior to calling this method, then
|
||||
// submit the hash of that number as userCommitment. (You can call `IEntropy.constructUserCommitment` with
|
||||
// the random number to generate the commitment.)
|
||||
function requestFlip(bytes32 userCommitment) external payable {
|
||||
// The entropy protocol requires the caller to pay a fee (in native gas tokens) per requested random number.
|
||||
// This fee can either be paid by the contract itself or passed on to the end user.
|
||||
// This implementation of the requestFlip method passes on the fee to the end user.
|
||||
uint256 fee = entropy.getFee(entropyProvider);
|
||||
if (msg.value < fee) {
|
||||
revert CoinFlipErrors.InsufficientFee();
|
||||
}
|
||||
|
||||
// Request the random number from the Entropy protocol. The call returns a sequence number that uniquely
|
||||
// identifies the generated random number. Callers should save this sequence number so that they can match
|
||||
// which request is being revealed in the next stage of the protocol.
|
||||
//
|
||||
// The final `true` parameter to this method incorporates the blockhash of the request's block into the
|
||||
// generated random value. The blockhash adds another level of security and manipulation-resistance to the
|
||||
// random value. Set this to `true` unless your blockchain has poor support for retrieving blockhashes.
|
||||
uint64 sequenceNumber = entropy.request{value: fee}(
|
||||
entropyProvider,
|
||||
userCommitment,
|
||||
true
|
||||
);
|
||||
requestedFlips[sequenceNumber] = msg.sender;
|
||||
|
||||
emit FlipRequest(sequenceNumber);
|
||||
}
|
||||
|
||||
// Reveal the result of the coin flip. The caller must have an in-flight request for a coin flip, which is
|
||||
// identified by `sequenceNumber`. The caller must additionally provide the random number that they previously
|
||||
// committed to, as well as the entropy provider's random number. The provider's random number can be retrieved
|
||||
// from them in a provider-dependent manner.
|
||||
//
|
||||
// For the moment, the provider 0x368397bDc956b4F23847bE244f350Bde4615F25E hosts a webservice at
|
||||
// https://fortuna-staging.pyth.network/ that allows anyone to retrieve their random values.
|
||||
// Fetch the following url:
|
||||
// https://fortuna-staging.pyth.network/v1/chains/<chain id>/revelations/<sequence number>
|
||||
//
|
||||
// The list of supported chain ids is available here https://fortuna-staging.pyth.network/v1/chains
|
||||
//
|
||||
// **Warning** users of this protocol can stall the protocol by choosing not to reveal their generated random number.
|
||||
// Developers using Pyth Entropy should ensure that users are always incentivized (or at least, not disincentivized)
|
||||
// to finish both stages of the protocol.
|
||||
function revealFlip(
|
||||
uint64 sequenceNumber,
|
||||
bytes32 userRandom,
|
||||
bytes32 providerRandom
|
||||
) public {
|
||||
// Validate that the caller is allowed to reveal the result of this particular in-flight request.
|
||||
if (requestedFlips[sequenceNumber] != msg.sender) {
|
||||
revert CoinFlipErrors.IncorrectSender();
|
||||
}
|
||||
// Optional: delete the in-flight request to save gas / chain storage.
|
||||
delete requestedFlips[sequenceNumber];
|
||||
|
||||
// Reveal the random number. This call reverts if the provided values fail to match the commitments
|
||||
// from the request phase. If the call returns, randomNumber is a uniformly distributed bytes32.
|
||||
bytes32 randomNumber = entropy.reveal(
|
||||
entropyProvider,
|
||||
sequenceNumber,
|
||||
userRandom,
|
||||
providerRandom
|
||||
);
|
||||
|
||||
// You can then convert the returned bytes32 into the range required by your application.
|
||||
emit FlipResult(uint256(randomNumber) % 2 == 0);
|
||||
}
|
||||
|
||||
receive() external payable {}
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
"solidity",
|
||||
"oracle"
|
||||
],
|
||||
"author": "Pyth Data Foundation",
|
||||
"author": "Pyth Data Association",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pyth-network/pyth-crosschain/issues"
|
||||
|
|
Loading…
Reference in New Issue