From a45c6120a47ff8327f23f554a94e0d99108923e2 Mon Sep 17 00:00:00 2001 From: dumprahul Date: Sun, 26 Oct 2025 22:30:17 +0530 Subject: [PATCH] Add WhipHash: Secure password generator using Pyth Entropy --- entropy/whiphash/README.md | 269 + entropy/whiphash/abi/DeployRandomnessGen.json | 1 + .../app/api/nildb/read-collection/route.ts | 153 + .../app/api/nildb/store-password/route.ts | 313 + .../app/api/nildb/test-config/route.ts | 64 + entropy/whiphash/app/favicon.ico | Bin 0 -> 25931 bytes entropy/whiphash/app/globals.css | 151 + entropy/whiphash/app/layout.tsx | 27 + entropy/whiphash/app/page.tsx | 73 + entropy/whiphash/app/providers.tsx | 22 + entropy/whiphash/app/test/page.tsx | 1029 ++ entropy/whiphash/app/view/page.tsx | 299 + entropy/whiphash/components.json | 24 + entropy/whiphash/components/Dither.css | 5 + entropy/whiphash/components/Dither.jsx | 295 + entropy/whiphash/components/Prism.css | 5 + entropy/whiphash/components/Prism.jsx | 436 + entropy/whiphash/components/ShinyText.css | 27 + entropy/whiphash/components/ShinyText.jsx | 13 + entropy/whiphash/components/SplitText.tsx | 207 + .../whiphash/components/ui/shimmer-button.tsx | 96 + .../whiphash/components/ui/shine-border.tsx | 63 + entropy/whiphash/demo-extension/README.md | 81 + .../whiphash/demo-extension/icons/icon.svg | 29 + entropy/whiphash/demo-extension/install.sh | 52 + entropy/whiphash/demo-extension/manifest.json | 15 + entropy/whiphash/demo-extension/popup.html | 147 + entropy/whiphash/demo-extension/popup.js | 164 + entropy/whiphash/demo-extension/test.html | 110 + entropy/whiphash/eslint.config.mjs | 18 + entropy/whiphash/genpassword.md | 38 + entropy/whiphash/lib/nildb-client.ts | 0 entropy/whiphash/lib/utils.ts | 6 + entropy/whiphash/next-env.d.ts | 6 + entropy/whiphash/next.config.ts | 7 + entropy/whiphash/package-lock.json | 12262 ++++++++++++++++ entropy/whiphash/package.json | 50 + entropy/whiphash/postcss.config.mjs | 7 + entropy/whiphash/public/file.svg | 1 + entropy/whiphash/public/globe.svg | 1 + entropy/whiphash/public/next.svg | 1 + entropy/whiphash/public/vercel.svg | 1 + entropy/whiphash/public/window.svg | 1 + entropy/whiphash/tsconfig.json | 35 + 44 files changed, 16604 insertions(+) create mode 100644 entropy/whiphash/README.md create mode 100644 entropy/whiphash/abi/DeployRandomnessGen.json create mode 100644 entropy/whiphash/app/api/nildb/read-collection/route.ts create mode 100644 entropy/whiphash/app/api/nildb/store-password/route.ts create mode 100644 entropy/whiphash/app/api/nildb/test-config/route.ts create mode 100644 entropy/whiphash/app/favicon.ico create mode 100644 entropy/whiphash/app/globals.css create mode 100644 entropy/whiphash/app/layout.tsx create mode 100644 entropy/whiphash/app/page.tsx create mode 100644 entropy/whiphash/app/providers.tsx create mode 100644 entropy/whiphash/app/test/page.tsx create mode 100644 entropy/whiphash/app/view/page.tsx create mode 100644 entropy/whiphash/components.json create mode 100644 entropy/whiphash/components/Dither.css create mode 100644 entropy/whiphash/components/Dither.jsx create mode 100644 entropy/whiphash/components/Prism.css create mode 100644 entropy/whiphash/components/Prism.jsx create mode 100644 entropy/whiphash/components/ShinyText.css create mode 100644 entropy/whiphash/components/ShinyText.jsx create mode 100644 entropy/whiphash/components/SplitText.tsx create mode 100644 entropy/whiphash/components/ui/shimmer-button.tsx create mode 100644 entropy/whiphash/components/ui/shine-border.tsx create mode 100644 entropy/whiphash/demo-extension/README.md create mode 100644 entropy/whiphash/demo-extension/icons/icon.svg create mode 100755 entropy/whiphash/demo-extension/install.sh create mode 100644 entropy/whiphash/demo-extension/manifest.json create mode 100644 entropy/whiphash/demo-extension/popup.html create mode 100644 entropy/whiphash/demo-extension/popup.js create mode 100644 entropy/whiphash/demo-extension/test.html create mode 100644 entropy/whiphash/eslint.config.mjs create mode 100644 entropy/whiphash/genpassword.md create mode 100644 entropy/whiphash/lib/nildb-client.ts create mode 100644 entropy/whiphash/lib/utils.ts create mode 100644 entropy/whiphash/next-env.d.ts create mode 100644 entropy/whiphash/next.config.ts create mode 100644 entropy/whiphash/package-lock.json create mode 100644 entropy/whiphash/package.json create mode 100644 entropy/whiphash/postcss.config.mjs create mode 100644 entropy/whiphash/public/file.svg create mode 100644 entropy/whiphash/public/globe.svg create mode 100644 entropy/whiphash/public/next.svg create mode 100644 entropy/whiphash/public/vercel.svg create mode 100644 entropy/whiphash/public/window.svg create mode 100644 entropy/whiphash/tsconfig.json diff --git a/entropy/whiphash/README.md b/entropy/whiphash/README.md new file mode 100644 index 0000000..2371e97 --- /dev/null +++ b/entropy/whiphash/README.md @@ -0,0 +1,269 @@ +# WhipHash - Secure Password Generator + +**Built with Pure High Pyth Entropy** + +A decentralized password generator that creates cryptographically secure passwords using high-entropy randomness from the Pyth Network, combined with advanced client-side encryption and secure storage in NilDB. + +## 🔗 Contract Explorer & Transaction Details + +**📊 Transaction Explorer**: [View on BaseScan](https://sepolia.basescan.org/address/0xE861DC68Eb976da0661035bBf132d6F3a3288B71) + +**💰 Pyth Network Fee**: **0.00000015 ETH** (constant throughout the project) +- **Cost Efficiency**: Ultra-low fees for high-entropy randomness + +## 📋 Deployed Contracts + +### Base Sepolia Testnet + +| Contract | Address | Purpose | +|----------|---------|---------| +| **RandomnessGen** | `0xE861DC68Eb976da0661035bBf132d6F3a3288B71` | Generates random number pairs using Pyth Network entropy | +| **Entropy** | `0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c` | Pyth Network entropy provider contract | + +### Contract Details +- **Network**: Base Sepolia Testnet (Chain ID: 84532) +- **Deployment Hash**: `0x39a943edca709c3337e2b01e6b58cf9db16af0b6403acb48448f7094b9354bb1` +- **Block**: 32774035 +- **Gas Used**: 1,857,888 gas +- **Cost**: 0.000001858038488928 ETH +- **Status**: ✅ Verified on Sourcify + +## 🏗️ Architecture Overview + +### Client-Side Password Generation +- **Pure Client-Side**: All password generation happens in the browser using Web Crypto API +- **No Server Dependency**: Passwords are never transmitted to servers in plaintext +- **Device Secrets**: Generated locally using `crypto.getRandomValues()` + +### Server-Side Storage +- **Encrypted Storage**: Passwords are encrypted before being sent to NilDB +- **NilDB Integration**: Decentralized database for secure password storage +- **Metadata Preservation**: Transaction hashes and sequence numbers stored for verification + +## 🔒 Encryption & Security Implementation + +### Multi-Layer Cryptographic Process + +#### 1. **Device Secret Generation (C)** +```javascript +// Generate 32-byte device secret locally +const deviceSecret = crypto.getRandomValues(new Uint8Array(32)) +``` +- **Purpose**: Local entropy source that never leaves the device +- **Storage**: Kept in memory only, never transmitted + +#### 2. **On-Chain Randomness (R1, R2)** +```solidity +// Pyth Network provides two random numbers +uint256 n1; // First random number (R1) +uint256 n2; // Second random number (R2) +``` +- **Source**: Pyth Network's high-entropy randomness +- **Verification**: Blockchain transaction provides cryptographic proof +- **Advantage**: Unpredictable, verifiable, and tamper-proof + +#### 3. **HKDF Key Derivation** +```javascript +// Mix R1 + C → local_raw using HKDF-SHA256 +const localRaw = await hkdf(ikm, 32, appSalt1, 'local_raw_v1') +``` +- **Algorithm**: HKDF-SHA256 (RFC 5869) +- **Purpose**: Combines on-chain and device entropy +- **Security**: Normalizes inputs and provides uniform seed + +#### 4. **Memory-Hard Key Derivation** +```javascript +// Harden local_raw → LocalKey using Argon2id/scrypt +const localKey = await argon2id(localRaw, salt1, params, 32) +``` +- **Algorithm**: Argon2id/scrypt (memory-hard) +- **Parameters**: 64MB memory, 3 iterations, 4 parallelism +- **Purpose**: Defends against offline brute force attacks + +#### 5. **Final Password Derivation** +```javascript +// Derive final password using LocalKey + R2 +const passwordBytes = await argon2id(seedRaw, passwordSalt, params, 32) +``` +- **Process**: HKDF + Argon2id for final hardening +- **Output**: 32-byte password material +- **Character Set**: Letters, numbers, symbols (94 characters) + +### Security Advantages + +#### **Pyth Network Entropy Benefits:** +1. **True Randomness**: Pyth provides cryptographically secure random numbers +2. **Verifiable**: Blockchain transactions provide proof of randomness +3. **Tamper-Proof**: Immutable blockchain prevents manipulation +4. **High Entropy**: Superior to pseudo-random number generators +5. **Decentralized**: No single point of failure or control + +#### **Multi-Layer Protection:** +- **Device Secret**: Local entropy prevents server-side attacks +- **On-Chain Proof**: Blockchain provides verifiable randomness +- **Memory Hardening**: Argon2id prevents GPU/ASIC attacks +- **HKDF Mixing**: Combines multiple entropy sources securely + +## 🚀 How to Run the Project + +### Prerequisites +- Node.js 18+ +- npm or yarn +- MetaMask wallet (for blockchain interaction) +- Git + +### 1. Clone the Repository +```bash +git clone +cd whiphash +``` + +### 2. Install Dependencies +```bash +npm install +``` + +### 3. Environment Setup +```bash +cp .env.example .env.local +``` + +#### Required Environment Variables +```env +# NilDB Configuration +NILLION_API_KEY=your-nillion-api-key +NILLION_COLLECTION_ID=your-collection-id + +# Alternative NilDB Configuration (if using different setup) +NILCHAIN_URL=http://rpc.testnet.nilchain-rpc-proxy.nilogy.xyz +NILAUTH_URL=https://nilauth.sandbox.app-cluster.sandbox.nilogy.xyz +NILDB_NODES=https://nildb-stg-n1.nillion.network,https://nildb-stg-n2.nillion.network,https://nildb-stg-n3.nillion.network +BUILDER_PRIVATE_KEY=your-builder-private-key +``` + +### 4. Start the Development Server +```bash +npm run dev +``` + +The app will be available at `http://localhost:3000` + +### 5. Browser Extension (Optional) + +#### Install the Extension +1. Open Chrome and go to `chrome://extensions/` +2. Enable "Developer mode" +3. Click "Load unpacked" +4. Select the `demo-extension` folder +5. Pin the extension for easy access + +#### Extension Features +- **🖼️ Embedded Mode**: View app within extension popup +- **⛶ Fullscreen Mode**: Open app in new tab +- **🔗 Wallet Mode**: Optimized for wallet interactions + +### 6. Usage Instructions + +#### Generate a Password +1. **Connect Wallet**: Click "Connect Wallet" and approve MetaMask +2. **Request Randomness**: Click "Generate Secure Password" +3. **Wait for Pyth**: System fetches randomness from Pyth Network +4. **Password Generated**: Secure password appears with copy option +5. **Store Password**: Enter socials and save to NilDB + +#### View Saved Passwords +1. Navigate to `/view` or click "View Saved Passwords →" +2. See all stored passwords with metadata +3. Click to show/hide passwords +4. Copy passwords to clipboard + +## 🛠️ Development + +### Project Structure +``` +whiphash/ +├── app/ # App router pages +│ ├── page.tsx # Landing page +│ ├── test/page.tsx # Password generation +│ ├── view/page.tsx # Password viewing +│ └── api/nildb/ # NilDB API routes +├── components/ # React components +├── lib/ # Utility functions +└── demo-extension/ # Browser extension +``` + +### Key Technologies +- **Frontend**: Next.js 16, React 19, TypeScript, Tailwind CSS +- **Blockchain**: Viem, Ethers.js, Base Sepolia +- **Randomness**: Pyth Network, Entropy Protocol +- **Storage**: NilDB (Nillion Network) +- **Crypto**: Web Crypto API, HKDF, Argon2id/scrypt +- **UI**: Three.js, GSAP, Custom animations + +### API Endpoints +- `POST /api/nildb/store-password` - Store encrypted password +- `GET /api/nildb/read-collection` - Retrieve stored passwords +- `GET /api/nildb/test-config` - Test NilDB configuration + +## 🔐 Security Considerations + +### What's Encrypted +- ✅ Passwords (client-side generation) +- ✅ Device secrets (never transmitted) +- ✅ Storage in NilDB (encrypted at rest) +- ✅ All sensitive data (socials, metadata) + +### What's Public +- ✅ Transaction hashes (for verification) +- ✅ Sequence numbers (for randomness proof) +- ✅ Blockchain randomness (verifiable on-chain) + +### Best Practices +- **Never share device secrets** +- **Verify transaction hashes** +- **Use strong master passwords** +- **Regular security audits** + +## 📊 Performance + +### Password Generation Time +- **Device Secret**: ~1ms (local generation) +- **Blockchain Call**: ~2-5s (Pyth Network) +- **HKDF Processing**: ~10ms +- **Argon2id**: ~100-500ms (memory-hard) +- **Total**: ~3-6 seconds per password + +### Storage Efficiency +- **Password**: 16-32 characters +- **Metadata**: ~1KB per entry +- **NilDB**: Decentralized, redundant storage + +## 🤝 Contributing + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Test thoroughly +5. Submit a pull request + +## 📄 License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## 🙏 Acknowledgments + +- **Pyth Network** for providing high-entropy randomness +- **Nillion Network** for decentralized storage +- **Base Network** for fast, low-cost transactions +- **MetaMask** for wallet integration +- **Next.js** for the React framework + +## 🔗 Links + +- **Live Demo**: [ETHGlobal Showcase](https://ethglobal.com/showcase/whiphash-9u5xj) +- **GitHub Repository**: [dumprahul/whiphash](https://github.com/dumprahul/whiphash) +- **Contract Explorer**: [BaseScan](https://sepolia.basescan.org/address/0xE861DC68Eb976da0661035bBf132d6F3a3288B71) + +--- + +**Built with ❤️ and pure high Pyth entropy** \ No newline at end of file diff --git a/entropy/whiphash/abi/DeployRandomnessGen.json b/entropy/whiphash/abi/DeployRandomnessGen.json new file mode 100644 index 0000000..6c4517c --- /dev/null +++ b/entropy/whiphash/abi/DeployRandomnessGen.json @@ -0,0 +1 @@ +{"abi":[{"type":"function","name":"IS_SCRIPT","inputs":[],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"run","inputs":[],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x60806040526001600c5f6101000a81548160ff0219169083151502179055506001600c60026101000a81548160ff0219169083151502179055503480156043575f5ffd5b50612a2b806100515f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063c040622614610038578063f8ccbf4714610042575b5f5ffd5b610040610060565b005b61004a61031a565b604051610057919061043e565b60405180910390f35b5f737109709ecfa91a80626ff3989d68f67f5b1dd12d73ffffffffffffffffffffffffffffffffffffffff1663c1978d1f6040518163ffffffff1660e01b81526004016100ac906104b1565b602060405180830381865afa1580156100c7573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100eb9190610506565b9050737109709ecfa91a80626ff3989d68f67f5b1dd12d73ffffffffffffffffffffffffffffffffffffffff1663ce817d47826040518263ffffffff1660e01b815260040161013a9190610540565b5f604051808303815f87803b158015610151575f5ffd5b505af1158015610163573d5f5f3e3d5ffd5b505050505f737109709ecfa91a80626ff3989d68f67f5b1dd12d73ffffffffffffffffffffffffffffffffffffffff1663350d56bf6040518163ffffffff1660e01b81526004016101b3906105a3565b602060405180830381865afa1580156101ce573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101f2919061061b565b90505f816040516102029061040d565b61020c9190610655565b604051809103905ff080158015610225573d5f5f3e3d5ffd5b5090506102676040518060400160405280601a81526020017f52616e646f6d6e65737347656e206465706c6f7965642061743a0000000000008152508261032d565b6102a66040518060400160405280601081526020017f456e74726f707920616464726573733a000000000000000000000000000000008152508361032d565b737109709ecfa91a80626ff3989d68f67f5b1dd12d73ffffffffffffffffffffffffffffffffffffffff166376eadd366040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156102ff575f5ffd5b505af1158015610311573d5f5f3e3d5ffd5b50505050505050565b600c60029054906101000a900460ff1681565b6103c582826040516024016103439291906106ce565b6040516020818303038152906040527f319af333000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506103c9565b5050565b6103e0816103d86103e3610402565b63ffffffff16565b50565b5f6a636f6e736f6c652e6c6f6790505f5f835160208501845afa505050565b61041a819050919050565b6122cc8061072a83390190565b6104226106fc565b565b5f8115159050919050565b61043881610424565b82525050565b5f6020820190506104515f83018461042f565b92915050565b5f82825260208201905092915050565b7f505249564154455f4b45590000000000000000000000000000000000000000005f82015250565b5f61049b600b83610457565b91506104a682610467565b602082019050919050565b5f6020820190508181035f8301526104c88161048f565b9050919050565b5f5ffd5b5f819050919050565b6104e5816104d3565b81146104ef575f5ffd5b50565b5f81519050610500816104dc565b92915050565b5f6020828403121561051b5761051a6104cf565b5b5f610528848285016104f2565b91505092915050565b61053a816104d3565b82525050565b5f6020820190506105535f830184610531565b92915050565b7f454e54524f50595f4144445245535300000000000000000000000000000000005f82015250565b5f61058d600f83610457565b915061059882610559565b602082019050919050565b5f6020820190508181035f8301526105ba81610581565b9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105ea826105c1565b9050919050565b6105fa816105e0565b8114610604575f5ffd5b50565b5f81519050610615816105f1565b92915050565b5f602082840312156106305761062f6104cf565b5b5f61063d84828501610607565b91505092915050565b61064f816105e0565b82525050565b5f6020820190506106685f830184610646565b92915050565b5f81519050919050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6106a08261066e565b6106aa8185610457565b93506106ba818560208601610678565b6106c381610686565b840191505092915050565b5f6040820190508181035f8301526106e68185610696565b90506106f56020830184610646565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52605160045260245ffdfe60a060405234801561000f575f5ffd5b506040516122cc3803806122cc83398181016040528101906100319190610137565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361009f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610096906101bc565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1681525050506101da565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610106826100dd565b9050919050565b610116816100fc565b8114610120575f5ffd5b50565b5f815190506101318161010d565b92915050565b5f6020828403121561014c5761014b6100d9565b5b5f61015984828501610123565b91505092915050565b5f82825260208201905092915050565b7f656e74726f7079207a65726f00000000000000000000000000000000000000005f82015250565b5f6101a6600c83610162565b91506101b182610172565b602082019050919050565b5f6020820190508181035f8301526101d38161019a565b9050919050565b60805161209b6102315f395f81816102af0152818161039f015281816106540152818161074401528181610a0101528181610a2601528181610b0b01528181610d4201528181610e3201526113a1015261209b5ff3fe608060405260043610610090575f3560e01c80634c44895b116100585780634c44895b1461013f57806352a5f1f81461015b578063d0dcdb6d14610183578063edff42d6146101c2578063f9ec879c1461020157610090565b806316fd20a81461009457806336dbcc15146100b0578063411010ec146100cc57806347ce07cc1461010b5780634b3813e614610135575b5f5ffd5b6100ae60048036038101906100a9919061183e565b61023e565b005b6100ca60048036038101906100c5919061188e565b6105e3565b005b3480156100d7575f5ffd5b506100f260048036038101906100ed9190611909565b610987565b6040516101029493929190611961565b60405180910390f35b348015610116575f5ffd5b5061011f6109ff565b60405161012c91906119ff565b60405180910390f35b61013d610a23565b005b61015960048036038101906101549190611a18565b610d3f565b005b348015610166575f5ffd5b50610181600480360381019061017c9190611a43565b611072565b005b34801561018e575f5ffd5b506101a960048036038101906101a49190611909565b61116a565b6040516101b99493929190611ac5565b60405180910390f35b3480156101cd575f5ffd5b506101e860048036038101906101e39190611909565b6111c2565b6040516101f89493929190611ac5565b60405180910390f35b34801561020c575f5ffd5b5061022760048036038101906102229190611b08565b61129f565b604051610235929190611b33565b60405180910390f35b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036102ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102a390611bb4565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ca1642e1836040518263ffffffff1660e01b81526004016103069190611bd2565b602060405180830381865afa158015610321573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103459190611c30565b9050806fffffffffffffffffffffffffffffffff1634101561039c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161039390611ca5565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f77b45e1836fffffffffffffffffffffffffffffffff168787876040518563ffffffff1660e01b815260040161040d93929190611cc3565b60206040518083038185885af1158015610429573d5f5f3e3d5ffd5b50505050506040513d601f19601f8201168201806040525081019061044e9190611d0c565b905060405180608001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018463ffffffff168152505f5f8367ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604082015181600201556060820151816003015f6101000a81548163ffffffff021916908363ffffffff16021790555090505061058e3483611306565b3373ffffffffffffffffffffffffffffffffffffffff168167ffffffffffffffff167f786d60918d121c88062273dc32f67fb36099904f9775332400757ed909be3a4a60405160405180910390a35050505050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610651576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161064890611bb4565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ca1642e1836040518263ffffffff1660e01b81526004016106ab9190611bd2565b602060405180830381865afa1580156106c6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106ea9190611c30565b9050806fffffffffffffffffffffffffffffffff16341015610741576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161073890611ca5565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630e33da29836fffffffffffffffffffffffffffffffff1686866040518463ffffffff1660e01b81526004016107b0929190611d37565b60206040518083038185885af11580156107cc573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906107f19190611d0c565b905060405180608001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1681526020015f5f1b81526020018463ffffffff168152505f5f8367ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604082015181600201556060820151816003015f6101000a81548163ffffffff021916908363ffffffff1602179055509050506109333483611306565b3373ffffffffffffffffffffffffffffffffffffffff168167ffffffffffffffff167f786d60918d121c88062273dc32f67fb36099904f9775332400757ed909be3a4a60405160405180910390a350505050565b5f602052805f5260405f205f91509050805f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806002015490806003015f9054906101000a900463ffffffff16905084565b7f000000000000000000000000000000000000000000000000000000000000000081565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16638204b67a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a8d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab19190611c30565b9050806fffffffffffffffffffffffffffffffff16341015610b08576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aff90611ca5565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16637b43155d836fffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b815260040160206040518083038185885af1158015610b86573d5f5f3e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190610bab9190611d0c565b905060405180608001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f5f1b81526020015f63ffffffff168152505f5f8367ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604082015181600201556060820151816003015f6101000a81548163ffffffff021916908363ffffffff160217905550905050610ced3483611306565b3373ffffffffffffffffffffffffffffffffffffffff168167ffffffffffffffff167f786d60918d121c88062273dc32f67fb36099904f9775332400757ed909be3a4a60405160405180910390a35050565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ca1642e1836040518263ffffffff1660e01b8152600401610d999190611bd2565b602060405180830381865afa158015610db4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd89190611c30565b9050806fffffffffffffffffffffffffffffffff16341015610e2f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e2690611ca5565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630bed189f836fffffffffffffffffffffffffffffffff16856040518363ffffffff1660e01b8152600401610e9c9190611bd2565b60206040518083038185885af1158015610eb8573d5f5f3e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190610edd9190611d0c565b905060405180608001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f5f1b81526020018463ffffffff168152505f5f8367ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604082015181600201556060820151816003015f6101000a81548163ffffffff021916908363ffffffff16021790555090505061101f3483611306565b3373ffffffffffffffffffffffffffffffffffffffff168167ffffffffffffffff167f786d60918d121c88062273dc32f67fb36099904f9775332400757ed909be3a4a60405160405180910390a3505050565b5f61107b61139e565b90505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036110eb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110e290611da8565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611159576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161115090611e36565b60405180910390fd5b6111648484846113c5565b50505050565b6001602052805f5260405f205f91509050805f015490806001015490806002015f9054906101000a900460ff16908060020160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905084565b5f5f5f5f5f60015f8767ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f206040518060800160405290815f820154815260200160018201548152602001600282015f9054906101000a900460ff161515151581526020016002820160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250509050805f01518160200151826040015183606001519450945094509450509193509193565b5f5f5f836040516020016112b39190611ec8565b6040516020818303038152906040528051906020012090505f846040516020016112dd9190611f37565b604051602081830303815290604052805190602001209050815f1c815f1c935093505050915091565b5f816fffffffffffffffffffffffffffffffff16836113259190611f89565b90505f811115611399575f3373ffffffffffffffffffffffffffffffffffffffff168260405161135490611fe9565b5f6040518083038185875af1925050503d805f811461138e576040519150601f19603f3d011682016040523d82523d5f602084013e611393565b606091505b50509050505b505050565b5f7f0000000000000000000000000000000000000000000000000000000000000000905090565b5f5f5f8567ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f206040518060800160405290815f82015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600182015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160028201548152602001600382015f9054906101000a900463ffffffff1663ffffffff1663ffffffff168152505090505f73ffffffffffffffffffffffffffffffffffffffff16815f015173ffffffffffffffffffffffffffffffffffffffff1603611540576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161153790612047565b60405180910390fd5b5f826040516020016115529190611ec8565b6040516020818303038152906040528051906020012090505f8360405160200161157c9190611f37565b6040516020818303038152906040528051906020012090505f825f1c90505f825f1c90506040518060800160405280838152602001828152602001600115158152602001865f015173ffffffffffffffffffffffffffffffffffffffff1681525060015f8a67ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f820151815f0155602082015181600101556040820151816002015f6101000a81548160ff02191690831515021790555060608201518160020160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550905050845f015173ffffffffffffffffffffffffffffffffffffffff168867ffffffffffffffff167f147b023a7abaa895e9c8ddf02f2b6a93ede2ad365ea7d622f8a384d544f1631e84846040516116d3929190611b33565b60405180910390a35f5f8967ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f5f82015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600182015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600282015f9055600382015f6101000a81549063ffffffff021916905550505050505050505050565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6117a182611778565b9050919050565b6117b181611797565b81146117bb575f5ffd5b50565b5f813590506117cc816117a8565b92915050565b5f819050919050565b6117e4816117d2565b81146117ee575f5ffd5b50565b5f813590506117ff816117db565b92915050565b5f63ffffffff82169050919050565b61181d81611805565b8114611827575f5ffd5b50565b5f8135905061183881611814565b92915050565b5f5f5f6060848603121561185557611854611774565b5b5f611862868287016117be565b9350506020611873868287016117f1565b92505060406118848682870161182a565b9150509250925092565b5f5f604083850312156118a4576118a3611774565b5b5f6118b1858286016117be565b92505060206118c28582860161182a565b9150509250929050565b5f67ffffffffffffffff82169050919050565b6118e8816118cc565b81146118f2575f5ffd5b50565b5f81359050611903816118df565b92915050565b5f6020828403121561191e5761191d611774565b5b5f61192b848285016118f5565b91505092915050565b61193d81611797565b82525050565b61194c816117d2565b82525050565b61195b81611805565b82525050565b5f6080820190506119745f830187611934565b6119816020830186611934565b61198e6040830185611943565b61199b6060830184611952565b95945050505050565b5f819050919050565b5f6119c76119c26119bd84611778565b6119a4565b611778565b9050919050565b5f6119d8826119ad565b9050919050565b5f6119e9826119ce565b9050919050565b6119f9816119df565b82525050565b5f602082019050611a125f8301846119f0565b92915050565b5f60208284031215611a2d57611a2c611774565b5b5f611a3a8482850161182a565b91505092915050565b5f5f5f60608486031215611a5a57611a59611774565b5b5f611a67868287016118f5565b9350506020611a78868287016117be565b9250506040611a89868287016117f1565b9150509250925092565b5f819050919050565b611aa581611a93565b82525050565b5f8115159050919050565b611abf81611aab565b82525050565b5f608082019050611ad85f830187611a9c565b611ae56020830186611a9c565b611af26040830185611ab6565b611aff6060830184611934565b95945050505050565b5f60208284031215611b1d57611b1c611774565b5b5f611b2a848285016117f1565b91505092915050565b5f604082019050611b465f830185611a9c565b611b536020830184611a9c565b9392505050565b5f82825260208201905092915050565b7f7a65726f2070726f7669646572000000000000000000000000000000000000005f82015250565b5f611b9e600d83611b5a565b9150611ba982611b6a565b602082019050919050565b5f6020820190508181035f830152611bcb81611b92565b9050919050565b5f602082019050611be55f830184611952565b92915050565b5f6fffffffffffffffffffffffffffffffff82169050919050565b611c0f81611beb565b8114611c19575f5ffd5b50565b5f81519050611c2a81611c06565b92915050565b5f60208284031215611c4557611c44611774565b5b5f611c5284828501611c1c565b91505092915050565b7f696e73756666696369656e7420666565000000000000000000000000000000005f82015250565b5f611c8f601083611b5a565b9150611c9a82611c5b565b602082019050919050565b5f6020820190508181035f830152611cbc81611c83565b9050919050565b5f606082019050611cd65f830186611934565b611ce36020830185611943565b611cf06040830184611952565b949350505050565b5f81519050611d06816118df565b92915050565b5f60208284031215611d2157611d20611774565b5b5f611d2e84828501611cf8565b91505092915050565b5f604082019050611d4a5f830185611934565b611d576020830184611952565b9392505050565b7f456e74726f70792061646472657373206e6f74207365740000000000000000005f82015250565b5f611d92601783611b5a565b9150611d9d82611d5e565b602082019050919050565b5f6020820190508181035f830152611dbf81611d86565b9050919050565b7f4f6e6c7920456e74726f70792063616e2063616c6c20746869732066756e63745f8201527f696f6e0000000000000000000000000000000000000000000000000000000000602082015250565b5f611e20602383611b5a565b9150611e2b82611dc6565b604082019050919050565b5f6020820190508181035f830152611e4d81611e14565b9050919050565b5f819050919050565b611e6e611e69826117d2565b611e54565b82525050565b5f81905092915050565b7f72310000000000000000000000000000000000000000000000000000000000005f82015250565b5f611eb2600283611e74565b9150611ebd82611e7e565b600282019050919050565b5f611ed38284611e5d565b602082019150611ee282611ea6565b915081905092915050565b7f72320000000000000000000000000000000000000000000000000000000000005f82015250565b5f611f21600283611e74565b9150611f2c82611eed565b600282019050919050565b5f611f428284611e5d565b602082019150611f5182611f15565b915081905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f611f9382611a93565b9150611f9e83611a93565b9250828203905081811115611fb657611fb5611f5c565b5b92915050565b5f81905092915050565b50565b5f611fd45f83611fbc565b9150611fdf82611fc6565b5f82019050919050565b5f611ff382611fc9565b9150819050919050565b7f756e6b6e6f776e207265717565737400000000000000000000000000000000005f82015250565b5f612031600f83611b5a565b915061203c82611ffd565b602082019050919050565b5f6020820190508181035f83015261205e81612025565b905091905056fea2646970667358221220dc4afc3eee4580bb177a99bb95a4905ac800e399127f9a7acdc93e79d6d3eb5a64736f6c634300081e0033a2646970667358221220ac0e7d655555dc17c39dc5b0f808a004a1ef0cc1fc964900260f75d9313037c564736f6c634300081e0033","sourceMap":"132:748:21:-:0;;;3200:4:2;3160:44;;;;;;;;;;;;;;;;;;;;873:4:1;849:28;;;;;;;;;;;;;;;;;;;;132:748:21;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063c040622614610038578063f8ccbf4714610042575b5f5ffd5b610040610060565b005b61004a61031a565b604051610057919061043e565b60405180910390f35b5f737109709ecfa91a80626ff3989d68f67f5b1dd12d73ffffffffffffffffffffffffffffffffffffffff1663c1978d1f6040518163ffffffff1660e01b81526004016100ac906104b1565b602060405180830381865afa1580156100c7573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100eb9190610506565b9050737109709ecfa91a80626ff3989d68f67f5b1dd12d73ffffffffffffffffffffffffffffffffffffffff1663ce817d47826040518263ffffffff1660e01b815260040161013a9190610540565b5f604051808303815f87803b158015610151575f5ffd5b505af1158015610163573d5f5f3e3d5ffd5b505050505f737109709ecfa91a80626ff3989d68f67f5b1dd12d73ffffffffffffffffffffffffffffffffffffffff1663350d56bf6040518163ffffffff1660e01b81526004016101b3906105a3565b602060405180830381865afa1580156101ce573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101f2919061061b565b90505f816040516102029061040d565b61020c9190610655565b604051809103905ff080158015610225573d5f5f3e3d5ffd5b5090506102676040518060400160405280601a81526020017f52616e646f6d6e65737347656e206465706c6f7965642061743a0000000000008152508261032d565b6102a66040518060400160405280601081526020017f456e74726f707920616464726573733a000000000000000000000000000000008152508361032d565b737109709ecfa91a80626ff3989d68f67f5b1dd12d73ffffffffffffffffffffffffffffffffffffffff166376eadd366040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156102ff575f5ffd5b505af1158015610311573d5f5f3e3d5ffd5b50505050505050565b600c60029054906101000a900460ff1681565b6103c582826040516024016103439291906106ce565b6040516020818303038152906040527f319af333000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506103c9565b5050565b6103e0816103d86103e3610402565b63ffffffff16565b50565b5f6a636f6e736f6c652e6c6f6790505f5f835160208501845afa505050565b61041a819050919050565b6122cc8061072a83390190565b6104226106fc565b565b5f8115159050919050565b61043881610424565b82525050565b5f6020820190506104515f83018461042f565b92915050565b5f82825260208201905092915050565b7f505249564154455f4b45590000000000000000000000000000000000000000005f82015250565b5f61049b600b83610457565b91506104a682610467565b602082019050919050565b5f6020820190508181035f8301526104c88161048f565b9050919050565b5f5ffd5b5f819050919050565b6104e5816104d3565b81146104ef575f5ffd5b50565b5f81519050610500816104dc565b92915050565b5f6020828403121561051b5761051a6104cf565b5b5f610528848285016104f2565b91505092915050565b61053a816104d3565b82525050565b5f6020820190506105535f830184610531565b92915050565b7f454e54524f50595f4144445245535300000000000000000000000000000000005f82015250565b5f61058d600f83610457565b915061059882610559565b602082019050919050565b5f6020820190508181035f8301526105ba81610581565b9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105ea826105c1565b9050919050565b6105fa816105e0565b8114610604575f5ffd5b50565b5f81519050610615816105f1565b92915050565b5f602082840312156106305761062f6104cf565b5b5f61063d84828501610607565b91505092915050565b61064f816105e0565b82525050565b5f6020820190506106685f830184610646565b92915050565b5f81519050919050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6106a08261066e565b6106aa8185610457565b93506106ba818560208601610678565b6106c381610686565b840191505092915050565b5f6040820190508181035f8301526106e68185610696565b90506106f56020830184610646565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52605160045260245ffdfe60a060405234801561000f575f5ffd5b506040516122cc3803806122cc83398181016040528101906100319190610137565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361009f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610096906101bc565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1681525050506101da565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610106826100dd565b9050919050565b610116816100fc565b8114610120575f5ffd5b50565b5f815190506101318161010d565b92915050565b5f6020828403121561014c5761014b6100d9565b5b5f61015984828501610123565b91505092915050565b5f82825260208201905092915050565b7f656e74726f7079207a65726f00000000000000000000000000000000000000005f82015250565b5f6101a6600c83610162565b91506101b182610172565b602082019050919050565b5f6020820190508181035f8301526101d38161019a565b9050919050565b60805161209b6102315f395f81816102af0152818161039f015281816106540152818161074401528181610a0101528181610a2601528181610b0b01528181610d4201528181610e3201526113a1015261209b5ff3fe608060405260043610610090575f3560e01c80634c44895b116100585780634c44895b1461013f57806352a5f1f81461015b578063d0dcdb6d14610183578063edff42d6146101c2578063f9ec879c1461020157610090565b806316fd20a81461009457806336dbcc15146100b0578063411010ec146100cc57806347ce07cc1461010b5780634b3813e614610135575b5f5ffd5b6100ae60048036038101906100a9919061183e565b61023e565b005b6100ca60048036038101906100c5919061188e565b6105e3565b005b3480156100d7575f5ffd5b506100f260048036038101906100ed9190611909565b610987565b6040516101029493929190611961565b60405180910390f35b348015610116575f5ffd5b5061011f6109ff565b60405161012c91906119ff565b60405180910390f35b61013d610a23565b005b61015960048036038101906101549190611a18565b610d3f565b005b348015610166575f5ffd5b50610181600480360381019061017c9190611a43565b611072565b005b34801561018e575f5ffd5b506101a960048036038101906101a49190611909565b61116a565b6040516101b99493929190611ac5565b60405180910390f35b3480156101cd575f5ffd5b506101e860048036038101906101e39190611909565b6111c2565b6040516101f89493929190611ac5565b60405180910390f35b34801561020c575f5ffd5b5061022760048036038101906102229190611b08565b61129f565b604051610235929190611b33565b60405180910390f35b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036102ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102a390611bb4565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ca1642e1836040518263ffffffff1660e01b81526004016103069190611bd2565b602060405180830381865afa158015610321573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103459190611c30565b9050806fffffffffffffffffffffffffffffffff1634101561039c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161039390611ca5565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f77b45e1836fffffffffffffffffffffffffffffffff168787876040518563ffffffff1660e01b815260040161040d93929190611cc3565b60206040518083038185885af1158015610429573d5f5f3e3d5ffd5b50505050506040513d601f19601f8201168201806040525081019061044e9190611d0c565b905060405180608001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018463ffffffff168152505f5f8367ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604082015181600201556060820151816003015f6101000a81548163ffffffff021916908363ffffffff16021790555090505061058e3483611306565b3373ffffffffffffffffffffffffffffffffffffffff168167ffffffffffffffff167f786d60918d121c88062273dc32f67fb36099904f9775332400757ed909be3a4a60405160405180910390a35050505050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610651576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161064890611bb4565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ca1642e1836040518263ffffffff1660e01b81526004016106ab9190611bd2565b602060405180830381865afa1580156106c6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106ea9190611c30565b9050806fffffffffffffffffffffffffffffffff16341015610741576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161073890611ca5565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630e33da29836fffffffffffffffffffffffffffffffff1686866040518463ffffffff1660e01b81526004016107b0929190611d37565b60206040518083038185885af11580156107cc573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906107f19190611d0c565b905060405180608001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1681526020015f5f1b81526020018463ffffffff168152505f5f8367ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604082015181600201556060820151816003015f6101000a81548163ffffffff021916908363ffffffff1602179055509050506109333483611306565b3373ffffffffffffffffffffffffffffffffffffffff168167ffffffffffffffff167f786d60918d121c88062273dc32f67fb36099904f9775332400757ed909be3a4a60405160405180910390a350505050565b5f602052805f5260405f205f91509050805f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806002015490806003015f9054906101000a900463ffffffff16905084565b7f000000000000000000000000000000000000000000000000000000000000000081565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16638204b67a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a8d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab19190611c30565b9050806fffffffffffffffffffffffffffffffff16341015610b08576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aff90611ca5565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16637b43155d836fffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b815260040160206040518083038185885af1158015610b86573d5f5f3e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190610bab9190611d0c565b905060405180608001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f5f1b81526020015f63ffffffff168152505f5f8367ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604082015181600201556060820151816003015f6101000a81548163ffffffff021916908363ffffffff160217905550905050610ced3483611306565b3373ffffffffffffffffffffffffffffffffffffffff168167ffffffffffffffff167f786d60918d121c88062273dc32f67fb36099904f9775332400757ed909be3a4a60405160405180910390a35050565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ca1642e1836040518263ffffffff1660e01b8152600401610d999190611bd2565b602060405180830381865afa158015610db4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd89190611c30565b9050806fffffffffffffffffffffffffffffffff16341015610e2f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e2690611ca5565b60405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630bed189f836fffffffffffffffffffffffffffffffff16856040518363ffffffff1660e01b8152600401610e9c9190611bd2565b60206040518083038185885af1158015610eb8573d5f5f3e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190610edd9190611d0c565b905060405180608001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f5f1b81526020018463ffffffff168152505f5f8367ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f820151815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550604082015181600201556060820151816003015f6101000a81548163ffffffff021916908363ffffffff16021790555090505061101f3483611306565b3373ffffffffffffffffffffffffffffffffffffffff168167ffffffffffffffff167f786d60918d121c88062273dc32f67fb36099904f9775332400757ed909be3a4a60405160405180910390a3505050565b5f61107b61139e565b90505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036110eb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110e290611da8565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611159576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161115090611e36565b60405180910390fd5b6111648484846113c5565b50505050565b6001602052805f5260405f205f91509050805f015490806001015490806002015f9054906101000a900460ff16908060020160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905084565b5f5f5f5f5f60015f8767ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f206040518060800160405290815f820154815260200160018201548152602001600282015f9054906101000a900460ff161515151581526020016002820160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250509050805f01518160200151826040015183606001519450945094509450509193509193565b5f5f5f836040516020016112b39190611ec8565b6040516020818303038152906040528051906020012090505f846040516020016112dd9190611f37565b604051602081830303815290604052805190602001209050815f1c815f1c935093505050915091565b5f816fffffffffffffffffffffffffffffffff16836113259190611f89565b90505f811115611399575f3373ffffffffffffffffffffffffffffffffffffffff168260405161135490611fe9565b5f6040518083038185875af1925050503d805f811461138e576040519150601f19603f3d011682016040523d82523d5f602084013e611393565b606091505b50509050505b505050565b5f7f0000000000000000000000000000000000000000000000000000000000000000905090565b5f5f5f8567ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f206040518060800160405290815f82015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600182015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160028201548152602001600382015f9054906101000a900463ffffffff1663ffffffff1663ffffffff168152505090505f73ffffffffffffffffffffffffffffffffffffffff16815f015173ffffffffffffffffffffffffffffffffffffffff1603611540576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161153790612047565b60405180910390fd5b5f826040516020016115529190611ec8565b6040516020818303038152906040528051906020012090505f8360405160200161157c9190611f37565b6040516020818303038152906040528051906020012090505f825f1c90505f825f1c90506040518060800160405280838152602001828152602001600115158152602001865f015173ffffffffffffffffffffffffffffffffffffffff1681525060015f8a67ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f820151815f0155602082015181600101556040820151816002015f6101000a81548160ff02191690831515021790555060608201518160020160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550905050845f015173ffffffffffffffffffffffffffffffffffffffff168867ffffffffffffffff167f147b023a7abaa895e9c8ddf02f2b6a93ede2ad365ea7d622f8a384d544f1631e84846040516116d3929190611b33565b60405180910390a35f5f8967ffffffffffffffff1667ffffffffffffffff1681526020019081526020015f205f5f82015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600182015f6101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600282015f9055600382015f6101000a81549063ffffffff021916905550505050505050505050565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6117a182611778565b9050919050565b6117b181611797565b81146117bb575f5ffd5b50565b5f813590506117cc816117a8565b92915050565b5f819050919050565b6117e4816117d2565b81146117ee575f5ffd5b50565b5f813590506117ff816117db565b92915050565b5f63ffffffff82169050919050565b61181d81611805565b8114611827575f5ffd5b50565b5f8135905061183881611814565b92915050565b5f5f5f6060848603121561185557611854611774565b5b5f611862868287016117be565b9350506020611873868287016117f1565b92505060406118848682870161182a565b9150509250925092565b5f5f604083850312156118a4576118a3611774565b5b5f6118b1858286016117be565b92505060206118c28582860161182a565b9150509250929050565b5f67ffffffffffffffff82169050919050565b6118e8816118cc565b81146118f2575f5ffd5b50565b5f81359050611903816118df565b92915050565b5f6020828403121561191e5761191d611774565b5b5f61192b848285016118f5565b91505092915050565b61193d81611797565b82525050565b61194c816117d2565b82525050565b61195b81611805565b82525050565b5f6080820190506119745f830187611934565b6119816020830186611934565b61198e6040830185611943565b61199b6060830184611952565b95945050505050565b5f819050919050565b5f6119c76119c26119bd84611778565b6119a4565b611778565b9050919050565b5f6119d8826119ad565b9050919050565b5f6119e9826119ce565b9050919050565b6119f9816119df565b82525050565b5f602082019050611a125f8301846119f0565b92915050565b5f60208284031215611a2d57611a2c611774565b5b5f611a3a8482850161182a565b91505092915050565b5f5f5f60608486031215611a5a57611a59611774565b5b5f611a67868287016118f5565b9350506020611a78868287016117be565b9250506040611a89868287016117f1565b9150509250925092565b5f819050919050565b611aa581611a93565b82525050565b5f8115159050919050565b611abf81611aab565b82525050565b5f608082019050611ad85f830187611a9c565b611ae56020830186611a9c565b611af26040830185611ab6565b611aff6060830184611934565b95945050505050565b5f60208284031215611b1d57611b1c611774565b5b5f611b2a848285016117f1565b91505092915050565b5f604082019050611b465f830185611a9c565b611b536020830184611a9c565b9392505050565b5f82825260208201905092915050565b7f7a65726f2070726f7669646572000000000000000000000000000000000000005f82015250565b5f611b9e600d83611b5a565b9150611ba982611b6a565b602082019050919050565b5f6020820190508181035f830152611bcb81611b92565b9050919050565b5f602082019050611be55f830184611952565b92915050565b5f6fffffffffffffffffffffffffffffffff82169050919050565b611c0f81611beb565b8114611c19575f5ffd5b50565b5f81519050611c2a81611c06565b92915050565b5f60208284031215611c4557611c44611774565b5b5f611c5284828501611c1c565b91505092915050565b7f696e73756666696369656e7420666565000000000000000000000000000000005f82015250565b5f611c8f601083611b5a565b9150611c9a82611c5b565b602082019050919050565b5f6020820190508181035f830152611cbc81611c83565b9050919050565b5f606082019050611cd65f830186611934565b611ce36020830185611943565b611cf06040830184611952565b949350505050565b5f81519050611d06816118df565b92915050565b5f60208284031215611d2157611d20611774565b5b5f611d2e84828501611cf8565b91505092915050565b5f604082019050611d4a5f830185611934565b611d576020830184611952565b9392505050565b7f456e74726f70792061646472657373206e6f74207365740000000000000000005f82015250565b5f611d92601783611b5a565b9150611d9d82611d5e565b602082019050919050565b5f6020820190508181035f830152611dbf81611d86565b9050919050565b7f4f6e6c7920456e74726f70792063616e2063616c6c20746869732066756e63745f8201527f696f6e0000000000000000000000000000000000000000000000000000000000602082015250565b5f611e20602383611b5a565b9150611e2b82611dc6565b604082019050919050565b5f6020820190508181035f830152611e4d81611e14565b9050919050565b5f819050919050565b611e6e611e69826117d2565b611e54565b82525050565b5f81905092915050565b7f72310000000000000000000000000000000000000000000000000000000000005f82015250565b5f611eb2600283611e74565b9150611ebd82611e7e565b600282019050919050565b5f611ed38284611e5d565b602082019150611ee282611ea6565b915081905092915050565b7f72320000000000000000000000000000000000000000000000000000000000005f82015250565b5f611f21600283611e74565b9150611f2c82611eed565b600282019050919050565b5f611f428284611e5d565b602082019150611f5182611f15565b915081905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f611f9382611a93565b9150611f9e83611a93565b9250828203905081811115611fb657611fb5611f5c565b5b92915050565b5f81905092915050565b50565b5f611fd45f83611fbc565b9150611fdf82611fc6565b5f82019050919050565b5f611ff382611fc9565b9150819050919050565b7f756e6b6e6f776e207265717565737400000000000000000000000000000000005f82015250565b5f612031600f83611b5a565b915061203c82611ffd565b602082019050919050565b5f6020820190508181035f83015261205e81612025565b905091905056fea2646970667358221220dc4afc3eee4580bb177a99bb95a4905ac800e399127f9a7acdc93e79d6d3eb5a64736f6c634300081e0033a2646970667358221220ac0e7d655555dc17c39dc5b0f808a004a1ef0cc1fc964900260f75d9313037c564736f6c634300081e0033","sourceMap":"132:748:21:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;177:701;;;:::i;:::-;;849:28:1;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;177:701:21;268:26;336:42:0;297:10:21;;;:25;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;268:54;;336:42:0;384:17:21;;;402:18;384:37;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;542:22;336:42:0;567:13:21;;;:32;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;542:57;;618:33;678:14;654:39;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;618:75;;712:65;;;;;;;;;;;;;;;;;;762:13;712:11;:65::i;:::-;787:47;;;;;;;;;;;;;;;;;;819:14;787:11;:47::i;:::-;336:42:0;853:16:21;;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;201:677;;;177:701::o;849:28:1:-;;;;;;;;;;;;;:::o;7740:145:11:-;7807:71;7870:2;7874;7823:54;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7807:15;:71::i;:::-;7740:145;;:::o;851:129::-;922:51;965:7;922:42;934:29;922:11;:42::i;:::-;:51;;:::i;:::-;851:129;:::o;180:463::-;265:22;131:42;265:40;;594:1;571;541:7;535:14;510:2;501:7;497:16;461:14;434:5;402:211;381:246;367:270;180:463;:::o;649:196::-;748:33;825:4;816:13;;649:196;;;:::o;-1:-1:-1:-;;;;;;;;:::o;:::-;;;:::i;:::-;:::o;7:90:23:-;41:7;84:5;77:13;70:21;59:32;;7:90;;;:::o;103:109::-;184:21;199:5;184:21;:::i;:::-;179:3;172:34;103:109;;:::o;218:210::-;305:4;343:2;332:9;328:18;320:26;;356:65;418:1;407:9;403:17;394:6;356:65;:::i;:::-;218:210;;;;:::o;434:169::-;518:11;552:6;547:3;540:19;592:4;587:3;583:14;568:29;;434:169;;;;:::o;609:161::-;749:13;745:1;737:6;733:14;726:37;609:161;:::o;776:366::-;918:3;939:67;1003:2;998:3;939:67;:::i;:::-;932:74;;1015:93;1104:3;1015:93;:::i;:::-;1133:2;1128:3;1124:12;1117:19;;776:366;;;:::o;1148:419::-;1314:4;1352:2;1341:9;1337:18;1329:26;;1401:9;1395:4;1391:20;1387:1;1376:9;1372:17;1365:47;1429:131;1555:4;1429:131;:::i;:::-;1421:139;;1148:419;;;:::o;1654:117::-;1763:1;1760;1753:12;1900:77;1937:7;1966:5;1955:16;;1900:77;;;:::o;1983:122::-;2056:24;2074:5;2056:24;:::i;:::-;2049:5;2046:35;2036:63;;2095:1;2092;2085:12;2036:63;1983:122;:::o;2111:143::-;2168:5;2199:6;2193:13;2184:22;;2215:33;2242:5;2215:33;:::i;:::-;2111:143;;;;:::o;2260:351::-;2330:6;2379:2;2367:9;2358:7;2354:23;2350:32;2347:119;;;2385:79;;:::i;:::-;2347:119;2505:1;2530:64;2586:7;2577:6;2566:9;2562:22;2530:64;:::i;:::-;2520:74;;2476:128;2260:351;;;;:::o;2617:118::-;2704:24;2722:5;2704:24;:::i;:::-;2699:3;2692:37;2617:118;;:::o;2741:222::-;2834:4;2872:2;2861:9;2857:18;2849:26;;2885:71;2953:1;2942:9;2938:17;2929:6;2885:71;:::i;:::-;2741:222;;;;:::o;2969:165::-;3109:17;3105:1;3097:6;3093:14;3086:41;2969:165;:::o;3140:366::-;3282:3;3303:67;3367:2;3362:3;3303:67;:::i;:::-;3296:74;;3379:93;3468:3;3379:93;:::i;:::-;3497:2;3492:3;3488:12;3481:19;;3140:366;;;:::o;3512:419::-;3678:4;3716:2;3705:9;3701:18;3693:26;;3765:9;3759:4;3755:20;3751:1;3740:9;3736:17;3729:47;3793:131;3919:4;3793:131;:::i;:::-;3785:139;;3512:419;;;:::o;3937:126::-;3974:7;4014:42;4007:5;4003:54;3992:65;;3937:126;;;:::o;4069:96::-;4106:7;4135:24;4153:5;4135:24;:::i;:::-;4124:35;;4069:96;;;:::o;4171:122::-;4244:24;4262:5;4244:24;:::i;:::-;4237:5;4234:35;4224:63;;4283:1;4280;4273:12;4224:63;4171:122;:::o;4299:143::-;4356:5;4387:6;4381:13;4372:22;;4403:33;4430:5;4403:33;:::i;:::-;4299:143;;;;:::o;4448:351::-;4518:6;4567:2;4555:9;4546:7;4542:23;4538:32;4535:119;;;4573:79;;:::i;:::-;4535:119;4693:1;4718:64;4774:7;4765:6;4754:9;4750:22;4718:64;:::i;:::-;4708:74;;4664:128;4448:351;;;;:::o;4805:118::-;4892:24;4910:5;4892:24;:::i;:::-;4887:3;4880:37;4805:118;;:::o;4929:222::-;5022:4;5060:2;5049:9;5045:18;5037:26;;5073:71;5141:1;5130:9;5126:17;5117:6;5073:71;:::i;:::-;4929:222;;;;:::o;5157:99::-;5209:6;5243:5;5237:12;5227:22;;5157:99;;;:::o;5262:139::-;5351:6;5346:3;5341;5335:23;5392:1;5383:6;5378:3;5374:16;5367:27;5262:139;;;:::o;5407:102::-;5448:6;5499:2;5495:7;5490:2;5483:5;5479:14;5475:28;5465:38;;5407:102;;;:::o;5515:377::-;5603:3;5631:39;5664:5;5631:39;:::i;:::-;5686:71;5750:6;5745:3;5686:71;:::i;:::-;5679:78;;5766:65;5824:6;5819:3;5812:4;5805:5;5801:16;5766:65;:::i;:::-;5856:29;5878:6;5856:29;:::i;:::-;5851:3;5847:39;5840:46;;5607:285;5515:377;;;;:::o;5898:423::-;6039:4;6077:2;6066:9;6062:18;6054:26;;6126:9;6120:4;6116:20;6112:1;6101:9;6097:17;6090:47;6154:78;6227:4;6218:6;6154:78;:::i;:::-;6146:86;;6242:72;6310:2;6299:9;6295:18;6286:6;6242:72;:::i;:::-;5898:423;;;;;:::o;6327:180::-;6375:77;6372:1;6365:88;6472:4;6469:1;6462:15;6496:4;6493:1;6486:15","linkReferences":{}},"methodIdentifiers":{"IS_SCRIPT()":"f8ccbf47","run()":"c0406226"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.30+commit.73712a01\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"IS_SCRIPT\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"run\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"script/DeployRandomnessGen.s.sol\":\"DeployRandomnessGen\"},\"evmVersion\":\"prague\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[\":@pythnetwork/entropy-sdk-solidity/=node_modules/@pythnetwork/entropy-sdk-solidity/\",\":forge-std/=lib/forge-std/src/\"]},\"sources\":{\"lib/forge-std/src/Base.sol\":{\"keccak256\":\"0x4b2a5a85e045dcf6a082700c7252e43854c2eed88f860aaa18ec1e85218ae2bf\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://98d060ed5be569a92d908fc358149039dc8f833d61973aa1b9d1d8235676bf6d\",\"dweb:/ipfs/QmaWQpn5dJmbMS5skwmPPMeUWZG35BLkignPpcA3zyagEs\"]},\"lib/forge-std/src/Script.sol\":{\"keccak256\":\"0xc942e27c7baae499beb01afbbae99f24d42af9a6e4aae675bc6901b704aa8e9b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0456008adf68947247f358b62863af4a8e349549d2260f2ff9569ff0e3cf5c98\",\"dweb:/ipfs/QmdviSUj2i7o3TPN5vd2xocqGMFVqjUzaiJTZRYyPxyHPx\"]},\"lib/forge-std/src/StdChains.sol\":{\"keccak256\":\"0xae394f477769a38276d98d4854bc865fc8d281edbd4e72167507adb8236812aa\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://34a0e609a4ec617b5c349f5e89a3352810cc5e4d3adaf939b32a27e4a5e46de2\",\"dweb:/ipfs/QmPfjimWAGGb6rzDjNMtLeZ93JJbCJJMov5gaNKyTy1doe\"]},\"lib/forge-std/src/StdCheats.sol\":{\"keccak256\":\"0x0fa6ec03602648b62cce41aab2096e6b7e052f2846075d967b6958dd586db746\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://cd84e2ca9c1eaed6b76768cc12bb8c1af8289170ea8b7706f58d516460d79c41\",\"dweb:/ipfs/QmQ7BK7co6DE4eWUqMyv11s5eHYkS1tyx8tDSZGZVtf2aK\"]},\"lib/forge-std/src/StdConstants.sol\":{\"keccak256\":\"0x319ccdabfa2c0b2428301445873270ffea20f0e039d4fd5e6eeba65158e4e534\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b633f9d3a719e1d035ce7daa6cc051ddf89a72d34200d14cec37728e245cdabc\",\"dweb:/ipfs/QmRP7HQJpHMx1CsFrY8tXVVx1DQmi2dcb2BoGfiWaA923r\"]},\"lib/forge-std/src/StdJson.sol\":{\"keccak256\":\"0xbc0132abe1c2accc2867c0f03667afffdf92f3e95a581bb03c9557eaa38ea500\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://eb6fab37dc73c219cfbb7b4f4998bcf7677ca5397a867e850f40232192073974\",\"dweb:/ipfs/QmUHsbVdp9SKmgek7ZfPcLTKrpZFXpqaqt4sVejzxGEQL3\"]},\"lib/forge-std/src/StdMath.sol\":{\"keccak256\":\"0xd90ad4fd8aeaeb8929964e686e769fdedd5eded3fc3815df194a0ab9f91a3fb2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7919b70f636c7b805223992f28ad1ad0145d6c1385b5931a3589aface5fe6c92\",\"dweb:/ipfs/QmY7FRaULwoGgFteF8GawjQJRfasNgpWnU2aiMsFrYpuTC\"]},\"lib/forge-std/src/StdStorage.sol\":{\"keccak256\":\"0x04102de0a79398e4bdea57b7a4818655b4cc66d6f81d1cff08bf428cd0b384cd\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://53edc6c8f7f67cafc0129f039637c77d979880f7f1947defea31e8f0c05095bc\",\"dweb:/ipfs/QmUKXJd1vFCkxxrkXNLURdXrx2apoyWQFrFb5UqNkjdHVi\"]},\"lib/forge-std/src/StdStyle.sol\":{\"keccak256\":\"0x43e2a8a9b9c2574dabe74f11adf6f782df218f463540e3b5b563609fe108597d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://51363ca97404cf4128e1141428949768c31929e75e014b02c85e887fbbb4f1b8\",\"dweb:/ipfs/QmVhtbQc2fU4rRmbcfBtz34mAgG4BAZBsbna1Ca4SkoPsK\"]},\"lib/forge-std/src/StdUtils.sol\":{\"keccak256\":\"0xb2469a902a326074034c4f7081d868113db0edbb7cf48b86528af2d6b07295f8\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1430a81c4978be875e2a3b31a8bfa4e1438fecd327f23771b690d64db63c020a\",\"dweb:/ipfs/QmW6aB2u1LNaRgGQFwjV7L7UbxsRg63iJ7AuujPouEa4cT\"]},\"lib/forge-std/src/Vm.sol\":{\"keccak256\":\"0x6f235e293b4406784ff3bd89b2e4d51d0922918cf6825e7a7a09413606932e39\",\"license\":\"MIT OR Apache-2.0\",\"urls\":[\"bzz-raw://afc4fceb24a18596a239e82eb354c41d9a4e28d3fc56fcce04146af2c692b1b9\",\"dweb:/ipfs/QmQCHSAb5k9KQW9kEzeoPdirSknkWso4q4t5CSVFLNnMLB\"]},\"lib/forge-std/src/console.sol\":{\"keccak256\":\"0x4bbf47eb762cef93729d6ef15e78789957147039b113e5d4df48e3d3fd16d0f5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://af9e3a7c3d82fb5b10b57ca4d1a82f2acbef80c077f6f6ef0cc0187c7bfd9f57\",\"dweb:/ipfs/QmR9VzmnBDJpgiDP6CHT6truehukF9HpYvuP6kRiJbDwPP\"]},\"lib/forge-std/src/console2.sol\":{\"keccak256\":\"0x3b8fe79f48f065a4e4d35362171304a33784c3a90febae5f2787805a438de12f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://61de63af08803549299e68b6e6e88d40f3c5afac450e4ee0a228c66a61ba003d\",\"dweb:/ipfs/QmWVoQ5rrVxnczD4ZZoPbD4PC9Z3uExJtzjD4awTqd14MZ\"]},\"lib/forge-std/src/interfaces/IMulticall3.sol\":{\"keccak256\":\"0x7aac1389150499a922d1f9ef5749c908cef127cb2075b92fa17e9cb611263d0a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d95ebb7c7c463e08ebc12dab639945752fb2480acfc6e86da32f72732a7fd0c0\",\"dweb:/ipfs/QmNXK8P8oPWwajsQHvAHw3JPyQidPLCGQN3hWu1Lk6PBL2\"]},\"lib/forge-std/src/safeconsole.sol\":{\"keccak256\":\"0xbef9786cb49d3eade757bad87568c49c8c8f35721f0193c95ffb055d9e466e11\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://3bafd2b0b2d28068d329f95ea8a1fbce3719c257fcb863fc01abcbafd8d531ab\",\"dweb:/ipfs/QmUeaFjKWTVDBsHVfSob4mwt6A5hTnKDz22HaUXeZhypa3\"]},\"node_modules/@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol\":{\"keccak256\":\"0x385eb7fb335b3c7037e5d2ecf119f42baa4f69fbc535daf1effbc26e774a6a4a\",\"license\":\"Apache-2.0\",\"urls\":[\"bzz-raw://b62bfbf9e5969390d22c4ad0a6c5d64a1091d0cdef3e19e72482333c88c0e39b\",\"dweb:/ipfs/QmaN1oB9u82CaxYcGyKDxZKDhjYiM8J324AE6j5yCjFReK\"]},\"node_modules/@pythnetwork/entropy-sdk-solidity/EntropyEventsV2.sol\":{\"keccak256\":\"0xc8c2438857680a605d6b441f0b5fa21054dce0ae1db0a7ef8b8ab14a97249e4e\",\"license\":\"Apache-2.0\",\"urls\":[\"bzz-raw://81739805ac90c9bc91e87e4e42d57c5420cfb179a3f9088fb8416e4ba2eeade3\",\"dweb:/ipfs/QmSvrb38Bn8tDCcaC9vZpV4J8BnXy8y9fSNMaUdNaKMA28\"]},\"node_modules/@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol\":{\"keccak256\":\"0xc23ba702644b68f402b5cd1ef98da7c194ae4a3d05249a88b75160c503704809\",\"license\":\"Apache 2\",\"urls\":[\"bzz-raw://b2ac288f4e8cd2484cf8abb7bb709e4709e4ffa10697945498ba365312cfe191\",\"dweb:/ipfs/Qme9QS4P94gb9B81qLYX3EE2pQrb7MJSAaZygHuydpZo6n\"]},\"node_modules/@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol\":{\"keccak256\":\"0xca3e9a064e5e557f767475d4a543399c315babce9681f98454950e7fe52ed44c\",\"license\":\"Apache 2\",\"urls\":[\"bzz-raw://149efc8c37b0d325da7ee2dae5bfffcbd6f420acdb8445f0744e351b4a392688\",\"dweb:/ipfs/QmUW5Z72iFDwxdeWh76kYNyT1agDao2AVmpc4zSC5q8Laz\"]},\"node_modules/@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol\":{\"keccak256\":\"0xf3d3dee1e9cbdef70b6c1f4d79aa8b438413e4636c00e79e615da9dc4df9c379\",\"license\":\"Apache 2\",\"urls\":[\"bzz-raw://0e473522447c8f92a43f4fa3e54d83a789e12cc44b2a86847bd238a7f8827952\",\"dweb:/ipfs/Qmdihx73a89EZYy2GpitTxK92SWDLyPWeWnJTZ4Acva958\"]},\"node_modules/@pythnetwork/entropy-sdk-solidity/IEntropyV2.sol\":{\"keccak256\":\"0xbfa09defc676b17bc4cb9d8e17f703c885de676d16396e45963dbe104afaba6a\",\"license\":\"Apache 2\",\"urls\":[\"bzz-raw://cdf892b3def9c3e506e197ffe1ed1c040d6e4e668b9249439eacac965d98c3d5\",\"dweb:/ipfs/QmRzQev7pKackFa33pVqwjf3iYt6GdDzCCvwUY5c8Z4tXU\"]},\"script/DeployRandomnessGen.s.sol\":{\"keccak256\":\"0xe50256a3ad078a72593b94ef80d19bb8bc3b2308df9fb89a41db67973ba56bb7\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://1092d5a23da5fa812d06cbdd514a4834155151c7f1ed5988902bdb9663b0e158\",\"dweb:/ipfs/QmNRuMPQCpT1sBn8ab8jgbGYjMmjE1qXEnsUrMqb6CdpNt\"]},\"src/RandomnessGen.sol\":{\"keccak256\":\"0x779a8c4fe24c1f21fcd7b97fb27aeb7edebefeded78894861cf4e2d5bab9de70\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://5ac56f310bc49d3da2ff0e7accba8ed31704cb3d0ec1c63a42156252640b11db\",\"dweb:/ipfs/QmazGk8UVQQpRKwJsmjS5PSve1rcZn4KgDqj5getBCAEPT\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.30+commit.73712a01"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"view","type":"function","name":"IS_SCRIPT","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"run"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@pythnetwork/entropy-sdk-solidity/=node_modules/@pythnetwork/entropy-sdk-solidity/","forge-std/=lib/forge-std/src/"],"optimizer":{"enabled":false,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"script/DeployRandomnessGen.s.sol":"DeployRandomnessGen"},"evmVersion":"prague","libraries":{}},"sources":{"lib/forge-std/src/Base.sol":{"keccak256":"0x4b2a5a85e045dcf6a082700c7252e43854c2eed88f860aaa18ec1e85218ae2bf","urls":["bzz-raw://98d060ed5be569a92d908fc358149039dc8f833d61973aa1b9d1d8235676bf6d","dweb:/ipfs/QmaWQpn5dJmbMS5skwmPPMeUWZG35BLkignPpcA3zyagEs"],"license":"MIT"},"lib/forge-std/src/Script.sol":{"keccak256":"0xc942e27c7baae499beb01afbbae99f24d42af9a6e4aae675bc6901b704aa8e9b","urls":["bzz-raw://0456008adf68947247f358b62863af4a8e349549d2260f2ff9569ff0e3cf5c98","dweb:/ipfs/QmdviSUj2i7o3TPN5vd2xocqGMFVqjUzaiJTZRYyPxyHPx"],"license":"MIT"},"lib/forge-std/src/StdChains.sol":{"keccak256":"0xae394f477769a38276d98d4854bc865fc8d281edbd4e72167507adb8236812aa","urls":["bzz-raw://34a0e609a4ec617b5c349f5e89a3352810cc5e4d3adaf939b32a27e4a5e46de2","dweb:/ipfs/QmPfjimWAGGb6rzDjNMtLeZ93JJbCJJMov5gaNKyTy1doe"],"license":"MIT"},"lib/forge-std/src/StdCheats.sol":{"keccak256":"0x0fa6ec03602648b62cce41aab2096e6b7e052f2846075d967b6958dd586db746","urls":["bzz-raw://cd84e2ca9c1eaed6b76768cc12bb8c1af8289170ea8b7706f58d516460d79c41","dweb:/ipfs/QmQ7BK7co6DE4eWUqMyv11s5eHYkS1tyx8tDSZGZVtf2aK"],"license":"MIT"},"lib/forge-std/src/StdConstants.sol":{"keccak256":"0x319ccdabfa2c0b2428301445873270ffea20f0e039d4fd5e6eeba65158e4e534","urls":["bzz-raw://b633f9d3a719e1d035ce7daa6cc051ddf89a72d34200d14cec37728e245cdabc","dweb:/ipfs/QmRP7HQJpHMx1CsFrY8tXVVx1DQmi2dcb2BoGfiWaA923r"],"license":"MIT"},"lib/forge-std/src/StdJson.sol":{"keccak256":"0xbc0132abe1c2accc2867c0f03667afffdf92f3e95a581bb03c9557eaa38ea500","urls":["bzz-raw://eb6fab37dc73c219cfbb7b4f4998bcf7677ca5397a867e850f40232192073974","dweb:/ipfs/QmUHsbVdp9SKmgek7ZfPcLTKrpZFXpqaqt4sVejzxGEQL3"],"license":"MIT"},"lib/forge-std/src/StdMath.sol":{"keccak256":"0xd90ad4fd8aeaeb8929964e686e769fdedd5eded3fc3815df194a0ab9f91a3fb2","urls":["bzz-raw://7919b70f636c7b805223992f28ad1ad0145d6c1385b5931a3589aface5fe6c92","dweb:/ipfs/QmY7FRaULwoGgFteF8GawjQJRfasNgpWnU2aiMsFrYpuTC"],"license":"MIT"},"lib/forge-std/src/StdStorage.sol":{"keccak256":"0x04102de0a79398e4bdea57b7a4818655b4cc66d6f81d1cff08bf428cd0b384cd","urls":["bzz-raw://53edc6c8f7f67cafc0129f039637c77d979880f7f1947defea31e8f0c05095bc","dweb:/ipfs/QmUKXJd1vFCkxxrkXNLURdXrx2apoyWQFrFb5UqNkjdHVi"],"license":"MIT"},"lib/forge-std/src/StdStyle.sol":{"keccak256":"0x43e2a8a9b9c2574dabe74f11adf6f782df218f463540e3b5b563609fe108597d","urls":["bzz-raw://51363ca97404cf4128e1141428949768c31929e75e014b02c85e887fbbb4f1b8","dweb:/ipfs/QmVhtbQc2fU4rRmbcfBtz34mAgG4BAZBsbna1Ca4SkoPsK"],"license":"MIT"},"lib/forge-std/src/StdUtils.sol":{"keccak256":"0xb2469a902a326074034c4f7081d868113db0edbb7cf48b86528af2d6b07295f8","urls":["bzz-raw://1430a81c4978be875e2a3b31a8bfa4e1438fecd327f23771b690d64db63c020a","dweb:/ipfs/QmW6aB2u1LNaRgGQFwjV7L7UbxsRg63iJ7AuujPouEa4cT"],"license":"MIT"},"lib/forge-std/src/Vm.sol":{"keccak256":"0x6f235e293b4406784ff3bd89b2e4d51d0922918cf6825e7a7a09413606932e39","urls":["bzz-raw://afc4fceb24a18596a239e82eb354c41d9a4e28d3fc56fcce04146af2c692b1b9","dweb:/ipfs/QmQCHSAb5k9KQW9kEzeoPdirSknkWso4q4t5CSVFLNnMLB"],"license":"MIT OR Apache-2.0"},"lib/forge-std/src/console.sol":{"keccak256":"0x4bbf47eb762cef93729d6ef15e78789957147039b113e5d4df48e3d3fd16d0f5","urls":["bzz-raw://af9e3a7c3d82fb5b10b57ca4d1a82f2acbef80c077f6f6ef0cc0187c7bfd9f57","dweb:/ipfs/QmR9VzmnBDJpgiDP6CHT6truehukF9HpYvuP6kRiJbDwPP"],"license":"MIT"},"lib/forge-std/src/console2.sol":{"keccak256":"0x3b8fe79f48f065a4e4d35362171304a33784c3a90febae5f2787805a438de12f","urls":["bzz-raw://61de63af08803549299e68b6e6e88d40f3c5afac450e4ee0a228c66a61ba003d","dweb:/ipfs/QmWVoQ5rrVxnczD4ZZoPbD4PC9Z3uExJtzjD4awTqd14MZ"],"license":"MIT"},"lib/forge-std/src/interfaces/IMulticall3.sol":{"keccak256":"0x7aac1389150499a922d1f9ef5749c908cef127cb2075b92fa17e9cb611263d0a","urls":["bzz-raw://d95ebb7c7c463e08ebc12dab639945752fb2480acfc6e86da32f72732a7fd0c0","dweb:/ipfs/QmNXK8P8oPWwajsQHvAHw3JPyQidPLCGQN3hWu1Lk6PBL2"],"license":"MIT"},"lib/forge-std/src/safeconsole.sol":{"keccak256":"0xbef9786cb49d3eade757bad87568c49c8c8f35721f0193c95ffb055d9e466e11","urls":["bzz-raw://3bafd2b0b2d28068d329f95ea8a1fbce3719c257fcb863fc01abcbafd8d531ab","dweb:/ipfs/QmUeaFjKWTVDBsHVfSob4mwt6A5hTnKDz22HaUXeZhypa3"],"license":"MIT"},"node_modules/@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol":{"keccak256":"0x385eb7fb335b3c7037e5d2ecf119f42baa4f69fbc535daf1effbc26e774a6a4a","urls":["bzz-raw://b62bfbf9e5969390d22c4ad0a6c5d64a1091d0cdef3e19e72482333c88c0e39b","dweb:/ipfs/QmaN1oB9u82CaxYcGyKDxZKDhjYiM8J324AE6j5yCjFReK"],"license":"Apache-2.0"},"node_modules/@pythnetwork/entropy-sdk-solidity/EntropyEventsV2.sol":{"keccak256":"0xc8c2438857680a605d6b441f0b5fa21054dce0ae1db0a7ef8b8ab14a97249e4e","urls":["bzz-raw://81739805ac90c9bc91e87e4e42d57c5420cfb179a3f9088fb8416e4ba2eeade3","dweb:/ipfs/QmSvrb38Bn8tDCcaC9vZpV4J8BnXy8y9fSNMaUdNaKMA28"],"license":"Apache-2.0"},"node_modules/@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol":{"keccak256":"0xc23ba702644b68f402b5cd1ef98da7c194ae4a3d05249a88b75160c503704809","urls":["bzz-raw://b2ac288f4e8cd2484cf8abb7bb709e4709e4ffa10697945498ba365312cfe191","dweb:/ipfs/Qme9QS4P94gb9B81qLYX3EE2pQrb7MJSAaZygHuydpZo6n"],"license":"Apache 2"},"node_modules/@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol":{"keccak256":"0xca3e9a064e5e557f767475d4a543399c315babce9681f98454950e7fe52ed44c","urls":["bzz-raw://149efc8c37b0d325da7ee2dae5bfffcbd6f420acdb8445f0744e351b4a392688","dweb:/ipfs/QmUW5Z72iFDwxdeWh76kYNyT1agDao2AVmpc4zSC5q8Laz"],"license":"Apache 2"},"node_modules/@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol":{"keccak256":"0xf3d3dee1e9cbdef70b6c1f4d79aa8b438413e4636c00e79e615da9dc4df9c379","urls":["bzz-raw://0e473522447c8f92a43f4fa3e54d83a789e12cc44b2a86847bd238a7f8827952","dweb:/ipfs/Qmdihx73a89EZYy2GpitTxK92SWDLyPWeWnJTZ4Acva958"],"license":"Apache 2"},"node_modules/@pythnetwork/entropy-sdk-solidity/IEntropyV2.sol":{"keccak256":"0xbfa09defc676b17bc4cb9d8e17f703c885de676d16396e45963dbe104afaba6a","urls":["bzz-raw://cdf892b3def9c3e506e197ffe1ed1c040d6e4e668b9249439eacac965d98c3d5","dweb:/ipfs/QmRzQev7pKackFa33pVqwjf3iYt6GdDzCCvwUY5c8Z4tXU"],"license":"Apache 2"},"script/DeployRandomnessGen.s.sol":{"keccak256":"0xe50256a3ad078a72593b94ef80d19bb8bc3b2308df9fb89a41db67973ba56bb7","urls":["bzz-raw://1092d5a23da5fa812d06cbdd514a4834155151c7f1ed5988902bdb9663b0e158","dweb:/ipfs/QmNRuMPQCpT1sBn8ab8jgbGYjMmjE1qXEnsUrMqb6CdpNt"],"license":"UNLICENSED"},"src/RandomnessGen.sol":{"keccak256":"0x779a8c4fe24c1f21fcd7b97fb27aeb7edebefeded78894861cf4e2d5bab9de70","urls":["bzz-raw://5ac56f310bc49d3da2ff0e7accba8ed31704cb3d0ec1c63a42156252640b11db","dweb:/ipfs/QmazGk8UVQQpRKwJsmjS5PSve1rcZn4KgDqj5getBCAEPT"],"license":"UNLICENSED"}},"version":1},"id":21} \ No newline at end of file diff --git a/entropy/whiphash/app/api/nildb/read-collection/route.ts b/entropy/whiphash/app/api/nildb/read-collection/route.ts new file mode 100644 index 0000000..39c951a --- /dev/null +++ b/entropy/whiphash/app/api/nildb/read-collection/route.ts @@ -0,0 +1,153 @@ +import { NextResponse } from 'next/server'; +import { Keypair } from '@nillion/nuc'; +import { SecretVaultBuilderClient } from '@nillion/secretvaults'; +import { config as loadEnv } from 'dotenv'; + +// Load environment variables +loadEnv(); + +export async function GET() { + try { + // Load environment variables + const NILLION_API_KEY = process.env.NILLION_API_KEY; + const NILLION_COLLECTION_ID = process.env.NILLION_COLLECTION_ID; + + // Validate environment variables + if (!NILLION_API_KEY || !NILLION_COLLECTION_ID) { + console.error('❌ Missing required environment variables:', { + hasApiKey: !!NILLION_API_KEY, + hasCollectionId: !!NILLION_COLLECTION_ID + }); + + return NextResponse.json( + { + success: false, + error: 'Missing required environment variables: NILLION_API_KEY and NILLION_COLLECTION_ID' + }, + { status: 500 } + ); + } + + console.log('🔍 Reading all records from NilDB collection...'); + console.log('📊 Collection ID:', NILLION_COLLECTION_ID); + + // Create builder client + const builder = await SecretVaultBuilderClient.from({ + keypair: Keypair.from(NILLION_API_KEY), + urls: { + chain: 'http://rpc.testnet.nilchain-rpc-proxy.nilogy.xyz', + auth: 'https://nilauth.sandbox.app-cluster.sandbox.nilogy.xyz', + dbs: [ + 'https://nildb-stg-n1.nillion.network', + 'https://nildb-stg-n2.nillion.network', + 'https://nildb-stg-n3.nillion.network', + ], + }, + blindfold: { operation: 'store' }, + }); + + console.log('✅ Builder client created successfully'); + + // Refresh authentication + await builder.refreshRootToken(); + console.log('✅ Authentication refreshed'); + + // First, get all document references from the collection + const response = await builder.findData({ + collection: NILLION_COLLECTION_ID, + filter: {}, // Empty filter returns all records + }); + + console.log(`✅ Found ${response.data.length} records in collection`); + + // Transform the data - check if builder's read permissions allow decryption + const transformedData = response.data.map((item: Record) => { + console.log(`🔍 Processing item:`, { + id: item._id, + name: item.name, + hasPhone: !!item.phone, + hasEmail: !!item.email, + phoneType: typeof item.phone, + emailType: typeof item.email + }); + + // Check if the data is already decrypted (builder has read permissions) + let password = '[Encrypted - Cannot decrypt]'; + let socials = '[Encrypted - Cannot decrypt]'; + + // If phone field exists and is a string (not an object with %share), it might be decrypted + if (item.phone && typeof item.phone === 'string') { + password = item.phone as string; + console.log(`✅ Password appears to be decrypted: ${password.substring(0, 10)}...`); + } else if (item.phone && typeof item.phone === 'object' && item.phone !== null && '%share' in item.phone) { + password = (item.phone as Record)['%share']; + console.log(`✅ Password from %share: ${password.substring(0, 10)}...`); + } + + // If email field exists and is a string (not an object with %share), it might be decrypted + if (item.email && typeof item.email === 'string') { + socials = item.email as string; + console.log(`✅ Socials appear to be decrypted: ${socials.substring(0, 20)}...`); + } else if (item.email && typeof item.email === 'object' && item.email !== null && '%share' in item.email) { + socials = (item.email as Record)['%share']; + console.log(`✅ Socials from %share: ${socials.substring(0, 20)}...`); + } + + return { + id: item._id || Math.random().toString(36), + name: item.name || 'Unnamed Password', + password: password, + socials: socials, + createdAt: item.createdAt || item.timestamp || new Date().toISOString(), + txHash: item.txHash || ((item.metadata as Record)?.txHash) || '', + sequenceNumber: item.sequenceNumber || ((item.metadata as Record)?.sequenceNumber) || '', + // Include raw data for debugging + rawData: item + }; + }); + + console.log('📊 Processed data:', transformedData); + + return NextResponse.json({ + success: true, + data: transformedData, + totalRecords: response.data.length, + collection: NILLION_COLLECTION_ID, + timestamp: new Date().toISOString() + }); + + } catch (error) { + console.error('❌ Error reading collection:', error); + + let errorMessage = 'Failed to read passwords from NilDB'; + let errorDetails = 'Unknown error'; + + if (error instanceof Error) { + errorMessage = error.message; + errorDetails = error.stack || error.message; + } else if (typeof error === 'string') { + errorMessage = error; + errorDetails = error; + } else if (error && typeof error === 'object') { + try { + errorMessage = (error as { message?: string }).message || 'Object error'; + errorDetails = JSON.stringify(error, null, 2); + } catch { + errorMessage = 'Object error (could not stringify)'; + errorDetails = String(error); + } + } + + console.error('❌ Processed error details:', { errorMessage, errorDetails }); + + return NextResponse.json( + { + success: false, + error: errorMessage, + details: errorDetails, + timestamp: new Date().toISOString() + }, + { status: 500 } + ); + } +} diff --git a/entropy/whiphash/app/api/nildb/store-password/route.ts b/entropy/whiphash/app/api/nildb/store-password/route.ts new file mode 100644 index 0000000..4212629 --- /dev/null +++ b/entropy/whiphash/app/api/nildb/store-password/route.ts @@ -0,0 +1,313 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { randomUUID } from 'crypto'; +import { config as loadEnv } from 'dotenv'; + +// Load environment variables +loadEnv(); + +// Import Nillion SDK components +import { + Keypair, + NucTokenBuilder, + Command, +} from '@nillion/nuc'; +import { + SecretVaultBuilderClient, + SecretVaultUserClient, +} from '@nillion/secretvaults'; + +// Configuration from environment variables (same as demo.js) +const config = { + NILCHAIN_URL: process.env.NILCHAIN_URL, + NILAUTH_URL: process.env.NILAUTH_URL, + NILDB_NODES: process.env.NILDB_NODES?.split(',') || [], + BUILDER_PRIVATE_KEY: process.env.BUILDER_PRIVATE_KEY, +}; + +// Validate configuration +if (!config.BUILDER_PRIVATE_KEY) { + console.error('❌ Please set BUILDER_PRIVATE_KEY in your .env file'); +} + +export async function POST(request: NextRequest) { + try { + const { password, name, metadata, socials } = await request.json(); + + // Check all required environment variables + const missingVars = []; + if (!config.BUILDER_PRIVATE_KEY) missingVars.push('BUILDER_PRIVATE_KEY'); + if (!config.NILCHAIN_URL) missingVars.push('NILCHAIN_URL'); + if (!config.NILAUTH_URL) missingVars.push('NILAUTH_URL'); + if (!config.NILDB_NODES || config.NILDB_NODES.length === 0) missingVars.push('NILDB_NODES'); + + if (missingVars.length > 0) { + console.error('❌ Missing environment variables:', missingVars); + return NextResponse.json( + { error: `Missing environment variables: ${missingVars.join(', ')}` }, + { status: 500 } + ); + } + + // Validate private key format + try { + if (!config.BUILDER_PRIVATE_KEY!.startsWith('0x') && config.BUILDER_PRIVATE_KEY!.length !== 64) { + throw new Error('Invalid private key format'); + } + } catch (keyError) { + console.error('❌ Invalid BUILDER_PRIVATE_KEY format:', keyError); + return NextResponse.json( + { error: 'BUILDER_PRIVATE_KEY must be a valid hex string (64 characters or 0x prefixed)' }, + { status: 500 } + ); + } + + console.log('💾 Storing password in NilDB using demo.js structure...'); + console.log('🔧 Config check:', { + hasBuilderKey: !!config.BUILDER_PRIVATE_KEY, + hasChainUrl: !!config.NILCHAIN_URL, + hasAuthUrl: !!config.NILAUTH_URL, + nodeCount: config.NILDB_NODES.length + }); + console.log('📝 Received data:', { + passwordLength: password?.length || 0, + name: name, + socialsLength: socials?.length || 0, + socials: socials, + hasMetadata: !!metadata + }); + + // Step 1: Create keypairs for builder and user (same as demo.js) + let builderKeypair; + try { + console.log('🔑 Creating builder keypair...'); + builderKeypair = Keypair.from(config.BUILDER_PRIVATE_KEY!); + console.log('✅ Builder keypair created successfully'); + } catch (keypairError) { + console.error('❌ Failed to create builder keypair:', keypairError); + return NextResponse.json( + { error: 'Invalid BUILDER_PRIVATE_KEY format. Must be a valid hex string.' }, + { status: 500 } + ); + } + + console.log('👤 Creating user keypair...'); + const userKeypair = Keypair.generate(); + console.log('✅ User keypair created successfully'); + + const builderDid = builderKeypair.toDid().toString(); + const userDid = userKeypair.toDid().toString(); + + console.log('Builder DID:', builderDid); + console.log('User DID:', userDid); + + // Step 2: Create payer (same as demo.js) + // const payer = await new PayerBuilder() + // .keypair(builderKeypair) + // .chainUrl(config.NILCHAIN_URL!) + // .build(); + + // Step 3: Create builder client (same as demo.js) + console.log('🏗️ Creating builder client...'); + const builder = await SecretVaultBuilderClient.from({ + keypair: builderKeypair, + urls: { + chain: config.NILCHAIN_URL!, + auth: config.NILAUTH_URL!, + dbs: config.NILDB_NODES, + }, + }); + console.log('✅ Builder client created successfully'); + + // Refresh token using existing subscription + console.log('🔄 Refreshing root token...'); + await builder.refreshRootToken(); + console.log('✅ Root token refreshed successfully'); + + // Step 4: Register builder (same as demo.js) + try { + const existingProfile = await builder.readProfile(); + console.log('✅ Builder already registered:', existingProfile.data.name); + } catch { + try { + await builder.register({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + did: builderDid as any, + name: 'My Demo Builder', // Same name as demo.js + }); + console.log('✅ Builder registered successfully'); + } catch (registerError: unknown) { + if ((registerError as Error)?.message?.includes('duplicate key')) { + console.log('✅ Builder already registered (duplicate key)'); + } else { + throw registerError; + } + } + } + + // Step 5: Define collection (exact same as demo.js) + const collectionId = '48fb09ba-cf33-4576-a0f8-9e00d39e9aec'; // Same ID as demo.js + + const collection = { + _id: collectionId, + type: 'owned' as const, // Every document in the collection will be user-owned + name: 'User Profile Collection', // Same name as demo.js + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'array', + uniqueItems: true, + items: { + type: 'object', + properties: { + _id: { type: 'string', format: 'uuid' }, + name: { type: 'string' }, // name will not be secret shared + email: { // email will be secret shared (we'll store socials here) + type: "object", + properties: { + "%share": { + type: "string" + } + }, + required: [ + "%share" + ] + }, + phone: { // phone will be secret shared (we'll store password here) + type: "object", + properties: { + "%share": { + type: "string" + } + }, + required: [ + "%share" + ] + }, + }, + required: ['_id', 'name', 'email'], // Same as demo.js + }, + }, + }; + + // Step 6: Create the owned collection (same as demo.js) + try { + const createResults = await builder.createCollection(collection); + console.log( + '✅ Password collection created on', + Object.keys(createResults).length, + 'nodes' + ); + } catch (error: unknown) { + console.log('✅ Password collection already exists or creation failed:', (error as Error)?.message); + } + + // Step 7: Create user client (same as demo.js) + console.log('👤 Creating user client...'); + const user = await SecretVaultUserClient.from({ + baseUrls: config.NILDB_NODES, + keypair: userKeypair, + blindfold: { + operation: 'store', + }, + }); + console.log('✅ User client created successfully'); + + // Step 8: Builder grants write access to the user (same as demo.js) + console.log('🔐 Creating delegation token...'); + const delegation = NucTokenBuilder.extending(builder.rootToken) + .command(new Command(['nil', 'db', 'data', 'create'])) + .audience(userKeypair.toDid()) + .expiresAt(Math.floor(Date.now() / 1000) + 3600) // 1 hour + .build(builderKeypair.privateKey()); + console.log('✅ Delegation token created successfully'); + + // Step 9: User's private data (exact same format as demo.js) + // %allot indicates that the client should encrypt this data + const userPrivateData = { + _id: randomUUID(), + name: name || `WhipHash Password - ${new Date().toLocaleString()}`, + email: { + '%allot': socials || 'No socials provided', // Store user's socials input in email field + }, + phone: { + '%allot': password, // Store password in phone field + }, + }; + + console.log('📦 Final data structure:', { + _id: userPrivateData._id, + name: userPrivateData.name, + emailLength: userPrivateData.email['%allot']?.length || 0, + phoneLength: userPrivateData.phone['%allot']?.length || 0, + }); + + // Step 10: User uploads data and grants builder limited access (same as demo.js) + console.log('📤 Uploading data to NilDB...'); + const uploadResults = await user.createData(delegation, { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + owner: userDid as any, + acl: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + grantee: builderDid as any, // Grant access to the builder + read: true, // Builder can read the data + write: false, // Builder cannot modify the data + execute: true, // Builder can run queries on the data + }, + collection: collectionId, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data: [userPrivateData as any], + }); + console.log('✅ Data uploaded successfully to', Object.keys(uploadResults).length, 'nodes'); + + console.log('✅ User uploaded private password data with builder access granted'); + + // Step 11: See what data the user has stored (same as demo.js) + const references = await user.listDataReferences(); + console.log('✅ User has', references.data.length, 'private password records stored'); + + return NextResponse.json({ + success: true, + message: 'Password stored successfully in NilDB', + documentId: userPrivateData._id, + collection: collectionId, + nodes: Object.keys(uploadResults).length, + timestamp: new Date().toISOString(), + totalRecords: references.data.length + }); + + } catch (error) { + console.error('❌ Failed to store password in NilDB:', error); + console.error('❌ Error type:', typeof error); + console.error('❌ Error constructor:', error?.constructor?.name); + console.error('❌ Error string:', String(error)); + + let errorMessage = 'Failed to store password in NilDB'; + let errorDetails = 'Unknown error'; + + if (error instanceof Error) { + errorMessage = error.message; + errorDetails = error.stack || error.message; + } else if (typeof error === 'string') { + errorMessage = error; + errorDetails = error; + } else if (error && typeof error === 'object') { + try { + errorMessage = (error as { message?: string }).message || 'Object error'; + errorDetails = JSON.stringify(error, null, 2); + } catch { + errorMessage = 'Object error (could not stringify)'; + errorDetails = String(error); + } + } + + console.error('❌ Processed error details:', { errorMessage, errorDetails }); + + return NextResponse.json( + { + error: errorMessage, + details: errorDetails, + timestamp: new Date().toISOString() + }, + { status: 500 } + ); + } +} diff --git a/entropy/whiphash/app/api/nildb/test-config/route.ts b/entropy/whiphash/app/api/nildb/test-config/route.ts new file mode 100644 index 0000000..edd8a2c --- /dev/null +++ b/entropy/whiphash/app/api/nildb/test-config/route.ts @@ -0,0 +1,64 @@ +import { NextResponse } from 'next/server'; +import { config as loadEnv } from 'dotenv'; + +// Load environment variables +loadEnv(); + +// Configuration from environment variables +const config = { + NILCHAIN_URL: process.env.NILCHAIN_URL, + NILAUTH_URL: process.env.NILAUTH_URL, + NILDB_NODES: process.env.NILDB_NODES?.split(',') || [], + BUILDER_PRIVATE_KEY: process.env.BUILDER_PRIVATE_KEY, +}; + +export async function GET() { + const missingVars = []; + if (!config.BUILDER_PRIVATE_KEY) missingVars.push('BUILDER_PRIVATE_KEY'); + if (!config.NILCHAIN_URL) missingVars.push('NILCHAIN_URL'); + if (!config.NILAUTH_URL) missingVars.push('NILAUTH_URL'); + if (!config.NILDB_NODES || config.NILDB_NODES.length === 0) missingVars.push('NILDB_NODES'); + + // Validate private key format + let privateKeyValid = false; + let privateKeyError = ''; + + if (config.BUILDER_PRIVATE_KEY) { + try { + if (config.BUILDER_PRIVATE_KEY.startsWith('0x') && config.BUILDER_PRIVATE_KEY.length === 66) { + privateKeyValid = true; + } else if (!config.BUILDER_PRIVATE_KEY.startsWith('0x') && config.BUILDER_PRIVATE_KEY.length === 64) { + privateKeyValid = true; + } else { + privateKeyError = 'Invalid format - must be 64 hex chars or 66 chars with 0x prefix'; + } + } catch (error) { + privateKeyError = 'Invalid hex string'; + } + } + + return NextResponse.json({ + configured: missingVars.length === 0 && privateKeyValid, + missingVariables: missingVars, + privateKeyValid, + privateKeyError, + config: { + NILCHAIN_URL: config.NILCHAIN_URL ? 'Set' : 'Not set', + NILAUTH_URL: config.NILAUTH_URL ? 'Set' : 'Not set', + NILDB_NODES: config.NILDB_NODES.length > 0 ? `${config.NILDB_NODES.length} nodes` : 'Not set', + BUILDER_PRIVATE_KEY: config.BUILDER_PRIVATE_KEY ? + (privateKeyValid ? 'Valid format' : `Invalid: ${privateKeyError}`) : 'Not set', + }, + recommendations: missingVars.length > 0 ? [ + 'Set up environment variables in .env.local', + 'Get values from Nillion documentation', + 'Ensure BUILDER_PRIVATE_KEY is a valid hex string' + ] : privateKeyValid ? [ + 'All configuration looks good!', + 'NilDB should work properly' + ] : [ + 'Fix BUILDER_PRIVATE_KEY format', + 'Must be 64 hex characters or 66 with 0x prefix' + ] + }); +} diff --git a/entropy/whiphash/app/favicon.ico b/entropy/whiphash/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/entropy/whiphash/app/globals.css b/entropy/whiphash/app/globals.css new file mode 100644 index 0000000..c7722b9 --- /dev/null +++ b/entropy/whiphash/app/globals.css @@ -0,0 +1,151 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --animate-shimmer-slide: shimmer-slide var(--speed) ease-in-out infinite alternate; + --animate-spin-around: spin-around calc(var(--speed) * 2) infinite linear; + @keyframes shimmer-slide { + to { + transform: translate(calc(100cqw - 100%), 0); + } + } + @keyframes spin-around { + 0% { + transform: translateZ(0) rotate(0); + } + 15%, 35% { + transform: translateZ(0) rotate(90deg); + } + 65%, 85% { + transform: translateZ(0) rotate(270deg); + } + 100% { + transform: translateZ(0) rotate(360deg); + } + } + --animate-shine: shine var(--duration) infinite linear +; + @keyframes shine { + 0% { + background-position: 0% 0%;} + 50% { + background-position: 100% 100%;} + to { + background-position: 0% 0%;}}} + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/entropy/whiphash/app/layout.tsx b/entropy/whiphash/app/layout.tsx new file mode 100644 index 0000000..e491549 --- /dev/null +++ b/entropy/whiphash/app/layout.tsx @@ -0,0 +1,27 @@ +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import './globals.css' +import Providers from './providers' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'WhipHash', + description: 'A decentralized application', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + + {children} + + + + ) +} \ No newline at end of file diff --git a/entropy/whiphash/app/page.tsx b/entropy/whiphash/app/page.tsx new file mode 100644 index 0000000..98eacef --- /dev/null +++ b/entropy/whiphash/app/page.tsx @@ -0,0 +1,73 @@ +'use client' + +import React from 'react' +import Dither from '../components/Dither' +import { ShimmerButton } from '../components/ui/shimmer-button' +import ShinyText from '../components/ShinyText' + +export default function HomePage() { + return ( +
+ {/* Dither Background */} +
+ +
+ + {/* Dark Overlay */} +
+ + {/* Main Content */} +
+
+ {/* Main Title */} +

+ whiphash. +

+ + {/* Shiny Text */} +
+ +
+ + {/* Shimmer Button */} +
+ window.location.href = '/test'} + > + get started + +
+ + {/* View Passwords Link */} +
+ +
+
+
+
+ ) +} diff --git a/entropy/whiphash/app/providers.tsx b/entropy/whiphash/app/providers.tsx new file mode 100644 index 0000000..522cc8b --- /dev/null +++ b/entropy/whiphash/app/providers.tsx @@ -0,0 +1,22 @@ +'use client'; + +import {PrivyProvider} from '@privy-io/react-auth'; + +export default function Providers({children}: {children: React.ReactNode}) { + return ( + + {children} + + ); +} diff --git a/entropy/whiphash/app/test/page.tsx b/entropy/whiphash/app/test/page.tsx new file mode 100644 index 0000000..e897ffc --- /dev/null +++ b/entropy/whiphash/app/test/page.tsx @@ -0,0 +1,1029 @@ +'use client' + +import React, { useState, useEffect } from 'react' +import { createPublicClient, http, parseAbi, parseEventLogs } from 'viem' +import { baseSepolia } from 'viem/chains' +import Dither from '../../components/Dither' +import { ShimmerButton } from '../../components/ui/shimmer-button' +import { ShineBorder } from '../../components/ui/shine-border' + +interface PasswordGenerationResult { + password: string + metadata: { + txHash: string + sequenceNumber: string + deviceSecret: string + r1: string + r2: string + localRaw: string + localKey: string + seedRaw: string + passwordBytes: string + salt1: string + passwordSalt: string + argon2Params: { + memory: number + time: number + parallelism: number + } + } +} + +// Client-side password generator +class ClientPasswordGenerator { + private appSalt1: Uint8Array + private appSalt2: Uint8Array + private context: string + private context2: string + + constructor() { + // Initialize app salts as specified in genpassword.md + // These should be consistent across the app + this.appSalt1 = new Uint8Array(32) + this.appSalt2 = new Uint8Array(32) + + // Fill with deterministic values (in production, these should be app-specific constants) + for (let i = 0; i < 32; i++) { + this.appSalt1[i] = i + this.appSalt2[i] = i + 32 + } + + this.context = 'local_raw_v1' + this.context2 = 'seed_v1' + } + + // Step 1: Generate device secret (C) - + private generateDeviceSecret(): Uint8Array { + const deviceSecret = crypto.getRandomValues(new Uint8Array(32)) + console.log('🔐 Step 1: Generated device secret (C):', this.arrayToBase64(deviceSecret)) + return deviceSecret + } + + // Step 2: Extract R1 from Pyth randomness - as per genpassword.md + private extractR1(pythRandomness: { n1: string; n2: string; txHash: string; sequenceNumber: string }): Uint8Array { + const r1 = BigInt(pythRandomness.n1) + const r1Bytes = this.bigIntToUint8Array(r1, 32) + console.log('🎲 Step 2: Extracted R1 from Pyth:', pythRandomness.n1) + console.log('🎲 R1 bytes:', this.arrayToBase64(r1Bytes)) + return r1Bytes + } + + // Step 3: Mix R1 + C → local_raw (HKDF) - as per genpassword.md + private async generateLocalRaw(r1: Uint8Array, deviceSecret: Uint8Array): Promise { + // IKM = R1 || C || context + const ikm = new Uint8Array(r1.length + deviceSecret.length + this.context.length) + ikm.set(r1, 0) + ikm.set(deviceSecret, r1.length) + ikm.set(new TextEncoder().encode(this.context), r1.length + deviceSecret.length) + + console.log('🔗 HKDF Step 3 Parameters:') + console.log('🔗 R1 length:', r1.length, 'bytes') + console.log('🔗 Device Secret length:', deviceSecret.length, 'bytes') + console.log('🔗 Context:', this.context) + console.log('🔗 Context length:', this.context.length, 'bytes') + console.log('🔗 IKM total length:', ikm.length, 'bytes') + console.log('🔗 App Salt1:', this.arrayToBase64(this.appSalt1)) + console.log('🔗 Info string:', 'local_raw_v1') + console.log('🔗 Output length:', 32, 'bytes') + + // HKDF-SHA256( IKM = R1 || C || context, salt = app_salt1, info="local_raw_v1" ) + const localRaw = await this.hkdf(ikm, 32, this.appSalt1, 'local_raw_v1') + console.log('🔗 Step 3: Generated local_raw (HKDF):', this.arrayToBase64(localRaw)) + return localRaw + } + + // Step 4: Harden local_raw → LocalKey (scrypt) - as per genpassword.md + private async generateLocalKey(localRaw: Uint8Array): Promise<{ + localKey: Uint8Array + salt1: Uint8Array + argon2Params: { memory: number; time: number; parallelism: number } + }> { + const salt1 = crypto.getRandomValues(new Uint8Array(16)) + const argon2Params = { + memory: 65536, // 64 MB + time: 3, + parallelism: 4 + } + + console.log('🛡️ Scrypt Step 4 Parameters:') + console.log('🛡️ Input (local_raw) length:', localRaw.length, 'bytes') + console.log('🛡️ Salt1 length:', salt1.length, 'bytes') + console.log('🛡️ Salt1 value:', this.arrayToBase64(salt1)) + console.log('🛡️ N (memory-hard parameter):', 16384) + console.log('🛡️ r (block size factor):', 8) + console.log('🛡️ p (parallelization factor):', 1) + console.log('🛡️ Output length:', 32, 'bytes') + + // LocalKey = scrypt(local_raw, salt1, N, r, p) (outlen 32) + const localKey = await this.argon2id(localRaw, salt1, argon2Params, 32) + + console.log('🛡️ Step 4: Generated LocalKey (scrypt):', this.arrayToBase64(localKey)) + console.log('🛡️ Salt1:', this.arrayToBase64(salt1)) + console.log('🛡️ Scrypt params:', { N: 16384, r: 8, p: 1 }) + + return { + localKey, + salt1, + argon2Params + } + } + + // Step 5: Extract R2 from Pyth randomness - as per genpassword.md + private extractR2(pythRandomness: { n1: string; n2: string; txHash: string; sequenceNumber: string }): Uint8Array { + const r2 = BigInt(pythRandomness.n2) + const r2Bytes = this.bigIntToUint8Array(r2, 32) + console.log('🎲 Step 5: Extracted R2 from Pyth:', pythRandomness.n2) + console.log('🎲 R2 bytes:', this.arrayToBase64(r2Bytes)) + return r2Bytes + } + + // Step 6: Derive seed and final harden → Password_bytes - as per genpassword.md + private async generatePasswordBytes( + localKey: Uint8Array, + r2: Uint8Array + ): Promise<{ + passwordBytes: Uint8Array + passwordSalt: Uint8Array + }> { + // seed_raw = HKDF-SHA256( IKM = LocalKey || R2 || context2, salt = app_salt2, info="seed_v1" ) + const ikm = new Uint8Array(localKey.length + r2.length + this.context2.length) + ikm.set(localKey, 0) + ikm.set(r2, localKey.length) + ikm.set(new TextEncoder().encode(this.context2), localKey.length + r2.length) + + console.log('🌱 HKDF Step 6a Parameters (seed_raw):') + console.log('🌱 LocalKey length:', localKey.length, 'bytes') + console.log('🌱 R2 length:', r2.length, 'bytes') + console.log('🌱 Context2:', this.context2) + console.log('🌱 Context2 length:', this.context2.length, 'bytes') + console.log('🌱 IKM total length:', ikm.length, 'bytes') + console.log('🌱 App Salt2:', this.arrayToBase64(this.appSalt2)) + console.log('🌱 Info string:', 'seed_v1') + console.log('🌱 Output length:', 32, 'bytes') + + const seedRaw = await this.hkdf(ikm, 32, this.appSalt2, 'seed_v1') + console.log('🌱 Step 6a: Generated seed_raw:', this.arrayToBase64(seedRaw)) + + // password_salt = randomBytes(16); Password_bytes = scrypt(seed_raw, password_salt, N, r, p) + const passwordSalt = crypto.getRandomValues(new Uint8Array(16)) + const argon2Params = { + memory: 65536, // 64 MB + time: 3, + parallelism: 4 + } + + console.log('🔑 Scrypt Step 6b Parameters (final password):') + console.log('🔑 Input (seed_raw) length:', seedRaw.length, 'bytes') + console.log('🔑 Password salt length:', passwordSalt.length, 'bytes') + console.log('🔑 Password salt value:', this.arrayToBase64(passwordSalt)) + console.log('🔑 N (memory-hard parameter):', 16384) + console.log('🔑 r (block size factor):', 8) + console.log('🔑 p (parallelization factor):', 1) + console.log('🔑 Output length:', 32, 'bytes') + + const passwordBytes = await this.argon2id(seedRaw, passwordSalt, argon2Params, 32) + + console.log('🔑 Step 6b: Generated Password_bytes (scrypt):', this.arrayToBase64(passwordBytes)) + console.log('🔑 Password salt:', this.arrayToBase64(passwordSalt)) + + return { + passwordBytes, + passwordSalt + } + } + + // Real HKDF implementation using futoin-hkdf library + private async hkdf(ikm: Uint8Array, length: number, salt: Uint8Array, info: string): Promise { + const hkdf = (await import('futoin-hkdf')).default + const derived = hkdf(Buffer.from(ikm), length, { + hash: 'SHA-256', + salt: Buffer.from(salt), + info: info + }) + return new Uint8Array(derived) + } + + // Memory-hard key derivation using scrypt-js (Argon2 alternative) + private async argon2id( + password: Uint8Array, + salt: Uint8Array, + params: { memory: number; time: number; parallelism: number }, + length: number + ): Promise { + const { scrypt } = await import('scrypt-js') + + // Convert scrypt parameters to match Argon2 security level + // N = 2^14 = 16384 (memory-hard parameter) + // r = 8 (block size factor) + // p = 1 (parallelization factor) + const N = 16384 // 2^14 - memory-hard parameter + const r = 8 // block size factor + const p = 1 // parallelization factor + + const hash = await scrypt(password, salt, N, r, p, length) + return new Uint8Array(hash) + } + + // Convert password bytes to human-readable password + private convertToPassword(passwordBytes: Uint8Array): string { + // Use a character set that includes letters, numbers, and symbols + const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?' + + let password = '' + for (let i = 0; i < passwordBytes.length; i++) { + const index = passwordBytes[i] % charset.length + password += charset[index] + } + + // Ensure minimum length of 16 characters + while (password.length < 16) { + const extraIndex = passwordBytes[password.length % passwordBytes.length] % charset.length + password += charset[extraIndex] + } + + console.log('🎯 Final password generated:', password) + return password + } + + // Convert BigInt to Uint8Array + private bigIntToUint8Array(bigInt: bigint, length: number): Uint8Array { + const hex = bigInt.toString(16).padStart(length * 2, '0') + const bytes = new Uint8Array(length) + for (let i = 0; i < length; i++) { + bytes[i] = parseInt(hex.substr(i * 2, 2), 16) + } + return bytes + } + + // Convert Uint8Array to base64 + private arrayToBase64(array: Uint8Array): string { + return btoa(String.fromCharCode(...array)) + } + + // Main password generation function + async generatePassword(pythRandomness: { n1: string; n2: string; txHash: string; sequenceNumber: string }): Promise { + console.log('🚀 STARTING PASSWORD GENERATION PROCESS...') + console.log('📊 Input Pyth Randomness:', pythRandomness) + console.log('📊 n1 (BigInt):', BigInt(pythRandomness.n1)) + console.log('📊 n2 (BigInt):', BigInt(pythRandomness.n2)) + console.log('📊 txHash:', pythRandomness.txHash) + console.log('📊 sequenceNumber:', pythRandomness.sequenceNumber) + + // Step 1: Generate device secret (C) + console.log('🔐 STEP 1: Generating device secret (C)...') + const deviceSecret = this.generateDeviceSecret() + console.log('🔐 Device Secret (C) generated:', this.arrayToBase64(deviceSecret)) + + // Step 2: Extract R1 from Pyth randomness + console.log('🎲 STEP 2: Extracting R1 from Pyth randomness...') + const r1 = this.extractR1(pythRandomness) + console.log('🎲 R1 extracted:', this.arrayToBase64(r1)) + + // Step 3: Mix R1 + C → local_raw (HKDF) + console.log('🔗 STEP 3: Mixing R1 + C → local_raw (HKDF)...') + console.log('🔗 HKDF Parameters:') + console.log('🔗 - IKM = R1 || C || context') + console.log('🔗 - Salt = app_salt1') + console.log('🔗 - Info = "local_raw_v1"') + console.log('🔗 - Length = 32 bytes') + const localRaw = await this.generateLocalRaw(r1, deviceSecret) + console.log('🔗 Local Raw generated:', this.arrayToBase64(localRaw)) + + // Step 4: Harden local_raw → LocalKey (scrypt) + console.log('🛡️ STEP 4: Hardening local_raw → LocalKey (scrypt)...') + console.log('🛡️ Scrypt Parameters:') + console.log('🛡️ - N = 16384 (memory-hard parameter)') + console.log('🛡️ - r = 8 (block size factor)') + console.log('🛡️ - p = 1 (parallelization factor)') + console.log('🛡️ - Output length = 32 bytes') + const { localKey, salt1, argon2Params } = await this.generateLocalKey(localRaw) + console.log('🛡️ LocalKey generated:', this.arrayToBase64(localKey)) + console.log('🛡️ Salt1 generated:', this.arrayToBase64(salt1)) + + // Step 5: Extract R2 from Pyth randomness + console.log('🎲 STEP 5: Extracting R2 from Pyth randomness...') + const r2 = this.extractR2(pythRandomness) + console.log('🎲 R2 extracted:', this.arrayToBase64(r2)) + + // Step 6: Derive seed and final harden → Password_bytes + console.log('🌱 STEP 6: Deriving seed and final hardening → Password_bytes...') + console.log('🌱 HKDF Parameters for seed_raw:') + console.log('🌱 - IKM = LocalKey || R2 || context2') + console.log('🌱 - Salt = app_salt2') + console.log('🌱 - Info = "seed_v1"') + console.log('🌱 - Length = 32 bytes') + console.log('🌱 Scrypt Parameters for final password:') + console.log('🌱 - N = 16384, r = 8, p = 1') + console.log('🌱 - Output length = 32 bytes') + const { passwordBytes, passwordSalt } = await this.generatePasswordBytes(localKey, r2) + console.log('🌱 Password bytes generated:', this.arrayToBase64(passwordBytes)) + console.log('🌱 Password salt generated:', this.arrayToBase64(passwordSalt)) + + // Convert to human-readable password + console.log('🎯 Converting password bytes to human-readable password...') + const password = this.convertToPassword(passwordBytes) + console.log('🎯 Final password:', password) + + const result: PasswordGenerationResult = { + password, + metadata: { + txHash: pythRandomness.txHash, + sequenceNumber: pythRandomness.sequenceNumber, + deviceSecret: this.arrayToBase64(deviceSecret), + r1: pythRandomness.n1, + r2: pythRandomness.n2, + localRaw: this.arrayToBase64(localRaw), + localKey: this.arrayToBase64(localKey), + seedRaw: this.arrayToBase64(await this.generateSeedRaw(localKey, r2)), + passwordBytes: this.arrayToBase64(passwordBytes), + salt1: this.arrayToBase64(salt1), + passwordSalt: this.arrayToBase64(passwordSalt), + argon2Params + } + } + + console.log('✅ PASSWORD GENERATION COMPLETED!') + console.log('✅ Final Result:', result) + return result + } + + // Helper method to generate seed_raw for metadata + private async generateSeedRaw(localKey: Uint8Array, r2: Uint8Array): Promise { + const ikm = new Uint8Array(localKey.length + r2.length + this.context2.length) + ikm.set(localKey, 0) + ikm.set(r2, localKey.length) + ikm.set(new TextEncoder().encode(this.context2), localKey.length + r2.length) + return await this.hkdf(ikm, 32, this.appSalt2, 'seed_v1') + } +} + +// Type for window.ethereum +interface EthereumProvider { + request: (args: { method: string; params?: unknown[] }) => Promise +} + +const RANDOMNESS_CONTRACT = '0xE861DC68Eb976da0661035bBf132d6F3a3288B71' +const ENTROPY_CONTRACT = '0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c' + +// Contract ABIs +const RANDOMNESS_ABI = parseAbi([ + 'function requestPair() external payable', + 'function getResult(uint64 sequenceNumber) external view returns (uint256 n1, uint256 n2, bool fulfilled, address requester)', + 'event Requested(uint64 indexed sequenceNumber, address indexed requester)', + 'event RandomPairGenerated(uint64 indexed sequenceNumber, uint256 n1, uint256 n2, address indexed requester)' +]) + +const ENTROPY_ABI = parseAbi([ + 'function getFeeV2() external view returns (uint128)' +]) + +export default function TestPage() { + const [fee, setFee] = useState('0') + const [isRequesting, setIsRequesting] = useState(false) + const [sequenceNumber, setSequenceNumber] = useState(null) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [result, setResult] = useState<{ + n1: string + n2: string + fulfilled: boolean + requester: string + } | null>(null) + const [isPolling, setIsPolling] = useState(false) + const [error, setError] = useState(null) + const [txHash, setTxHash] = useState(null) + const [account, setAccount] = useState(null) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const [publicClient, setPublicClient] = useState(null) + const [passwordResult, setPasswordResult] = useState(null) + const [isGeneratingPassword, setIsGeneratingPassword] = useState(false) + const [passwordGenerator] = useState(() => new ClientPasswordGenerator()) + const [isStoringPassword, setIsStoringPassword] = useState(false) + const [storageSuccess, setStorageSuccess] = useState(false) + const [socials, setSocials] = useState('') + const [showSocialsInput, setShowSocialsInput] = useState(false) + + // Initialize Viem client + useEffect(() => { + const client = createPublicClient({ + chain: baseSepolia, + transport: http('https://sepolia.base.org') + }) + + setPublicClient(client) + + // Get fee when client is ready + getFee(client) + }, []) + + // Check for wallet connection + useEffect(() => { + const checkWallet = async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (typeof window !== 'undefined' && (window as any).ethereum) { + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const ethereum = (window as any).ethereum as EthereumProvider + const accounts = await ethereum.request({ method: 'eth_accounts' }) as string[] + if (accounts.length > 0) { + setAccount(accounts[0]) + } + } catch (err) { + console.error('Error checking wallet:', err) + } + } + } + + checkWallet() + }, []) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const getFee = async (client: any) => { + try { + const feeValue = await client.readContract({ + address: ENTROPY_CONTRACT as `0x${string}`, + abi: ENTROPY_ABI, + functionName: 'getFeeV2' + }) + + setFee(feeValue.toString()) + } catch (err) { + console.error('Error getting fee:', err) + setError('Failed to get fee') + } + } + + const connectWallet = async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (typeof window !== 'undefined' && (window as any).ethereum) { + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const ethereum = (window as any).ethereum as EthereumProvider + const accounts = await ethereum.request({ method: 'eth_requestAccounts' }) as string[] + if (accounts.length > 0) { + setAccount(accounts[0]) + } + } catch (err) { + console.error('Error connecting wallet:', err) + setError('Failed to connect wallet') + } + } else { + setError('No wallet found. Please install MetaMask or similar wallet.') + } + } + + const requestRandomness = async () => { + if (!account || !publicClient) { + setError('No wallet connected or client not ready') + return + } + + setIsRequesting(true) + setError(null) + setResult(null) + setSequenceNumber(null) + setTxHash(null) + setPasswordResult(null) + setIsPolling(false) + setIsGeneratingPassword(false) + setIsStoringPassword(false) + setStorageSuccess(false) + setSocials('') + setShowSocialsInput(false) + + try { + // Send transaction using window.ethereum directly + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const ethereum = (window as any).ethereum as EthereumProvider + const txHash = await ethereum.request({ + method: 'eth_sendTransaction', + params: [{ + from: account, + to: RANDOMNESS_CONTRACT, + value: '0x' + BigInt(fee).toString(16), + data: '0x4b3813e6' // requestPair() function selector + }] + }) as string + + console.log('Transaction sent:', txHash) + setTxHash(txHash) + + // Wait for transaction receipt + const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash }) + + // Parse events from receipt + const requestEvents = parseEventLogs({ + abi: RANDOMNESS_ABI, + logs: receipt.logs, + eventName: 'Requested' + }) + + console.log('📋 CONTRACT TRANSACTION DATA:') + console.log('📋 Transaction Hash:', txHash) + console.log('📋 Transaction Receipt:', receipt) + console.log('📋 All Receipt Logs:', receipt.logs) + console.log('📋 Parsed Request Events:', requestEvents) + + if (requestEvents.length > 0) { + const seqNum = requestEvents[0].args.sequenceNumber.toString() + setSequenceNumber(seqNum) + + console.log('📋 Contract Event Data:') + console.log('📋 Sequence Number:', seqNum) + console.log('📋 Requester:', requestEvents[0].args.requester) + console.log('📋 Event Args:', requestEvents[0].args) + + console.log('⏳ Waiting for Pyth randomness to be fulfilled...') + console.log('⏳ Starting polling for real Pyth randomness...') + + // Wait a moment for transaction to be fully processed, then check immediately + setTimeout(() => { + checkRandomnessImmediately(seqNum, txHash) + }, 1000) // Wait 1 second before checking + } else { + setError('Could not find sequence number in transaction') + } + + } catch (err) { + console.error('Error requesting randomness:', err) + setError('Failed to request randomness') + } finally { + setIsRequesting(false) + } + } + + const checkRandomnessImmediately = async (seqNum: string, txHash: string) => { + console.log('🚀 Checking randomness immediately for sequence:', seqNum) + + try { + if (!publicClient) { + console.error('❌ Public client not available') + return + } + + const result = await publicClient.readContract({ + address: RANDOMNESS_CONTRACT as `0x${string}`, + abi: RANDOMNESS_ABI, + functionName: 'getResult', + args: [BigInt(seqNum)] + }) + + console.log('🚀 Immediate check result:', result) + console.log('🚀 Fulfilled status:', result[2]) + + if (result[2]) { + const randomnessResult = { + n1: result[0].toString(), + n2: result[1].toString(), + fulfilled: result[2], + requester: result[3] + } + + console.log('🎉 RANDOMNESS ALREADY FULFILLED IMMEDIATELY!') + console.log('🎉 n1:', randomnessResult.n1) + console.log('🎉 n2:', randomnessResult.n2) + console.log('🎉 Full randomnessResult:', randomnessResult) + + setResult(randomnessResult) + setIsPolling(false) + + // Generate password immediately + console.log('🚀 Generating password with IMMEDIATE Pyth randomness...') + console.log('🚀 About to call generatePassword with:', randomnessResult) + + try { + await generatePassword(randomnessResult, txHash, seqNum) + console.log('✅ generatePassword completed successfully!') + } catch (error) { + console.error('❌ Error in generatePassword:', error) + } + } else { + console.log('⏳ Randomness not ready yet, starting polling...') + startPolling(seqNum, txHash) + } + } catch (err) { + console.error('❌ Error in immediate check:', err) + console.log('⏳ Starting polling as fallback...') + startPolling(seqNum, txHash) + } + } + + const startPolling = (seqNum: string, txHash: string) => { + setIsPolling(true) + let pollCount = 0 + const maxPolls = 60 // Poll for up to 60 seconds (60 * 1 second) + + console.log('🔄 Starting polling for sequence number:', seqNum) + console.log('🔄 Transaction hash:', txHash) + console.log('🔄 Will poll every 1 second for up to 60 seconds') + + const pollInterval = setInterval(async () => { + try { + pollCount++ + console.log(`🔄 Polling attempt ${pollCount}/${maxPolls} for sequence ${seqNum}`) + + if (!publicClient) { + console.error('❌ Public client not available') + return + } + + const result = await publicClient.readContract({ + address: RANDOMNESS_CONTRACT as `0x${string}`, + abi: RANDOMNESS_ABI, + functionName: 'getResult', + args: [BigInt(seqNum)] + }) + + console.log(`🔄 Poll ${pollCount} result:`, result) + console.log(`🔄 Fulfilled status:`, result[2]) + + if (result[2]) { // result[2] is the fulfilled boolean + const randomnessResult = { + n1: result[0].toString(), // result[0] is n1 + n2: result[1].toString(), // result[1] is n2 + fulfilled: result[2], // result[2] is fulfilled + requester: result[3] // result[3] is requester + } + + console.log('📋 PYTH RANDOMNESS CONTRACT DATA:') + console.log('📋 Raw Contract Result:', result) + console.log('📋 n1 (raw):', result[0]) + console.log('📋 n2 (raw):', result[1]) + console.log('📋 fulfilled (raw):', result[2]) + console.log('📋 requester (raw):', result[3]) + console.log('📋 Processed Randomness Result:', randomnessResult) + + console.log('✅ PYTH RANDOMNESS IS NOW FULFILLED!') + console.log('✅ n1:', randomnessResult.n1) + console.log('✅ n2:', randomnessResult.n2) + console.log('✅ fulfilled:', randomnessResult.fulfilled) + console.log('✅ requester:', randomnessResult.requester) + console.log('✅ Full randomnessResult:', randomnessResult) + + setResult(randomnessResult) + setIsPolling(false) + clearInterval(pollInterval) + + // Generate password ONLY with real Pyth randomness + console.log('🚀 Generating password with REAL Pyth randomness...') + console.log('🚀 About to call generatePassword with:', randomnessResult) + + try { + await generatePassword(randomnessResult, txHash, seqNum) + console.log('✅ generatePassword completed successfully!') + } catch (error) { + console.error('❌ Error in generatePassword:', error) + } + } else if (pollCount >= maxPolls) { + console.error('❌ Polling timeout reached. Pyth randomness not fulfilled.') + setIsPolling(false) + clearInterval(pollInterval) + setError('Timeout waiting for Pyth randomness. Please try again.') + } + } catch (err) { + console.error('❌ Error polling result:', err) + if (pollCount >= maxPolls) { + setIsPolling(false) + clearInterval(pollInterval) + setError('Error polling for randomness. Please try again.') + } + } + }, 1000) // Poll every 1 second + } + + const generatePassword = async ( + randomnessResult: { + n1: string + n2: string + fulfilled: boolean + requester: string + }, + txHashParam?: string, + sequenceNumberParam?: string + ) => { + const currentTxHash = txHashParam || txHash + const currentSequenceNumber = sequenceNumberParam || sequenceNumber + + console.log('🔐 ===== GENERATE PASSWORD FUNCTION CALLED =====') + console.log('🔐 PASSWORD GENERATION INPUT PARAMETERS:') + console.log('🔐 randomnessResult:', randomnessResult) + console.log('🔐 txHashParam:', txHashParam) + console.log('🔐 sequenceNumberParam:', sequenceNumberParam) + console.log('🔐 currentTxHash:', currentTxHash) + console.log('🔐 currentSequenceNumber:', currentSequenceNumber) + console.log('🔐 isGeneratingPassword:', isGeneratingPassword) + + if (!currentTxHash || !currentSequenceNumber) { + console.error('❌ Missing transaction hash or sequence number') + console.error('❌ currentTxHash:', currentTxHash) + console.error('❌ currentSequenceNumber:', currentSequenceNumber) + return + } + + if (isGeneratingPassword) { + console.log('⚠️ Password generation already in progress, skipping...') + console.log('⚠️ This might be why password is not generated on first attempt!') + return + } + + console.log('🔐 Starting password generation process...') + setIsGeneratingPassword(true) + setPasswordResult(null) + + try { + console.log('🔐 Starting client-side password generation with REAL Pyth randomness...') + console.log('🔐 Using ONLY onchain randomness data from contract...') + + const pythRandomness = { + n1: randomnessResult.n1, + n2: randomnessResult.n2, + txHash: currentTxHash, + sequenceNumber: currentSequenceNumber + } + + console.log('🔐 REAL PYTH RANDOMNESS FOR PASSWORD GENERATION:') + console.log('🔐 pythRandomness:', pythRandomness) + console.log('🔐 n1 (onchain):', pythRandomness.n1) + console.log('🔐 n2 (onchain):', pythRandomness.n2) + console.log('🔐 txHash:', pythRandomness.txHash) + console.log('🔐 sequenceNumber:', pythRandomness.sequenceNumber) + console.log('🔐 These are the ACTUAL random numbers from Pyth Network!') + console.log('🔐 About to call passwordGenerator.generatePassword...') + + // Generate password client-side using Web Crypto API + const passwordResult = await passwordGenerator.generatePassword(pythRandomness) + console.log('🔐 passwordGenerator.generatePassword completed!') + setPasswordResult(passwordResult) + + console.log('✅ Client-side password generation completed!') + console.log('✅ Final Password Result:', passwordResult) + + // Show socials input after password generation + setShowSocialsInput(true) + console.log('📝 Password generated! Please enter socials to store in NilDB.') + + } catch (err) { + console.error('Error generating password:', err) + setError('Failed to generate password') + } finally { + setIsGeneratingPassword(false) + } + } + + const storePasswordWithSocials = async () => { + if (!passwordResult) { + setError('No password generated yet') + return + } + + if (!socials.trim()) { + setError('Please enter socials information') + return + } + + setIsStoringPassword(true) + setStorageSuccess(false) + + try { + console.log('💾 Storing password with socials in NilDB...') + console.log('💾 Socials:', socials) + + // First test configuration + console.log('🔍 Testing NilDB configuration...') + const configResponse = await fetch('/api/nildb/test-config') + const config = await configResponse.json() + + if (!config.configured) { + console.warn('⚠️ NilDB not properly configured:', config) + setStorageSuccess(true) // Mark as success since password was generated + setError(`Password generated successfully (NilDB not configured: ${config.missingVariables.join(', ')})`) + return + } + + console.log('✅ NilDB configuration is valid') + + const passwordName = `WhipHash Password - ${new Date().toLocaleString()}` + + const response = await fetch('/api/nildb/store-password', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + password: passwordResult.password, + name: passwordName, + metadata: passwordResult.metadata, + socials: socials, + txHash: passwordResult.metadata.txHash, + sequenceNumber: passwordResult.metadata.sequenceNumber + }) + }) + + if (response.ok) { + const result = await response.json() + setStorageSuccess(true) + setShowSocialsInput(false) + console.log('✅ Password and socials successfully stored in NilDB!', result) + } else { + let error + try { + error = await response.json() + } catch (parseError) { + console.error('❌ Failed to parse error response:', parseError) + error = { error: `HTTP ${response.status}: ${response.statusText}` } + } + + console.error('❌ NilDB API Error:', error) + console.error('❌ Response status:', response.status) + + // Check if it's a configuration error + if (error.error && error.error.includes('Missing environment variables')) { + console.warn('⚠️ NilDB not configured - skipping storage') + setStorageSuccess(true) // Mark as success since password was generated + setError('Password generated successfully (NilDB not configured)') + } else { + const errorMessage = error.error || error.message || `HTTP ${response.status}: ${response.statusText}` + throw new Error(errorMessage) + } + } + + } catch (err) { + console.error('❌ Failed to store password in NilDB:', err) + setError('Failed to store password in NilDB') + } finally { + setIsStoringPassword(false) + } + } + + + + if (!publicClient) { + return ( +
+ {/* Dither Background */} +
+ +
+ + {/* Dark Overlay */} +
+ + {/* Loading Content */} +
+
+
+

Loading...

+
+
+
+ ) + } + + return ( +
+ {/* Dither Background */} +
+ +
+ + {/* Dark Overlay */} +
+ + {/* Main Content */} +
+
+ {/* Title */} +

+ whiphash. +

+ + {/* Main Button */} +
+ {!account ? ( + + Connect Wallet + + ) : ( + + {isRequesting ? 'Requesting Randomness...' : + isPolling ? 'Waiting for Randomness...' : + isGeneratingPassword ? 'Generating Password...' : + isStoringPassword ? 'Storing in NilDB...' : + 'Generate Secure Password'} + + )} +
+ + {/* Status Messages */} + {error && ( +
+

{error}

+
+ )} + + {passwordResult && ( +
+ {/* Password Display with ShineBorder */} +
+ + +
+

🔐 Secure Password Generated!

+ + {/* Generated Password */} +
+
+ {passwordResult.password} +
+ +
+ + {/* Socials Input */} + {showSocialsInput && ( +
+

📝 Enter Your Socials

+