Skip to content

Commit df21cd9

Browse files
authored
Merge pull request #3 from randa-mu/feat/randomness
Add randomness consumer contract and deployment script, and readme updates
2 parents 2c3a30d + 3662421 commit df21cd9

12 files changed

+125
-26
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ ignition/deployments/chain-31337
1919

2020
# Foundry files
2121
/broadcast
22+
23+
# contract interaction files
24+
foundry.txt

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77
[submodule "lib/blocklock-solidity"]
88
path = lib/blocklock-solidity
99
url = https://github.com/randa-mu/blocklock-solidity
10+
[submodule "lib/randomness-solidity"]
11+
path = lib/randomness-solidity
12+
url = https://github.com/randa-mu/randomness-solidity

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ source .env
5959
forge script script/SealedBidAuction.s.sol --rpc-url $CALIBRATION_TESTNET_RPC_URL --private-key $CALIBRATION_TESTNET_PRIVATE_KEY --broadcast
6060
```
6161

62+
For common deployment issues (e.g., setting the gas estimate multiplier -g, --gas-estimate-multiplier) with the Filecoin calibration testnet, please refer to the [FEVM (Ethereum Virtual Machine on Filecoin) Foundry toolkit](https://github.com/filecoin-project/fevm-foundry-kit).
63+
6264

6365
## How It Works
6466
1. Encrypting Your Bid (Off-Chain): Bidders encrypt their bid amounts off-chain, generating ciphertexts for submission.
@@ -75,6 +77,5 @@ forge script script/SealedBidAuction.s.sol --rpc-url $CALIBRATION_TESTNET_RPC_UR
7577
Check out the detailed tutorial on [our blog](https://drand.love/blog/2025/03/04/onchain-sealed-bid-auction/).
7678

7779

78-
7980
## Licensing
8081
This source code is licensed under the MIT License which can be accessed [here](LICENSE).

lib/randomness-solidity

Submodule randomness-solidity added at d9d1057

remappings.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ config/=config/
77

88
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
99
@blocklock-solidity/src/=lib/blocklock-solidity/src/
10+
@randomness-solidity/src/=lib/randomness-solidity/src/

script/RandomnessConsumer.s.sol

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.28;
3+
4+
import {Script} from "forge-std/Script.sol";
5+
import {console} from "forge-std/console.sol";
6+
7+
import {RandomnessConsumer} from "src/RandomnessConsumer.sol";
8+
9+
contract RandomnessConsumerScript is Script {
10+
function run() external {
11+
vm.broadcast();
12+
RandomnessConsumer randomnessConsumer = new RandomnessConsumer();
13+
14+
console.log("RandomnessConsumer deployed at: ", address(randomnessConsumer));
15+
}
16+
}

script/SealedBidAuction.s.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ import {SealedBidAuction} from "src/SealedBidAuction.sol";
88

99
contract SealedBidAuctionScript is Script {
1010
function run() external {
11-
uint256 biddingDurationInBlocks = block.number + 200;
11+
console.log("Current chain height: ", block.number);
12+
13+
uint256 biddingDurationInBlocks = block.number + 10;
1214
address blocklockSenderContractAddress = 0xfF66908E1d7d23ff62791505b2eC120128918F44;
1315

16+
console.log("Bid closes at chain height: ", biddingDurationInBlocks);
17+
1418
vm.broadcast();
1519
SealedBidAuction auction = new SealedBidAuction(biddingDurationInBlocks, blocklockSenderContractAddress);
1620

src/RandomnessConsumer.sol

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.28;
3+
4+
import {RandomnessReceiverBase} from "@randomness-solidity/src/RandomnessReceiverBase.sol";
5+
6+
/// @title Randomness Consumer contract
7+
/// @author Randamu developers
8+
/// @notice A mock RandomnessConsumer contract that makes use of the Randamu randomness-solidity
9+
/// library to request verifiable randomness
10+
/// The library is available at: https://github.com/randa-mu/randomness-solidity/tree/main
11+
/// The library also contains logic for array shuffling using Feistel shuffle
12+
/// and selecting n items randomly from an array
13+
contract RandomnessConsumer is RandomnessReceiverBase {
14+
// RandomnessSender proxy address on Filecoin calibration testnet
15+
address public constant RANDOMNESS_SENDER = 0x9c789bc7F2B5c6619Be1572A39F2C3d6f33001dC;
16+
17+
bytes32 public randomness;
18+
uint256 public requestId;
19+
20+
constructor() RandomnessReceiverBase(RANDOMNESS_SENDER) {}
21+
22+
/// @dev Requests randomness.
23+
/// This function calls the `requestRandomness` method to request a random value.
24+
/// The `requestId` is updated with the ID returned from the randomness request.
25+
function rollDice() external {
26+
requestId = requestRandomness();
27+
}
28+
29+
/// @dev Callback function that is called when randomness is received.
30+
/// @param requestID The ID of the randomness request that was made.
31+
/// @param _randomness The random value received.
32+
/// This function verifies that the received `requestID` matches the one that
33+
/// was previously stored. If they match, it updates the `randomness` state variable
34+
/// with the newly received random value.
35+
/// Reverts if the `requestID` does not match the stored `requestId`, ensuring that
36+
/// the randomness is received in response to a valid request.
37+
function onRandomnessReceived(uint256 requestID, bytes32 _randomness) internal override {
38+
require(requestId == requestID, "Request ID mismatch");
39+
randomness = _randomness;
40+
}
41+
}

src/SealedBidAuction.sol

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,21 @@ import {AbstractBlocklockReceiver} from "@blocklock-solidity/src/AbstractBlocklo
88
// Import ReentrancyGuard which is an Openzeppelin solidity library that helps prevent reentrant calls to a function.
99
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
1010

11+
/// @title Sealed Bid Auction interface
12+
/// @author Randamu developers
13+
/// @notice An interface for a sealed bid auction smart contract
14+
/// allowing encrypted bids which are decrypted at a future block number.
1115
interface ISealedBidAuction {
1216
function placeSealedBid(TypesLib.Ciphertext calldata sealedBid) external payable returns (uint256);
1317
function withdrawRefund() external;
1418
function fulfillHighestBid() external payable;
1519
function finalizeAuction() external;
1620
}
1721

22+
/// @title Sealed Bid Auction contract
23+
/// @author Randamu developers
24+
/// @notice A bid smart contract which allows sealed bids to be made in an auction.
25+
/// The bids are automatically decrypted at a future block number (block number that closes the bidding window).
1826
contract SealedBidAuction is ISealedBidAuction, AbstractBlocklockReceiver, ReentrancyGuard {
1927
struct Bid {
2028
uint256 bidID; // Unique identifier for the bid
@@ -77,11 +85,12 @@ contract SealedBidAuction is ISealedBidAuction, AbstractBlocklockReceiver, Reent
7785
seller = msg.sender;
7886
}
7987

80-
// BID PHASES
81-
/**
82-
* Phase 1. Bidding Phase
83-
*/
84-
// Submit a sealed bid
88+
/// BID PHASES
89+
90+
/// @dev Phase 1. Bidding Phase
91+
/// @notice Submit a sealed bid to participate in the auction.
92+
/// @param sealedBid The encrypted bid amount to submit to the auction.
93+
/// @return bidID The unique identifier for the submitted bid.
8594
function placeSealedBid(TypesLib.Ciphertext calldata sealedBid)
8695
external
8796
payable
@@ -111,10 +120,10 @@ contract SealedBidAuction is ISealedBidAuction, AbstractBlocklockReceiver, Reent
111120
return bidID;
112121
}
113122

114-
/**
115-
* Phase 2. Reveal Phase
116-
*/
117-
// Unseal sealed bid
123+
/// @dev Phase 2. Reveal Phase
124+
/// @notice Unseals the sealed bid after the bidding phase has ended.
125+
/// @param requestID The unique identifier for the bid to unseal.
126+
/// @param decryptionKey The key used to decrypt the sealed bid.
118127
function receiveBlocklock(uint256 requestID, bytes calldata decryptionKey)
119128
external
120129
override
@@ -140,6 +149,9 @@ contract SealedBidAuction is ISealedBidAuction, AbstractBlocklockReceiver, Reent
140149
emit BidUnsealed(bid.bidID, bid.bidder, bid.unsealedBid);
141150
}
142151

152+
/// @dev Updates the highest bid during the reveal phase.
153+
/// @param bidID The unique identifier of the bid being evaluated.
154+
/// @param unsealedBid The decrypted bid amount.
143155
function updateHighestBid(uint256 bidID, uint256 unsealedBid) internal {
144156
Bid storage bid = bidsById[bidID];
145157

@@ -155,10 +167,8 @@ contract SealedBidAuction is ISealedBidAuction, AbstractBlocklockReceiver, Reent
155167
emit BidUnsealed(bidID, bid.bidder, unsealedBid);
156168
}
157169

158-
/**
159-
* Phase 3. Auction Finalization
160-
*/
161-
// Withdraw refundable reserve amounts paid during bidding
170+
/// @dev Phase 3. Auction Finalization
171+
/// @notice Allows bidders (except the highest bidder) to withdraw refundable reserve amounts.
162172
function withdrawRefund() external onlyAfter(biddingEndBlock) onlyAfterBidsUnsealed nonReentrant {
163173
require(msg.sender != highestBidder, "Highest bidder cannot withdraw refund.");
164174
Bid memory bid = bids[msg.sender];
@@ -169,7 +179,8 @@ contract SealedBidAuction is ISealedBidAuction, AbstractBlocklockReceiver, Reent
169179
emit ReservePriceClaimed(bid.bidID, msg.sender, amount);
170180
}
171181

172-
// Fulfil highest bid
182+
/// @dev Fulfill the highest bid after the bidding ends and bids are unsealed.
183+
/// @notice Only the highest bidder can fulfill the bid and pay the difference.
173184
function fulfillHighestBid() external payable onlyAfter(biddingEndBlock) onlyAfterBidsUnsealed {
174185
require(highestBid > 0, "Highest bid is zero.");
175186
require(msg.sender == highestBidder, "Only the highest bidder can fulfil.");
@@ -183,16 +194,23 @@ contract SealedBidAuction is ISealedBidAuction, AbstractBlocklockReceiver, Reent
183194
emit HighestBidFulfilled(msg.sender, msg.value + RESERVE_PRICE);
184195
}
185196

186-
// Finalize auction
197+
/// @dev Finalizes the auction and declares the winner.
198+
/// @notice Marks the auction as ended and emits an event with the winning bid and bidder.
187199
function finalizeAuction() external onlyAfterBidsUnsealed {
188200
require(!auctionEnded, "Auction already finalised.");
189201
auctionEnded = true;
190202
emit AuctionEnded(highestBidder, highestBid);
191203
}
192204

193-
/**
194-
* Getters
195-
*/
205+
/// GETTERS
206+
207+
/// @dev Retrieves bid details using the bid ID.
208+
/// @param bidID The unique identifier for the bid.
209+
/// @return sealedBid The encrypted sealed bid.
210+
/// @return decryptionKey The decryption key used for revealing the bid.
211+
/// @return unsealedBid The decrypted bid amount.
212+
/// @return bidder The address of the bidder.
213+
/// @return revealed Whether the bid has been revealed.
196214
function getBidWithBidID(uint256 bidID)
197215
external
198216
view
@@ -211,6 +229,13 @@ contract SealedBidAuction is ISealedBidAuction, AbstractBlocklockReceiver, Reent
211229
revealed = bidsById[bidID].revealed;
212230
}
213231

232+
/// @dev Retrieves bid details using the bidder's address.
233+
/// @param bidder The address of the bidder.
234+
/// @return sealedBid The encrypted sealed bid.
235+
/// @return decryptionKey The decryption key used for revealing the bid.
236+
/// @return unsealedBid The decrypted bid amount.
237+
/// @return _bidder The address of the bidder.
238+
/// @return revealed Whether the bid has been revealed.
214239
function getBidWithBidder(address bidder)
215240
external
216241
view
@@ -229,6 +254,9 @@ contract SealedBidAuction is ISealedBidAuction, AbstractBlocklockReceiver, Reent
229254
revealed = bidsById[bidderToBidID[bidder]].revealed;
230255
}
231256

257+
/// @dev Retrieves the current highest bid and bidder's address.
258+
/// @return highestBidAmount The amount of the highest bid.
259+
/// @return highestBidderAddress The address of the highest bidder.
232260
function getHighestBid() external view returns (uint256 highestBidAmount, address highestBidderAddress) {
233261
highestBidAmount = highestBid;
234262
highestBidderAddress = highestBidder;

0 commit comments

Comments
 (0)