feat(entropy-v2): request with callback (#1342)

* request with callback

* address comments

* pre-commit

* compilation successful

* pre-commit

* add tests

* generate-abis

* pre-commit

* correct version

* address comments

* pre-commit

* remove unused

* add comments

* pre-commit

* gen abi

* naming consistency

* remove gas limit comment

* requestWithCallback comment

* remove unnecessary asserts

* pre commit

* update request with callback coment

* abis regen

* refactor as per feedback

* abi gen

* rename

* implement ientropyconsumer

* gen abi

* comment entropy consumer

* test fix

* add comment

* reintroduce blockhash

* add error for invalid reveal call

* use getEntropy in entropy consumer

* add test for requestAndRevealWithCallback

* pass through for entropy consumer

* pre commit fix

* abi gen

* address comments

* address feedback

* gen abis

* pre commit run
This commit is contained in:
Dev Kalra 2024-03-12 01:58:57 +05:30 committed by GitHub
parent 926aa55f75
commit e7bf47a18e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 898 additions and 79 deletions

View File

@ -6,6 +6,7 @@ import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol";
import "@pythnetwork/entropy-sdk-solidity/EntropyErrors.sol";
import "@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "./EntropyState.sol";
@ -163,6 +164,54 @@ abstract contract Entropy is IEntropy, EntropyState {
require(sent, "withdrawal to msg.sender failed");
}
// requestHelper allocates and returns a new request for the given provider.
// Note: This method will revert unless the caller provides a sufficient fee
// (at least getFee(provider)) as msg.value.
function requestHelper(
address provider,
bytes32 userCommitment,
bool useBlockhash,
bool isRequestWithCallback
) internal returns (EntropyStructs.Request storage req) {
EntropyStructs.ProviderInfo storage providerInfo = _state.providers[
provider
];
if (_state.providers[provider].sequenceNumber == 0)
revert EntropyErrors.NoSuchProvider();
// Assign a sequence number to the request
uint64 assignedSequenceNumber = providerInfo.sequenceNumber;
if (assignedSequenceNumber >= providerInfo.endSequenceNumber)
revert EntropyErrors.OutOfRandomness();
providerInfo.sequenceNumber += 1;
// Check that fees were paid and increment the pyth / provider balances.
uint128 requiredFee = getFee(provider);
if (msg.value < requiredFee) revert EntropyErrors.InsufficientFee();
providerInfo.accruedFeesInWei += providerInfo.feeInWei;
_state.accruedPythFeesInWei += (SafeCast.toUint128(msg.value) -
providerInfo.feeInWei);
// Store the user's commitment so that we can fulfill the request later.
// Warning: this code needs to overwrite *every* field in the request, because the returned request can be
// filled with arbitrary data.
req = allocRequest(provider, assignedSequenceNumber);
req.provider = provider;
req.sequenceNumber = assignedSequenceNumber;
req.numHashes = SafeCast.toUint32(
assignedSequenceNumber -
providerInfo.currentCommitmentSequenceNumber
);
req.commitment = keccak256(
bytes.concat(userCommitment, providerInfo.currentCommitment)
);
req.requester = msg.sender;
req.blockNumber = SafeCast.toUint64(block.number);
req.useBlockhash = useBlockhash;
req.isRequestWithCallback = isRequestWithCallback;
}
// 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.)
@ -178,89 +227,68 @@ abstract contract Entropy is IEntropy, EntropyState {
bytes32 userCommitment,
bool useBlockHash
) public payable override returns (uint64 assignedSequenceNumber) {
EntropyStructs.ProviderInfo storage providerInfo = _state.providers[
provider
];
if (_state.providers[provider].sequenceNumber == 0)
revert EntropyErrors.NoSuchProvider();
// Assign a sequence number to the request
assignedSequenceNumber = providerInfo.sequenceNumber;
if (assignedSequenceNumber >= providerInfo.endSequenceNumber)
revert EntropyErrors.OutOfRandomness();
providerInfo.sequenceNumber += 1;
// Check that fees were paid and increment the pyth / provider balances.
uint128 requiredFee = getFee(provider);
if (msg.value < requiredFee) revert EntropyErrors.InsufficientFee();
providerInfo.accruedFeesInWei += providerInfo.feeInWei;
_state.accruedPythFeesInWei += (SafeCast.toUint128(msg.value) -
providerInfo.feeInWei);
// Store the user's commitment so that we can fulfill the request later.
// Warning: this code needs to overwrite *every* field in the request, because the returned request can be
// filled with arbitrary data.
EntropyStructs.Request storage req = allocRequest(
EntropyStructs.Request storage req = requestHelper(
provider,
assignedSequenceNumber
userCommitment,
useBlockHash,
false
);
req.provider = provider;
req.sequenceNumber = assignedSequenceNumber;
req.numHashes = SafeCast.toUint32(
assignedSequenceNumber -
providerInfo.currentCommitmentSequenceNumber
);
req.commitment = keccak256(
bytes.concat(userCommitment, providerInfo.currentCommitment)
);
req.requester = msg.sender;
req.blockNumber = SafeCast.toUint64(block.number);
req.useBlockhash = useBlockHash;
assignedSequenceNumber = req.sequenceNumber;
emit Requested(req);
}
// 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.
// Request a random number. The method expects the provider address and a secret random number
// in the arguments. It returns a sequence 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.
// The address calling this function should be a contract that inherits from the IEntropyConsumer interface.
// The `entropyCallback` method on that interface will receive a callback with the generated random number.
//
// This function must be called by the same `msg.sender` that originally requested the random number. This check
// prevents denial-of-service attacks where another actor front-runs the requester's reveal transaction.
function reveal(
// 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 requestWithCallback(
address provider,
uint64 sequenceNumber,
bytes32 userRandomness,
bytes32 providerRevelation
) public override returns (bytes32 randomNumber) {
EntropyStructs.Request storage req = findRequest(
bytes32 userRandomNumber
) public payable override returns (uint64) {
EntropyStructs.Request storage req = requestHelper(
provider,
sequenceNumber
constructUserCommitment(userRandomNumber),
// If useBlockHash is set to true, it allows a scenario in which the provider and miner can collude.
// If we remove the blockHash from this, the provider would have no choice but to provide its committed
// random number. Hence, useBlockHash is set to false.
false,
true
);
// Check that there is an active request for the given provider / sequence number.
if (
req.sequenceNumber == 0 ||
req.provider != provider ||
req.sequenceNumber != sequenceNumber
) revert EntropyErrors.NoSuchRequest();
if (req.requester != msg.sender) revert EntropyErrors.Unauthorized();
emit RequestedWithCallback(
provider,
req.requester,
req.sequenceNumber,
userRandomNumber,
req
);
return req.sequenceNumber;
}
// This method validates the provided user's revelation and provider's revelation against the corresponding
// 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,
bytes32 userRevelation,
bytes32 providerRevelation
) internal returns (bytes32 randomNumber, bytes32 blockHash) {
bytes32 providerCommitment = constructProviderCommitment(
req.numHashes,
providerRevelation
);
bytes32 userCommitment = constructUserCommitment(userRandomness);
bytes32 userCommitment = constructUserCommitment(userRevelation);
if (
keccak256(bytes.concat(userCommitment, providerCommitment)) !=
req.commitment
) revert EntropyErrors.IncorrectRevelation();
bytes32 blockHash = bytes32(uint256(0));
blockHash = bytes32(uint256(0));
if (req.useBlockhash) {
bytes32 _blockHash = blockhash(req.blockNumber);
@ -277,28 +305,110 @@ abstract contract Entropy is IEntropy, EntropyState {
}
randomNumber = combineRandomValues(
userRandomness,
userRevelation,
providerRevelation,
blockHash
);
EntropyStructs.ProviderInfo storage providerInfo = _state.providers[
req.provider
];
if (providerInfo.currentCommitmentSequenceNumber < req.sequenceNumber) {
providerInfo.currentCommitmentSequenceNumber = req.sequenceNumber;
providerInfo.currentCommitment = providerRevelation;
}
}
// 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.
//
// This function must be called by the same `msg.sender` that originally requested the random number. This check
// prevents denial-of-service attacks where another actor front-runs the requester's reveal transaction.
function reveal(
address provider,
uint64 sequenceNumber,
bytes32 userRevelation,
bytes32 providerRevelation
) public override returns (bytes32 randomNumber) {
EntropyStructs.Request storage req = findActiveRequest(
provider,
sequenceNumber
);
if (req.isRequestWithCallback) {
revert EntropyErrors.InvalidRevealCall();
}
if (req.requester != msg.sender) {
revert EntropyErrors.Unauthorized();
}
bytes32 blockHash;
(randomNumber, blockHash) = revealHelper(
req,
userRevelation,
providerRevelation
);
emit Revealed(
req,
userRandomness,
userRevelation,
providerRevelation,
blockHash,
randomNumber
);
clearRequest(provider, sequenceNumber);
}
// Fulfill a request for a random number and call back the requester. This method validates the provided userRandomness
// and provider's revelation against the corresponding commitment in the in-flight request. If both values are validated,
// this function calls the requester's entropyCallback method with the sequence number and the random number as arguments.
//
// 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.
//
// Anyone can call this method to fulfill a request, but the callback will only be made to the original requester.
function revealWithCallback(
address provider,
uint64 sequenceNumber,
bytes32 userRandomNumber,
bytes32 providerRevelation
) public override {
EntropyStructs.Request storage req = findActiveRequest(
provider,
sequenceNumber
);
if (!req.isRequestWithCallback) {
revert EntropyErrors.InvalidRevealCall();
}
bytes32 blockHash;
bytes32 randomNumber;
(randomNumber, blockHash) = revealHelper(
req,
userRandomNumber,
providerRevelation
);
address callAddress = req.requester;
emit RevealedWithCallback(
req,
userRandomNumber,
providerRevelation,
randomNumber
);
clearRequest(provider, sequenceNumber);
EntropyStructs.ProviderInfo storage providerInfo = _state.providers[
provider
];
if (providerInfo.currentCommitmentSequenceNumber < sequenceNumber) {
providerInfo.currentCommitmentSequenceNumber = sequenceNumber;
providerInfo.currentCommitment = providerRevelation;
}
IEntropyConsumer(callAddress)._entropyCallback(
sequenceNumber,
randomNumber
);
}
function getProviderInfo(
@ -408,6 +518,23 @@ abstract contract Entropy is IEntropy, EntropyState {
}
}
// Find an in-flight active request for given the provider and the sequence number.
// This method returns a reference to the request, and will revert if the request is
// not active.
function findActiveRequest(
address provider,
uint64 sequenceNumber
) internal view returns (EntropyStructs.Request storage req) {
req = findRequest(provider, sequenceNumber);
// Check there is an active request for the given provider and sequence number.
if (
!isActive(req) ||
req.provider != provider ||
req.sequenceNumber != sequenceNumber
) revert EntropyErrors.NoSuchRequest();
}
// Find an in-flight request.
// Note that this method can return requests that are not currently active. The caller is responsible for checking
// that the returned request is active (if they care).

View File

@ -105,6 +105,6 @@ contract EntropyUpgradable is
}
function version() public pure returns (string memory) {
return "0.1.0";
return "0.2.0";
}
}

View File

@ -4,13 +4,16 @@ pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol";
import "@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "./utils/EntropyTestUtils.t.sol";
import "../contracts/entropy/EntropyUpgradable.sol";
// TODO
// - fuzz test?
contract EntropyTest is Test, EntropyTestUtils {
contract EntropyTest is Test, EntropyTestUtils, EntropyEvents {
ERC1967Proxy public proxy;
EntropyUpgradable public random;
@ -222,6 +225,12 @@ contract EntropyTest is Test, EntropyTestUtils {
ALL_ZEROS
);
EntropyStructs.Request memory reqAfterReveal = random.getRequest(
provider1,
sequenceNumber
);
assertEq(reqAfterReveal.sequenceNumber, 0);
// You can only reveal the random number once. This isn't a feature of the contract per se, but it is
// the expected behavior.
assertRevealReverts(
@ -729,4 +738,205 @@ contract EntropyTest is Test, EntropyTestUtils {
vm.expectRevert();
random.setProviderUri(newUri);
}
function testRequestWithCallbackAndReveal() public {
bytes32 userRandomNumber = bytes32(uint(42));
uint fee = random.getFee(provider1);
EntropyStructs.ProviderInfo memory providerInfo = random
.getProviderInfo(provider1);
vm.roll(1234);
vm.deal(user1, fee);
vm.startPrank(user1);
vm.expectEmit(false, false, false, true, address(random));
emit RequestedWithCallback(
provider1,
user1,
providerInfo.sequenceNumber,
userRandomNumber,
EntropyStructs.Request({
provider: provider1,
sequenceNumber: providerInfo.sequenceNumber,
numHashes: SafeCast.toUint32(
providerInfo.sequenceNumber -
providerInfo.currentCommitmentSequenceNumber
),
commitment: keccak256(
bytes.concat(
random.constructUserCommitment(userRandomNumber),
providerInfo.currentCommitment
)
),
blockNumber: 1234,
requester: user1,
useBlockhash: false,
isRequestWithCallback: true
})
);
vm.roll(1234);
uint64 assignedSequenceNumber = random.requestWithCallback{value: fee}(
provider1,
userRandomNumber
);
assertEq(
random.getRequest(provider1, assignedSequenceNumber).requester,
user1
);
assertEq(
random.getRequest(provider1, assignedSequenceNumber).provider,
provider1
);
vm.expectRevert(EntropyErrors.InvalidRevealCall.selector);
random.reveal(
provider1,
assignedSequenceNumber,
userRandomNumber,
provider1Proofs[assignedSequenceNumber]
);
vm.stopPrank();
}
function testRequestWithCallbackAndRevealWithCallback() public {
bytes32 userRandomNumber = bytes32(uint(42));
uint fee = random.getFee(provider1);
EntropyConsumer consumer = new EntropyConsumer(address(random));
vm.deal(user1, fee);
vm.prank(user1);
uint64 assignedSequenceNumber = consumer.requestEntropy{value: fee}(
userRandomNumber
);
EntropyStructs.Request memory req = random.getRequest(
provider1,
assignedSequenceNumber
);
bytes32 blockHash = bytes32(uint256(0));
vm.expectEmit(false, false, false, true, address(random));
emit RevealedWithCallback(
req,
userRandomNumber,
provider1Proofs[assignedSequenceNumber],
random.combineRandomValues(
userRandomNumber,
provider1Proofs[assignedSequenceNumber],
0
)
);
vm.prank(user1);
random.revealWithCallback(
provider1,
assignedSequenceNumber,
userRandomNumber,
provider1Proofs[assignedSequenceNumber]
);
assertEq(consumer.sequence(), assignedSequenceNumber);
assertEq(
consumer.randomness(),
random.combineRandomValues(
userRandomNumber,
provider1Proofs[assignedSequenceNumber],
// No blockhash is being used in callback method. As it
// is being depreceated. Passing 0 for it.
0
)
);
EntropyStructs.Request memory reqAfterReveal = random.getRequest(
provider1,
assignedSequenceNumber
);
assertEq(reqAfterReveal.sequenceNumber, 0);
}
function testRequestAndRevealWithCallback() public {
uint64 sequenceNumber = request(user2, provider1, 42, false);
assertEq(random.getRequest(provider1, sequenceNumber).requester, user2);
vm.expectRevert(EntropyErrors.InvalidRevealCall.selector);
vm.prank(user2);
random.revealWithCallback(
provider1,
sequenceNumber,
bytes32(uint256(42)),
provider1Proofs[sequenceNumber]
);
}
function testRequestWithCallbackAndRevealWithCallbackFailing() public {
bytes32 userRandomNumber = bytes32(uint(42));
uint fee = random.getFee(provider1);
EntropyConsumerFails consumer = new EntropyConsumerFails(
address(random)
);
vm.deal(address(consumer), fee);
vm.startPrank(address(consumer));
uint64 assignedSequenceNumber = random.requestWithCallback{value: fee}(
provider1,
userRandomNumber
);
vm.expectRevert();
random.revealWithCallback(
provider1,
assignedSequenceNumber,
userRandomNumber,
provider1Proofs[assignedSequenceNumber]
);
}
}
contract EntropyConsumer is IEntropyConsumer {
uint64 public sequence;
bytes32 public randomness;
address public entropy;
constructor(address _entropy) {
entropy = _entropy;
}
function requestEntropy(
bytes32 randomNumber
) public payable returns (uint64 sequenceNumber) {
address provider = IEntropy(entropy).getDefaultProvider();
sequenceNumber = IEntropy(entropy).requestWithCallback{
value: msg.value
}(provider, randomNumber);
}
function getEntropy() internal view override returns (address) {
return entropy;
}
function entropyCallback(
uint64 _sequence,
bytes32 _randomness
) internal override {
sequence = _sequence;
randomness = _randomness;
}
}
contract EntropyConsumerFails is IEntropyConsumer {
uint64 public sequence;
bytes32 public randomness;
address public entropy;
constructor(address _entropy) {
entropy = _entropy;
}
function getEntropy() internal view override returns (address) {
return entropy;
}
function entropyCallback(
uint64 _sequence,
bytes32 _randomness
) internal override {
revert("Callback failed");
}
}

View File

@ -34,4 +34,8 @@ library EntropyErrors {
// The blockhash is 0.
// Signature: 0x92555c0e
error BlockhashUnavailable();
// if a request was made using `requestWithCallback`, request should be fulfilled using `revealWithCallback`
// else if a request was made using `request`, request should be fulfilled using `reveal`
// Signature: 0x50f0dc92
error InvalidRevealCall();
}

View File

@ -7,6 +7,13 @@ interface EntropyEvents {
event Registered(EntropyStructs.ProviderInfo provider);
event Requested(EntropyStructs.Request request);
event RequestedWithCallback(
address indexed provider,
address indexed requestor,
uint64 indexed sequenceNumber,
bytes32 userRandomNumber,
EntropyStructs.Request request
);
event Revealed(
EntropyStructs.Request request,
@ -15,6 +22,12 @@ interface EntropyEvents {
bytes32 blockHash,
bytes32 randomNumber
);
event RevealedWithCallback(
EntropyStructs.Request request,
bytes32 userRandomNumber,
bytes32 providerRevelation,
bytes32 randomNumber
);
event ProviderFeeUpdated(address provider, uint128 oldFee, uint128 newFee);

View File

@ -54,6 +54,8 @@ contract EntropyStructs {
address requester;
// If true, incorporate the blockhash of blockNumber into the generated random value.
bool useBlockhash;
// There are 3 remaining bytes of free space in this slot.
// If true, the requester will be called back with the generated random value.
bool isRequestWithCallback;
// There are 2 remaining bytes of free space in this slot.
}
}

View File

@ -38,7 +38,20 @@ interface IEntropy is EntropyEvents {
bool useBlockHash
) external payable returns (uint64 assignedSequenceNumber);
// Fulfill a request for a random number. This method validates the provided userRandomness and provider's proof
// Request a random number. The method expects the provider address and a secret random number
// in the arguments. It returns a sequence number.
//
// The address calling this function should be a contract that inherits from the IEntropyConsumer interface.
// The `entropyCallback` method on that interface will receive a callback with the generated 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 requestWithCallback(
address provider,
bytes32 userRandomNumber
) external payable returns (uint64 assignedSequenceNumber);
// Fulfill a request for a random number. This method validates the provided userRevelation 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.
//
@ -48,10 +61,26 @@ interface IEntropy is EntropyEvents {
function reveal(
address provider,
uint64 sequenceNumber,
bytes32 userRandomness,
bytes32 userRevelation,
bytes32 providerRevelation
) external returns (bytes32 randomNumber);
// Fulfill a request for a random number and call back the requester. This method validates the provided userRandomness
// and provider's revelation against the corresponding commitment in the in-flight request. If both values are validated,
// this function calls the requester's entropyCallback method with the sequence number and the random number as arguments.
//
// 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.
//
// Anyone can call this method to fulfill a request, but the callback will only be made to the original requester.
function revealWithCallback(
address provider,
uint64 sequenceNumber,
bytes32 userRandomNumber,
bytes32 providerRevelation
) external;
function getProviderInfo(
address provider
) external view returns (EntropyStructs.ProviderInfo memory info);

View File

@ -0,0 +1,28 @@
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
abstract contract IEntropyConsumer {
// This method is called by Entropy to provide the random number to the consumer.
// It asserts that the msg.sender is the Entropy contract. It is not meant to be
// override by the consumer.
function _entropyCallback(uint64 sequence, bytes32 randomNumber) external {
address entropy = getEntropy();
require(entropy != address(0), "Entropy address not set");
require(msg.sender == entropy, "Only Entropy can call this function");
entropyCallback(sequence, randomNumber);
}
// getEntropy returns Entropy contract address. The method is being used to check that the
// callback is indeed from Entropy contract. The consumer is expected to implement this method.
// Entropy address can be found here - https://docs.pyth.network/entropy/contract-addresses
function getEntropy() internal view virtual returns (address);
// This method is expected to be implemented by the consumer to handle the random number.
// It will be called by _entropyCallback after _entropyCallback ensures that the call is
// indeed from Entropy contract.
function entropyCallback(
uint64 sequence,
bytes32 randomNumber
) internal virtual;
}

View File

@ -19,6 +19,11 @@
"name": "InsufficientFee",
"type": "error"
},
{
"inputs": [],
"name": "InvalidRevealCall",
"type": "error"
},
{
"inputs": [],
"name": "InvalidUpgradeMagic",

View File

@ -153,6 +153,11 @@
"internalType": "bool",
"name": "useBlockhash",
"type": "bool"
},
{
"internalType": "bool",
"name": "isRequestWithCallback",
"type": "bool"
}
],
"indexed": false,
@ -164,6 +169,85 @@
"name": "Requested",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "provider",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "requestor",
"type": "address"
},
{
"indexed": true,
"internalType": "uint64",
"name": "sequenceNumber",
"type": "uint64"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "userRandomNumber",
"type": "bytes32"
},
{
"components": [
{
"internalType": "address",
"name": "provider",
"type": "address"
},
{
"internalType": "uint64",
"name": "sequenceNumber",
"type": "uint64"
},
{
"internalType": "uint32",
"name": "numHashes",
"type": "uint32"
},
{
"internalType": "bytes32",
"name": "commitment",
"type": "bytes32"
},
{
"internalType": "uint64",
"name": "blockNumber",
"type": "uint64"
},
{
"internalType": "address",
"name": "requester",
"type": "address"
},
{
"internalType": "bool",
"name": "useBlockhash",
"type": "bool"
},
{
"internalType": "bool",
"name": "isRequestWithCallback",
"type": "bool"
}
],
"indexed": false,
"internalType": "struct EntropyStructs.Request",
"name": "request",
"type": "tuple"
}
],
"name": "RequestedWithCallback",
"type": "event"
},
{
"anonymous": false,
"inputs": [
@ -203,6 +287,11 @@
"internalType": "bool",
"name": "useBlockhash",
"type": "bool"
},
{
"internalType": "bool",
"name": "isRequestWithCallback",
"type": "bool"
}
],
"indexed": false,
@ -237,5 +326,78 @@
],
"name": "Revealed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "provider",
"type": "address"
},
{
"internalType": "uint64",
"name": "sequenceNumber",
"type": "uint64"
},
{
"internalType": "uint32",
"name": "numHashes",
"type": "uint32"
},
{
"internalType": "bytes32",
"name": "commitment",
"type": "bytes32"
},
{
"internalType": "uint64",
"name": "blockNumber",
"type": "uint64"
},
{
"internalType": "address",
"name": "requester",
"type": "address"
},
{
"internalType": "bool",
"name": "useBlockhash",
"type": "bool"
},
{
"internalType": "bool",
"name": "isRequestWithCallback",
"type": "bool"
}
],
"indexed": false,
"internalType": "struct EntropyStructs.Request",
"name": "request",
"type": "tuple"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "userRandomNumber",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "providerRevelation",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "randomNumber",
"type": "bytes32"
}
],
"name": "RevealedWithCallback",
"type": "event"
}
]

View File

@ -153,6 +153,11 @@
"internalType": "bool",
"name": "useBlockhash",
"type": "bool"
},
{
"internalType": "bool",
"name": "isRequestWithCallback",
"type": "bool"
}
],
"indexed": false,
@ -164,6 +169,85 @@
"name": "Requested",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "provider",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "requestor",
"type": "address"
},
{
"indexed": true,
"internalType": "uint64",
"name": "sequenceNumber",
"type": "uint64"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "userRandomNumber",
"type": "bytes32"
},
{
"components": [
{
"internalType": "address",
"name": "provider",
"type": "address"
},
{
"internalType": "uint64",
"name": "sequenceNumber",
"type": "uint64"
},
{
"internalType": "uint32",
"name": "numHashes",
"type": "uint32"
},
{
"internalType": "bytes32",
"name": "commitment",
"type": "bytes32"
},
{
"internalType": "uint64",
"name": "blockNumber",
"type": "uint64"
},
{
"internalType": "address",
"name": "requester",
"type": "address"
},
{
"internalType": "bool",
"name": "useBlockhash",
"type": "bool"
},
{
"internalType": "bool",
"name": "isRequestWithCallback",
"type": "bool"
}
],
"indexed": false,
"internalType": "struct EntropyStructs.Request",
"name": "request",
"type": "tuple"
}
],
"name": "RequestedWithCallback",
"type": "event"
},
{
"anonymous": false,
"inputs": [
@ -203,6 +287,11 @@
"internalType": "bool",
"name": "useBlockhash",
"type": "bool"
},
{
"internalType": "bool",
"name": "isRequestWithCallback",
"type": "bool"
}
],
"indexed": false,
@ -238,6 +327,79 @@
"name": "Revealed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "provider",
"type": "address"
},
{
"internalType": "uint64",
"name": "sequenceNumber",
"type": "uint64"
},
{
"internalType": "uint32",
"name": "numHashes",
"type": "uint32"
},
{
"internalType": "bytes32",
"name": "commitment",
"type": "bytes32"
},
{
"internalType": "uint64",
"name": "blockNumber",
"type": "uint64"
},
{
"internalType": "address",
"name": "requester",
"type": "address"
},
{
"internalType": "bool",
"name": "useBlockhash",
"type": "bool"
},
{
"internalType": "bool",
"name": "isRequestWithCallback",
"type": "bool"
}
],
"indexed": false,
"internalType": "struct EntropyStructs.Request",
"name": "request",
"type": "tuple"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "userRandomNumber",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "providerRevelation",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "randomNumber",
"type": "bytes32"
}
],
"name": "RevealedWithCallback",
"type": "event"
},
{
"inputs": [
{
@ -453,6 +615,11 @@
"internalType": "bool",
"name": "useBlockhash",
"type": "bool"
},
{
"internalType": "bool",
"name": "isRequestWithCallback",
"type": "bool"
}
],
"internalType": "struct EntropyStructs.Request",
@ -525,6 +692,30 @@
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "provider",
"type": "address"
},
{
"internalType": "bytes32",
"name": "userRandomNumber",
"type": "bytes32"
}
],
"name": "requestWithCallback",
"outputs": [
{
"internalType": "uint64",
"name": "assignedSequenceNumber",
"type": "uint64"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
@ -539,7 +730,7 @@
},
{
"internalType": "bytes32",
"name": "userRandomness",
"name": "userRevelation",
"type": "bytes32"
},
{
@ -559,6 +750,34 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "provider",
"type": "address"
},
{
"internalType": "uint64",
"name": "sequenceNumber",
"type": "uint64"
},
{
"internalType": "bytes32",
"name": "userRandomNumber",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "providerRevelation",
"type": "bytes32"
}
],
"name": "revealWithCallback",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{

View File

@ -0,0 +1,20 @@
[
{
"inputs": [
{
"internalType": "uint64",
"name": "sequence",
"type": "uint64"
},
{
"internalType": "bytes32",
"name": "randomNumber",
"type": "bytes32"
}
],
"name": "_entropyCallback",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

View File

@ -1,6 +1,6 @@
{
"name": "@pythnetwork/entropy-sdk-solidity",
"version": "1.1.3",
"version": "1.2.0",
"description": "Generate secure random numbers with Pyth Entropy",
"repository": {
"type": "git",
@ -12,7 +12,7 @@
},
"scripts": {
"format": "npx prettier --write .",
"generate-abi": "npx generate-abis IEntropy EntropyErrors EntropyEvents EntropyStructs",
"generate-abi": "npx generate-abis IEntropy IEntropyConsumer EntropyErrors EntropyEvents EntropyStructs",
"check-abi": "git diff --exit-code abis"
},
"keywords": [