diff --git a/apps/fortuna/src/chain/ethereum.rs b/apps/fortuna/src/chain/ethereum.rs index 939f8f7cc8..daae50a4ee 100644 --- a/apps/fortuna/src/chain/ethereum.rs +++ b/apps/fortuna/src/chain/ethereum.rs @@ -93,6 +93,38 @@ impl SignablePythContractInner { } } + /// Submit a request for a random number to the contract. + /// + /// This method is a version of the autogenned `request` method that parses the emitted logs + /// to return the sequence number of the created Request. + pub async fn request_with_callback_wrapper( + &self, + provider: &Address, + user_randomness: &[u8; 32], + ) -> Result { + let fee = self.get_fee(*provider).call().await?; + + if let Some(r) = self + .request_with_callback(*provider, *user_randomness) + .value(fee) + .send() + .await? + .await? + { + // Extract Log from TransactionReceipt. + let l: RawLog = r.logs[0].clone().into(); + if let PythRandomEvents::RequestedWithCallbackFilter(r) = + PythRandomEvents::decode_log(&l)? + { + Ok(r.request.sequence_number) + } else { + Err(anyhow!("No log with sequence number")) + } + } else { + Err(anyhow!("Request failed")) + } + } + /// Reveal the generated random number to the contract. /// /// This method is a version of the autogenned `reveal` method that parses the emitted logs diff --git a/apps/fortuna/src/command/generate.rs b/apps/fortuna/src/command/generate.rs index a1216d0b14..825c1f1386 100644 --- a/apps/fortuna/src/command/generate.rs +++ b/apps/fortuna/src/command/generate.rs @@ -1,12 +1,13 @@ use { crate::{ - api::GetRandomValueResponse, - chain::ethereum::SignablePythContract, + chain::ethereum::{RevealedWithCallbackFilter, SignablePythContract}, config::{Config, GenerateOptions}, }, anyhow::Result, base64::{engine::general_purpose::STANDARD as base64_standard_engine, Engine as _}, + ethers::providers::Middleware, std::sync::Arc, + tokio::time::{self, Duration}, }; /// Run the entire random number generation protocol to produce a random number. @@ -22,42 +23,54 @@ pub async fn generate(opts: &GenerateOptions) -> Result<()> { let user_randomness = rand::random::<[u8; 32]>(); let provider = opts.provider; + let mut last_block_number = contract.provider().get_block_number().await?; + tracing::info!(block_number = last_block_number.as_u64(), "block number"); + + tracing::info!("Requesting random number..."); + // Request a random number on the contract let sequence_number = contract - .request_wrapper(&provider, &user_randomness, opts.blockhash) + .request_with_callback_wrapper(&provider, &user_randomness) .await?; - tracing::info!(sequence_number = sequence_number, "random number requested",); + tracing::info!(sequence_number = sequence_number, "Random number requested",); - // Get the committed value from the provider - let resp = reqwest::get(opts.url.join(&format!( - "/v1/chains/{}/revelations/{}", - opts.chain_id, sequence_number - ))?) - .await? - .json::() - .await?; + let mut num_retries = 0; + let mut found_request = false; + while !found_request && num_retries < 10 { + let current_block_number = contract.provider().get_block_number().await?; + tracing::info!( + start_block = last_block_number.as_u64(), + end_block = current_block_number.as_u64(), + "Checking events between blocks." + ); - tracing::info!( - response = base64_standard_engine.encode(resp.value.data()), - "Retrieved the provider's random value.", - ); - let provider_randomness = resp.value.data(); - - // Submit the provider's and our values to the contract to reveal the random number. - let random_value = contract - .reveal_wrapper( - &provider, - sequence_number, - &user_randomness, - provider_randomness, - ) - .await?; + let mut event = contract.revealed_with_callback_filter(); + event.filter = event + .filter + .from_block(last_block_number) + .to_block(current_block_number); - tracing::info!( - number = base64_standard_engine.encode(random_value), - "Random number generated." - ); + let res: Vec = event.query().await?; + + for r in res.iter() { + if r.request.sequence_number == sequence_number && r.request.provider == provider { + tracing::info!( + number = base64_standard_engine.encode(r.random_number), + "Random number generated." + ); + found_request = true; + } + } + + last_block_number = current_block_number; + num_retries += 1; + time::sleep(Duration::from_secs(1)).await; + } + + if !found_request { + tracing::info!("Failed to receive a callback with the random number."); + } Ok(()) } diff --git a/apps/fortuna/src/command/inspect.rs b/apps/fortuna/src/command/inspect.rs index de88c7738d..4ab1541fe9 100644 --- a/apps/fortuna/src/command/inspect.rs +++ b/apps/fortuna/src/command/inspect.rs @@ -1,6 +1,6 @@ use { crate::{ - chain::ethereum::{PythContract, Request}, + chain::ethereum::{EntropyStructsV2Request, PythContract}, config::{Config, EthereumConfig, InspectOptions}, }, anyhow::Result, @@ -66,7 +66,7 @@ async fn inspect_chain( ); current_request_number -= 1; } - let return_data: Vec = multicall.call_array().await?; + let return_data: Vec = multicall.call_array().await?; for request in return_data { process_request(rpc_provider.clone(), request).await?; } @@ -89,7 +89,10 @@ async fn inspect_chain( Ok(()) } -async fn process_request(rpc_provider: Provider, request: Request) -> Result<()> { +async fn process_request( + rpc_provider: Provider, + request: EntropyStructsV2Request, +) -> Result<()> { if request.sequence_number != 0 && request.callback_status != 0 { let block = rpc_provider .get_block(request.block_number) diff --git a/apps/fortuna/src/command/setup_provider.rs b/apps/fortuna/src/command/setup_provider.rs index 3c587b0d14..8d56b15b60 100644 --- a/apps/fortuna/src/command/setup_provider.rs +++ b/apps/fortuna/src/command/setup_provider.rs @@ -1,7 +1,7 @@ use { crate::{ api::{get_register_uri, ChainId}, - chain::ethereum::{ProviderInfo, SignablePythContract}, + chain::ethereum::{EntropyStructsV2ProviderInfo, SignablePythContract}, command::register_provider::{register_provider_from_config, CommitmentMetadata}, config::{Config, EthereumConfig, SetupProviderOptions}, state::{HashChainState, PebbleHashChain}, @@ -178,7 +178,7 @@ async fn setup_chain_provider( async fn sync_uri( contract: &Arc, - provider_info: &ProviderInfo, + provider_info: &EntropyStructsV2ProviderInfo, uri: String, ) -> Result<()> { let uri_as_bytes: Bytes = AbiBytes::from(uri.as_str()).into(); @@ -198,7 +198,7 @@ async fn sync_uri( async fn sync_fee( contract: &Arc, - provider_info: &ProviderInfo, + provider_info: &EntropyStructsV2ProviderInfo, provider_fee: u128, ) -> Result<()> { if provider_info.fee_in_wei != provider_fee { @@ -217,7 +217,7 @@ async fn sync_fee( async fn sync_fee_manager( contract: &Arc, - provider_info: &ProviderInfo, + provider_info: &EntropyStructsV2ProviderInfo, fee_manager: Address, ) -> Result<()> { if provider_info.fee_manager != fee_manager { @@ -231,7 +231,7 @@ async fn sync_fee_manager( async fn sync_max_num_hashes( contract: &Arc, - provider_info: &ProviderInfo, + provider_info: &EntropyStructsV2ProviderInfo, max_num_hashes: u32, ) -> Result<()> { if provider_info.max_num_hashes != max_num_hashes { diff --git a/apps/fortuna/src/config.rs b/apps/fortuna/src/config.rs index 70014ddcb7..5bf3582992 100644 --- a/apps/fortuna/src/config.rs +++ b/apps/fortuna/src/config.rs @@ -25,7 +25,6 @@ mod setup_provider; mod withdraw_fees; const DEFAULT_RPC_ADDR: &str = "127.0.0.1:34000"; -const DEFAULT_HTTP_ADDR: &str = "http://127.0.0.1:34000"; #[derive(Parser, Debug)] #[command(name = crate_name!())] diff --git a/apps/fortuna/src/config/generate.rs b/apps/fortuna/src/config/generate.rs index 198b15eee4..71198f9eea 100644 --- a/apps/fortuna/src/config/generate.rs +++ b/apps/fortuna/src/config/generate.rs @@ -2,7 +2,6 @@ use { crate::{api::ChainId, config::ConfigOptions}, clap::Args, ethers::types::Address, - reqwest::Url, }; #[derive(Args, Clone, Debug)] @@ -27,11 +26,4 @@ pub struct GenerateOptions { /// Submit a randomness request to this provider #[arg(long = "provider")] pub provider: Address, - - #[arg(long = "url")] - #[arg(default_value = super::DEFAULT_HTTP_ADDR)] - pub url: Url, - - #[arg(short = 'b')] - pub blockhash: bool, } diff --git a/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol b/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol index 8e9986715d..ec4e7b2e54 100644 --- a/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol +++ b/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyErrors.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol"; import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol"; @@ -11,6 +11,7 @@ import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "@nomad-xyz/excessively-safe-call/src/ExcessivelySafeCall.sol"; import "./EntropyState.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyStatusConstants.sol"; +import "./EntropyStructConverter.sol"; // Entropy 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: @@ -106,7 +107,7 @@ abstract contract Entropy is IEntropy, EntropyState { // use a more consistent amount of gas. // Note that these requests are not live because their sequenceNumber is 0. for (uint8 i = 0; i < NUM_REQUESTS; i++) { - EntropyStructs.Request storage req = _state.requests[i]; + EntropyStructsV2.Request storage req = _state.requests[i]; req.provider = address(1); req.blockNumber = 1234; req.commitment = hex"0123"; @@ -128,7 +129,7 @@ abstract contract Entropy is IEntropy, EntropyState { ) public override { if (chainLength == 0) revert EntropyErrors.AssertionFailure(); - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; @@ -149,14 +150,14 @@ abstract contract Entropy is IEntropy, EntropyState { provider.sequenceNumber += 1; - emit Registered(provider); + emit Registered(EntropyStructConverter.toV1ProviderInfo(provider)); } // 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(uint128 amount) public override { - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ msg.sender ]; @@ -178,7 +179,7 @@ abstract contract Entropy is IEntropy, EntropyState { address provider, uint128 amount ) external override { - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ provider ]; @@ -213,8 +214,8 @@ abstract contract Entropy is IEntropy, EntropyState { bool useBlockhash, bool isRequestWithCallback, uint32 callbackGasLimit - ) internal returns (EntropyStructs.Request storage req) { - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + ) internal returns (EntropyStructsV2.Request storage req) { + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ provider ]; if (_state.providers[provider].sequenceNumber == 0) @@ -294,7 +295,7 @@ abstract contract Entropy is IEntropy, EntropyState { bytes32 userCommitment, bool useBlockHash ) public payable override returns (uint64 assignedSequenceNumber) { - EntropyStructs.Request storage req = requestHelper( + EntropyStructsV2.Request storage req = requestHelper( provider, userCommitment, useBlockHash, @@ -302,7 +303,7 @@ abstract contract Entropy is IEntropy, EntropyState { 0 ); assignedSequenceNumber = req.sequenceNumber; - emit Requested(req); + emit Requested(EntropyStructConverter.toV1Request(req)); } // Request a random number. The method expects the provider address and a secret random number @@ -330,7 +331,7 @@ abstract contract Entropy is IEntropy, EntropyState { bytes32 userRandomNumber, uint32 gasLimit ) public payable override returns (uint64) { - EntropyStructs.Request storage req = requestHelper( + EntropyStructsV2.Request storage req = requestHelper( provider, constructUserCommitment(userRandomNumber), // If useBlockHash is set to true, it allows a scenario in which the provider and miner can collude. @@ -346,7 +347,7 @@ abstract contract Entropy is IEntropy, EntropyState { req.requester, req.sequenceNumber, userRandomNumber, - req + EntropyStructConverter.toV1Request(req) ); return req.sequenceNumber; } @@ -355,7 +356,7 @@ abstract contract Entropy is IEntropy, EntropyState { // commitment in the in-flight request. If both values are validated, this method will update the provider // current commitment and returns the generated random number. function revealHelper( - EntropyStructs.Request storage req, + EntropyStructsV2.Request storage req, bytes32 userRevelation, bytes32 providerRevelation ) internal returns (bytes32 randomNumber, bytes32 blockHash) { @@ -391,7 +392,7 @@ abstract contract Entropy is IEntropy, EntropyState { blockHash ); - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ req.provider ]; if (providerInfo.currentCommitmentSequenceNumber < req.sequenceNumber) { @@ -407,7 +408,7 @@ abstract contract Entropy is IEntropy, EntropyState { uint64 advancedSequenceNumber, bytes32 providerRevelation ) public override { - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ provider ]; if ( @@ -461,7 +462,7 @@ abstract contract Entropy is IEntropy, EntropyState { bytes32 userRevelation, bytes32 providerRevelation ) public override returns (bytes32 randomNumber) { - EntropyStructs.Request storage req = findActiveRequest( + EntropyStructsV2.Request storage req = findActiveRequest( provider, sequenceNumber ); @@ -482,7 +483,7 @@ abstract contract Entropy is IEntropy, EntropyState { providerRevelation ); emit Revealed( - req, + EntropyStructConverter.toV1Request(req), userRevelation, providerRevelation, blockHash, @@ -507,7 +508,7 @@ abstract contract Entropy is IEntropy, EntropyState { bytes32 userRandomNumber, bytes32 providerRevelation ) public override { - EntropyStructs.Request storage req = findActiveRequest( + EntropyStructsV2.Request storage req = findActiveRequest( provider, sequenceNumber ); @@ -564,7 +565,7 @@ abstract contract Entropy is IEntropy, EntropyState { if (success) { emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, providerRevelation, randomNumber @@ -602,7 +603,7 @@ abstract contract Entropy is IEntropy, EntropyState { } else { // This case uses the checks-effects-interactions pattern to avoid reentry attacks emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, providerRevelation, randomNumber @@ -627,7 +628,7 @@ abstract contract Entropy is IEntropy, EntropyState { function getProviderInfo( address provider - ) public view override returns (EntropyStructs.ProviderInfo memory info) { + ) public view override returns (EntropyStructsV2.ProviderInfo memory info) { info = _state.providers[provider]; } @@ -643,7 +644,7 @@ abstract contract Entropy is IEntropy, EntropyState { function getRequest( address provider, uint64 sequenceNumber - ) public view override returns (EntropyStructs.Request memory req) { + ) public view override returns (EntropyStructsV2.Request memory req) { req = findRequest(provider, sequenceNumber); } @@ -664,7 +665,7 @@ abstract contract Entropy is IEntropy, EntropyState { address providerAddr, uint32 gasLimit ) internal view returns (uint128 feeAmount) { - EntropyStructs.ProviderInfo memory provider = _state.providers[ + EntropyStructsV2.ProviderInfo memory provider = _state.providers[ providerAddr ]; @@ -704,7 +705,7 @@ abstract contract Entropy is IEntropy, EntropyState { // Set provider fee. It will revert if provider is not registered. function setProviderFee(uint128 newFeeInWei) external override { - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; @@ -720,7 +721,7 @@ abstract contract Entropy is IEntropy, EntropyState { address provider, uint128 newFeeInWei ) external override { - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ provider ]; @@ -740,7 +741,7 @@ abstract contract Entropy is IEntropy, EntropyState { // Set provider uri. It will revert if provider is not registered. function setProviderUri(bytes calldata newUri) external override { - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; if (provider.sequenceNumber == 0) { @@ -752,7 +753,7 @@ abstract contract Entropy is IEntropy, EntropyState { } function setFeeManager(address manager) external override { - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; if (provider.sequenceNumber == 0) { @@ -767,7 +768,7 @@ abstract contract Entropy is IEntropy, EntropyState { // Set the maximum number of hashes to record in a request. This should be set according to the maximum gas limit // the provider supports for callbacks. function setMaxNumHashes(uint32 maxNumHashes) external override { - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; if (provider.sequenceNumber == 0) { @@ -785,7 +786,7 @@ abstract contract Entropy is IEntropy, EntropyState { // Set the default gas limit for a request. function setDefaultGasLimit(uint32 gasLimit) external override { - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; if (provider.sequenceNumber == 0) { @@ -861,7 +862,7 @@ abstract contract Entropy is IEntropy, EntropyState { function findActiveRequest( address provider, uint64 sequenceNumber - ) internal view returns (EntropyStructs.Request storage req) { + ) internal view returns (EntropyStructsV2.Request storage req) { req = findRequest(provider, sequenceNumber); // Check there is an active request for the given provider and sequence number. @@ -878,7 +879,7 @@ abstract contract Entropy is IEntropy, EntropyState { function findRequest( address provider, uint64 sequenceNumber - ) internal view returns (EntropyStructs.Request storage req) { + ) internal view returns (EntropyStructsV2.Request storage req) { (bytes32 key, uint8 shortKey) = requestKey(provider, sequenceNumber); req = _state.requests[shortKey]; @@ -893,7 +894,7 @@ abstract contract Entropy is IEntropy, EntropyState { function clearRequest(address provider, uint64 sequenceNumber) internal { (bytes32 key, uint8 shortKey) = requestKey(provider, sequenceNumber); - EntropyStructs.Request storage req = _state.requests[shortKey]; + EntropyStructsV2.Request storage req = _state.requests[shortKey]; if (req.provider == provider && req.sequenceNumber == sequenceNumber) { req.sequenceNumber = 0; } else { @@ -908,7 +909,7 @@ abstract contract Entropy is IEntropy, EntropyState { function allocRequest( address provider, uint64 sequenceNumber - ) internal returns (EntropyStructs.Request storage req) { + ) internal returns (EntropyStructsV2.Request storage req) { (, uint8 shortKey) = requestKey(provider, sequenceNumber); req = _state.requests[shortKey]; @@ -929,7 +930,7 @@ abstract contract Entropy is IEntropy, EntropyState { // Returns true if a request is active, i.e., its corresponding random value has not yet been revealed. function isActive( - EntropyStructs.Request storage req + EntropyStructsV2.Request storage req ) internal view returns (bool) { // Note that a provider's initial registration occupies sequence number 0, so there is no way to construct // a randomness request with sequence number 0. diff --git a/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol b/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol index 94b6e0ec59..a2a777f8c5 100644 --- a/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol +++ b/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol"; contract EntropyInternalStructs { struct State { @@ -30,10 +30,10 @@ contract EntropyInternalStructs { // considered active if their sequenceNumber is > 0. // // WARNING: the number of requests must be kept in sync with the constants below - EntropyStructs.Request[32] requests; - mapping(bytes32 => EntropyStructs.Request) requestsOverflow; + EntropyStructsV2.Request[32] requests; + mapping(bytes32 => EntropyStructsV2.Request) requestsOverflow; // Mapping from randomness providers to information about each them. - mapping(address => EntropyStructs.ProviderInfo) providers; + mapping(address => EntropyStructsV2.ProviderInfo) providers; // proposedAdmin is the new admin's account address proposed by either the owner or the current admin. // If there is no pending transfer request, this value will hold `address(0)`. address proposedAdmin; diff --git a/target_chains/ethereum/contracts/contracts/entropy/EntropyStructConverter.sol b/target_chains/ethereum/contracts/contracts/entropy/EntropyStructConverter.sol new file mode 100644 index 0000000000..fe8cf7e237 --- /dev/null +++ b/target_chains/ethereum/contracts/contracts/entropy/EntropyStructConverter.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol"; + +// Conversions from V2 structs to their V1 equivalents +library EntropyStructConverter { + function toV1Request( + EntropyStructsV2.Request memory v2Request + ) internal pure returns (EntropyStructs.Request memory v1Request) { + v1Request = EntropyStructs.Request({ + provider: v2Request.provider, + sequenceNumber: v2Request.sequenceNumber, + numHashes: v2Request.numHashes, + commitment: v2Request.commitment, + blockNumber: v2Request.blockNumber, + requester: v2Request.requester, + useBlockhash: v2Request.useBlockhash, + isRequestWithCallback: v2Request.callbackStatus > 0 + }); + } + + function toV1ProviderInfo( + EntropyStructsV2.ProviderInfo memory v2ProviderInfo + ) + internal + pure + returns (EntropyStructs.ProviderInfo memory v1ProviderInfo) + { + v1ProviderInfo = EntropyStructs.ProviderInfo({ + feeInWei: v2ProviderInfo.feeInWei, + accruedFeesInWei: v2ProviderInfo.accruedFeesInWei, + originalCommitment: v2ProviderInfo.originalCommitment, + originalCommitmentSequenceNumber: v2ProviderInfo + .originalCommitmentSequenceNumber, + commitmentMetadata: v2ProviderInfo.commitmentMetadata, + uri: v2ProviderInfo.uri, + endSequenceNumber: v2ProviderInfo.endSequenceNumber, + sequenceNumber: v2ProviderInfo.sequenceNumber, + currentCommitment: v2ProviderInfo.currentCommitment, + currentCommitmentSequenceNumber: v2ProviderInfo + .currentCommitmentSequenceNumber, + feeManager: v2ProviderInfo.feeManager, + maxNumHashes: v2ProviderInfo.maxNumHashes + }); + } +} diff --git a/target_chains/ethereum/contracts/forge-test/Entropy.t.sol b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol index a2638e6de1..3a6cd0b15f 100644 --- a/target_chains/ethereum/contracts/forge-test/Entropy.t.sol +++ b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.0; import "forge-std/Test.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol"; import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol"; import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol"; @@ -181,7 +182,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { random.getAccruedPythFees(); assertEq(address(random).balance, expectedBalance); - EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assert( @@ -190,7 +191,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); assert(info1.currentCommitmentSequenceNumber < info1.sequenceNumber); assert(info1.sequenceNumber <= info1.endSequenceNumber); - EntropyStructs.ProviderInfo memory info2 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info2 = random.getProviderInfo( provider2 ); assert( @@ -219,7 +220,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ALL_ZEROS ); - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, sequenceNumber ); @@ -520,7 +521,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { provider1Uri ); assertInvariants(); - EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assertEq(info1.endSequenceNumber, newHashChainOffset + 10); @@ -733,7 +734,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { } function testGetProviderInfo() public { - EntropyStructs.ProviderInfo memory providerInfo1 = random + EntropyStructsV2.ProviderInfo memory providerInfo1 = random .getProviderInfo(provider1); // These two fields aren't used by the Entropy contract itself -- they're just convenient info to store // on-chain -- so they aren't tested in the other tests. @@ -777,7 +778,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { function testRequestWithCallbackAndReveal() public { bytes32 userRandomNumber = bytes32(uint(42)); uint fee = random.getFee(provider1); - EntropyStructs.ProviderInfo memory providerInfo = random + EntropyStructsV2.ProviderInfo memory providerInfo = random .getProviderInfo(provider1); vm.roll(1234); @@ -805,8 +806,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { blockNumber: 1234, requester: user1, useBlockhash: false, - callbackStatus: EntropyStatusConstants.CALLBACK_NOT_STARTED, - gasLimit10k: 0 + isRequestWithCallback: true }) ); vm.roll(1234); @@ -843,14 +843,14 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { uint64 assignedSequenceNumber = consumer.requestEntropy{value: fee}( userRandomNumber ); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, assignedSequenceNumber ); vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, provider1Proofs[assignedSequenceNumber], random.combineRandomValues( @@ -880,7 +880,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ) ); - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, assignedSequenceNumber ); @@ -896,14 +896,14 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { provider1, userRandomNumber ); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, assignedSequenceNumber ); vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, provider1Proofs[assignedSequenceNumber], random.combineRandomValues( @@ -919,7 +919,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { provider1Proofs[assignedSequenceNumber] ); - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, assignedSequenceNumber ); @@ -973,7 +973,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { uint64 assignedSequenceNumber = consumer.requestEntropy{value: fee}( userRandomNumber ); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, assignedSequenceNumber ); @@ -983,7 +983,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, provider1Proofs[assignedSequenceNumber], random.combineRandomValues( @@ -1010,7 +1010,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ) ); - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, assignedSequenceNumber ); @@ -1058,7 +1058,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is still active after failure - EntropyStructs.Request memory reqAfterFailure = random.getRequest( + EntropyStructsV2.Request memory reqAfterFailure = random.getRequest( provider1, assignedSequenceNumber ); @@ -1089,7 +1089,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { consumer.setReverts(false); vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - reqAfterFailure, + EntropyStructConverter.toV1Request(reqAfterFailure), userRandomNumber, provider1Proofs[assignedSequenceNumber], random.combineRandomValues( @@ -1106,7 +1106,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is cleared after successful reveal - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, assignedSequenceNumber ); @@ -1129,7 +1129,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { uint64 assignedSequenceNumber = consumer.requestEntropy{value: fee}( userRandomNumber ); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, assignedSequenceNumber ); @@ -1172,7 +1172,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is still active after failure - EntropyStructs.Request memory reqAfterFailure = random.getRequest( + EntropyStructsV2.Request memory reqAfterFailure = random.getRequest( provider1, assignedSequenceNumber ); @@ -1202,7 +1202,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { // Calling without a gas limit should succeed vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - reqAfterFailure, + EntropyStructConverter.toV1Request(reqAfterFailure), userRandomNumber, provider1Proofs[assignedSequenceNumber], random.combineRandomValues( @@ -1219,7 +1219,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is cleared after successful reveal - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, assignedSequenceNumber ); @@ -1283,7 +1283,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { request(user1, provider1, 42, false); } assertInvariants(); - EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assertEq(info1.currentCommitmentSequenceNumber, 0); @@ -1353,7 +1353,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { seqNumber, provider1Proofs[seqNumber] ); - EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assertEq(info1.sequenceNumber, seqNumber + 1); @@ -1374,7 +1374,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { function testSetMaxNumHashes(uint32 maxNumHashes) public { vm.prank(provider1); random.setMaxNumHashes(maxNumHashes); - EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assertEq(info1.maxNumHashes, maxNumHashes); @@ -1455,7 +1455,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { emit ProviderDefaultGasLimitUpdated(provider1, 0, newGasLimit); random.setDefaultGasLimit(newGasLimit); - EntropyStructs.ProviderInfo memory info = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info = random.getProviderInfo( provider1 ); assertEq(info.defaultGasLimit, newGasLimit); @@ -1500,7 +1500,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { userRandomNumber ); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, sequenceNumber ); @@ -1601,7 +1601,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Check the gasLimit10k field in the request - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, sequenceNumber ); @@ -1649,7 +1649,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { consumer.setTargetGasUsage(callbackGasUsage); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, sequenceNumber ); @@ -1678,7 +1678,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is still active after failure - EntropyStructs.Request memory reqAfterFailure = random.getRequest( + EntropyStructsV2.Request memory reqAfterFailure = random.getRequest( provider1, sequenceNumber ); @@ -1690,7 +1690,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { } else { vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, provider1Proofs[sequenceNumber], random.combineRandomValues( @@ -1707,7 +1707,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is cleared after successful callback - EntropyStructs.Request memory reqAfterSuccess = random.getRequest( + EntropyStructsV2.Request memory reqAfterSuccess = random.getRequest( provider1, sequenceNumber ); diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol index 120cb0c858..d62c3d9295 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.0; import "./EntropyStructs.sol"; +// Deprecated -- these events are still emitted, but the lack of indexing +// makes them hard to use. interface EntropyEvents { event Registered(EntropyStructs.ProviderInfo provider); diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol index 002ab7be20..58f2aaafa6 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol @@ -2,6 +2,11 @@ pragma solidity ^0.8.0; +// This contract holds old versions of the Entropy structs that are no longer used for contract storage. +// However, they are still used in EntropyEvents to maintain the public interface of prior versions of +// the Entropy contract. +// +// See EntropyStructsV2 for the struct definitions currently in use. contract EntropyStructs { struct ProviderInfo { uint128 feeInWei; @@ -37,8 +42,6 @@ contract EntropyStructs { // Maximum number of hashes to record in a request. This should be set according to the maximum gas limit // the provider supports for callbacks. uint32 maxNumHashes; - // Default gas limit to use for callbacks. - uint32 defaultGasLimit; } struct Request { @@ -61,11 +64,7 @@ contract EntropyStructs { address requester; // If true, incorporate the blockhash of blockNumber into the generated random value. bool useBlockhash; - // Status flag for requests with callbacks. See EntropyConstants for the possible values of this flag. - uint8 callbackStatus; - // The gasLimit in units of 10k gas. (i.e., 2 = 20k gas). We're using units of 10k in order to fit this - // field into the remaining 2 bytes of this storage slot. The dynamic range here is 10k - 655M, which should - // cover all real-world use cases. - uint16 gasLimit10k; + // True if this is a request that expects a callback. + bool isRequestWithCallback; } } diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyStructsV2.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructsV2.sol new file mode 100644 index 0000000000..9997a3cb23 --- /dev/null +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructsV2.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +contract EntropyStructsV2 { + struct ProviderInfo { + uint128 feeInWei; + uint128 accruedFeesInWei; + // The commitment that the provider posted to the blockchain, and the sequence number + // where they committed to this. This value is not advanced after the provider commits, + // and instead is stored to help providers track where they are in the hash chain. + bytes32 originalCommitment; + uint64 originalCommitmentSequenceNumber; + // Metadata for the current commitment. Providers may optionally use this field to help + // manage rotations (i.e., to pick the sequence number from the correct hash chain). + bytes commitmentMetadata; + // Optional URI where clients can retrieve revelations for the provider. + // Client SDKs can use this field to automatically determine how to retrieve random values for each provider. + // TODO: specify the API that must be implemented at this URI + bytes uri; + // The first sequence number that is *not* included in the current commitment (i.e., an exclusive end index). + // The contract maintains the invariant that sequenceNumber <= endSequenceNumber. + // If sequenceNumber == endSequenceNumber, the provider must rotate their commitment to add additional random values. + uint64 endSequenceNumber; + // The sequence number that will be assigned to the next inbound user request. + uint64 sequenceNumber; + // The current commitment represents an index/value in the provider's hash chain. + // These values are used to verify requests for future sequence numbers. Note that + // currentCommitmentSequenceNumber < sequenceNumber. + // + // The currentCommitment advances forward through the provider's hash chain as values + // are revealed on-chain. + bytes32 currentCommitment; + uint64 currentCommitmentSequenceNumber; + // An address that is authorized to set / withdraw fees on behalf of this provider. + address feeManager; + // Maximum number of hashes to record in a request. This should be set according to the maximum gas limit + // the provider supports for callbacks. + uint32 maxNumHashes; + // Default gas limit to use for callbacks. + uint32 defaultGasLimit; + } + + struct Request { + // Storage slot 1 // + address provider; + uint64 sequenceNumber; + // The number of hashes required to verify the provider revelation. + uint32 numHashes; + // Storage slot 2 // + // The commitment is keccak256(userCommitment, providerCommitment). Storing the hash instead of both saves 20k gas by + // eliminating 1 store. + bytes32 commitment; + // Storage slot 3 // + // The number of the block where this request was created. + // Note that we're using a uint64 such that we have an additional space for an address and other fields in + // this storage slot. Although block.number returns a uint256, 64 bits should be plenty to index all of the + // blocks ever generated. + uint64 blockNumber; + // The address that requested this random number. + address requester; + // If true, incorporate the blockhash of blockNumber into the generated random value. + bool useBlockhash; + // Status flag for requests with callbacks. See EntropyConstants for the possible values of this flag. + uint8 callbackStatus; + // The gasLimit in units of 10k gas. (i.e., 2 = 20k gas). We're using units of 10k in order to fit this + // field into the remaining 2 bytes of this storage slot. The dynamic range here is 10k - 655M, which should + // cover all real-world use cases. + uint16 gasLimit10k; + } +} diff --git a/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol b/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol index 9d1f13a8c6..55690b2038 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; import "./EntropyEvents.sol"; +import "./EntropyStructsV2.sol"; interface IEntropy is EntropyEvents { // Register msg.sender as a randomness provider. The arguments are the provider's configuration parameters @@ -111,14 +112,14 @@ interface IEntropy is EntropyEvents { function getProviderInfo( address provider - ) external view returns (EntropyStructs.ProviderInfo memory info); + ) external view returns (EntropyStructsV2.ProviderInfo memory info); function getDefaultProvider() external view returns (address provider); function getRequest( address provider, uint64 sequenceNumber - ) external view returns (EntropyStructs.Request memory req); + ) external view returns (EntropyStructsV2.Request memory req); // Get the fee charged by provider for a request with the default gasLimit (`request` or `requestWithCallback`). // If you are calling `requestWithCallbackAndGasLimit`, please use `getFeeForGas`. diff --git a/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyEvents.json b/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyEvents.json index f257fccf35..7d39346333 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyEvents.json +++ b/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyEvents.json @@ -237,11 +237,6 @@ "internalType": "uint32", "name": "maxNumHashes", "type": "uint32" - }, - { - "internalType": "uint32", - "name": "defaultGasLimit", - "type": "uint32" } ], "indexed": false, @@ -294,14 +289,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -378,14 +368,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -438,14 +423,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -522,14 +502,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, diff --git a/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyStructsV2.json b/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyStructsV2.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyStructsV2.json @@ -0,0 +1 @@ +[] diff --git a/target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json b/target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json index 3012caf605..96084f204d 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json +++ b/target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json @@ -237,11 +237,6 @@ "internalType": "uint32", "name": "maxNumHashes", "type": "uint32" - }, - { - "internalType": "uint32", - "name": "defaultGasLimit", - "type": "uint32" } ], "indexed": false, @@ -294,14 +289,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -378,14 +368,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -438,14 +423,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -522,14 +502,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -802,7 +777,7 @@ "type": "uint32" } ], - "internalType": "struct EntropyStructs.ProviderInfo", + "internalType": "struct EntropyStructsV2.ProviderInfo", "name": "info", "type": "tuple" } @@ -873,7 +848,7 @@ "type": "uint16" } ], - "internalType": "struct EntropyStructs.Request", + "internalType": "struct EntropyStructsV2.Request", "name": "req", "type": "tuple" } diff --git a/target_chains/ethereum/entropy_sdk/solidity/package.json b/target_chains/ethereum/entropy_sdk/solidity/package.json index c8fb9e3e84..e0ef46a626 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/package.json +++ b/target_chains/ethereum/entropy_sdk/solidity/package.json @@ -14,7 +14,7 @@ "scripts": { "test:format": "prettier --check .", "fix:format": "prettier --write .", - "build": "generate-abis IEntropy IEntropyConsumer EntropyErrors EntropyEvents EntropyStructs EntropyStatusConstants PRNG", + "build": "generate-abis IEntropy IEntropyConsumer EntropyErrors EntropyEvents EntropyStructs EntropyStructsV2 EntropyStatusConstants PRNG", "test": "git diff --exit-code abis" }, "keywords": [