diff --git a/entropy/0xSlither/.gitignore b/entropy/0xSlither/.gitignore
new file mode 100644
index 0000000..e2c550f
--- /dev/null
+++ b/entropy/0xSlither/.gitignore
@@ -0,0 +1,11 @@
+node_modules/
+dist/
+*.log
+.DS_Store
+.pnpm-store/
+
+# Don't commit compiled JS files from shared package
+shared/**/*.js
+!shared/**/*.config.js
+
+*.env
\ No newline at end of file
diff --git a/entropy/0xSlither/PROJECT_OVERVIEW.md b/entropy/0xSlither/PROJECT_OVERVIEW.md
new file mode 100644
index 0000000..e0f555c
--- /dev/null
+++ b/entropy/0xSlither/PROJECT_OVERVIEW.md
@@ -0,0 +1,194 @@
+# ๐ฎ Slither.io Core - Project Overview
+
+## โจ Status: COMPLETE & RUNNING
+
+Your complete Slither.io game is built and running!
+
+### ๐ Latest Updates (November 22, 2025)
+- **Jerkiness Removed**: Ultra-smooth camera movement with easing
+- **Dynamic Growth**: Pellet size now affects growth (larger = more segments)
+- **Enhanced Interpolation**: Seamless movement between server updates
+- **Collision Improvements**: More precise boundary and snake detection
+
+## ๐ Quick Access
+
+**Play Now**: Open http://localhost:3000 in your browser
+
+**Servers Running**:
+- Game Server (WebSocket): Port 8080 โ
+- Web Client (HTTP): Port 3000 โ
+
+## ๐ Documentation
+
+| Document | Purpose |
+|----------|---------|
+| **README.md** | Full project documentation and setup |
+| **QUICKSTART.md** | Get started playing in 30 seconds |
+| **TESTING_GUIDE.md** | Comprehensive testing instructions |
+| **IMPLEMENTATION_SUMMARY.md** | Technical implementation details |
+
+## ๐ฏ What Was Built
+
+### Complete Feature Set
+
+โ
**Core Gameplay**
+- Real-time multiplayer (unlimited players)
+- Ultra-smooth snake movement with mouse control (interpolated)
+- 500 pellets in a 5000ร5000 world
+- Eat pellets to grow (growth proportional to pellet size)
+- Precise collision detection (snake-to-snake)
+- Death and instant respawn
+
+โ
**Server (Node.js + TypeScript)**
+- Authoritative game server (20 TPS)
+- WebSocket communication
+- Fixed-timestep game loop
+- Collision detection algorithms
+- Pellet spawning system
+- Player management
+- Leaderboard computation
+
+โ
**Client (Browser + TypeScript)**
+- Canvas 2D rendering (60 FPS)
+- Ultra-smooth camera following with easing
+- Client-side interpolation (no jerkiness)
+- Precise mouse input handling
+- Start screen with name input
+- Death screen with score
+- Live leaderboard (Top 5)
+- Connection status indicator
+
+โ
**Visual Polish**
+- Unique colored snakes
+- Gradient body segments
+- Snake eyes
+- Grid background
+- Glassmorphism UI
+- Gold/silver/bronze medals
+- Name tags above snakes
+- Smooth animations
+
+## ๐ Project Structure
+
+```
+0xSlither/
+โโโ server/ # Game server (20 TPS authoritative)
+โโโ client/ # Web client (60 FPS Canvas)
+โโโ shared/ # Shared types & protocol
+โโโ README.md # Main documentation
+โโโ QUICKSTART.md # Play in 30 seconds
+โโโ TESTING_GUIDE.md # Testing instructions
+โโโ IMPLEMENTATION_SUMMARY.md # Technical details
+```
+
+## ๐ฎ How to Play
+
+1. **Open**: http://localhost:3000
+2. **Enter** your name
+3. **Click** "Play"
+4. **Move** your mouse to control direction
+5. **Eat** pellets to grow
+6. **Avoid** other snakes!
+
+## ๐งช Test Multiplayer
+
+Open multiple browser tabs to http://localhost:3000 - each tab is a different player!
+
+## ๐ ๏ธ Development Commands
+
+```bash
+# Start both servers
+pnpm run dev
+
+# Or start separately:
+pnpm run server # Start game server (port 8080)
+pnpm run client # Start web client (port 3000)
+
+# Build for production
+cd server && pnpm run build
+cd client && pnpm run build
+```
+
+## ๐จ Customization
+
+Edit **shared/constants.ts** to adjust:
+- World size
+- Snake speed
+- Pellet count
+- Growth rate
+- Tick rate
+- Leaderboard size
+
+## ๐ Technical Specs
+
+| Aspect | Details |
+|--------|---------|
+| **Language** | TypeScript (full stack) |
+| **Server** | Node.js + ws library |
+| **Client** | Vite + Canvas 2D |
+| **Network** | WebSocket (JSON) |
+| **Server TPS** | 20 updates/second |
+| **Client FPS** | 60 frames/second |
+| **World Size** | 5000ร5000 units |
+| **Players** | Unlimited (tested 20+) |
+
+## ๐ฏ What's Next?
+
+The core game is complete! Ready for:
+
+### Phase 2: Web3 Integration
+- Wallet connection
+- Blockchain integration
+- Token rewards
+- NFT snake skins
+
+### Phase 3: Advanced Features
+- Multiple game rooms
+- Power-ups
+- Special abilities
+- Mobile support
+- Audio/sound effects
+- Particle effects
+- Chat system
+
+### Phase 4: Deployment
+- Oasis ROFL deployment
+- Pyth randomness integration
+- Production infrastructure
+- CDN setup
+- SSL/HTTPS
+
+## ๐ Achievement Unlocked!
+
+You now have a **fully functional**, **real-time multiplayer** Slither.io game with:
+
+โจ Smooth 60 FPS gameplay
+โจ Real-time multiplayer
+โจ Professional UI/UX
+โจ Clean, type-safe code
+โจ Comprehensive documentation
+โจ Ready for Web3 integration
+
+## ๐ Getting Help
+
+1. Check server logs (Terminal 1)
+2. Check browser console (F12)
+3. Review TESTING_GUIDE.md
+4. Verify both servers are running
+
+## ๐ Resources
+
+- **Play Game**: http://localhost:3000
+- **Server Port**: 8080 (WebSocket)
+- **Client Port**: 3000 (HTTP)
+
+---
+
+**Built with TypeScript, Node.js, Vite, and Canvas 2D**
+
+**Status**: Production Ready โ
+**Version**: 1.0.0
+**Created**: November 2025
+
+๐ฎ **ENJOY YOUR GAME!** ๐ฎ
+
diff --git a/entropy/0xSlither/README.md b/entropy/0xSlither/README.md
new file mode 100644
index 0000000..03220a1
--- /dev/null
+++ b/entropy/0xSlither/README.md
@@ -0,0 +1,607 @@
+# ๐ฎ 0xSlither
+
+A real-time multiplayer snake game with a complete on-chain economy powered by blockchain technology.
+
+**Built for ETHGlobal Buenos Aires 2025** ๐ฆ๐ท
+
+[](https://0xslither.vercel.app)
+[](LICENSE)
+
+**๐ด LIVE NOW**: [https://0xslither.vercel.app](https://0xslither.vercel.app)
+
+---
+
+## ๐ What is 0xSlither?
+
+0xSlither is a multiplayer snake game where players stake tokens to enter matches and winners collect the stakes of players they eliminate. Every match uses cryptographically verifiable randomness for fair spawn positions and pellet placement. All game results, stakes, and leaderboards are stored permanently on-chain.
+
+**๐ Fully Deployed & Production Ready**
+- **Frontend**: Hosted on Vercel
+- **Game Server**: Hosted on Fluence CPU (decentralized compute)
+- **Smart Contracts**: Deployed on Saga Chainlet & Base Sepolia
+
+### ๐ฏ Core Features
+
+#### Gameplay
+- **Real-time Multiplayer**: Authoritative 20 TPS server with WebSocket communication
+- **Ultra-Smooth Movement**: 60 FPS rendering with advanced interpolation
+- **Dynamic Growth System**: Eat pellets to grow (growth proportional to pellet size)
+- **Precise Collision Detection**: Snake-to-snake collisions and pellet consumption
+- **Responsive Camera**: Smooth camera following with easing
+
+#### Blockchain Economy
+- **Stake-to-Enter**: Players stake SSS tokens to join matches
+- **Winner Takes All**: Collect 100% of eliminated players' stakes
+- **Tap-Out Anytime**: Exit safely and withdraw your current stake
+- **On-Chain Leaderboard**: Top players tracked permanently on [Saga Explorer](https://slither-2763767854157000-1.sagaexplorer.io/txs)
+- **Match Finalization**: Results and best scores stored on-chain
+- **Auto Network Config**: MetaMask automatically configured (no manual setup!)
+
+#### Fair Randomness (Pyth Entropy)
+- **Cryptographically Secure RNG**: Powered by [Pyth Entropy on Base Sepolia](https://sepolia.basescan.org/address/0x662371163C3797b66ab80dCB592761718537F492)
+- **Cross-Chain Architecture**: Bridges randomness between Base Sepolia (entropy) and Saga (game)
+- **Deterministic Gameplay**: Single on-chain seed generates all match-critical random values
+- **Provable Fairness**: Spawn positions, colors, and pellet layouts derived from verifiable entropy
+- **Reproducible Matches**: Same seed + player roster = identical match conditions
+- **On-Chain Verification**: Match seed hash committed to blockchain for auditability
+
+---
+
+## ๐ Quick Start
+
+### ๐ฎ Play Online Now
+
+**Visit [0xslither.vercel.app](https://0xslither.vercel.app)** and start playing immediately!
+
+1. Open [https://0xslither.vercel.app](https://0xslither.vercel.app)
+2. Connect your MetaMask wallet (optional)
+3. Enter your name
+4. Stake SSS tokens or play for free
+5. Start eating and growing!
+
+> **Server**: Running on Fluence CPU (decentralized compute platform)
+> **Frontend**: Hosted on Vercel
+> **Blockchain**: Saga Chainlet + Base Sepolia
+
+### ๐ง Run Locally (For Development)
+
+Want to modify the code or run your own instance?
+
+```bash
+# Install dependencies
+pnpm install
+
+# Start server and client
+pnpm run dev
+```
+
+Open http://localhost:3000 to play your local version.
+
+---
+
+## ๐ Prerequisites
+
+- **Node.js** 18+ ([Download](https://nodejs.org/))
+- **pnpm** package manager ([Install](https://pnpm.io/installation))
+- **MetaMask** browser extension (for blockchain features)
+- **Test tokens** (SSS for Saga, ETH for Base Sepolia - see [Getting Test Tokens](#getting-test-tokens))
+
+---
+
+## ๐ ๏ธ Installation
+
+### 1. Clone the Repository
+
+```bash
+git clone https://github.com/yourusername/0xSlither.git
+cd 0xSlither
+```
+
+### 2. Install Dependencies
+
+```bash
+pnpm install
+```
+
+This installs dependencies for all workspaces (server, client, shared, contracts).
+
+### 3. Start the Game
+
+```bash
+pnpm run dev
+```
+
+This starts:
+- Game server on **port 8080** (WebSocket)
+- Web client on **port 3000** (HTTP)
+
+### 4. Play!
+
+Open http://localhost:3000 in your browser.
+
+---
+
+## ๐ฎ How to Play
+
+**Live Game**: [0xslither.vercel.app](https://0xslither.vercel.app)
+
+1. **Connect Wallet** (optional - only for staking features)
+2. **Enter Your Name**
+3. **Stake SSS Tokens** or click "Play" to play for free
+4. **Move Mouse** to control your snake's direction
+5. **Eat Pellets** to grow bigger
+6. **Avoid Other Snakes** - hitting their body kills you!
+7. **Eliminate Others** to collect their stakes
+8. **Tap Out** anytime to safely exit and withdraw
+
+---
+
+## ๐ Production Deployment
+
+0xSlither is fully deployed and running in production:
+
+| Component | Platform | URL/Details |
+|-----------|----------|-------------|
+| **Frontend** | Vercel | [0xslither.vercel.app](https://0xslither.vercel.app) |
+| **Game Server** | Fluence CPU | Decentralized compute platform |
+| **Smart Contracts** | Saga Chainlet | [View on Explorer](https://slither-2763767854157000-1.sagaexplorer.io/txs) |
+| **Randomness Oracle** | Base Sepolia | [View Contract](https://sepolia.basescan.org/address/0x662371163C3797b66ab80dCB592761718537F492) |
+
+### Why Fluence CPU?
+
+Fluence CPU provides decentralized compute infrastructure, ensuring:
+- โ
No single point of failure
+- โ
Censorship resistance
+- โ
Transparent execution
+- โ
Perfect fit for Web3 gaming
+
+---
+
+## โ๏ธ Blockchain Architecture
+
+### Multi-Chain Setup
+
+0xSlither uses a dual-chain architecture:
+
+1. **Saga Chainlet** (Primary Game Chain)
+ - Dedicated EVM L1 with recycled gas fees
+ - Hosts the main game contract (`StakeArena`)
+ - Handles staking, leaderboards, and match results
+ - Uses native SSS tokens
+
+2. **Base Sepolia** (Randomness Chain)
+ - L2 testnet hosting Pyth Entropy oracle
+ - Provides cryptographically secure randomness
+ - Server bridges entropy from Base to Saga
+
+### Getting Test Tokens
+
+#### Saga Chainlet SSS Tokens
+1. Add Saga Chainlet to MetaMask (automatic via the game UI)
+2. Get SSS tokens from the [Saga Faucet](https://faucet.saga.xyz/) or ask in Discord
+
+#### Base Sepolia ETH
+1. Add Base Sepolia to MetaMask
+2. Get testnet ETH from [Base Sepolia Faucet](https://www.basescan.org/faucet) or [Alchemy Faucet](https://www.alchemy.com/faucets/base-sepolia)
+
+### Contract Deployment
+
+#### 1. Deploy to Saga Chainlet
+
+```bash
+cd contracts
+
+# Create .env file
+cat > .env << EOF
+SAGA_CHAINLET_RPC_URL=https://slither-2763767854157000-1.jsonrpc.sagarpc.io
+DEPLOYER_PRIVATE_KEY=your_private_key_here
+EOF
+
+# Deploy StakeArena contract
+pnpm run deploy:saga
+```
+
+Copy the deployed `StakeArena` address.
+
+#### 2. Deploy to Base Sepolia (For Entropy)
+
+```bash
+# Add Base Sepolia config to .env
+cat >> .env << EOF
+BASE_SEPOLIA_RPC_URL=https://sepolia.base.org
+BASE_SEPOLIA_PRIVATE_KEY=your_private_key_here
+EOF
+
+# Deploy EntropyOracle contract
+pnpm run deploy:entropy
+```
+
+Copy the deployed `EntropyOracle` address.
+
+### Server Configuration
+
+Create `server/.env`:
+
+```bash
+cd ../server
+cat > .env << EOF
+# Required
+SERVER_PRIVATE_KEY=0x... # Server wallet private key
+STAKE_ARENA_ADDRESS=0x... # StakeArena contract (Saga)
+
+# Optional (for Pyth Entropy)
+ENTROPY_ORACLE_ADDRESS=0x... # EntropyOracle contract (Base Sepolia)
+BASE_SEPOLIA_RPC_URL=https://sepolia.base.org
+SAGA_CHAINLET_RPC_URL=https://slither-2763767854157000-1.jsonrpc.sagarpc.io
+
+# Optional (for match finalization)
+ENABLE_BLOCKCHAIN=true
+EOF
+```
+
+### Client Configuration
+
+Create `client/.env`:
+
+```bash
+cd ../client
+cat > .env << EOF
+VITE_BLOCKCHAIN_ENABLED=true
+VITE_STAKE_ARENA_ADDRESS=0x... # StakeArena contract address
+VITE_SAGA_CHAINLET_RPC_URL=https://slither-2763767854157000-1.jsonrpc.sagarpc.io
+VITE_SAGA_CHAIN_ID=2763767854157000
+EOF
+```
+
+### Authorize the Server
+
+The server needs permission to finalize matches:
+
+```bash
+cd contracts
+pnpm run update:server
+```
+
+Enter your StakeArena address and server wallet address when prompted.
+
+### Start With Blockchain
+
+```bash
+cd ..
+pnpm run dev
+```
+
+Now when you play:
+- Connect your MetaMask wallet
+- Stake SSS tokens to enter matches
+- Earn stakes from eliminated players
+- View your rank on the on-chain leaderboard
+
+---
+
+## ๐ฒ How Pyth Entropy Works
+
+### Cross-Chain Fair Randomness
+
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ 1. Server requests entropy from Base Sepolia โ
+โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ โ
+ โผ
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ โ 2. Pyth Oracle reveals โ
+ โ random seed โ
+ โ (10-30 sec delay) โ
+ โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ
+ โ
+ โผ
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ โ 3. Server reads seed from โ
+ โ EntropyOracle (Base) โ
+ โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ
+ โ
+ โผ
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ โ 4. Server commits hash to โ
+ โ StakeArena (Saga) โ
+ โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ
+ โ
+ โผ
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ โ 5. Generate using RNG: โ
+ โ โข Spawn positions โ
+ โ โข Snake colors โ
+ โ โข Pellet layout โ
+ โ โข Map type โ
+ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+### What Gets Randomized
+
+1. **Player Spawns**: Derived from `keccak256(seed, "spawn", playerAddress, retryCount)`
+2. **Snake Colors**: Derived from `keccak256(seed, "color", playerAddress)`
+3. **Pellet Field**: All 500 pellets from `keccak256(seed, "pellets")`
+4. **Map Layout**: Pattern type from `keccak256(seed, "map")`
+
+### Verification
+
+- **Seed Hash**: `StakeArena.entropySeedByMatch[matchId]` on Saga
+- **Request ID**: `EntropyOracle.entropyRequestIdByMatch[matchId]` on Base Sepolia
+- **Fair Match Badge**: โ Shown in-game with match ID and entropy details
+
+---
+
+## ๐ Project Structure
+
+```
+0xSlither/
+โโโ client/ # Frontend (Vite + TypeScript)
+โ โโโ src/
+โ โโโ main.ts # Entry point
+โ โโโ Game.ts # Game state management
+โ โโโ Renderer.ts # Canvas rendering (60 FPS)
+โ โโโ Camera.ts # Smooth camera system
+โ โโโ InputHandler.ts # Mouse controls
+โ โโโ WalletService.ts# Web3 wallet integration
+โ โโโ UI.ts # UI components
+โ
+โโโ server/ # Backend (Node.js + TypeScript)
+โ โโโ src/
+โ โโโ index.ts # WebSocket server
+โ โโโ GameServer.ts # Main game loop (20 TPS)
+โ โโโ Snake.ts # Snake entity
+โ โโโ Pellet.ts # Pellet management
+โ โโโ CollisionDetection.ts # Physics
+โ โโโ BlockchainService.ts # Saga Web3
+โ โโโ EntropyBridgeService.ts # Pyth cross-chain
+โ โโโ DeterministicRNG.ts # Seeded RNG
+โ โโโ Leaderboard.ts # Rankings
+โ
+โโโ contracts/ # Smart contracts (Solidity)
+โ โโโ contracts/
+โ โ โโโ StakeArena.sol # Main game (Saga)
+โ โ โโโ EntropyOracle.sol # Pyth oracle (Base)
+โ โ โโโ GameToken.sol # ERC20 token
+โ โโโ scripts/
+โ โโโ deployStakeArena.ts # Deploy to Saga
+โ โโโ deployEntropyOracle.ts # Deploy to Base
+โ โโโ updateServer.ts # Authorize server
+โ โโโ getLeaderboard.ts # Query rankings
+โ
+โโโ shared/ # Shared TypeScript types
+ โโโ constants.ts # Game configuration
+ โโโ types.ts # Entity definitions
+ โโโ protocol.ts # Network messages
+```
+
+---
+
+## ๐ง Development
+
+### Run Separately
+
+```bash
+# Terminal 1 - Server
+pnpm run server
+
+# Terminal 2 - Client
+pnpm run client
+```
+
+### Build for Production
+
+```bash
+# Build server
+cd server && pnpm run build
+pnpm start
+
+# Build client
+cd client && pnpm run build
+pnpm run preview
+```
+
+### Test Multiplayer
+
+Open multiple browser tabs to [0xslither.vercel.app](https://0xslither.vercel.app) - each tab is a separate player!
+
+For local testing, use http://localhost:3000 instead.
+
+### Configuration
+
+Edit `shared/constants.ts`:
+
+```typescript
+export const WORLD_WIDTH = 5000; // Game world width
+export const WORLD_HEIGHT = 5000; // Game world height
+export const TICK_RATE = 20; // Server updates/sec
+export const SNAKE_BASE_SPEED = 100; // Snake speed
+export const PELLET_COUNT = 500; // Number of pellets
+export const LEADERBOARD_SIZE = 5; // Top players shown
+```
+
+### Change Server Port
+
+```bash
+PORT=9000 pnpm run server
+```
+
+---
+
+## ๐ ๏ธ Tech Stack
+
+### Frontend
+- **TypeScript** - Type-safe development
+- **Vite** - Fast build tool
+- **Vercel** - Production hosting
+- **Canvas 2D** - High-performance rendering
+- **ethers.js v6** - Blockchain interactions
+
+### Backend
+- **Node.js** - Server runtime
+- **Fluence CPU** - Decentralized compute hosting
+- **ws** - WebSocket library
+- **ethers.js v6** - Blockchain interactions
+
+### Blockchain
+- **Saga Chainlet** - EVM L1 for game economy
+- **Base Sepolia** - L2 testnet for Pyth Entropy
+- **Solidity 0.8.20** - Smart contracts
+- **Hardhat** - Development framework
+- **OpenZeppelin** - Secure contract libraries
+- **Pyth Entropy** - Verifiable randomness
+
+---
+
+## ๐ฏ Roadmap
+
+### โ
Completed
+- [x] Real-time multiplayer gameplay
+- [x] On-chain staking economy
+- [x] Pyth Entropy integration
+- [x] Cross-chain architecture
+- [x] Deterministic match replay
+- [x] Leaderboard system
+- [x] Smooth camera and interpolation
+- [x] **Production deployment on Vercel**
+- [x] **Decentralized server on Fluence CPU**
+
+### ๐ง Future Enhancements
+- [ ] Mobile touch controls
+- [ ] Audio and sound effects
+- [ ] Multiple game rooms
+- [ ] Power-ups
+- [ ] Snake skins/NFT customization
+- [ ] Tournament mode with brackets
+- [ ] Achievement system
+
+---
+
+## ๐ Troubleshooting
+
+### Connection Issues
+- โ
Ensure server is running on port 8080
+- โ
Check browser console (F12) for WebSocket errors
+- โ
Verify firewall isn't blocking connections
+- โ
Try `ws://localhost:8080` instead of `wss://`
+
+### Blockchain Issues
+- โ
Ensure MetaMask is installed and unlocked
+- โ
Verify you're on Saga Chainlet network
+- โ
Check you have enough SSS tokens
+- โ
Look for transaction errors in MetaMask
+- โ
View contract on [Saga Explorer](https://slither-2763767854157000-1.sagaexplorer.io/)
+
+### Performance Issues
+- โ
Reduce `PELLET_COUNT` in `shared/constants.ts`
+- โ
Close other browser tabs
+- โ
Check CPU usage (should be <50%)
+- โ
Try a different browser (Chrome recommended)
+
+### Game Not Starting
+1. Check that both servers are running
+2. Verify `pnpm install` completed successfully
+3. Clear browser cache and reload
+4. Check for errors in terminal and browser console
+
+---
+
+## ๐ Documentation
+
+- **Project Overview**: [PROJECT_OVERVIEW.md](PROJECT_OVERVIEW.md)
+- **Smart Contracts**: See `contracts/README.md`
+- **Network Protocol**: See "Network Protocol" section below
+
+---
+
+## ๐ก Network Protocol
+
+### Client โ Server
+
+```typescript
+{ type: 'JOIN', name: string, address?: string, stakeAmount?: string }
+{ type: 'INPUT', angle: number }
+{ type: 'PING', timestamp: number }
+{ type: 'TAPOUT' }
+```
+
+### Server โ Client
+
+```typescript
+{ type: 'STATE', snakes: Snake[], pellets: Pellet[], leaderboard: Player[] }
+{ type: 'DEAD', score: number, rank: number }
+{ type: 'PONG', timestamp: number }
+```
+
+---
+
+## ๐ฎ Game Mechanics
+
+- **Movement**: Snake moves forward continuously, mouse controls rotation
+- **Growth**: Pellets add 2-4 segments based on size
+- **Death**: Hitting another snake's body eliminates you
+- **Stakes**: Winner collects all stakes from eliminated players
+- **Respawn**: Click "Play Again" to rejoin with new stake
+- **Score**: Based on total snake length
+- **Leaderboard**: Top 5 players by score
+
+---
+
+## ๐ Performance
+
+- **Server**: 20 TPS (authoritative game loop)
+- **Client**: 60 FPS rendering with interpolation
+- **Players**: Supports 10-20+ concurrent players
+- **Latency**: <50ms typical (local), <200ms acceptable
+- **World**: 5000ร5000 units with 500 pellets
+
+---
+
+## ๐ค Contributing
+
+Contributions are welcome! Please:
+
+1. Fork the repository
+2. Create a feature branch (`git checkout -b feature/amazing-feature`)
+3. Commit your changes (`git commit -m 'Add amazing feature'`)
+4. Push to the branch (`git push origin feature/amazing-feature`)
+5. Open a Pull Request
+
+---
+
+## ๐ License
+
+This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
+
+---
+
+## ๐ Acknowledgments
+
+- **ETHGlobal** - For the hackathon opportunity
+- **Saga** - For the dedicated chainlet infrastructure
+- **Fluence** - For decentralized compute hosting
+- **Pyth Network** - For Entropy randomness oracle
+- **Vercel** - For frontend hosting
+- **OpenZeppelin** - For secure contract libraries
+- **Slither.io** - Original game inspiration
+
+---
+
+## ๐ Support & Links
+
+- **๐ฎ Play Game**: [0xslither.vercel.app](https://0xslither.vercel.app)
+- **Issues**: [GitHub Issues](https://github.com/yourusername/0xSlither/issues)
+- **Saga Explorer**: [View Transactions](https://slither-2763767854157000-1.sagaexplorer.io/txs)
+- **Base Sepolia Explorer**: [View Entropy Contract](https://sepolia.basescan.org/address/0x662371163C3797b66ab80dCB592761718537F492)
+
+---
+
+
+
+**Built with โค๏ธ using TypeScript, Fluence CPU, and Blockchain**
+
+**Deployed on Vercel | Server on Fluence CPU | Contracts on Saga & Base**
+
+**[๐ฎ Play Now](https://0xslither.vercel.app)** | **[Report Bug](https://github.com/yourusername/0xSlither/issues)** | **[Request Feature](https://github.com/yourusername/0xSlither/issues)**
+
+
diff --git a/entropy/0xSlither/client/index.html b/entropy/0xSlither/client/index.html
new file mode 100644
index 0000000..b55d0f5
--- /dev/null
+++ b/entropy/0xSlither/client/index.html
@@ -0,0 +1,555 @@
+
+
+
+
+
+ Slither.io Core
+
+
+
+
+
+
+
+
+
+
+
0xSlither
+
Connect your wallet to play
+
+
+
+ Balance: 0 SSS
+
+
Stake 1 SSS
+
Required to enter the game
+
+
Play
+
+
+
+
You Died!
+
Final Score: 0
+
+ Best Score: --
+
+
Play Again
+
+
+
Connecting...
+
+
+
Leaderboard
+
+
+
Current Score
+
+ Score:
+ 0 SSS
+
+
+
+
+
+ Tap Out & Withdraw
+
+
+
+
+
+
Loading...
+
Retry Transaction
+
+
+
+
+
+
+
+
diff --git a/entropy/0xSlither/client/package.json b/entropy/0xSlither/client/package.json
new file mode 100644
index 0000000..08aee1b
--- /dev/null
+++ b/entropy/0xSlither/client/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "client",
+ "version": "1.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@0xslither/shared": "workspace:*",
+ "ethers": "^6.9.0"
+ },
+ "devDependencies": {
+ "@types/node": "^22.0.0",
+ "typescript": "^5.5.4",
+ "vite": "^5.3.5"
+ }
+}
+
diff --git a/entropy/0xSlither/client/src/Camera.ts b/entropy/0xSlither/client/src/Camera.ts
new file mode 100644
index 0000000..367471c
--- /dev/null
+++ b/entropy/0xSlither/client/src/Camera.ts
@@ -0,0 +1,28 @@
+import { Position } from '@0xslither/shared';
+
+export class Camera {
+ x = 0;
+ y = 0;
+ private targetX = 0;
+ private targetY = 0;
+ private smoothing = 0.2;
+
+ setTarget(target: Position): void {
+ this.targetX = target.x;
+ this.targetY = target.y;
+ }
+
+ update(): void {
+ // Smooth camera movement
+ this.x += (this.targetX - this.x) * this.smoothing;
+ this.y += (this.targetY - this.y) * this.smoothing;
+ }
+
+ worldToScreen(worldX: number, worldY: number, canvasWidth: number, canvasHeight: number): Position {
+ return {
+ x: worldX - this.x + canvasWidth / 2,
+ y: worldY - this.y + canvasHeight / 2,
+ };
+ }
+}
+
diff --git a/entropy/0xSlither/client/src/Game.ts b/entropy/0xSlither/client/src/Game.ts
new file mode 100644
index 0000000..3d3da68
--- /dev/null
+++ b/entropy/0xSlither/client/src/Game.ts
@@ -0,0 +1,280 @@
+import {
+ MessageType,
+ ServerMessage,
+ StateMessage,
+ DeltaStateMessage,
+ DeadMessage,
+ JoinMessage,
+ InputMessage,
+ SerializedSnake,
+ SerializedPellet,
+} from '@0xslither/shared';
+
+export class Game {
+ private ws: WebSocket | null = null;
+ private connected = false;
+ private playerId: string | null = null;
+ private currentState: StateMessage | null = null;
+ private previousState: StateMessage | null = null;
+ private lastUpdateTime = 0;
+ private onStateUpdateCallback: ((state: StateMessage) => void) | null = null;
+ private onDeadCallback: ((score: number) => void) | null = null;
+ private onConnectedCallback: (() => void) | null = null;
+ private onPlayerIdReceivedCallback: ((playerId: string) => void) | null = null;
+
+ constructor(serverUrl: string) {
+ this.connect(serverUrl);
+ }
+
+ private connect(serverUrl: string): void {
+ this.ws = new WebSocket(serverUrl);
+
+ this.ws.onopen = () => {
+ console.log('Connected to server');
+ this.connected = true;
+ if (this.onConnectedCallback) {
+ this.onConnectedCallback();
+ }
+ };
+
+ this.ws.onmessage = (event) => {
+ const message = JSON.parse(event.data) as ServerMessage;
+ this.handleServerMessage(message);
+ };
+
+ this.ws.onclose = () => {
+ console.log('Disconnected from server');
+ this.connected = false;
+ };
+
+ this.ws.onerror = (error) => {
+ console.error('WebSocket error:', error);
+ };
+ }
+
+ private handleServerMessage(message: ServerMessage): void {
+ if (message.type === MessageType.STATE) {
+ this.handleFullState(message);
+ } else if (message.type === MessageType.DELTA_STATE) {
+ this.handleDeltaState(message as DeltaStateMessage);
+ } else if (message.type === MessageType.DEAD) {
+ const deadMessage = message as DeadMessage;
+ console.log('You died! Final score:', deadMessage.finalScore);
+ if (this.onDeadCallback) {
+ this.onDeadCallback(deadMessage.finalScore);
+ }
+ }
+ }
+
+ private handleFullState(message: StateMessage): void {
+ this.previousState = this.currentState;
+ this.currentState = message;
+ this.lastUpdateTime = Date.now();
+
+ // When server sends yourId, it means we have an active snake (or just joined/rejoined)
+ // Always process it, even if it's the same ID (handles respawn case)
+ if (message.yourId) {
+ const isNewOrChanged = this.playerId !== message.yourId;
+ this.playerId = message.yourId;
+
+ if (isNewOrChanged) {
+ console.log('Received player ID:', this.playerId);
+ } else {
+ console.log('Received player ID (rejoined with same ID):', this.playerId);
+ }
+
+ // Always notify when server sends yourId (handles respawn/rejoin)
+ if (this.onPlayerIdReceivedCallback) {
+ this.onPlayerIdReceivedCallback(this.playerId);
+ }
+ }
+
+ if (this.onStateUpdateCallback) {
+ this.onStateUpdateCallback(message);
+ }
+ }
+
+ private handleDeltaState(delta: DeltaStateMessage): void {
+ // If we don't have a current state, we can't apply delta (shouldn't happen)
+ if (!this.currentState) {
+ console.warn('Received delta without current state, requesting full state...');
+ return;
+ }
+
+ this.previousState = this.currentState;
+
+ // Create a new state by applying delta to current state
+ const snakesMap = new Map();
+
+ // Start with current snakes
+ for (const snake of this.currentState.snakes) {
+ snakesMap.set(snake.id, snake);
+ }
+
+ // Remove snakes
+ for (const id of delta.snakesRemoved) {
+ snakesMap.delete(id);
+ }
+
+ // Add new snakes
+ for (const snake of delta.snakesAdded) {
+ snakesMap.set(snake.id, snake);
+ }
+
+ // Update changed snakes
+ for (const snake of delta.snakesUpdated) {
+ snakesMap.set(snake.id, snake);
+ }
+
+ // Apply pellet changes efficiently using ID-based tracking
+ // Build a map of current pellets by ID
+ const pelletsMap = new Map();
+ for (const pellet of this.currentState.pellets) {
+ pelletsMap.set(pellet[0], pellet); // pellet[0] is the ID
+ }
+
+ // Remove eaten pellets (pellets never respawn, so only removals are tracked)
+ for (const id of delta.pelletsRemoved) {
+ pelletsMap.delete(id);
+ }
+
+ const pellets = Array.from(pelletsMap.values());
+
+ // Create new state, preserving entropy info from initial full state
+ // (entropy fields are only sent in full state messages, not deltas, to save bandwidth)
+ this.currentState = {
+ type: MessageType.STATE,
+ tick: delta.tick,
+ snakes: Array.from(snakesMap.values()),
+ pellets,
+ leaderboard: delta.leaderboardChanged || this.currentState.leaderboard,
+ matchId: delta.matchId || this.currentState.matchId,
+ entropyPending: delta.entropyPending !== undefined ? delta.entropyPending : this.currentState.entropyPending,
+ entropyRequestId: this.currentState.entropyRequestId, // Preserved from full state
+ useFairRNG: delta.useFairRNG !== undefined ? delta.useFairRNG : this.currentState.useFairRNG,
+ entropySeed: this.currentState.entropySeed, // Preserved from full state
+ mapType: this.currentState.mapType, // Preserved from full state
+ };
+
+ this.lastUpdateTime = Date.now();
+
+ // Handle yourId if present in delta (for respawn)
+ if (delta.yourId) {
+ const isNewOrChanged = this.playerId !== delta.yourId;
+ this.playerId = delta.yourId;
+
+ if (isNewOrChanged) {
+ console.log('Received player ID (delta):', this.playerId);
+ }
+
+ if (this.onPlayerIdReceivedCallback) {
+ this.onPlayerIdReceivedCallback(this.playerId);
+ }
+ }
+
+ if (this.onStateUpdateCallback) {
+ this.onStateUpdateCallback(this.currentState);
+ }
+ }
+
+ join(name: string, address: string): void {
+ if (!this.connected || !this.ws) return;
+
+ const message: JoinMessage = {
+ type: MessageType.JOIN,
+ name,
+ address,
+ };
+
+ this.ws.send(JSON.stringify(message));
+ console.log(`Sent JOIN message for wallet: ${address}`);
+ }
+
+ sendInput(targetAngle: number): void {
+ if (!this.connected || !this.ws) return;
+
+ const message: InputMessage = {
+ type: MessageType.INPUT,
+ targetAngle,
+ };
+
+ this.ws.send(JSON.stringify(message));
+ }
+
+ sendCustomMessage(message: any): void {
+ if (!this.connected || !this.ws) return;
+ this.ws.send(JSON.stringify(message));
+ }
+
+ onStateUpdate(callback: (state: StateMessage) => void): void {
+ this.onStateUpdateCallback = callback;
+ }
+
+ onDead(callback: (score: number) => void): void {
+ this.onDeadCallback = callback;
+ }
+
+ onConnected(callback: () => void): void {
+ this.onConnectedCallback = callback;
+ }
+
+ onPlayerIdReceived(callback: (playerId: string) => void): void {
+ this.onPlayerIdReceivedCallback = callback;
+ }
+
+ getInterpolatedState(): StateMessage | null {
+ if (!this.currentState) return null;
+ if (!this.previousState) return this.currentState;
+
+ // Calculate time-based interpolation
+ const timeSinceUpdate = Date.now() - this.lastUpdateTime;
+ const alpha = Math.min(timeSinceUpdate / 50, 1); // 50ms = TICK_INTERVAL
+
+ if (alpha >= 1) return this.currentState;
+
+ // Create interpolated state
+ const interpolated: StateMessage = {
+ ...this.currentState,
+ snakes: this.currentState.snakes.map((currentSnake) => {
+ const prevSnake = this.previousState!.snakes.find((s: SerializedSnake) => s.id === currentSnake.id);
+ if (!prevSnake) return currentSnake;
+
+ return {
+ ...currentSnake,
+ head: [
+ prevSnake.head[0] + (currentSnake.head[0] - prevSnake.head[0]) * alpha,
+ prevSnake.head[1] + (currentSnake.head[1] - prevSnake.head[1]) * alpha,
+ ] as [number, number],
+ segments: currentSnake.segments.map((seg: [number, number], segIndex: number) => {
+ const prevSeg = prevSnake.segments[segIndex];
+ if (!prevSeg) return seg;
+ return [
+ prevSeg[0] + (seg[0] - prevSeg[0]) * alpha,
+ prevSeg[1] + (seg[1] - prevSeg[1]) * alpha,
+ ] as [number, number];
+ }),
+ };
+ }),
+ };
+
+ return interpolated;
+ }
+
+ getCurrentState(): StateMessage | null {
+ return this.currentState;
+ }
+
+ getPlayerId(): string | null {
+ return this.playerId;
+ }
+
+ isConnected(): boolean {
+ return this.connected;
+ }
+
+ getPlayerSnake(): SerializedSnake | null {
+ if (!this.currentState || !this.playerId) return null;
+ return this.currentState.snakes.find((s: SerializedSnake) => s.id === this.playerId) || null;
+ }
+}
+
diff --git a/entropy/0xSlither/client/src/InputHandler.ts b/entropy/0xSlither/client/src/InputHandler.ts
new file mode 100644
index 0000000..14eb166
--- /dev/null
+++ b/entropy/0xSlither/client/src/InputHandler.ts
@@ -0,0 +1,42 @@
+import { Camera } from './Camera';
+
+export class InputHandler {
+ private mouseX = 0;
+ private mouseY = 0;
+ private canvas: HTMLCanvasElement;
+
+ constructor(canvas: HTMLCanvasElement) {
+ this.canvas = canvas;
+
+ canvas.addEventListener('mousemove', (e) => {
+ this.mouseX = e.clientX;
+ this.mouseY = e.clientY;
+ });
+
+ // Touch support for mobile (optional but nice to have)
+ canvas.addEventListener('touchmove', (e) => {
+ e.preventDefault();
+ if (e.touches.length > 0) {
+ this.mouseX = e.touches[0].clientX;
+ this.mouseY = e.touches[0].clientY;
+ }
+ });
+ }
+
+ getTargetAngle(playerX: number, playerY: number, camera: Camera): number {
+ // Convert player world position to screen position
+ const screenPos = camera.worldToScreen(
+ playerX,
+ playerY,
+ this.canvas.width,
+ this.canvas.height
+ );
+
+ // Calculate angle from player to mouse
+ const dx = this.mouseX - screenPos.x;
+ const dy = this.mouseY - screenPos.y;
+
+ return Math.atan2(dy, dx);
+ }
+}
+
diff --git a/entropy/0xSlither/client/src/Renderer.ts b/entropy/0xSlither/client/src/Renderer.ts
new file mode 100644
index 0000000..7dd9ba0
--- /dev/null
+++ b/entropy/0xSlither/client/src/Renderer.ts
@@ -0,0 +1,295 @@
+import { StateMessage, SerializedSnake, SNAKE_HEAD_RADIUS, SNAKE_SEGMENT_RADIUS, WORLD_WIDTH, WORLD_HEIGHT } from '@0xslither/shared';
+import { Camera } from './Camera';
+
+export class Renderer {
+ private ctx: CanvasRenderingContext2D;
+ private canvas: HTMLCanvasElement;
+ private camera: Camera;
+ private isSpectatorMode = false;
+
+ constructor(canvas: HTMLCanvasElement) {
+ this.canvas = canvas;
+ this.ctx = canvas.getContext('2d')!;
+ this.camera = new Camera();
+ this.resizeCanvas();
+
+ window.addEventListener('resize', () => this.resizeCanvas());
+ }
+
+ private resizeCanvas(): void {
+ this.canvas.width = window.innerWidth;
+ this.canvas.height = window.innerHeight;
+ }
+
+ render(state: StateMessage, playerId: string | null): void {
+ // Determine if in spectator mode
+ this.isSpectatorMode = playerId === null;
+
+ // Clear canvas with crypto black background
+ this.ctx.fillStyle = '#0a0e0a';
+ this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
+
+ // Update camera to follow player if actively playing
+ const playerSnake = state.snakes.find((s: any) => s.id === playerId);
+ if (playerSnake) {
+ this.camera.setTarget({ x: playerSnake.head[0], y: playerSnake.head[1] });
+ }
+ // If no player snake, camera stays at last position (don't update target)
+ this.camera.update();
+
+ // Apply faded effect if in spectator mode
+ if (this.isSpectatorMode) {
+ this.ctx.globalAlpha = 0.4;
+ }
+
+ // Draw world boundaries
+ this.drawBoundaries();
+
+ // Draw grid
+ this.drawGrid();
+
+ // Draw pellets
+ for (const pellet of state.pellets) {
+ this.drawPellet(pellet);
+ }
+
+ // Draw snakes (bodies first, then heads)
+ for (const snake of state.snakes) {
+ this.drawSnakeBody(snake);
+ }
+ for (const snake of state.snakes) {
+ this.drawSnakeHead(snake);
+ }
+
+ // Draw snake names
+ for (const snake of state.snakes) {
+ this.drawSnakeName(snake);
+ }
+
+ // Reset alpha for UI elements
+ this.ctx.globalAlpha = 1.0;
+ }
+
+ private drawBoundaries(): void {
+ // Draw boundary box
+ const topLeft = this.camera.worldToScreen(0, 0, this.canvas.width, this.canvas.height);
+ const bottomRight = this.camera.worldToScreen(WORLD_WIDTH, WORLD_HEIGHT, this.canvas.width, this.canvas.height);
+
+ this.ctx.strokeStyle = '#00ff88';
+ this.ctx.lineWidth = 4;
+ this.ctx.setLineDash([20, 10]);
+ this.ctx.strokeRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
+ this.ctx.setLineDash([]);
+
+ // Add glowing effect
+ this.ctx.shadowColor = 'rgba(0, 255, 136, 0.5)';
+ this.ctx.shadowBlur = 10;
+ this.ctx.strokeRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
+ this.ctx.shadowBlur = 0;
+
+ // Add a semi-transparent overlay outside boundaries
+ this.ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
+
+ // Top
+ if (topLeft.y > 0) {
+ this.ctx.fillRect(0, 0, this.canvas.width, topLeft.y);
+ }
+ // Bottom
+ if (bottomRight.y < this.canvas.height) {
+ this.ctx.fillRect(0, bottomRight.y, this.canvas.width, this.canvas.height - bottomRight.y);
+ }
+ // Left
+ if (topLeft.x > 0) {
+ this.ctx.fillRect(0, 0, topLeft.x, this.canvas.height);
+ }
+ // Right
+ if (bottomRight.x < this.canvas.width) {
+ this.ctx.fillRect(bottomRight.x, 0, this.canvas.width - bottomRight.x, this.canvas.height);
+ }
+ }
+
+ private drawGrid(): void {
+ const gridSize = 50;
+ const startX = Math.floor((this.camera.x - this.canvas.width / 2) / gridSize) * gridSize;
+ const startY = Math.floor((this.camera.y - this.canvas.height / 2) / gridSize) * gridSize;
+ const endX = startX + this.canvas.width + gridSize;
+ const endY = startY + this.canvas.height + gridSize;
+
+ this.ctx.strokeStyle = 'rgba(0, 255, 136, 0.08)';
+ this.ctx.lineWidth = 1;
+
+ for (let x = startX; x < endX; x += gridSize) {
+ const screenPos = this.camera.worldToScreen(x, 0, this.canvas.width, this.canvas.height);
+ this.ctx.beginPath();
+ this.ctx.moveTo(screenPos.x, 0);
+ this.ctx.lineTo(screenPos.x, this.canvas.height);
+ this.ctx.stroke();
+ }
+
+ for (let y = startY; y < endY; y += gridSize) {
+ const screenPos = this.camera.worldToScreen(0, y, this.canvas.width, this.canvas.height);
+ this.ctx.beginPath();
+ this.ctx.moveTo(0, screenPos.y);
+ this.ctx.lineTo(this.canvas.width, screenPos.y);
+ this.ctx.stroke();
+ }
+ }
+
+ private drawPellet(pellet: [string, number, number, number, string, number]): void {
+ const [id, x, y, size, color, tokenAmount] = pellet;
+ const screenPos = this.camera.worldToScreen(x, y, this.canvas.width, this.canvas.height);
+
+ // Skip if off-screen
+ if (
+ screenPos.x < -20 ||
+ screenPos.x > this.canvas.width + 20 ||
+ screenPos.y < -20 ||
+ screenPos.y > this.canvas.height + 20
+ ) {
+ return;
+ }
+
+ this.ctx.fillStyle = color;
+ this.ctx.beginPath();
+ this.ctx.arc(screenPos.x, screenPos.y, size, 0, Math.PI * 2);
+ this.ctx.fill();
+ }
+
+ private drawSnakeBody(snake: SerializedSnake): void {
+ for (const segment of snake.segments) {
+ const screenPos = this.camera.worldToScreen(segment[0], segment[1], this.canvas.width, this.canvas.height);
+
+ // Skip if off-screen
+ if (
+ screenPos.x < -50 ||
+ screenPos.x > this.canvas.width + 50 ||
+ screenPos.y < -50 ||
+ screenPos.y > this.canvas.height + 50
+ ) {
+ continue;
+ }
+
+ // Draw segment with gradient
+ const gradient = this.ctx.createRadialGradient(screenPos.x, screenPos.y, 0, screenPos.x, screenPos.y, SNAKE_SEGMENT_RADIUS);
+ gradient.addColorStop(0, snake.color);
+ gradient.addColorStop(1, this.darkenColor(snake.color, 0.7));
+
+ this.ctx.fillStyle = gradient;
+ this.ctx.beginPath();
+ this.ctx.arc(screenPos.x, screenPos.y, SNAKE_SEGMENT_RADIUS, 0, Math.PI * 2);
+ this.ctx.fill();
+
+ // Outline
+ this.ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)';
+ this.ctx.lineWidth = 1;
+ this.ctx.stroke();
+ }
+ }
+
+ private drawSnakeHead(snake: SerializedSnake): void {
+ const screenPos = this.camera.worldToScreen(snake.head[0], snake.head[1], this.canvas.width, this.canvas.height);
+
+ // Skip if off-screen
+ if (
+ screenPos.x < -50 ||
+ screenPos.x > this.canvas.width + 50 ||
+ screenPos.y < -50 ||
+ screenPos.y > this.canvas.height + 50
+ ) {
+ return;
+ }
+
+ // Draw head with gradient
+ const gradient = this.ctx.createRadialGradient(screenPos.x, screenPos.y, 0, screenPos.x, screenPos.y, SNAKE_HEAD_RADIUS);
+ gradient.addColorStop(0, this.lightenColor(snake.color, 1.2));
+ gradient.addColorStop(1, snake.color);
+
+ this.ctx.fillStyle = gradient;
+ this.ctx.beginPath();
+ this.ctx.arc(screenPos.x, screenPos.y, SNAKE_HEAD_RADIUS, 0, Math.PI * 2);
+ this.ctx.fill();
+
+ // Outline
+ this.ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)';
+ this.ctx.lineWidth = 2;
+ this.ctx.stroke();
+
+ // Eyes
+ const eyeOffset = 4;
+ const eyeSize = 2;
+ const eyeX1 = screenPos.x + Math.cos(snake.angle - 0.5) * eyeOffset;
+ const eyeY1 = screenPos.y + Math.sin(snake.angle - 0.5) * eyeOffset;
+ const eyeX2 = screenPos.x + Math.cos(snake.angle + 0.5) * eyeOffset;
+ const eyeY2 = screenPos.y + Math.sin(snake.angle + 0.5) * eyeOffset;
+
+ this.ctx.fillStyle = 'white';
+ this.ctx.beginPath();
+ this.ctx.arc(eyeX1, eyeY1, eyeSize, 0, Math.PI * 2);
+ this.ctx.arc(eyeX2, eyeY2, eyeSize, 0, Math.PI * 2);
+ this.ctx.fill();
+
+ this.ctx.fillStyle = 'black';
+ this.ctx.beginPath();
+ this.ctx.arc(eyeX1, eyeY1, eyeSize * 0.6, 0, Math.PI * 2);
+ this.ctx.arc(eyeX2, eyeY2, eyeSize * 0.6, 0, Math.PI * 2);
+ this.ctx.fill();
+ }
+
+ private drawSnakeName(snake: SerializedSnake): void {
+ const screenPos = this.camera.worldToScreen(snake.head[0], snake.head[1], this.canvas.width, this.canvas.height);
+
+ // Skip if off-screen
+ if (
+ screenPos.x < -50 ||
+ screenPos.x > this.canvas.width + 50 ||
+ screenPos.y < -50 ||
+ screenPos.y > this.canvas.height + 50
+ ) {
+ return;
+ }
+
+ this.ctx.font = '600 13px Orbitron, monospace, sans-serif';
+ this.ctx.textAlign = 'center';
+ this.ctx.textBaseline = 'bottom';
+
+ // Display shortened address only
+ const displayName = snake.address
+ ? this.shortenAddress(snake.address)
+ : snake.name;
+
+ // Background
+ const textWidth = this.ctx.measureText(displayName).width;
+ this.ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
+ this.ctx.fillRect(screenPos.x - textWidth / 2 - 4, screenPos.y - SNAKE_HEAD_RADIUS - 24, textWidth + 8, 18);
+
+ // Text
+ this.ctx.fillStyle = 'white';
+ this.ctx.fillText(displayName, screenPos.x, screenPos.y - SNAKE_HEAD_RADIUS - 8);
+ }
+
+ private shortenAddress(address: string): string {
+ if (!address || address.length < 10) return address;
+ return `${address.slice(0, 6)}โฆ${address.slice(-4)}`;
+ }
+
+ private lightenColor(color: string, factor: number): string {
+ // Parse HSL color
+ const match = color.match(/hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)/);
+ if (match) {
+ const h = parseInt(match[1]);
+ const s = parseInt(match[2]);
+ const l = Math.min(100, parseInt(match[3]) * factor);
+ return `hsl(${h}, ${s}%, ${l}%)`;
+ }
+ return color;
+ }
+
+ private darkenColor(color: string, factor: number): string {
+ return this.lightenColor(color, factor);
+ }
+
+ getCamera(): Camera {
+ return this.camera;
+ }
+}
+
diff --git a/entropy/0xSlither/client/src/UI.ts b/entropy/0xSlither/client/src/UI.ts
new file mode 100644
index 0000000..85bcd1b
--- /dev/null
+++ b/entropy/0xSlither/client/src/UI.ts
@@ -0,0 +1,530 @@
+import { StateMessage } from '@0xslither/shared';
+
+export class UI {
+ private startScreen: HTMLElement;
+ private deathScreen: HTMLElement;
+ private deathScreenTitle: HTMLElement;
+ private leaderboard: HTMLElement;
+ private leaderboardList: HTMLElement;
+ private connectionStatus: HTMLElement;
+ private playButton: HTMLButtonElement;
+ private respawnButton: HTMLButtonElement;
+ private finalScoreElement: HTMLElement;
+ private bestScoreDisplay: HTMLElement;
+ private connectWalletButton: HTMLButtonElement;
+ private walletStatus: HTMLElement;
+ private stakeSection: HTMLElement;
+ private stakeButton: HTMLButtonElement;
+ private tokenBalance: HTMLElement;
+ private onChainStats: HTMLElement;
+ private currentScore: HTMLElement;
+ private gameControls: HTMLElement;
+ private tapOutButton: HTMLButtonElement;
+ private loadingOverlay: HTMLElement;
+ private loadingMessage: HTMLElement;
+ private retryButton: HTMLButtonElement;
+
+ constructor() {
+ this.startScreen = document.getElementById('startScreen')!;
+ this.deathScreen = document.getElementById('deathScreen')!;
+ this.deathScreenTitle = document.getElementById('deathScreenTitle')!;
+ this.leaderboard = document.getElementById('leaderboard')!;
+ this.leaderboardList = document.getElementById('leaderboardList')!;
+ this.connectionStatus = document.getElementById('connectionStatus')!;
+ this.playButton = document.getElementById('playButton') as HTMLButtonElement;
+ this.respawnButton = document.getElementById('respawnButton') as HTMLButtonElement;
+ this.finalScoreElement = document.getElementById('finalScore')!;
+ this.bestScoreDisplay = document.getElementById('bestScoreDisplay')!;
+ this.connectWalletButton = document.getElementById('connectWalletButton') as HTMLButtonElement;
+ this.walletStatus = document.getElementById('walletStatus')!;
+ this.stakeSection = document.getElementById('stakeSection')!;
+ this.stakeButton = document.getElementById('stakeButton') as HTMLButtonElement;
+ this.tokenBalance = document.getElementById('tokenBalance')!;
+ this.onChainStats = document.getElementById('onChainStats')!;
+ this.currentScore = document.getElementById('currentScore')!;
+ this.gameControls = document.getElementById('gameControls')!;
+ this.tapOutButton = document.getElementById('tapOutButton') as HTMLButtonElement;
+ this.loadingOverlay = document.getElementById('loadingOverlay')!;
+ this.loadingMessage = document.getElementById('loadingMessage')!;
+ this.retryButton = document.getElementById('retryButton') as HTMLButtonElement;
+ }
+
+ showStartScreen(): void {
+ this.startScreen.classList.remove('hidden');
+ this.deathScreen.classList.add('hidden');
+ this.leaderboard.classList.add('hidden');
+ }
+
+ hideStartScreen(): void {
+ this.startScreen.classList.add('hidden');
+ this.leaderboard.classList.remove('hidden');
+ this.gameControls.classList.remove('hidden');
+ this.onChainStats.classList.remove('hidden');
+ }
+
+ showDeathScreen(score: number): void {
+ this.deathScreen.classList.remove('hidden');
+ this.finalScoreElement.textContent = score.toString();
+
+ // Reset title to default
+ this.deathScreenTitle.textContent = 'You Died!';
+ this.deathScreenTitle.style.color = '#00ff88';
+
+ // Set best score to loading initially
+ const scoreSpan = this.bestScoreDisplay.querySelector('.score');
+ if (scoreSpan) {
+ scoreSpan.textContent = 'Loading...';
+ }
+ }
+
+ hideDeathScreen(): void {
+ this.deathScreen.classList.add('hidden');
+ }
+
+ updateConnectionStatus(status: string): void {
+ this.connectionStatus.textContent = status;
+
+ if (status === 'Connected') {
+ this.connectionStatus.style.background = 'rgba(0, 255, 136, 0.2)';
+ this.connectionStatus.style.borderColor = 'rgba(0, 255, 136, 0.5)';
+ this.connectionStatus.style.boxShadow = '0 0 15px rgba(0, 255, 136, 0.3)';
+ } else if (status === 'Disconnected') {
+ this.connectionStatus.style.background = 'rgba(255, 51, 102, 0.2)';
+ this.connectionStatus.style.borderColor = 'rgba(255, 51, 102, 0.5)';
+ this.connectionStatus.style.boxShadow = '0 0 15px rgba(255, 51, 102, 0.3)';
+ } else {
+ this.connectionStatus.style.background = 'rgba(255, 204, 0, 0.2)';
+ this.connectionStatus.style.borderColor = 'rgba(255, 204, 0, 0.5)';
+ this.connectionStatus.style.boxShadow = '0 0 15px rgba(255, 204, 0, 0.3)';
+ }
+ }
+
+ updateLeaderboard(state: StateMessage): void {
+ this.leaderboardList.innerHTML = '';
+
+ state.leaderboard.forEach(([name, score, address]: [string, number, string?], index: number) => {
+ const li = document.createElement('li');
+ // Since name is now the wallet address, just display the shortened address
+ const displayName = this.shortenAddress(address || name);
+ li.innerHTML = `
+ ${index + 1}. ${displayName}
+ ${score}
+ `;
+ this.leaderboardList.appendChild(li);
+ });
+ }
+
+ private shortenAddress(address: string): string {
+ if (!address || address.length < 10) return address;
+ return `${address.slice(0, 6)}โฆ${address.slice(-4)}`;
+ }
+
+ onPlay(callback: () => void): void {
+ this.playButton.addEventListener('click', () => {
+ callback();
+ });
+ }
+
+ onRespawn(callback: () => void): void {
+ this.respawnButton.addEventListener('click', () => {
+ callback();
+ });
+ }
+
+ onConnectWallet(callback: () => Promise): void {
+ this.connectWalletButton.addEventListener('click', async () => {
+ this.connectWalletButton.disabled = true;
+ this.walletStatus.textContent = 'Connecting...';
+ this.walletStatus.className = '';
+
+ const address = await callback();
+
+ this.connectWalletButton.disabled = false;
+
+ if (address) {
+ this.setWalletConnected(address);
+ } else {
+ this.walletStatus.textContent = 'Connection failed. Please try again.';
+ this.walletStatus.className = 'error';
+ }
+ });
+ }
+
+ setWalletConnected(address: string): void {
+ this.connectWalletButton.textContent = 'Wallet Connected';
+ this.connectWalletButton.disabled = true;
+ this.walletStatus.textContent = `Connected: ${this.shortenAddress(address)}`;
+ this.walletStatus.className = 'success';
+ this.stakeSection.classList.remove('hidden');
+ // VAULT MODE: Enable deposit immediately (no match ID wait)
+ this.stakeButton.disabled = false;
+ this.stakeButton.textContent = 'Deposit 1 SSS to Vault';
+ }
+
+ updateWalletAddress(address: string): void {
+ this.walletStatus.textContent = `Connected: ${this.shortenAddress(address)}`;
+ this.walletStatus.className = 'success';
+ }
+
+ setWalletNotConnected(): void {
+ this.connectWalletButton.textContent = 'Connect Wallet';
+ this.connectWalletButton.disabled = false;
+ this.walletStatus.textContent = 'Not connected';
+ this.walletStatus.className = '';
+ this.stakeSection.classList.add('hidden');
+ this.playButton.classList.add('hidden');
+ }
+
+ setDeposited(): void {
+ this.stakeButton.textContent = 'โ Deposited to Vault';
+ this.stakeButton.disabled = true;
+ this.playButton.classList.remove('hidden');
+ this.playButton.disabled = false;
+ }
+
+ resetDepositState(): void {
+ this.stakeButton.textContent = 'Deposit 1 SSS to Vault';
+ this.stakeButton.disabled = false;
+ this.playButton.classList.add('hidden');
+ this.playButton.disabled = true;
+ }
+
+ /**
+ * @deprecated Use setDeposited() for vault mode
+ */
+ setStaked(): void {
+ this.setDeposited();
+ }
+
+ /**
+ * @deprecated Use resetDepositState() for vault mode
+ */
+ resetStakeState(): void {
+ this.resetDepositState();
+ }
+
+ setWalletNotAvailable(): void {
+ this.connectWalletButton.disabled = true;
+ this.walletStatus.textContent = 'No wallet detected. Cannot play.';
+ this.walletStatus.className = 'error';
+ }
+
+ updateTokenBalance(balance: string): void {
+ this.tokenBalance.textContent = balance;
+ }
+
+ getDepositAmount(): string {
+ return '1'; // Fixed deposit amount for vault
+ }
+
+ /**
+ * @deprecated Use getDepositAmount() for vault mode
+ */
+ getStakeAmount(): string {
+ return this.getDepositAmount();
+ }
+
+ onDeposit(callback: () => void): void {
+ this.stakeButton.addEventListener('click', () => {
+ callback();
+ });
+ }
+
+ /**
+ * @deprecated Use onDeposit() for vault mode
+ */
+ onStake(callback: () => void): void {
+ this.onDeposit(callback);
+ }
+
+ updateCurrentScore(pelletTokens: string): void {
+ // VAULT MODE: Display pellet tokens only (kill rewards go directly to wallet)
+ this.currentScore.textContent = `${pelletTokens} SSS (Pellets)`;
+ }
+
+ updateDeathScreenWithBestScore(finalScore: number, bestScore: number): void {
+ // Update the best score display
+ const scoreSpan = this.bestScoreDisplay.querySelector('.score');
+ if (scoreSpan) {
+ scoreSpan.textContent = bestScore.toString();
+ }
+
+ // If it's a new high score, update the title
+ if (finalScore >= bestScore && finalScore > 0) {
+ this.deathScreenTitle.textContent = 'New High Score!';
+ this.deathScreenTitle.style.color = '#FFD700'; // Gold color
+ } else {
+ this.deathScreenTitle.textContent = 'You Died!';
+ this.deathScreenTitle.style.color = '#00ff88'; // Reset to default
+ }
+ }
+
+ onTapOut(callback: () => void): void {
+ this.tapOutButton.addEventListener('click', () => {
+ callback();
+ });
+ }
+
+ setTapOutEnabled(enabled: boolean): void {
+ this.tapOutButton.disabled = !enabled;
+ }
+
+ showGameControls(): void {
+ this.gameControls.classList.remove('hidden');
+ }
+
+ hideGameControls(): void {
+ this.gameControls.classList.add('hidden');
+ }
+
+ showLoading(message: string): void {
+ this.loadingMessage.textContent = message;
+ this.retryButton.classList.add('hidden');
+ this.loadingOverlay.classList.remove('hidden');
+ }
+
+ hideLoading(): void {
+ this.loadingOverlay.classList.add('hidden');
+ }
+
+ showLoadingWithRetry(message: string): void {
+ this.loadingMessage.textContent = message;
+ this.retryButton.classList.remove('hidden');
+ this.loadingOverlay.classList.remove('hidden');
+ }
+
+ showError(message: string, duration: number = 5000): void {
+ // Create or get error notification element
+ let errorNotification = document.getElementById('errorNotification');
+
+ if (!errorNotification) {
+ errorNotification = document.createElement('div');
+ errorNotification.id = 'errorNotification';
+ errorNotification.style.cssText = `
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ background: rgba(255, 51, 102, 0.95);
+ color: white;
+ padding: 15px 20px;
+ border-radius: 8px;
+ border: 2px solid rgba(255, 51, 102, 1);
+ box-shadow: 0 4px 15px rgba(255, 51, 102, 0.3);
+ z-index: 10000;
+ max-width: 400px;
+ font-family: 'Orbitron', sans-serif;
+ font-size: 14px;
+ animation: slideIn 0.3s ease-out;
+ `;
+ document.body.appendChild(errorNotification);
+ }
+
+ errorNotification.textContent = message;
+ errorNotification.style.display = 'block';
+
+ // Auto-hide after duration
+ setTimeout(() => {
+ if (errorNotification) {
+ errorNotification.style.display = 'none';
+ }
+ }, duration);
+ }
+
+ onRetry(callback: () => void): void {
+ this.retryButton.addEventListener('click', () => {
+ callback();
+ });
+ }
+
+ /**
+ * Render entropy information panel showing Pyth Entropy status
+ */
+ renderEntropyInfo(gameState: StateMessage | null): void {
+ // Create entropy info panel if it doesn't exist
+ let entropyPanel = document.getElementById('entropy-panel');
+ if (!entropyPanel) {
+ entropyPanel = document.createElement('div');
+ entropyPanel.id = 'entropy-panel';
+ entropyPanel.style.cssText = `
+ position: fixed;
+ top: 80px;
+ right: 20px;
+ background: rgba(15, 23, 42, 0.95);
+ backdrop-filter: blur(10px);
+ border: 2px solid #00ff88;
+ border-radius: 12px;
+ padding: 16px 20px;
+ font-family: 'Orbitron', sans-serif;
+ font-size: 12px;
+ color: #fff;
+ max-width: 300px;
+ z-index: 1000;
+ box-shadow: 0 0 20px rgba(0, 255, 136, 0.3), 0 4px 15px rgba(0, 0, 0, 0.5);
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+ animation: slideInRight 0.5s ease-out;
+ `;
+
+ // Add keyframe animation for slide in
+ if (!document.getElementById('entropy-panel-animation')) {
+ const style = document.createElement('style');
+ style.id = 'entropy-panel-animation';
+ style.textContent = `
+ @keyframes slideInRight {
+ from {
+ opacity: 0;
+ transform: translateX(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+ }
+ @keyframes pulse {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.6;
+ }
+ }
+ `;
+ document.head.appendChild(style);
+ }
+
+ document.body.appendChild(entropyPanel);
+ }
+
+ // Only show RNG info on the start screen, not during gameplay
+ const isStartScreenVisible = !this.startScreen.classList.contains('hidden');
+
+ // Hide the panel if we're in-game (start screen is hidden)
+ if (!isStartScreenVisible) {
+ entropyPanel.style.display = 'none';
+ return;
+ }
+
+ // Only show if we have game state and match ID
+ if (!gameState || !gameState.matchId) {
+ entropyPanel.style.display = 'none';
+ return;
+ }
+
+ entropyPanel.style.display = 'block';
+
+ // Build entropy info content
+ let content = '';
+
+ if (gameState.entropyPending) {
+ // Waiting for Pyth Entropy reveal - animated loading state
+ entropyPanel.style.borderColor = '#fbbf24';
+ entropyPanel.style.boxShadow = '0 0 20px rgba(251, 191, 36, 0.4), 0 4px 15px rgba(0, 0, 0, 0.5)';
+
+ content = `
+
+
+
โณ
+
+ Generating Seed
+
+
+
+ Requesting randomness from
+ Pyth Entropy on Base Sepolia
+
+
+ `;
+ } else if (gameState.useFairRNG) {
+ // Using Pyth Entropy - show success status with green theme
+ entropyPanel.style.borderColor = '#00ff88';
+ entropyPanel.style.boxShadow = '0 0 20px rgba(0, 255, 136, 0.3), 0 4px 15px rgba(0, 0, 0, 0.5)';
+
+ const requestIdShort = gameState.entropyRequestId?.substring(0, 8) || 'N/A';
+ const seedShort = gameState.entropySeed ? `${gameState.entropySeed.slice(0, 10)}...${gameState.entropySeed.slice(-6)}` : null;
+
+ content = `
+
+
+
+ Match:
+ ${gameState.matchId.slice(0, 10)}...
+
+ ${gameState.entropyRequestId ? `
+
+ Request:
+ #${requestIdShort}
+
+ ` : ''}
+ ${seedShort ? `
+
+ Seed:
+ ${seedShort}
+
+ ` : ''}
+ ${gameState.mapType ? `
+
+ Map:
+ ${gameState.mapType}
+
+ ` : ''}
+
+ Powered by Pyth Entropy
+
+
+ `;
+ } else if (gameState.useFairRNG === false) {
+ // Fallback mode warning - orange/yellow theme
+ entropyPanel.style.borderColor = '#fbbf24';
+ entropyPanel.style.boxShadow = '0 0 20px rgba(251, 191, 36, 0.3), 0 4px 15px rgba(0, 0, 0, 0.5)';
+
+ content = `
+
+
+ Using fallback RNG
+ (not cryptographically fair)
+
+ `;
+ } else {
+ // Hide if status is unknown
+ entropyPanel.style.display = 'none';
+ }
+
+ entropyPanel.innerHTML = content;
+ }
+}
+
diff --git a/entropy/0xSlither/client/src/WalletService.ts b/entropy/0xSlither/client/src/WalletService.ts
new file mode 100644
index 0000000..fd80c43
--- /dev/null
+++ b/entropy/0xSlither/client/src/WalletService.ts
@@ -0,0 +1,472 @@
+import { ethers } from 'ethers';
+import { NETWORK_CONFIG, getAddChainParameters, getSwitchChainParameters } from './networkConfig';
+
+// Contract ABIs (minimal, only what we need)
+const STAKE_ARENA_ABI = [
+ 'function depositToVault() external payable',
+ 'function enterMatch(bytes32 matchId) external payable',
+ 'function tapOut(bytes32 matchId, uint256 score) external',
+ 'function getLeaderboard() external view returns (tuple(address player, uint256 score)[])',
+ 'function bestScore(address player) external view returns (uint256)',
+ 'function getStake(bytes32 matchId, address player) external view returns (uint256)',
+ 'function isActive(bytes32 matchId, address player) external view returns (bool)',
+];
+
+interface LeaderboardEntry {
+ player: string;
+ score: bigint;
+}
+
+export class WalletService {
+ private provider: ethers.BrowserProvider | null = null;
+ private signer: ethers.Signer | null = null;
+ private address: string | null = null;
+ private stakeArena: ethers.Contract | null = null;
+ private isConnecting: boolean = false; // Track if we're in the connection process
+
+ async connectWallet(): Promise {
+ this.isConnecting = true;
+ try {
+ // Check if MetaMask is installed
+ if (!window.ethereum) {
+ console.error('โ MetaMask not detected');
+ console.error('Please install MetaMask from https://metamask.io');
+ throw new Error('MetaMask not installed');
+ }
+
+ console.log('๐ Connecting to wallet...');
+ this.provider = new ethers.BrowserProvider(window.ethereum);
+
+ // Request account access
+ const accounts = await this.provider.send('eth_requestAccounts', []);
+ this.address = accounts[0];
+ console.log('โ
Account connected:', this.address);
+
+ this.signer = await this.provider.getSigner();
+
+ // Ensure we're on the correct network
+ await this.ensureCorrectNetwork();
+
+ console.log('โ
Wallet fully connected and configured');
+ console.log(`๐ก Network: ${NETWORK_CONFIG.chainName}`);
+ console.log(`๐ Using native ${NETWORK_CONFIG.nativeCurrency.symbol} token`);
+
+ this.isConnecting = false;
+ return this.address;
+ } catch (error: any) {
+ console.error('โ Failed to connect wallet:', error);
+
+ // Check both direct code and nested error code (ethers.js wraps errors)
+ const errorCode = error.code || error.error?.code || error.info?.error?.code;
+
+ // Provide user-friendly error messages
+ if (errorCode === 4001) {
+ console.error('๐ญ User rejected the connection request');
+ } else if (errorCode === -32002) {
+ console.error('โณ Connection request already pending - please check your wallet');
+ } else if (error.message?.includes('rejected')) {
+ console.error('๐ญ User rejected the request');
+ }
+
+ this.isConnecting = false;
+ return null;
+ }
+ }
+
+ /**
+ * Ensures the wallet is connected to the correct network
+ * Automatically switches or adds the network if needed
+ */
+ private async ensureCorrectNetwork(): Promise {
+ if (!this.provider) {
+ throw new Error('Provider not initialized');
+ }
+
+ try {
+ // Check current network
+ const network = await this.provider.getNetwork();
+ console.log(`๐ก Current network: Chain ID ${network.chainId}`);
+
+ if (network.chainId !== NETWORK_CONFIG.chainId) {
+ console.log(`๐ Wrong network detected. Switching to ${NETWORK_CONFIG.chainName}...`);
+ await this.switchToCorrectNetwork();
+
+ // After network switch, reinitialize signer to get fresh state
+ this.signer = await this.provider.getSigner();
+ console.log('โ
Signer reinitialized after network switch');
+ } else {
+ console.log(`โ
Already on ${NETWORK_CONFIG.chainName}`);
+ }
+ } catch (error) {
+ console.error('Error ensuring correct network:', error);
+ throw error;
+ }
+ }
+
+ /**
+ * Attempts to switch to the correct network
+ * If the network is not added to the wallet, it will be added automatically
+ */
+ private async switchToCorrectNetwork(): Promise {
+ if (!this.provider) {
+ throw new Error('Provider not initialized');
+ }
+
+ try {
+ // Try to switch to the network
+ console.log(`๐ Requesting network switch to ${NETWORK_CONFIG.chainName}...`);
+ await this.provider.send('wallet_switchEthereumChain', [
+ getSwitchChainParameters()
+ ]);
+ console.log(`โ
Successfully switched to ${NETWORK_CONFIG.chainName}`);
+ } catch (switchError: any) {
+ // Error code 4902 means the chain has not been added to MetaMask
+ // ethers.js wraps MetaMask errors, so we need to check multiple places
+ const errorCode = switchError.code || switchError.error?.code || switchError.info?.error?.code;
+ const errorMessage = switchError.message || '';
+
+ // Check for error code 4902 or error message containing 4902
+ const isNetworkNotFound = errorCode === 4902 || errorMessage.includes('"code": 4902') || errorMessage.includes('4902');
+ const isUserRejection = errorCode === 4001 || errorCode === 'ACTION_REJECTED';
+
+ console.log('๐ Switch error debug:', { errorCode, isNetworkNotFound, errorMessage: errorMessage.substring(0, 100) });
+
+ if (isNetworkNotFound) {
+ console.log(`โ Network not found in wallet. Adding ${NETWORK_CONFIG.chainName}...`);
+ await this.addNetworkToWallet();
+ } else if (isUserRejection) {
+ // User rejected the request
+ console.error('โ User rejected network switch request');
+ throw new Error('Network switch rejected by user');
+ } else {
+ console.error('โ Failed to switch network:', switchError);
+ throw switchError;
+ }
+ }
+ }
+
+ /**
+ * Adds the network to the user's wallet
+ */
+ private async addNetworkToWallet(): Promise {
+ if (!this.provider) {
+ throw new Error('Provider not initialized');
+ }
+
+ try {
+ console.log('โ Adding network to wallet...');
+ console.log('Network details:', {
+ chainId: NETWORK_CONFIG.chainIdHex,
+ chainName: NETWORK_CONFIG.chainName,
+ rpcUrls: NETWORK_CONFIG.rpcUrls,
+ blockExplorerUrls: NETWORK_CONFIG.blockExplorerUrls,
+ nativeCurrency: NETWORK_CONFIG.nativeCurrency,
+ });
+
+ await this.provider.send('wallet_addEthereumChain', [
+ getAddChainParameters()
+ ]);
+
+ console.log(`โ
Successfully added ${NETWORK_CONFIG.chainName} to wallet`);
+ console.log('โณ Waiting for network switch to complete...');
+
+ // Wait a moment for MetaMask to complete the network switch
+ await this.sleep(1000);
+
+ // Reinitialize provider to get fresh network state
+ this.provider = new ethers.BrowserProvider(window.ethereum);
+
+ // Verify we're now on the correct network
+ const network = await this.provider.getNetwork();
+ if (network.chainId !== NETWORK_CONFIG.chainId) {
+ console.warn('โ ๏ธ Network not switched yet, waiting...');
+ await this.sleep(1000);
+ this.provider = new ethers.BrowserProvider(window.ethereum);
+ const retryNetwork = await this.provider.getNetwork();
+ if (retryNetwork.chainId !== NETWORK_CONFIG.chainId) {
+ throw new Error('Network switch did not complete');
+ }
+ }
+
+ console.log('โ
Network switch confirmed');
+ } catch (addError: any) {
+ // Check both direct code and nested error code (ethers.js wraps errors)
+ const errorCode = addError.code || addError.error?.code || addError.info?.error?.code;
+ const errorMessage = addError.message || '';
+ const isUserRejection = errorCode === 4001 || errorCode === 'ACTION_REJECTED' || errorMessage.includes('4001');
+
+ if (isUserRejection) {
+ console.error('โ User rejected adding the network');
+ throw new Error('Adding network rejected by user');
+ } else {
+ console.error('โ Failed to add network:', addError);
+ throw addError;
+ }
+ }
+ }
+
+ /**
+ * Sleep utility for waiting
+ */
+ private sleep(ms: number): Promise {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+
+ initializeContracts(stakeArenaAddress: string): void {
+ if (!this.signer) {
+ throw new Error('Wallet not connected');
+ }
+
+ this.stakeArena = new ethers.Contract(stakeArenaAddress, STAKE_ARENA_ABI, this.signer);
+
+ console.log('Contracts initialized');
+ console.log('StakeArena:', stakeArenaAddress);
+ console.log('Using native SSS token (no approval needed)');
+ }
+
+ async getTokenBalance(): Promise {
+ if (!this.address || !this.provider) return '0';
+
+ try {
+ // Get native SSS balance
+ const balance: bigint = await this.provider.getBalance(this.address);
+ return Math.floor(parseFloat(ethers.formatEther(balance))).toString();
+ } catch (error) {
+ console.error('Error getting SSS balance:', error);
+ return '0';
+ }
+ }
+
+ /**
+ * Deposit SSS to the server vault for continuous gameplay
+ * This replaces the per-match enterMatch flow
+ */
+ async depositToVault(amount: string): Promise {
+ if (!this.stakeArena) {
+ console.error('StakeArena not initialized');
+ throw new Error('StakeArena not initialized');
+ }
+
+ console.log(`Depositing ${amount} SSS to vault...`);
+
+ const amountWei = ethers.parseEther(amount);
+ console.log('Amount in wei:', amountWei);
+
+ // Send SSS directly to vault (no match ID needed)
+ const tx = await this.stakeArena.depositToVault({ value: amountWei });
+ console.log('Transaction:', tx);
+ const receipt = await tx.wait(2); // Wait for 2 confirmations for better finality
+ console.log('Transaction confirmed:', receipt.hash);
+
+ // Add a small delay to ensure all RPC nodes have indexed the transaction
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ console.log('โ
Successfully deposited to vault');
+ return true;
+ }
+
+ /**
+ * @deprecated Use depositToVault() for continuous matches
+ */
+ async enterMatch(matchId: string, amount: string): Promise {
+ if (!this.stakeArena) {
+ console.error('StakeArena not initialized');
+ throw new Error('StakeArena not initialized');
+ }
+ console.log('Entering match', matchId, amount);
+
+ const amountWei = ethers.parseEther(amount);
+ // Convert string match ID to bytes32
+ const matchIdBytes32 = ethers.id(matchId);
+ console.log('Amount in wei:', amountWei);
+ console.log('Match ID as bytes32:', matchIdBytes32);
+ console.log(`Entering match ${matchId} with ${amount} SSS...`);
+ // Send SSS with the transaction (no approval needed!)
+ const tx = await this.stakeArena.enterMatch(matchIdBytes32, { value: amountWei });
+ console.log('Transaction:', tx);
+ const receipt = await tx.wait(2); // Wait for 2 confirmations for better finality
+ console.log('Transaction confirmed:', receipt.hash);
+
+ // Add a small delay to ensure all RPC nodes have indexed the transaction
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ console.log('Successfully entered match');
+ return true;
+ }
+
+ /**
+ * @deprecated In vault mode, tap-out is handled by server via direct transfers
+ * No on-chain tapOut call needed
+ */
+ async tapOut(matchId: string, score: number): Promise {
+ if (!this.stakeArena) {
+ throw new Error('StakeArena not initialized');
+ }
+
+ // Convert string match ID to bytes32
+ const matchIdBytes32 = ethers.id(matchId);
+ console.log(`Tapping out of match ${matchId} with score ${score}...`);
+ const tx = await this.stakeArena.tapOut(matchIdBytes32, score);
+ await tx.wait();
+
+ console.log('Successfully tapped out');
+ return true;
+ }
+
+ /**
+ * @deprecated Not used in vault mode (no per-match active tracking on-chain)
+ */
+ async isActive(matchId: string, playerAddress: string): Promise {
+ if (!this.stakeArena) {
+ throw new Error('StakeArena not initialized');
+ }
+
+ // Convert string match ID to bytes32
+ const matchIdBytes32 = ethers.id(matchId);
+ return await this.stakeArena.isActive(matchIdBytes32, playerAddress);
+ }
+
+ async getOnChainLeaderboard(): Promise> {
+ if (!this.stakeArena) {
+ console.warn('StakeArena not initialized');
+ return [];
+ }
+
+ try {
+ const entries: LeaderboardEntry[] = await this.stakeArena.getLeaderboard();
+ return entries.map(entry => ({
+ address: entry.player,
+ score: Number(entry.score),
+ }));
+ } catch (error) {
+ console.error('Error fetching on-chain leaderboard:', error);
+ return [];
+ }
+ }
+
+ async getBestScore(playerAddress?: string): Promise {
+ if (!this.stakeArena) return 0;
+
+ const address = playerAddress || this.address;
+ if (!address) return 0;
+
+ try {
+ const score: bigint = await this.stakeArena.bestScore(address);
+ return Number(score);
+ } catch (error) {
+ console.error('Error fetching best score:', error);
+ return 0;
+ }
+ }
+
+ /**
+ * @deprecated Not used in vault mode (stakes tracked server-side, not on-chain per match)
+ * In vault mode, stakes go directly to server wallet on deposit
+ */
+ async getCurrentStake(matchId: string, playerAddress?: string): Promise {
+ if (!this.stakeArena) return '0';
+
+ const address = playerAddress || this.address;
+ if (!address) return '0';
+
+ try {
+ // Convert string match ID to bytes32
+ const matchIdBytes32 = ethers.id(matchId);
+ const stake: bigint = await this.stakeArena.getStake(matchIdBytes32, address);
+ return Math.floor(parseFloat(ethers.formatEther(stake))).toString();
+ } catch (error) {
+ console.error('Error fetching current stake:', error);
+ return '0';
+ }
+ }
+
+ getAddress(): string | null {
+ return this.address;
+ }
+
+ isConnected(): boolean {
+ return this.address !== null;
+ }
+
+ /**
+ * Set up listeners for wallet events (account changes, network changes, etc.)
+ * Call this after connecting the wallet
+ */
+ setupWalletListeners(onAccountChange?: (address: string | null) => void, onNetworkChange?: () => void): void {
+ if (!window.ethereum) return;
+
+ // Listen for account changes
+ window.ethereum.on('accountsChanged', (accounts: string[]) => {
+ console.log('๐ Account changed');
+ if (accounts.length === 0) {
+ console.log('โ Wallet disconnected');
+ this.address = null;
+ this.signer = null;
+ if (onAccountChange) onAccountChange(null);
+ } else {
+ console.log('โ
New account:', accounts[0]);
+ this.address = accounts[0];
+ if (onAccountChange) onAccountChange(accounts[0]);
+ }
+ });
+
+ // Listen for network changes
+ window.ethereum.on('chainChanged', async (chainId: string) => {
+ const chainIdBigInt = BigInt(chainId);
+ console.log('๐ Network changed to chain ID:', chainIdBigInt);
+
+ // Don't show errors if we're in the connection process (e.g., auto-adding network)
+ if (this.isConnecting) {
+ console.log('โณ Network change detected during connection process, ignoring...');
+ return;
+ }
+
+ if (chainIdBigInt !== NETWORK_CONFIG.chainId) {
+ console.warn(`โ ๏ธ You are no longer on ${NETWORK_CONFIG.chainName}`);
+ console.warn('Please switch back to continue playing');
+ if (onNetworkChange) onNetworkChange();
+ } else {
+ console.log(`โ
Back on ${NETWORK_CONFIG.chainName}`);
+ }
+
+ // Reload the page on network change (recommended by MetaMask)
+ // window.location.reload();
+ });
+
+ console.log('๐ Wallet event listeners set up');
+ }
+
+ /**
+ * Get the current network information
+ */
+ async getCurrentNetwork(): Promise<{ chainId: bigint; name: string } | null> {
+ if (!this.provider) return null;
+
+ try {
+ const network = await this.provider.getNetwork();
+ return {
+ chainId: network.chainId,
+ name: network.name,
+ };
+ } catch (error) {
+ console.error('Error getting current network:', error);
+ return null;
+ }
+ }
+
+ /**
+ * Check if the current network is correct
+ */
+ async isOnCorrectNetwork(): Promise {
+ const network = await this.getCurrentNetwork();
+ return network ? network.chainId === NETWORK_CONFIG.chainId : false;
+ }
+}
+
+// Extend Window interface for TypeScript
+declare global {
+ interface Window {
+ ethereum?: any;
+ }
+}
+
diff --git a/entropy/0xSlither/client/src/main.ts b/entropy/0xSlither/client/src/main.ts
new file mode 100644
index 0000000..cf45eb1
--- /dev/null
+++ b/entropy/0xSlither/client/src/main.ts
@@ -0,0 +1,436 @@
+import { Game } from './Game';
+import { Renderer } from './Renderer';
+import { InputHandler } from './InputHandler';
+import { UI } from './UI';
+import { WalletService } from './WalletService';
+import { TICK_INTERVAL, MessageType, TapOutMessage } from '@0xslither/shared';
+
+// WebSocket server URL (adjust for production)
+const WSS_URL = import.meta.env.VITE_WSS_URL;
+
+// Contract addresses (configure these after deployment)
+const STAKE_ARENA_ADDRESS = import.meta.env.VITE_STAKE_ARENA_ADDRESS as string;
+
+class GameClient {
+ private game: Game;
+ private renderer: Renderer;
+ private inputHandler: InputHandler;
+ private ui: UI;
+ private wallet: WalletService | null = null;
+ private canvas: HTMLCanvasElement;
+ private isPlaying = false;
+ private isSpectating = false;
+ private lastInputTime = 0;
+ private inputThrottle = 50; // Send input at most every 50ms
+ private animationFrameId: number | null = null;
+ private walletAddress: string | null = null;
+ private hasDeposited = false; // Track if player has deposited to vault
+ private lastPelletTokens: number = 0; // Track pellet tokens for UI updates
+ private readonly FIXED_DEPOSIT_AMOUNT = '1'; // Fixed 1 SSS deposit amount
+
+ constructor() {
+ this.canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;
+ this.renderer = new Renderer(this.canvas);
+ this.inputHandler = new InputHandler(this.canvas);
+ this.ui = new UI();
+ this.game = new Game(WSS_URL);
+
+ // Wallet is now required
+ this.wallet = new WalletService();
+
+ this.setupEventHandlers();
+ this.ui.showStartScreen();
+ this.checkWalletAvailability();
+
+ // Start game loop immediately for spectator mode
+ this.gameLoop();
+ }
+
+ private checkWalletAvailability(): void {
+ if (!this.isWalletAvailable()) {
+ this.ui.setWalletNotAvailable();
+ }
+ }
+
+ private isWalletAvailable(): boolean {
+ return typeof (window as any).ethereum !== 'undefined';
+ }
+
+ private setupEventHandlers(): void {
+ this.game.onConnected(() => {
+ console.log('Connected to game server');
+ this.ui.updateConnectionStatus('Connected');
+ });
+
+ this.game.onStateUpdate((state) => {
+ this.ui.updateLeaderboard(state);
+ this.ui.renderEntropyInfo(state);
+
+ // Update pellet token display during gameplay
+ if (this.isPlaying && !this.isSpectating) {
+ this.updatePelletTokensFromGameState();
+ }
+ });
+
+ this.game.onPlayerIdReceived((playerId) => {
+ console.log('Player ID received, enabling gameplay:', playerId);
+ // Now that we have a player ID, we can start playing
+ if (!this.isPlaying) {
+ this.isPlaying = true;
+ this.ui.hideStartScreen();
+ this.ui.hideDeathScreen();
+
+ // Show tap out button during gameplay
+ this.ui.showGameControls();
+ this.ui.setTapOutEnabled(true);
+
+ // Initialize pellet token tracking
+ this.lastPelletTokens = 0;
+ this.ui.updateCurrentScore('0.00'); // Start at 0 pellet tokens
+ }
+ });
+
+ this.game.onDead(async (score) => {
+ console.log('Player died with score:', score);
+ this.isPlaying = false;
+ this.isSpectating = true;
+ this.ui.hideGameControls(); // Hide tap out button after death
+
+ // Reset tracking
+ this.lastPelletTokens = 0;
+
+ // VAULT MODE: On death, player loses everything
+ // No blockchain settlement needed - just show death screen
+ console.log('๐ Death in vault mode - deposit and pellet tokens lost');
+
+ // Update best score if needed
+ if (this.wallet && this.walletAddress) {
+ const bestScore = await this.wallet.getBestScore();
+ this.ui.updateDeathScreenWithBestScore(score, bestScore);
+ }
+
+ // Show death screen with respawn option
+ this.ui.showDeathScreen(score);
+ });
+
+ this.ui.onDeposit(async () => {
+ await this.handleDeposit();
+ });
+
+ this.ui.onPlay(async () => {
+ await this.startPlaying();
+ });
+
+ this.ui.onRespawn(async () => {
+ // Reset state and return to home page for a clean restart
+ this.isPlaying = false;
+ this.isSpectating = false;
+ this.ui.hideDeathScreen();
+ this.ui.resetDepositState(); // Reset deposit state (was resetStakeState)
+ this.ui.showStartScreen();
+
+ // Reset tracking
+ this.lastPelletTokens = 0;
+ });
+
+ this.ui.onConnectWallet(async () => {
+ return await this.connectWallet();
+ });
+
+ this.ui.onTapOut(async () => {
+ await this.handleTapOut();
+ });
+
+ this.ui.onRetry(async () => {
+ // VAULT MODE: No retry needed - server handles payouts
+ // Just hide loading and return to start screen
+ this.ui.hideLoading();
+ this.ui.showStartScreen();
+ });
+ }
+
+ private async connectWallet(): Promise {
+ if (!this.wallet) {
+ console.error('Wallet service not available');
+ return null;
+ }
+
+ try {
+ this.ui.showLoading('Connecting to MetaMask...');
+ this.walletAddress = await this.wallet.connectWallet();
+
+ if (this.walletAddress) {
+ // Initialize contracts if blockchain enabled
+ if (STAKE_ARENA_ADDRESS) {
+ this.wallet.initializeContracts(STAKE_ARENA_ADDRESS);
+ console.log('โ
Wallet connected and contracts initialized');
+ } else {
+ console.log('โ
Wallet connected (blockchain features disabled)');
+ }
+
+ // Set up wallet event listeners
+ this.wallet.setupWalletListeners(
+ // On account change
+ (newAddress) => {
+ if (newAddress) {
+ console.log('Account changed to:', newAddress);
+ this.walletAddress = newAddress;
+ this.ui.updateWalletAddress(newAddress);
+ // Update balance for new account
+ this.wallet?.getTokenBalance().then(balance => {
+ this.ui.updateTokenBalance(balance);
+ });
+ } else {
+ console.log('Wallet disconnected');
+ this.walletAddress = null;
+ this.ui.setWalletNotConnected();
+ }
+ },
+ // On network change
+ async () => {
+ console.warn('โ ๏ธ Network changed! Checking if on correct network...');
+ const isCorrect = await this.wallet?.isOnCorrectNetwork();
+ if (!isCorrect) {
+ this.ui.showError('Wrong network! Please switch back to 0xSlither Saga Chainlet');
+ }
+ }
+ );
+
+ // Update balance
+ const balance = await this.wallet.getTokenBalance();
+ this.ui.updateTokenBalance(balance);
+ }
+
+ this.ui.hideLoading();
+ return this.walletAddress;
+ } catch (error) {
+ console.error('Error connecting wallet:', error);
+ this.ui.hideLoading();
+ return null;
+ }
+ }
+
+ private async handleDeposit(): Promise {
+ // Wallet is required to deposit
+ if (!this.walletAddress) {
+ alert('Please connect your wallet first');
+ return;
+ }
+
+ // Vault mode: deposit directly to server vault
+ if (STAKE_ARENA_ADDRESS) {
+ try {
+ console.log(`Depositing ${this.FIXED_DEPOSIT_AMOUNT} SSS to vault...`);
+ this.ui.showLoading(`Depositing ${this.FIXED_DEPOSIT_AMOUNT} SSS... Please sign the transaction in MetaMask.`);
+
+ // Deposit to vault (no match ID needed)
+ await this.wallet!.depositToVault(this.FIXED_DEPOSIT_AMOUNT);
+
+ console.log('โ
Successfully deposited to vault');
+ this.ui.hideLoading();
+ this.ui.setDeposited(); // Update UI to show deposited state
+ this.hasDeposited = true;
+
+ // Update balance
+ const balance = await this.wallet!.getTokenBalance();
+ this.ui.updateTokenBalance(balance);
+ } catch (error: any) {
+ console.error('Error during deposit process:', error);
+ this.ui.hideLoading();
+
+ // Check if user rejected the transaction
+ if (error.code === 4001 || error.code === 'ACTION_REJECTED') {
+ alert('Transaction rejected. Please deposit to play.');
+ } else {
+ alert('Failed to deposit tokens. See console for details.');
+ }
+ return;
+ }
+ }
+ }
+
+ private async startPlaying(): Promise {
+ // Wallet is required to play
+ if (!this.walletAddress) {
+ alert('Please connect your wallet to play');
+ return;
+ }
+
+ // Show loading state while waiting for server connection and player ID
+ this.ui.showLoading('Connecting to game server...');
+
+ // Wait for WebSocket connection before joining (prevent race condition)
+ if (!this.game.isConnected()) {
+ console.log('WebSocket not connected yet, waiting...');
+ await this.waitForConnection();
+ }
+
+ this.ui.showLoading('Joining game...');
+
+ // Use wallet address as the player name
+ // The actual playing state will be enabled when we receive the player ID from the server
+ this.isSpectating = false;
+ this.game.join(this.walletAddress, this.walletAddress);
+
+ // Hide loading after a short delay (the onPlayerIdReceived callback will handle the rest)
+ setTimeout(() => {
+ this.ui.hideLoading();
+ }, 1000);
+ }
+
+ private async waitForConnection(): Promise {
+ return new Promise((resolve) => {
+ const checkInterval = setInterval(() => {
+ if (this.game.isConnected()) {
+ clearInterval(checkInterval);
+ console.log('WebSocket connected!');
+ resolve();
+ }
+ }, 100); // Check every 100ms
+
+ // Timeout after 10 seconds
+ setTimeout(() => {
+ clearInterval(checkInterval);
+ console.error('WebSocket connection timeout');
+ alert('Could not connect to game server. Please refresh and try again.');
+ resolve();
+ }, 10000);
+ });
+ }
+
+
+ private async handleTapOut(): Promise {
+ if (!this.wallet || !this.walletAddress) {
+ console.log('Cannot tap out: wallet not connected');
+ return;
+ }
+
+ // Get current pellet tokens before disconnecting
+ const playerSnake = this.game.getPlayerSnake();
+ const pelletTokens = playerSnake ? playerSnake.pelletTokens : 0;
+ const currentScore = playerSnake ? playerSnake.segments.length : 0;
+
+ console.log(`Tapping out with ${pelletTokens.toFixed(2)} pellet tokens (score: ${currentScore})`);
+
+ // Step 1: Immediately disconnect from game (remove player from server)
+ this.isPlaying = false;
+ this.isSpectating = true;
+ this.ui.hideGameControls();
+ this.ui.hideDeathScreen();
+
+ // Get match ID from server state
+ const state = this.game.getCurrentState();
+ const matchId = state?.matchId || 'permanent-match-v1';
+
+ // Send tap out message to server (server handles pellet token payout)
+ const tapOutMsg: TapOutMessage = {
+ type: MessageType.TAPOUT,
+ matchId: matchId,
+ };
+ this.game.sendCustomMessage(tapOutMsg);
+
+ // VAULT MODE: Server handles pellet token payout via direct transfer
+ // No on-chain transaction needed from client
+ this.ui.showLoading(`Tapping out... Server will pay out ${pelletTokens.toFixed(2)} SSS in pellet tokens.`);
+
+ // Wait a moment for server to process
+ await new Promise(resolve => setTimeout(resolve, 2000));
+
+ this.ui.hideLoading();
+
+ // Update balance and return to home screen
+ const balance = await this.wallet.getTokenBalance();
+ this.ui.updateTokenBalance(balance);
+
+ this.isSpectating = false;
+ this.ui.resetDepositState();
+ this.ui.showStartScreen();
+
+ console.log('โ
Tapped out successfully - pellet tokens paid by server');
+ }
+
+ /**
+ * Update pellet tokens display from game state
+ * In vault mode, we only track pellet tokens (no stake tracking)
+ */
+ private updatePelletTokensFromGameState(): void {
+ if (!this.game) return;
+
+ const playerSnake = this.game.getPlayerSnake();
+ if (!playerSnake) return;
+
+ const currentPelletTokens = playerSnake.pelletTokens || 0;
+
+ // Update UI if pellet tokens changed
+ if (currentPelletTokens !== this.lastPelletTokens) {
+ this.lastPelletTokens = currentPelletTokens;
+ this.ui.updateCurrentScore(currentPelletTokens.toFixed(2));
+ }
+ }
+
+ private isSpectatorMode(): boolean {
+ return this.isSpectating || !this.isPlaying;
+ }
+
+
+ private gameLoop = (): void => {
+ this.update();
+ this.render();
+ this.animationFrameId = requestAnimationFrame(this.gameLoop);
+ };
+
+ private update(): void {
+ if (!this.isPlaying) return;
+
+ const playerSnake = this.game.getPlayerSnake();
+ if (!playerSnake) return;
+
+ // Send input to server (throttled)
+ const now = Date.now();
+ if (now - this.lastInputTime > this.inputThrottle) {
+ const targetAngle = this.inputHandler.getTargetAngle(
+ playerSnake.head[0],
+ playerSnake.head[1],
+ this.renderer.getCamera()
+ );
+ this.game.sendInput(targetAngle);
+ this.lastInputTime = now;
+ }
+ }
+
+ private render(): void {
+ const state = this.game.getCurrentState();
+ if (!state) return;
+
+ const playerId = this.game.getPlayerId();
+ const isSpectator = this.isSpectatorMode();
+
+ // Get interpolated state for smoother visuals
+ const interpolatedState = this.game.getInterpolatedState();
+ const renderState = interpolatedState || state;
+
+ if (isSpectator) {
+ // Filter out dead player's snake from rendering
+ const filteredState = {
+ ...renderState,
+ snakes: renderState.snakes.filter((snake: any) => snake.id !== playerId)
+ };
+ this.renderer.render(filteredState, null);
+ } else {
+ this.renderer.render(renderState, playerId);
+ }
+ }
+}
+
+// Initialize the game when the page loads
+new GameClient();
+
+console.log('%c0xSlither Game Started!', 'color: #9B59B6; font-size: 20px; font-weight: bold;');
+console.log('Controls: Move your mouse to control your snake');
+console.log('โ ๏ธ Wallet connection required to play');
+if (STAKE_ARENA_ADDRESS) {
+ console.log('๐ Blockchain features enabled (Native SSS token)');
+} else {
+ console.log('โน๏ธ Set VITE_STAKE_ARENA_ADDRESS to enable blockchain features');
+}
+
diff --git a/entropy/0xSlither/client/src/networkConfig.ts b/entropy/0xSlither/client/src/networkConfig.ts
new file mode 100644
index 0000000..9f902db
--- /dev/null
+++ b/entropy/0xSlither/client/src/networkConfig.ts
@@ -0,0 +1,57 @@
+/**
+ * Network Configuration for 0xSlither
+ * This configuration is used to automatically add the network to MetaMask
+ */
+
+export interface NetworkConfig {
+ chainId: bigint;
+ chainIdHex: string;
+ chainName: string;
+ nativeCurrency: {
+ name: string;
+ symbol: string;
+ decimals: number;
+ };
+ rpcUrls: string[];
+ blockExplorerUrls: string[];
+}
+
+// Chain ID as a number for calculations
+const CHAIN_ID_DECIMAL = 2763767854157000n;
+
+// Saga Chainlet Configuration
+export const NETWORK_CONFIG: NetworkConfig = {
+ chainId: CHAIN_ID_DECIMAL,
+ chainIdHex: '0x' + CHAIN_ID_DECIMAL.toString(16), // Auto-computed: 0x9d1a1d9304cc8
+ chainName: '0xSlither Saga Chainlet',
+ nativeCurrency: {
+ name: 'SSS',
+ symbol: 'SSS',
+ decimals: 18,
+ },
+ rpcUrls: ['https://slither-2763767854157000-1.jsonrpc.sagarpc.io'],
+ blockExplorerUrls: ['https://slither-2763767854157000-1.sagaexplorer.io'],
+};
+
+/**
+ * Get the parameters needed for wallet_addEthereumChain RPC call
+ */
+export function getAddChainParameters() {
+ return {
+ chainId: NETWORK_CONFIG.chainIdHex,
+ chainName: NETWORK_CONFIG.chainName,
+ nativeCurrency: NETWORK_CONFIG.nativeCurrency,
+ rpcUrls: NETWORK_CONFIG.rpcUrls,
+ blockExplorerUrls: NETWORK_CONFIG.blockExplorerUrls,
+ };
+}
+
+/**
+ * Get the parameters needed for wallet_switchEthereumChain RPC call
+ */
+export function getSwitchChainParameters() {
+ return {
+ chainId: NETWORK_CONFIG.chainIdHex,
+ };
+}
+
diff --git a/entropy/0xSlither/client/src/vite-env.d.ts b/entropy/0xSlither/client/src/vite-env.d.ts
new file mode 100644
index 0000000..767b220
--- /dev/null
+++ b/entropy/0xSlither/client/src/vite-env.d.ts
@@ -0,0 +1,10 @@
+///
+
+interface ImportMetaEnv {
+ readonly VITE_STAKE_ARENA_ADDRESS?: string;
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv;
+}
+
diff --git a/entropy/0xSlither/client/tsconfig.json b/entropy/0xSlither/client/tsconfig.json
new file mode 100644
index 0000000..ae4d87e
--- /dev/null
+++ b/entropy/0xSlither/client/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "strict": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noFallthroughCasesInSwitch": true,
+ "baseUrl": ".",
+ "paths": {
+ "@0xslither/shared": ["../shared"]
+ }
+ },
+ "include": ["src", "../shared"]
+}
+
diff --git a/entropy/0xSlither/client/vite.config.ts b/entropy/0xSlither/client/vite.config.ts
new file mode 100644
index 0000000..a325700
--- /dev/null
+++ b/entropy/0xSlither/client/vite.config.ts
@@ -0,0 +1,14 @@
+import { defineConfig } from 'vite';
+import path from 'path';
+
+export default defineConfig({
+ server: {
+ port: 3000,
+ },
+ resolve: {
+ alias: {
+ '@0xslither/shared': path.resolve(__dirname, '../shared/index.ts'),
+ },
+ },
+});
+
diff --git a/entropy/0xSlither/contracts/.gitignore b/entropy/0xSlither/contracts/.gitignore
new file mode 100644
index 0000000..c377ca8
--- /dev/null
+++ b/entropy/0xSlither/contracts/.gitignore
@@ -0,0 +1,10 @@
+node_modules
+coverage
+coverage.json
+typechain
+typechain-types
+
+# Hardhat files
+cache
+artifacts
+
diff --git a/entropy/0xSlither/contracts/README.md b/entropy/0xSlither/contracts/README.md
new file mode 100644
index 0000000..8cc9488
--- /dev/null
+++ b/entropy/0xSlither/contracts/README.md
@@ -0,0 +1,211 @@
+# 0xSlither Smart Contracts
+
+Smart contracts for the 0xSlither game economy on Saga Chainlet using **native SSS token**.
+
+## Contracts
+
+### StakeArena
+
+Main game contract using native SSS for all staking and rewards:
+
+**Features:**
+- **Stake-to-Enter**: Players stake SSS to join matches (payable function)
+- **Loot-on-Eat**: Winners receive 100% of eaten players' stakes
+- **Tap-Out**: Players can voluntarily exit and withdraw their stake
+- **Match Finalization**: Server finalizes matches and updates leaderboards
+- **On-Chain Leaderboard**: Top 10 players by best score
+- **Entropy Commitment**: Placeholder for Pyth Entropy integration
+
+**Why Native Token?**
+- โ
No ERC20 deployment needed
+- โ
No token approval required (faster UX)
+- โ
Single transaction to stake
+- โ
Uses Saga's native SSS directly
+
+## Setup
+
+1. Install dependencies:
+```bash
+pnpm install
+```
+
+2. Create `.env` file:
+```bash
+cp .env.example .env
+# Edit .env with your private key
+```
+
+3. Compile contracts:
+```bash
+pnpm run compile
+```
+
+4. Deploy to Saga Chainlet:
+```bash
+pnpm run deploy
+```
+
+## Environment Variables
+
+```env
+# Deployer wallet private key (with SSS for gas)
+PRIVATE_KEY=0x...
+
+# After deployment, add:
+STAKE_ARENA_ADDRESS=0x...
+SERVER_ADDRESS=0x... # For update-server script
+```
+
+## Saga Chainlet Details
+
+- **RPC**: https://slither-2763767854157000-1.jsonrpc.sagarpc.io
+- **Explorer**: https://slither-2763767854157000-1.sagaexplorer.io
+- **Chain ID**: 2763767854157000
+- **Native Token**: SSS (1000 total supply)
+
+## Deployment
+
+### Quick Deploy
+
+```bash
+# 1. Set your genesis private key
+echo "PRIVATE_KEY=0xYOUR_GENESIS_KEY" > .env
+
+# 2. Deploy
+pnpm run deploy
+
+# Output will show:
+# StakeArena: 0x...
+# Save this address!
+```
+
+### Post-Deployment Steps
+
+1. **Save the StakeArena address** in your `.env` files
+2. **Authorize your server wallet:**
+```bash
+echo "SERVER_ADDRESS=0xYOUR_SERVER_WALLET" >> .env
+pnpm run update-server
+```
+
+3. **Distribute SSS to players:**
+ - Use MetaMask or a script to send SSS from genesis account
+ - Suggested: 50-100 SSS per player for testing
+
+## Scripts
+
+```bash
+pnpm run compile # Compile contracts
+pnpm run deploy # Deploy to Saga
+pnpm run update-server # Update authorized server address
+pnpm run balance # Check SSS balance of address
+pnpm run leaderboard # View on-chain leaderboard
+```
+
+## Contract Functions
+
+### Player Functions
+- `enterMatch(bytes32 matchId) payable` - Stake SSS to enter
+- `tapOut(bytes32 matchId)` - Exit match and withdraw stake
+
+### Server Functions (requires authorization)
+- `reportEat(bytes32 matchId, address eater, address eaten)` - Transfer loot
+- `commitEntropy(bytes32 matchId, bytes32 entropyId)` - Commit entropy seed
+- `finalizeMatch(bytes32 matchId, address[] players, uint256[] scores, address winner)` - Finalize match
+
+### View Functions
+- `getLeaderboard()` - Get top 10 players
+- `bestScore(address player)` - Get player's best score
+- `getStake(bytes32 matchId, address player)` - Get player's current stake
+- `isActive(bytes32 matchId, address player)` - Check if player is active
+- `getMatchSummary(bytes32 matchId)` - Get match details
+
+## Events
+
+- `Entered(bytes32 matchId, address player, uint256 amount)`
+- `EatLoot(bytes32 matchId, address eater, address eaten, uint256 amount, uint256 timestamp)`
+- `TappedOut(bytes32 matchId, address player, uint256 amount)`
+- `EntropyCommitted(bytes32 matchId, bytes32 entropyRequestId)`
+- `MatchFinalized(bytes32 matchId, address winner, uint256 timestamp)`
+- `BestScoreUpdated(address player, uint256 newScore)`
+
+## Token Economics
+
+**Total Supply:** 1000 SSS (fixed, cannot mint more)
+
+## Security
+
+- โ
ReentrancyGuard on all state-changing functions
+- โ
Access control (onlyOwner, onlyAuthorizedServer)
+- โ
Safe native transfers with `call{value}`
+- โ
Input validation
+- โ
No unchecked math (Solidity 0.8.20)
+
+## Future Extensions
+
+The contract is designed to support:
+- NFT cosmetic skins (ERC721)
+- Saga Dollar prize pools
+- MatchManager lifecycle contract
+- Pyth Entropy randomness
+- ROFL enclave verification
+
+## Examples
+
+### Deploy and Configure
+
+```bash
+# Deploy
+pnpm run deploy
+# Output: StakeArena: 0xABC123...
+
+# Check balance
+pnpm run balance 0x027dc86AEFE8aa96353c2aeE9FF06d3BE4ff40Eb
+# Output: Balance: 1000 SSS
+
+# Authorize server
+echo "SERVER_ADDRESS=0xDEF456..." >> .env
+pnpm run update-server
+
+# View leaderboard
+pnpm run leaderboard
+```
+
+### Player Flow (via MetaMask)
+
+1. Connect wallet to Saga Chainlet
+2. Ensure you have SSS (receive from someone)
+3. Call `enterMatch(matchId)` with SSS value
+4. Play the game!
+5. Either win more SSS or tap out to withdraw
+
+### Server Flow
+
+1. Detect kill in game
+2. Call `reportEat(matchId, killerAddress, victimAddress)`
+3. Loot automatically transferred on-chain
+4. At match end, call `finalizeMatch()`
+
+## Troubleshooting
+
+**"Insufficient balance"**
+- Make sure your wallet has SSS
+- Get SSS from the genesis account
+
+**"Only authorized server"**
+- Run `pnpm run update-server` to authorize your server wallet
+- Verify with: Check `authorizedServer` on contract
+
+**"Transfer failed"**
+- Ensure contract has enough SSS for withdrawals
+- Check transaction on Saga Explorer
+
+## Links
+
+- **Saga Docs**: https://docs.saga.xyz
+- **Block Explorer**: https://slither-2763767854157000-1.sagaexplorer.io
+- **Project Repo**: (your repo link)
+
+---
+
+**Built for ETHGlobal Buenos Aires 2025** ๐ฆ๐ท
diff --git a/entropy/0xSlither/contracts/contracts/EntropyOracle.sol b/entropy/0xSlither/contracts/contracts/EntropyOracle.sol
new file mode 100644
index 0000000..09691f3
--- /dev/null
+++ b/entropy/0xSlither/contracts/contracts/EntropyOracle.sol
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import "@openzeppelin/contracts/access/Ownable.sol";
+import "@pythnetwork/entropy-sdk-solidity/IEntropyV2.sol";
+import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";
+
+/**
+ * @title EntropyOracle
+ * @dev Fetches randomness from Pyth Entropy for 0xSlither matches
+ * Deployed on Base Sepolia, server reads seeds cross-chain
+ */
+contract EntropyOracle is IEntropyConsumer, Ownable {
+ IEntropyV2 public entropy;
+ address public entropyProvider;
+ address public authorizedServer;
+
+ // Match ID => Pyth sequence number (request ID)
+ mapping(bytes32 => uint64) public entropyRequestIdByMatch;
+
+ // Match ID => Revealed random seed
+ mapping(bytes32 => bytes32) public entropySeedByMatch;
+
+ // Sequence number => Match ID (for callback routing)
+ mapping(uint64 => bytes32) private sequenceToMatchId;
+
+ // Events
+ event EntropyRequested(bytes32 indexed matchId, uint64 requestId);
+ event EntropyStored(bytes32 indexed matchId, bytes32 seed);
+ event AuthorizedServerUpdated(address indexed newServer);
+
+ error InsufficientFee();
+ error OnlyAuthorizedServer();
+ error EntropyAlreadyRequested();
+ error EntropyNotAvailable();
+
+ modifier onlyAuthorizedServer() {
+ if (msg.sender != authorizedServer) revert OnlyAuthorizedServer();
+ _;
+ }
+
+ /**
+ * @dev Constructor
+ * @param _entropy Address of Pyth Entropy contract on Base Sepolia
+ * @param _authorizedServer Address of the game server wallet
+ */
+ constructor(
+ address _entropy,
+ address _authorizedServer
+ ) Ownable(msg.sender) {
+ require(_entropy != address(0), "Invalid entropy address");
+ require(_authorizedServer != address(0), "Invalid server address");
+
+ entropy = IEntropyV2(_entropy);
+ authorizedServer = _authorizedServer;
+
+ // Use Pyth's default provider
+ entropyProvider = entropy.getDefaultProvider();
+ }
+
+ /**
+ * @dev Request entropy for a new match
+ * @param matchId Unique match identifier (keccak256 hash)
+ */
+ function requestMatchEntropy(bytes32 matchId) external payable onlyAuthorizedServer {
+ require(matchId != bytes32(0), "Invalid match ID");
+
+ // Ensure we haven't already requested entropy for this match
+ if (entropyRequestIdByMatch[matchId] != 0) {
+ revert EntropyAlreadyRequested();
+ }
+
+ // Get the fee required by Pyth Entropy for default provider
+ uint256 fee = entropy.getFeeV2();
+ if (msg.value < fee) {
+ revert InsufficientFee();
+ }
+
+ // Request random number from Pyth Entropy using default provider
+ // The protocol will call entropyCallback when the random number is revealed
+ uint64 sequenceNumber = entropy.requestV2{value: fee}();
+
+ // Store mappings for callback routing
+ entropyRequestIdByMatch[matchId] = sequenceNumber;
+ sequenceToMatchId[sequenceNumber] = matchId;
+
+ emit EntropyRequested(matchId, sequenceNumber);
+
+ // Refund excess payment
+ if (msg.value > fee) {
+ (bool success, ) = payable(msg.sender).call{value: msg.value - fee}("");
+ require(success, "Refund failed");
+ }
+ }
+
+ /**
+ * @dev Callback function called by Pyth Entropy contract
+ * This is called automatically when the random number is revealed
+ * @param sequenceNumber The sequence number of the request
+ * @param randomNumber The revealed random number
+ */
+ function entropyCallback(
+ uint64 sequenceNumber,
+ address, // provider (unused in our case)
+ bytes32 randomNumber
+ ) internal override {
+ // Look up which match this sequence number belongs to
+ bytes32 matchId = sequenceToMatchId[sequenceNumber];
+ require(matchId != bytes32(0), "Unknown sequence number");
+
+ // Store the revealed seed
+ entropySeedByMatch[matchId] = randomNumber;
+
+ emit EntropyStored(matchId, randomNumber);
+ }
+
+ /**
+ * @dev Get the entropy contract address (required by IEntropyConsumer)
+ */
+ function getEntropy() internal view override returns (address) {
+ return address(entropy);
+ }
+
+ /**
+ * @dev Get the revealed seed for a match
+ * @param matchId Match identifier
+ * @return seed The revealed random seed (bytes32(0) if not yet available)
+ */
+ function getMatchSeed(bytes32 matchId) external view returns (bytes32) {
+ return entropySeedByMatch[matchId];
+ }
+
+ /**
+ * @dev Check if entropy has been requested for a match
+ * @param matchId Match identifier
+ * @return True if entropy has been requested
+ */
+ function hasRequestedEntropy(bytes32 matchId) external view returns (bool) {
+ return entropyRequestIdByMatch[matchId] != 0;
+ }
+
+ /**
+ * @dev Check if entropy seed is available for a match
+ * @param matchId Match identifier
+ * @return True if seed is available
+ */
+ function isSeedAvailable(bytes32 matchId) external view returns (bool) {
+ return entropySeedByMatch[matchId] != bytes32(0);
+ }
+
+ /**
+ * @dev Get the current Entropy fee
+ * @return Fee in wei
+ */
+ function getEntropyFee() external view returns (uint256) {
+ return entropy.getFeeV2();
+ }
+
+ /**
+ * @dev Update authorized server address
+ * @param newServer New server address
+ */
+ function updateAuthorizedServer(address newServer) external onlyOwner {
+ require(newServer != address(0), "Invalid address");
+ authorizedServer = newServer;
+ emit AuthorizedServerUpdated(newServer);
+ }
+
+ /**
+ * @dev Allow contract to receive ETH
+ */
+ receive() external payable {}
+
+ /**
+ * @dev Withdraw ETH from contract (for refunds/cleanup)
+ */
+ function withdraw() external onlyOwner {
+ uint256 balance = address(this).balance;
+ (bool success, ) = payable(owner()).call{value: balance}("");
+ require(success, "Withdrawal failed");
+ }
+}
+
diff --git a/entropy/0xSlither/contracts/contracts/GameToken.sol b/entropy/0xSlither/contracts/contracts/GameToken.sol
new file mode 100644
index 0000000..dc588be
--- /dev/null
+++ b/entropy/0xSlither/contracts/contracts/GameToken.sol
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/access/Ownable.sol";
+
+/**
+ * @title GameToken
+ * @dev ERC20 token for 0xSlither game economy
+ */
+contract GameToken is ERC20, Ownable {
+ constructor(uint256 initialSupply) ERC20("Slither", "SSS") Ownable(msg.sender) {
+ _mint(msg.sender, initialSupply);
+ }
+
+ /**
+ * @dev Mint new tokens (only owner)
+ * @param to Address to receive tokens
+ * @param amount Amount to mint
+ */
+ function mint(address to, uint256 amount) external onlyOwner {
+ _mint(to, amount);
+ }
+
+ /**
+ * @dev Override decimals to use 18 (standard)
+ */
+ function decimals() public pure override returns (uint8) {
+ return 18;
+ }
+}
+
diff --git a/entropy/0xSlither/contracts/contracts/StakeArena.sol b/entropy/0xSlither/contracts/contracts/StakeArena.sol
new file mode 100644
index 0000000..943287e
--- /dev/null
+++ b/entropy/0xSlither/contracts/contracts/StakeArena.sol
@@ -0,0 +1,427 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import "@openzeppelin/contracts/access/Ownable.sol";
+import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
+
+/**
+ * @title StakeArena
+ * @dev Main game contract using native SSS token for staking
+ */
+contract StakeArena is Ownable, ReentrancyGuard {
+ address public authorizedServer;
+
+ // Match state
+ struct MatchSummary {
+ uint256 startTime;
+ uint256 endTime;
+ address winner;
+ uint256 totalStaked;
+ bool finalized;
+ }
+
+ // Player state per match
+ mapping(bytes32 => mapping(address => uint256)) public stakeBalance;
+ mapping(bytes32 => mapping(address => bool)) public activeInMatch;
+
+ // Match data
+ mapping(bytes32 => MatchSummary) public matches;
+ mapping(bytes32 => bytes32) public matchEntropyCommit;
+ mapping(bytes32 => bytes32) public entropySeedByMatch; // matchId => keccak256(actualSeed)
+
+ // Leaderboard
+ mapping(address => uint256) public bestScore;
+
+ struct LeaderboardEntry {
+ address player;
+ uint256 score;
+ }
+
+ LeaderboardEntry[] public leaderboard;
+ uint256 public constant MAX_LEADERBOARD_SIZE = 10;
+
+ // Events
+ event DepositedToVault(address indexed player, uint256 amount, uint256 timestamp);
+ event Entered(bytes32 indexed matchId, address indexed player, uint256 amount);
+ event EatReported(
+ bytes32 indexed matchId,
+ address indexed eater,
+ address indexed eaten,
+ uint256 timestamp
+ );
+ event EatLoot(
+ bytes32 indexed matchId,
+ address indexed eater,
+ address indexed eaten,
+ uint256 amountTransferred,
+ uint256 timestamp
+ );
+ event TappedOut(bytes32 indexed matchId, address indexed player, uint256 amountWithdrawn);
+ event SelfDeathReported(bytes32 indexed matchId, address indexed player, uint256 score, uint256 timestamp);
+ event SelfDeath(bytes32 indexed matchId, address indexed player, uint256 amountToServer, uint256 timestamp);
+ event EntropyCommitted(bytes32 indexed matchId, bytes32 entropyRequestId);
+ event MatchFinalized(bytes32 indexed matchId, address indexed winner, uint256 timestamp);
+ event BestScoreUpdated(address indexed player, uint256 newScore);
+ event AuthorizedServerUpdated(address indexed newServer);
+
+ modifier onlyAuthorizedServer() {
+ require(msg.sender == authorizedServer, "Only authorized server");
+ _;
+ }
+
+ constructor(address _authorizedServer) Ownable(msg.sender) {
+ require(_authorizedServer != address(0), "Invalid server address");
+ authorizedServer = _authorizedServer;
+ }
+
+ /**
+ * @dev Player deposits native SSS to server vault for continuous gameplay
+ * This replaces the per-match enterMatch flow for continuous matches
+ */
+ function depositToVault() external payable nonReentrant {
+ require(msg.value > 0, "Amount must be > 0");
+
+ // Transfer directly to server wallet (vault)
+ (bool success, ) = payable(authorizedServer).call{value: msg.value}("");
+ require(success, "Transfer to vault failed");
+
+ emit DepositedToVault(msg.sender, msg.value, block.timestamp);
+ }
+
+ /**
+ * @dev Player stakes native SSS to enter a match
+ * @param matchId Unique match identifier
+ * @notice DEPRECATED: Use depositToVault() for continuous matches
+ */
+ function enterMatch(bytes32 matchId) external payable nonReentrant {
+ require(msg.value > 0, "Amount must be > 0");
+ require(!activeInMatch[matchId][msg.sender], "Already in match");
+ require(!matches[matchId].finalized, "Match already finalized");
+
+ stakeBalance[matchId][msg.sender] = msg.value;
+ activeInMatch[matchId][msg.sender] = true;
+
+ // Initialize match if first entry
+ if (matches[matchId].startTime == 0) {
+ matches[matchId].startTime = block.timestamp;
+ }
+ matches[matchId].totalStaked += msg.value;
+
+ emit Entered(matchId, msg.sender, msg.value);
+ }
+
+ /**
+ * @dev Server reports that one player ate another (stats/leaderboard only)
+ * In continuous vault mode, stake transfers happen off-chain via server wallet
+ * @param matchId Match identifier
+ * @param eater Address of the player who ate
+ * @param eaten Address of the player who was eaten
+ */
+ function reportEat(
+ bytes32 matchId,
+ address eater,
+ address eaten
+ ) external onlyAuthorizedServer {
+ require(eater != eaten, "Cannot eat self");
+
+ // Just emit event for tracking - no on-chain transfers in vault mode
+ emit EatReported(matchId, eater, eaten, block.timestamp);
+ }
+
+ /**
+ * @dev Legacy function for match-based gameplay with on-chain stake transfers
+ * @notice DEPRECATED: Use reportEat() for continuous matches (vault mode)
+ */
+ function reportEatWithTransfer(
+ bytes32 matchId,
+ address eater,
+ address eaten
+ ) external onlyAuthorizedServer nonReentrant {
+ require(activeInMatch[matchId][eater], "Eater not active");
+ require(activeInMatch[matchId][eaten], "Eaten not active");
+ require(eater != eaten, "Cannot eat self");
+
+ uint256 lootAmount = stakeBalance[matchId][eaten];
+ require(lootAmount > 0, "No stake to loot");
+
+ // Transfer 100% of eaten player's stake to eater
+ stakeBalance[matchId][eaten] = 0;
+ stakeBalance[matchId][eater] += lootAmount;
+ activeInMatch[matchId][eaten] = false;
+
+ emit EatLoot(matchId, eater, eaten, lootAmount, block.timestamp);
+ }
+
+ /**
+ * @dev Server reports that a player died from self-inflicted causes
+ * (eating self, wall collision, disconnect, etc.) - updates leaderboard only
+ * In continuous vault mode, stakes are already in server wallet
+ * @param matchId Match identifier
+ * @param player Address of the player who died
+ * @param score Player's final score in the match
+ */
+ function reportSelfDeath(
+ bytes32 matchId,
+ address player,
+ uint256 score
+ ) external onlyAuthorizedServer {
+ // Update best score if this score is higher
+ if (score > bestScore[player]) {
+ bestScore[player] = score;
+ _updateLeaderboard(player, score);
+ emit BestScoreUpdated(player, score);
+ }
+
+ emit SelfDeathReported(matchId, player, score, block.timestamp);
+ }
+
+ /**
+ * @dev Legacy function for match-based gameplay with on-chain stake transfers
+ * @notice DEPRECATED: Use reportSelfDeath() for continuous matches (vault mode)
+ */
+ function reportSelfDeathWithTransfer(
+ bytes32 matchId,
+ address player,
+ uint256 score
+ ) external onlyAuthorizedServer nonReentrant {
+ require(activeInMatch[matchId][player], "Player not active");
+
+ uint256 stakeAmount = stakeBalance[matchId][player];
+ require(stakeAmount > 0, "No stake to collect");
+
+ // Transfer player's stake to server wallet
+ stakeBalance[matchId][player] = 0;
+ activeInMatch[matchId][player] = false;
+
+ // Update best score if this score is higher
+ if (score > bestScore[player]) {
+ bestScore[player] = score;
+ _updateLeaderboard(player, score);
+ emit BestScoreUpdated(player, score);
+ }
+
+ // Send SSS to server wallet
+ (bool success, ) = payable(authorizedServer).call{value: stakeAmount}("");
+ require(success, "Transfer to server failed");
+
+ emit SelfDeath(matchId, player, stakeAmount, block.timestamp);
+ }
+
+ /**
+ * @dev Player voluntarily exits match and withdraws stake
+ * @param matchId Match identifier
+ * @param score Player's final score in the match
+ * @notice DEPRECATED: In vault mode, server handles payouts via direct transfers
+ */
+ function tapOut(bytes32 matchId, uint256 score) external nonReentrant {
+ require(activeInMatch[matchId][msg.sender], "Not active in match");
+ require(!matches[matchId].finalized, "Match finalized");
+
+ uint256 withdrawAmount = stakeBalance[matchId][msg.sender];
+ require(withdrawAmount > 0, "No stake to withdraw");
+
+ stakeBalance[matchId][msg.sender] = 0;
+ activeInMatch[matchId][msg.sender] = false;
+
+ // Update best score if this score is higher
+ if (score > bestScore[msg.sender]) {
+ bestScore[msg.sender] = score;
+ _updateLeaderboard(msg.sender, score);
+ emit BestScoreUpdated(msg.sender, score);
+ }
+
+ // Send SSS back to player
+ (bool success, ) = payable(msg.sender).call{value: withdrawAmount}("");
+ require(success, "Transfer failed");
+
+ emit TappedOut(matchId, msg.sender, withdrawAmount);
+ }
+
+ /**
+ * @dev Server commits entropy seed for match (Pyth integration)
+ * @param matchId Match identifier (can be permanent ID for continuous matches)
+ * @param entropyRequestId Entropy request identifier from Base Sepolia
+ * @param seedHash keccak256 hash of the actual seed for verification
+ */
+ function commitEntropy(bytes32 matchId, bytes32 entropyRequestId, bytes32 seedHash)
+ external
+ onlyAuthorizedServer
+ {
+ require(seedHash != bytes32(0), "Invalid seed hash");
+
+ // Allow re-committing entropy for continuous matches (e.g., on server restart)
+ // In continuous mode, we may update entropy periodically
+
+ matchEntropyCommit[matchId] = entropyRequestId;
+ entropySeedByMatch[matchId] = seedHash;
+ emit EntropyCommitted(matchId, entropyRequestId);
+ }
+
+ /**
+ * @dev Finalize match and update leaderboard
+ * @param matchId Match identifier
+ * @param players Array of player addresses
+ * @param scores Array of final scores (must match players length)
+ * @param winner Address of the winner
+ */
+ function finalizeMatch(
+ bytes32 matchId,
+ address[] calldata players,
+ uint256[] calldata scores,
+ address winner
+ ) external onlyAuthorizedServer nonReentrant {
+ require(!matches[matchId].finalized, "Already finalized");
+ require(players.length == scores.length, "Array length mismatch");
+ require(players.length > 0, "No players");
+
+ matches[matchId].finalized = true;
+ matches[matchId].endTime = block.timestamp;
+ matches[matchId].winner = winner;
+
+ // Update best scores and distribute remaining stakes
+ for (uint256 i = 0; i < players.length; i++) {
+ address player = players[i];
+ uint256 score = scores[i];
+
+ // Update best score
+ if (score > bestScore[player]) {
+ bestScore[player] = score;
+ _updateLeaderboard(player, score);
+ emit BestScoreUpdated(player, score);
+ }
+
+ // Return remaining stake to player
+ uint256 remainingStake = stakeBalance[matchId][player];
+ if (remainingStake > 0) {
+ stakeBalance[matchId][player] = 0;
+ (bool success, ) = payable(player).call{value: remainingStake}("");
+ require(success, "Transfer failed");
+ }
+
+ activeInMatch[matchId][player] = false;
+ }
+
+ emit MatchFinalized(matchId, winner, block.timestamp);
+ }
+
+ /**
+ * @dev Allow contract to receive SSS
+ */
+ receive() external payable {}
+
+ /**
+ * @dev Withdraw contract balance to owner
+ * Allows owner to withdraw accumulated SSS from self-deaths and other sources
+ */
+ function withdrawBalance() external onlyOwner nonReentrant {
+ uint256 balance = address(this).balance;
+ require(balance > 0, "No balance to withdraw");
+
+ (bool success, ) = payable(owner()).call{value: balance}("");
+ require(success, "Transfer failed");
+ }
+
+ /**
+ * @dev Internal function to update leaderboard
+ * @param player Player address
+ * @param score New score
+ */
+ function _updateLeaderboard(address player, uint256 score) internal {
+ // Check if player already on leaderboard
+ int256 existingIndex = -1;
+ for (uint256 i = 0; i < leaderboard.length; i++) {
+ if (leaderboard[i].player == player) {
+ existingIndex = int256(i);
+ break;
+ }
+ }
+
+ // Remove old entry if exists
+ if (existingIndex >= 0) {
+ for (uint256 i = uint256(existingIndex); i < leaderboard.length - 1; i++) {
+ leaderboard[i] = leaderboard[i + 1];
+ }
+ leaderboard.pop();
+ }
+
+ // Find insertion position
+ uint256 insertPos = leaderboard.length;
+ for (uint256 i = 0; i < leaderboard.length; i++) {
+ if (score > leaderboard[i].score) {
+ insertPos = i;
+ break;
+ }
+ }
+
+ // Only insert if in top MAX_LEADERBOARD_SIZE or leaderboard not full
+ if (insertPos < MAX_LEADERBOARD_SIZE || leaderboard.length < MAX_LEADERBOARD_SIZE) {
+ // Insert new entry
+ leaderboard.push(LeaderboardEntry(address(0), 0));
+ for (uint256 i = leaderboard.length - 1; i > insertPos; i--) {
+ leaderboard[i] = leaderboard[i - 1];
+ }
+ leaderboard[insertPos] = LeaderboardEntry(player, score);
+
+ // Trim to MAX_LEADERBOARD_SIZE
+ while (leaderboard.length > MAX_LEADERBOARD_SIZE) {
+ leaderboard.pop();
+ }
+ }
+ }
+
+ /**
+ * @dev Update authorized server address
+ * @param newServer New server address
+ */
+ function updateAuthorizedServer(address newServer) external onlyOwner {
+ require(newServer != address(0), "Invalid address");
+ authorizedServer = newServer;
+ emit AuthorizedServerUpdated(newServer);
+ }
+
+ /**
+ * @dev Get full leaderboard
+ * @return Array of leaderboard entries
+ */
+ function getLeaderboard() external view returns (LeaderboardEntry[] memory) {
+ return leaderboard;
+ }
+
+ /**
+ * @dev Get leaderboard size
+ * @return Current number of entries
+ */
+ function getLeaderboardSize() external view returns (uint256) {
+ return leaderboard.length;
+ }
+
+ /**
+ * @dev Get player's stake in a match
+ * @param matchId Match identifier
+ * @param player Player address
+ * @return Current stake amount
+ */
+ function getStake(bytes32 matchId, address player) external view returns (uint256) {
+ return stakeBalance[matchId][player];
+ }
+
+ /**
+ * @dev Check if player is active in match
+ * @param matchId Match identifier
+ * @param player Player address
+ * @return True if active
+ */
+ function isActive(bytes32 matchId, address player) external view returns (bool) {
+ return activeInMatch[matchId][player];
+ }
+
+ /**
+ * @dev Get match summary
+ * @param matchId Match identifier
+ * @return Match summary struct
+ */
+ function getMatchSummary(bytes32 matchId) external view returns (MatchSummary memory) {
+ return matches[matchId];
+ }
+}
+
diff --git a/entropy/0xSlither/contracts/hardhat.config.ts b/entropy/0xSlither/contracts/hardhat.config.ts
new file mode 100644
index 0000000..126f674
--- /dev/null
+++ b/entropy/0xSlither/contracts/hardhat.config.ts
@@ -0,0 +1,38 @@
+import { HardhatUserConfig } from "hardhat/config";
+import "@nomicfoundation/hardhat-toolbox";
+import * as dotenv from "dotenv";
+
+dotenv.config();
+
+const config: HardhatUserConfig = {
+ solidity: {
+ version: "0.8.20",
+ settings: {
+ optimizer: {
+ enabled: true,
+ runs: 1000,
+ },
+ },
+ },
+ networks: {
+ saga: {
+ url: process.env.SAGA_RPC_URL as string,
+ accounts: [process.env.PRIVATE_KEY as string],
+ chainId: parseInt(process.env.SAGA_CHAIN_ID as string),
+ },
+ baseSepolia: {
+ url: process.env.BASE_SEPOLIA_RPC_URL || "https://sepolia.base.org",
+ accounts: [process.env.PRIVATE_KEY as string],
+ chainId: 84532,
+ },
+ },
+ paths: {
+ sources: "./contracts",
+ tests: "./test",
+ cache: "./cache",
+ artifacts: "./artifacts",
+ },
+};
+
+export default config;
+
diff --git a/entropy/0xSlither/contracts/package.json b/entropy/0xSlither/contracts/package.json
new file mode 100644
index 0000000..bc970ee
--- /dev/null
+++ b/entropy/0xSlither/contracts/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "contracts",
+ "version": "1.0.0",
+ "description": "0xSlither Smart Contracts on Saga Chainlet",
+ "scripts": {
+ "compile": "hardhat compile",
+ "test": "hardhat test",
+ "deploy": "hardhat run scripts/deploy.ts --network saga",
+ "update-server": "hardhat run scripts/updateServer.ts --network saga",
+ "mint": "hardhat run scripts/mintTokens.ts --network saga --",
+ "balance": "hardhat run scripts/checkBalance.ts --network saga --",
+ "leaderboard": "hardhat run scripts/getLeaderboard.ts --network saga"
+ },
+ "devDependencies": {
+ "@nomicfoundation/hardhat-toolbox": "^4.0.0",
+ "dotenv": "^16.6.1",
+ "hardhat": "^2.19.4"
+ },
+ "dependencies": {
+ "@openzeppelin/contracts": "^5.0.1",
+ "@pythnetwork/entropy-sdk-solidity": "^2.2.1",
+ "ethers": "^6.9.0"
+ }
+}
diff --git a/entropy/0xSlither/contracts/scripts/checkBalance.ts b/entropy/0xSlither/contracts/scripts/checkBalance.ts
new file mode 100644
index 0000000..11cbcea
--- /dev/null
+++ b/entropy/0xSlither/contracts/scripts/checkBalance.ts
@@ -0,0 +1,33 @@
+import { ethers } from "hardhat";
+
+async function main() {
+ const gameTokenAddress = process.env.GAME_TOKEN_ADDRESS;
+
+ if (!gameTokenAddress) {
+ console.error("Please set GAME_TOKEN_ADDRESS in .env");
+ process.exit(1);
+ }
+
+ const address = process.argv[2];
+
+ if (!address) {
+ console.error("Usage: npx hardhat run scripts/checkBalance.ts --network saga -- ");
+ process.exit(1);
+ }
+
+ const GameToken = await ethers.getContractAt("GameToken", gameTokenAddress);
+
+ const balance = await GameToken.balanceOf(address);
+ const symbol = await GameToken.symbol();
+
+ console.log("Address:", address);
+ console.log("Balance:", Math.floor(parseFloat(ethers.formatEther(balance))), symbol);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+
diff --git a/entropy/0xSlither/contracts/scripts/deployEntropyOracle.ts b/entropy/0xSlither/contracts/scripts/deployEntropyOracle.ts
new file mode 100644
index 0000000..7f083f3
--- /dev/null
+++ b/entropy/0xSlither/contracts/scripts/deployEntropyOracle.ts
@@ -0,0 +1,118 @@
+import { ethers } from "hardhat";
+import * as dotenv from "dotenv";
+import * as fs from "fs";
+import * as path from "path";
+
+dotenv.config();
+
+/**
+ * Deploy EntropyOracle contract to Base Sepolia
+ * This contract interfaces with Pyth Entropy for cross-chain randomness
+ */
+async function main() {
+ console.log("๐ฒ Deploying EntropyOracle to Base Sepolia...\n");
+
+ // Pyth Entropy V2 contract address on Base Sepolia
+ // Source: https://docs.pyth.network/entropy/contract-addresses
+ // Updated address from Pyth documentation
+ const PYTH_ENTROPY_ADDRESS = "0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c";
+
+ console.log("๐ Using Pyth Entropy at:", PYTH_ENTROPY_ADDRESS);
+ console.log("๐ Network: Base Sepolia (Chain ID: 84532)");
+ console.log();
+
+ const [deployer] = await ethers.getSigners();
+ console.log("๐ Deployer address:", deployer.address);
+
+ // Get deployer balance
+ const balance = await ethers.provider.getBalance(deployer.address);
+ console.log("๐ฐ Deployer balance:", ethers.formatEther(balance), "ETH\n");
+
+ if (balance === 0n) {
+ console.error("โ Deployer has no ETH balance");
+ process.exit(1);
+ }
+
+ // Get authorized server address
+ const authorizedServer = process.env.SERVER_WALLET_ADDRESS || deployer.address;
+ console.log("๐ Authorized server:", authorizedServer);
+ console.log("๐ฒ Pyth Entropy contract:", PYTH_ENTROPY_ADDRESS);
+ console.log();
+
+ // Deploy EntropyOracle
+ console.log("Deploying EntropyOracle...");
+ const EntropyOracle = await ethers.getContractFactory("EntropyOracle");
+ const entropyOracle = await EntropyOracle.deploy(
+ PYTH_ENTROPY_ADDRESS,
+ authorizedServer
+ );
+
+ await entropyOracle.waitForDeployment();
+ const entropyOracleAddress = await entropyOracle.getAddress();
+
+ console.log("โ
EntropyOracle deployed to:", entropyOracleAddress);
+ console.log();
+
+ // Get entropy provider info (with error handling)
+ try {
+ const entropyProvider = await entropyOracle.entropyProvider();
+ console.log("๐ฒ Entropy provider:", entropyProvider);
+
+ // Get current entropy fee
+ const fee = await entropyOracle.getEntropyFee();
+ console.log("๐ฐ Current entropy fee:", ethers.formatEther(fee), "ETH");
+ } catch (error) {
+ console.warn("โ ๏ธ Could not read entropy provider (contract may need to initialize)");
+ console.warn("This is normal if the Pyth Entropy contract needs setup");
+ }
+ console.log();
+
+ // Save deployment info
+ const deploymentInfo = {
+ network: "baseSepolia",
+ chainId: 84532,
+ entropyOracle: entropyOracleAddress,
+ pythEntropy: PYTH_ENTROPY_ADDRESS,
+ authorizedServer: authorizedServer,
+ entropyProvider: entropyProvider,
+ deployedAt: new Date().toISOString(),
+ deployer: deployer.address,
+ };
+
+ const deploymentsDir = path.join(__dirname, "../deployments");
+ if (!fs.existsSync(deploymentsDir)) {
+ fs.mkdirSync(deploymentsDir, { recursive: true });
+ }
+
+ const deploymentPath = path.join(deploymentsDir, "entropyOracle-baseSepolia.json");
+ fs.writeFileSync(deploymentPath, JSON.stringify(deploymentInfo, null, 2));
+ console.log("๐ Deployment info saved to:", deploymentPath);
+
+ // Print environment variable update instructions
+ console.log("\nโ
Deployment complete!");
+ console.log("\n๐ Add these to your .env file:");
+ console.log("โ".repeat(60));
+ console.log(`BASE_SEPOLIA_RPC_URL=https://sepolia.base.org`);
+ console.log(`BASE_SEPOLIA_CHAIN_ID=84532`);
+ console.log(`PYTH_ENTROPY_ADDRESS=${PYTH_ENTROPY_ADDRESS}`);
+ console.log(`ENTROPY_ORACLE_ADDRESS=${entropyOracleAddress}`);
+ console.log(`SERVER_WALLET_ADDRESS=${authorizedServer}`);
+ console.log("โ".repeat(60));
+
+ console.log("\n๐ View on Base Sepolia Explorer:");
+ console.log(`https://sepolia.basescan.org/address/${entropyOracleAddress}`);
+
+ console.log("\nโ ๏ธ IMPORTANT:");
+ console.log("1. Fund the server wallet with ETH for entropy requests");
+ console.log("2. Each entropy request costs approximately:", ethers.formatEther(fee), "ETH");
+ console.log("3. Update your server's .env with the above values");
+ console.log("4. Verify the contract on BaseScan for transparency");
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+
diff --git a/entropy/0xSlither/contracts/scripts/deployStakeArena.ts b/entropy/0xSlither/contracts/scripts/deployStakeArena.ts
new file mode 100644
index 0000000..f30843b
--- /dev/null
+++ b/entropy/0xSlither/contracts/scripts/deployStakeArena.ts
@@ -0,0 +1,47 @@
+import { ethers } from "hardhat";
+
+async function main() {
+ console.log("Deploying 0xSlither contracts to Saga Chainlet...");
+ console.log("Using native SSS token for game economy\n");
+
+ const [deployer] = await ethers.getSigners();
+ console.log("Deploying with account:", deployer.address);
+
+ const balance = await ethers.provider.getBalance(deployer.address);
+ console.log("Account balance:", Math.floor(parseFloat(ethers.formatEther(balance))), "SSS");
+
+ // Deploy StakeArena (no GameToken needed!)
+ console.log("\n1. Deploying StakeArena...");
+ const StakeArena = await ethers.getContractFactory("StakeArena");
+ const stakeArena = await StakeArena.deploy(deployer.address);
+ await stakeArena.waitForDeployment();
+ const stakeArenaAddress = await stakeArena.getAddress();
+ console.log("StakeArena deployed to:", stakeArenaAddress);
+
+ // Verify deployment
+ console.log("\n2. Verifying deployment...");
+ const authorizedServer = await stakeArena.authorizedServer();
+ console.log(`StakeArena authorized server: ${authorizedServer}`);
+
+ // Save deployment addresses
+ console.log("\n3. Deployment Summary:");
+ console.log("=======================");
+ console.log(`StakeArena: ${stakeArenaAddress}`);
+ console.log(`Deployer: ${deployer.address}`);
+ console.log(`Token: Native SSS`);
+ console.log(`Total SSS in circulation: 1000 SSS`);
+ console.log("\nSave this address in your .env files:");
+ console.log(`STAKE_ARENA_ADDRESS=${stakeArenaAddress}`);
+ console.log("\nNote: Players will stake native SSS directly (no token approval needed!)");
+ console.log("\nUpdate authorizedServer by calling:");
+ console.log("stakeArena.updateAuthorizedServer()");
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+
+
diff --git a/entropy/0xSlither/contracts/scripts/getLeaderboard.ts b/entropy/0xSlither/contracts/scripts/getLeaderboard.ts
new file mode 100644
index 0000000..26dc14c
--- /dev/null
+++ b/entropy/0xSlither/contracts/scripts/getLeaderboard.ts
@@ -0,0 +1,36 @@
+import { ethers } from "hardhat";
+
+async function main() {
+ const stakeArenaAddress = process.env.STAKE_ARENA_ADDRESS;
+
+ if (!stakeArenaAddress) {
+ console.error("Please set STAKE_ARENA_ADDRESS in .env");
+ process.exit(1);
+ }
+
+ console.log("Fetching on-chain leaderboard...");
+ console.log("StakeArena:", stakeArenaAddress);
+
+ const StakeArena = await ethers.getContractAt("StakeArena", stakeArenaAddress);
+ const leaderboard = await StakeArena.getLeaderboard();
+
+ console.log("\n๐ On-Chain Leaderboard:");
+ console.log("========================");
+
+ if (leaderboard.length === 0) {
+ console.log("No entries yet");
+ } else {
+ leaderboard.forEach((entry: any, index: number) => {
+ const medal = index === 0 ? "๐ฅ" : index === 1 ? "๐ฅ" : index === 2 ? "๐ฅ" : " ";
+ console.log(`${medal} ${index + 1}. ${entry.player} - Score: ${entry.score}`);
+ });
+ }
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+
diff --git a/entropy/0xSlither/contracts/scripts/mintTokens.ts b/entropy/0xSlither/contracts/scripts/mintTokens.ts
new file mode 100644
index 0000000..87fba24
--- /dev/null
+++ b/entropy/0xSlither/contracts/scripts/mintTokens.ts
@@ -0,0 +1,45 @@
+import { ethers } from "hardhat";
+
+async function main() {
+ const gameTokenAddress = process.env.GAME_TOKEN_ADDRESS;
+
+ if (!gameTokenAddress) {
+ console.error("Please set GAME_TOKEN_ADDRESS in .env");
+ process.exit(1);
+ }
+
+ // Get recipient from command line or use default
+ const recipientAddress = process.argv[2];
+ const amount = process.argv[3] || "1000";
+
+ if (!recipientAddress) {
+ console.error("Usage: npx hardhat run scripts/mintTokens.ts --network saga -- [amount]");
+ process.exit(1);
+ }
+
+ console.log("Minting tokens...");
+ console.log("GameToken:", gameTokenAddress);
+ console.log("Recipient:", recipientAddress);
+ console.log("Amount:", amount, "SSS");
+
+ const GameToken = await ethers.getContractAt("GameToken", gameTokenAddress);
+ const amountWei = ethers.parseEther(amount);
+
+ const tx = await GameToken.mint(recipientAddress, amountWei);
+ console.log("Transaction submitted:", tx.hash);
+
+ await tx.wait();
+ console.log("โ
Tokens minted successfully!");
+
+ // Verify balance
+ const balance = await GameToken.balanceOf(recipientAddress);
+ console.log("New balance:", Math.floor(parseFloat(ethers.formatEther(balance))), "SSS");
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+
diff --git a/entropy/0xSlither/contracts/scripts/updateServer.ts b/entropy/0xSlither/contracts/scripts/updateServer.ts
new file mode 100644
index 0000000..cce1eef
--- /dev/null
+++ b/entropy/0xSlither/contracts/scripts/updateServer.ts
@@ -0,0 +1,50 @@
+import { ethers } from "hardhat";
+
+async function main() {
+ const stakeArenaAddress = process.env.STAKE_ARENA_ADDRESS;
+ const serverAddress = process.env.SERVER_ADDRESS;
+
+ if (!stakeArenaAddress || !serverAddress) {
+ console.error("Please set STAKE_ARENA_ADDRESS and SERVER_ADDRESS in .env");
+ process.exit(1);
+ }
+
+ console.log("Updating authorized server...");
+ console.log("StakeArena:", stakeArenaAddress);
+ console.log("New Server:", serverAddress);
+
+ const StakeArena = await ethers.getContractAt("StakeArena", stakeArenaAddress);
+
+ const tx = await StakeArena.updateAuthorizedServer(serverAddress);
+ console.log("Transaction submitted:", tx.hash);
+
+ const receipt = await tx.wait();
+ console.log("โ
Transaction confirmed in block:", receipt?.blockNumber);
+ console.log("โ
Authorized server updated successfully!");
+
+ // Verify the update
+ try {
+ // Small delay to ensure state is updated
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ const currentServer = await StakeArena.authorizedServer();
+ console.log("Current authorized server:", currentServer);
+
+ if (currentServer.toLowerCase() === serverAddress.toLowerCase()) {
+ console.log("โ Verification successful - server address matches!");
+ } else {
+ console.warn("โ Warning: Server address doesn't match expected value");
+ }
+ } catch (error: any) {
+ console.warn("โ Could not verify server address (this is okay if the transaction succeeded)");
+ console.warn("Error:", error.message);
+ }
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+
diff --git a/entropy/0xSlither/contracts/scripts/withdrawContractTokens.ts b/entropy/0xSlither/contracts/scripts/withdrawContractTokens.ts
new file mode 100644
index 0000000..4469cee
--- /dev/null
+++ b/entropy/0xSlither/contracts/scripts/withdrawContractTokens.ts
@@ -0,0 +1,89 @@
+import { ethers } from "hardhat";
+
+/**
+ * Script to withdraw SSS tokens from the GameToken contract to the server wallet
+ * This retrieves any SSS tokens that are held by the GameToken contract itself
+ */
+async function main() {
+ const gameTokenAddress = process.env.GAME_TOKEN_ADDRESS;
+
+ if (!gameTokenAddress) {
+ console.error("Please set GAME_TOKEN_ADDRESS in .env");
+ process.exit(1);
+ }
+
+ // Get the signer (server wallet)
+ const signers = await ethers.getSigners();
+ const signer = signers[0];
+ const serverWallet = signer.address;
+
+ console.log("=".repeat(60));
+ console.log("Withdrawing SSS tokens from GameToken contract");
+ console.log("=".repeat(60));
+ console.log("GameToken Contract:", gameTokenAddress);
+ console.log("Server Wallet:", serverWallet);
+ console.log("");
+
+ // Connect to the GameToken contract
+ const GameToken = await ethers.getContractAt("GameToken", gameTokenAddress);
+
+ // Check the balance of SSS tokens held by the contract itself
+ const contractBalance = await GameToken.balanceOf(gameTokenAddress);
+ const contractBalanceFormatted = ethers.formatEther(contractBalance);
+
+ console.log(`Contract SSS Balance: ${contractBalanceFormatted} SSS`);
+ console.log("");
+
+ if (contractBalance === 0n) {
+ console.log("โ ๏ธ No SSS tokens to withdraw from the contract");
+ console.log("=".repeat(60));
+ return;
+ }
+
+ // Transfer all SSS tokens from the contract to the server wallet
+ console.log(`Transferring ${contractBalanceFormatted} SSS to server wallet...`);
+
+ try {
+ // Use transfer function to send tokens from contract to server wallet
+ // Note: This requires the server wallet to be the owner of the contract
+ // or have appropriate permissions
+ const tx = await GameToken.transfer(serverWallet, contractBalance);
+ console.log("Transaction submitted:", tx.hash);
+
+ await tx.wait();
+ console.log("โ
Transfer successful!");
+ console.log("");
+
+ // Verify balances after transfer
+ const newContractBalance = await GameToken.balanceOf(gameTokenAddress);
+ const serverBalance = await GameToken.balanceOf(serverWallet);
+
+ console.log("=".repeat(60));
+ console.log("Final Balances:");
+ console.log("=".repeat(60));
+ console.log(`Contract Balance: ${ethers.formatEther(newContractBalance)} SSS`);
+ console.log(`Server Wallet Balance: ${ethers.formatEther(serverBalance)} SSS`);
+ console.log("=".repeat(60));
+ } catch (error: any) {
+ console.error("โ Transfer failed:", error.message);
+
+ // Provide helpful error messages
+ if (error.message.includes("insufficient balance")) {
+ console.log("\nโ ๏ธ The contract doesn't have enough SSS tokens");
+ } else if (error.message.includes("transfer amount exceeds balance")) {
+ console.log("\nโ ๏ธ Transfer amount exceeds the contract's balance");
+ } else {
+ console.log("\n๐ก Make sure the server wallet has permission to withdraw from the contract");
+ }
+
+ process.exit(1);
+ }
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+
diff --git a/entropy/0xSlither/contracts/scripts/withdrawNativeSSS.ts b/entropy/0xSlither/contracts/scripts/withdrawNativeSSS.ts
new file mode 100644
index 0000000..4c92d6e
--- /dev/null
+++ b/entropy/0xSlither/contracts/scripts/withdrawNativeSSS.ts
@@ -0,0 +1,157 @@
+import { ethers } from "hardhat";
+
+/**
+ * Script to withdraw native SSS tokens from a contract to the server wallet
+ * This retrieves any native SSS (chain native token) that is held by a contract
+ * Use this for contracts like StakeArena that hold native SSS
+ */
+async function main() {
+ // Get contract address from command line or environment variable
+ const contractAddress = process.argv[2] || process.env.STAKE_ARENA_ADDRESS;
+
+ if (!contractAddress) {
+ console.error("Usage: npx hardhat run scripts/withdrawNativeSSS.ts --network saga -- ");
+ console.error("Or set STAKE_ARENA_ADDRESS in .env");
+ process.exit(1);
+ }
+
+ // Validate address
+ if (!ethers.isAddress(contractAddress)) {
+ console.error("Invalid contract address:", contractAddress);
+ process.exit(1);
+ }
+
+ // Get the signer (server wallet)
+ const signers = await ethers.getSigners();
+ const signer = signers[0];
+ const serverWallet = signer.address;
+
+ console.log("=".repeat(60));
+ console.log("Withdrawing Native SSS from Contract");
+ console.log("=".repeat(60));
+ console.log("Contract Address:", contractAddress);
+ console.log("Server Wallet:", serverWallet);
+ console.log("");
+
+ // Check the native SSS balance of the contract
+ const provider = ethers.provider;
+ const contractBalance = await provider.getBalance(contractAddress);
+ const contractBalanceFormatted = ethers.formatEther(contractBalance);
+
+ console.log(`Contract Native SSS Balance: ${contractBalanceFormatted} SSS`);
+ console.log("");
+
+ if (contractBalance === 0n) {
+ console.log("โ ๏ธ No native SSS tokens to withdraw from the contract");
+ console.log("=".repeat(60));
+ return;
+ }
+
+ // Get server wallet balance before
+ const serverBalanceBefore = await provider.getBalance(serverWallet);
+ console.log(`Server Wallet Balance (before): ${ethers.formatEther(serverBalanceBefore)} SSS`);
+ console.log("");
+
+ // To withdraw native tokens, we need to call a withdrawal function on the contract
+ // For StakeArena, there's no explicit withdrawal function, but the owner can receive funds
+ // Let's try to send a transaction that triggers the receive() function
+
+ console.log(`Attempting to withdraw ${contractBalanceFormatted} SSS...`);
+ console.log("");
+
+ try {
+ // Method 1: If contract has a withdraw function (you may need to add this to your contract)
+ // For now, we'll just show the balance and provide instructions
+
+ console.log("โ ๏ธ IMPORTANT: Contract Withdrawal Method Needed");
+ console.log("=".repeat(60));
+ console.log("");
+ console.log("The contract holds native SSS but doesn't have a withdrawal function.");
+ console.log("To withdraw these funds, you have two options:");
+ console.log("");
+ console.log("Option 1: Add a withdrawal function to your contract");
+ console.log("Add this function to StakeArena.sol:");
+ console.log("");
+ console.log(" function withdrawBalance() external onlyOwner {");
+ console.log(" uint256 balance = address(this).balance;");
+ console.log(" require(balance > 0, \"No balance to withdraw\");");
+ console.log(" (bool success, ) = payable(owner()).call{value: balance}(\"\");");
+ console.log(" require(success, \"Transfer failed\");");
+ console.log(" }");
+ console.log("");
+ console.log("Option 2: Use low-level call (attempting now...)");
+ console.log("");
+
+ // Method 2: Try to use a low-level call to trigger receive() or fallback()
+ // This will only work if the contract has logic to send funds back
+
+ // Create a simple contract interface with owner() function
+ const contractWithOwner = new ethers.Contract(
+ contractAddress,
+ [
+ "function owner() view returns (address)",
+ "function withdrawBalance() external"
+ ],
+ signer
+ );
+
+ // Check if we're the owner
+ try {
+ const owner = await contractWithOwner.owner();
+ console.log(`Contract Owner: ${owner}`);
+
+ if (owner.toLowerCase() !== serverWallet.toLowerCase()) {
+ console.log("โ You are not the contract owner!");
+ console.log(" Only the owner can withdraw funds.");
+ process.exit(1);
+ }
+ } catch (error) {
+ console.log("โ ๏ธ Could not verify ownership - proceeding anyway...");
+ }
+
+ // Try to call withdrawBalance function if it exists
+ console.log("Attempting to call withdrawBalance()...");
+ const tx = await contractWithOwner.withdrawBalance();
+ console.log("Transaction submitted:", tx.hash);
+
+ await tx.wait();
+ console.log("โ
Withdrawal successful!");
+ console.log("");
+
+ // Verify balances after withdrawal
+ const newContractBalance = await provider.getBalance(contractAddress);
+ const serverBalanceAfter = await provider.getBalance(serverWallet);
+
+ console.log("=".repeat(60));
+ console.log("Final Balances:");
+ console.log("=".repeat(60));
+ console.log(`Contract Balance: ${ethers.formatEther(newContractBalance)} SSS`);
+ console.log(`Server Wallet Balance: ${ethers.formatEther(serverBalanceAfter)} SSS`);
+ console.log(`Amount Withdrawn: ${ethers.formatEther(serverBalanceAfter - serverBalanceBefore)} SSS`);
+ console.log("=".repeat(60));
+
+ } catch (error: any) {
+ console.log("");
+ console.log("โ Withdrawal function not found or failed:", error.message);
+ console.log("");
+ console.log("=".repeat(60));
+ console.log("Next Steps:");
+ console.log("=".repeat(60));
+ console.log("1. Add a withdrawBalance() function to your contract (see above)");
+ console.log("2. Redeploy the contract or upgrade it if using a proxy pattern");
+ console.log("3. Run this script again");
+ console.log("");
+ console.log("Current Holdings:");
+ console.log(` Contract: ${contractBalanceFormatted} SSS (locked)`);
+ console.log(` Server Wallet: ${ethers.formatEther(serverBalanceBefore)} SSS`);
+ console.log("=".repeat(60));
+ }
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+
diff --git a/entropy/0xSlither/contracts/tsconfig.json b/entropy/0xSlither/contracts/tsconfig.json
new file mode 100644
index 0000000..8216e27
--- /dev/null
+++ b/entropy/0xSlither/contracts/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "commonjs",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "resolveJsonModule": true
+ }
+}
+
diff --git a/entropy/0xSlither/logo.svg b/entropy/0xSlither/logo.svg
new file mode 100644
index 0000000..8d5f1f3
--- /dev/null
+++ b/entropy/0xSlither/logo.svg
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0x
+
+
+
+
+ SLITHER
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/entropy/0xSlither/package.json b/entropy/0xSlither/package.json
new file mode 100644
index 0000000..32c02ed
--- /dev/null
+++ b/entropy/0xSlither/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "slither-core",
+ "version": "1.0.0",
+ "private": true,
+ "workspaces": [
+ "server",
+ "client",
+ "shared"
+ ],
+ "scripts": {
+ "build:shared": "pnpm --filter shared build",
+ "build:server": "pnpm --filter server build",
+ "build:client": "pnpm --filter client build",
+ "build": "pnpm build:shared && pnpm build:server && pnpm build:client",
+ "server": "pnpm --filter server dev",
+ "client": "pnpm --filter client dev",
+ "dev": "pnpm --filter server dev & pnpm --filter client dev"
+ }
+}
+
diff --git a/entropy/0xSlither/pnpm-lock.yaml b/entropy/0xSlither/pnpm-lock.yaml
new file mode 100644
index 0000000..134d4d7
--- /dev/null
+++ b/entropy/0xSlither/pnpm-lock.yaml
@@ -0,0 +1,7360 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .: {}
+
+ client:
+ dependencies:
+ '@0xslither/shared':
+ specifier: workspace:*
+ version: link:../shared
+ ethers:
+ specifier: ^6.9.0
+ version: 6.15.0
+ devDependencies:
+ '@types/node':
+ specifier: ^22.0.0
+ version: 22.19.1
+ typescript:
+ specifier: ^5.5.4
+ version: 5.9.3
+ vite:
+ specifier: ^5.3.5
+ version: 5.4.21(@types/node@22.19.1)
+
+ contracts:
+ dependencies:
+ '@openzeppelin/contracts':
+ specifier: ^5.0.1
+ version: 5.4.0
+ '@pythnetwork/entropy-sdk-solidity':
+ specifier: ^2.2.1
+ version: 2.2.1
+ ethers:
+ specifier: ^6.9.0
+ version: 6.15.0
+ devDependencies:
+ '@nomicfoundation/hardhat-toolbox':
+ specifier: ^4.0.0
+ version: 4.0.0(2e5368d32e9c0f5e1ee8a2b58e01fa08)
+ dotenv:
+ specifier: ^16.6.1
+ version: 16.6.1
+ hardhat:
+ specifier: ^2.19.4
+ version: 2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)
+
+ server:
+ dependencies:
+ '@0xslither/shared':
+ specifier: workspace:*
+ version: link:../shared
+ dotenv:
+ specifier: ^16.3.1
+ version: 16.6.1
+ ethers:
+ specifier: ^6.9.0
+ version: 6.15.0
+ ws:
+ specifier: ^8.18.0
+ version: 8.18.3
+ devDependencies:
+ '@types/jest':
+ specifier: ^29.5.12
+ version: 29.5.14
+ '@types/node':
+ specifier: ^22.0.0
+ version: 22.19.1
+ '@types/ws':
+ specifier: ^8.5.12
+ version: 8.18.1
+ jest:
+ specifier: ^29.7.0
+ version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
+ ts-jest:
+ specifier: ^29.1.2
+ version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3)
+ tsx:
+ specifier: ^4.16.2
+ version: 4.20.6
+ typescript:
+ specifier: ^5.5.4
+ version: 5.9.3
+
+ shared:
+ devDependencies:
+ typescript:
+ specifier: ^5.5.4
+ version: 5.9.3
+
+packages:
+
+ '@adraffy/ens-normalize@1.10.1':
+ resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==}
+
+ '@babel/code-frame@7.27.1':
+ resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.28.5':
+ resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.28.5':
+ resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.28.5':
+ resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.27.2':
+ resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.27.1':
+ resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.3':
+ resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.27.1':
+ resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.4':
+ resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.28.5':
+ resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-syntax-async-generators@7.8.4':
+ resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-bigint@7.8.3':
+ resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-class-properties@7.12.13':
+ resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-class-static-block@7.14.5':
+ resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-attributes@7.27.1':
+ resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-meta@7.10.4':
+ resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-json-strings@7.8.3':
+ resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-jsx@7.27.1':
+ resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-logical-assignment-operators@7.10.4':
+ resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3':
+ resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-numeric-separator@7.10.4':
+ resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-object-rest-spread@7.8.3':
+ resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-optional-catch-binding@7.8.3':
+ resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-optional-chaining@7.8.3':
+ resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-private-property-in-object@7.14.5':
+ resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-top-level-await@7.14.5':
+ resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-typescript@7.27.1':
+ resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/template@7.27.2':
+ resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.28.5':
+ resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.28.5':
+ resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
+ engines: {node: '>=6.9.0'}
+
+ '@bcoe/v8-coverage@0.2.3':
+ resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
+
+ '@cspotcode/source-map-support@0.8.1':
+ resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
+ engines: {node: '>=12'}
+
+ '@esbuild/aix-ppc64@0.21.5':
+ resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/aix-ppc64@0.25.12':
+ resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.21.5':
+ resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm64@0.25.12':
+ resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.21.5':
+ resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.12':
+ resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.21.5':
+ resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.12':
+ resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.21.5':
+ resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-arm64@0.25.12':
+ resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.21.5':
+ resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.12':
+ resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-arm64@0.25.12':
+ resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.21.5':
+ resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.12':
+ resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.21.5':
+ resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm64@0.25.12':
+ resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.21.5':
+ resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.12':
+ resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.21.5':
+ resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.12':
+ resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.21.5':
+ resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.12':
+ resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.21.5':
+ resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.12':
+ resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.21.5':
+ resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.12':
+ resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.21.5':
+ resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.12':
+ resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.21.5':
+ resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.12':
+ resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.21.5':
+ resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.12':
+ resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.12':
+ resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.21.5':
+ resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.12':
+ resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.12':
+ resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.21.5':
+ resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.12':
+ resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.25.12':
+ resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.21.5':
+ resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/sunos-x64@0.25.12':
+ resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.21.5':
+ resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-arm64@0.25.12':
+ resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.21.5':
+ resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.12':
+ resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.21.5':
+ resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.12':
+ resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@ethereumjs/rlp@4.0.1':
+ resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ '@ethereumjs/rlp@5.0.2':
+ resolution: {integrity: sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ '@ethereumjs/util@8.1.0':
+ resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==}
+ engines: {node: '>=14'}
+
+ '@ethereumjs/util@9.1.0':
+ resolution: {integrity: sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==}
+ engines: {node: '>=18'}
+
+ '@ethersproject/abi@5.8.0':
+ resolution: {integrity: sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==}
+
+ '@ethersproject/abstract-provider@5.8.0':
+ resolution: {integrity: sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==}
+
+ '@ethersproject/abstract-signer@5.8.0':
+ resolution: {integrity: sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==}
+
+ '@ethersproject/address@5.8.0':
+ resolution: {integrity: sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==}
+
+ '@ethersproject/base64@5.8.0':
+ resolution: {integrity: sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==}
+
+ '@ethersproject/basex@5.8.0':
+ resolution: {integrity: sha512-PIgTszMlDRmNwW9nhS6iqtVfdTAKosA7llYXNmGPw4YAI1PUyMv28988wAb41/gHF/WqGdoLv0erHaRcHRKW2Q==}
+
+ '@ethersproject/bignumber@5.8.0':
+ resolution: {integrity: sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==}
+
+ '@ethersproject/bytes@5.8.0':
+ resolution: {integrity: sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==}
+
+ '@ethersproject/constants@5.8.0':
+ resolution: {integrity: sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==}
+
+ '@ethersproject/contracts@5.8.0':
+ resolution: {integrity: sha512-0eFjGz9GtuAi6MZwhb4uvUM216F38xiuR0yYCjKJpNfSEy4HUM8hvqqBj9Jmm0IUz8l0xKEhWwLIhPgxNY0yvQ==}
+
+ '@ethersproject/hash@5.8.0':
+ resolution: {integrity: sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==}
+
+ '@ethersproject/hdnode@5.8.0':
+ resolution: {integrity: sha512-4bK1VF6E83/3/Im0ERnnUeWOY3P1BZml4ZD3wcH8Ys0/d1h1xaFt6Zc+Dh9zXf9TapGro0T4wvO71UTCp3/uoA==}
+
+ '@ethersproject/json-wallets@5.8.0':
+ resolution: {integrity: sha512-HxblNck8FVUtNxS3VTEYJAcwiKYsBIF77W15HufqlBF9gGfhmYOJtYZp8fSDZtn9y5EaXTE87zDwzxRoTFk11w==}
+
+ '@ethersproject/keccak256@5.8.0':
+ resolution: {integrity: sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==}
+
+ '@ethersproject/logger@5.8.0':
+ resolution: {integrity: sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==}
+
+ '@ethersproject/networks@5.8.0':
+ resolution: {integrity: sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==}
+
+ '@ethersproject/pbkdf2@5.8.0':
+ resolution: {integrity: sha512-wuHiv97BrzCmfEaPbUFpMjlVg/IDkZThp9Ri88BpjRleg4iePJaj2SW8AIyE8cXn5V1tuAaMj6lzvsGJkGWskg==}
+
+ '@ethersproject/properties@5.8.0':
+ resolution: {integrity: sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==}
+
+ '@ethersproject/providers@5.8.0':
+ resolution: {integrity: sha512-3Il3oTzEx3o6kzcg9ZzbE+oCZYyY+3Zh83sKkn4s1DZfTUjIegHnN2Cm0kbn9YFy45FDVcuCLLONhU7ny0SsCw==}
+
+ '@ethersproject/random@5.8.0':
+ resolution: {integrity: sha512-E4I5TDl7SVqyg4/kkA/qTfuLWAQGXmSOgYyO01So8hLfwgKvYK5snIlzxJMk72IFdG/7oh8yuSqY2KX7MMwg+A==}
+
+ '@ethersproject/rlp@5.8.0':
+ resolution: {integrity: sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==}
+
+ '@ethersproject/sha2@5.8.0':
+ resolution: {integrity: sha512-dDOUrXr9wF/YFltgTBYS0tKslPEKr6AekjqDW2dbn1L1xmjGR+9GiKu4ajxovnrDbwxAKdHjW8jNcwfz8PAz4A==}
+
+ '@ethersproject/signing-key@5.8.0':
+ resolution: {integrity: sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==}
+
+ '@ethersproject/solidity@5.8.0':
+ resolution: {integrity: sha512-4CxFeCgmIWamOHwYN9d+QWGxye9qQLilpgTU0XhYs1OahkclF+ewO+3V1U0mvpiuQxm5EHHmv8f7ClVII8EHsA==}
+
+ '@ethersproject/strings@5.8.0':
+ resolution: {integrity: sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==}
+
+ '@ethersproject/transactions@5.8.0':
+ resolution: {integrity: sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==}
+
+ '@ethersproject/units@5.8.0':
+ resolution: {integrity: sha512-lxq0CAnc5kMGIiWW4Mr041VT8IhNM+Pn5T3haO74XZWFulk7wH1Gv64HqE96hT4a7iiNMdOCFEBgaxWuk8ETKQ==}
+
+ '@ethersproject/wallet@5.8.0':
+ resolution: {integrity: sha512-G+jnzmgg6UxurVKRKvw27h0kvG75YKXZKdlLYmAHeF32TGUzHkOFd7Zn6QHOTYRFWnfjtSSFjBowKo7vfrXzPA==}
+
+ '@ethersproject/web@5.8.0':
+ resolution: {integrity: sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==}
+
+ '@ethersproject/wordlists@5.8.0':
+ resolution: {integrity: sha512-2df9bbXicZws2Sb5S6ET493uJ0Z84Fjr3pC4tu/qlnZERibZCeUVuqdtt+7Tv9xxhUxHoIekIA7avrKUWHrezg==}
+
+ '@fastify/busboy@2.1.1':
+ resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
+ engines: {node: '>=14'}
+
+ '@isaacs/balanced-match@4.0.1':
+ resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
+ engines: {node: 20 || >=22}
+
+ '@isaacs/brace-expansion@5.0.0':
+ resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
+ engines: {node: 20 || >=22}
+
+ '@istanbuljs/load-nyc-config@1.1.0':
+ resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
+ engines: {node: '>=8'}
+
+ '@istanbuljs/schema@0.1.3':
+ resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+ engines: {node: '>=8'}
+
+ '@jest/console@29.7.0':
+ resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/core@29.7.0':
+ resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ '@jest/environment@29.7.0':
+ resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/expect-utils@29.7.0':
+ resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/expect@29.7.0':
+ resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/fake-timers@29.7.0':
+ resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/globals@29.7.0':
+ resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/reporters@29.7.0':
+ resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ '@jest/schemas@29.6.3':
+ resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/source-map@29.6.3':
+ resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/test-result@29.7.0':
+ resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/test-sequencer@29.7.0':
+ resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/transform@29.7.0':
+ resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/types@29.6.3':
+ resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@jridgewell/trace-mapping@0.3.9':
+ resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
+
+ '@noble/curves@1.2.0':
+ resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==}
+
+ '@noble/curves@1.4.2':
+ resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==}
+
+ '@noble/curves@1.8.2':
+ resolution: {integrity: sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@noble/hashes@1.2.0':
+ resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==}
+
+ '@noble/hashes@1.3.2':
+ resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==}
+ engines: {node: '>= 16'}
+
+ '@noble/hashes@1.4.0':
+ resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==}
+ engines: {node: '>= 16'}
+
+ '@noble/hashes@1.7.2':
+ resolution: {integrity: sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@noble/hashes@1.8.0':
+ resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@noble/secp256k1@1.7.1':
+ resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@nomicfoundation/edr-darwin-arm64@0.12.0-next.16':
+ resolution: {integrity: sha512-no/8BPVBzVxDGGbDba0zsAxQmVNIq6SLjKzzhCxVKt4tatArXa6+24mr4jXJEmhVBvTNpQsNBO+MMpuEDVaTzQ==}
+ engines: {node: '>= 20'}
+
+ '@nomicfoundation/edr-darwin-x64@0.12.0-next.16':
+ resolution: {integrity: sha512-tf36YbcC6po3XYRbi+v0gjwzqg1MvyRqVUujNMXPHgjNWATXNRNOLyjwt2qDn+RD15qtzk70SHVnz9n9mPWzwg==}
+ engines: {node: '>= 20'}
+
+ '@nomicfoundation/edr-linux-arm64-gnu@0.12.0-next.16':
+ resolution: {integrity: sha512-Kr6t9icKSaKtPVbb0TjUcbn3XHqXOGIn+KjKKSSpm6542OkL0HyOi06amh6/8CNke9Gf6Lwion8UJ0aGQhnFwA==}
+ engines: {node: '>= 20'}
+
+ '@nomicfoundation/edr-linux-arm64-musl@0.12.0-next.16':
+ resolution: {integrity: sha512-HaStgfxctSg5PYF+6ooDICL1O59KrgM4XEUsIqoRrjrQax9HnMBXcB8eAj+0O52FWiO9FlchBni2dzh4RjQR2g==}
+ engines: {node: '>= 20'}
+
+ '@nomicfoundation/edr-linux-x64-gnu@0.12.0-next.16':
+ resolution: {integrity: sha512-8JPTxEZkwOPTgnN4uTWut9ze9R8rp7+T4IfmsKK9i+lDtdbJIxkrFY275YHG2BEYLd7Y5jTa/I4nC74ZpTAvpA==}
+ engines: {node: '>= 20'}
+
+ '@nomicfoundation/edr-linux-x64-musl@0.12.0-next.16':
+ resolution: {integrity: sha512-KugTrq3iHukbG64DuCYg8uPgiBtrrtX4oZSLba5sjocp0Ul6WWI1FeP1Qule+vClUrHSpJ+wR1G6SE7G0lyS/Q==}
+ engines: {node: '>= 20'}
+
+ '@nomicfoundation/edr-win32-x64-msvc@0.12.0-next.16':
+ resolution: {integrity: sha512-Idy0ZjurxElfSmepUKXh6QdptLbW5vUNeIaydvqNogWoTbkJIM6miqZd9lXUy1TYxY7G4Rx5O50c52xc4pFwXQ==}
+ engines: {node: '>= 20'}
+
+ '@nomicfoundation/edr@0.12.0-next.16':
+ resolution: {integrity: sha512-bBL/nHmQwL1WCveALwg01VhJcpVVklJyunG1d/bhJbHgbjzAn6kohVJc7A6gFZegw+Rx38vdxpBkeCDjAEprzw==}
+ engines: {node: '>= 20'}
+
+ '@nomicfoundation/hardhat-chai-matchers@2.1.0':
+ resolution: {integrity: sha512-GPhBNafh1fCnVD9Y7BYvoLnblnvfcq3j8YDbO1gGe/1nOFWzGmV7gFu5DkwFXF+IpYsS+t96o9qc/mPu3V3Vfw==}
+ peerDependencies:
+ '@nomicfoundation/hardhat-ethers': ^3.1.0
+ chai: ^4.2.0
+ ethers: ^6.14.0
+ hardhat: ^2.26.0
+
+ '@nomicfoundation/hardhat-ethers@3.1.2':
+ resolution: {integrity: sha512-7xEaz2X8p47qWIAqtV2z03MmusheHm8bvY2mDlxo9JiT2BgSx59GSdv5+mzwOvsuKDbTij7oqDnwFyYOlHREEQ==}
+ peerDependencies:
+ ethers: ^6.14.0
+ hardhat: ^2.26.0
+
+ '@nomicfoundation/hardhat-network-helpers@1.1.2':
+ resolution: {integrity: sha512-p7HaUVDbLj7ikFivQVNhnfMHUBgiHYMwQWvGn9AriieuopGOELIrwj2KjyM2a6z70zai5YKO264Vwz+3UFJZPQ==}
+ peerDependencies:
+ hardhat: ^2.26.0
+
+ '@nomicfoundation/hardhat-toolbox@4.0.0':
+ resolution: {integrity: sha512-jhcWHp0aHaL0aDYj8IJl80v4SZXWMS1A2XxXa1CA6pBiFfJKuZinCkO6wb+POAt0LIfXB3gA3AgdcOccrcwBwA==}
+ peerDependencies:
+ '@nomicfoundation/hardhat-chai-matchers': ^2.0.0
+ '@nomicfoundation/hardhat-ethers': ^3.0.0
+ '@nomicfoundation/hardhat-network-helpers': ^1.0.0
+ '@nomicfoundation/hardhat-verify': ^2.0.0
+ '@typechain/ethers-v6': ^0.5.0
+ '@typechain/hardhat': ^9.0.0
+ '@types/chai': ^4.2.0
+ '@types/mocha': '>=9.1.0'
+ '@types/node': '>=16.0.0'
+ chai: ^4.2.0
+ ethers: ^6.4.0
+ hardhat: ^2.11.0
+ hardhat-gas-reporter: ^1.0.8
+ solidity-coverage: ^0.8.1
+ ts-node: '>=8.0.0'
+ typechain: ^8.3.0
+ typescript: '>=4.5.0'
+
+ '@nomicfoundation/hardhat-verify@2.1.3':
+ resolution: {integrity: sha512-danbGjPp2WBhLkJdQy9/ARM3WQIK+7vwzE0urNem1qZJjh9f54Kf5f1xuQv8DvqewUAkuPxVt/7q4Grz5WjqSg==}
+ peerDependencies:
+ hardhat: ^2.26.0
+
+ '@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.2':
+ resolution: {integrity: sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==}
+ engines: {node: '>= 12'}
+
+ '@nomicfoundation/solidity-analyzer-darwin-x64@0.1.2':
+ resolution: {integrity: sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==}
+ engines: {node: '>= 12'}
+
+ '@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.2':
+ resolution: {integrity: sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==}
+ engines: {node: '>= 12'}
+
+ '@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.2':
+ resolution: {integrity: sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==}
+ engines: {node: '>= 12'}
+
+ '@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.2':
+ resolution: {integrity: sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==}
+ engines: {node: '>= 12'}
+
+ '@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.2':
+ resolution: {integrity: sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==}
+ engines: {node: '>= 12'}
+
+ '@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.2':
+ resolution: {integrity: sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==}
+ engines: {node: '>= 12'}
+
+ '@nomicfoundation/solidity-analyzer@0.1.2':
+ resolution: {integrity: sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==}
+ engines: {node: '>= 12'}
+
+ '@openzeppelin/contracts@5.4.0':
+ resolution: {integrity: sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A==}
+
+ '@pythnetwork/entropy-sdk-solidity@2.2.1':
+ resolution: {integrity: sha512-vwP7uJby591PWl78nA/lFkAsy9vSNV4DdvcqHxkAZEHhcAobVqErAMFSqp6HRO/epe07WKCorT+7favHh4sCjQ==}
+ engines: {node: '>=22.14.0'}
+
+ '@rollup/rollup-android-arm-eabi@4.53.3':
+ resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.53.3':
+ resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.53.3':
+ resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.53.3':
+ resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.53.3':
+ resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.53.3':
+ resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.53.3':
+ resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.53.3':
+ resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.53.3':
+ resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.53.3':
+ resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-gnu@4.53.3':
+ resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.53.3':
+ resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.53.3':
+ resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.53.3':
+ resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.53.3':
+ resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.53.3':
+ resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.53.3':
+ resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openharmony-arm64@4.53.3':
+ resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.53.3':
+ resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.53.3':
+ resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.53.3':
+ resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.53.3':
+ resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==}
+ cpu: [x64]
+ os: [win32]
+
+ '@scure/base@1.1.9':
+ resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==}
+
+ '@scure/base@1.2.6':
+ resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==}
+
+ '@scure/bip32@1.1.5':
+ resolution: {integrity: sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==}
+
+ '@scure/bip32@1.4.0':
+ resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==}
+
+ '@scure/bip39@1.1.1':
+ resolution: {integrity: sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==}
+
+ '@scure/bip39@1.3.0':
+ resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==}
+
+ '@sentry/core@5.30.0':
+ resolution: {integrity: sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==}
+ engines: {node: '>=6'}
+
+ '@sentry/hub@5.30.0':
+ resolution: {integrity: sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==}
+ engines: {node: '>=6'}
+
+ '@sentry/minimal@5.30.0':
+ resolution: {integrity: sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==}
+ engines: {node: '>=6'}
+
+ '@sentry/node@5.30.0':
+ resolution: {integrity: sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==}
+ engines: {node: '>=6'}
+
+ '@sentry/tracing@5.30.0':
+ resolution: {integrity: sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==}
+ engines: {node: '>=6'}
+
+ '@sentry/types@5.30.0':
+ resolution: {integrity: sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==}
+ engines: {node: '>=6'}
+
+ '@sentry/utils@5.30.0':
+ resolution: {integrity: sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==}
+ engines: {node: '>=6'}
+
+ '@sinclair/typebox@0.27.8':
+ resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
+
+ '@sinonjs/commons@3.0.1':
+ resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==}
+
+ '@sinonjs/fake-timers@10.3.0':
+ resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==}
+
+ '@solidity-parser/parser@0.14.5':
+ resolution: {integrity: sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==}
+
+ '@solidity-parser/parser@0.20.2':
+ resolution: {integrity: sha512-rbu0bzwNvMcwAjH86hiEAcOeRI2EeK8zCkHDrFykh/Al8mvJeFmjy3UrE7GYQjNwOgbGUUtCn5/k8CB8zIu7QA==}
+
+ '@tsconfig/node10@1.0.12':
+ resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==}
+
+ '@tsconfig/node12@1.0.11':
+ resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
+
+ '@tsconfig/node14@1.0.3':
+ resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
+
+ '@tsconfig/node16@1.0.4':
+ resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
+
+ '@typechain/ethers-v6@0.5.1':
+ resolution: {integrity: sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==}
+ peerDependencies:
+ ethers: 6.x
+ typechain: ^8.3.2
+ typescript: '>=4.7.0'
+
+ '@typechain/hardhat@9.1.0':
+ resolution: {integrity: sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA==}
+ peerDependencies:
+ '@typechain/ethers-v6': ^0.5.1
+ ethers: ^6.1.0
+ hardhat: ^2.9.9
+ typechain: ^8.3.2
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.28.0':
+ resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
+ '@types/bn.js@5.2.0':
+ resolution: {integrity: sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==}
+
+ '@types/chai-as-promised@7.1.8':
+ resolution: {integrity: sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==}
+
+ '@types/chai@4.3.20':
+ resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==}
+
+ '@types/concat-stream@1.6.1':
+ resolution: {integrity: sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/form-data@0.0.33':
+ resolution: {integrity: sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==}
+
+ '@types/glob@7.2.0':
+ resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
+
+ '@types/graceful-fs@4.1.9':
+ resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
+
+ '@types/istanbul-lib-coverage@2.0.6':
+ resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
+
+ '@types/istanbul-lib-report@3.0.3':
+ resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==}
+
+ '@types/istanbul-reports@3.0.4':
+ resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
+
+ '@types/jest@29.5.14':
+ resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==}
+
+ '@types/minimatch@6.0.0':
+ resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==}
+ deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed.
+
+ '@types/mocha@10.0.10':
+ resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==}
+
+ '@types/node@10.17.60':
+ resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==}
+
+ '@types/node@22.19.1':
+ resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==}
+
+ '@types/node@22.7.5':
+ resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==}
+
+ '@types/node@8.10.66':
+ resolution: {integrity: sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==}
+
+ '@types/pbkdf2@3.1.2':
+ resolution: {integrity: sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==}
+
+ '@types/prettier@2.7.3':
+ resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==}
+
+ '@types/qs@6.14.0':
+ resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==}
+
+ '@types/secp256k1@4.0.7':
+ resolution: {integrity: sha512-Rcvjl6vARGAKRO6jHeKMatGrvOMGrR/AR11N1x2LqintPCyDZ7NBhrh238Z2VZc7aM7KIwnFpFQ7fnfK4H/9Qw==}
+
+ '@types/stack-utils@2.0.3':
+ resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
+
+ '@types/ws@8.18.1':
+ resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
+
+ '@types/yargs-parser@21.0.3':
+ resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
+
+ '@types/yargs@17.0.35':
+ resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==}
+
+ abbrev@1.0.9:
+ resolution: {integrity: sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==}
+
+ acorn-walk@8.3.4:
+ resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==}
+ engines: {node: '>=0.4.0'}
+
+ acorn@8.15.0:
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ adm-zip@0.4.16:
+ resolution: {integrity: sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==}
+ engines: {node: '>=0.3.0'}
+
+ aes-js@3.0.0:
+ resolution: {integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==}
+
+ aes-js@4.0.0-beta.5:
+ resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==}
+
+ agent-base@6.0.2:
+ resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
+ engines: {node: '>= 6.0.0'}
+
+ aggregate-error@3.1.0:
+ resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
+ engines: {node: '>=8'}
+
+ ajv@8.17.1:
+ resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
+
+ amdefine@1.0.1:
+ resolution: {integrity: sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==}
+ engines: {node: '>=0.4.2'}
+
+ ansi-align@3.0.1:
+ resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
+
+ ansi-colors@4.1.3:
+ resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
+ engines: {node: '>=6'}
+
+ ansi-escapes@4.3.2:
+ resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@3.0.1:
+ resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==}
+ engines: {node: '>=4'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-styles@3.2.1:
+ resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+ engines: {node: '>=4'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
+ antlr4ts@0.5.0-alpha.4:
+ resolution: {integrity: sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ arg@4.1.3:
+ resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
+
+ argparse@1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ array-back@3.1.0:
+ resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==}
+ engines: {node: '>=6'}
+
+ array-back@4.0.2:
+ resolution: {integrity: sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==}
+ engines: {node: '>=8'}
+
+ array-union@2.1.0:
+ resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+ engines: {node: '>=8'}
+
+ array-uniq@1.0.3:
+ resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==}
+ engines: {node: '>=0.10.0'}
+
+ asap@2.0.6:
+ resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
+
+ assertion-error@1.1.0:
+ resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
+
+ astral-regex@2.0.0:
+ resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
+ engines: {node: '>=8'}
+
+ async@1.5.2:
+ resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==}
+
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+ at-least-node@1.0.0:
+ resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
+ engines: {node: '>= 4.0.0'}
+
+ available-typed-arrays@1.0.7:
+ resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+ engines: {node: '>= 0.4'}
+
+ axios@1.13.2:
+ resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==}
+
+ babel-jest@29.7.0:
+ resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ peerDependencies:
+ '@babel/core': ^7.8.0
+
+ babel-plugin-istanbul@6.1.1:
+ resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
+ engines: {node: '>=8'}
+
+ babel-plugin-jest-hoist@29.6.3:
+ resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ babel-preset-current-node-syntax@1.2.0:
+ resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0 || ^8.0.0-0
+
+ babel-preset-jest@29.6.3:
+ resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ base-x@3.0.11:
+ resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==}
+
+ baseline-browser-mapping@2.8.30:
+ resolution: {integrity: sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==}
+ hasBin: true
+
+ bech32@1.1.4:
+ resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==}
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ blakejs@1.2.1:
+ resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==}
+
+ bn.js@4.11.6:
+ resolution: {integrity: sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==}
+
+ bn.js@4.12.2:
+ resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==}
+
+ bn.js@5.2.2:
+ resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==}
+
+ boxen@5.1.2:
+ resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==}
+ engines: {node: '>=10'}
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ brace-expansion@2.0.2:
+ resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ brorand@1.1.0:
+ resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==}
+
+ browser-stdout@1.3.1:
+ resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==}
+
+ browserify-aes@1.2.0:
+ resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==}
+
+ browserslist@4.28.0:
+ resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ bs-logger@0.2.6:
+ resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==}
+ engines: {node: '>= 6'}
+
+ bs58@4.0.1:
+ resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==}
+
+ bs58check@2.1.2:
+ resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==}
+
+ bser@2.1.1:
+ resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
+
+ buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+ buffer-xor@1.0.3:
+ resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==}
+
+ bytes@3.1.2:
+ resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+ engines: {node: '>= 0.8'}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bind@1.0.8:
+ resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
+ engines: {node: '>= 0.4'}
+
+ call-bound@1.0.4:
+ resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+ engines: {node: '>= 0.4'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ camelcase@5.3.1:
+ resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+ engines: {node: '>=6'}
+
+ camelcase@6.3.0:
+ resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+ engines: {node: '>=10'}
+
+ caniuse-lite@1.0.30001756:
+ resolution: {integrity: sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==}
+
+ caseless@0.12.0:
+ resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
+
+ cbor@8.1.0:
+ resolution: {integrity: sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==}
+ engines: {node: '>=12.19'}
+
+ chai-as-promised@7.1.2:
+ resolution: {integrity: sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==}
+ peerDependencies:
+ chai: '>= 2.1.2 < 6'
+
+ chai@4.5.0:
+ resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
+ engines: {node: '>=4'}
+
+ chalk@2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ char-regex@1.0.2:
+ resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
+ engines: {node: '>=10'}
+
+ charenc@0.0.2:
+ resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
+
+ check-error@1.0.3:
+ resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ chokidar@4.0.3:
+ resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+ engines: {node: '>= 14.16.0'}
+
+ ci-info@2.0.0:
+ resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
+
+ ci-info@3.9.0:
+ resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
+ engines: {node: '>=8'}
+
+ cipher-base@1.0.7:
+ resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==}
+ engines: {node: '>= 0.10'}
+
+ cjs-module-lexer@1.4.3:
+ resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==}
+
+ clean-stack@2.2.0:
+ resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
+ engines: {node: '>=6'}
+
+ cli-boxes@2.2.1:
+ resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==}
+ engines: {node: '>=6'}
+
+ cli-table3@0.5.1:
+ resolution: {integrity: sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==}
+ engines: {node: '>=6'}
+
+ cliui@7.0.4:
+ resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+
+ cliui@8.0.1:
+ resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+ engines: {node: '>=12'}
+
+ co@4.6.0:
+ resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
+ engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
+
+ collect-v8-coverage@1.0.3:
+ resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==}
+
+ color-convert@1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.3:
+ resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ colors@1.4.0:
+ resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==}
+ engines: {node: '>=0.1.90'}
+
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
+ command-exists@1.2.9:
+ resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==}
+
+ command-line-args@5.2.1:
+ resolution: {integrity: sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==}
+ engines: {node: '>=4.0.0'}
+
+ command-line-usage@6.1.3:
+ resolution: {integrity: sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==}
+ engines: {node: '>=8.0.0'}
+
+ commander@8.3.0:
+ resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+ engines: {node: '>= 12'}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ concat-stream@1.6.2:
+ resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
+ engines: {'0': node >= 0.8}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ cookie@0.4.2:
+ resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
+ engines: {node: '>= 0.6'}
+
+ core-util-is@1.0.3:
+ resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+
+ create-hash@1.2.0:
+ resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
+
+ create-hmac@1.1.7:
+ resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==}
+
+ create-jest@29.7.0:
+ resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ hasBin: true
+
+ create-require@1.1.1:
+ resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ crypt@0.0.2:
+ resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
+
+ death@1.1.0:
+ resolution: {integrity: sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decamelize@4.0.0:
+ resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==}
+ engines: {node: '>=10'}
+
+ dedent@1.7.0:
+ resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==}
+ peerDependencies:
+ babel-plugin-macros: ^3.1.0
+ peerDependenciesMeta:
+ babel-plugin-macros:
+ optional: true
+
+ deep-eql@4.1.4:
+ resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==}
+ engines: {node: '>=6'}
+
+ deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
+ define-data-property@1.1.4:
+ resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ engines: {node: '>= 0.4'}
+
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
+ depd@2.0.0:
+ resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+ engines: {node: '>= 0.8'}
+
+ detect-newline@3.1.0:
+ resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
+ engines: {node: '>=8'}
+
+ diff-sequences@29.6.3:
+ resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ diff@4.0.2:
+ resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
+ engines: {node: '>=0.3.1'}
+
+ diff@5.2.0:
+ resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
+ engines: {node: '>=0.3.1'}
+
+ difflib@0.2.4:
+ resolution: {integrity: sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==}
+
+ dir-glob@3.0.1:
+ resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+ engines: {node: '>=8'}
+
+ dotenv@16.6.1:
+ resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
+ engines: {node: '>=12'}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ electron-to-chromium@1.5.259:
+ resolution: {integrity: sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==}
+
+ elliptic@6.6.1:
+ resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==}
+
+ emittery@0.13.1:
+ resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
+ engines: {node: '>=12'}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ enquirer@2.4.1:
+ resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==}
+ engines: {node: '>=8.6'}
+
+ env-paths@2.2.1:
+ resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
+ engines: {node: '>=6'}
+
+ error-ex@1.3.4:
+ resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ esbuild@0.21.5:
+ resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ esbuild@0.25.12:
+ resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+
+ escape-string-regexp@2.0.0:
+ resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+ engines: {node: '>=8'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ escodegen@1.8.1:
+ resolution: {integrity: sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==}
+ engines: {node: '>=0.12.0'}
+ hasBin: true
+
+ esprima@2.7.3:
+ resolution: {integrity: sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==}
+ engines: {node: '>=0.10.0'}
+ hasBin: true
+
+ esprima@4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ estraverse@1.9.3:
+ resolution: {integrity: sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==}
+ engines: {node: '>=0.10.0'}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ eth-gas-reporter@0.2.27:
+ resolution: {integrity: sha512-femhvoAM7wL0GcI8ozTdxfuBtBFJ9qsyIAsmKVjlWAHUbdnnXHt+lKzz/kmldM5lA9jLuNHGwuIxorNpLbR1Zw==}
+ peerDependencies:
+ '@codechecks/client': ^0.1.0
+ peerDependenciesMeta:
+ '@codechecks/client':
+ optional: true
+
+ ethereum-bloom-filters@1.2.0:
+ resolution: {integrity: sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA==}
+
+ ethereum-cryptography@0.1.3:
+ resolution: {integrity: sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==}
+
+ ethereum-cryptography@1.2.0:
+ resolution: {integrity: sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==}
+
+ ethereum-cryptography@2.2.1:
+ resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==}
+
+ ethereumjs-util@7.1.5:
+ resolution: {integrity: sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==}
+ engines: {node: '>=10.0.0'}
+
+ ethers@5.8.0:
+ resolution: {integrity: sha512-DUq+7fHrCg1aPDFCHx6UIPb3nmt2XMpM7Y/g2gLhsl3lIBqeAfOJIl1qEvRf2uq3BiKxmh6Fh5pfp2ieyek7Kg==}
+
+ ethers@6.15.0:
+ resolution: {integrity: sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ==}
+ engines: {node: '>=14.0.0'}
+
+ ethjs-unit@0.1.6:
+ resolution: {integrity: sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==}
+ engines: {node: '>=6.5.0', npm: '>=3'}
+
+ evp_bytestokey@1.0.3:
+ resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==}
+
+ execa@5.1.1:
+ resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+ engines: {node: '>=10'}
+
+ exit@0.1.2:
+ resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
+ engines: {node: '>= 0.8.0'}
+
+ expect@29.7.0:
+ resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fast-uri@3.1.0:
+ resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
+
+ fastq@1.19.1:
+ resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
+ fb-watchman@2.0.2:
+ resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-replace@3.0.0:
+ resolution: {integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==}
+ engines: {node: '>=4.0.0'}
+
+ find-up@4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat@5.0.2:
+ resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
+ hasBin: true
+
+ follow-redirects@1.15.11:
+ resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
+ for-each@0.3.5:
+ resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
+ engines: {node: '>= 0.4'}
+
+ form-data@2.5.5:
+ resolution: {integrity: sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==}
+ engines: {node: '>= 0.12'}
+
+ form-data@4.0.5:
+ resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
+ engines: {node: '>= 6'}
+
+ fp-ts@1.19.3:
+ resolution: {integrity: sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==}
+
+ fs-extra@7.0.1:
+ resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
+ engines: {node: '>=6 <7 || >=8'}
+
+ fs-extra@8.1.0:
+ resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
+ engines: {node: '>=6 <7 || >=8'}
+
+ fs-extra@9.1.0:
+ resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
+ engines: {node: '>=10'}
+
+ fs-readdir-recursive@1.1.0:
+ resolution: {integrity: sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
+ get-func-name@2.0.2:
+ resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-package-type@0.1.0:
+ resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
+ engines: {node: '>=8.0.0'}
+
+ get-port@3.2.0:
+ resolution: {integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==}
+ engines: {node: '>=4'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-stream@6.0.1:
+ resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+ engines: {node: '>=10'}
+
+ get-tsconfig@4.13.0:
+ resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
+
+ ghost-testrpc@0.0.2:
+ resolution: {integrity: sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==}
+ hasBin: true
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob@5.0.15:
+ resolution: {integrity: sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ glob@7.1.7:
+ resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ glob@8.1.0:
+ resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
+ engines: {node: '>=12'}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ global-modules@2.0.0:
+ resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==}
+ engines: {node: '>=6'}
+
+ global-prefix@3.0.0:
+ resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==}
+ engines: {node: '>=6'}
+
+ globby@10.0.2:
+ resolution: {integrity: sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==}
+ engines: {node: '>=8'}
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ handlebars@4.7.8:
+ resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
+ engines: {node: '>=0.4.7'}
+ hasBin: true
+
+ hardhat-gas-reporter@1.0.10:
+ resolution: {integrity: sha512-02N4+So/fZrzJ88ci54GqwVA3Zrf0C9duuTyGt0CFRIh/CdNwbnTgkXkRfojOMLBQ+6t+lBIkgbsOtqMvNwikA==}
+ peerDependencies:
+ hardhat: ^2.0.2
+
+ hardhat@2.27.0:
+ resolution: {integrity: sha512-du7ecjx1/ueAUjvtZhVkJvWytPCjlagG3ZktYTphfzAbc1Flc6sRolw5mhKL/Loub1EIFRaflutM4bdB/YsUUw==}
+ hasBin: true
+ peerDependencies:
+ ts-node: '*'
+ typescript: '*'
+ peerDependenciesMeta:
+ ts-node:
+ optional: true
+ typescript:
+ optional: true
+
+ has-flag@1.0.0:
+ resolution: {integrity: sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==}
+ engines: {node: '>=0.10.0'}
+
+ has-flag@3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-property-descriptors@1.0.2:
+ resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hash-base@3.1.2:
+ resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==}
+ engines: {node: '>= 0.8'}
+
+ hash.js@1.1.7:
+ resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ he@1.2.0:
+ resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+ hasBin: true
+
+ heap@0.2.7:
+ resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==}
+
+ hmac-drbg@1.0.1:
+ resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==}
+
+ html-escaper@2.0.2:
+ resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
+ http-basic@8.1.3:
+ resolution: {integrity: sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==}
+ engines: {node: '>=6.0.0'}
+
+ http-errors@2.0.0:
+ resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+ engines: {node: '>= 0.8'}
+
+ http-response-object@3.0.2:
+ resolution: {integrity: sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==}
+
+ https-proxy-agent@5.0.1:
+ resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
+ engines: {node: '>= 6'}
+
+ human-signals@2.1.0:
+ resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+ engines: {node: '>=10.17.0'}
+
+ iconv-lite@0.4.24:
+ resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+ engines: {node: '>=0.10.0'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ immutable@4.3.7:
+ resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
+
+ import-local@3.2.0:
+ resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+ interpret@1.4.0:
+ resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
+ engines: {node: '>= 0.10'}
+
+ io-ts@1.10.4:
+ resolution: {integrity: sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==}
+
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-callable@1.2.7:
+ resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+ engines: {node: '>= 0.4'}
+
+ is-core-module@2.16.1:
+ resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+ engines: {node: '>= 0.4'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@2.0.0:
+ resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==}
+ engines: {node: '>=4'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-generator-fn@2.1.0:
+ resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
+ engines: {node: '>=6'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-hex-prefixed@1.0.0:
+ resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==}
+ engines: {node: '>=6.5.0', npm: '>=3'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-plain-obj@2.1.0:
+ resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
+ engines: {node: '>=8'}
+
+ is-stream@2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+
+ is-typed-array@1.1.15:
+ resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
+ engines: {node: '>= 0.4'}
+
+ is-unicode-supported@0.1.0:
+ resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
+ engines: {node: '>=10'}
+
+ isarray@1.0.0:
+ resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+
+ isarray@2.0.5:
+ resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-instrument@5.2.1:
+ resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-instrument@6.0.3:
+ resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-report@3.0.1:
+ resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-source-maps@4.0.1:
+ resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
+ engines: {node: '>=10'}
+
+ istanbul-reports@3.2.0:
+ resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==}
+ engines: {node: '>=8'}
+
+ jest-changed-files@29.7.0:
+ resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-circus@29.7.0:
+ resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-cli@29.7.0:
+ resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ hasBin: true
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ jest-config@29.7.0:
+ resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ peerDependencies:
+ '@types/node': '*'
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ ts-node:
+ optional: true
+
+ jest-diff@29.7.0:
+ resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-docblock@29.7.0:
+ resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-each@29.7.0:
+ resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-environment-node@29.7.0:
+ resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-get-type@29.6.3:
+ resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-haste-map@29.7.0:
+ resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-leak-detector@29.7.0:
+ resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-matcher-utils@29.7.0:
+ resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-message-util@29.7.0:
+ resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-mock@29.7.0:
+ resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-pnp-resolver@1.2.3:
+ resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==}
+ engines: {node: '>=6'}
+ peerDependencies:
+ jest-resolve: '*'
+ peerDependenciesMeta:
+ jest-resolve:
+ optional: true
+
+ jest-regex-util@29.6.3:
+ resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-resolve-dependencies@29.7.0:
+ resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-resolve@29.7.0:
+ resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-runner@29.7.0:
+ resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-runtime@29.7.0:
+ resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-snapshot@29.7.0:
+ resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-util@29.7.0:
+ resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-validate@29.7.0:
+ resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-watcher@29.7.0:
+ resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-worker@29.7.0:
+ resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest@29.7.0:
+ resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ hasBin: true
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ js-sha3@0.8.0:
+ resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@3.14.2:
+ resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==}
+ hasBin: true
+
+ js-yaml@4.1.1:
+ resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
+ hasBin: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
+ json-schema-traverse@1.0.0:
+ resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
+ json-stream-stringify@3.1.6:
+ resolution: {integrity: sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==}
+ engines: {node: '>=7.10.1'}
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsonfile@4.0.0:
+ resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+
+ jsonfile@6.2.0:
+ resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
+
+ jsonschema@1.5.0:
+ resolution: {integrity: sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==}
+
+ keccak@3.0.4:
+ resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==}
+ engines: {node: '>=10.0.0'}
+
+ kind-of@6.0.3:
+ resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
+ engines: {node: '>=0.10.0'}
+
+ kleur@3.0.3:
+ resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
+ engines: {node: '>=6'}
+
+ leven@3.1.0:
+ resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
+ engines: {node: '>=6'}
+
+ levn@0.3.0:
+ resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==}
+ engines: {node: '>= 0.8.0'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ locate-path@5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.camelcase@4.3.0:
+ resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
+ lodash.clonedeep@4.5.0:
+ resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==}
+
+ lodash.isequal@4.5.0:
+ resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
+ deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
+
+ lodash.memoize@4.1.2:
+ resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
+
+ lodash.truncate@4.4.2:
+ resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
+
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ log-symbols@4.1.0:
+ resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
+ engines: {node: '>=10'}
+
+ loupe@2.3.7:
+ resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ lru_map@0.3.3:
+ resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==}
+
+ make-dir@4.0.0:
+ resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+ engines: {node: '>=10'}
+
+ make-error@1.3.6:
+ resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
+
+ makeerror@1.0.12:
+ resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
+
+ markdown-table@1.1.3:
+ resolution: {integrity: sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==}
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ md5.js@1.3.5:
+ resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==}
+
+ memorystream@0.3.1:
+ resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
+ engines: {node: '>= 0.10.0'}
+
+ merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micro-eth-signer@0.14.0:
+ resolution: {integrity: sha512-5PLLzHiVYPWClEvZIXXFu5yutzpadb73rnQCpUqIHu3No3coFuWQNfE5tkBQJ7djuLYl6aRLaS0MgWJYGoqiBw==}
+
+ micro-ftch@0.3.1:
+ resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==}
+
+ micro-packed@0.7.3:
+ resolution: {integrity: sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg==}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
+ mimic-fn@2.1.0:
+ resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+ engines: {node: '>=6'}
+
+ minimalistic-assert@1.0.1:
+ resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
+
+ minimalistic-crypto-utils@1.0.1:
+ resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==}
+
+ minimatch@10.1.1:
+ resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==}
+ engines: {node: 20 || >=22}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@5.1.6:
+ resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
+ engines: {node: '>=10'}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ mkdirp@0.5.6:
+ resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+ hasBin: true
+
+ mkdirp@1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ mnemonist@0.38.5:
+ resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==}
+
+ mocha@10.8.2:
+ resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==}
+ engines: {node: '>= 14.0.0'}
+ hasBin: true
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ neo-async@2.6.2:
+ resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+
+ node-addon-api@2.0.2:
+ resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==}
+
+ node-addon-api@5.1.0:
+ resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==}
+
+ node-emoji@1.11.0:
+ resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==}
+
+ node-gyp-build@4.8.4:
+ resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
+ hasBin: true
+
+ node-int64@0.4.0:
+ resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
+
+ node-releases@2.0.27:
+ resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
+
+ nofilter@3.1.0:
+ resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==}
+ engines: {node: '>=12.19'}
+
+ nopt@3.0.6:
+ resolution: {integrity: sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==}
+ hasBin: true
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ npm-run-path@4.0.1:
+ resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+ engines: {node: '>=8'}
+
+ number-to-bn@1.7.0:
+ resolution: {integrity: sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==}
+ engines: {node: '>=6.5.0', npm: '>=3'}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-inspect@1.13.4:
+ resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ engines: {node: '>= 0.4'}
+
+ obliterator@2.0.5:
+ resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ onetime@5.1.2:
+ resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+ engines: {node: '>=6'}
+
+ optionator@0.8.3:
+ resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==}
+ engines: {node: '>= 0.8.0'}
+
+ ordinal@1.0.3:
+ resolution: {integrity: sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==}
+
+ os-tmpdir@1.0.2:
+ resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
+ engines: {node: '>=0.10.0'}
+
+ p-limit@2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ p-map@4.0.0:
+ resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
+ engines: {node: '>=10'}
+
+ p-try@2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+
+ parse-cache-control@1.0.1:
+ resolution: {integrity: sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==}
+
+ parse-json@5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ path-type@4.0.0:
+ resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+ engines: {node: '>=8'}
+
+ pathval@1.1.1:
+ resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
+
+ pbkdf2@3.1.5:
+ resolution: {integrity: sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==}
+ engines: {node: '>= 0.10'}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ pify@4.0.1:
+ resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+ engines: {node: '>=6'}
+
+ pirates@4.0.7:
+ resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+ engines: {node: '>= 6'}
+
+ pkg-dir@4.2.0:
+ resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
+ engines: {node: '>=8'}
+
+ possible-typed-array-names@1.1.0:
+ resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
+ engines: {node: '>= 0.4'}
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.1.2:
+ resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier@2.8.8:
+ resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+
+ pretty-format@29.7.0:
+ resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ process-nextick-args@2.0.1:
+ resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
+ promise@8.3.0:
+ resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==}
+
+ prompts@2.4.2:
+ resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
+ engines: {node: '>= 6'}
+
+ proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+ pure-rand@6.1.0:
+ resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==}
+
+ qs@6.14.0:
+ resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
+ engines: {node: '>=0.6'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ randombytes@2.1.0:
+ resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
+
+ raw-body@2.5.2:
+ resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
+ engines: {node: '>= 0.8'}
+
+ react-is@18.3.1:
+ resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+
+ readable-stream@2.3.8:
+ resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
+
+ readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ readdirp@4.1.2:
+ resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+ engines: {node: '>= 14.18.0'}
+
+ rechoir@0.6.2:
+ resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==}
+ engines: {node: '>= 0.10'}
+
+ recursive-readdir@2.2.3:
+ resolution: {integrity: sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==}
+ engines: {node: '>=6.0.0'}
+
+ reduce-flatten@2.0.0:
+ resolution: {integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==}
+ engines: {node: '>=6'}
+
+ req-cwd@2.0.0:
+ resolution: {integrity: sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==}
+ engines: {node: '>=4'}
+
+ req-from@2.0.0:
+ resolution: {integrity: sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==}
+ engines: {node: '>=4'}
+
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ resolve-cwd@3.0.0:
+ resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
+ engines: {node: '>=8'}
+
+ resolve-from@3.0.0:
+ resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==}
+ engines: {node: '>=4'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ resolve.exports@2.0.3:
+ resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==}
+ engines: {node: '>=10'}
+
+ resolve@1.1.7:
+ resolution: {integrity: sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==}
+
+ resolve@1.17.0:
+ resolution: {integrity: sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==}
+
+ resolve@1.22.11:
+ resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ ripemd160@2.0.3:
+ resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==}
+ engines: {node: '>= 0.8'}
+
+ rlp@2.2.7:
+ resolution: {integrity: sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==}
+ hasBin: true
+
+ rollup@4.53.3:
+ resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ safe-buffer@5.1.2:
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ sc-istanbul@0.4.6:
+ resolution: {integrity: sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==}
+ hasBin: true
+
+ scrypt-js@3.0.1:
+ resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==}
+
+ secp256k1@4.0.4:
+ resolution: {integrity: sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==}
+ engines: {node: '>=18.0.0'}
+
+ semver@5.7.2:
+ resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
+ hasBin: true
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.7.3:
+ resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ serialize-javascript@6.0.2:
+ resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
+
+ set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+ engines: {node: '>= 0.4'}
+
+ setimmediate@1.0.5:
+ resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
+
+ setprototypeof@1.2.0:
+ resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+
+ sha.js@2.4.12:
+ resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==}
+ engines: {node: '>= 0.10'}
+ hasBin: true
+
+ sha1@1.1.1:
+ resolution: {integrity: sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==}
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ shelljs@0.8.5:
+ resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ side-channel-list@1.0.0:
+ resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-map@1.0.1:
+ resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-weakmap@1.0.2:
+ resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.1.0:
+ resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+ engines: {node: '>= 0.4'}
+
+ signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+ sisteransi@1.0.5:
+ resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
+ slash@3.0.0:
+ resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+ engines: {node: '>=8'}
+
+ slice-ansi@4.0.0:
+ resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
+ engines: {node: '>=10'}
+
+ solc@0.8.26:
+ resolution: {integrity: sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==}
+ engines: {node: '>=10.0.0'}
+ hasBin: true
+
+ solidity-coverage@0.8.16:
+ resolution: {integrity: sha512-qKqgm8TPpcnCK0HCDLJrjbOA2tQNEJY4dHX/LSSQ9iwYFS973MwjtgYn2Iv3vfCEQJTj5xtm4cuUMzlJsJSMbg==}
+ hasBin: true
+ peerDependencies:
+ hardhat: ^2.11.0
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ source-map-support@0.5.13:
+ resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==}
+
+ source-map-support@0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+ source-map@0.2.0:
+ resolution: {integrity: sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==}
+ engines: {node: '>=0.8.0'}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ sprintf-js@1.0.3:
+ resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+ stack-utils@2.0.6:
+ resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
+ engines: {node: '>=10'}
+
+ stacktrace-parser@0.1.11:
+ resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==}
+ engines: {node: '>=6'}
+
+ statuses@2.0.1:
+ resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+ engines: {node: '>= 0.8'}
+
+ string-format@2.0.0:
+ resolution: {integrity: sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==}
+
+ string-length@4.0.2:
+ resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
+ engines: {node: '>=10'}
+
+ string-width@2.1.1:
+ resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==}
+ engines: {node: '>=4'}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string_decoder@1.1.1:
+ resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+
+ string_decoder@1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
+ strip-ansi@4.0.0:
+ resolution: {integrity: sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==}
+ engines: {node: '>=4'}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-bom@4.0.0:
+ resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
+ engines: {node: '>=8'}
+
+ strip-final-newline@2.0.0:
+ resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+ engines: {node: '>=6'}
+
+ strip-hex-prefix@1.0.0:
+ resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==}
+ engines: {node: '>=6.5.0', npm: '>=3'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ supports-color@3.2.3:
+ resolution: {integrity: sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==}
+ engines: {node: '>=0.8.0'}
+
+ supports-color@5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-color@8.1.1:
+ resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+ engines: {node: '>=10'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ sync-request@6.1.0:
+ resolution: {integrity: sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==}
+ engines: {node: '>=8.0.0'}
+
+ sync-rpc@1.3.6:
+ resolution: {integrity: sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==}
+
+ table-layout@1.0.2:
+ resolution: {integrity: sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==}
+ engines: {node: '>=8.0.0'}
+
+ table@6.9.0:
+ resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
+ engines: {node: '>=10.0.0'}
+
+ test-exclude@6.0.0:
+ resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
+ engines: {node: '>=8'}
+
+ then-request@6.0.2:
+ resolution: {integrity: sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==}
+ engines: {node: '>=6.0.0'}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ tmp@0.0.33:
+ resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
+ engines: {node: '>=0.6.0'}
+
+ tmpl@1.0.5:
+ resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
+
+ to-buffer@1.2.2:
+ resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==}
+ engines: {node: '>= 0.4'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ toidentifier@1.0.1:
+ resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+ engines: {node: '>=0.6'}
+
+ ts-command-line-args@2.5.1:
+ resolution: {integrity: sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==}
+ hasBin: true
+
+ ts-essentials@7.0.3:
+ resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==}
+ peerDependencies:
+ typescript: '>=3.7.0'
+
+ ts-jest@29.4.5:
+ resolution: {integrity: sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==}
+ engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@babel/core': '>=7.0.0-beta.0 <8'
+ '@jest/transform': ^29.0.0 || ^30.0.0
+ '@jest/types': ^29.0.0 || ^30.0.0
+ babel-jest: ^29.0.0 || ^30.0.0
+ esbuild: '*'
+ jest: ^29.0.0 || ^30.0.0
+ jest-util: ^29.0.0 || ^30.0.0
+ typescript: '>=4.3 <6'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ '@jest/transform':
+ optional: true
+ '@jest/types':
+ optional: true
+ babel-jest:
+ optional: true
+ esbuild:
+ optional: true
+ jest-util:
+ optional: true
+
+ ts-node@10.9.2:
+ resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
+ hasBin: true
+ peerDependencies:
+ '@swc/core': '>=1.2.50'
+ '@swc/wasm': '>=1.2.50'
+ '@types/node': '*'
+ typescript: '>=2.7'
+ peerDependenciesMeta:
+ '@swc/core':
+ optional: true
+ '@swc/wasm':
+ optional: true
+
+ tslib@1.14.1:
+ resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
+
+ tslib@2.7.0:
+ resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
+
+ tsort@0.0.1:
+ resolution: {integrity: sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==}
+
+ tsx@4.20.6:
+ resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
+ type-check@0.3.2:
+ resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
+ engines: {node: '>= 0.8.0'}
+
+ type-detect@4.0.8:
+ resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+ engines: {node: '>=4'}
+
+ type-detect@4.1.0:
+ resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==}
+ engines: {node: '>=4'}
+
+ type-fest@0.20.2:
+ resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+ engines: {node: '>=10'}
+
+ type-fest@0.21.3:
+ resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+ engines: {node: '>=10'}
+
+ type-fest@0.7.1:
+ resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==}
+ engines: {node: '>=8'}
+
+ type-fest@4.41.0:
+ resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
+ engines: {node: '>=16'}
+
+ typechain@8.3.2:
+ resolution: {integrity: sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==}
+ hasBin: true
+ peerDependencies:
+ typescript: '>=4.3.0'
+
+ typed-array-buffer@1.0.3:
+ resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
+ engines: {node: '>= 0.4'}
+
+ typedarray@0.0.6:
+ resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
+
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ typical@4.0.0:
+ resolution: {integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==}
+ engines: {node: '>=8'}
+
+ typical@5.2.0:
+ resolution: {integrity: sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==}
+ engines: {node: '>=8'}
+
+ uglify-js@3.19.3:
+ resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==}
+ engines: {node: '>=0.8.0'}
+ hasBin: true
+
+ undici-types@6.19.8:
+ resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
+
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+ undici@5.29.0:
+ resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==}
+ engines: {node: '>=14.0'}
+
+ universalify@0.1.2:
+ resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
+ engines: {node: '>= 4.0.0'}
+
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
+ unpipe@1.0.0:
+ resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+ engines: {node: '>= 0.8'}
+
+ update-browserslist-db@1.1.4:
+ resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ utf8@3.0.0:
+ resolution: {integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==}
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ uuid@8.3.2:
+ resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+ hasBin: true
+
+ v8-compile-cache-lib@3.0.1:
+ resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
+
+ v8-to-istanbul@9.3.0:
+ resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==}
+ engines: {node: '>=10.12.0'}
+
+ vite@5.4.21:
+ resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+
+ walker@1.0.8:
+ resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
+
+ web3-utils@1.10.4:
+ resolution: {integrity: sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==}
+ engines: {node: '>=8.0.0'}
+
+ which-typed-array@1.1.19:
+ resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
+ engines: {node: '>= 0.4'}
+
+ which@1.3.1:
+ resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+ hasBin: true
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ widest-line@3.1.0:
+ resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==}
+ engines: {node: '>=8'}
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wordwrap@1.0.0:
+ resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
+
+ wordwrapjs@4.0.1:
+ resolution: {integrity: sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==}
+ engines: {node: '>=8.0.0'}
+
+ workerpool@6.5.1:
+ resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ write-file-atomic@4.0.2:
+ resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
+ ws@7.5.10:
+ resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
+ engines: {node: '>=8.3.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ^5.0.2
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ ws@8.17.1:
+ resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ ws@8.18.0:
+ resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ ws@8.18.3:
+ resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yargs-parser@20.2.9:
+ resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
+ engines: {node: '>=10'}
+
+ yargs-parser@21.1.1:
+ resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+ engines: {node: '>=12'}
+
+ yargs-unparser@2.0.0:
+ resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==}
+ engines: {node: '>=10'}
+
+ yargs@16.2.0:
+ resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
+ engines: {node: '>=10'}
+
+ yargs@17.7.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
+ yn@3.1.1:
+ resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
+ engines: {node: '>=6'}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+snapshots:
+
+ '@adraffy/ens-normalize@1.10.1': {}
+
+ '@babel/code-frame@7.27.1':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.28.5': {}
+
+ '@babel/core@7.28.5':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.5
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5)
+ '@babel/helpers': 7.28.4
+ '@babel/parser': 7.28.5
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3(supports-color@8.1.1)
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.28.5':
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.27.2':
+ dependencies:
+ '@babel/compat-data': 7.28.5
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.28.0
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-module-imports@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.5
+ '@babel/types': 7.28.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.28.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.27.1': {}
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.28.4':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.5
+
+ '@babel/parser@7.28.5':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/template@7.27.2':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
+
+ '@babel/traverse@7.28.5':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.5
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.28.5
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.5
+ debug: 4.4.3(supports-color@8.1.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.28.5':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@bcoe/v8-coverage@0.2.3': {}
+
+ '@cspotcode/source-map-support@0.8.1':
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.9
+
+ '@esbuild/aix-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.25.12':
+ optional: true
+
+ '@esbuild/android-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/android-arm@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm@0.25.12':
+ optional: true
+
+ '@esbuild/android-x64@0.21.5':
+ optional: true
+
+ '@esbuild/android-x64@0.25.12':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/darwin-x64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.12':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-arm@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.12':
+ optional: true
+
+ '@esbuild/linux-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.12':
+ optional: true
+
+ '@esbuild/linux-loong64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.21.5':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.12':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-s390x@0.21.5':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.12':
+ optional: true
+
+ '@esbuild/linux-x64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.12':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/sunos-x64@0.21.5':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.12':
+ optional: true
+
+ '@esbuild/win32-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/win32-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.12':
+ optional: true
+
+ '@esbuild/win32-x64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.12':
+ optional: true
+
+ '@ethereumjs/rlp@4.0.1': {}
+
+ '@ethereumjs/rlp@5.0.2': {}
+
+ '@ethereumjs/util@8.1.0':
+ dependencies:
+ '@ethereumjs/rlp': 4.0.1
+ ethereum-cryptography: 2.2.1
+ micro-ftch: 0.3.1
+
+ '@ethereumjs/util@9.1.0':
+ dependencies:
+ '@ethereumjs/rlp': 5.0.2
+ ethereum-cryptography: 2.2.1
+
+ '@ethersproject/abi@5.8.0':
+ dependencies:
+ '@ethersproject/address': 5.8.0
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/constants': 5.8.0
+ '@ethersproject/hash': 5.8.0
+ '@ethersproject/keccak256': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/strings': 5.8.0
+
+ '@ethersproject/abstract-provider@5.8.0':
+ dependencies:
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/networks': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/transactions': 5.8.0
+ '@ethersproject/web': 5.8.0
+
+ '@ethersproject/abstract-signer@5.8.0':
+ dependencies:
+ '@ethersproject/abstract-provider': 5.8.0
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/properties': 5.8.0
+
+ '@ethersproject/address@5.8.0':
+ dependencies:
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/keccak256': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/rlp': 5.8.0
+
+ '@ethersproject/base64@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+
+ '@ethersproject/basex@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/properties': 5.8.0
+
+ '@ethersproject/bignumber@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ bn.js: 5.2.2
+
+ '@ethersproject/bytes@5.8.0':
+ dependencies:
+ '@ethersproject/logger': 5.8.0
+
+ '@ethersproject/constants@5.8.0':
+ dependencies:
+ '@ethersproject/bignumber': 5.8.0
+
+ '@ethersproject/contracts@5.8.0':
+ dependencies:
+ '@ethersproject/abi': 5.8.0
+ '@ethersproject/abstract-provider': 5.8.0
+ '@ethersproject/abstract-signer': 5.8.0
+ '@ethersproject/address': 5.8.0
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/constants': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/transactions': 5.8.0
+
+ '@ethersproject/hash@5.8.0':
+ dependencies:
+ '@ethersproject/abstract-signer': 5.8.0
+ '@ethersproject/address': 5.8.0
+ '@ethersproject/base64': 5.8.0
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/keccak256': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/strings': 5.8.0
+
+ '@ethersproject/hdnode@5.8.0':
+ dependencies:
+ '@ethersproject/abstract-signer': 5.8.0
+ '@ethersproject/basex': 5.8.0
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/pbkdf2': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/sha2': 5.8.0
+ '@ethersproject/signing-key': 5.8.0
+ '@ethersproject/strings': 5.8.0
+ '@ethersproject/transactions': 5.8.0
+ '@ethersproject/wordlists': 5.8.0
+
+ '@ethersproject/json-wallets@5.8.0':
+ dependencies:
+ '@ethersproject/abstract-signer': 5.8.0
+ '@ethersproject/address': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/hdnode': 5.8.0
+ '@ethersproject/keccak256': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/pbkdf2': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/random': 5.8.0
+ '@ethersproject/strings': 5.8.0
+ '@ethersproject/transactions': 5.8.0
+ aes-js: 3.0.0
+ scrypt-js: 3.0.1
+
+ '@ethersproject/keccak256@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+ js-sha3: 0.8.0
+
+ '@ethersproject/logger@5.8.0': {}
+
+ '@ethersproject/networks@5.8.0':
+ dependencies:
+ '@ethersproject/logger': 5.8.0
+
+ '@ethersproject/pbkdf2@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/sha2': 5.8.0
+
+ '@ethersproject/properties@5.8.0':
+ dependencies:
+ '@ethersproject/logger': 5.8.0
+
+ '@ethersproject/providers@5.8.0':
+ dependencies:
+ '@ethersproject/abstract-provider': 5.8.0
+ '@ethersproject/abstract-signer': 5.8.0
+ '@ethersproject/address': 5.8.0
+ '@ethersproject/base64': 5.8.0
+ '@ethersproject/basex': 5.8.0
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/constants': 5.8.0
+ '@ethersproject/hash': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/networks': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/random': 5.8.0
+ '@ethersproject/rlp': 5.8.0
+ '@ethersproject/sha2': 5.8.0
+ '@ethersproject/strings': 5.8.0
+ '@ethersproject/transactions': 5.8.0
+ '@ethersproject/web': 5.8.0
+ bech32: 1.1.4
+ ws: 8.18.0
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ '@ethersproject/random@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/logger': 5.8.0
+
+ '@ethersproject/rlp@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/logger': 5.8.0
+
+ '@ethersproject/sha2@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ hash.js: 1.1.7
+
+ '@ethersproject/signing-key@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ bn.js: 5.2.2
+ elliptic: 6.6.1
+ hash.js: 1.1.7
+
+ '@ethersproject/solidity@5.8.0':
+ dependencies:
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/keccak256': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/sha2': 5.8.0
+ '@ethersproject/strings': 5.8.0
+
+ '@ethersproject/strings@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/constants': 5.8.0
+ '@ethersproject/logger': 5.8.0
+
+ '@ethersproject/transactions@5.8.0':
+ dependencies:
+ '@ethersproject/address': 5.8.0
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/constants': 5.8.0
+ '@ethersproject/keccak256': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/rlp': 5.8.0
+ '@ethersproject/signing-key': 5.8.0
+
+ '@ethersproject/units@5.8.0':
+ dependencies:
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/constants': 5.8.0
+ '@ethersproject/logger': 5.8.0
+
+ '@ethersproject/wallet@5.8.0':
+ dependencies:
+ '@ethersproject/abstract-provider': 5.8.0
+ '@ethersproject/abstract-signer': 5.8.0
+ '@ethersproject/address': 5.8.0
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/hash': 5.8.0
+ '@ethersproject/hdnode': 5.8.0
+ '@ethersproject/json-wallets': 5.8.0
+ '@ethersproject/keccak256': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/random': 5.8.0
+ '@ethersproject/signing-key': 5.8.0
+ '@ethersproject/transactions': 5.8.0
+ '@ethersproject/wordlists': 5.8.0
+
+ '@ethersproject/web@5.8.0':
+ dependencies:
+ '@ethersproject/base64': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/strings': 5.8.0
+
+ '@ethersproject/wordlists@5.8.0':
+ dependencies:
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/hash': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/strings': 5.8.0
+
+ '@fastify/busboy@2.1.1': {}
+
+ '@isaacs/balanced-match@4.0.1': {}
+
+ '@isaacs/brace-expansion@5.0.0':
+ dependencies:
+ '@isaacs/balanced-match': 4.0.1
+
+ '@istanbuljs/load-nyc-config@1.1.0':
+ dependencies:
+ camelcase: 5.3.1
+ find-up: 4.1.0
+ get-package-type: 0.1.0
+ js-yaml: 3.14.2
+ resolve-from: 5.0.0
+
+ '@istanbuljs/schema@0.1.3': {}
+
+ '@jest/console@29.7.0':
+ dependencies:
+ '@jest/types': 29.6.3
+ '@types/node': 22.19.1
+ chalk: 4.1.2
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
+ slash: 3.0.0
+
+ '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))':
+ dependencies:
+ '@jest/console': 29.7.0
+ '@jest/reporters': 29.7.0
+ '@jest/test-result': 29.7.0
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 22.19.1
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ exit: 0.1.2
+ graceful-fs: 4.2.11
+ jest-changed-files: 29.7.0
+ jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
+ jest-haste-map: 29.7.0
+ jest-message-util: 29.7.0
+ jest-regex-util: 29.6.3
+ jest-resolve: 29.7.0
+ jest-resolve-dependencies: 29.7.0
+ jest-runner: 29.7.0
+ jest-runtime: 29.7.0
+ jest-snapshot: 29.7.0
+ jest-util: 29.7.0
+ jest-validate: 29.7.0
+ jest-watcher: 29.7.0
+ micromatch: 4.0.8
+ pretty-format: 29.7.0
+ slash: 3.0.0
+ strip-ansi: 6.0.1
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+ - ts-node
+
+ '@jest/environment@29.7.0':
+ dependencies:
+ '@jest/fake-timers': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 22.19.1
+ jest-mock: 29.7.0
+
+ '@jest/expect-utils@29.7.0':
+ dependencies:
+ jest-get-type: 29.6.3
+
+ '@jest/expect@29.7.0':
+ dependencies:
+ expect: 29.7.0
+ jest-snapshot: 29.7.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/fake-timers@29.7.0':
+ dependencies:
+ '@jest/types': 29.6.3
+ '@sinonjs/fake-timers': 10.3.0
+ '@types/node': 22.19.1
+ jest-message-util: 29.7.0
+ jest-mock: 29.7.0
+ jest-util: 29.7.0
+
+ '@jest/globals@29.7.0':
+ dependencies:
+ '@jest/environment': 29.7.0
+ '@jest/expect': 29.7.0
+ '@jest/types': 29.6.3
+ jest-mock: 29.7.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/reporters@29.7.0':
+ dependencies:
+ '@bcoe/v8-coverage': 0.2.3
+ '@jest/console': 29.7.0
+ '@jest/test-result': 29.7.0
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
+ '@jridgewell/trace-mapping': 0.3.31
+ '@types/node': 22.19.1
+ chalk: 4.1.2
+ collect-v8-coverage: 1.0.3
+ exit: 0.1.2
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-instrument: 6.0.3
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 4.0.1
+ istanbul-reports: 3.2.0
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
+ jest-worker: 29.7.0
+ slash: 3.0.0
+ string-length: 4.0.2
+ strip-ansi: 6.0.1
+ v8-to-istanbul: 9.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/schemas@29.6.3':
+ dependencies:
+ '@sinclair/typebox': 0.27.8
+
+ '@jest/source-map@29.6.3':
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ callsites: 3.1.0
+ graceful-fs: 4.2.11
+
+ '@jest/test-result@29.7.0':
+ dependencies:
+ '@jest/console': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/istanbul-lib-coverage': 2.0.6
+ collect-v8-coverage: 1.0.3
+
+ '@jest/test-sequencer@29.7.0':
+ dependencies:
+ '@jest/test-result': 29.7.0
+ graceful-fs: 4.2.11
+ jest-haste-map: 29.7.0
+ slash: 3.0.0
+
+ '@jest/transform@29.7.0':
+ dependencies:
+ '@babel/core': 7.28.5
+ '@jest/types': 29.6.3
+ '@jridgewell/trace-mapping': 0.3.31
+ babel-plugin-istanbul: 6.1.1
+ chalk: 4.1.2
+ convert-source-map: 2.0.0
+ fast-json-stable-stringify: 2.1.0
+ graceful-fs: 4.2.11
+ jest-haste-map: 29.7.0
+ jest-regex-util: 29.6.3
+ jest-util: 29.7.0
+ micromatch: 4.0.8
+ pirates: 4.0.7
+ slash: 3.0.0
+ write-file-atomic: 4.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/types@29.6.3':
+ dependencies:
+ '@jest/schemas': 29.6.3
+ '@types/istanbul-lib-coverage': 2.0.6
+ '@types/istanbul-reports': 3.0.4
+ '@types/node': 22.19.1
+ '@types/yargs': 17.0.35
+ chalk: 4.1.2
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@jridgewell/trace-mapping@0.3.9':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@noble/curves@1.2.0':
+ dependencies:
+ '@noble/hashes': 1.3.2
+
+ '@noble/curves@1.4.2':
+ dependencies:
+ '@noble/hashes': 1.4.0
+
+ '@noble/curves@1.8.2':
+ dependencies:
+ '@noble/hashes': 1.7.2
+
+ '@noble/hashes@1.2.0': {}
+
+ '@noble/hashes@1.3.2': {}
+
+ '@noble/hashes@1.4.0': {}
+
+ '@noble/hashes@1.7.2': {}
+
+ '@noble/hashes@1.8.0': {}
+
+ '@noble/secp256k1@1.7.1': {}
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.19.1
+
+ '@nomicfoundation/edr-darwin-arm64@0.12.0-next.16': {}
+
+ '@nomicfoundation/edr-darwin-x64@0.12.0-next.16': {}
+
+ '@nomicfoundation/edr-linux-arm64-gnu@0.12.0-next.16': {}
+
+ '@nomicfoundation/edr-linux-arm64-musl@0.12.0-next.16': {}
+
+ '@nomicfoundation/edr-linux-x64-gnu@0.12.0-next.16': {}
+
+ '@nomicfoundation/edr-linux-x64-musl@0.12.0-next.16': {}
+
+ '@nomicfoundation/edr-win32-x64-msvc@0.12.0-next.16': {}
+
+ '@nomicfoundation/edr@0.12.0-next.16':
+ dependencies:
+ '@nomicfoundation/edr-darwin-arm64': 0.12.0-next.16
+ '@nomicfoundation/edr-darwin-x64': 0.12.0-next.16
+ '@nomicfoundation/edr-linux-arm64-gnu': 0.12.0-next.16
+ '@nomicfoundation/edr-linux-arm64-musl': 0.12.0-next.16
+ '@nomicfoundation/edr-linux-x64-gnu': 0.12.0-next.16
+ '@nomicfoundation/edr-linux-x64-musl': 0.12.0-next.16
+ '@nomicfoundation/edr-win32-x64-msvc': 0.12.0-next.16
+
+ '@nomicfoundation/hardhat-chai-matchers@2.1.0(@nomicfoundation/hardhat-ethers@3.1.2(ethers@6.15.0)(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)))(chai@4.5.0)(ethers@6.15.0)(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))':
+ dependencies:
+ '@nomicfoundation/hardhat-ethers': 3.1.2(ethers@6.15.0)(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))
+ '@types/chai-as-promised': 7.1.8
+ chai: 4.5.0
+ chai-as-promised: 7.1.2(chai@4.5.0)
+ deep-eql: 4.1.4
+ ethers: 6.15.0
+ hardhat: 2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)
+ ordinal: 1.0.3
+
+ '@nomicfoundation/hardhat-ethers@3.1.2(ethers@6.15.0)(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))':
+ dependencies:
+ debug: 4.4.3(supports-color@8.1.1)
+ ethers: 6.15.0
+ hardhat: 2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)
+ lodash.isequal: 4.5.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@nomicfoundation/hardhat-network-helpers@1.1.2(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))':
+ dependencies:
+ ethereumjs-util: 7.1.5
+ hardhat: 2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)
+
+ '@nomicfoundation/hardhat-toolbox@4.0.0(2e5368d32e9c0f5e1ee8a2b58e01fa08)':
+ dependencies:
+ '@nomicfoundation/hardhat-chai-matchers': 2.1.0(@nomicfoundation/hardhat-ethers@3.1.2(ethers@6.15.0)(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)))(chai@4.5.0)(ethers@6.15.0)(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))
+ '@nomicfoundation/hardhat-ethers': 3.1.2(ethers@6.15.0)(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))
+ '@nomicfoundation/hardhat-network-helpers': 1.1.2(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))
+ '@nomicfoundation/hardhat-verify': 2.1.3(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))
+ '@typechain/ethers-v6': 0.5.1(ethers@6.15.0)(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3)
+ '@typechain/hardhat': 9.1.0(@typechain/ethers-v6@0.5.1(ethers@6.15.0)(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3))(ethers@6.15.0)(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))(typechain@8.3.2(typescript@5.9.3))
+ '@types/chai': 4.3.20
+ '@types/mocha': 10.0.10
+ '@types/node': 22.19.1
+ chai: 4.5.0
+ ethers: 6.15.0
+ hardhat: 2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)
+ hardhat-gas-reporter: 1.0.10(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))
+ solidity-coverage: 0.8.16(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))
+ ts-node: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
+ typechain: 8.3.2(typescript@5.9.3)
+ typescript: 5.9.3
+
+ '@nomicfoundation/hardhat-verify@2.1.3(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))':
+ dependencies:
+ '@ethersproject/abi': 5.8.0
+ '@ethersproject/address': 5.8.0
+ cbor: 8.1.0
+ debug: 4.4.3(supports-color@8.1.1)
+ hardhat: 2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)
+ lodash.clonedeep: 4.5.0
+ picocolors: 1.1.1
+ semver: 6.3.1
+ table: 6.9.0
+ undici: 5.29.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.2':
+ optional: true
+
+ '@nomicfoundation/solidity-analyzer-darwin-x64@0.1.2':
+ optional: true
+
+ '@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.2':
+ optional: true
+
+ '@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.2':
+ optional: true
+
+ '@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.2':
+ optional: true
+
+ '@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.2':
+ optional: true
+
+ '@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.2':
+ optional: true
+
+ '@nomicfoundation/solidity-analyzer@0.1.2':
+ optionalDependencies:
+ '@nomicfoundation/solidity-analyzer-darwin-arm64': 0.1.2
+ '@nomicfoundation/solidity-analyzer-darwin-x64': 0.1.2
+ '@nomicfoundation/solidity-analyzer-linux-arm64-gnu': 0.1.2
+ '@nomicfoundation/solidity-analyzer-linux-arm64-musl': 0.1.2
+ '@nomicfoundation/solidity-analyzer-linux-x64-gnu': 0.1.2
+ '@nomicfoundation/solidity-analyzer-linux-x64-musl': 0.1.2
+ '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.2
+
+ '@openzeppelin/contracts@5.4.0': {}
+
+ '@pythnetwork/entropy-sdk-solidity@2.2.1': {}
+
+ '@rollup/rollup-android-arm-eabi@4.53.3':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.53.3':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.53.3':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.53.3':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.53.3':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.53.3':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.53.3':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.53.3':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.53.3':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.53.3':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.53.3':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.53.3':
+ optional: true
+
+ '@scure/base@1.1.9': {}
+
+ '@scure/base@1.2.6': {}
+
+ '@scure/bip32@1.1.5':
+ dependencies:
+ '@noble/hashes': 1.2.0
+ '@noble/secp256k1': 1.7.1
+ '@scure/base': 1.1.9
+
+ '@scure/bip32@1.4.0':
+ dependencies:
+ '@noble/curves': 1.4.2
+ '@noble/hashes': 1.4.0
+ '@scure/base': 1.1.9
+
+ '@scure/bip39@1.1.1':
+ dependencies:
+ '@noble/hashes': 1.2.0
+ '@scure/base': 1.1.9
+
+ '@scure/bip39@1.3.0':
+ dependencies:
+ '@noble/hashes': 1.4.0
+ '@scure/base': 1.1.9
+
+ '@sentry/core@5.30.0':
+ dependencies:
+ '@sentry/hub': 5.30.0
+ '@sentry/minimal': 5.30.0
+ '@sentry/types': 5.30.0
+ '@sentry/utils': 5.30.0
+ tslib: 1.14.1
+
+ '@sentry/hub@5.30.0':
+ dependencies:
+ '@sentry/types': 5.30.0
+ '@sentry/utils': 5.30.0
+ tslib: 1.14.1
+
+ '@sentry/minimal@5.30.0':
+ dependencies:
+ '@sentry/hub': 5.30.0
+ '@sentry/types': 5.30.0
+ tslib: 1.14.1
+
+ '@sentry/node@5.30.0':
+ dependencies:
+ '@sentry/core': 5.30.0
+ '@sentry/hub': 5.30.0
+ '@sentry/tracing': 5.30.0
+ '@sentry/types': 5.30.0
+ '@sentry/utils': 5.30.0
+ cookie: 0.4.2
+ https-proxy-agent: 5.0.1
+ lru_map: 0.3.3
+ tslib: 1.14.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@sentry/tracing@5.30.0':
+ dependencies:
+ '@sentry/hub': 5.30.0
+ '@sentry/minimal': 5.30.0
+ '@sentry/types': 5.30.0
+ '@sentry/utils': 5.30.0
+ tslib: 1.14.1
+
+ '@sentry/types@5.30.0': {}
+
+ '@sentry/utils@5.30.0':
+ dependencies:
+ '@sentry/types': 5.30.0
+ tslib: 1.14.1
+
+ '@sinclair/typebox@0.27.8': {}
+
+ '@sinonjs/commons@3.0.1':
+ dependencies:
+ type-detect: 4.0.8
+
+ '@sinonjs/fake-timers@10.3.0':
+ dependencies:
+ '@sinonjs/commons': 3.0.1
+
+ '@solidity-parser/parser@0.14.5':
+ dependencies:
+ antlr4ts: 0.5.0-alpha.4
+
+ '@solidity-parser/parser@0.20.2': {}
+
+ '@tsconfig/node10@1.0.12': {}
+
+ '@tsconfig/node12@1.0.11': {}
+
+ '@tsconfig/node14@1.0.3': {}
+
+ '@tsconfig/node16@1.0.4': {}
+
+ '@typechain/ethers-v6@0.5.1(ethers@6.15.0)(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3)':
+ dependencies:
+ ethers: 6.15.0
+ lodash: 4.17.21
+ ts-essentials: 7.0.3(typescript@5.9.3)
+ typechain: 8.3.2(typescript@5.9.3)
+ typescript: 5.9.3
+
+ '@typechain/hardhat@9.1.0(@typechain/ethers-v6@0.5.1(ethers@6.15.0)(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3))(ethers@6.15.0)(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3))(typechain@8.3.2(typescript@5.9.3))':
+ dependencies:
+ '@typechain/ethers-v6': 0.5.1(ethers@6.15.0)(typechain@8.3.2(typescript@5.9.3))(typescript@5.9.3)
+ ethers: 6.15.0
+ fs-extra: 9.1.0
+ hardhat: 2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)
+ typechain: 8.3.2(typescript@5.9.3)
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.28.0
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@types/bn.js@5.2.0':
+ dependencies:
+ '@types/node': 22.19.1
+
+ '@types/chai-as-promised@7.1.8':
+ dependencies:
+ '@types/chai': 4.3.20
+
+ '@types/chai@4.3.20': {}
+
+ '@types/concat-stream@1.6.1':
+ dependencies:
+ '@types/node': 22.19.1
+
+ '@types/estree@1.0.8': {}
+
+ '@types/form-data@0.0.33':
+ dependencies:
+ '@types/node': 22.19.1
+
+ '@types/glob@7.2.0':
+ dependencies:
+ '@types/minimatch': 6.0.0
+ '@types/node': 22.19.1
+
+ '@types/graceful-fs@4.1.9':
+ dependencies:
+ '@types/node': 22.19.1
+
+ '@types/istanbul-lib-coverage@2.0.6': {}
+
+ '@types/istanbul-lib-report@3.0.3':
+ dependencies:
+ '@types/istanbul-lib-coverage': 2.0.6
+
+ '@types/istanbul-reports@3.0.4':
+ dependencies:
+ '@types/istanbul-lib-report': 3.0.3
+
+ '@types/jest@29.5.14':
+ dependencies:
+ expect: 29.7.0
+ pretty-format: 29.7.0
+
+ '@types/minimatch@6.0.0':
+ dependencies:
+ minimatch: 10.1.1
+
+ '@types/mocha@10.0.10': {}
+
+ '@types/node@10.17.60': {}
+
+ '@types/node@22.19.1':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@types/node@22.7.5':
+ dependencies:
+ undici-types: 6.19.8
+
+ '@types/node@8.10.66': {}
+
+ '@types/pbkdf2@3.1.2':
+ dependencies:
+ '@types/node': 22.19.1
+
+ '@types/prettier@2.7.3': {}
+
+ '@types/qs@6.14.0': {}
+
+ '@types/secp256k1@4.0.7':
+ dependencies:
+ '@types/node': 22.19.1
+
+ '@types/stack-utils@2.0.3': {}
+
+ '@types/ws@8.18.1':
+ dependencies:
+ '@types/node': 22.19.1
+
+ '@types/yargs-parser@21.0.3': {}
+
+ '@types/yargs@17.0.35':
+ dependencies:
+ '@types/yargs-parser': 21.0.3
+
+ abbrev@1.0.9: {}
+
+ acorn-walk@8.3.4:
+ dependencies:
+ acorn: 8.15.0
+
+ acorn@8.15.0: {}
+
+ adm-zip@0.4.16: {}
+
+ aes-js@3.0.0: {}
+
+ aes-js@4.0.0-beta.5: {}
+
+ agent-base@6.0.2:
+ dependencies:
+ debug: 4.4.3(supports-color@8.1.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ aggregate-error@3.1.0:
+ dependencies:
+ clean-stack: 2.2.0
+ indent-string: 4.0.0
+
+ ajv@8.17.1:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-uri: 3.1.0
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+
+ amdefine@1.0.1:
+ optional: true
+
+ ansi-align@3.0.1:
+ dependencies:
+ string-width: 4.2.3
+
+ ansi-colors@4.1.3: {}
+
+ ansi-escapes@4.3.2:
+ dependencies:
+ type-fest: 0.21.3
+
+ ansi-regex@3.0.1: {}
+
+ ansi-regex@5.0.1: {}
+
+ ansi-styles@3.2.1:
+ dependencies:
+ color-convert: 1.9.3
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@5.2.0: {}
+
+ antlr4ts@0.5.0-alpha.4: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ arg@4.1.3: {}
+
+ argparse@1.0.10:
+ dependencies:
+ sprintf-js: 1.0.3
+
+ argparse@2.0.1: {}
+
+ array-back@3.1.0: {}
+
+ array-back@4.0.2: {}
+
+ array-union@2.1.0: {}
+
+ array-uniq@1.0.3: {}
+
+ asap@2.0.6: {}
+
+ assertion-error@1.1.0: {}
+
+ astral-regex@2.0.0: {}
+
+ async@1.5.2: {}
+
+ asynckit@0.4.0: {}
+
+ at-least-node@1.0.0: {}
+
+ available-typed-arrays@1.0.7:
+ dependencies:
+ possible-typed-array-names: 1.1.0
+
+ axios@1.13.2:
+ dependencies:
+ follow-redirects: 1.15.11(debug@4.4.3)
+ form-data: 4.0.5
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
+ babel-jest@29.7.0(@babel/core@7.28.5):
+ dependencies:
+ '@babel/core': 7.28.5
+ '@jest/transform': 29.7.0
+ '@types/babel__core': 7.20.5
+ babel-plugin-istanbul: 6.1.1
+ babel-preset-jest: 29.6.3(@babel/core@7.28.5)
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ slash: 3.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-istanbul@6.1.1:
+ dependencies:
+ '@babel/helper-plugin-utils': 7.27.1
+ '@istanbuljs/load-nyc-config': 1.1.0
+ '@istanbuljs/schema': 0.1.3
+ istanbul-lib-instrument: 5.2.1
+ test-exclude: 6.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-jest-hoist@29.6.3:
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.5
+ '@types/babel__core': 7.20.5
+ '@types/babel__traverse': 7.28.0
+
+ babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.5):
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.5)
+ '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.5)
+ '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.5)
+ '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.5)
+ '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5)
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.5)
+ '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.5)
+ '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.5)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.5)
+ '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.5)
+ '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.5)
+ '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.5)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.5)
+ '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.5)
+ '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.5)
+
+ babel-preset-jest@29.6.3(@babel/core@7.28.5):
+ dependencies:
+ '@babel/core': 7.28.5
+ babel-plugin-jest-hoist: 29.6.3
+ babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5)
+
+ balanced-match@1.0.2: {}
+
+ base-x@3.0.11:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ baseline-browser-mapping@2.8.30: {}
+
+ bech32@1.1.4: {}
+
+ binary-extensions@2.3.0: {}
+
+ blakejs@1.2.1: {}
+
+ bn.js@4.11.6: {}
+
+ bn.js@4.12.2: {}
+
+ bn.js@5.2.2: {}
+
+ boxen@5.1.2:
+ dependencies:
+ ansi-align: 3.0.1
+ camelcase: 6.3.0
+ chalk: 4.1.2
+ cli-boxes: 2.2.1
+ string-width: 4.2.3
+ type-fest: 0.20.2
+ widest-line: 3.1.0
+ wrap-ansi: 7.0.0
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.2:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ brorand@1.1.0: {}
+
+ browser-stdout@1.3.1: {}
+
+ browserify-aes@1.2.0:
+ dependencies:
+ buffer-xor: 1.0.3
+ cipher-base: 1.0.7
+ create-hash: 1.2.0
+ evp_bytestokey: 1.0.3
+ inherits: 2.0.4
+ safe-buffer: 5.2.1
+
+ browserslist@4.28.0:
+ dependencies:
+ baseline-browser-mapping: 2.8.30
+ caniuse-lite: 1.0.30001756
+ electron-to-chromium: 1.5.259
+ node-releases: 2.0.27
+ update-browserslist-db: 1.1.4(browserslist@4.28.0)
+
+ bs-logger@0.2.6:
+ dependencies:
+ fast-json-stable-stringify: 2.1.0
+
+ bs58@4.0.1:
+ dependencies:
+ base-x: 3.0.11
+
+ bs58check@2.1.2:
+ dependencies:
+ bs58: 4.0.1
+ create-hash: 1.2.0
+ safe-buffer: 5.2.1
+
+ bser@2.1.1:
+ dependencies:
+ node-int64: 0.4.0
+
+ buffer-from@1.1.2: {}
+
+ buffer-xor@1.0.3: {}
+
+ bytes@3.1.2: {}
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bind@1.0.8:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ get-intrinsic: 1.3.0
+ set-function-length: 1.2.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.0
+
+ callsites@3.1.0: {}
+
+ camelcase@5.3.1: {}
+
+ camelcase@6.3.0: {}
+
+ caniuse-lite@1.0.30001756: {}
+
+ caseless@0.12.0: {}
+
+ cbor@8.1.0:
+ dependencies:
+ nofilter: 3.1.0
+
+ chai-as-promised@7.1.2(chai@4.5.0):
+ dependencies:
+ chai: 4.5.0
+ check-error: 1.0.3
+
+ chai@4.5.0:
+ dependencies:
+ assertion-error: 1.1.0
+ check-error: 1.0.3
+ deep-eql: 4.1.4
+ get-func-name: 2.0.2
+ loupe: 2.3.7
+ pathval: 1.1.1
+ type-detect: 4.1.0
+
+ chalk@2.4.2:
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ char-regex@1.0.2: {}
+
+ charenc@0.0.2: {}
+
+ check-error@1.0.3:
+ dependencies:
+ get-func-name: 2.0.2
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.2
+
+ ci-info@2.0.0: {}
+
+ ci-info@3.9.0: {}
+
+ cipher-base@1.0.7:
+ dependencies:
+ inherits: 2.0.4
+ safe-buffer: 5.2.1
+ to-buffer: 1.2.2
+
+ cjs-module-lexer@1.4.3: {}
+
+ clean-stack@2.2.0: {}
+
+ cli-boxes@2.2.1: {}
+
+ cli-table3@0.5.1:
+ dependencies:
+ object-assign: 4.1.1
+ string-width: 2.1.1
+ optionalDependencies:
+ colors: 1.4.0
+
+ cliui@7.0.4:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ co@4.6.0: {}
+
+ collect-v8-coverage@1.0.3: {}
+
+ color-convert@1.9.3:
+ dependencies:
+ color-name: 1.1.3
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.3: {}
+
+ color-name@1.1.4: {}
+
+ colors@1.4.0: {}
+
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
+ command-exists@1.2.9: {}
+
+ command-line-args@5.2.1:
+ dependencies:
+ array-back: 3.1.0
+ find-replace: 3.0.0
+ lodash.camelcase: 4.3.0
+ typical: 4.0.0
+
+ command-line-usage@6.1.3:
+ dependencies:
+ array-back: 4.0.2
+ chalk: 2.4.2
+ table-layout: 1.0.2
+ typical: 5.2.0
+
+ commander@8.3.0: {}
+
+ concat-map@0.0.1: {}
+
+ concat-stream@1.6.2:
+ dependencies:
+ buffer-from: 1.1.2
+ inherits: 2.0.4
+ readable-stream: 2.3.8
+ typedarray: 0.0.6
+
+ convert-source-map@2.0.0: {}
+
+ cookie@0.4.2: {}
+
+ core-util-is@1.0.3: {}
+
+ create-hash@1.2.0:
+ dependencies:
+ cipher-base: 1.0.7
+ inherits: 2.0.4
+ md5.js: 1.3.5
+ ripemd160: 2.0.3
+ sha.js: 2.4.12
+
+ create-hmac@1.1.7:
+ dependencies:
+ cipher-base: 1.0.7
+ create-hash: 1.2.0
+ inherits: 2.0.4
+ ripemd160: 2.0.3
+ safe-buffer: 5.2.1
+ sha.js: 2.4.12
+
+ create-jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
+ dependencies:
+ '@jest/types': 29.6.3
+ chalk: 4.1.2
+ exit: 0.1.2
+ graceful-fs: 4.2.11
+ jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
+ jest-util: 29.7.0
+ prompts: 2.4.2
+ transitivePeerDependencies:
+ - '@types/node'
+ - babel-plugin-macros
+ - supports-color
+ - ts-node
+
+ create-require@1.1.1: {}
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ crypt@0.0.2: {}
+
+ death@1.1.0: {}
+
+ debug@4.4.3(supports-color@8.1.1):
+ dependencies:
+ ms: 2.1.3
+ optionalDependencies:
+ supports-color: 8.1.1
+
+ decamelize@4.0.0: {}
+
+ dedent@1.7.0: {}
+
+ deep-eql@4.1.4:
+ dependencies:
+ type-detect: 4.1.0
+
+ deep-extend@0.6.0: {}
+
+ deep-is@0.1.4: {}
+
+ deepmerge@4.3.1: {}
+
+ define-data-property@1.1.4:
+ dependencies:
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ delayed-stream@1.0.0: {}
+
+ depd@2.0.0: {}
+
+ detect-newline@3.1.0: {}
+
+ diff-sequences@29.6.3: {}
+
+ diff@4.0.2: {}
+
+ diff@5.2.0: {}
+
+ difflib@0.2.4:
+ dependencies:
+ heap: 0.2.7
+
+ dir-glob@3.0.1:
+ dependencies:
+ path-type: 4.0.0
+
+ dotenv@16.6.1: {}
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ electron-to-chromium@1.5.259: {}
+
+ elliptic@6.6.1:
+ dependencies:
+ bn.js: 4.12.2
+ brorand: 1.1.0
+ hash.js: 1.1.7
+ hmac-drbg: 1.0.1
+ inherits: 2.0.4
+ minimalistic-assert: 1.0.1
+ minimalistic-crypto-utils: 1.0.1
+
+ emittery@0.13.1: {}
+
+ emoji-regex@8.0.0: {}
+
+ enquirer@2.4.1:
+ dependencies:
+ ansi-colors: 4.1.3
+ strip-ansi: 6.0.1
+
+ env-paths@2.2.1: {}
+
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ esbuild@0.21.5:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.21.5
+ '@esbuild/android-arm': 0.21.5
+ '@esbuild/android-arm64': 0.21.5
+ '@esbuild/android-x64': 0.21.5
+ '@esbuild/darwin-arm64': 0.21.5
+ '@esbuild/darwin-x64': 0.21.5
+ '@esbuild/freebsd-arm64': 0.21.5
+ '@esbuild/freebsd-x64': 0.21.5
+ '@esbuild/linux-arm': 0.21.5
+ '@esbuild/linux-arm64': 0.21.5
+ '@esbuild/linux-ia32': 0.21.5
+ '@esbuild/linux-loong64': 0.21.5
+ '@esbuild/linux-mips64el': 0.21.5
+ '@esbuild/linux-ppc64': 0.21.5
+ '@esbuild/linux-riscv64': 0.21.5
+ '@esbuild/linux-s390x': 0.21.5
+ '@esbuild/linux-x64': 0.21.5
+ '@esbuild/netbsd-x64': 0.21.5
+ '@esbuild/openbsd-x64': 0.21.5
+ '@esbuild/sunos-x64': 0.21.5
+ '@esbuild/win32-arm64': 0.21.5
+ '@esbuild/win32-ia32': 0.21.5
+ '@esbuild/win32-x64': 0.21.5
+
+ esbuild@0.25.12:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.12
+ '@esbuild/android-arm': 0.25.12
+ '@esbuild/android-arm64': 0.25.12
+ '@esbuild/android-x64': 0.25.12
+ '@esbuild/darwin-arm64': 0.25.12
+ '@esbuild/darwin-x64': 0.25.12
+ '@esbuild/freebsd-arm64': 0.25.12
+ '@esbuild/freebsd-x64': 0.25.12
+ '@esbuild/linux-arm': 0.25.12
+ '@esbuild/linux-arm64': 0.25.12
+ '@esbuild/linux-ia32': 0.25.12
+ '@esbuild/linux-loong64': 0.25.12
+ '@esbuild/linux-mips64el': 0.25.12
+ '@esbuild/linux-ppc64': 0.25.12
+ '@esbuild/linux-riscv64': 0.25.12
+ '@esbuild/linux-s390x': 0.25.12
+ '@esbuild/linux-x64': 0.25.12
+ '@esbuild/netbsd-arm64': 0.25.12
+ '@esbuild/netbsd-x64': 0.25.12
+ '@esbuild/openbsd-arm64': 0.25.12
+ '@esbuild/openbsd-x64': 0.25.12
+ '@esbuild/openharmony-arm64': 0.25.12
+ '@esbuild/sunos-x64': 0.25.12
+ '@esbuild/win32-arm64': 0.25.12
+ '@esbuild/win32-ia32': 0.25.12
+ '@esbuild/win32-x64': 0.25.12
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@1.0.5: {}
+
+ escape-string-regexp@2.0.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ escodegen@1.8.1:
+ dependencies:
+ esprima: 2.7.3
+ estraverse: 1.9.3
+ esutils: 2.0.3
+ optionator: 0.8.3
+ optionalDependencies:
+ source-map: 0.2.0
+
+ esprima@2.7.3: {}
+
+ esprima@4.0.1: {}
+
+ estraverse@1.9.3: {}
+
+ esutils@2.0.3: {}
+
+ eth-gas-reporter@0.2.27:
+ dependencies:
+ '@solidity-parser/parser': 0.14.5
+ axios: 1.13.2
+ cli-table3: 0.5.1
+ colors: 1.4.0
+ ethereum-cryptography: 1.2.0
+ ethers: 5.8.0
+ fs-readdir-recursive: 1.1.0
+ lodash: 4.17.21
+ markdown-table: 1.1.3
+ mocha: 10.8.2
+ req-cwd: 2.0.0
+ sha1: 1.1.1
+ sync-request: 6.1.0
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - utf-8-validate
+
+ ethereum-bloom-filters@1.2.0:
+ dependencies:
+ '@noble/hashes': 1.8.0
+
+ ethereum-cryptography@0.1.3:
+ dependencies:
+ '@types/pbkdf2': 3.1.2
+ '@types/secp256k1': 4.0.7
+ blakejs: 1.2.1
+ browserify-aes: 1.2.0
+ bs58check: 2.1.2
+ create-hash: 1.2.0
+ create-hmac: 1.1.7
+ hash.js: 1.1.7
+ keccak: 3.0.4
+ pbkdf2: 3.1.5
+ randombytes: 2.1.0
+ safe-buffer: 5.2.1
+ scrypt-js: 3.0.1
+ secp256k1: 4.0.4
+ setimmediate: 1.0.5
+
+ ethereum-cryptography@1.2.0:
+ dependencies:
+ '@noble/hashes': 1.2.0
+ '@noble/secp256k1': 1.7.1
+ '@scure/bip32': 1.1.5
+ '@scure/bip39': 1.1.1
+
+ ethereum-cryptography@2.2.1:
+ dependencies:
+ '@noble/curves': 1.4.2
+ '@noble/hashes': 1.4.0
+ '@scure/bip32': 1.4.0
+ '@scure/bip39': 1.3.0
+
+ ethereumjs-util@7.1.5:
+ dependencies:
+ '@types/bn.js': 5.2.0
+ bn.js: 5.2.2
+ create-hash: 1.2.0
+ ethereum-cryptography: 0.1.3
+ rlp: 2.2.7
+
+ ethers@5.8.0:
+ dependencies:
+ '@ethersproject/abi': 5.8.0
+ '@ethersproject/abstract-provider': 5.8.0
+ '@ethersproject/abstract-signer': 5.8.0
+ '@ethersproject/address': 5.8.0
+ '@ethersproject/base64': 5.8.0
+ '@ethersproject/basex': 5.8.0
+ '@ethersproject/bignumber': 5.8.0
+ '@ethersproject/bytes': 5.8.0
+ '@ethersproject/constants': 5.8.0
+ '@ethersproject/contracts': 5.8.0
+ '@ethersproject/hash': 5.8.0
+ '@ethersproject/hdnode': 5.8.0
+ '@ethersproject/json-wallets': 5.8.0
+ '@ethersproject/keccak256': 5.8.0
+ '@ethersproject/logger': 5.8.0
+ '@ethersproject/networks': 5.8.0
+ '@ethersproject/pbkdf2': 5.8.0
+ '@ethersproject/properties': 5.8.0
+ '@ethersproject/providers': 5.8.0
+ '@ethersproject/random': 5.8.0
+ '@ethersproject/rlp': 5.8.0
+ '@ethersproject/sha2': 5.8.0
+ '@ethersproject/signing-key': 5.8.0
+ '@ethersproject/solidity': 5.8.0
+ '@ethersproject/strings': 5.8.0
+ '@ethersproject/transactions': 5.8.0
+ '@ethersproject/units': 5.8.0
+ '@ethersproject/wallet': 5.8.0
+ '@ethersproject/web': 5.8.0
+ '@ethersproject/wordlists': 5.8.0
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ ethers@6.15.0:
+ dependencies:
+ '@adraffy/ens-normalize': 1.10.1
+ '@noble/curves': 1.2.0
+ '@noble/hashes': 1.3.2
+ '@types/node': 22.7.5
+ aes-js: 4.0.0-beta.5
+ tslib: 2.7.0
+ ws: 8.17.1
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ ethjs-unit@0.1.6:
+ dependencies:
+ bn.js: 4.11.6
+ number-to-bn: 1.7.0
+
+ evp_bytestokey@1.0.3:
+ dependencies:
+ md5.js: 1.3.5
+ safe-buffer: 5.2.1
+
+ execa@5.1.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 6.0.1
+ human-signals: 2.1.0
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+ strip-final-newline: 2.0.0
+
+ exit@0.1.2: {}
+
+ expect@29.7.0:
+ dependencies:
+ '@jest/expect-utils': 29.7.0
+ jest-get-type: 29.6.3
+ jest-matcher-utils: 29.7.0
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fast-uri@3.1.0: {}
+
+ fastq@1.19.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fb-watchman@2.0.2:
+ dependencies:
+ bser: 2.1.1
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-replace@3.0.0:
+ dependencies:
+ array-back: 3.1.0
+
+ find-up@4.1.0:
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat@5.0.2: {}
+
+ follow-redirects@1.15.11(debug@4.4.3):
+ optionalDependencies:
+ debug: 4.4.3(supports-color@8.1.1)
+
+ for-each@0.3.5:
+ dependencies:
+ is-callable: 1.2.7
+
+ form-data@2.5.5:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ hasown: 2.0.2
+ mime-types: 2.1.35
+ safe-buffer: 5.2.1
+
+ form-data@4.0.5:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ hasown: 2.0.2
+ mime-types: 2.1.35
+
+ fp-ts@1.19.3: {}
+
+ fs-extra@7.0.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 4.0.0
+ universalify: 0.1.2
+
+ fs-extra@8.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 4.0.0
+ universalify: 0.1.2
+
+ fs-extra@9.1.0:
+ dependencies:
+ at-least-node: 1.0.0
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
+ fs-readdir-recursive@1.1.0: {}
+
+ fs.realpath@1.0.0: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ gensync@1.0.0-beta.2: {}
+
+ get-caller-file@2.0.5: {}
+
+ get-func-name@2.0.2: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-package-type@0.1.0: {}
+
+ get-port@3.2.0: {}
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-stream@6.0.1: {}
+
+ get-tsconfig@4.13.0:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ ghost-testrpc@0.0.2:
+ dependencies:
+ chalk: 2.4.2
+ node-emoji: 1.11.0
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@5.0.15:
+ dependencies:
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ glob@7.1.7:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ glob@8.1.0:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 5.1.6
+ once: 1.4.0
+
+ global-modules@2.0.0:
+ dependencies:
+ global-prefix: 3.0.0
+
+ global-prefix@3.0.0:
+ dependencies:
+ ini: 1.3.8
+ kind-of: 6.0.3
+ which: 1.3.1
+
+ globby@10.0.2:
+ dependencies:
+ '@types/glob': 7.2.0
+ array-union: 2.1.0
+ dir-glob: 3.0.1
+ fast-glob: 3.3.3
+ glob: 7.2.3
+ ignore: 5.3.2
+ merge2: 1.4.1
+ slash: 3.0.0
+
+ gopd@1.2.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ handlebars@4.7.8:
+ dependencies:
+ minimist: 1.2.8
+ neo-async: 2.6.2
+ source-map: 0.6.1
+ wordwrap: 1.0.0
+ optionalDependencies:
+ uglify-js: 3.19.3
+
+ hardhat-gas-reporter@1.0.10(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)):
+ dependencies:
+ array-uniq: 1.0.3
+ eth-gas-reporter: 0.2.27
+ hardhat: 2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)
+ sha1: 1.1.1
+ transitivePeerDependencies:
+ - '@codechecks/client'
+ - bufferutil
+ - debug
+ - utf-8-validate
+
+ hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3):
+ dependencies:
+ '@ethereumjs/util': 9.1.0
+ '@ethersproject/abi': 5.8.0
+ '@nomicfoundation/edr': 0.12.0-next.16
+ '@nomicfoundation/solidity-analyzer': 0.1.2
+ '@sentry/node': 5.30.0
+ adm-zip: 0.4.16
+ aggregate-error: 3.1.0
+ ansi-escapes: 4.3.2
+ boxen: 5.1.2
+ chokidar: 4.0.3
+ ci-info: 2.0.0
+ debug: 4.4.3(supports-color@8.1.1)
+ enquirer: 2.4.1
+ env-paths: 2.2.1
+ ethereum-cryptography: 1.2.0
+ find-up: 5.0.0
+ fp-ts: 1.19.3
+ fs-extra: 7.0.1
+ immutable: 4.3.7
+ io-ts: 1.10.4
+ json-stream-stringify: 3.1.6
+ keccak: 3.0.4
+ lodash: 4.17.21
+ micro-eth-signer: 0.14.0
+ mnemonist: 0.38.5
+ mocha: 10.8.2
+ p-map: 4.0.0
+ picocolors: 1.1.1
+ raw-body: 2.5.2
+ resolve: 1.17.0
+ semver: 6.3.1
+ solc: 0.8.26(debug@4.4.3)
+ source-map-support: 0.5.21
+ stacktrace-parser: 0.1.11
+ tinyglobby: 0.2.15
+ tsort: 0.0.1
+ undici: 5.29.0
+ uuid: 8.3.2
+ ws: 7.5.10
+ optionalDependencies:
+ ts-node: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ has-flag@1.0.0: {}
+
+ has-flag@3.0.0: {}
+
+ has-flag@4.0.0: {}
+
+ has-property-descriptors@1.0.2:
+ dependencies:
+ es-define-property: 1.0.1
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hash-base@3.1.2:
+ dependencies:
+ inherits: 2.0.4
+ readable-stream: 2.3.8
+ safe-buffer: 5.2.1
+ to-buffer: 1.2.2
+
+ hash.js@1.1.7:
+ dependencies:
+ inherits: 2.0.4
+ minimalistic-assert: 1.0.1
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ he@1.2.0: {}
+
+ heap@0.2.7: {}
+
+ hmac-drbg@1.0.1:
+ dependencies:
+ hash.js: 1.1.7
+ minimalistic-assert: 1.0.1
+ minimalistic-crypto-utils: 1.0.1
+
+ html-escaper@2.0.2: {}
+
+ http-basic@8.1.3:
+ dependencies:
+ caseless: 0.12.0
+ concat-stream: 1.6.2
+ http-response-object: 3.0.2
+ parse-cache-control: 1.0.1
+
+ http-errors@2.0.0:
+ dependencies:
+ depd: 2.0.0
+ inherits: 2.0.4
+ setprototypeof: 1.2.0
+ statuses: 2.0.1
+ toidentifier: 1.0.1
+
+ http-response-object@3.0.2:
+ dependencies:
+ '@types/node': 10.17.60
+
+ https-proxy-agent@5.0.1:
+ dependencies:
+ agent-base: 6.0.2
+ debug: 4.4.3(supports-color@8.1.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ human-signals@2.1.0: {}
+
+ iconv-lite@0.4.24:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ignore@5.3.2: {}
+
+ immutable@4.3.7: {}
+
+ import-local@3.2.0:
+ dependencies:
+ pkg-dir: 4.2.0
+ resolve-cwd: 3.0.0
+
+ imurmurhash@0.1.4: {}
+
+ indent-string@4.0.0: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ ini@1.3.8: {}
+
+ interpret@1.4.0: {}
+
+ io-ts@1.10.4:
+ dependencies:
+ fp-ts: 1.19.3
+
+ is-arrayish@0.2.1: {}
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-callable@1.2.7: {}
+
+ is-core-module@2.16.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@2.0.0: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-generator-fn@2.1.0: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-hex-prefixed@1.0.0: {}
+
+ is-number@7.0.0: {}
+
+ is-plain-obj@2.1.0: {}
+
+ is-stream@2.0.1: {}
+
+ is-typed-array@1.1.15:
+ dependencies:
+ which-typed-array: 1.1.19
+
+ is-unicode-supported@0.1.0: {}
+
+ isarray@1.0.0: {}
+
+ isarray@2.0.5: {}
+
+ isexe@2.0.0: {}
+
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-instrument@5.2.1:
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/parser': 7.28.5
+ '@istanbuljs/schema': 0.1.3
+ istanbul-lib-coverage: 3.2.2
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-lib-instrument@6.0.3:
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/parser': 7.28.5
+ '@istanbuljs/schema': 0.1.3
+ istanbul-lib-coverage: 3.2.2
+ semver: 7.7.3
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-lib-report@3.0.1:
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+
+ istanbul-lib-source-maps@4.0.1:
+ dependencies:
+ debug: 4.4.3(supports-color@8.1.1)
+ istanbul-lib-coverage: 3.2.2
+ source-map: 0.6.1
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-reports@3.2.0:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
+ jest-changed-files@29.7.0:
+ dependencies:
+ execa: 5.1.1
+ jest-util: 29.7.0
+ p-limit: 3.1.0
+
+ jest-circus@29.7.0:
+ dependencies:
+ '@jest/environment': 29.7.0
+ '@jest/expect': 29.7.0
+ '@jest/test-result': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 22.19.1
+ chalk: 4.1.2
+ co: 4.6.0
+ dedent: 1.7.0
+ is-generator-fn: 2.1.0
+ jest-each: 29.7.0
+ jest-matcher-utils: 29.7.0
+ jest-message-util: 29.7.0
+ jest-runtime: 29.7.0
+ jest-snapshot: 29.7.0
+ jest-util: 29.7.0
+ p-limit: 3.1.0
+ pretty-format: 29.7.0
+ pure-rand: 6.1.0
+ slash: 3.0.0
+ stack-utils: 2.0.6
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+
+ jest-cli@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
+ dependencies:
+ '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
+ '@jest/test-result': 29.7.0
+ '@jest/types': 29.6.3
+ chalk: 4.1.2
+ create-jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
+ exit: 0.1.2
+ import-local: 3.2.0
+ jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
+ jest-util: 29.7.0
+ jest-validate: 29.7.0
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - '@types/node'
+ - babel-plugin-macros
+ - supports-color
+ - ts-node
+
+ jest-config@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
+ dependencies:
+ '@babel/core': 7.28.5
+ '@jest/test-sequencer': 29.7.0
+ '@jest/types': 29.6.3
+ babel-jest: 29.7.0(@babel/core@7.28.5)
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ deepmerge: 4.3.1
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ jest-circus: 29.7.0
+ jest-environment-node: 29.7.0
+ jest-get-type: 29.6.3
+ jest-regex-util: 29.6.3
+ jest-resolve: 29.7.0
+ jest-runner: 29.7.0
+ jest-util: 29.7.0
+ jest-validate: 29.7.0
+ micromatch: 4.0.8
+ parse-json: 5.2.0
+ pretty-format: 29.7.0
+ slash: 3.0.0
+ strip-json-comments: 3.1.1
+ optionalDependencies:
+ '@types/node': 22.19.1
+ ts-node: 10.9.2(@types/node@22.19.1)(typescript@5.9.3)
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+
+ jest-diff@29.7.0:
+ dependencies:
+ chalk: 4.1.2
+ diff-sequences: 29.6.3
+ jest-get-type: 29.6.3
+ pretty-format: 29.7.0
+
+ jest-docblock@29.7.0:
+ dependencies:
+ detect-newline: 3.1.0
+
+ jest-each@29.7.0:
+ dependencies:
+ '@jest/types': 29.6.3
+ chalk: 4.1.2
+ jest-get-type: 29.6.3
+ jest-util: 29.7.0
+ pretty-format: 29.7.0
+
+ jest-environment-node@29.7.0:
+ dependencies:
+ '@jest/environment': 29.7.0
+ '@jest/fake-timers': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 22.19.1
+ jest-mock: 29.7.0
+ jest-util: 29.7.0
+
+ jest-get-type@29.6.3: {}
+
+ jest-haste-map@29.7.0:
+ dependencies:
+ '@jest/types': 29.6.3
+ '@types/graceful-fs': 4.1.9
+ '@types/node': 22.19.1
+ anymatch: 3.1.3
+ fb-watchman: 2.0.2
+ graceful-fs: 4.2.11
+ jest-regex-util: 29.6.3
+ jest-util: 29.7.0
+ jest-worker: 29.7.0
+ micromatch: 4.0.8
+ walker: 1.0.8
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ jest-leak-detector@29.7.0:
+ dependencies:
+ jest-get-type: 29.6.3
+ pretty-format: 29.7.0
+
+ jest-matcher-utils@29.7.0:
+ dependencies:
+ chalk: 4.1.2
+ jest-diff: 29.7.0
+ jest-get-type: 29.6.3
+ pretty-format: 29.7.0
+
+ jest-message-util@29.7.0:
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@jest/types': 29.6.3
+ '@types/stack-utils': 2.0.3
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ micromatch: 4.0.8
+ pretty-format: 29.7.0
+ slash: 3.0.0
+ stack-utils: 2.0.6
+
+ jest-mock@29.7.0:
+ dependencies:
+ '@jest/types': 29.6.3
+ '@types/node': 22.19.1
+ jest-util: 29.7.0
+
+ jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
+ optionalDependencies:
+ jest-resolve: 29.7.0
+
+ jest-regex-util@29.6.3: {}
+
+ jest-resolve-dependencies@29.7.0:
+ dependencies:
+ jest-regex-util: 29.6.3
+ jest-snapshot: 29.7.0
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-resolve@29.7.0:
+ dependencies:
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ jest-haste-map: 29.7.0
+ jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0)
+ jest-util: 29.7.0
+ jest-validate: 29.7.0
+ resolve: 1.22.11
+ resolve.exports: 2.0.3
+ slash: 3.0.0
+
+ jest-runner@29.7.0:
+ dependencies:
+ '@jest/console': 29.7.0
+ '@jest/environment': 29.7.0
+ '@jest/test-result': 29.7.0
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 22.19.1
+ chalk: 4.1.2
+ emittery: 0.13.1
+ graceful-fs: 4.2.11
+ jest-docblock: 29.7.0
+ jest-environment-node: 29.7.0
+ jest-haste-map: 29.7.0
+ jest-leak-detector: 29.7.0
+ jest-message-util: 29.7.0
+ jest-resolve: 29.7.0
+ jest-runtime: 29.7.0
+ jest-util: 29.7.0
+ jest-watcher: 29.7.0
+ jest-worker: 29.7.0
+ p-limit: 3.1.0
+ source-map-support: 0.5.13
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-runtime@29.7.0:
+ dependencies:
+ '@jest/environment': 29.7.0
+ '@jest/fake-timers': 29.7.0
+ '@jest/globals': 29.7.0
+ '@jest/source-map': 29.6.3
+ '@jest/test-result': 29.7.0
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 22.19.1
+ chalk: 4.1.2
+ cjs-module-lexer: 1.4.3
+ collect-v8-coverage: 1.0.3
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ jest-haste-map: 29.7.0
+ jest-message-util: 29.7.0
+ jest-mock: 29.7.0
+ jest-regex-util: 29.6.3
+ jest-resolve: 29.7.0
+ jest-snapshot: 29.7.0
+ jest-util: 29.7.0
+ slash: 3.0.0
+ strip-bom: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-snapshot@29.7.0:
+ dependencies:
+ '@babel/core': 7.28.5
+ '@babel/generator': 7.28.5
+ '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5)
+ '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5)
+ '@babel/types': 7.28.5
+ '@jest/expect-utils': 29.7.0
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
+ babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5)
+ chalk: 4.1.2
+ expect: 29.7.0
+ graceful-fs: 4.2.11
+ jest-diff: 29.7.0
+ jest-get-type: 29.6.3
+ jest-matcher-utils: 29.7.0
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
+ natural-compare: 1.4.0
+ pretty-format: 29.7.0
+ semver: 7.7.3
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-util@29.7.0:
+ dependencies:
+ '@jest/types': 29.6.3
+ '@types/node': 22.19.1
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ graceful-fs: 4.2.11
+ picomatch: 2.3.1
+
+ jest-validate@29.7.0:
+ dependencies:
+ '@jest/types': 29.6.3
+ camelcase: 6.3.0
+ chalk: 4.1.2
+ jest-get-type: 29.6.3
+ leven: 3.1.0
+ pretty-format: 29.7.0
+
+ jest-watcher@29.7.0:
+ dependencies:
+ '@jest/test-result': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 22.19.1
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ emittery: 0.13.1
+ jest-util: 29.7.0
+ string-length: 4.0.2
+
+ jest-worker@29.7.0:
+ dependencies:
+ '@types/node': 22.19.1
+ jest-util: 29.7.0
+ merge-stream: 2.0.0
+ supports-color: 8.1.1
+
+ jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)):
+ dependencies:
+ '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
+ '@jest/types': 29.6.3
+ import-local: 3.2.0
+ jest-cli: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
+ transitivePeerDependencies:
+ - '@types/node'
+ - babel-plugin-macros
+ - supports-color
+ - ts-node
+
+ js-sha3@0.8.0: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@3.14.2:
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+
+ js-yaml@4.1.1:
+ dependencies:
+ argparse: 2.0.1
+
+ jsesc@3.1.0: {}
+
+ json-parse-even-better-errors@2.3.1: {}
+
+ json-schema-traverse@1.0.0: {}
+
+ json-stream-stringify@3.1.6: {}
+
+ json5@2.2.3: {}
+
+ jsonfile@4.0.0:
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ jsonfile@6.2.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ jsonschema@1.5.0: {}
+
+ keccak@3.0.4:
+ dependencies:
+ node-addon-api: 2.0.2
+ node-gyp-build: 4.8.4
+ readable-stream: 3.6.2
+
+ kind-of@6.0.3: {}
+
+ kleur@3.0.3: {}
+
+ leven@3.1.0: {}
+
+ levn@0.3.0:
+ dependencies:
+ prelude-ls: 1.1.2
+ type-check: 0.3.2
+
+ lines-and-columns@1.2.4: {}
+
+ locate-path@5.0.0:
+ dependencies:
+ p-locate: 4.1.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.camelcase@4.3.0: {}
+
+ lodash.clonedeep@4.5.0: {}
+
+ lodash.isequal@4.5.0: {}
+
+ lodash.memoize@4.1.2: {}
+
+ lodash.truncate@4.4.2: {}
+
+ lodash@4.17.21: {}
+
+ log-symbols@4.1.0:
+ dependencies:
+ chalk: 4.1.2
+ is-unicode-supported: 0.1.0
+
+ loupe@2.3.7:
+ dependencies:
+ get-func-name: 2.0.2
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ lru_map@0.3.3: {}
+
+ make-dir@4.0.0:
+ dependencies:
+ semver: 7.7.3
+
+ make-error@1.3.6: {}
+
+ makeerror@1.0.12:
+ dependencies:
+ tmpl: 1.0.5
+
+ markdown-table@1.1.3: {}
+
+ math-intrinsics@1.1.0: {}
+
+ md5.js@1.3.5:
+ dependencies:
+ hash-base: 3.1.2
+ inherits: 2.0.4
+ safe-buffer: 5.2.1
+
+ memorystream@0.3.1: {}
+
+ merge-stream@2.0.0: {}
+
+ merge2@1.4.1: {}
+
+ micro-eth-signer@0.14.0:
+ dependencies:
+ '@noble/curves': 1.8.2
+ '@noble/hashes': 1.7.2
+ micro-packed: 0.7.3
+
+ micro-ftch@0.3.1: {}
+
+ micro-packed@0.7.3:
+ dependencies:
+ '@scure/base': 1.2.6
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mime-db@1.52.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ mimic-fn@2.1.0: {}
+
+ minimalistic-assert@1.0.1: {}
+
+ minimalistic-crypto-utils@1.0.1: {}
+
+ minimatch@10.1.1:
+ dependencies:
+ '@isaacs/brace-expansion': 5.0.0
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minimatch@5.1.6:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minimist@1.2.8: {}
+
+ mkdirp@0.5.6:
+ dependencies:
+ minimist: 1.2.8
+
+ mkdirp@1.0.4: {}
+
+ mnemonist@0.38.5:
+ dependencies:
+ obliterator: 2.0.5
+
+ mocha@10.8.2:
+ dependencies:
+ ansi-colors: 4.1.3
+ browser-stdout: 1.3.1
+ chokidar: 3.6.0
+ debug: 4.4.3(supports-color@8.1.1)
+ diff: 5.2.0
+ escape-string-regexp: 4.0.0
+ find-up: 5.0.0
+ glob: 8.1.0
+ he: 1.2.0
+ js-yaml: 4.1.1
+ log-symbols: 4.1.0
+ minimatch: 5.1.6
+ ms: 2.1.3
+ serialize-javascript: 6.0.2
+ strip-json-comments: 3.1.1
+ supports-color: 8.1.1
+ workerpool: 6.5.1
+ yargs: 16.2.0
+ yargs-parser: 20.2.9
+ yargs-unparser: 2.0.0
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.11: {}
+
+ natural-compare@1.4.0: {}
+
+ neo-async@2.6.2: {}
+
+ node-addon-api@2.0.2: {}
+
+ node-addon-api@5.1.0: {}
+
+ node-emoji@1.11.0:
+ dependencies:
+ lodash: 4.17.21
+
+ node-gyp-build@4.8.4: {}
+
+ node-int64@0.4.0: {}
+
+ node-releases@2.0.27: {}
+
+ nofilter@3.1.0: {}
+
+ nopt@3.0.6:
+ dependencies:
+ abbrev: 1.0.9
+
+ normalize-path@3.0.0: {}
+
+ npm-run-path@4.0.1:
+ dependencies:
+ path-key: 3.1.1
+
+ number-to-bn@1.7.0:
+ dependencies:
+ bn.js: 4.11.6
+ strip-hex-prefix: 1.0.0
+
+ object-assign@4.1.1: {}
+
+ object-inspect@1.13.4: {}
+
+ obliterator@2.0.5: {}
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ onetime@5.1.2:
+ dependencies:
+ mimic-fn: 2.1.0
+
+ optionator@0.8.3:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.3.0
+ prelude-ls: 1.1.2
+ type-check: 0.3.2
+ word-wrap: 1.2.5
+
+ ordinal@1.0.3: {}
+
+ os-tmpdir@1.0.2: {}
+
+ p-limit@2.3.0:
+ dependencies:
+ p-try: 2.2.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@4.1.0:
+ dependencies:
+ p-limit: 2.3.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ p-map@4.0.0:
+ dependencies:
+ aggregate-error: 3.1.0
+
+ p-try@2.2.0: {}
+
+ parse-cache-control@1.0.1: {}
+
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ error-ex: 1.3.4
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
+ path-exists@4.0.0: {}
+
+ path-is-absolute@1.0.1: {}
+
+ path-key@3.1.1: {}
+
+ path-parse@1.0.7: {}
+
+ path-type@4.0.0: {}
+
+ pathval@1.1.1: {}
+
+ pbkdf2@3.1.5:
+ dependencies:
+ create-hash: 1.2.0
+ create-hmac: 1.1.7
+ ripemd160: 2.0.3
+ safe-buffer: 5.2.1
+ sha.js: 2.4.12
+ to-buffer: 1.2.2
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.3: {}
+
+ pify@4.0.1: {}
+
+ pirates@4.0.7: {}
+
+ pkg-dir@4.2.0:
+ dependencies:
+ find-up: 4.1.0
+
+ possible-typed-array-names@1.1.0: {}
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.1.2: {}
+
+ prettier@2.8.8: {}
+
+ pretty-format@29.7.0:
+ dependencies:
+ '@jest/schemas': 29.6.3
+ ansi-styles: 5.2.0
+ react-is: 18.3.1
+
+ process-nextick-args@2.0.1: {}
+
+ promise@8.3.0:
+ dependencies:
+ asap: 2.0.6
+
+ prompts@2.4.2:
+ dependencies:
+ kleur: 3.0.3
+ sisteransi: 1.0.5
+
+ proxy-from-env@1.1.0: {}
+
+ pure-rand@6.1.0: {}
+
+ qs@6.14.0:
+ dependencies:
+ side-channel: 1.1.0
+
+ queue-microtask@1.2.3: {}
+
+ randombytes@2.1.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ raw-body@2.5.2:
+ dependencies:
+ bytes: 3.1.2
+ http-errors: 2.0.0
+ iconv-lite: 0.4.24
+ unpipe: 1.0.0
+
+ react-is@18.3.1: {}
+
+ readable-stream@2.3.8:
+ dependencies:
+ core-util-is: 1.0.3
+ inherits: 2.0.4
+ isarray: 1.0.0
+ process-nextick-args: 2.0.1
+ safe-buffer: 5.1.2
+ string_decoder: 1.1.1
+ util-deprecate: 1.0.2
+
+ readable-stream@3.6.2:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ readdirp@4.1.2: {}
+
+ rechoir@0.6.2:
+ dependencies:
+ resolve: 1.22.11
+
+ recursive-readdir@2.2.3:
+ dependencies:
+ minimatch: 3.1.2
+
+ reduce-flatten@2.0.0: {}
+
+ req-cwd@2.0.0:
+ dependencies:
+ req-from: 2.0.0
+
+ req-from@2.0.0:
+ dependencies:
+ resolve-from: 3.0.0
+
+ require-directory@2.1.1: {}
+
+ require-from-string@2.0.2: {}
+
+ resolve-cwd@3.0.0:
+ dependencies:
+ resolve-from: 5.0.0
+
+ resolve-from@3.0.0: {}
+
+ resolve-from@5.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ resolve.exports@2.0.3: {}
+
+ resolve@1.1.7: {}
+
+ resolve@1.17.0:
+ dependencies:
+ path-parse: 1.0.7
+
+ resolve@1.22.11:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ reusify@1.1.0: {}
+
+ ripemd160@2.0.3:
+ dependencies:
+ hash-base: 3.1.2
+ inherits: 2.0.4
+
+ rlp@2.2.7:
+ dependencies:
+ bn.js: 5.2.2
+
+ rollup@4.53.3:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.53.3
+ '@rollup/rollup-android-arm64': 4.53.3
+ '@rollup/rollup-darwin-arm64': 4.53.3
+ '@rollup/rollup-darwin-x64': 4.53.3
+ '@rollup/rollup-freebsd-arm64': 4.53.3
+ '@rollup/rollup-freebsd-x64': 4.53.3
+ '@rollup/rollup-linux-arm-gnueabihf': 4.53.3
+ '@rollup/rollup-linux-arm-musleabihf': 4.53.3
+ '@rollup/rollup-linux-arm64-gnu': 4.53.3
+ '@rollup/rollup-linux-arm64-musl': 4.53.3
+ '@rollup/rollup-linux-loong64-gnu': 4.53.3
+ '@rollup/rollup-linux-ppc64-gnu': 4.53.3
+ '@rollup/rollup-linux-riscv64-gnu': 4.53.3
+ '@rollup/rollup-linux-riscv64-musl': 4.53.3
+ '@rollup/rollup-linux-s390x-gnu': 4.53.3
+ '@rollup/rollup-linux-x64-gnu': 4.53.3
+ '@rollup/rollup-linux-x64-musl': 4.53.3
+ '@rollup/rollup-openharmony-arm64': 4.53.3
+ '@rollup/rollup-win32-arm64-msvc': 4.53.3
+ '@rollup/rollup-win32-ia32-msvc': 4.53.3
+ '@rollup/rollup-win32-x64-gnu': 4.53.3
+ '@rollup/rollup-win32-x64-msvc': 4.53.3
+ fsevents: 2.3.3
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ safe-buffer@5.1.2: {}
+
+ safe-buffer@5.2.1: {}
+
+ safer-buffer@2.1.2: {}
+
+ sc-istanbul@0.4.6:
+ dependencies:
+ abbrev: 1.0.9
+ async: 1.5.2
+ escodegen: 1.8.1
+ esprima: 2.7.3
+ glob: 5.0.15
+ handlebars: 4.7.8
+ js-yaml: 3.14.2
+ mkdirp: 0.5.6
+ nopt: 3.0.6
+ once: 1.4.0
+ resolve: 1.1.7
+ supports-color: 3.2.3
+ which: 1.3.1
+ wordwrap: 1.0.0
+
+ scrypt-js@3.0.1: {}
+
+ secp256k1@4.0.4:
+ dependencies:
+ elliptic: 6.6.1
+ node-addon-api: 5.1.0
+ node-gyp-build: 4.8.4
+
+ semver@5.7.2: {}
+
+ semver@6.3.1: {}
+
+ semver@7.7.3: {}
+
+ serialize-javascript@6.0.2:
+ dependencies:
+ randombytes: 2.1.0
+
+ set-function-length@1.2.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+
+ setimmediate@1.0.5: {}
+
+ setprototypeof@1.2.0: {}
+
+ sha.js@2.4.12:
+ dependencies:
+ inherits: 2.0.4
+ safe-buffer: 5.2.1
+ to-buffer: 1.2.2
+
+ sha1@1.1.1:
+ dependencies:
+ charenc: 0.0.2
+ crypt: 0.0.2
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ shelljs@0.8.5:
+ dependencies:
+ glob: 7.2.3
+ interpret: 1.4.0
+ rechoir: 0.6.2
+
+ side-channel-list@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-map@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-weakmap@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-map: 1.0.1
+
+ side-channel@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-list: 1.0.0
+ side-channel-map: 1.0.1
+ side-channel-weakmap: 1.0.2
+
+ signal-exit@3.0.7: {}
+
+ sisteransi@1.0.5: {}
+
+ slash@3.0.0: {}
+
+ slice-ansi@4.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ astral-regex: 2.0.0
+ is-fullwidth-code-point: 3.0.0
+
+ solc@0.8.26(debug@4.4.3):
+ dependencies:
+ command-exists: 1.2.9
+ commander: 8.3.0
+ follow-redirects: 1.15.11(debug@4.4.3)
+ js-sha3: 0.8.0
+ memorystream: 0.3.1
+ semver: 5.7.2
+ tmp: 0.0.33
+ transitivePeerDependencies:
+ - debug
+
+ solidity-coverage@0.8.16(hardhat@2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)):
+ dependencies:
+ '@ethersproject/abi': 5.8.0
+ '@solidity-parser/parser': 0.20.2
+ chalk: 2.4.2
+ death: 1.1.0
+ difflib: 0.2.4
+ fs-extra: 8.1.0
+ ghost-testrpc: 0.0.2
+ global-modules: 2.0.0
+ globby: 10.0.2
+ hardhat: 2.27.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))(typescript@5.9.3)
+ jsonschema: 1.5.0
+ lodash: 4.17.21
+ mocha: 10.8.2
+ node-emoji: 1.11.0
+ pify: 4.0.1
+ recursive-readdir: 2.2.3
+ sc-istanbul: 0.4.6
+ semver: 7.7.3
+ shelljs: 0.8.5
+ web3-utils: 1.10.4
+
+ source-map-js@1.2.1: {}
+
+ source-map-support@0.5.13:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
+ source-map-support@0.5.21:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
+ source-map@0.2.0:
+ dependencies:
+ amdefine: 1.0.1
+ optional: true
+
+ source-map@0.6.1: {}
+
+ sprintf-js@1.0.3: {}
+
+ stack-utils@2.0.6:
+ dependencies:
+ escape-string-regexp: 2.0.0
+
+ stacktrace-parser@0.1.11:
+ dependencies:
+ type-fest: 0.7.1
+
+ statuses@2.0.1: {}
+
+ string-format@2.0.0: {}
+
+ string-length@4.0.2:
+ dependencies:
+ char-regex: 1.0.2
+ strip-ansi: 6.0.1
+
+ string-width@2.1.1:
+ dependencies:
+ is-fullwidth-code-point: 2.0.0
+ strip-ansi: 4.0.0
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string_decoder@1.1.1:
+ dependencies:
+ safe-buffer: 5.1.2
+
+ string_decoder@1.3.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ strip-ansi@4.0.0:
+ dependencies:
+ ansi-regex: 3.0.1
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-bom@4.0.0: {}
+
+ strip-final-newline@2.0.0: {}
+
+ strip-hex-prefix@1.0.0:
+ dependencies:
+ is-hex-prefixed: 1.0.0
+
+ strip-json-comments@3.1.1: {}
+
+ supports-color@3.2.3:
+ dependencies:
+ has-flag: 1.0.0
+
+ supports-color@5.5.0:
+ dependencies:
+ has-flag: 3.0.0
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-color@8.1.1:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ sync-request@6.1.0:
+ dependencies:
+ http-response-object: 3.0.2
+ sync-rpc: 1.3.6
+ then-request: 6.0.2
+
+ sync-rpc@1.3.6:
+ dependencies:
+ get-port: 3.2.0
+
+ table-layout@1.0.2:
+ dependencies:
+ array-back: 4.0.2
+ deep-extend: 0.6.0
+ typical: 5.2.0
+ wordwrapjs: 4.0.1
+
+ table@6.9.0:
+ dependencies:
+ ajv: 8.17.1
+ lodash.truncate: 4.4.2
+ slice-ansi: 4.0.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ test-exclude@6.0.0:
+ dependencies:
+ '@istanbuljs/schema': 0.1.3
+ glob: 7.2.3
+ minimatch: 3.1.2
+
+ then-request@6.0.2:
+ dependencies:
+ '@types/concat-stream': 1.6.1
+ '@types/form-data': 0.0.33
+ '@types/node': 8.10.66
+ '@types/qs': 6.14.0
+ caseless: 0.12.0
+ concat-stream: 1.6.2
+ form-data: 2.5.5
+ http-basic: 8.1.3
+ http-response-object: 3.0.2
+ promise: 8.3.0
+ qs: 6.14.0
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ tmp@0.0.33:
+ dependencies:
+ os-tmpdir: 1.0.2
+
+ tmpl@1.0.5: {}
+
+ to-buffer@1.2.2:
+ dependencies:
+ isarray: 2.0.5
+ safe-buffer: 5.2.1
+ typed-array-buffer: 1.0.3
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ toidentifier@1.0.1: {}
+
+ ts-command-line-args@2.5.1:
+ dependencies:
+ chalk: 4.1.2
+ command-line-args: 5.2.1
+ command-line-usage: 6.1.3
+ string-format: 2.0.0
+
+ ts-essentials@7.0.3(typescript@5.9.3):
+ dependencies:
+ typescript: 5.9.3
+
+ ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3):
+ dependencies:
+ bs-logger: 0.2.6
+ fast-json-stable-stringify: 2.1.0
+ handlebars: 4.7.8
+ jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
+ json5: 2.2.3
+ lodash.memoize: 4.1.2
+ make-error: 1.3.6
+ semver: 7.7.3
+ type-fest: 4.41.0
+ typescript: 5.9.3
+ yargs-parser: 21.1.1
+ optionalDependencies:
+ '@babel/core': 7.28.5
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
+ babel-jest: 29.7.0(@babel/core@7.28.5)
+ jest-util: 29.7.0
+
+ ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3):
+ dependencies:
+ '@cspotcode/source-map-support': 0.8.1
+ '@tsconfig/node10': 1.0.12
+ '@tsconfig/node12': 1.0.11
+ '@tsconfig/node14': 1.0.3
+ '@tsconfig/node16': 1.0.4
+ '@types/node': 22.19.1
+ acorn: 8.15.0
+ acorn-walk: 8.3.4
+ arg: 4.1.3
+ create-require: 1.1.1
+ diff: 4.0.2
+ make-error: 1.3.6
+ typescript: 5.9.3
+ v8-compile-cache-lib: 3.0.1
+ yn: 3.1.1
+
+ tslib@1.14.1: {}
+
+ tslib@2.7.0: {}
+
+ tsort@0.0.1: {}
+
+ tsx@4.20.6:
+ dependencies:
+ esbuild: 0.25.12
+ get-tsconfig: 4.13.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ type-check@0.3.2:
+ dependencies:
+ prelude-ls: 1.1.2
+
+ type-detect@4.0.8: {}
+
+ type-detect@4.1.0: {}
+
+ type-fest@0.20.2: {}
+
+ type-fest@0.21.3: {}
+
+ type-fest@0.7.1: {}
+
+ type-fest@4.41.0: {}
+
+ typechain@8.3.2(typescript@5.9.3):
+ dependencies:
+ '@types/prettier': 2.7.3
+ debug: 4.4.3(supports-color@8.1.1)
+ fs-extra: 7.0.1
+ glob: 7.1.7
+ js-sha3: 0.8.0
+ lodash: 4.17.21
+ mkdirp: 1.0.4
+ prettier: 2.8.8
+ ts-command-line-args: 2.5.1
+ ts-essentials: 7.0.3(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ typed-array-buffer@1.0.3:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-typed-array: 1.1.15
+
+ typedarray@0.0.6: {}
+
+ typescript@5.9.3: {}
+
+ typical@4.0.0: {}
+
+ typical@5.2.0: {}
+
+ uglify-js@3.19.3:
+ optional: true
+
+ undici-types@6.19.8: {}
+
+ undici-types@6.21.0: {}
+
+ undici@5.29.0:
+ dependencies:
+ '@fastify/busboy': 2.1.1
+
+ universalify@0.1.2: {}
+
+ universalify@2.0.1: {}
+
+ unpipe@1.0.0: {}
+
+ update-browserslist-db@1.1.4(browserslist@4.28.0):
+ dependencies:
+ browserslist: 4.28.0
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ utf8@3.0.0: {}
+
+ util-deprecate@1.0.2: {}
+
+ uuid@8.3.2: {}
+
+ v8-compile-cache-lib@3.0.1: {}
+
+ v8-to-istanbul@9.3.0:
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ '@types/istanbul-lib-coverage': 2.0.6
+ convert-source-map: 2.0.0
+
+ vite@5.4.21(@types/node@22.19.1):
+ dependencies:
+ esbuild: 0.21.5
+ postcss: 8.5.6
+ rollup: 4.53.3
+ optionalDependencies:
+ '@types/node': 22.19.1
+ fsevents: 2.3.3
+
+ walker@1.0.8:
+ dependencies:
+ makeerror: 1.0.12
+
+ web3-utils@1.10.4:
+ dependencies:
+ '@ethereumjs/util': 8.1.0
+ bn.js: 5.2.2
+ ethereum-bloom-filters: 1.2.0
+ ethereum-cryptography: 2.2.1
+ ethjs-unit: 0.1.6
+ number-to-bn: 1.7.0
+ randombytes: 2.1.0
+ utf8: 3.0.0
+
+ which-typed-array@1.1.19:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ for-each: 0.3.5
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+
+ which@1.3.1:
+ dependencies:
+ isexe: 2.0.0
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ widest-line@3.1.0:
+ dependencies:
+ string-width: 4.2.3
+
+ word-wrap@1.2.5: {}
+
+ wordwrap@1.0.0: {}
+
+ wordwrapjs@4.0.1:
+ dependencies:
+ reduce-flatten: 2.0.0
+ typical: 5.2.0
+
+ workerpool@6.5.1: {}
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrappy@1.0.2: {}
+
+ write-file-atomic@4.0.2:
+ dependencies:
+ imurmurhash: 0.1.4
+ signal-exit: 3.0.7
+
+ ws@7.5.10: {}
+
+ ws@8.17.1: {}
+
+ ws@8.18.0: {}
+
+ ws@8.18.3: {}
+
+ y18n@5.0.8: {}
+
+ yallist@3.1.1: {}
+
+ yargs-parser@20.2.9: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs-unparser@2.0.0:
+ dependencies:
+ camelcase: 6.3.0
+ decamelize: 4.0.0
+ flat: 5.0.2
+ is-plain-obj: 2.1.0
+
+ yargs@16.2.0:
+ dependencies:
+ cliui: 7.0.4
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 20.2.9
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
+ yn@3.1.1: {}
+
+ yocto-queue@0.1.0: {}
diff --git a/entropy/0xSlither/pnpm-workspace.yaml b/entropy/0xSlither/pnpm-workspace.yaml
new file mode 100644
index 0000000..49e9ffc
--- /dev/null
+++ b/entropy/0xSlither/pnpm-workspace.yaml
@@ -0,0 +1,6 @@
+packages:
+ - 'server'
+ - 'client'
+ - 'shared'
+ - 'contracts'
+
diff --git a/entropy/0xSlither/server/jest.config.js b/entropy/0xSlither/server/jest.config.js
new file mode 100644
index 0000000..6a70625
--- /dev/null
+++ b/entropy/0xSlither/server/jest.config.js
@@ -0,0 +1,18 @@
+module.exports = {
+ preset: 'ts-jest',
+ testEnvironment: 'node',
+ roots: ['/tests'],
+ testMatch: ['**/*.test.ts'],
+ moduleNameMapper: {
+ '^@0xslither/shared$': '/../shared/dist/index.js',
+ },
+ transform: {
+ '^.+\\.ts$': ['ts-jest', {
+ tsconfig: {
+ esModuleInterop: true,
+ allowSyntheticDefaultImports: true,
+ },
+ }],
+ },
+};
+
diff --git a/entropy/0xSlither/server/package.json b/entropy/0xSlither/server/package.json
new file mode 100644
index 0000000..8a7687e
--- /dev/null
+++ b/entropy/0xSlither/server/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "server",
+ "version": "1.0.0",
+ "main": "dist/index.js",
+ "scripts": {
+ "build": "tsc",
+ "dev": "tsx watch src/index.ts",
+ "start": "node dist/index.js",
+ "test": "jest",
+ "withdraw": "tsx src/withdrawFunds.ts"
+ },
+ "dependencies": {
+ "ws": "^8.18.0",
+ "@0xslither/shared": "workspace:*",
+ "ethers": "^6.9.0",
+ "dotenv": "^16.3.1"
+ },
+ "devDependencies": {
+ "@types/node": "^22.0.0",
+ "@types/ws": "^8.5.12",
+ "typescript": "^5.5.4",
+ "tsx": "^4.16.2",
+ "jest": "^29.7.0",
+ "@types/jest": "^29.5.12",
+ "ts-jest": "^29.1.2"
+ }
+}
+
diff --git a/entropy/0xSlither/server/src/BlockchainService.ts b/entropy/0xSlither/server/src/BlockchainService.ts
new file mode 100644
index 0000000..d0cd98c
--- /dev/null
+++ b/entropy/0xSlither/server/src/BlockchainService.ts
@@ -0,0 +1,585 @@
+import { ethers } from 'ethers';
+
+// Contract ABIs (minimal, only what we need)
+const STAKE_ARENA_ABI = [
+ 'function depositToVault() external payable',
+ 'function reportEat(bytes32 matchId, address eater, address eaten) external',
+ 'function reportSelfDeath(bytes32 matchId, address player, uint256 score) external',
+ 'function commitEntropy(bytes32 matchId, bytes32 entropyRequestId, bytes32 seedHash) external',
+ 'function finalizeMatch(bytes32 matchId, address[] calldata players, uint256[] calldata scores, address winner) external',
+ 'function withdrawBalance() external',
+ 'function getLeaderboard() external view returns (tuple(address player, uint256 score)[])',
+ 'function bestScore(address player) external view returns (uint256)',
+ 'function getStake(bytes32 matchId, address player) external view returns (uint256)',
+ 'function isActive(bytes32 matchId, address player) external view returns (bool)',
+ 'event DepositedToVault(address indexed player, uint256 amount, uint256 timestamp)',
+ 'event EatReported(bytes32 indexed matchId, address indexed eater, address indexed eaten, uint256 timestamp)',
+ 'event SelfDeathReported(bytes32 indexed matchId, address indexed player, uint256 score, uint256 timestamp)',
+ 'event EatLoot(bytes32 indexed matchId, address indexed eater, address indexed eaten, uint256 amountTransferred, uint256 timestamp)',
+ 'event SelfDeath(bytes32 indexed matchId, address indexed player, uint256 amountToServer, uint256 timestamp)',
+ 'event MatchFinalized(bytes32 indexed matchId, address indexed winner, uint256 timestamp)',
+ 'event EntropyCommitted(bytes32 indexed matchId, bytes32 entropyRequestId)',
+];
+
+
+interface LeaderboardEntry {
+ player: string;
+ score: bigint;
+}
+
+interface PendingTransaction {
+ promise: Promise;
+ description: string;
+}
+
+export class BlockchainService {
+ private provider: ethers.JsonRpcProvider;
+ private wallet: ethers.Wallet;
+ private stakeArena: ethers.Contract;
+ private pendingTxs: PendingTransaction[] = [];
+ private readonly maxRetries = 3;
+ private readonly retryDelay = 2000; // 2 seconds
+
+ // Transaction queue per address to prevent race conditions
+ // Each address has a promise chain that ensures operations are serialized
+ private addressQueues: Map> = new Map();
+
+ constructor(
+ rpcUrl: string,
+ privateKey: string,
+ stakeArenaAddress: string
+ ) {
+ this.provider = new ethers.JsonRpcProvider(rpcUrl);
+ this.wallet = new ethers.Wallet(privateKey, this.provider);
+ this.stakeArena = new ethers.Contract(
+ stakeArenaAddress,
+ STAKE_ARENA_ABI,
+ this.wallet
+ );
+
+ console.log('BlockchainService initialized');
+ console.log('Server wallet:', this.wallet.address);
+ console.log('StakeArena contract:', stakeArenaAddress);
+ console.log('Using native SSS token for game economy');
+ }
+
+ /**
+ * Generate a match ID from a unique string
+ */
+ generateMatchId(uniqueString: string): string {
+ return ethers.id(uniqueString);
+ }
+
+ /**
+ * Verify that a player has deposited to the vault recently
+ * Checks DepositedToVault events from the contract
+ * @param playerAddress Player's address
+ * @param minAmount Minimum deposit amount required (in SSS)
+ * @param blocksBack How many blocks back to check (default: 1000)
+ * @returns True if player has deposited at least minAmount
+ */
+ async verifyVaultDeposit(
+ playerAddress: string,
+ minAmount: string = '1',
+ blocksBack: number = 1000
+ ): Promise {
+ try {
+ const currentBlock = await this.provider.getBlockNumber();
+ const fromBlock = Math.max(0, currentBlock - blocksBack);
+
+ // Query DepositedToVault events for this player
+ const filter = this.stakeArena.filters.DepositedToVault(playerAddress);
+ const events = await this.stakeArena.queryFilter(filter, fromBlock, currentBlock);
+
+ if (events.length === 0) {
+ console.log(`[Blockchain] No deposits found for ${playerAddress.slice(0, 8)} in last ${blocksBack} blocks`);
+ return false;
+ }
+
+ // Check if any deposit meets the minimum amount
+ const minAmountWei = ethers.parseEther(minAmount);
+ for (const event of events) {
+ // Type guard: Check if event is EventLog (has args property)
+ if ('args' in event && event.args) {
+ const depositAmount = event.args.amount || 0n;
+ if (depositAmount >= minAmountWei) {
+ console.log(`[Blockchain] โ
Verified deposit: ${ethers.formatEther(depositAmount)} SSS from ${playerAddress.slice(0, 8)}`);
+ return true;
+ }
+ }
+ }
+
+ console.log(`[Blockchain] Deposits found but below minimum ${minAmount} SSS for ${playerAddress.slice(0, 8)}`);
+ return false;
+ } catch (error) {
+ console.error('[Blockchain] Error verifying vault deposit:', error);
+ return false;
+ }
+ }
+
+ /**
+ * Transfer kill reward directly from server wallet to killer
+ * This is used in vault mode where stakes are held in server wallet
+ * @param killerAddress Address of the player who got the kill
+ * @param amount Amount to transfer (victim's stake, in SSS)
+ */
+ async transferKillReward(
+ killerAddress: string,
+ amount: number
+ ): Promise {
+ const description = `transferKillReward: ${amount.toFixed(2)} SSS to ${killerAddress.slice(0, 8)}`;
+
+ // Queue this operation for the killer's address
+ const operation = async () => {
+ await this.executeWithRetry(async () => {
+ console.log(`[Blockchain] ${description}`);
+
+ // Convert amount to wei (18 decimals for native SSS)
+ const amountWei = ethers.parseEther(amount.toFixed(18));
+
+ // Check server wallet balance
+ const serverBalance = await this.provider.getBalance(this.wallet.address);
+ if (serverBalance < amountWei) {
+ console.error(`[Blockchain] Insufficient server balance for kill reward: ${ethers.formatEther(serverBalance)} < ${amount.toFixed(2)} SSS`);
+ throw new Error('Insufficient server balance for kill reward');
+ }
+
+ // Transfer native SSS from server to killer
+ const tx = await this.wallet.sendTransaction({
+ to: killerAddress,
+ value: amountWei,
+ });
+ const receipt = await tx.wait();
+ console.log(`[Blockchain] โ
Kill reward sent: ${amount.toFixed(2)} SSS to ${killerAddress.slice(0, 8)}, tx: ${receipt!.hash}`);
+ return receipt;
+ }, description);
+ };
+
+ // Queue for the killer's address
+ this.queueForAddress(killerAddress, operation);
+ }
+
+ /**
+ * Report that one player ate another (stats/leaderboard only in vault mode)
+ * In vault mode, stake transfers happen via transferKillReward()
+ * Non-blocking, fire-and-forget with retry logic
+ */
+ async reportEat(
+ matchId: string,
+ eaterAddress: string,
+ eatenAddress: string
+ ): Promise {
+ const description = `reportEat: ${eaterAddress.slice(0, 8)} ate ${eatenAddress.slice(0, 8)} in match ${matchId.slice(0, 10)}`;
+
+ // Fire-and-forget, no queueing needed since no state changes
+ const txPromise = this.executeWithRetry(async () => {
+ console.log(`[Blockchain] ${description}`);
+
+ // Convert string match ID to bytes32
+ const matchIdBytes32 = ethers.id(matchId);
+ const tx = await this.stakeArena.reportEat(
+ matchIdBytes32,
+ eaterAddress,
+ eatenAddress
+ );
+ console.log(`[Blockchain] reportEat tx sent: ${tx.hash}`);
+ const receipt = await tx.wait();
+ console.log(`[Blockchain] โ
reportEat confirmed (stats only): ${receipt.hash}`);
+ return receipt;
+ }, description);
+
+ this.pendingTxs.push({ promise: txPromise, description });
+ this.cleanupPendingTxs();
+ }
+
+ /**
+ * Report that a player died from self-inflicted causes (leaderboard update only in vault mode)
+ * (wall collision, eating self, disconnect, etc.)
+ * In vault mode, stakes are already in server wallet, no transfer needed
+ * Non-blocking, fire-and-forget with retry logic
+ */
+ async reportSelfDeath(
+ matchId: string,
+ playerAddress: string,
+ score: number
+ ): Promise {
+ const description = `reportSelfDeath: ${playerAddress.slice(0, 8)} died with score ${score} in match ${matchId.slice(0, 10)}`;
+
+ // Fire-and-forget, no queueing needed since no state changes
+ const txPromise = this.executeWithRetry(async () => {
+ console.log(`[Blockchain] ${description}`);
+
+ // Convert string match ID to bytes32
+ const matchIdBytes32 = ethers.id(matchId);
+ const tx = await this.stakeArena.reportSelfDeath(
+ matchIdBytes32,
+ playerAddress,
+ score
+ );
+ console.log(`[Blockchain] reportSelfDeath tx sent: ${tx.hash}`);
+ const receipt = await tx.wait();
+ console.log(`[Blockchain] โ
reportSelfDeath confirmed (leaderboard only): ${receipt.hash}`);
+ return receipt;
+ }, description);
+
+ this.pendingTxs.push({ promise: txPromise, description });
+ this.cleanupPendingTxs();
+ }
+
+ /**
+ * Commit entropy seed hash to Saga for a match
+ * @param matchId Match identifier string
+ * @param entropyRequestId Entropy request ID from Base Sepolia (sequence number)
+ * @param seed The actual random seed from Pyth Entropy
+ * Non-blocking with retry logic
+ */
+ async commitEntropyToSaga(matchId: string, entropyRequestId: string, seed: string): Promise {
+ const description = `commitEntropy for match ${matchId.slice(0, 10)}`;
+
+ const txPromise = this.executeWithRetry(async () => {
+ console.log(`[Blockchain] ${description}`);
+ console.log(`[Blockchain] Entropy request ID: ${entropyRequestId}`);
+ console.log(`[Blockchain] Seed: ${seed}`);
+
+ // Convert match ID to bytes32
+ const matchIdBytes32 = ethers.id(matchId);
+
+ // Convert request ID to bytes32 (it's a uint64, so pad it)
+ const requestIdBytes32 = ethers.zeroPadValue(ethers.toBeHex(entropyRequestId), 32);
+
+ // Hash the seed for on-chain storage
+ const seedHash = ethers.keccak256(ethers.toUtf8Bytes(seed));
+
+ console.log(`[Blockchain] Match ID (bytes32): ${matchIdBytes32}`);
+ console.log(`[Blockchain] Request ID (bytes32): ${requestIdBytes32}`);
+ console.log(`[Blockchain] Seed hash: ${seedHash}`);
+
+ const tx = await this.stakeArena.commitEntropy(matchIdBytes32, requestIdBytes32, seedHash);
+ const receipt = await tx.wait();
+ console.log(`[Blockchain] โ
commitEntropy confirmed: ${receipt.hash}`);
+ console.log(`[Blockchain] View on explorer: ${process.env.SAGA_EXPLORER_URL || 'N/A'}/tx/${receipt.hash}`);
+ return receipt;
+ }, description);
+
+ this.pendingTxs.push({ promise: txPromise, description });
+ this.cleanupPendingTxs();
+ }
+
+ /**
+ * Legacy method for backward compatibility
+ * @deprecated Use commitEntropyToSaga instead
+ */
+ async commitEntropy(matchId: string, entropyRequestId: string): Promise {
+ console.warn('[Blockchain] commitEntropy is deprecated, use commitEntropyToSaga with seed parameter');
+ // Create a dummy seed hash for backward compatibility
+ const dummySeedHash = ethers.keccak256(ethers.toUtf8Bytes(entropyRequestId));
+ await this.commitEntropyToSaga(matchId, entropyRequestId, entropyRequestId);
+ }
+
+ /**
+ * Finalize a match and update leaderboard
+ * Non-blocking with retry logic
+ */
+ async finalizeMatch(
+ matchId: string,
+ players: string[],
+ scores: number[],
+ winner: string
+ ): Promise {
+ const description = `finalizeMatch ${matchId.slice(0, 10)} with ${players.length} players`;
+
+ const txPromise = this.executeWithRetry(async () => {
+ console.log(`[Blockchain] ${description}`);
+ // Convert string match ID to bytes32
+ const matchIdBytes32 = ethers.id(matchId);
+ const tx = await this.stakeArena.finalizeMatch(
+ matchIdBytes32,
+ players,
+ scores,
+ winner
+ );
+ const receipt = await tx.wait();
+ console.log(`[Blockchain] finalizeMatch confirmed: ${receipt.hash}`);
+ return receipt;
+ }, description);
+
+ this.pendingTxs.push({ promise: txPromise, description });
+ this.cleanupPendingTxs();
+ }
+
+ /**
+ * Get on-chain leaderboard
+ */
+ async getLeaderboard(): Promise> {
+ try {
+ const entries: LeaderboardEntry[] = await this.stakeArena.getLeaderboard();
+ return entries.map(entry => ({
+ address: entry.player,
+ score: Number(entry.score),
+ }));
+ } catch (error) {
+ console.error('[Blockchain] Error fetching leaderboard:', error);
+ return [];
+ }
+ }
+
+ /**
+ * Get best score for a player
+ */
+ async getBestScore(playerAddress: string): Promise {
+ try {
+ const score: bigint = await this.stakeArena.bestScore(playerAddress);
+ return Number(score);
+ } catch (error) {
+ console.error('[Blockchain] Error fetching best score:', error);
+ return 0;
+ }
+ }
+
+ /**
+ * Get player's stake in a match
+ */
+ async getStake(matchId: string, playerAddress: string): Promise {
+ try {
+ // Convert string match ID to bytes32
+ const matchIdBytes32 = ethers.id(matchId);
+ const stake: bigint = await this.stakeArena.getStake(matchIdBytes32, playerAddress);
+ return Math.floor(Number(ethers.formatEther(stake)));
+ } catch (error) {
+ console.error('[Blockchain] Error fetching stake:', error);
+ return 0;
+ }
+ }
+
+ /**
+ * Check if player is active in a match
+ */
+ async isActive(matchId: string, playerAddress: string): Promise {
+ try {
+ // Convert string match ID to bytes32
+ const matchIdBytes32 = ethers.id(matchId);
+ return await this.stakeArena.isActive(matchIdBytes32, playerAddress);
+ } catch (error) {
+ console.error('[Blockchain] Error checking active status:', error);
+ return false;
+ }
+ }
+
+ /**
+ * Queue an operation for a specific address to prevent race conditions
+ * Operations for the same address are serialized (executed one after another)
+ */
+ private queueForAddress(address: string, operation: () => Promise): void {
+ // Get the current queue for this address (or create an empty resolved promise)
+ const currentQueue = this.addressQueues.get(address) || Promise.resolve();
+
+ // Chain the new operation to the existing queue
+ const newQueue = currentQueue
+ .then(() => operation())
+ .catch((error) => {
+ console.error(`[Blockchain] Queued operation failed for ${address.slice(0, 8)}:`, error);
+ });
+
+ // Update the queue for this address
+ this.addressQueues.set(address, newQueue);
+
+ // Clean up the queue entry after operation completes
+ newQueue.finally(() => {
+ // If this is still the active queue, remove it
+ if (this.addressQueues.get(address) === newQueue) {
+ this.addressQueues.delete(address);
+ }
+ });
+ }
+
+ /**
+ * Execute a transaction with retry logic
+ */
+ private async executeWithRetry(
+ fn: () => Promise,
+ description: string
+ ): Promise {
+ for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
+ try {
+ return await fn();
+ } catch (error: any) {
+ // Enhanced error logging
+ console.error(
+ `[Blockchain] ${description} failed (attempt ${attempt}/${this.maxRetries}):`
+ );
+
+ if (error.code) {
+ console.error(` Error code: ${error.code}`);
+ }
+ if (error.reason) {
+ console.error(` Reason: ${error.reason}`);
+ }
+ if (error.data) {
+ console.error(` Data: ${error.data}`);
+ }
+ console.error(` Message: ${error.message || error}`);
+
+ if (attempt < this.maxRetries) {
+ await this.sleep(this.retryDelay * attempt);
+ }
+ }
+ }
+
+ console.error(`[Blockchain] ${description} failed after ${this.maxRetries} attempts`);
+ return null;
+ }
+
+ /**
+ * Clean up completed pending transactions
+ */
+ private cleanupPendingTxs(): void {
+ this.pendingTxs = this.pendingTxs.filter(tx => {
+ // Check if promise is settled
+ let settled = false;
+ tx.promise.then(() => { settled = true; }).catch(() => { settled = true; });
+ return !settled;
+ });
+ }
+
+ /**
+ * Wait for all pending transactions to complete
+ */
+ async waitForPendingTransactions(): Promise {
+ console.log(`[Blockchain] Waiting for ${this.pendingTxs.length} pending transactions...`);
+ await Promise.allSettled(this.pendingTxs.map(tx => tx.promise));
+ this.pendingTxs = [];
+ }
+
+ /**
+ * Get number of pending transactions
+ */
+ getPendingTxCount(): number {
+ return this.pendingTxs.length;
+ }
+
+ /**
+ * Withdraw accumulated native SSS from StakeArena contract to server wallet
+ * This retrieves funds from self-deaths and other contract accumulations
+ */
+ async withdrawContractBalance(): Promise {
+ try {
+ // Get the StakeArena contract address
+ const contractAddress = await this.stakeArena.getAddress();
+
+ // Check the contract's native SSS balance
+ const balance = await this.provider.getBalance(contractAddress);
+ const balanceFormatted = ethers.formatEther(balance);
+
+ console.log(`[Blockchain] StakeArena contract balance: ${balanceFormatted} SSS`);
+
+ // Only withdraw if balance is above a threshold (e.g., 10 SSS)
+ const threshold = ethers.parseEther("10");
+
+ if (balance < threshold) {
+ console.log(`[Blockchain] Balance below threshold (${ethers.formatEther(threshold)} SSS), skipping withdrawal`);
+ return false;
+ }
+
+ console.log(`[Blockchain] Withdrawing ${balanceFormatted} SSS from contract to server wallet...`);
+
+ // Call withdrawBalance function
+ const tx = await this.stakeArena.withdrawBalance();
+ const receipt = await tx.wait();
+
+ console.log(`[Blockchain] โ
Successfully withdrew ${balanceFormatted} SSS`);
+ console.log(`[Blockchain] Transaction: ${receipt?.hash}`);
+
+ // Check new server wallet balance
+ const serverBalance = await this.provider.getBalance(this.wallet.address);
+ console.log(`[Blockchain] Server wallet balance: ${ethers.formatEther(serverBalance)} SSS`);
+
+ return true;
+ } catch (error) {
+ console.error('[Blockchain] Error withdrawing contract balance:', error instanceof Error ? error.message : error);
+ return false;
+ }
+ }
+
+ /**
+ * Get the StakeArena contract's native SSS balance
+ */
+ async getContractBalance(): Promise {
+ try {
+ const contractAddress = await this.stakeArena.getAddress();
+ const balance = await this.provider.getBalance(contractAddress);
+ return ethers.formatEther(balance);
+ } catch (error) {
+ console.error('[Blockchain] Error getting contract balance:', error);
+ return "0";
+ }
+ }
+
+ /**
+ * Get the server wallet's native SSS balance
+ */
+ async getServerWalletBalance(): Promise {
+ try {
+ const balance = await this.provider.getBalance(this.wallet.address);
+ return ethers.formatEther(balance);
+ } catch (error) {
+ console.error('[Blockchain] Error getting server wallet balance:', error);
+ return "0";
+ }
+ }
+
+ /**
+ * Settle pellet tokens at match end/tap-out
+ * Server pays out accumulated pellet tokens to player via native SSS transfer
+ * @param playerAddress Player's address
+ * @param pelletTokens Amount of pellet tokens the player accumulated
+ */
+ async settlePelletTokens(
+ playerAddress: string,
+ pelletTokens: number
+ ): Promise {
+ const description = `settlePelletTokens: ${pelletTokens.toFixed(2)} SSS to ${playerAddress.slice(0, 8)}`;
+
+ // If player has no pellet tokens, nothing to settle
+ if (pelletTokens <= 0) {
+ console.log(`[Blockchain] No pellet tokens to settle for ${playerAddress.slice(0, 8)}`);
+ return;
+ }
+
+ // Queue this operation for the player's address
+ const operation = async () => {
+ await this.executeWithRetry(async () => {
+ console.log(`[Blockchain] ${description}`);
+
+ // Convert pellet tokens to wei (18 decimals for native SSS)
+ const amountWei = ethers.parseEther(pelletTokens.toFixed(18));
+
+ // Check server wallet balance (native token balance)
+ const serverBalance = await this.provider.getBalance(this.wallet.address);
+ if (serverBalance < amountWei) {
+ console.error(`[Blockchain] Insufficient server balance for pellet token payout: ${ethers.formatEther(serverBalance)} < ${pelletTokens.toFixed(2)} SSS`);
+ throw new Error('Insufficient server balance for pellet token payout');
+ }
+
+ // Transfer native SSS from server to player
+ const tx = await this.wallet.sendTransaction({
+ to: playerAddress,
+ value: amountWei,
+ });
+ const receipt = await tx.wait();
+ console.log(`[Blockchain] โ
Pellet tokens settled: ${pelletTokens.toFixed(2)} SSS to ${playerAddress.slice(0, 8)}, tx: ${receipt!.hash}`);
+ return receipt;
+ }, description);
+ };
+
+ // Queue for the player's address to prevent race conditions
+ this.queueForAddress(playerAddress, operation);
+ }
+
+ /**
+ * Sleep utility
+ */
+ private sleep(ms: number): Promise {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+}
+
diff --git a/entropy/0xSlither/server/src/CollisionDetection.ts b/entropy/0xSlither/server/src/CollisionDetection.ts
new file mode 100644
index 0000000..9b6fe93
--- /dev/null
+++ b/entropy/0xSlither/server/src/CollisionDetection.ts
@@ -0,0 +1,71 @@
+import { SnakeEntity } from './Snake';
+import { Pellet, SNAKE_HEAD_RADIUS, COLLISION_DISTANCE } from '@0xslither/shared';
+
+export interface CollisionResult {
+ victimId: string;
+ killerId: string | null; // null if self-collision or world bounds
+}
+
+export class CollisionDetection {
+ static checkPelletCollision(snake: SnakeEntity, pellet: Pellet): boolean {
+ const dx = snake.headPosition.x - pellet.x;
+ const dy = snake.headPosition.y - pellet.y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ return distance < SNAKE_HEAD_RADIUS + pellet.size;
+ }
+
+ static checkSnakeCollisions(snakes: SnakeEntity[]): CollisionResult[] {
+ const collisions: CollisionResult[] = [];
+ const processedVictims = new Set();
+
+ for (const snake of snakes) {
+ if (!snake.alive || processedVictims.has(snake.id)) continue;
+
+ // Check collision with all other snakes' body segments
+ for (const otherSnake of snakes) {
+ if (!otherSnake.alive) continue;
+
+ // Check against all segments (including head for other snakes)
+ const segmentsToCheck = otherSnake.segments;
+
+ for (let i = 0; i < segmentsToCheck.length; i++) {
+ const segment = segmentsToCheck[i];
+
+ // Skip checking against own first 3 segments to prevent immediate self-collision
+ // A snake can only hit its own body if it's long enough and turns back on itself
+ if (snake.id === otherSnake.id && i < 3) {
+ continue;
+ }
+
+ const dx = snake.headPosition.x - segment.x;
+ const dy = snake.headPosition.y - segment.y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance < COLLISION_DISTANCE) {
+ // Determine killer: if self-collision, killer is null
+ const killerId = snake.id === otherSnake.id ? null : otherSnake.id;
+ collisions.push({ victimId: snake.id, killerId });
+ processedVictims.add(snake.id);
+ break;
+ }
+ }
+
+ if (processedVictims.has(snake.id)) break;
+ }
+ }
+
+ return collisions;
+ }
+
+ static checkWorldBounds(snake: SnakeEntity, worldWidth: number, worldHeight: number): boolean {
+ // Die when the head (with its radius) touches or crosses the boundary
+ return (
+ snake.headPosition.x - SNAKE_HEAD_RADIUS < 0 ||
+ snake.headPosition.x + SNAKE_HEAD_RADIUS > worldWidth ||
+ snake.headPosition.y - SNAKE_HEAD_RADIUS < 0 ||
+ snake.headPosition.y + SNAKE_HEAD_RADIUS > worldHeight
+ );
+ }
+}
+
diff --git a/entropy/0xSlither/server/src/DeltaCompressor.ts b/entropy/0xSlither/server/src/DeltaCompressor.ts
new file mode 100644
index 0000000..3ff724c
--- /dev/null
+++ b/entropy/0xSlither/server/src/DeltaCompressor.ts
@@ -0,0 +1,190 @@
+import {
+ StateMessage,
+ DeltaStateMessage,
+ MessageType,
+ SerializedSnake,
+ SerializedPellet,
+ SerializedLeaderboard,
+} from '@0xslither/shared';
+
+interface PlayerState {
+ lastTick: number;
+ lastSnakes: Map;
+ lastPellets: Map; // Map by pellet ID for efficient lookups
+ lastLeaderboard: SerializedLeaderboard;
+}
+
+/**
+ * DeltaCompressor tracks previous state per player and computes deltas
+ * This significantly reduces bandwidth by only sending changed data
+ */
+export class DeltaCompressor {
+ private playerStates: Map = new Map();
+ private readonly maxTicksToKeep = 5; // Send full state if player missed >5 ticks
+
+ /**
+ * Get delta state for a player, or full state if needed
+ * @param playerId Player identifier
+ * @param currentState Current full game state
+ * @returns Delta state or full state if too many ticks missed
+ */
+ getDeltaOrFullState(playerId: string, currentState: StateMessage): StateMessage | DeltaStateMessage {
+ const playerState = this.playerStates.get(playerId);
+
+ // First time or too many missed ticks - send full state
+ if (!playerState || currentState.tick - playerState.lastTick > this.maxTicksToKeep) {
+ this.updatePlayerState(playerId, currentState);
+ return currentState; // Full state
+ }
+
+ // Compute delta
+ const delta = this.computeDelta(playerState, currentState);
+
+ // Update stored state
+ this.updatePlayerState(playerId, currentState);
+
+ return delta;
+ }
+
+ /**
+ * Compute delta between previous and current state
+ */
+ private computeDelta(playerState: PlayerState, currentState: StateMessage): DeltaStateMessage {
+ const snakesUpdated: SerializedSnake[] = [];
+ const snakesRemoved: string[] = [];
+ const snakesAdded: SerializedSnake[] = [];
+
+ // Check for new/updated snakes
+ const currentSnakeIds = new Set();
+ for (const snake of currentState.snakes) {
+ currentSnakeIds.add(snake.id);
+ const prevSnake = playerState.lastSnakes.get(snake.id);
+
+ if (!prevSnake) {
+ // New snake
+ snakesAdded.push(snake);
+ } else if (this.snakeChanged(prevSnake, snake)) {
+ // Snake changed
+ snakesUpdated.push(snake);
+ }
+ }
+
+ // Check for removed snakes
+ for (const [id] of playerState.lastSnakes) {
+ if (!currentSnakeIds.has(id)) {
+ snakesRemoved.push(id);
+ }
+ }
+
+ // Check pellets - only track removed (pellets never respawn, so no additions after init)
+ const pelletsRemoved: string[] = [];
+
+ // Build set of current pellet IDs
+ const currentPelletIds = new Set();
+ for (const pellet of currentState.pellets) {
+ currentPelletIds.add(pellet[0]); // pellet[0] is the ID
+ }
+
+ // Find removed pellets (in previous state but not in current)
+ for (const [id] of playerState.lastPellets) {
+ if (!currentPelletIds.has(id)) {
+ pelletsRemoved.push(id);
+ }
+ }
+
+ // Check leaderboard
+ const leaderboardChanged = this.leaderboardChanged(
+ playerState.lastLeaderboard,
+ currentState.leaderboard
+ );
+
+ return {
+ type: MessageType.DELTA_STATE,
+ tick: currentState.tick,
+ snakesAdded,
+ snakesUpdated,
+ snakesRemoved,
+ pelletsRemoved, // Only removals - pellets never respawn after initial spawn
+ leaderboardChanged: leaderboardChanged ? currentState.leaderboard : undefined,
+ matchId: currentState.matchId,
+ entropyPending: currentState.entropyPending,
+ useFairRNG: currentState.useFairRNG,
+ // Entropy details (seed, requestId, mapType) are only sent in full state messages
+ // to reduce bandwidth during gameplay - they're only needed on the start screen
+ };
+ }
+
+ /**
+ * Check if snake changed significantly
+ */
+ private snakeChanged(prev: SerializedSnake, current: SerializedSnake): boolean {
+ // Check head position (with threshold for minor movements)
+ const headMoved = Math.abs(prev.head[0] - current.head[0]) > 1 ||
+ Math.abs(prev.head[1] - current.head[1]) > 1;
+
+ // Check if segment count changed
+ const segmentsChanged = prev.segments.length !== current.segments.length;
+
+ // Check if angle changed significantly (>0.1 radians)
+ const angleChanged = Math.abs(prev.angle - current.angle) > 0.1;
+
+ return headMoved || segmentsChanged || angleChanged;
+ }
+
+
+ /**
+ * Check if leaderboard changed
+ */
+ private leaderboardChanged(prev: SerializedLeaderboard, current: SerializedLeaderboard): boolean {
+ if (prev.length !== current.length) return true;
+
+ for (let i = 0; i < prev.length; i++) {
+ if (prev[i][0] !== current[i][0] || prev[i][1] !== current[i][1]) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Update stored state for a player
+ */
+ private updatePlayerState(playerId: string, state: StateMessage): void {
+ const snakesMap = new Map();
+ for (const snake of state.snakes) {
+ snakesMap.set(snake.id, snake);
+ }
+
+ // Store pellets in a Map by ID for efficient delta comparison
+ const pelletsMap = new Map();
+ for (const pellet of state.pellets) {
+ pelletsMap.set(pellet[0], pellet); // pellet[0] is the ID
+ }
+
+ this.playerStates.set(playerId, {
+ lastTick: state.tick,
+ lastSnakes: snakesMap,
+ lastPellets: pelletsMap,
+ lastLeaderboard: state.leaderboard,
+ });
+ }
+
+ /**
+ * Remove player state when they disconnect
+ */
+ removePlayer(playerId: string): void {
+ this.playerStates.delete(playerId);
+ }
+
+ /**
+ * Get compression stats for monitoring
+ */
+ getStats(): { playerCount: number; avgStateSize: number } {
+ return {
+ playerCount: this.playerStates.size,
+ avgStateSize: 0, // Could be calculated if needed
+ };
+ }
+}
+
diff --git a/entropy/0xSlither/server/src/DeterministicRNG.ts b/entropy/0xSlither/server/src/DeterministicRNG.ts
new file mode 100644
index 0000000..3114c97
--- /dev/null
+++ b/entropy/0xSlither/server/src/DeterministicRNG.ts
@@ -0,0 +1,192 @@
+import { ethers } from 'ethers';
+import {
+ WORLD_WIDTH,
+ WORLD_HEIGHT,
+ PELLET_MIN_SIZE,
+ PELLET_MAX_SIZE,
+ PELLET_COLORS,
+} from '@0xslither/shared';
+
+/**
+ * DeterministicRNG generates reproducible pseudo-random values
+ * from a single on-chain entropy seed using domain separation
+ */
+export class DeterministicRNG {
+ private baseSeed: string;
+
+ constructor(baseSeed: string) {
+ if (!baseSeed || baseSeed === ethers.ZeroHash) {
+ throw new Error('Invalid base seed');
+ }
+ this.baseSeed = baseSeed;
+ }
+
+ /**
+ * Generate a domain-separated sub-seed
+ * @param domain Domain identifier (e.g., "spawn", "color", "pellets")
+ * @param args Additional arguments for uniqueness
+ */
+ private deriveSubSeed(domain: string, ...args: string[]): string {
+ const types = ['bytes32', 'string', ...args.map(() => 'string')];
+ const values = [this.baseSeed, domain, ...args];
+ return ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(types, values));
+ }
+
+ /**
+ * Convert a bytes32 seed to a float in range [0, 1)
+ */
+ private seedToFloat(seed: string): number {
+ const bigNum = BigInt(seed);
+ const maxUint256 = BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
+ return Number(bigNum) / Number(maxUint256);
+ }
+
+ /**
+ * Generate deterministic spawn position for a player
+ * @param playerAddress Player's wallet address
+ * @param retryCount Retry counter for collision avoidance
+ */
+ getSpawnPosition(playerAddress: string, retryCount: number = 0): { x: number; y: number } {
+ const subSeed = this.deriveSubSeed('spawn', playerAddress.toLowerCase(), retryCount.toString());
+
+ // Use two subsequent hashes for x and y to get independent randomness
+ const xSeed = ethers.keccak256(ethers.solidityPacked(['bytes32', 'uint8'], [subSeed, 0]));
+ const ySeed = ethers.keccak256(ethers.solidityPacked(['bytes32', 'uint8'], [subSeed, 1]));
+
+ const xRandom = this.seedToFloat(xSeed);
+ const yRandom = this.seedToFloat(ySeed);
+
+ // Spawn with margin from edges (500px) to prevent immediate wall collision
+ const margin = 500;
+ const x = margin + xRandom * (WORLD_WIDTH - 2 * margin);
+ const y = margin + yRandom * (WORLD_HEIGHT - 2 * margin);
+
+ return { x, y };
+ }
+
+ /**
+ * Check if a spawn position is valid (doesn't collide with existing snakes)
+ * @param position Position to check
+ * @param existingPositions Array of existing snake head positions
+ * @param minDistance Minimum distance from other snakes
+ */
+ isSpawnPositionValid(
+ position: { x: number; y: number },
+ existingPositions: Array<{ x: number; y: number }>,
+ minDistance: number = 200
+ ): boolean {
+ for (const existing of existingPositions) {
+ const dx = position.x - existing.x;
+ const dy = position.y - existing.y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < minDistance) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get spawn position with automatic retry on collision
+ * @param playerAddress Player's wallet address
+ * @param existingPositions Existing snake positions to avoid
+ * @param maxRetries Maximum retry attempts
+ */
+ getSpawnPositionWithRetry(
+ playerAddress: string,
+ existingPositions: Array<{ x: number; y: number }>,
+ maxRetries: number = 10
+ ): { x: number; y: number } {
+ for (let retry = 0; retry < maxRetries; retry++) {
+ const position = this.getSpawnPosition(playerAddress, retry);
+ if (this.isSpawnPositionValid(position, existingPositions)) {
+ return position;
+ }
+ }
+
+ // If all retries fail, return the last position anyway
+ console.warn(`โ ๏ธ Could not find collision-free spawn for ${playerAddress} after ${maxRetries} retries`);
+ return this.getSpawnPosition(playerAddress, maxRetries - 1);
+ }
+
+ /**
+ * Generate deterministic snake color for a player
+ * @param playerAddress Player's wallet address
+ */
+ getSnakeColor(playerAddress: string): string {
+ const subSeed = this.deriveSubSeed('color', playerAddress.toLowerCase());
+ const colorRandom = this.seedToFloat(subSeed);
+
+ // Generate HSL color with good saturation and lightness for visibility
+ const hue = Math.floor(colorRandom * 360);
+ const saturation = 70 + Math.floor((colorRandom * 360) % 30); // 70-100%
+ const lightness = 50 + Math.floor((colorRandom * 720) % 20); // 50-70%
+
+ return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
+ }
+
+ /**
+ * Generate deterministic pellet field
+ * @param count Number of pellets to generate
+ */
+ getPelletPositions(count: number): Array<{
+ x: number;
+ y: number;
+ size: number;
+ color: string;
+ }> {
+ const mapSeed = this.deriveSubSeed('pellets');
+ const pellets: Array<{ x: number; y: number; size: number; color: string }> = [];
+
+ for (let i = 0; i < count; i++) {
+ // Generate independent seed for each pellet
+ const pelletSeed = ethers.keccak256(
+ ethers.solidityPacked(['bytes32', 'uint256'], [mapSeed, i])
+ );
+
+ // Derive x, y, size, colorIndex from the pellet seed
+ const xSeed = ethers.keccak256(ethers.solidityPacked(['bytes32', 'uint8'], [pelletSeed, 0]));
+ const ySeed = ethers.keccak256(ethers.solidityPacked(['bytes32', 'uint8'], [pelletSeed, 1]));
+ const sizeSeed = ethers.keccak256(ethers.solidityPacked(['bytes32', 'uint8'], [pelletSeed, 2]));
+ const colorSeed = ethers.keccak256(ethers.solidityPacked(['bytes32', 'uint8'], [pelletSeed, 3]));
+
+ const x = this.seedToFloat(xSeed) * WORLD_WIDTH;
+ const y = this.seedToFloat(ySeed) * WORLD_HEIGHT;
+ const size = PELLET_MIN_SIZE + this.seedToFloat(sizeSeed) * (PELLET_MAX_SIZE - PELLET_MIN_SIZE);
+ const colorIndex = Math.floor(this.seedToFloat(colorSeed) * PELLET_COLORS.length);
+ const color = PELLET_COLORS[colorIndex];
+
+ pellets.push({ x, y, size, color });
+ }
+
+ return pellets;
+ }
+
+ /**
+ * Get map type (optional feature for varied gameplay)
+ */
+ getMapType(): 'uniform' | 'clustered' | 'ring' {
+ const mapTypeSeed = this.deriveSubSeed('map');
+ const typeRandom = this.seedToFloat(mapTypeSeed);
+
+ // Distribute evenly across 3 map types
+ if (typeRandom < 0.33) return 'uniform';
+ if (typeRandom < 0.66) return 'clustered';
+ return 'ring';
+ }
+
+ /**
+ * Get the base seed (for debugging/logging)
+ */
+ getBaseSeed(): string {
+ return this.baseSeed;
+ }
+
+ /**
+ * Get seed hash for on-chain commit
+ */
+ getSeedHash(): string {
+ return ethers.keccak256(ethers.toUtf8Bytes(this.baseSeed));
+ }
+}
+
diff --git a/entropy/0xSlither/server/src/EntropyBridgeService.ts b/entropy/0xSlither/server/src/EntropyBridgeService.ts
new file mode 100644
index 0000000..6026387
--- /dev/null
+++ b/entropy/0xSlither/server/src/EntropyBridgeService.ts
@@ -0,0 +1,296 @@
+import { ethers } from 'ethers';
+import * as dotenv from 'dotenv';
+
+dotenv.config();
+
+// EntropyOracle ABI (minimal, only what we need)
+const ENTROPY_ORACLE_ABI = [
+ 'function requestMatchEntropy(bytes32 matchId) external payable',
+ 'function getMatchSeed(bytes32 matchId) external view returns (bytes32)',
+ 'function hasRequestedEntropy(bytes32 matchId) external view returns (bool)',
+ 'function isSeedAvailable(bytes32 matchId) external view returns (bool)',
+ 'function getEntropyFee() external view returns (uint256)',
+ 'function entropyRequestIdByMatch(bytes32 matchId) external view returns (uint64)',
+ 'event EntropyRequested(bytes32 indexed matchId, uint64 requestId)',
+ 'event EntropyStored(bytes32 indexed matchId, bytes32 seed)',
+];
+
+/**
+ * EntropyBridgeService handles cross-chain entropy requests
+ * Requests randomness from Pyth Entropy on Base Sepolia
+ * Polls for revealed seeds and provides them to game server
+ */
+export class EntropyBridgeService {
+ private baseSepoliaProvider: ethers.JsonRpcProvider;
+ private baseSepoliaWallet: ethers.Wallet;
+ private entropyOracle: ethers.Contract;
+ private enabled: boolean = false;
+
+ constructor() {
+ const baseSepoliaRpcUrl = process.env.BASE_SEPOLIA_RPC_URL;
+ const privateKey = process.env.SERVER_PRIVATE_KEY;
+ const entropyOracleAddress = process.env.ENTROPY_ORACLE_ADDRESS;
+
+ if (!baseSepoliaRpcUrl || !privateKey || !entropyOracleAddress) {
+ console.warn('โ ๏ธ Entropy Bridge disabled: Missing BASE_SEPOLIA_RPC_URL, SERVER_PRIVATE_KEY, or ENTROPY_ORACLE_ADDRESS');
+ console.warn('โ ๏ธ Game will use fallback RNG');
+ this.enabled = false;
+ // Create dummy provider/wallet to avoid null checks
+ this.baseSepoliaProvider = new ethers.JsonRpcProvider('http://localhost:8545');
+ this.baseSepoliaWallet = new ethers.Wallet(ethers.Wallet.createRandom().privateKey);
+ this.entropyOracle = new ethers.Contract(ethers.ZeroAddress, ENTROPY_ORACLE_ABI, this.baseSepoliaWallet);
+ return;
+ }
+
+ this.baseSepoliaProvider = new ethers.JsonRpcProvider(baseSepoliaRpcUrl);
+ this.baseSepoliaWallet = new ethers.Wallet(privateKey, this.baseSepoliaProvider);
+ this.entropyOracle = new ethers.Contract(
+ entropyOracleAddress,
+ ENTROPY_ORACLE_ABI,
+ this.baseSepoliaWallet
+ );
+ this.enabled = true;
+
+ console.log('โ
Entropy Bridge Service initialized');
+ console.log('๐ Base Sepolia RPC:', baseSepoliaRpcUrl);
+ console.log('๐ Server wallet:', this.baseSepoliaWallet.address);
+ console.log('๐ฒ EntropyOracle:', entropyOracleAddress);
+ }
+
+ /**
+ * Check if the service is enabled
+ */
+ isEnabled(): boolean {
+ return this.enabled;
+ }
+
+ /**
+ * Request entropy for a match from Base Sepolia
+ * @param matchId Match identifier string
+ * @returns Request ID (sequence number) or null if failed
+ */
+ async requestMatchEntropy(matchId: string): Promise {
+ if (!this.enabled) {
+ console.warn('[EntropyBridge] Service disabled, skipping entropy request');
+ return null;
+ }
+
+ try {
+ const matchIdBytes32 = ethers.id(matchId);
+ console.log(`[EntropyBridge] Requesting entropy for match ${matchId}`);
+ console.log(`[EntropyBridge] Match ID (bytes32): ${matchIdBytes32}`);
+
+ // Check if already requested
+ const alreadyRequested = await this.entropyOracle.hasRequestedEntropy(matchIdBytes32);
+ if (alreadyRequested) {
+ console.log(`[EntropyBridge] Entropy already requested for match ${matchId}`);
+ const requestId = await this.entropyOracle.entropyRequestIdByMatch(matchIdBytes32);
+ return requestId.toString();
+ }
+
+ // Get entropy fee
+ const fee = await this.entropyOracle.getEntropyFee();
+ console.log(`[EntropyBridge] Entropy fee: ${ethers.formatEther(fee)} ETH`);
+
+ // Check wallet balance
+ const balance = await this.baseSepoliaProvider.getBalance(this.baseSepoliaWallet.address);
+ if (balance < fee) {
+ console.error(`[EntropyBridge] Insufficient balance: ${ethers.formatEther(balance)} ETH, need ${ethers.formatEther(fee)} ETH`);
+ return null;
+ }
+
+ // Request entropy
+ console.log('[EntropyBridge] Sending requestMatchEntropy transaction...');
+ const tx = await this.entropyOracle.requestMatchEntropy(matchIdBytes32, { value: fee });
+ console.log(`[EntropyBridge] Transaction sent: ${tx.hash}`);
+ console.log(`[EntropyBridge] View on Base Sepolia: https://sepolia.basescan.org/tx/${tx.hash}`);
+
+ const receipt = await tx.wait();
+ console.log(`[EntropyBridge] Transaction confirmed in block ${receipt.blockNumber}`);
+
+ // Parse EntropyRequested event
+ const entropyRequestedEvent = receipt.logs
+ .map((log: any) => {
+ try {
+ return this.entropyOracle.interface.parseLog(log);
+ } catch {
+ return null;
+ }
+ })
+ .find((event: any) => event && event.name === 'EntropyRequested');
+
+ if (entropyRequestedEvent) {
+ const requestId = entropyRequestedEvent.args.requestId.toString();
+ console.log(`[EntropyBridge] โ
Entropy requested! Request ID: ${requestId}`);
+ return requestId;
+ } else {
+ console.error('[EntropyBridge] EntropyRequested event not found in receipt');
+ return null;
+ }
+ } catch (error) {
+ console.error('[EntropyBridge] Error requesting entropy:', error);
+ return null;
+ }
+ }
+
+ /**
+ * Check if seed is available for a match
+ * @param matchId Match identifier string
+ */
+ async isSeedAvailable(matchId: string): Promise {
+ if (!this.enabled) {
+ return false;
+ }
+
+ try {
+ const matchIdBytes32 = ethers.id(matchId);
+ return await this.entropyOracle.isSeedAvailable(matchIdBytes32);
+ } catch (error) {
+ console.error('[EntropyBridge] Error checking seed availability:', error);
+ return false;
+ }
+ }
+
+ /**
+ * Get the revealed seed for a match
+ * @param matchId Match identifier string
+ * @returns Seed (bytes32 string) or null if not available
+ */
+ async getMatchSeed(matchId: string): Promise {
+ if (!this.enabled) {
+ return null;
+ }
+
+ try {
+ const matchIdBytes32 = ethers.id(matchId);
+ const seed = await this.entropyOracle.getMatchSeed(matchIdBytes32);
+
+ if (seed === ethers.ZeroHash) {
+ return null;
+ }
+
+ console.log(`[EntropyBridge] โ
Retrieved seed for match ${matchId}: ${seed}`);
+ return seed;
+ } catch (error) {
+ console.error('[EntropyBridge] Error getting match seed:', error);
+ return null;
+ }
+ }
+
+ /**
+ * Wait for match seed to be available (with timeout)
+ * Non-blocking: polls asynchronously without blocking game loop
+ * @param matchId Match identifier string
+ * @param maxWaitMs Maximum time to wait in milliseconds (default 60s)
+ * @param pollIntervalMs Polling interval in milliseconds (default 3s)
+ * @returns Seed or null if timeout
+ */
+ async waitForMatchSeed(
+ matchId: string,
+ maxWaitMs: number = 60000,
+ pollIntervalMs: number = 3000
+ ): Promise {
+ if (!this.enabled) {
+ console.warn('[EntropyBridge] Service disabled, cannot wait for seed');
+ return null;
+ }
+
+ console.log(`[EntropyBridge] Waiting for seed for match ${matchId}...`);
+ console.log(`[EntropyBridge] Max wait: ${maxWaitMs / 1000}s, poll interval: ${pollIntervalMs / 1000}s`);
+
+ const startTime = Date.now();
+ let attempt = 0;
+
+ while (Date.now() - startTime < maxWaitMs) {
+ attempt++;
+
+ // Check if seed is available
+ const available = await this.isSeedAvailable(matchId);
+
+ if (available) {
+ const seed = await this.getMatchSeed(matchId);
+ if (seed) {
+ console.log(`[EntropyBridge] โ
Seed available after ${(Date.now() - startTime) / 1000}s (${attempt} attempts)`);
+ return seed;
+ }
+ }
+
+ // Calculate dynamic poll interval with exponential backoff
+ const currentPollInterval = Math.min(pollIntervalMs * Math.pow(1.2, Math.floor(attempt / 3)), 10000);
+
+ console.log(`[EntropyBridge] Seed not yet available (attempt ${attempt}, waited ${Math.floor((Date.now() - startTime) / 1000)}s)...`);
+
+ // Sleep before next poll
+ await this.sleep(currentPollInterval);
+ }
+
+ console.error(`[EntropyBridge] โฑ๏ธ Timeout waiting for seed after ${maxWaitMs / 1000}s`);
+ return null;
+ }
+
+ /**
+ * Request entropy and wait for result in one call
+ * @param matchId Match identifier
+ * @param maxWaitMs Maximum wait time
+ * @returns Seed or null
+ */
+ async requestAndWaitForSeed(matchId: string, maxWaitMs: number = 60000): Promise {
+ const requestId = await this.requestMatchEntropy(matchId);
+
+ if (!requestId) {
+ console.error('[EntropyBridge] Failed to request entropy');
+ return null;
+ }
+
+ console.log('[EntropyBridge] Waiting for Pyth to reveal randomness...');
+ return await this.waitForMatchSeed(matchId, maxWaitMs);
+ }
+
+ /**
+ * Get current entropy fee
+ */
+ async getEntropyFee(): Promise {
+ if (!this.enabled) {
+ return 0n;
+ }
+
+ try {
+ return await this.entropyOracle.getEntropyFee();
+ } catch (error) {
+ console.error('[EntropyBridge] Error getting entropy fee:', error);
+ return 0n;
+ }
+ }
+
+ /**
+ * Sleep utility for polling
+ */
+ private sleep(ms: number): Promise {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+
+ /**
+ * Get server wallet address on Base Sepolia
+ */
+ getServerAddress(): string {
+ return this.baseSepoliaWallet.address;
+ }
+
+ /**
+ * Get server balance on Base Sepolia
+ */
+ async getServerBalance(): Promise {
+ if (!this.enabled) {
+ return '0';
+ }
+
+ try {
+ const balance = await this.baseSepoliaProvider.getBalance(this.baseSepoliaWallet.address);
+ return ethers.formatEther(balance);
+ } catch (error) {
+ console.error('[EntropyBridge] Error getting server balance:', error);
+ return '0';
+ }
+ }
+}
+
diff --git a/entropy/0xSlither/server/src/GameServer.ts b/entropy/0xSlither/server/src/GameServer.ts
new file mode 100644
index 0000000..08e0834
--- /dev/null
+++ b/entropy/0xSlither/server/src/GameServer.ts
@@ -0,0 +1,499 @@
+import { SnakeEntity } from './Snake';
+import { PelletManager } from './Pellet';
+import { CollisionDetection } from './CollisionDetection';
+import { Leaderboard } from './Leaderboard';
+import { BlockchainService } from './BlockchainService';
+import { EntropyBridgeService } from './EntropyBridgeService';
+import { DeterministicRNG } from './DeterministicRNG';
+import {
+ StateMessage,
+ MessageType,
+ SerializedSnake,
+ SerializedPellet,
+ TICK_INTERVAL,
+ WORLD_WIDTH,
+ WORLD_HEIGHT,
+ PELLET_COUNT,
+} from '@0xslither/shared';
+
+interface PendingSnakeAdd {
+ id: string;
+ snake: SnakeEntity;
+}
+
+export class GameServer {
+ private snakes: Map = new Map();
+ private pelletManager: PelletManager;
+ private tickCount = 0;
+ private lastTickTime = Date.now();
+ private gameLoop: NodeJS.Timeout | null = null;
+ private blockchain: BlockchainService | null = null;
+ private permanentMatchId: string = `permanent-match-${Date.now()}`; // Unique match ID per server restart
+ private entropyBridge: EntropyBridgeService | null = null;
+ private matchRNG: DeterministicRNG | null = null;
+ private entropyRequestId: string | null = null;
+ private entropyPending: boolean = false;
+ private devFallbackMode: boolean = false;
+ private entropySeed: string | null = null;
+ private consumedPelletsThisTick: Set = new Set();
+ private pelletTokensDistributed: boolean = false;
+
+ // Track player stakes for kill rewards (deposited via vault)
+ private playerStakes: Map = new Map(); // address -> stake amount
+ private readonly FIXED_STAKE_AMOUNT = 1; // Fixed 1 SSS stake per player
+
+ // Atomic operation queues - processed at start of each tick
+ private pendingAdds: PendingSnakeAdd[] = [];
+ private pendingRemoves: string[] = [];
+
+ constructor() {
+ // Pellet manager will be initialized after entropy is available
+ this.pelletManager = new PelletManager(PELLET_COUNT);
+ console.log(`๐ฎ Game server initialized with permanent match ID: ${this.permanentMatchId}`);
+ }
+
+ setBlockchainService(blockchain: BlockchainService): void {
+ this.blockchain = blockchain;
+
+ // Initialize entropy bridge service
+ this.entropyBridge = new EntropyBridgeService();
+
+ // Request entropy for the permanent match in the background
+ this.initializeMatchEntropy();
+ }
+
+ /**
+ * Get the permanent match ID for this continuous game
+ */
+ getMatchId(): string {
+ return this.permanentMatchId;
+ }
+
+ /**
+ * Register a player's stake when they join (for kill reward tracking)
+ * @param address Player's address
+ * @param amount Stake amount (default: 1 SSS)
+ */
+ registerPlayerStake(address: string, amount: number = 1): void {
+ this.playerStakes.set(address, amount);
+ console.log(`๐ฐ Registered stake: ${amount} SSS for ${address.slice(0, 8)}`);
+ }
+
+ /**
+ * Initialize match entropy (non-blocking)
+ * Requests randomness from Base Sepolia and initializes RNG when available
+ * Uses permanent match ID for continuous gameplay
+ */
+ private async initializeMatchEntropy(): Promise {
+ if (!this.entropyBridge || !this.entropyBridge.isEnabled()) {
+ console.warn('โ ๏ธ Entropy bridge not available, using fallback RNG');
+ this.devFallbackMode = true;
+ return;
+ }
+
+ console.log('๐ฒ Initializing match entropy for permanent match...');
+ this.entropyPending = true;
+
+ try {
+ // Request entropy and wait for reveal (non-blocking, runs in background)
+ const seed = await this.entropyBridge.requestAndWaitForSeed(this.permanentMatchId, 60000);
+
+ if (seed) {
+ console.log('โ
Entropy seed received:', seed);
+
+ // Store the seed for display
+ this.entropySeed = seed;
+
+ // Initialize deterministic RNG
+ this.matchRNG = new DeterministicRNG(seed);
+ console.log('โ
Deterministic RNG initialized');
+
+ // Reinitialize pellet field with deterministic positions
+ const deterministicPellets = this.matchRNG.getPelletPositions(PELLET_COUNT);
+ this.pelletManager = new PelletManager(PELLET_COUNT, deterministicPellets);
+
+ // Distribute pellet tokens after pellets are initialized
+ this.distributePelletTokens();
+
+ // Get map type for logging
+ const mapType = this.matchRNG.getMapType();
+ console.log(`๐บ๏ธ Map type: ${mapType}`);
+
+ // Commit seed hash to Saga
+ if (this.blockchain && this.entropyRequestId) {
+ await this.blockchain.commitEntropyToSaga(this.permanentMatchId, this.entropyRequestId, seed);
+ console.log('โ
Seed hash committed to Saga');
+ }
+
+ this.entropyPending = false;
+ this.devFallbackMode = false;
+ console.log('๐ฎ Continuous match ready with fair RNG!');
+ } else {
+ console.error('โฑ๏ธ Entropy timeout - falling back to non-cryptographic RNG');
+ this.devFallbackMode = true;
+ this.entropyPending = false;
+ }
+ } catch (error) {
+ console.error('โ Error initializing match entropy:', error);
+ this.devFallbackMode = true;
+ this.entropyPending = false;
+ }
+ }
+
+ start(onTickComplete?: () => void): void {
+ console.log('Game server started');
+ this.gameLoop = setInterval(() => {
+ this.tick();
+ // Call callback after each tick to synchronize broadcasts
+ if (onTickComplete) {
+ onTickComplete();
+ }
+ }, TICK_INTERVAL);
+ }
+
+ stop(): void {
+ if (this.gameLoop) {
+ clearInterval(this.gameLoop);
+ this.gameLoop = null;
+ }
+ }
+
+ /**
+ * Process pending add/remove operations atomically at the start of each tick
+ * This prevents race conditions from snakes being added/removed mid-tick
+ */
+ private processPendingOperations(): void {
+ // Process removals first
+ for (const snakeId of this.pendingRemoves) {
+ this.snakes.delete(snakeId);
+ console.log(`Snake ${snakeId} removed (queued operation)`);
+ }
+ this.pendingRemoves = [];
+
+ // Then process additions - use the already-created snake instances
+ for (const addOp of this.pendingAdds) {
+ this.snakes.set(addOp.id, addOp.snake);
+ console.log(`Snake ${addOp.snake.name} (${addOp.id}) added to game (queued operation)`);
+ }
+ this.pendingAdds = [];
+ }
+
+ /**
+ * Internal method to create a snake immediately (called during tick processing)
+ */
+ private createSnakeImmediate(id: string, name: string, address?: string): SnakeEntity {
+ let x: number, y: number, color: string;
+
+ if (this.matchRNG && address) {
+ // Deterministic mode: use entropy-derived spawn and color
+ const existingPositions = Array.from(this.snakes.values())
+ .filter(s => s.alive)
+ .map(s => ({ x: s.headPosition.x, y: s.headPosition.y }));
+
+ const position = this.matchRNG.getSpawnPositionWithRetry(address, existingPositions, 10);
+ x = position.x;
+ y = position.y;
+ color = this.matchRNG.getSnakeColor(address);
+
+ console.log(`โ
Deterministic spawn for ${address.slice(0, 8)}... at (${x.toFixed(0)}, ${y.toFixed(0)})`);
+ } else {
+ // Fallback mode: random spawn
+ const margin = 500;
+ x = margin + Math.random() * (WORLD_WIDTH - 2 * margin);
+ y = margin + Math.random() * (WORLD_HEIGHT - 2 * margin);
+
+ // Generate color based on player ID or address
+ const seedString = address || id;
+ const hue = parseInt(seedString.slice(-6), 36) % 360;
+ color = `hsl(${hue}, 70%, 60%)`;
+
+ if (this.devFallbackMode) {
+ console.warn(`โ ๏ธ Fallback spawn for ${name} at (${x.toFixed(0)}, ${y.toFixed(0)}) - not deterministic`);
+ }
+ }
+
+ return new SnakeEntity(id, name, x, y, color, address);
+ }
+
+ private tick(): void {
+ const now = Date.now();
+ const deltaTime = (now - this.lastTickTime) / 1000; // Convert to seconds
+ this.lastTickTime = now;
+ this.tickCount++;
+
+ // Process pending operations atomically at the start of tick
+ this.processPendingOperations();
+
+ // Distribute pellet tokens if not already done (fallback mode)
+ if (!this.pelletTokensDistributed && this.snakes.size > 0) {
+ this.distributePelletTokens();
+ }
+
+ // Clear consumed pellets tracking for this tick
+ this.consumedPelletsThisTick.clear();
+
+ // Update all snakes
+ for (const snake of this.snakes.values()) {
+ if (snake.alive) {
+ snake.update(deltaTime);
+ }
+ }
+
+ // Check pellet collisions
+ for (const snake of this.snakes.values()) {
+ if (!snake.alive) continue;
+
+ for (const pellet of this.pelletManager.getPellets()) {
+ // Skip if this pellet was already consumed this tick (race condition prevention)
+ if (this.consumedPelletsThisTick.has(pellet.id)) {
+ continue;
+ }
+
+ if (CollisionDetection.checkPelletCollision(snake, pellet)) {
+ // Mark pellet as consumed to prevent duplicate consumption
+ this.consumedPelletsThisTick.add(pellet.id);
+
+ // Growth is proportional to pellet size
+ // Min size (4) gives 2 segments, max size (8) gives 4 segments
+ const growthAmount = Math.round(pellet.size / 2);
+ snake.grow(growthAmount);
+
+ // Transfer pellet tokens to snake
+ snake.addPelletTokens(pellet.tokenAmount);
+
+ this.pelletManager.removePellet(pellet.id);
+ }
+ }
+ }
+
+ // Check snake collisions
+ const aliveSnakes = Array.from(this.snakes.values()).filter(s => s.alive);
+ const collisions = CollisionDetection.checkSnakeCollisions(aliveSnakes);
+
+ for (const collision of collisions) {
+ const victim = this.snakes.get(collision.victimId);
+ if (victim && victim.alive) {
+ const victimScore = victim.getScore();
+ victim.kill();
+
+ // If killed by another snake (not self-collision), transfer rewards
+ if (collision.killerId && collision.killerId !== collision.victimId) {
+ const killer = this.snakes.get(collision.killerId);
+ if (killer && killer.alive) {
+ // Killer gains the victim's score
+ killer.grow(victimScore);
+
+ // Transfer victim's pellet tokens to killer (internal balance)
+ const victimPelletTokens = victim.getPelletTokens();
+ killer.addPelletTokens(victimPelletTokens);
+
+ console.log(`${killer.name} ate ${victim.name} and gained ${victimScore} points and ${victimPelletTokens.toFixed(2)} pellet tokens!`);
+
+ // VAULT MODE: Transfer victim's STAKE from server wallet to killer immediately
+ if (this.blockchain && killer.address && victim.address) {
+ const victimStake = this.playerStakes.get(victim.address) || this.FIXED_STAKE_AMOUNT;
+
+ // Transfer stake reward from server wallet to killer
+ this.blockchain.transferKillReward(killer.address, victimStake)
+ .catch(err => console.error('Error transferring kill reward:', err));
+
+ // Update stake tracking: killer gets victim's stake, victim loses it
+ const killerCurrentStake = this.playerStakes.get(killer.address) || this.FIXED_STAKE_AMOUNT;
+ this.playerStakes.set(killer.address, killerCurrentStake + victimStake);
+ this.playerStakes.delete(victim.address); // Victim loses their stake
+
+ console.log(`๐ฐ Kill reward: ${victimStake} SSS transferred from server wallet to ${killer.address.slice(0, 8)}`);
+
+ // Report eat to blockchain for stats/leaderboard (no stake transfer on-chain)
+ this.blockchain.reportEat(this.permanentMatchId, killer.address, victim.address)
+ .catch(err => console.error('Error reporting eat to blockchain:', err));
+ }
+ }
+ } else {
+ // Self-collision or no killer - report self death to blockchain
+ console.log(`${victim.name} died from self-collision with score ${victimScore}`);
+ if (this.blockchain && victim.address) {
+ // Remove victim's stake tracking (server keeps it)
+ this.playerStakes.delete(victim.address);
+
+ console.log(`Reporting self-collision death for ${victim.address} to blockchain`);
+ this.blockchain.reportSelfDeath(this.permanentMatchId, victim.address, victimScore)
+ .catch(err => console.error('Error reporting self death:', err));
+ }
+ }
+ }
+ }
+
+ // Check world bounds
+ for (const snake of this.snakes.values()) {
+ if (snake.alive && CollisionDetection.checkWorldBounds(snake, WORLD_WIDTH, WORLD_HEIGHT)) {
+ const snakeScore = snake.getScore();
+ console.log(`${snake.name} died from wall collision with score ${snakeScore}`);
+ snake.kill();
+
+ // Report wall collision death to blockchain
+ if (this.blockchain && snake.address) {
+ // Remove victim's stake tracking (server keeps it)
+ this.playerStakes.delete(snake.address);
+
+ console.log(`Reporting wall collision death for ${snake.address} to blockchain`);
+ this.blockchain.reportSelfDeath(this.permanentMatchId, snake.address, snakeScore)
+ .catch(err => console.error('Error reporting wall death:', err));
+ }
+ }
+ }
+ }
+
+ addSnake(id: string, name: string, address?: string, immediate: boolean = true): SnakeEntity {
+ // Create the snake
+ const snake = this.createSnakeImmediate(id, name, address);
+
+ if (immediate) {
+ // Add immediately - safe for player joins which happen between ticks
+ this.snakes.set(id, snake);
+ const addressLog = address ? ` wallet: ${address}` : '';
+ console.log(`Snake ${name} (${id})${addressLog} added immediately`);
+ } else {
+ // Queue it for addition to the game on next tick
+ // Use this when adding during tick processing to prevent race conditions
+ this.pendingAdds.push({ id, snake });
+ console.log(`Snake ${name} (${id}) created and queued for addition on next tick`);
+ }
+
+ return snake;
+ }
+
+ removeSnake(id: string, immediate: boolean = false): void {
+ if (immediate) {
+ // Remove immediately - safe when called between ticks (e.g., during disconnect)
+ this.snakes.delete(id);
+ console.log(`Snake ${id} removed immediately`);
+ } else {
+ // Queue the remove operation for next tick processing
+ // Use this to avoid removing during tick processing
+ if (!this.pendingRemoves.includes(id)) {
+ this.pendingRemoves.push(id);
+ console.log(`Snake ${id} queued for removal on next tick`);
+ }
+ }
+ }
+
+ removeSnakeByAddress(address: string, immediate: boolean = true): void {
+ // Find snake with this address and remove it
+ // Default to immediate since this is typically called during join handling
+ for (const [id, snake] of this.snakes.entries()) {
+ if (snake.address === address) {
+ if (immediate) {
+ this.snakes.delete(id);
+ console.log(`Snake ${id} with address ${address} removed immediately (duplicate login cleanup)`);
+ } else {
+ if (!this.pendingRemoves.includes(id)) {
+ this.pendingRemoves.push(id);
+ console.log(`Snake ${id} with address ${address} queued for removal (duplicate login cleanup)`);
+ }
+ }
+ return;
+ }
+ }
+ }
+
+ getSnake(id: string): SnakeEntity | undefined {
+ return this.snakes.get(id);
+ }
+
+ setSnakeTargetAngle(id: string, angle: number): void {
+ const snake = this.snakes.get(id);
+ if (snake && snake.alive) {
+ snake.setTargetAngle(angle);
+ }
+ }
+
+ getGameState(): StateMessage {
+ const snakes: SerializedSnake[] = Array.from(this.snakes.values())
+ .filter(snake => snake.alive)
+ .map(snake => ({
+ id: snake.id,
+ name: snake.name,
+ address: snake.address,
+ head: [snake.headPosition.x, snake.headPosition.y] as [number, number],
+ angle: snake.angle,
+ segments: snake.segments.map(seg => [seg.x, seg.y] as [number, number]),
+ color: snake.color,
+ pelletTokens: snake.pelletTokens,
+ }));
+
+ const pellets: SerializedPellet[] = this.pelletManager
+ .getPellets()
+ .map(pellet => [pellet.id, pellet.x, pellet.y, pellet.size, pellet.color, pellet.tokenAmount] as SerializedPellet);
+
+ const leaderboard = Leaderboard.compute(Array.from(this.snakes.values()));
+
+ return {
+ type: MessageType.STATE,
+ tick: this.tickCount,
+ snakes,
+ pellets,
+ leaderboard: leaderboard.map(entry => [entry.name, entry.score, entry.address] as [string, number, string?]),
+ matchId: this.permanentMatchId,
+ entropyPending: this.entropyPending,
+ entropyRequestId: this.entropyRequestId || undefined,
+ useFairRNG: !this.devFallbackMode,
+ mapType: this.matchRNG?.getMapType(),
+ entropySeed: this.entropySeed || undefined,
+ };
+ }
+
+ /**
+ * Get match entropy info for debugging/display
+ */
+ getEntropyInfo(): {
+ requestId: string | null;
+ hasSeed: boolean;
+ pending: boolean;
+ fallbackMode: boolean;
+ mapType?: string;
+ } {
+ return {
+ requestId: this.entropyRequestId,
+ hasSeed: this.matchRNG !== null,
+ pending: this.entropyPending,
+ fallbackMode: this.devFallbackMode,
+ mapType: this.matchRNG?.getMapType(),
+ };
+ }
+
+ isSnakeDead(id: string): boolean {
+ const snake = this.snakes.get(id);
+ return snake ? !snake.alive : true;
+ }
+
+ getSnakeScore(id: string): number {
+ const snake = this.snakes.get(id);
+ return snake ? snake.getScore() : 0;
+ }
+
+ /**
+ * Distribute pellet tokens across all pellets
+ * Formula: (# pellets * # players) / 300 SSS
+ */
+ private distributePelletTokens(): void {
+ if (this.pelletTokensDistributed) {
+ return; // Already distributed
+ }
+
+ const pelletCount = this.pelletManager.getPellets().length;
+ const playerCount = this.snakes.size;
+
+ if (playerCount === 0) {
+ // No players yet, wait
+ return;
+ }
+
+ const totalPelletTokens = (pelletCount * playerCount) / 300;
+ this.pelletManager.distributePelletTokens(totalPelletTokens);
+ this.pelletTokensDistributed = true;
+
+ console.log(`๐ฐ Pellet tokens distributed: ${totalPelletTokens.toFixed(2)} SSS across ${pelletCount} pellets for ${playerCount} players`);
+ }
+}
+
diff --git a/entropy/0xSlither/server/src/Leaderboard.ts b/entropy/0xSlither/server/src/Leaderboard.ts
new file mode 100644
index 0000000..75b5fb0
--- /dev/null
+++ b/entropy/0xSlither/server/src/Leaderboard.ts
@@ -0,0 +1,17 @@
+import { SnakeEntity } from './Snake';
+import { LeaderboardEntry, LEADERBOARD_SIZE } from '@0xslither/shared';
+
+export class Leaderboard {
+ static compute(snakes: SnakeEntity[]): LeaderboardEntry[] {
+ return snakes
+ .filter(snake => snake.alive)
+ .map(snake => ({
+ name: snake.name,
+ score: snake.getScore(),
+ address: snake.address,
+ }))
+ .sort((a, b) => b.score - a.score)
+ .slice(0, LEADERBOARD_SIZE);
+ }
+}
+
diff --git a/entropy/0xSlither/server/src/Pellet.ts b/entropy/0xSlither/server/src/Pellet.ts
new file mode 100644
index 0000000..ce64f0f
--- /dev/null
+++ b/entropy/0xSlither/server/src/Pellet.ts
@@ -0,0 +1,121 @@
+import {
+ Pellet,
+ WORLD_WIDTH,
+ WORLD_HEIGHT,
+ PELLET_MIN_SIZE,
+ PELLET_MAX_SIZE,
+ PELLET_COLORS,
+} from '@0xslither/shared';
+
+export class PelletManager {
+ private pellets: Map = new Map();
+ private nextId = 0;
+ private deterministicMode: boolean = false;
+ private changedPelletIds: Set = new Set();
+
+ constructor(count: number, initialPellets?: Array<{ x: number; y: number; size: number; color: string }>) {
+ if (initialPellets && initialPellets.length > 0) {
+ // Deterministic mode: use provided pellet positions
+ this.deterministicMode = true;
+ for (const pellet of initialPellets) {
+ this.addPellet(pellet.x, pellet.y, pellet.size, pellet.color);
+ }
+ console.log(`โ
Pellet field initialized deterministically with ${initialPellets.length} pellets`);
+ } else {
+ // Fallback mode: use random spawning
+ this.deterministicMode = false;
+ for (let i = 0; i < count; i++) {
+ this.spawnPellet();
+ }
+ console.log(`โ ๏ธ Pellet field initialized with random positions (${count} pellets)`);
+ }
+ }
+
+ private addPellet(x: number, y: number, size: number, color: string): void {
+ const pellet: Pellet = {
+ id: `pellet-${this.nextId++}`,
+ x,
+ y,
+ size,
+ color,
+ tokenAmount: 0, // Will be set during token distribution
+ };
+ this.pellets.set(pellet.id, pellet);
+ }
+
+ private spawnPellet(): void {
+ const pellet: Pellet = {
+ id: `pellet-${this.nextId++}`,
+ x: Math.random() * WORLD_WIDTH,
+ y: Math.random() * WORLD_HEIGHT,
+ size: PELLET_MIN_SIZE + Math.random() * (PELLET_MAX_SIZE - PELLET_MIN_SIZE),
+ color: PELLET_COLORS[Math.floor(Math.random() * PELLET_COLORS.length)],
+ tokenAmount: 0, // Will be set during token distribution
+ };
+
+ this.pellets.set(pellet.id, pellet);
+ }
+
+ removePellet(id: string): void {
+ this.pellets.delete(id);
+ // Pellets are NOT respawned - once consumed, they're gone
+ }
+
+ getPellets(): Pellet[] {
+ return Array.from(this.pellets.values());
+ }
+
+ getPellet(id: string): Pellet | undefined {
+ return this.pellets.get(id);
+ }
+
+ getChangedPellets(): Pellet[] {
+ const changed: Pellet[] = [];
+ for (const id of this.changedPelletIds) {
+ const pellet = this.pellets.get(id);
+ if (pellet) {
+ changed.push(pellet);
+ }
+ }
+ return changed;
+ }
+
+ clearChanges(): void {
+ this.changedPelletIds.clear();
+ }
+
+ hasChanges(): boolean {
+ return this.changedPelletIds.size > 0;
+ }
+
+ /**
+ * Distribute tokens across all pellets proportionally by size
+ * @param totalAmount Total SSS tokens to distribute
+ */
+ distributePelletTokens(totalAmount: number): void {
+ const pellets = Array.from(this.pellets.values());
+
+ if (pellets.length === 0) {
+ console.warn('No pellets to distribute tokens to');
+ return;
+ }
+
+ // Calculate total size weight
+ const totalSizeWeight = pellets.reduce((sum, p) => sum + p.size, 0);
+
+ // Distribute tokens proportionally by size
+ let distributedTotal = 0;
+ pellets.forEach((pellet, index) => {
+ if (index === pellets.length - 1) {
+ // Last pellet gets remainder to avoid rounding errors
+ pellet.tokenAmount = totalAmount - distributedTotal;
+ } else {
+ pellet.tokenAmount = (pellet.size / totalSizeWeight) * totalAmount;
+ distributedTotal += pellet.tokenAmount;
+ }
+ });
+
+ console.log(`โ
Distributed ${totalAmount} SSS tokens across ${pellets.length} pellets`);
+ }
+}
+
diff --git a/entropy/0xSlither/server/src/Snake.ts b/entropy/0xSlither/server/src/Snake.ts
new file mode 100644
index 0000000..f1e6ddf
--- /dev/null
+++ b/entropy/0xSlither/server/src/Snake.ts
@@ -0,0 +1,164 @@
+import {
+ Position,
+ Snake,
+ SnakeSegment,
+ SNAKE_BASE_SPEED,
+ SNAKE_MAX_ROTATION_SPEED,
+ SNAKE_INITIAL_LENGTH,
+ SNAKE_SEGMENT_SPACING,
+ SNAKE_HEAD_RADIUS,
+ SNAKE_SEGMENT_RADIUS,
+ SNAKE_GROWTH_PER_PELLET,
+ TICK_RATE,
+} from '@0xslither/shared';
+
+export class SnakeEntity implements Snake {
+ id: string;
+ name: string;
+ address?: string; // Optional EVM wallet address
+ headPosition: Position;
+ angle: number;
+ segments: SnakeSegment[];
+ length: number;
+ color: string;
+ alive: boolean;
+ targetAngle: number;
+ pelletTokens: number;
+
+ // Track position history for body segments
+ private positionHistory: Position[] = [];
+
+ constructor(id: string, name: string, x: number, y: number, color: string, address?: string) {
+ this.id = id;
+ this.name = name;
+ this.address = address;
+ this.headPosition = { x, y };
+ this.angle = Math.random() * Math.PI * 2;
+ this.targetAngle = this.angle;
+ this.length = SNAKE_INITIAL_LENGTH;
+ this.color = color;
+ this.alive = true;
+ this.pelletTokens = 0;
+ this.segments = [];
+
+ // Initialize position history with segments trailing behind the head
+ // This prevents immediate self-collision
+ const historyLength = this.length * 3;
+ for (let i = 0; i < historyLength; i++) {
+ // Place each history point behind the head based on initial angle
+ const distance = i * 2; // Small spacing for smooth initial trail
+ this.positionHistory.push({
+ x: x - Math.cos(this.angle) * distance,
+ y: y - Math.sin(this.angle) * distance,
+ });
+ }
+
+ this.updateSegments();
+ }
+
+ setTargetAngle(angle: number): void {
+ this.targetAngle = angle;
+ }
+
+ update(deltaTime: number): void {
+ if (!this.alive) return;
+
+ // Smooth rotation toward target angle
+ const maxRotation = SNAKE_MAX_ROTATION_SPEED * deltaTime;
+ let angleDiff = this.targetAngle - this.angle;
+
+ // Normalize angle difference to [-ฯ, ฯ]
+ while (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
+ while (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
+
+ if (Math.abs(angleDiff) < maxRotation) {
+ this.angle = this.targetAngle;
+ } else {
+ this.angle += Math.sign(angleDiff) * maxRotation;
+ }
+
+ // Normalize angle to [0, 2ฯ]
+ this.angle = (this.angle + Math.PI * 2) % (Math.PI * 2);
+
+ // Move head forward
+ const speed = SNAKE_BASE_SPEED * deltaTime;
+ this.headPosition.x += Math.cos(this.angle) * speed;
+ this.headPosition.y += Math.sin(this.angle) * speed;
+
+ // Add current head position to history
+ this.positionHistory.unshift({ ...this.headPosition });
+
+ // Keep history size reasonable
+ const maxHistory = this.length * 3;
+ if (this.positionHistory.length > maxHistory) {
+ this.positionHistory.length = maxHistory;
+ }
+
+ this.updateSegments();
+ }
+
+ private updateSegments(): void {
+ this.segments = [];
+ let distanceAlongPath = SNAKE_SEGMENT_SPACING;
+
+ for (let i = 0; i < this.length; i++) {
+ const position = this.getPositionAtDistance(distanceAlongPath);
+ if (position) {
+ this.segments.push({
+ x: position.x,
+ y: position.y,
+ radius: SNAKE_SEGMENT_RADIUS,
+ });
+ }
+ distanceAlongPath += SNAKE_SEGMENT_SPACING;
+ }
+ }
+
+ private getPositionAtDistance(targetDistance: number): Position | null {
+ let accumulated = 0;
+
+ for (let i = 0; i < this.positionHistory.length - 1; i++) {
+ const current = this.positionHistory[i];
+ const next = this.positionHistory[i + 1];
+
+ const dx = next.x - current.x;
+ const dy = next.y - current.y;
+ const segmentLength = Math.sqrt(dx * dx + dy * dy);
+
+ if (accumulated + segmentLength >= targetDistance) {
+ // Interpolate position
+ const ratio = (targetDistance - accumulated) / segmentLength;
+ return {
+ x: current.x + dx * ratio,
+ y: current.y + dy * ratio,
+ };
+ }
+
+ accumulated += segmentLength;
+ }
+
+ // Return last position if not enough history
+ return this.positionHistory[this.positionHistory.length - 1] || null;
+ }
+
+ grow(amount: number = SNAKE_GROWTH_PER_PELLET): void {
+ this.length += amount;
+ }
+
+ kill(): void {
+ this.alive = false;
+ }
+
+ getScore(): number {
+ return this.length;
+ }
+
+ addPelletTokens(amount: number): void {
+ this.pelletTokens += amount;
+ }
+
+ getPelletTokens(): number {
+ return this.pelletTokens;
+ }
+}
+
diff --git a/entropy/0xSlither/server/src/index.ts b/entropy/0xSlither/server/src/index.ts
new file mode 100644
index 0000000..c9f148c
--- /dev/null
+++ b/entropy/0xSlither/server/src/index.ts
@@ -0,0 +1,311 @@
+import { WebSocketServer, WebSocket } from 'ws';
+import { GameServer } from './GameServer';
+import { BlockchainService } from './BlockchainService';
+import { DeltaCompressor } from './DeltaCompressor';
+import * as dotenv from 'dotenv';
+import {
+ ClientMessage,
+ ServerMessage,
+ MessageType,
+ DeadMessage,
+ TapOutSuccessMessage,
+ isJoinMessage,
+ isInputMessage,
+ isPingMessage,
+ isTapOutMessage,
+} from '@0xslither/shared';
+
+dotenv.config();
+
+const PORT = process.env.PORT;
+
+class Player {
+ public disconnecting: boolean = false;
+
+ constructor(
+ public ws: WebSocket,
+ public id: string,
+ public snakeId: string | null = null
+ ) {}
+}
+
+class WebSocketGameServer {
+ private wss: WebSocketServer;
+ private gameServer: GameServer;
+ private blockchain: BlockchainService | null = null;
+ private players: Map = new Map();
+ private nextPlayerId = 0;
+ private deltaCompressor: DeltaCompressor = new DeltaCompressor();
+ private readonly FIXED_STAKE_AMOUNT = 1; // Fixed 1 SSS stake per player
+
+ constructor(port: number) {
+ this.gameServer = new GameServer();
+ this.wss = new WebSocketServer({ port });
+
+ // Initialize blockchain
+ try {
+ const rpcUrl = process.env.SAGA_RPC_URL as string;
+ const privateKey = process.env.SERVER_PRIVATE_KEY as string;
+ const stakeArenaAddress = process.env.STAKE_ARENA_ADDRESS as string;
+
+ if (!privateKey || !stakeArenaAddress) {
+ console.warn('โ ๏ธ Blockchain integration disabled: Missing SERVER_PRIVATE_KEY or STAKE_ARENA_ADDRESS');
+ } else {
+ this.blockchain = new BlockchainService(rpcUrl, privateKey, stakeArenaAddress);
+ console.log('โ
Blockchain integration enabled (Native SSS)');
+ console.log('๐ฆ Using server vault model for continuous gameplay');
+ console.log(`๐ Permanent Match ID: ${this.gameServer.getMatchId()}`);
+ console.log(`๐ Match ID (bytes32): ${this.blockchain.generateMatchId(this.gameServer.getMatchId())}`);
+ }
+ } catch (error) {
+ console.error('Failed to initialize blockchain:', error);
+ }
+ this.wss.on('connection', (ws: WebSocket) => this.handleConnection(ws));
+
+ console.log(`WebSocket server listening on port ${port}`);
+ }
+
+ start(): void {
+ // Connect blockchain to game server if enabled
+ if (this.blockchain) {
+ this.gameServer.setBlockchainService(this.blockchain);
+ }
+
+ // Start game server with synchronized broadcast callback
+ // Broadcasts happen immediately after each tick completes
+ // Game runs continuously until server is killed
+ this.gameServer.start(() => {
+ this.broadcastGameState();
+ this.checkDeadSnakes();
+ });
+
+ console.log('๐ฎ Continuous match started! Players can join anytime.');
+ }
+
+ private handleConnection(ws: WebSocket): void {
+ const playerId = `player-${this.nextPlayerId++}`;
+ const player = new Player(ws, playerId);
+ this.players.set(ws, player);
+
+ console.log(`Player ${playerId} connected`);
+
+ ws.on('message', (data: Buffer) => {
+ try {
+ const message = JSON.parse(data.toString()) as ClientMessage;
+ this.handleMessage(player, message);
+ } catch (error) {
+ console.error('Error parsing message:', error);
+ }
+ });
+
+ ws.on('close', () => {
+ this.handleDisconnect(player);
+ });
+
+ ws.on('error', (error) => {
+ console.error(`WebSocket error for player ${playerId}:`, error);
+ });
+ }
+
+ private handleMessage(player: Player, message: ClientMessage): void {
+ if (isJoinMessage(message)) {
+ this.handleJoin(player, message.name, message.address);
+ } else if (isInputMessage(message)) {
+ this.handleInput(player, message.targetAngle);
+ } else if (isPingMessage(message)) {
+ this.handlePing(player, message.timestamp);
+ } else if (isTapOutMessage(message)) {
+ this.handleTapOut(player, message.matchId);
+ }
+ }
+
+ private async handleJoin(player: Player, name: string, address: string): Promise {
+ // VAULT MODE: Verify player has deposited to vault (optional check)
+ // In production, you may want to enforce this more strictly
+ if (this.blockchain) {
+ const hasDeposited = await this.blockchain.verifyVaultDeposit(address, this.FIXED_STAKE_AMOUNT.toString());
+ if (!hasDeposited) {
+ console.warn(`โ ๏ธ Player ${address.slice(0, 8)} joining without verified vault deposit`);
+ // For now, allow them to join anyway (server can enforce deposits client-side)
+ // In stricter mode, you could reject: return;
+ }
+ }
+
+ // Remove old snake if exists for this player object
+ // Use immediate removal since this is between ticks (during message handling)
+ if (player.snakeId) {
+ this.gameServer.removeSnake(player.snakeId, true);
+ }
+
+ // Remove any existing snake with the same wallet address
+ // (handles case where player disconnected improperly and is rejoining)
+ this.gameServer.removeSnakeByAddress(address);
+
+ // Register player's stake for kill reward tracking
+ this.gameServer.registerPlayerStake(address, this.FIXED_STAKE_AMOUNT);
+
+ // Create new snake with required wallet address
+ const snake = this.gameServer.addSnake(player.id, name, address);
+ player.snakeId = snake.id;
+
+ console.log(`Player ${player.id} joined as wallet ${address} (continuous match)`);
+
+ // Send initial state with player ID
+ const state = this.gameServer.getGameState();
+ this.sendMessage(player.ws, { ...state, yourId: player.id });
+ }
+
+ private handleInput(player: Player, targetAngle: number): void {
+ if (player.snakeId) {
+ this.gameServer.setSnakeTargetAngle(player.snakeId, targetAngle);
+ }
+ }
+
+ private handlePing(player: Player, timestamp: number): void {
+ this.sendMessage(player.ws, {
+ type: MessageType.PONG,
+ timestamp,
+ });
+ }
+
+ private async handleTapOut(player: Player, matchId: string): Promise {
+ if (!player.snakeId) {
+ console.log(`Player ${player.id} tried to tap out without active snake`);
+ return;
+ }
+
+ const snake = this.gameServer.getSnake(player.snakeId);
+ if (!snake || !snake.address) {
+ console.log(`Cannot tap out: no wallet address for player ${player.id}`);
+ return;
+ }
+
+ console.log(`Player ${player.id} tapping out from continuous match (address: ${snake.address.slice(0, 8)})`);
+
+ // Get pellet tokens before removing snake
+ const pelletTokens = snake.getPelletTokens();
+ const finalScore = snake.getScore();
+
+ // Remove snake from game immediately
+ // Use immediate removal since this is between ticks (during message handling)
+ this.gameServer.removeSnake(player.snakeId, true);
+ player.snakeId = null;
+
+ // VAULT MODE: Transfer pellet tokens only (stakes already distributed via kills)
+ // Player keeps any stake rewards they earned from kills (already in their wallet)
+ if (this.blockchain && snake.address) {
+ // Only settle pellet tokens - stake rewards were transferred immediately on kills
+ await this.blockchain.settlePelletTokens(snake.address, pelletTokens);
+ console.log(`๐ฐ Tap-out payout: ${pelletTokens.toFixed(2)} SSS (pellet tokens) to ${snake.address.slice(0, 8)}`);
+ }
+
+ // Send success message with pellet token amount
+ const tapOutMsg: TapOutSuccessMessage = {
+ type: MessageType.TAPOUT_SUCCESS,
+ amountWithdrawn: pelletTokens, // Only pellet tokens, not stake
+ };
+ this.sendMessage(player.ws, tapOutMsg);
+ }
+
+ private async handleDisconnect(player: Player): Promise {
+ console.log(`Player ${player.id} disconnected`);
+
+ // Mark player as disconnecting to prevent race conditions with game tick
+ player.disconnecting = true;
+
+ if (player.snakeId) {
+ // Get snake info before killing it
+ const snake = this.gameServer.getSnake(player.snakeId);
+
+ // If snake is still alive and has a wallet address, handle vault mode disconnect
+ if (snake && snake.alive && snake.address && this.blockchain) {
+ const snakeScore = snake.getScore();
+ const pelletTokens = snake.getPelletTokens();
+ console.log(`Player ${player.id} disconnected with active snake (score: ${snakeScore}, pellet tokens: ${pelletTokens.toFixed(2)})`);
+
+ // CRITICAL: Immediately kill the snake to prevent other players from eating it
+ // while we're doing async blockchain operations
+ snake.kill();
+
+ try {
+ // VAULT MODE: On disconnect, player loses everything
+ // - Server keeps the deposit (already in vault)
+ // - Pellet tokens are lost (not paid out)
+ // - Only report death for leaderboard
+ console.log(`๐ Player ${snake.address.slice(0, 8)} disconnected - loses deposit and pellet tokens (${pelletTokens.toFixed(2)} SSS)`);
+ await this.blockchain.reportSelfDeath(this.gameServer.getMatchId(), snake.address, snakeScore);
+
+ // NO pellet token payout on disconnect - server keeps everything
+ // This is the penalty for disconnecting
+ } catch (err) {
+ console.error('Error reporting disconnect:', err);
+ }
+ }
+
+ // Remove snake only after blockchain operations complete
+ // Use immediate removal since this is between ticks (during disconnect handling)
+ this.gameServer.removeSnake(player.snakeId, true);
+ }
+
+ // Remove player's delta compression state
+ this.deltaCompressor.removePlayer(player.id);
+
+ this.players.delete(player.ws);
+ }
+
+ private broadcastGameState(): void {
+ const fullState = this.gameServer.getGameState();
+
+ for (const [ws, player] of this.players) {
+ // Skip players that are disconnecting to avoid race conditions
+ if (player.disconnecting || ws.readyState !== WebSocket.OPEN) {
+ continue;
+ }
+
+ // Get delta or full state for this player
+ const stateForPlayer = this.deltaCompressor.getDeltaOrFullState(player.id, fullState);
+
+ // Send the state
+ ws.send(JSON.stringify(stateForPlayer));
+ }
+ }
+
+ private checkDeadSnakes(): void {
+ for (const [ws, player] of this.players) {
+ // Skip players that are disconnecting to avoid race conditions
+ if (player.disconnecting) continue;
+
+ if (player.snakeId && this.gameServer.isSnakeDead(player.snakeId)) {
+ const score = this.gameServer.getSnakeScore(player.snakeId);
+ const deadMessage: DeadMessage = {
+ type: MessageType.DEAD,
+ finalScore: score,
+ };
+
+ this.sendMessage(ws, deadMessage);
+
+ // Don't remove the snake yet - player can respawn
+ player.snakeId = null;
+ }
+ }
+ }
+
+ private sendMessage(ws: WebSocket, message: ServerMessage): void {
+ if (ws.readyState === WebSocket.OPEN) {
+ ws.send(JSON.stringify(message));
+ }
+ }
+}
+
+// Start the server
+const server = new WebSocketGameServer(Number(PORT));
+server.start();
+
+console.log(`
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ Slither.io Server Running โ
+โ Port: ${PORT} โ
+โ Ready for connections! โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+`);
+
diff --git a/entropy/0xSlither/server/src/withdrawFunds.ts b/entropy/0xSlither/server/src/withdrawFunds.ts
new file mode 100644
index 0000000..1278410
--- /dev/null
+++ b/entropy/0xSlither/server/src/withdrawFunds.ts
@@ -0,0 +1,167 @@
+/**
+ * Standalone script to withdraw accumulated SSS from StakeArena to server wallet
+ * Run this script periodically to collect funds from self-deaths and contract fees
+ *
+ * Usage:
+ * npm run build && node dist/server/src/withdrawFunds.js
+ *
+ * Or with ts-node:
+ * npx ts-node src/withdrawFunds.ts
+ */
+
+import { ethers } from 'ethers';
+import * as dotenv from 'dotenv';
+import { fileURLToPath } from 'url';
+import { dirname, join } from 'path';
+
+// Load environment variables
+dotenv.config();
+
+const STAKE_ARENA_ABI = [
+ 'function withdrawBalance() external',
+ 'function owner() external view returns (address)',
+];
+
+async function main() {
+ console.log('='.repeat(70));
+ console.log('0xSlither - Withdraw Accumulated SSS from StakeArena');
+ console.log('='.repeat(70));
+ console.log('');
+
+ // Validate environment variables
+ const rpcUrl = process.env.SAGA_RPC_URL;
+ const privateKey = process.env.PRIVATE_KEY;
+ const stakeArenaAddress = process.env.STAKE_ARENA_ADDRESS;
+
+ if (!rpcUrl || !privateKey || !stakeArenaAddress) {
+ console.error('โ Missing required environment variables:');
+ if (!rpcUrl) console.error(' - SAGA_RPC_URL');
+ if (!privateKey) console.error(' - PRIVATE_KEY');
+ if (!stakeArenaAddress) console.error(' - STAKE_ARENA_ADDRESS');
+ console.log('');
+ console.log('Please set these in your .env file');
+ process.exit(1);
+ }
+
+ // Setup provider and wallet
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
+ const wallet = new ethers.Wallet(privateKey, provider);
+ const stakeArena = new ethers.Contract(stakeArenaAddress, STAKE_ARENA_ABI, wallet);
+
+ console.log('Configuration:');
+ console.log(` Server Wallet: ${wallet.address}`);
+ console.log(` StakeArena: ${stakeArenaAddress}`);
+ console.log(` RPC: ${rpcUrl}`);
+ console.log('');
+
+ try {
+ // Check if wallet is the owner
+ console.log('Checking ownership...');
+ const owner = await stakeArena.owner();
+
+ if (owner.toLowerCase() !== wallet.address.toLowerCase()) {
+ console.error('โ Error: Wallet is not the contract owner');
+ console.log(` Contract Owner: ${owner}`);
+ console.log(` Your Wallet: ${wallet.address}`);
+ console.log('');
+ console.log('Only the contract owner can withdraw funds');
+ process.exit(1);
+ }
+
+ console.log('โ
Ownership verified');
+ console.log('');
+
+ // Check balances before withdrawal
+ console.log('Checking balances...');
+ const contractBalance = await provider.getBalance(stakeArenaAddress);
+ const walletBalance = await provider.getBalance(wallet.address);
+
+ const contractBalanceFormatted = ethers.formatEther(contractBalance);
+ const walletBalanceFormatted = ethers.formatEther(walletBalance);
+
+ console.log(` Contract Balance: ${contractBalanceFormatted} SSS`);
+ console.log(` Wallet Balance: ${walletBalanceFormatted} SSS`);
+ console.log('');
+
+ // Check if there's anything to withdraw
+ if (contractBalance === 0n) {
+ console.log('โ ๏ธ No SSS to withdraw from the contract');
+ console.log(' This is normal if no players have died recently');
+ console.log('='.repeat(70));
+ process.exit(0);
+ }
+
+ // Perform withdrawal
+ console.log(`Withdrawing ${contractBalanceFormatted} SSS...`);
+ console.log('');
+
+ const tx = await stakeArena.withdrawBalance();
+ console.log(`๐ค Transaction submitted: ${tx.hash}`);
+
+ // Wait for confirmation
+ const receipt = await tx.wait();
+ console.log(`โ
Transaction confirmed in block ${receipt?.blockNumber}`);
+ console.log('');
+
+ // Check balances after withdrawal
+ console.log('Final balances:');
+ const newContractBalance = await provider.getBalance(stakeArenaAddress);
+ const newWalletBalance = await provider.getBalance(wallet.address);
+
+ console.log(` Contract Balance: ${ethers.formatEther(newContractBalance)} SSS`);
+ console.log(` Wallet Balance: ${ethers.formatEther(newWalletBalance)} SSS`);
+ console.log('');
+
+ // Calculate amounts
+ const amountWithdrawn = contractBalance;
+ const gasUsed = BigInt(receipt?.gasUsed || 0n);
+ const gasPrice = BigInt(receipt?.gasPrice || 0n);
+ const gasCost = gasUsed * gasPrice;
+ const netGain = amountWithdrawn - gasCost;
+
+ console.log('Transaction summary:');
+ console.log(` Amount Withdrawn: ${ethers.formatEther(amountWithdrawn)} SSS`);
+ console.log(` Gas Used: ${gasUsed.toString()} units`);
+ console.log(` Gas Cost: ${ethers.formatEther(gasCost)} SSS`);
+ console.log(` Net Gain: ${ethers.formatEther(netGain)} SSS`);
+ console.log('');
+
+ console.log('='.repeat(70));
+ console.log('โ
Withdrawal completed successfully!');
+ console.log('='.repeat(70));
+
+ } catch (error: any) {
+ console.log('');
+ console.log('='.repeat(70));
+ console.error('โ Withdrawal failed:', error.message);
+ console.log('='.repeat(70));
+ console.log('');
+
+ if (error.message.includes('No balance to withdraw')) {
+ console.log('The contract has no SSS to withdraw.');
+ } else if (error.message.includes('Only owner')) {
+ console.log('Only the contract owner can withdraw funds.');
+ } else if (error.message.includes('insufficient funds')) {
+ console.log('Insufficient gas funds in server wallet.');
+ console.log(`Current balance: ${ethers.formatEther(await provider.getBalance(wallet.address))} SSS`);
+ } else {
+ console.log('Possible causes:');
+ console.log(' - Network connectivity issues');
+ console.log(' - Contract not deployed at specified address');
+ console.log(' - Insufficient gas for transaction');
+ console.log(' - Contract function not available (needs upgrade)');
+ }
+
+ console.log('');
+ process.exit(1);
+ }
+}
+
+// Run the main function
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error('Unexpected error:', error);
+ process.exit(1);
+ });
+
diff --git a/entropy/0xSlither/server/tsconfig.json b/entropy/0xSlither/server/tsconfig.json
new file mode 100644
index 0000000..8274b7f
--- /dev/null
+++ b/entropy/0xSlither/server/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "CommonJS",
+ "lib": ["ES2022"],
+ "outDir": "./dist",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "moduleResolution": "node",
+ "baseUrl": ".",
+ "paths": {
+ "@0xslither/shared": ["../shared"]
+ }
+ },
+ "include": ["src/**/*", "../shared/**/*"],
+ "exclude": ["node_modules", "dist"]
+}
+
diff --git a/entropy/0xSlither/shared/constants.ts b/entropy/0xSlither/shared/constants.ts
new file mode 100644
index 0000000..be6bd74
--- /dev/null
+++ b/entropy/0xSlither/shared/constants.ts
@@ -0,0 +1,35 @@
+// Game constants shared between client and server
+
+// World settings
+export const WORLD_WIDTH = 5000;
+export const WORLD_HEIGHT = 5000;
+
+// Server settings
+export const TICK_RATE = 20; // Updates per second
+export const TICK_INTERVAL = 1000 / TICK_RATE; // 50ms
+
+// Snake settings
+export const SNAKE_BASE_SPEED = 150; // Units per second
+export const SNAKE_MAX_ROTATION_SPEED = Math.PI * 2; // Radians per second (360 degrees)
+export const SNAKE_INITIAL_LENGTH = 5;
+export const SNAKE_SEGMENT_SPACING = 15; // Distance between segments
+export const SNAKE_HEAD_RADIUS = 10;
+export const SNAKE_SEGMENT_RADIUS = 8;
+export const SNAKE_GROWTH_PER_PELLET = 3; // Default segments added per pellet (actual growth varies by pellet size)
+
+// Pellet settings
+export const PELLET_COUNT = 200;
+export const PELLET_MIN_SIZE = 4;
+export const PELLET_MAX_SIZE = 8;
+export const PELLET_COLORS = [
+ '#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A',
+ '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E2',
+ '#F8B739', '#52B788'
+];
+
+// Collision detection
+export const COLLISION_DISTANCE = SNAKE_HEAD_RADIUS + SNAKE_SEGMENT_RADIUS;
+
+// Leaderboard
+export const LEADERBOARD_SIZE = 5;
+
diff --git a/entropy/0xSlither/shared/index.ts b/entropy/0xSlither/shared/index.ts
new file mode 100644
index 0000000..bc0bf69
--- /dev/null
+++ b/entropy/0xSlither/shared/index.ts
@@ -0,0 +1,5 @@
+// Shared module exports
+export * from './constants';
+export * from './types';
+export * from './protocol';
+
diff --git a/entropy/0xSlither/shared/package.json b/entropy/0xSlither/shared/package.json
new file mode 100644
index 0000000..034e7f9
--- /dev/null
+++ b/entropy/0xSlither/shared/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "@0xslither/shared",
+ "version": "1.0.0",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "../node_modules/.pnpm/node_modules/.bin/tsc",
+ "clean": "rm -rf dist"
+ },
+ "devDependencies": {
+ "typescript": "^5.5.4"
+ }
+}
+
diff --git a/entropy/0xSlither/shared/protocol.ts b/entropy/0xSlither/shared/protocol.ts
new file mode 100644
index 0000000..5929fb9
--- /dev/null
+++ b/entropy/0xSlither/shared/protocol.ts
@@ -0,0 +1,109 @@
+// WebSocket message protocol
+
+import { SerializedSnake, SerializedPellet, SerializedLeaderboard } from './types';
+
+// Message types
+export enum MessageType {
+ JOIN = 'JOIN',
+ INPUT = 'INPUT',
+ STATE = 'STATE',
+ DELTA_STATE = 'DELTA_STATE', // Delta-compressed state for bandwidth optimization
+ DEAD = 'DEAD',
+ PING = 'PING',
+ PONG = 'PONG',
+ TAPOUT = 'TAPOUT',
+ TAPOUT_SUCCESS = 'TAPOUT_SUCCESS',
+}
+
+// Client to Server messages
+export interface JoinMessage {
+ type: MessageType.JOIN;
+ name: string;
+ address: string; // Required EVM wallet address
+}
+
+export interface InputMessage {
+ type: MessageType.INPUT;
+ targetAngle: number; // Desired angle in radians
+}
+
+export interface PingMessage {
+ type: MessageType.PING;
+ timestamp: number;
+}
+
+export interface TapOutMessage {
+ type: MessageType.TAPOUT;
+ matchId: string;
+}
+
+export type ClientMessage = JoinMessage | InputMessage | PingMessage | TapOutMessage;
+
+// Server to Client messages
+export interface StateMessage {
+ type: MessageType.STATE;
+ tick: number;
+ snakes: SerializedSnake[];
+ pellets: SerializedPellet[];
+ leaderboard: SerializedLeaderboard;
+ yourId?: string; // Only sent to new players
+ matchId?: string; // Match ID for blockchain operations
+ entropyPending?: boolean; // True while waiting for Pyth Entropy reveal
+ entropyRequestId?: string; // Pyth Entropy request ID (sequence number)
+ useFairRNG?: boolean; // True if using Pyth Entropy (not fallback)
+ mapType?: 'uniform' | 'clustered' | 'ring'; // Map generation type
+ entropySeed?: string; // The actual entropy seed from Pyth (for display/verification)
+}
+
+// Delta-compressed state message - only sends changes since last update
+// Note: Entropy details (seed, requestId, mapType) are only sent in full state messages
+// to reduce bandwidth during gameplay - they're preserved on the client side
+export interface DeltaStateMessage {
+ type: MessageType.DELTA_STATE;
+ tick: number;
+ snakesAdded: SerializedSnake[]; // New snakes since last update
+ snakesUpdated: SerializedSnake[]; // Snakes that moved or changed
+ snakesRemoved: string[]; // IDs of snakes that died/left
+ pelletsRemoved: string[]; // IDs of pellets that were eaten (pellets never respawn)
+ leaderboardChanged?: SerializedLeaderboard; // Only sent if leaderboard changed
+ yourId?: string; // Only sent to new players
+ matchId?: string;
+ entropyPending?: boolean; // True while waiting for entropy reveal
+ useFairRNG?: boolean; // True if using Pyth Entropy (not fallback)
+}
+
+export interface DeadMessage {
+ type: MessageType.DEAD;
+ finalScore: number;
+}
+
+export interface PongMessage {
+ type: MessageType.PONG;
+ timestamp: number;
+}
+
+export interface TapOutSuccessMessage {
+ type: MessageType.TAPOUT_SUCCESS;
+ amountWithdrawn: number;
+}
+
+export type ServerMessage = StateMessage | DeltaStateMessage | DeadMessage | PongMessage | TapOutSuccessMessage;
+
+// Type guards
+export function isJoinMessage(msg: any): msg is JoinMessage {
+ return msg.type === MessageType.JOIN && typeof msg.name === 'string' &&
+ typeof msg.address === 'string';
+}
+
+export function isInputMessage(msg: any): msg is InputMessage {
+ return msg.type === MessageType.INPUT && typeof msg.targetAngle === 'number';
+}
+
+export function isPingMessage(msg: any): msg is PingMessage {
+ return msg.type === MessageType.PING && typeof msg.timestamp === 'number';
+}
+
+export function isTapOutMessage(msg: any): msg is TapOutMessage {
+ return msg.type === MessageType.TAPOUT && typeof msg.matchId === 'string';
+}
+
diff --git a/entropy/0xSlither/shared/tsconfig.json b/entropy/0xSlither/shared/tsconfig.json
new file mode 100644
index 0000000..471ff04
--- /dev/null
+++ b/entropy/0xSlither/shared/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "CommonJS",
+ "lib": ["ES2022"],
+ "outDir": "./dist",
+ "declaration": true,
+ "declarationMap": true,
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "moduleResolution": "node"
+ },
+ "include": ["./*.ts"],
+ "exclude": ["node_modules", "dist"]
+}
+
diff --git a/entropy/0xSlither/shared/types.ts b/entropy/0xSlither/shared/types.ts
new file mode 100644
index 0000000..6c56351
--- /dev/null
+++ b/entropy/0xSlither/shared/types.ts
@@ -0,0 +1,55 @@
+// Shared types for game entities
+
+export interface Position {
+ x: number;
+ y: number;
+}
+
+export interface SnakeSegment extends Position {
+ radius: number;
+}
+
+export interface Snake {
+ id: string;
+ name: string;
+ address?: string; // Optional EVM wallet address
+ headPosition: Position;
+ angle: number;
+ segments: SnakeSegment[];
+ length: number;
+ color: string;
+ alive: boolean;
+ pelletTokens: number;
+}
+
+export interface Pellet {
+ id: string;
+ x: number;
+ y: number;
+ size: number;
+ color: string;
+ tokenAmount: number;
+}
+
+export interface LeaderboardEntry {
+ name: string;
+ score: number;
+ address?: string; // Optional EVM wallet address
+}
+
+// Serialized types for network transmission (more compact)
+export type SerializedSnake = {
+ id: string;
+ name: string;
+ address?: string; // Optional EVM wallet address
+ head: [number, number];
+ angle: number;
+ segments: [number, number][];
+ color: string;
+ pelletTokens: number;
+};
+
+export type SerializedPellet = [string, number, number, number, string, number]; // [id, x, y, size, color, tokenAmount]
+
+export type SerializedLeaderboard = [string, number, string?][]; // [name, score, address?]
+