Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kevincharm/solshuffle
Gas-efficient stateless shuffle on Ethereum
https://github.com/kevincharm/solshuffle
ethereum evm feistel permutation shuffle smart-contracts solidity yul
Last synced: about 4 hours ago
JSON representation
Gas-efficient stateless shuffle on Ethereum
- Host: GitHub
- URL: https://github.com/kevincharm/solshuffle
- Owner: kevincharm
- License: mit
- Created: 2022-09-21T15:12:00.000Z (about 2 years ago)
- Default Branch: master
- Last Pushed: 2024-06-30T15:31:28.000Z (4 months ago)
- Last Synced: 2024-11-01T15:41:11.705Z (7 days ago)
- Topics: ethereum, evm, feistel, permutation, shuffle, smart-contracts, solidity, yul
- Language: Solidity
- Homepage:
- Size: 63.5 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# π solshuffle π
Gas-efficient stateless shuffle implemented in Solidity/Yul, for all your onchain permutation needs.
πππππππππππππππ
```sh
npm install solshuffle
```πππππππππππππππ
## 1) What
You've probably tried writing a raffle in Solidity. How much does it cost to pick 10 winners? 100? 1000? Probably millions of gas. Using `solshuffle`, you can determine the draw sequence of the user at the time of claiming. Combine this with a Merkle tree and you can have extremely efficient raffles (think cutting 10M gas down to <100k gas). Check out [my talk at EthCC](https://www.youtube.com/watch?v=d7C1pLKM_Oc) to learn how you can do extremely gas-efficient raffles with the [Lazy Merkle Raffle](https://docs.fairy.dev/theory/lazy-merkle-raffle).
Another application for `solshuffle` is to shuffle NFT token identifiers. You've probably seen NFT contracts that simply add a randomised offset and call that a "shuffle". Now you can stop faking it and actually shuffle your token identifiers.
Shoutout to [@rpal\_](https://twitter.com/rpal_) for shilling me cool shuffle algos!
Find the accompanying TypeScript library (and reference implementation) [here](https://github.com/kevincharm/gfc-fpe).
## Usage
### Example: Just-in-time NFT tokenId<->metadata shuffle
> Difficulty level: SHADOWY SUPER CODER π₯·
Shown below is a truncated example of how you'd shuffle your ERC721 metadata using the `FeistelShuffleOptimised` library, after calling a VRF (or whatever) to set a `randomSeed`.
```solidity
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { ERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import { FeistelShuffleOptimised } from "solshuffle/contracts/FeistelShuffleOptimised.sol";contract ERC721Shuffled is ERC721, ERC721Enumerable {
using Strings for uint256;/// @notice The first token ID. For most NFT collections, this is 1.
uint256 public constant FIRST_TOKEN_ID = 1;
/// @notice The max supply is used as the modulus parameter in the shuffle algos.
uint256 public immutable maxSupply;
/// @notice Random seed that determines the permutation of the shuffle,
/// should only be set once.
bytes32 public randomSeed;/// @notice Return a shuffled tokenURI, calculated just-in-time when this function
/// is called
/// @param tokenId token id
/// @return URI pointing to metadata
function tokenURI(
uint256 tokenId
) public view virtual override returns (string memory) {
require(randomSeed != 0, "random seed must be initialised!!!");
_requireMinted(tokenId);// statelessly map tokenId -> shuffled tokenId,
// deterministically according to the `randomSeed` and `rounds` parameters
uint256 shuffledTokenId = FIRST_TOKEN_ID +
FeistelShuffleOptimised.shuffle(
tokenId -
FIRST_TOKEN_ID /** shuffle is 0-indexed, so we add offsets */,
maxSupply /** Must stay constant */,
uint256(randomSeed) /** Must stay constant (once set) */,
4 /** Must stay constant */
);// use the shuffled tokenId as the metadata index
return string(abi.encodePacked(_baseURI(), shuffledTokenId.toString()));
}
}
```## Specifications
The stateless shuffle implemented by `solshuffle` is the Generalised Feistel Cipher, but we'll just call it the Feistel Shuffle. The Feistel shuffle is cheap, coming in at ~4350 gas to calculate a permutation for a single index for a list size of 10,000.
Feistel networks are based on _round functions_, and these are run a fixed number of times, as specified in the `rounds` parameter. As long as you input a cryptographically secure random `seed`, it is sufficient to set `rounds = 4` to make a _strong_ pseudorandom permutation [[1]](#m-luby-and-c-rackoff-1988).
The figure below shows the distribution of shuffled indices (y-axis) against their original indices (x-axis) when picking $y \mid 0 \leq y \lt 1000$ with `modulus = 10_000`. Each colour represents a different run, with its own 32-byte cryptorandom seed. Every run sets `rounds = 4`. Re-run this for yourself with `yarn plot:feistel`.
![feistel_1000_1000](https://user-images.githubusercontent.com/10385659/193012477-60f74cef-c7eb-4a91-ad93-30ee6c7ab4c6.png)
### Gas Benchmarks
```
domain = 96_722
βββββββββββββββββββββββββββ¬βββββββββ¬βββββββ¬ββββββββ¬βββββββ
β (index) β rounds β min β max β avg β
βββββββββββββββββββββββββββΌβββββββββΌβββββββΌββββββββΌβββββββ€
β FeistelShuffleOptimised β 4 β 4008 β 5430 β 4040 β
β FeistelShuffle β 4 β 7255 β 11786 β 7297 β
βββββββββββββββββββββββββββ΄βββββββββ΄βββββββ΄ββββββββ΄βββββββ
```## Security
This repository has not received an individual security audit. However, both `FeistelShuffle.sol` and `FeistelShuffleOptimised.sol` were audited by Trail of Bits as part of the Ethereum Foundation's [Devcon Auction-Raffle contracts](https://github.com/efdevcon/devcon-raffle). [View the audit here](https://github.com/efdevcon/devcon-raffle/blob/849ad0b18e48a10900c37a5275e5b16b997abf59/audits/Ethereum%20Foundation%20Devcon%20Auction-Raffle%20Summary%20Report.pdf).
## License
This library is permissively licenced with the MIT license. Send tokens to `kevincharm.eth` if you find the library useful for your project :^)
## Disclaimer
Ensure you understand the theory behind the [Generalised Feistel Cipher](https://github.com/kevincharm/gfc-fpe/blob/master/README.md), such as the iteration upper bounds, which may consume more gas than the expected average in unlucky scenarios.
## WEN TOKEN?
soonβ’
## References
[1] M. Luby and C. Rackoff, βHow to construct pseudorandom permutations from pseudorandom functions,β SIAM Journal on Computing, vol. 17, no. 2, pp. 373β386, 1988.
[2] V. T. Hoang, B. Morris, and P. Rogaway, βAn enciphering scheme based on a card shuffle,β in Annual Cryptology Conference, 2012, pp. 1β13.